using Cognex.VisionPro.ToolBlock; using Cognex.VisionPro; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.RootFinding; using NPOI.SS.Formula.Functions; using NPOI.Util; using OpenCvSharp; using OpenCvSharp.Flann; using Prism.Events; using Prism.Ioc; using Prism.Mvvm; using Prism.Regions; using Prism.Services.Dialogs; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Documents; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Media3D; using Team.FFFeederService; using Team.FFFeederService.Interfaces; using TeamAAS_VP.Data; using TeamAAS_VP.Enums; using TeamAAS_VP.Events; using TeamAAS_VP.Models; using TeamAAS_VP.ViewModels.Home; using TeamAAS_VP; using TouchSocket.Core; using TouchSocket.Sockets; using static MaterialDesignThemes.Wpf.Theme.ToolBar; using System.ComponentModel; using System.Collections; using System.Reflection.Metadata; using TeamAAS_VP.Resources.Languages; using TeamAAS_VP.Core.PLCs; using TeamAAS_VP.Interfaces; using Opc.Ua; using TeamAAS_VP.Core.Robots; using TeamAAS_VP.Core.RFID; using TeamAAS_VP.Controls; using TeamAAS_VP.Core.Sfis; namespace TeamAAS_VP.Core { public class Management : BindableBase { /// EthernetIP 底栏状态项固定 Id(与设备 Id 无关) public static readonly Guid EthernetIpStatusId = new Guid("E4E10000-0001-4000-8000-000000000001"); #region 字段 IRegionManager _regionManager; IEventAggregator _eventAggregator; IContainerProvider _container; System.Threading.Timer cTtimer; System.Threading.Timer yieldtimer; private bool IsStart = false; private DateTime StartTime = DateTime.Now; #endregion #region 属性 /// /// 当前用户 /// public User CurrentUser { get; set; } = new User() { UserName = "操作员", UserPassword = "", IsRemember = false, userPart = Enums.UserPart.Operator, CreateTime = DateTime.Now }; /// /// 设备参数 /// public DeviceConfiguration DeviceConfig { get; set; } = new DeviceConfiguration(); private Language _CurrentLanguage = Language.ChineseSimplified; /// /// 当前软件使用的语言 /// public Language CurrentLanguage { get { return _CurrentLanguage; } set { SetProperty(ref _CurrentLanguage, value); } } private ObservableCollection _Renders; /// /// 显示界面 /// public ObservableCollection Renders { get { return _Renders; } set { SetProperty(ref _Renders, value); } } private Dictionary _plcTrigger = new Dictionary(); /// /// 当前plc触发监控 /// public Dictionary PlcTrigger { get => _plcTrigger; set => _plcTrigger = value; } private ObservableCollection _Products; /// /// 产品列表 /// public ObservableCollection Products { get { return _Products; } set { SetProperty(ref _Products, value); } } private ProductModel _CurrentProduct; /// /// 当前产品 /// public ProductModel CurrentProduct { get { return _CurrentProduct; } set { SetProperty(ref _CurrentProduct, value); } } public FeederService FeederService { get; set; } public RobotService RobotService { get; set; } public CameraService CameraService { get; set; } public PlcService PlcService { get; set; } public BgCommunicate BgCommunicate { get; private set; } public BgModbusTcpCommunicate BgModbusTcpCommunicate { get; private set; } private CancellationTokenSource _handshakeCts; private Task _handshakeTask; private CancellationTokenSource _heartbeatCts; private Task _heartbeatTask; private CancellationTokenSource _sfisSignalCts; private Task _sfisSignalTask; private SfisWorkflowService _sfisWorkflow; private RFIDTagData _lastRfidTag; private readonly object _sync = new object(); public SIG350RFIDClient BgEipCommunicate { get; private set; } private CancellationTokenSource _mcReconnectCts; private Task _mcReconnectTask; private readonly object _mcReconnectSync = new object(); private DateTime _mcLastReconnectAttempt = DateTime.MinValue; private const int McReconnectIntervalMs = 3000; private Action _mcPlcLogHandler; private volatile bool _mcMonitorLoopsStarted; public ModbusTcpMasterCommunicate ModbusTcpMasterCommunicate { get; private set; } /// /// Modbus TCP 主站配置(连接电批等设备) /// public ModbusTcpMasterConfig ModbusTcpMasterConfig { get; set; } = new ModbusTcpMasterConfig(); private ObservableCollection _Status = new ObservableCollection(); /// /// 状态栏集合 /// public ObservableCollection Status { get { return _Status; } set { SetProperty(ref _Status, value); } } private TimeSpan _CT; public TimeSpan CT { get { return _CT; } set { SetProperty(ref _CT, value); } } private Int64 _TotalQuantity; /// /// 总产量 /// public Int64 TotalQuantity { get { return _TotalQuantity; } set { SetProperty(ref _TotalQuantity, value); } } private Int64 _TotalQuantityToday; /// /// 当天的总产量 /// public Int64 TotalQuantityToday { get { return _TotalQuantityToday; } set { SetProperty(ref _TotalQuantityToday, value); } } private Int64 _CurrentUPH; /// /// 当前的UPH /// public Int64 CurrentUPH { get { return _CurrentUPH; } set { SetProperty(ref _CurrentUPH, value); } } public Dictionary BackupObject { get; set; } = new Dictionary(); #endregion #region 命令 #endregion #region 事件 #endregion public Management(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container) { _regionManager = regionManager; _eventAggregator = ea; _container = container; cTtimer = new Timer(DoCycleTime, null, 5000, 100); yieldtimer = new Timer(DoYieldTime, null, 10000, 1000); Renders = new ObservableCollection(); } #region 配置参数读写操作 /// /// 读取配置参数 /// public void ReadConfig() { ReadCamerasDeviceConfig(); ReadRobotsDeviceConfig(); ReadFeedersDeviceConfig(); ReadPlcDeviceConfig(); ReadBgTcpIpDeviceConfig(); ReadBgModbusTcpDeviceConfig(); ReadModbusTcpMasterDeviceConfig(); ReadBgEthernetIPDeviceConfig(); ReadBgSfisDeviceConfig(); } /// /// 读取相机设备配置参数 /// public void ReadCamerasDeviceConfig() { try { if (File.Exists(FilePath.CamerasConfigurationPath)) { DeviceConfig.Cameras = FileHelper.ReadJsonFile>(FilePath.CamerasConfigurationPath); } else { DeviceConfig.Cameras = new ObservableCollection(); } } catch (Exception ex) { DeviceConfig.Robots = new ObservableCollection(); LogHelper.WriteLogError("加载相机配置文件时出错!", ex); } } /// /// 保存相机设备配置参数 /// public void SaveCamerasDeviceConfig() { try { FileHelper.WriteJsonFile(DeviceConfig.Cameras, FilePath.CamerasConfigurationPath); } catch (Exception ex) { LogHelper.WriteLogError("保存相机配置参数至文件时出错!", ex); } } /// /// 读取机器人设备配置参数 /// public void ReadRobotsDeviceConfig() { try { if (File.Exists(FilePath.RobotsConfigurationPath)) { DeviceConfig.Robots = FileHelper.ReadJsonFile>(FilePath.RobotsConfigurationPath); } else { DeviceConfig.Robots = new ObservableCollection(); } } catch (Exception ex) { DeviceConfig.Robots = new ObservableCollection(); LogHelper.WriteLogError("加载机器人配置文件时出错!", ex); } } /// /// 保存机器人设备配置参数 /// public void SaveRobotsDeviceConfig() { try { FileHelper.WriteJsonFile(DeviceConfig.Robots, FilePath.RobotsConfigurationPath); } catch (Exception ex) { LogHelper.WriteLogError("保存机器人配置参数至文件时出错!", ex); } } /// /// 读取Feeder设备配置参数 /// public void ReadFeedersDeviceConfig() { try { if (File.Exists(FilePath.FeedersConfigurationPath)) { DeviceConfig.Feeders = FileHelper.ReadJsonFile>(FilePath.FeedersConfigurationPath); } else { DeviceConfig.Feeders = new ObservableCollection(); } } catch (Exception ex) { DeviceConfig.Feeders = new ObservableCollection(); LogHelper.WriteLogError("加载Feeder配置文件时出错!", ex); } } /// /// 保存Feeder设备配置参数 /// public void SaveFeederDeviceConfig() { try { FileHelper.WriteJsonFile(DeviceConfig.Feeders, FilePath.FeedersConfigurationPath); } catch (Exception ex) { LogHelper.WriteLogError("保存Feeder配置参数至文件时出错!", ex); } } /// /// 读取PLC设备配置参数 /// public void ReadPlcDeviceConfig() { try { if (File.Exists(FilePath.PlcConfigurationPath)) { DeviceConfig.Plcs = FileHelper.ReadJsonFile>(FilePath.PlcConfigurationPath); } else { DeviceConfig.Plcs = new ObservableCollection(); } } catch (Exception ex) { DeviceConfig.Plcs = new ObservableCollection(); LogHelper.WriteLogError("加载PLC配置文件时出错!", ex); } TryMigrateLegacyMcConfig(); } private void TryMigrateLegacyMcConfig() { if (DeviceConfig.Plcs.Any(p => p.CommunicationType == CommunicationType.Mitsubishi_MC)) return; try { if (!File.Exists(FilePath.BgMCToPcCommunicatePath)) return; var legacy = FileHelper.ReadJsonFile(FilePath.BgMCToPcCommunicatePath); if (legacy == null) return; var mcPlc = new PlcInfo { Id = Guid.NewGuid(), Name = "Mitsubishi MC", CommunicationType = CommunicationType.Mitsubishi_MC }; mcPlc.ApplyMcInfo(legacy); DeviceConfig.Plcs.Add(mcPlc); SavePlcDeviceConfig(); } catch (Exception ex) { LogHelper.WriteLogError("迁移旧版 MC 配置至 PLC 列表时出错!", ex); } } /// /// 保存PLC设备配置参数 /// public void SavePlcDeviceConfig() { try { FileHelper.WriteJsonFile(DeviceConfig.Plcs, FilePath.PlcConfigurationPath); } catch (Exception ex) { LogHelper.WriteLogError("保存PLC配置参数至文件时出错!", ex); } } /// /// 读取语言配置参数 /// public void ReadLanguageDeviceConfig() { try { if (File.Exists(FilePath.LanguageConfigurationPath)) { CurrentLanguage = FileHelper.ReadJsonFile(FilePath.LanguageConfigurationPath); } else { CurrentLanguage = Language.ChineseSimplified; } } catch (Exception ex) { CurrentLanguage = Language.ChineseSimplified; LogHelper.WriteLogError("加载Language配置文件时出错!", ex); } } /// /// 保存语言配置参数 /// public void SaveLanguageDeviceConfig() { try { FileHelper.WriteJsonFile(CurrentLanguage, FilePath.LanguageConfigurationPath); } catch (Exception ex) { LogHelper.WriteLogError("保存Language配置参数至文件时出错!", ex); } } /// /// 读取后台TCPIP通讯配置参数 /// public void ReadBgTcpIpDeviceConfig() { try { if (File.Exists(FilePath.BgTcpIpConfigurationPath)) { DeviceConfig.BgCommunicate = FileHelper.ReadJsonFile(FilePath.BgTcpIpConfigurationPath); } else { DeviceConfig.BgCommunicate = new BgTcpIP(); } } catch (Exception ex) { DeviceConfig.BgCommunicate = new BgTcpIP(); LogHelper.WriteLogError("加载后台TCPIP通讯配置文件时出错!", ex); } } /// /// 保存后台TCPIP通讯配置参数 /// public void SaveBgTcpIpDeviceConfig() { try { FileHelper.WriteJsonFile(DeviceConfig.BgCommunicate, FilePath.BgTcpIpConfigurationPath); } catch (Exception ex) { LogHelper.WriteLogError("保存后台TCPIP通讯配置参数至文件时出错!", ex); } } /// /// 读取后台ModbusTcp通讯配置参数 /// public void ReadBgModbusTcpDeviceConfig() { try { if (File.Exists(FilePath.BgModbusTcpConfigurationPath)) { DeviceConfig.BgModbusCommunicate = FileHelper.ReadJsonFile(FilePath.BgModbusTcpConfigurationPath); } else { DeviceConfig.BgModbusCommunicate = new BgModbusTcp(); } } catch (Exception ex) { DeviceConfig.BgModbusCommunicate = new BgModbusTcp(); LogHelper.WriteLogError("加载后台Modbus TCP通讯配置文件时出错!", ex); } } /// /// 保存后台ModbusTcp通讯配置参数 /// public void SaveBgModbusTcpDeviceConfig() { try { FileHelper.WriteJsonFile(DeviceConfig.BgModbusCommunicate, FilePath.BgModbusTcpConfigurationPath); } catch (Exception ex) { LogHelper.WriteLogError("保存后台ModbusTcp通讯配置参数至文件时出错!", ex); } } /// /// 读取后台EthernetIP通讯配置参数 /// public void ReadBgEthernetIPDeviceConfig() { try { if (File.Exists(FilePath.BgEthernetIPCommunicatePath)) { DeviceConfig.BgEthernetIPCommunicate = FileHelper.ReadJsonFile(FilePath.BgEthernetIPCommunicatePath); } else { DeviceConfig.BgEthernetIPCommunicate = new BgEthernetIP(); FileHelper.WriteJsonFile(DeviceConfig.BgEthernetIPCommunicate, FilePath.BgEthernetIPCommunicatePath); } } catch (Exception ex) { DeviceConfig.BgEthernetIPCommunicate = new BgEthernetIP(); LogHelper.WriteLogError("加载后台Eip通讯配置文件时出错!", ex); } } public void ReadBgSfisDeviceConfig() { try { if (File.Exists(FilePath.BgSfisCommunicatePath)) { DeviceConfig.BgSfisCommunicate = FileHelper.ReadJsonFile(FilePath.BgSfisCommunicatePath); } else { DeviceConfig.BgSfisCommunicate = new BgSfisConfig(); FileHelper.WriteJsonFile(DeviceConfig.BgSfisCommunicate, FilePath.BgSfisCommunicatePath); } } catch (Exception ex) { DeviceConfig.BgSfisCommunicate = new BgSfisConfig(); LogHelper.WriteLogError("加载 SFIS 配置文件时出错!", ex); } } /// /// 保存EIP配置 /// public void SaveBgEthernetIPDeviceConfig() { try { FileHelper.WriteJsonFile(DeviceConfig.BgEthernetIPCommunicate, FilePath.BgEthernetIPCommunicatePath); } catch (Exception ex) { LogHelper.WriteLogError("保存后台Eip通讯配置参数至文件时出错!", ex); } } public void SaveBgSfisDeviceConfig() { try { FileHelper.WriteJsonFile(DeviceConfig.BgSfisCommunicate, FilePath.BgSfisCommunicatePath); } catch (Exception ex) { LogHelper.WriteLogError("保存 SFIS 配置参数至文件时出错!", ex); } } #endregion #region 初始化硬件模块 /// /// 初始化视觉 /// public Tuple InitVision() { try { return new Tuple(true, ""); } catch (Exception ex) { LogHelper.WriteLogError("初始化VM视觉方式时出错!", ex); return new Tuple(false, ex.Message); } } /// /// 初始化Feeder /// /// public async Task> InitFeeders() { FeederService = new FeederService(); StringBuilder sb = new StringBuilder(); foreach (var item in DeviceConfig.Feeders) { FeederService.CreateFeeder(item.Id, item.IP, item.Port, item.FeederBrand); Status.Add(new StatusInfo(item.Id, item.FeederName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red))); try { FeederService.GetFeeder(item.Id).FeederConnectedEvent += Management_FeederConnectedEvent; FeederService.GetFeeder(item.Id).FeederDisconnectedEvent += Management_FeederDisconnectedEvent; await FeederService.GetFeeder(item.Id).ConnectAsync(); //连接Feeder //SendTaskMessage($"{item.FeederName}已连接!", MessageLevel.Debug); } catch (Exception ex) { sb.AppendLine($"{item.FeederName}:{Lang.断开连接}![{ex.Message}]"); SendTaskMessage($"{item.FeederName}{Lang.断开连接}!", MessageLevel.Alarm); } } if (sb.Length == 0) { return new Tuple(true, ""); } else { return new Tuple(false, sb.ToString()); } } /// /// 初始化机器人 /// /// public async Task> InitRobots() { RobotService = new RobotService(); StringBuilder sb = new StringBuilder(); foreach (var item in DeviceConfig.Robots) { RobotService.CreateRobot(item.Id, item); if (item.RobotBrand == RobotBrand.XYZ_Platform || item.RobotBrand == RobotBrand.XYZU_Platform) { RobotService.GetRobot(item.Id).TrggerChangeEvent += Management_TrggerChangeEvent; continue; } try { Status.Add(new StatusInfo(item.Id, item.RobotName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red))); RobotService.GetRobot(item.Id).ReceivedEvent += Management_ReceivedEvent; RobotService.GetRobot(item.Id).SendEvent += Management_SendEvent; RobotService.GetRobot(item.Id).ConnectedEvent += Management_ConnectedEvent; RobotService.GetRobot(item.Id).DisconnectedEvent += Management_DisconnectedEvent; await RobotService.GetRobot(item.Id).ConnectAsync(); //连接Robot SendTaskMessage($"{item.RobotName}{Lang.已连接}!", MessageLevel.Debug); } catch (Exception ex) { sb.AppendLine($"{item.RobotName}:{Lang.断开连接}![{ex.Message}]"); SendTaskMessage($"{item.RobotName}{Lang.断开连接}!", MessageLevel.Alarm); } } if (sb.Length == 0) { return new Tuple(true, ""); } else { return new Tuple(false, sb.ToString()); } } /// /// 初始化PLC /// /// public async Task> InitPlc() { PlcService = new PlcService(); StringBuilder sb = new StringBuilder(); foreach (var item in DeviceConfig.Plcs) { try { if (!PlcService.CreatePlc(item.Id, item)) { sb.AppendLine($"{item.Name}: 不支持的 PLC 通讯类型 [{item.CommunicationType}]"); continue; } var plc = PlcService.GetPlc(item.Id); if (plc == null) { sb.AppendLine($"{item.Name}: 创建 PLC 失败"); continue; } Status.Add(new StatusInfo(item.Id, item.Name + $"{Lang.未连接}", new SolidColorBrush(Colors.Red))); plc.ConnectChangedEvent += Management_PlcConnectChangedEvent; await plc.ConnectAsync(); } catch (Exception ex) { sb.AppendLine($"{item.Name}:{Lang.断开连接}![{ex.Message}]"); SendTaskMessage($"{item.Name}{Lang.断开连接}!", MessageLevel.Alarm); } } if (sb.Length == 0) { return new Tuple(true, ""); } else { return new Tuple(false, sb.ToString()); } } /// /// 初始化相机 /// /// public Task> InitCameras() { CameraService = new CameraService(); StringBuilder sb = new StringBuilder(); foreach (var item in DeviceConfig.Cameras) { try { Renders.Add(new ShowRender() { Id = item.Id, CameraName = item.CameraName }); CameraService.CreateCamera(item.Id, item); Status.Add(new StatusInfo(item.Id, item.CameraName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red))); CameraService.GetCamera(item.Id).CameraConnectChangedEvent += Management_CameraConnectChangedEvent; if (CameraService.GetCamera(item.Id).OpenDevice()) { SendTaskMessage($"{item.CameraName}{Lang.已连接}!", MessageLevel.Debug); } else { SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm); sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!"); } } catch (Exception ex) { SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm); sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!:{ex.Message}"); } } _container.Resolve().Renders = Renders; if (sb.Length == 0) { return Task.FromResult(new Tuple(true, "")); } else { return Task.FromResult(new Tuple(false, sb.ToString())); } } /// /// 初始化后台TCP服务器通讯 /// public void InitBgTcp() { try { BgCommunicate = new BgCommunicate(DeviceConfig.BgCommunicate); BgCommunicate.ConnectedEvent += BgCommunicate_ConnectedEvent; BgCommunicate.DisconnectedEvent += BgCommunicate_DisconnectedEvent; BgCommunicate.ReceivedEvent += BgCommunicate_ReceivedEvent; BgCommunicate.SendEvent += BgCommunicate_SendEvent; BgCommunicate.StartListening(); SendTaskMessage($"{Lang.已开启监听端口}:{DeviceConfig.BgCommunicate.Port}", MessageLevel.Debug); } catch (Exception ex) { SendTaskMessage($"{Lang.未开启监听端口}:{DeviceConfig.BgCommunicate.Port}", MessageLevel.Error); LogHelper.WriteLogError("开启后台服务器通讯时出错!", ex); } } /// /// 初始化后台ModbusTCP从站 /// public void InitModbusTcp() { try { BgModbusTcpCommunicate = new BgModbusTcpCommunicate(DeviceConfig.BgModbusCommunicate); if (DeviceConfig.BgModbusCommunicate.IsEnabled) { BgModbusTcpCommunicate.Listen(); SendTaskMessage($"Start Modbus Tcp Slave:{BgModbusTcpCommunicate.IP}:{BgModbusTcpCommunicate.Port}:{BgModbusTcpCommunicate.SlaveID}", MessageLevel.Debug); } } catch (Exception ex) { SendTaskMessage($"No Start Modbus Tcp Slave:{ex.Message}", MessageLevel.Alarm); LogHelper.WriteLogError("开启后台Modbus TCP通讯时出错!", ex); } } /// /// 读取 Modbus TCP 主站配置 /// public void ReadModbusTcpMasterDeviceConfig() { try { if (File.Exists(FilePath.ModbusTcpMasterConfigurationPath)) { ModbusTcpMasterConfig = FileHelper.ReadJsonFile(FilePath.ModbusTcpMasterConfigurationPath); } else { ModbusTcpMasterConfig = new ModbusTcpMasterConfig(); } } catch (Exception ex) { ModbusTcpMasterConfig = new ModbusTcpMasterConfig(); LogHelper.WriteLogError("加载 Modbus TCP 主站配置文件时出错!", ex); } } /// /// 保存 Modbus TCP 主站配置 /// public void SaveModbusTcpMasterDeviceConfig() { try { FileHelper.WriteJsonFile(ModbusTcpMasterConfig, FilePath.ModbusTcpMasterConfigurationPath); } catch (Exception ex) { LogHelper.WriteLogError("保存 Modbus TCP 主站配置时出错!", ex); } } /// /// 初始化 Modbus TCP 主站(轮询电批/扭矩控制器) /// public void InitModbusTcpMaster() { try { ModbusTcpMasterCommunicate?.Stop(); ModbusTcpMasterCommunicate = new ModbusTcpMasterCommunicate(ModbusTcpMasterConfig, _eventAggregator); if (!ModbusTcpMasterConfig.IsEnabled) { return; } ModbusTcpMasterCommunicate.Start(); SendTaskMessage($"Start Modbus Tcp Master:{ModbusTcpMasterConfig.IP}:{ModbusTcpMasterConfig.Port}:{ModbusTcpMasterConfig.SlaveID}", MessageLevel.Debug); } catch (Exception ex) { SendTaskMessage($"No Start Modbus Tcp Master:{ex.Message}", MessageLevel.Alarm); LogHelper.WriteLogError("开启 Modbus TCP 主站通讯时出错!", ex); } } /// /// 初始化 EthernetIP /// public void InitEthernetIP() { try { EnsureEthernetIpStatusEntry(); DisposeRfidClient(); BgEipCommunicate = new SIG350RFIDClient(DeviceConfig.BgEthernetIPCommunicate); BgEipCommunicate.BeforeStartRfidReading = ApplyEthernetIpRfidConfiguration; BgEipCommunicate.OnLog += BgEipCommunicate_OnLog; BgEipCommunicate.OnTagDataReceived += RfidClient_OnTagDataReceived; BgEipCommunicate.OnConnectReceive += Management_EipConnectChangedEvent; if (!DeviceConfig.BgEthernetIPCommunicate.IsEnabled) { UpdateEthernetIpStatus(false); return; } if (BgEipCommunicate.Connect()) { UpdateEthernetIpStatus(true); SendTaskMessage($"EthernetIP:{DeviceConfig.BgEthernetIPCommunicate.IP}", MessageLevel.Debug); StartEthernetIpRfidIO(); } else { UpdateEthernetIpStatus(false); // Connect 失败时会触发 OnConnectReceive(false),内部自动重连 } } catch (Exception ex) { UpdateEthernetIpStatus(false); SendTaskMessage($"EthernetIP err:{ex.Message}", MessageLevel.Alarm); LogHelper.WriteLogError("开启 EthernetIP通讯时出错!", ex); } } private void EnsureEthernetIpStatusEntry() { if (Status.Any(s => s.ID == EthernetIpStatusId)) return; App.Current.Dispatcher.Invoke(() => { if (!Status.Any(s => s.ID == EthernetIpStatusId)) { Status.Add(new StatusInfo( EthernetIpStatusId, $"{Lang.EthernetIP}{Lang.未连接}", new SolidColorBrush(Colors.Red))); } }); } private void UpdateEthernetIpStatus(bool isConnected) { var sta = Status.FirstOrDefault(s => s.ID == EthernetIpStatusId); if (sta == null) return; App.Current.Dispatcher.Invoke(() => { if (isConnected) { sta.Message = $"{Lang.EthernetIP}{Lang.已连接}"; sta.Background = new SolidColorBrush(Colors.Green); } else { sta.Message = $"{Lang.EthernetIP}{Lang.未连接}"; sta.Background = new SolidColorBrush(Colors.Red); } }); } private void Management_EipConnectChangedEvent(bool isConnected) { if (isConnected) { SendTaskMessage($"{Lang.EthernetIP}{Lang.已连接}!", MessageLevel.Debug); TryStartHandshakeAfterEipReady(); } else { SendTaskMessage($"{Lang.EthernetIP}{Lang.断开连接}!", MessageLevel.Error); } UpdateEthernetIpStatus(isConnected); } /// 配置 RFID 端口(1..N)并固定为 UID 读模式。 private void ApplyEthernetIpRfidConfiguration() { if (BgEipCommunicate == null || !BgEipCommunicate.IsConnected) return; var eipConfig = DeviceConfig.BgEthernetIPCommunicate; var ports = eipConfig.GetEnabledPortNumbers(); BgEipCommunicate.ConfigureRFIDPorts(ports, PortMode.IOLAutostart); BgEipCommunicate.ConfigureReadOptions(eipConfig.ToRfh5xxReadOptions()); SendTaskMessage($"RFID 已配置端口 1–{eipConfig.RfidPortCount},读 UID", MessageLevel.Debug); } private void StartEthernetIpRfidIO() { if (BgEipCommunicate == null || !BgEipCommunicate.IsConnected) return; try { ApplyEthernetIpRfidConfiguration(); } catch (Exception ex) { SendTaskMessage("RFID 端口配置失败: " + ex.Message, MessageLevel.Alarm); LogHelper.WriteLogError("RFID 端口配置失败", ex); } BgEipCommunicate.StartRFIDReading(DeviceConfig.BgEthernetIPCommunicate.CycleTime); TryStartHandshakeAfterEipReady(); } /// EIP 就绪后尝试启动 Ready 握手;MC 未连接则跳过,由 MC 重连循环补启。 private void TryStartHandshakeAfterEipReady() { var mcPlc = GetMcPlcInfo(); if (mcPlc == null) return; var client = GetMcPlcClient(); if (client == null || !client.IsConnected) return; TryStartHandshakeLoop(mcPlc); } /// /// 初始化三菱 MC PLC:自动连接、心跳、Ready 握手、SFIS 信号监听 /// public void InitMcCommunicate() { var mcPlc = GetMcPlcInfo(); if (mcPlc == null) { SendTaskMessage("未配置三菱 MC PLC,跳过 MC 通讯初始化", MessageLevel.Debug); return; } try { StopMcCommunicateLoops(); var mcWrapper = PlcService?.GetPlc(mcPlc.Id) as MitsubishiMcPLC; if (mcWrapper == null) { SendTaskMessage("MC PLC 未通过 PlcService 创建,请检查 PLC 配置", MessageLevel.Alarm); return; } var client = mcWrapper.Client; if (_mcPlcLogHandler == null) _mcPlcLogHandler = msg => SendTaskMessage($"PLC {msg}", MessageLevel.Info); client.OnLog -= _mcPlcLogHandler; client.OnLog += _mcPlcLogHandler; client.ConnectionLost -= McPlcConnectionLost; client.ConnectionLost += McPlcConnectionLost; if (!client.IsConnected) { mcWrapper.Connect(); } if (!client.IsConnected) { SendTaskMessage("PLC 连接失败,将自动重连", MessageLevel.Alarm); StartMcReconnectLoop(); return; } SendTaskMessage("PLC TCP 连接成功,正在测试 MC 读写...", MessageLevel.Info); string testDev = string.IsNullOrWhiteSpace(mcPlc.PlcDataStartDevice) ? "D110" : mcPlc.PlcDataStartDevice.Trim(); if (testDev == "0") testDev = "D110"; if (!client.TestCommunication(testDev)) { SendTaskMessage("MC 通讯测试失败,请检查:二进制3E、端口、允许RUN中写入", MessageLevel.Alarm); } else { SendTaskMessage("连接成功,MC 字通讯正常", MessageLevel.Info); } string bitTestDev = ResolveBitTestDevice(mcPlc); if (bitTestDev != null) { if (!client.TestBitCommunication(bitTestDev)) SendTaskMessage("MC 位通讯测试失败,请检查 OK/NG 位地址及 RUN 中写入", MessageLevel.Alarm); } else { SendTaskMessage("MC 位通讯测试跳过:请配置读OK或读NG位地址", MessageLevel.Debug); } client.ResetHeartbeatState(); EnsureMcCommunicateLoopsRunning(mcPlc, client); StartMcReconnectLoop(); } catch (Exception ex) { SendTaskMessage("PLC 连接失败: " + ex.Message, MessageLevel.Alarm); LogHelper.WriteLogError("PLC 连接失败", ex); StopMcCommunicateLoops(); } } private void McPlcConnectionLost() { _mcMonitorLoopsStarted = false; SendTaskMessage("MC PLC 连接中断", MessageLevel.Alarm); } /// MC 连接可用时确保心跳 / SFIS / Ready 握手循环已启动。 private void EnsureMcCommunicateLoopsRunning(PlcInfo mcPlc, MitsubishiPLC client) { if (mcPlc == null || client == null || !client.IsConnected) return; client.ResetHeartbeatState(); StartHeartbeatLoop(mcPlc); StartSfisSignalLoop(); TryStartHandshakeLoop(mcPlc); _mcMonitorLoopsStarted = true; } private void TryStartHandshakeLoop(PlcInfo mcPlc) { if (mcPlc == null || !mcPlc.PlcForwardEnabled || !CanStartHandshake(mcPlc)) return; StartHandshakeLoop(); } private PlcInfo GetMcPlcInfo() { return DeviceConfig?.Plcs?.FirstOrDefault(p => p.CommunicationType == CommunicationType.Mitsubishi_MC); } private MitsubishiPLC GetMcPlcClient() { var mcInfo = GetMcPlcInfo(); if (mcInfo == null || PlcService == null) return null; return (PlcService.GetPlc(mcInfo.Id) as MitsubishiMcPLC)?.Client; } private bool CanStartHandshake(PlcInfo mcPlc) { if (BgEipCommunicate == null || !BgEipCommunicate.IsConnected || !BgEipCommunicate.IsIOModeActive) return false; var mapping = mcPlc.ToPlcMapping(); return MitsubishiPLC.IsDeviceEnabled(mapping.ReadyBitDevice) && MitsubishiPLC.IsDeviceEnabled(mapping.PortDevice) && MitsubishiPLC.IsDeviceEnabled(mapping.DataStartDevice); } private string ResolveBitTestDevice(PlcInfo mcPlc) { if (mcPlc == null) return null; if (MitsubishiPLC.IsDeviceEnabled(mcPlc.PlcOkBitDevice)) return mcPlc.PlcOkBitDevice.Trim(); if (MitsubishiPLC.IsDeviceEnabled(mcPlc.PlcNgBitDevice)) return mcPlc.PlcNgBitDevice.Trim(); return null; } private void StopMcCommunicateLoops() { try { _mcMonitorLoopsStarted = false; StopMcReconnectLoop(); StopHandshakeLoop(); StopHeartbeatLoop(); StopSfisSignalLoop(); ResetSfisWorkflow(); } catch { /* ignore */ } } private void StartMcReconnectLoop() { StopMcReconnectLoop(); _mcReconnectCts = new CancellationTokenSource(); var token = _mcReconnectCts.Token; _mcReconnectTask = Task.Run(() => McReconnectLoop(token), token); } private void StopMcReconnectLoop() { try { if (_mcReconnectCts != null) { _mcReconnectCts.Cancel(); try { if (_mcReconnectTask != null) _mcReconnectTask.Wait(2000); } catch (AggregateException) { /* ignore */ } _mcReconnectCts.Dispose(); } } catch { /* ignore */ } finally { _mcReconnectCts = null; _mcReconnectTask = null; } } private void McReconnectLoop(CancellationToken token) { int attempt = 0; while (!token.IsCancellationRequested) { try { var mcPlc = GetMcPlcInfo(); if (mcPlc == null) { Thread.Sleep(1000); continue; } var mcWrapper = PlcService?.GetPlc(mcPlc.Id) as MitsubishiMcPLC; var client = mcWrapper?.Client; if (client == null) { Thread.Sleep(1000); continue; } if (client.IsConnected) { if (!_mcMonitorLoopsStarted) EnsureMcCommunicateLoopsRunning(mcPlc, client); attempt = 0; Thread.Sleep(500); continue; } if ((DateTime.UtcNow - _mcLastReconnectAttempt).TotalMilliseconds < McReconnectIntervalMs) { Thread.Sleep(500); continue; } _mcLastReconnectAttempt = DateTime.UtcNow; attempt++; SendTaskMessage($"MC PLC 连接断开,正在重连(第 {attempt} 次)...", MessageLevel.Info); bool connected; lock (_mcReconnectSync) { if (client.IsConnected) { connected = true; } else { mcWrapper.Connect(); connected = client.IsConnected; } } if (connected) { EnsureMcCommunicateLoopsRunning(mcPlc, client); SendTaskMessage("MC PLC 重连成功", MessageLevel.Info); attempt = 0; } else { SendTaskMessage("MC PLC 重连失败,将继续重试", MessageLevel.Alarm); } } catch (Exception ex) { if (token.IsCancellationRequested) break; LogHelper.WriteLogError("MC 重连循环异常", ex); Thread.Sleep(McReconnectIntervalMs); } } } private void StartHeartbeatLoop(PlcInfo mcPlc) { StopHeartbeatLoop(); if (mcPlc == null) return; string device = mcPlc.PlcHeartbeatDevice; if (!MitsubishiPLC.IsDeviceEnabled(device)) return; PlcHeartbeatKind kind; try { kind = MitsubishiPLC.ResolveHeartbeatKind(device); } catch (Exception ex) { SendTaskMessage("PLC 心跳地址无效", MessageLevel.Alarm); LogHelper.WriteLogError("PLC 心跳地址无效", ex); return; } if (kind == PlcHeartbeatKind.Disabled) return; int intervalMs = Math.Max(200, mcPlc.PlcHeartbeatIntervalMs); _heartbeatCts = new CancellationTokenSource(); var token = _heartbeatCts.Token; _heartbeatTask = Task.Run(() => HeartbeatLoop(token, device.Trim(), intervalMs), token); SendTaskMessage("PLC 已启动心跳 " + device.Trim() + (kind == PlcHeartbeatKind.Bit ? "(位翻转)" : "(字递增)") + " 间隔=" + intervalMs + "ms", MessageLevel.Info); } private void StopHeartbeatLoop() { try { if (_heartbeatCts != null) { _heartbeatCts.Cancel(); try { if (_heartbeatTask != null) _heartbeatTask.Wait(2000); } catch (AggregateException) { /* ignore */ } _heartbeatCts.Dispose(); } } catch { /* ignore */ } finally { _heartbeatCts = null; _heartbeatTask = null; } } private void HeartbeatLoop(CancellationToken token, string device, int intervalMs) { while (!token.IsCancellationRequested) { try { MitsubishiPLC plc; lock (_sync) plc = GetMcPlcClient(); if (plc == null || !plc.IsConnected) { Thread.Sleep(100); continue; } plc.PulseHeartbeat(device); } catch (Exception ex) { if (!token.IsCancellationRequested) { SendTaskMessage("心跳异常: " + ex.Message, MessageLevel.Alarm); LogHelper.WriteLogError("心跳异常", ex); } } Thread.Sleep(intervalMs); } } private void StartHandshakeLoop() { StopHandshakeLoop(); _handshakeCts = new CancellationTokenSource(); var token = _handshakeCts.Token; _handshakeTask = Task.Run(() => HandshakeLoop(token), token); SendTaskMessage("已启动 Ready 握手循环", MessageLevel.Info); } private void HandshakeLoop(CancellationToken token) { bool lastReady = false; while (!token.IsCancellationRequested) { try { MitsubishiPLC plc; SIG350RFIDClient rfid; lock (_sync) { plc = GetMcPlcClient(); rfid = BgEipCommunicate; } if (plc == null || !plc.IsConnected || rfid == null || !rfid.IsConnected || !rfid.IsIOModeActive) { Thread.Sleep(50); continue; } PlcRfidMapping mapping = SnapshotMapping(); if (!MitsubishiPLC.IsDeviceEnabled(mapping.ReadyBitDevice)) { Thread.Sleep(100); continue; } bool ready = plc.ReadReadyBit(mapping); if (ready && !lastReady) { SendTaskMessage("PLC 检测到 Ready=ON,开始读卡", MessageLevel.Info); ProcessReadyRequest(plc, rfid, mapping, token); while (!token.IsCancellationRequested) { if (!plc.IsConnected) break; if (!plc.ReadReadyBit(mapping)) break; Thread.Sleep(20); } if (!token.IsCancellationRequested && plc.IsConnected) { plc.ClearReceiveBit(mapping); SendTaskMessage("PLC Ready=OFF,已清除 Receive", MessageLevel.Info); } lastReady = false; continue; } lastReady = ready; Thread.Sleep(20); } catch (Exception ex) { if (token.IsCancellationRequested) break; SendTaskMessage("PLC 握手异常", MessageLevel.Alarm); LogHelper.WriteLogError("PLC 握手异常", ex); Thread.Sleep(100); lastReady = false; } } } private PlcRfidMapping SnapshotMapping() { lock (_sync) { var mcPlc = GetMcPlcInfo(); return mcPlc?.ToPlcMapping() ?? new PlcRfidMapping(); } } private void ProcessReadyRequest(MitsubishiPLC plc, SIG350RFIDClient rfid, PlcRfidMapping mapping, CancellationToken token) { int port; try { port = plc.ReadRequestPort(mapping); } catch (Exception ex) { SendTaskMessage("PLC 读端口号失败", MessageLevel.Alarm); LogHelper.WriteLogError("PLC 读端口号失败", ex); try { plc.WriteReadFailure(mapping); } catch { /* ignore */ } return; } SendTaskMessage("PLC 请求端口 " + port, MessageLevel.Info); if (port < 1 || port > 8) { SendTaskMessage("端口号无效: " + port, MessageLevel.Alarm); try { plc.WriteReadFailure(mapping); } catch { /* ignore */ } return; } if (token.IsCancellationRequested) return; RFIDTagData tag = null; try { int timeout = mapping.ReadTimeoutMs > 0 ? mapping.ReadTimeoutMs : 2000; var readOpts = DeviceConfig.BgEthernetIPCommunicate.ToRfh5xxReadOptions(); tag = rfid.ReadRFIDTag(port, timeout, readOpts); } catch (Exception ex) { SendTaskMessage("RFID 读卡异常", MessageLevel.Alarm); LogHelper.WriteLogError("RFID 读卡异常", ex); } if (tag != null && tag.Data != null && tag.Data.Length > 0) { lock (_sync) _lastRfidTag = CloneTag(tag); try { plc.WriteReadSuccess(mapping, tag); } catch (Exception ex) { SendTaskMessage("RFID 写 OK/数据失败", MessageLevel.Alarm); LogHelper.WriteLogError("RFID 写 OK/数据失败", ex); } } else { SendTaskMessage("RFID 端口 " + port + " 未读到标签", MessageLevel.Alarm); try { plc.WriteReadFailure(mapping); } catch (Exception ex) { SendTaskMessage("RFID 写 NG 失败", MessageLevel.Alarm); LogHelper.WriteLogError("RFID 写 NG 失败", ex); } } } private bool SnapshotSfisEnabled() { lock (_sync) return DeviceConfig.BgSfisCommunicate?.IsEnabled == true; } private SfisConfig SnapshotSfisConfig() { lock (_sync) return DeviceConfig.BgSfisCommunicate?.ToSfisConfig() ?? new SfisConfig(); } private PlcSfisFeedback SnapshotSfisPlcFeedback() { lock (_sync) return DeviceConfig.BgSfisCommunicate?.ToPlcSfisFeedback() ?? new PlcSfisFeedback(); } private SfisWorkflowService CreateSfisWorkflow() { var workflow = new SfisWorkflowService(SnapshotSfisConfig()); workflow.OnLog += msg => SendTaskMessage("SFIS " + msg, MessageLevel.Info); return workflow; } private SfisWorkflowService GetOrCreateSfisWorkflow() { lock (_sync) { if (_sfisWorkflow != null) return _sfisWorkflow; } var workflow = CreateSfisWorkflow(); lock (_sync) { if (_sfisWorkflow == null) _sfisWorkflow = workflow; else { try { workflow.Dispose(); } catch { /* ignore */ } } return _sfisWorkflow; } } private void ResetSfisWorkflow() { lock (_sync) { if (_sfisWorkflow != null) { try { _sfisWorkflow.Dispose(); } catch { /* ignore */ } _sfisWorkflow = null; } } } private void StartSfisSignalLoop() { StopSfisSignalLoop(); var sfis = DeviceConfig.BgSfisCommunicate; if (sfis == null || !sfis.IsEnabled) return; bool passReady = sfis.PassEnabled && MitsubishiPLC.IsDeviceEnabled(sfis.PlcSfisPassSignalDevice); bool uploadReady = sfis.UploadEnabled && MitsubishiPLC.IsDeviceEnabled(sfis.PlcSfisUploadSignalDevice); if (!passReady && !uploadReady) return; _sfisSignalCts = new CancellationTokenSource(); var token = _sfisSignalCts.Token; _sfisSignalTask = Task.Run(() => SfisSignalLoop(token), token); SendTaskMessage("SFIS 已启动信号监听" + (passReady ? " 过站=" + sfis.PlcSfisPassSignalDevice.Trim() : string.Empty) + (uploadReady ? " 上传=" + sfis.PlcSfisUploadSignalDevice.Trim() : string.Empty), MessageLevel.Info); } private void StopSfisSignalLoop() { try { if (_sfisSignalCts != null) { _sfisSignalCts.Cancel(); try { if (_sfisSignalTask != null) _sfisSignalTask.Wait(2000); } catch (AggregateException) { /* ignore */ } _sfisSignalCts.Dispose(); } } catch { /* ignore */ } finally { _sfisSignalCts = null; _sfisSignalTask = null; } } private void SfisSignalLoop(CancellationToken token) { bool lastPass = false; bool lastUpload = false; while (!token.IsCancellationRequested) { try { if (!SnapshotSfisEnabled()) { lastPass = false; lastUpload = false; Thread.Sleep(200); continue; } MitsubishiPLC plc; lock (_sync) plc = GetMcPlcClient(); if (plc == null || !plc.IsConnected) { Thread.Sleep(100); continue; } var sfis = DeviceConfig.BgSfisCommunicate; bool passEnabled = sfis.PassEnabled; bool uploadEnabled = sfis.UploadEnabled; string passDev = sfis.PlcSfisPassSignalDevice; string uploadDev = sfis.PlcSfisUploadSignalDevice; bool passOn = passEnabled && MitsubishiPLC.IsDeviceEnabled(passDev) && plc.ReadBit(passDev); bool uploadOn = uploadEnabled && MitsubishiPLC.IsDeviceEnabled(uploadDev) && plc.ReadBit(uploadDev); if (passEnabled && passOn && !lastPass) { SendTaskMessage("SFIS 过站信号上升沿 " + passDev.Trim(), MessageLevel.Info); RunSfisOnSignal(plc, runPass: true, runUpload: false); } else if (passEnabled && !passOn && lastPass) { try { plc.ClearSfisReceiveBit(SnapshotSfisPlcFeedback(), isPassStation: true); SendTaskMessage("过站信号 OFF,已清除 Receive", MessageLevel.Info); } catch (Exception ex) { SendTaskMessage("清除过站 Receive 失败: " + ex.Message, MessageLevel.Alarm); } } if (uploadEnabled && uploadOn && !lastUpload) { SendTaskMessage("SFIS 上传信号上升沿 " + uploadDev.Trim(), MessageLevel.Info); RunSfisOnSignal(plc, runPass: false, runUpload: true); } else if (uploadEnabled && !uploadOn && lastUpload) { try { plc.ClearSfisReceiveBit(SnapshotSfisPlcFeedback(), isPassStation: false); SendTaskMessage("上传信号 OFF,已清除 Receive", MessageLevel.Info); } catch (Exception ex) { SendTaskMessage("清除上传 Receive 失败: " + ex.Message, MessageLevel.Alarm); } } lastPass = passOn; lastUpload = uploadOn; Thread.Sleep(50); } catch (Exception ex) { if (token.IsCancellationRequested) break; SendTaskMessage("SFIS 信号监听异常: " + ex.Message, MessageLevel.Alarm); Thread.Sleep(200); lastPass = false; lastUpload = false; } } } private struct SfisRunPlan { public bool RunPass; public bool RunUpload; } private void RunSfisOnSignal(MitsubishiPLC plc, bool runPass, bool runUpload) { bool isPassStation = runPass; PlcSfisFeedback feedback = SnapshotSfisPlcFeedback(); bool success = false; try { RFIDTagData tag = TryLoadTagForSfis(plc); if (tag == null) { SendTaskMessage("SFIS 无可用 RFID 数据,写 PLC NG", MessageLevel.Alarm); WriteSfisPlcResult(plc, feedback, isPassStation, false); return; } success = TryRunSfisWorkflow(tag, new SfisRunPlan { RunPass = runPass, RunUpload = runUpload }); } catch (Exception ex) { SendTaskMessage("SFIS 流程异常: " + ex.Message, MessageLevel.Alarm); success = false; } WriteSfisPlcResult(plc, feedback, isPassStation, success); } private void WriteSfisPlcResult(MitsubishiPLC plc, PlcSfisFeedback feedback, bool isPassStation, bool success) { try { plc.WriteSfisResult(feedback, isPassStation, success); } catch (Exception ex) { SendTaskMessage((isPassStation ? "过站" : "上传") + " 回写失败: " + ex.Message, MessageLevel.Alarm); } } private RFIDTagData TryLoadTagForSfis(MitsubishiPLC plc) { PlcRfidMapping mapping = SnapshotMapping(); var readOpts = DeviceConfig.BgEthernetIPCommunicate.ToRfh5xxReadOptions(); if (MitsubishiPLC.IsDeviceEnabled(mapping.DataStartDevice)) { try { byte[] data = plc.ReadDeviceBytes(mapping.DataStartDevice, mapping.MaxDataBytes); data = TrimTrailingZeros(data); if (data.Length > 0) { int port = 0; if (MitsubishiPLC.IsDeviceEnabled(mapping.PortDevice)) port = plc.ReadRequestPort(mapping); return new RFIDTagData { Port = port, Data = data, Timestamp = DateTime.Now, PayloadKind = readOpts.Kind }; } } catch (Exception ex) { SendTaskMessage("SFIS 从 PLC 读取 RFID 数据失败: " + ex.Message, MessageLevel.Alarm); } } lock (_sync) { if (_lastRfidTag != null && _lastRfidTag.Data != null && _lastRfidTag.Data.Length > 0) return CloneTag(_lastRfidTag); } return null; } private static RFIDTagData CloneTag(RFIDTagData tag) { return new RFIDTagData { Port = tag.Port, Data = (byte[])tag.Data.Clone(), Timestamp = tag.Timestamp, Quality = tag.Quality, PayloadKind = tag.PayloadKind }; } private static byte[] TrimTrailingZeros(byte[] data) { if (data == null || data.Length == 0) return new byte[0]; int len = data.Length; while (len > 0 && data[len - 1] == 0) len--; if (len == data.Length) return data; var trimmed = new byte[len]; Array.Copy(data, trimmed, len); return trimmed; } private bool TryRunSfisWorkflow(RFIDTagData tag, SfisRunPlan plan) { try { var config = SnapshotSfisConfig(); var context = SfisRfidMapper.FromRfidTag(tag, config); var result = GetOrCreateSfisWorkflow().ProcessUnit(context, plan.RunPass, plan.RunUpload); PrintSfisResult(result); return result.Success; } catch (ArgumentException ex) { SendTaskMessage("SFIS 数据无效: " + ex.Message, MessageLevel.Alarm); return false; } catch (Exception ex) { SendTaskMessage("SFIS 流程异常: " + ex.Message, MessageLevel.Alarm); return false; } } private void PrintSfisResult(SfisWorkflowResult result) { if (result == null) return; SendTaskMessage(result.Success ? "SFIS 流程成功 ISN=" + result.Isn : "SFIS 流程失败 ISN=" + result.Isn + " => " + result.ErrorMessage, MessageLevel.Info); if (result.Steps == null) return; foreach (var step in result.Steps) { string msg = step.Response != null ? step.Response.Message : string.Empty; int code = step.Response != null ? step.Response.ReturnCode : -1; SendTaskMessage("SFIS " + step.StepName + ": P_RET=" + code + " " + msg, MessageLevel.Debug); } } private void BgEipCommunicate_OnLog(string obj) { SendTaskMessage(obj, MessageLevel.Info); } /// /// 初始化后台脚本 /// public void InitBackgroundcript() { try { if (!Directory.Exists(FilePath.BackgroundScriptPath)) { Directory.CreateDirectory(FilePath.BackgroundScriptPath); } try { string[] files = Directory.GetFiles(FilePath.BackgroundScriptPath); List ScriptFilesCollection = new List(); foreach (string file in files) { string fileName = Path.GetFileName(file); string fileExtension = Path.GetExtension(file); if (fileExtension == ".cs") { ScriptFilesCollection.Add(file); } } foreach (var item in ScriptFilesCollection) { try { string script = FileHelper.ReadFile(item); SendTaskMessage($"{Lang.执行脚本}:{item}", MessageLevel.Info); Task.Factory.StartNew(() => { ScriptHelper.ExecuteScriptingAsync(script, _regionManager, _eventAggregator, _container, rst => { if (!rst.Item1) { foreach (var err in rst.Item2) { SendTaskMessage($"{Lang.后台脚本出错}:{err}", MessageLevel.Error); } } }); }); } catch (Exception ex) { LogHelper.WriteLogError($"开启脚本时出错:{item}", ex); } } } catch (Exception ex) { LogHelper.WriteLogError("读取本地脚本文件列表时出错", ex); } } catch (Exception ex) { LogHelper.WriteLogError("开启后台脚本时出错!", ex); } } #endregion #region 产品操作 /// /// 加载所有产品 /// public void LoadAllProducts() { Products = new ObservableCollection(); try { if (File.Exists(FilePath.ProductsPath + "//ProductList.cfg")) { Dictionary list = FileHelper.ReadJsonFile>(FilePath.ProductsPath + "//ProductList.cfg"); foreach (var item in list) { Products.Add(FileHelper.ReadJsonFile(FilePath.ProductsPath + "//" + item.Value + "//" + item.Value + ".cfg")); //for (int i = 0; i < Products[Products.Count - 1].CameraProcedures.Count; i++) //{ // for (int j = 0; j < Products[Products.Count - 1].CameraProcedures[i].ProcedureModels.Count; j++) // { // string prcPath = FilePath.ProductsPath + "//" + item.Value + "//" + Products[Products.Count - 1].CameraProcedures[i].Camera + "//" + Products[Products.Count - 1].CameraProcedures[i].ProcedureModels[j].Name + ".prc"; // Products[Products.Count - 1].CameraProcedures[i].ProcedureModels[j].Procedure = VmProcedure.Load(prcPath); // } //} } SendTaskMessage($"{Lang.所有产品加载完成}!", MessageLevel.Debug); } else { SendTaskMessage($"{Lang.无产品}!", MessageLevel.Alarm); } } catch (Exception ex) { SendTaskMessage(ex.Message, MessageLevel.Error); LogHelper.WriteLogError("加载所有产品时出错!", ex); } } /// /// 加载产品 /// public void LoadProducts(ProductModel product) { var prd = Products.FirstOrDefault(x => x.ID == product.ID); if (prd == null) { return; } try { //清除上一个产品的相机拍照数据 feederCurentBlobArea = new Dictionary>(); LastPointsForFeeder = new Dictionary>>(); PointCollection = new Dictionary>>(); for (int i = 0; i < prd.CameraProcedures.Count; i++) { for (int j = 0; j < prd.CameraProcedures[i].ProcedureModels.Count; j++) { string prcPath = FilePath.ProductsPath + "//" + prd.Name + "//" + prd.CameraProcedures[i].Camera + "//" + prd.CameraProcedures[i].ProcedureModels[j].Name + ".vpp"; prd.CameraProcedures[i].ProcedureModels[j].ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock; } } //设置Feeder的光源亮度及Feeder的振动参数 prd.IsLoad = true; foreach (var item in Products) { if (item.ID != prd.ID) { item.IsLoad = false; } } CurrentProduct = prd; Dictionary> keyValuePairs = new Dictionary>(); Dictionary> keyValuePairsT = new Dictionary>(); foreach (var item in prd.CameraProcedures) { foreach (var itemT in item.ProcedureModels) { IRobot robot = RobotService.GetRobot(itemT.RobotId); if (robot != null && robot.Brand == RobotBrand.XYZ_Platform) { if (keyValuePairs.ContainsKey(itemT.Id)) { keyValuePairs[itemT.Id].Add(itemT.TriggerCommand); } else { keyValuePairs.Add(itemT.Id, new List { itemT.TriggerCommand }); } if (!keyValuePairsT.ContainsKey(itemT.Id)) { keyValuePairsT.Add(itemT.Id, itemT.ProcedureOutputsOpcAdrs); } } } } Guid[] Threadkeys = PlcTrigger.Keys.ToArray(); Thread[] ThreadValues = PlcTrigger.Values.ToArray(); for (int i = 0; i < PlcTrigger.Count; i++) { var procedure = prd.CameraProcedures.FirstOrDefault(p => p.ProcedureModels?.FirstOrDefault(f => f.Id == Threadkeys[i]) != null); Guid guid = procedure.ProcedureModels.FirstOrDefault(f => f.Id == Threadkeys[i]).RobotId; XYZ_Platform robot = RobotService.GetRobot(guid) as XYZ_Platform; robot.IsTrigger = false; ThreadValues[i].Abort(); } PlcTrigger.Clear(); Guid[] keys = keyValuePairs.Keys.ToArray(); List[] values = keyValuePairs.Values.ToArray(); for (int i = 0; i < keyValuePairs?.Count; i++) { var procedure = prd.CameraProcedures.FirstOrDefault(p => p.ProcedureModels?.FirstOrDefault(f => f.Id == keys[i]) != null); Guid guid = procedure.ProcedureModels.FirstOrDefault(f => f.Id == keys[i]).RobotId; XYZ_Platform robot = RobotService.GetRobot(guid) as XYZ_Platform; robot.IsTrigger = false; if (keyValuePairsT.ContainsKey(keys[i])) { List ProcedureOutputOpcAdrs = new List(); var saas = keyValuePairsT[keys[i]]; for (int j = 0; j < keyValuePairsT[keys[i]]?.Count; j++) { ProcedureOutputOpcAdrs.Add(keyValuePairsT[keys[i]][j]); } robot.IsTrigger = true; Thread thread = robot.CreateThreadToTrigger(values[i].ToArray(), ProcedureOutputOpcAdrs); PlcTrigger.Add(keys[i], thread); } } DatabaseHelper.UpdateProductDatabase(product); SendTaskMessage(Lang.产品加载完成.Replace("{0}", CurrentProduct.Name), MessageLevel.Debug); App.Current.Dispatcher.Invoke(() => { _eventAggregator.GetEvent().Publish(CurrentProduct); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.产品加载完成.Replace("{0}", CurrentProduct.Name), Duration = 1 }); }); } catch (Exception ex) { SendTaskMessage(ex.Message, MessageLevel.Error); LogHelper.WriteLogError($"加载产品[{prd.Name}]时出错!", ex); } } /// /// 保存产品 /// /// public void SaveProducts(ProductModel product) { try { product.DateTime = DateTime.Now; var prod = Products.FirstOrDefault(item => item.ID == product.ID); if (prod != null) { prod = product; } else { product.IsCreate = false; Products.Add(product); } Dictionary list = new Dictionary(); foreach (var item in Products) { list.Add(item.ID, item.Name); } FileHelper.WriteJsonFile(list, FilePath.ProductsPath + "//ProductList.cfg"); FileHelper.WriteJsonFile(product, FilePath.ProductsPath + "//" + product.Name + "//" + product.Name + ".cfg"); foreach (var item in product.CameraProcedures) { foreach (var item1 in item.ProcedureModels) { string prcPath = FilePath.ProductsPath + "//" + product.Name + "//" + item.Camera + "//" + item1.Name + ".vpp"; if (!Directory.Exists(Path.GetDirectoryName(prcPath))) { Directory.CreateDirectory(Path.GetDirectoryName(prcPath)); } if (item1.ToolBlock != null) { CogSerializer.SaveObjectToFile(item1.ToolBlock, prcPath); //item1.ToolBlock= CogSerializer.LoadObjectFromFile(FilePath.SimpleProcedurePath) as CogToolBlock; } else { if (File.Exists(prcPath)) { item1.ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock; } else { item1.ToolBlock = CogSerializer.LoadObjectFromFile(FilePath.SimpleProcedurePath) as CogToolBlock; } } } } DatabaseHelper.UpdateProductDatabase(product); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 }); } catch (Exception ex) { SendTaskMessage(ex.Message, MessageLevel.Error); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 }); LogHelper.WriteLogError("保存产品时出错!", ex); } } /// /// 保存产品 /// public void SaveProducts() { Dictionary list = new Dictionary(); foreach (var item in Products) { list.Add(item.ID, item.Name); } FileHelper.WriteJsonFile(list, FilePath.ProductsPath + "//ProductList.cfg"); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 }); } /// /// 保存产品模板 /// /// public void SaveProductTemplate(ProductModel product) { try { product.DateTime = DateTime.Now; var prod = Products.FirstOrDefault(item => item.ID == product.ID); if (prod != null) { prod = product; } else { product.IsCreate = false; } FileHelper.WriteJsonFile(product, FilePath.ProductTemplatePath + "//" + product.Name + "//" + product.Name + ".cfg"); foreach (var item in product.CameraProcedures) { foreach (var item1 in item.ProcedureModels) { string prcPath = FilePath.ProductTemplatePath + "//" + product.Name + "//" + item.Camera + "//" + item1.Name + ".vpp"; if (!Directory.Exists(Path.GetDirectoryName(prcPath))) { Directory.CreateDirectory(Path.GetDirectoryName(prcPath)); } if (item1.ToolBlock != null) { CogSerializer.SaveObjectToFile(item1.ToolBlock, prcPath); //item1.ToolBlock= CogSerializer.LoadObjectFromFile(FilePath.SimpleProcedurePath) as CogToolBlock; } else { if (File.Exists(prcPath)) { item1.ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock; } else { item1.ToolBlock = CogSerializer.LoadObjectFromFile(FilePath.SimpleProcedurePath) as CogToolBlock; } } } } DatabaseHelper.UpdateProductDatabase(product); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 }); } catch (Exception ex) { SendTaskMessage(ex.Message, MessageLevel.Error); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 }); LogHelper.WriteLogError("保存产品模板时出错!", ex); } } /// /// 复制产品 /// /// /// public ProductModel CopyProduct(ProductModel product, string newName) { var p = product.Clone(); p.Name = newName; p.ID = Guid.NewGuid(); p.DateTime = DateTime.Now; for (int i = 0; i < p.CameraProcedures.Count; i++) { for (int j = 0; j < p.CameraProcedures[i].ProcedureModels.Count; j++) { string sorPath = FilePath.ProductsPath + "//" + ((ProductModel)product).Name + "//" + p.CameraProcedures[i].Camera + "//" + ((ProductModel)product).CameraProcedures[i].ProcedureModels[j].Name + ".vpp"; string procname = p.CameraProcedures[i].ProcedureModels[j].Name.Split('-')[2]; procname = $"{p.Name}-{p.CameraProcedures[i].Camera}-{procname}"; p.CameraProcedures[i].ProcedureModels[j].Name = procname; string prcPath = FilePath.ProductsPath + "//" + p.Name + "//" + p.CameraProcedures[i].Camera + "//" + p.CameraProcedures[i].ProcedureModels[j].Name + ".vpp"; if (!Directory.Exists(Path.GetDirectoryName(prcPath))) { Directory.CreateDirectory(Path.GetDirectoryName(prcPath)); } if (File.Exists(sorPath)) { File.Copy(sorPath, prcPath, true); if (p.CameraProcedures[i].ProcedureModels[j].ToolBlock == null) { p.CameraProcedures[i].ProcedureModels[j].ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock; } } } } Products.Add(p); SaveProducts(p); return p; } /// /// 重新命名产品 /// /// /// public void RenameProduct(ProductModel product, string newName) { string oldname = product.Name; //更改产品名称 product.Name = newName; //遍历所有的相机流程,更改相机流程的名称 for (int i = 0; i < product.CameraProcedures.Count; i++) { for (int j = 0; j < product.CameraProcedures[i].ProcedureModels.Count; j++) { //旧路径 string sorPath = FilePath.ProductsPath + "//" + oldname + "//" + product.CameraProcedures[0].Camera + "//" + product.CameraProcedures[i].ProcedureModels[j].Name + ".vpp"; string procname = product.CameraProcedures[i].ProcedureModels[j].Name.Split('-')[2]; procname = $"{product.Name}-{product.CameraProcedures[i].Camera}-{procname}"; product.CameraProcedures[i].ProcedureModels[j].Name = procname; //新路径 string prcPath = FilePath.ProductsPath + "//" + product.Name + "//" + product.CameraProcedures[0].Camera + "//" + product.CameraProcedures[i].ProcedureModels[j].Name + ".vpp"; if (!Directory.Exists(Path.GetDirectoryName(prcPath))) { Directory.CreateDirectory(Path.GetDirectoryName(prcPath)); } if (File.Exists(sorPath)) { File.Copy(sorPath, prcPath, true); File.Delete(sorPath); } } string prcPath1 = FilePath.ProductsPath + "//" + oldname + "//" + product.CameraProcedures[0].Camera; if (Directory.Exists(prcPath1)) { Directory.Delete(prcPath1); } } //更改产品数据库 string src = FilePath.ProductsPath + "//" + oldname + "//" + oldname + ".db"; string dsc = FilePath.ProductsPath + "//" + product.Name + "//" + product.Name + ".db"; if (File.Exists(src)) { File.Copy(src, dsc, true); File.Delete(src); } //更改数据库中有关的产品名称 DatabaseHelper.RevampProductName(product, oldname); SaveProducts(product); //删除旧产品路径 string sorPath1 = FilePath.ProductsPath + "//" + oldname + "//" + oldname + ".cfg"; if (File.Exists(sorPath1)) { File.Delete(sorPath1); } sorPath1 = FilePath.ProductsPath + "//" + oldname; if (Directory.Exists(sorPath1)) { Directory.Delete(sorPath1); } } /// /// 移除产品 /// /// public void RemoveProduct(ProductModel product) { var prod = Products.FirstOrDefault(p => p.ID == product.ID); if (prod != null) { Products.Remove(prod); SaveProducts(); } } #endregion #region 相机校准操作 /// /// 加载所有校准 /// public void LoadAllCalibration() { try { if (File.Exists(FilePath.CalibrationPath + "//CalibrationList.cfg")) { DeviceConfig.Calibrations = new ObservableCollection(); Dictionary list = FileHelper.ReadJsonFile>(FilePath.CalibrationPath + "//CalibrationList.cfg"); foreach (var item in list) { DeviceConfig.Calibrations.Add(FileHelper.ReadJsonFile(FilePath.CalibrationPath + "//" + item.Value + "//" + item.Value + ".cfg")); } SendTaskMessage(Lang.校准加载完成, MessageLevel.Debug); } else { SendTaskMessage(Lang.无校准, MessageLevel.Alarm); } } catch (Exception ex) { DeviceConfig.Calibrations = new ObservableCollection(); LogHelper.WriteLogError("加载校准配置文件时出错!", ex); } } /// /// 保存校准 /// /// public void SaveCalibration(CalibrationInfo calibration) { try { calibration.DateTime = DateTime.Now; var calib = DeviceConfig.Calibrations.FirstOrDefault(item => item.Id == calibration.Id); if (calib != null) { calib = calibration; } else { calibration.IsCreate = false; DeviceConfig.Calibrations.Add(calibration); } Dictionary list = new Dictionary(); foreach (var item in DeviceConfig.Calibrations) { list.Add(item.Id, item.Name); } FileHelper.WriteJsonFile(list, FilePath.CalibrationPath + "//CalibrationList.cfg"); FileHelper.WriteJsonFile(calibration, FilePath.CalibrationPath + "//" + calibration.Name + "//" + calibration.Name + ".cfg"); if (calibration.ToolBlock != null) { string prcPath = FilePath.CalibrationPath + "//" + calibration.Name + "//" + calibration.Name + ".vpp"; if (!Directory.Exists(Path.GetDirectoryName(prcPath))) { Directory.CreateDirectory(Path.GetDirectoryName(prcPath)); } CogSerializer.SaveObjectToFile(calibration.ToolBlock, prcPath); } _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 }); } catch (Exception ex) { SendTaskMessage(ex.Message, MessageLevel.Error); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 }); LogHelper.WriteLogError("保存校准时出错!", ex); } } /// /// 移除校准 /// /// public void RemoveCalibration(CalibrationInfo calibration) { var calib = DeviceConfig.Calibrations.FirstOrDefault(p => p.Id == calibration.Id); if (calib != null) { DeviceConfig.Calibrations.Remove(calib); Dictionary list = new Dictionary(); foreach (var item in DeviceConfig.Calibrations) { list.Add(item.Id, item.Name); } FileHelper.WriteJsonFile(list, FilePath.CalibrationPath + "//CalibrationList.cfg"); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.保存完成, Duration = 1 }); } } #endregion #region Feeder清料动作 /// /// 加载Feeder清料动作 /// public Dictionary> LoadFeedersClearTask() { Dictionary> FeedersClearWork = new Dictionary>(); try { if (File.Exists(FilePath.FeedersClearTaskPath)) { FeedersClearWork = FileHelper.ReadJsonFile>>(FilePath.FeedersClearTaskPath); } return FeedersClearWork; } catch (Exception ex) { LogHelper.WriteLogError("加载Feeder清料动作配置文件时出错!", ex); return FeedersClearWork; } } /// /// 保存Feeder清料动作 /// public void SaveFeedersClearTask(Dictionary> FeedersClearWork) { FileHelper.WriteJsonFile(FeedersClearWork, FilePath.FeedersClearTaskPath); } /// /// 执行Feeder清料 /// /// /// /// public async Task ExecuteFeederClear(Guid FeederId, FeederClearWork feederClearWork, CancellationToken cancellationToken) { IVoiceCoilMotorFeeder Feeder = FeederService.GetFeeder(FeederId); if (!Feeder.IsConnected) { return; } for (int i = 0; i < feederClearWork.ClearCount; i++) { //1.Feeder振动之前,打开挡板 await Feeder.OutputAsync((int)feederClearWork.BeforeOutput + 1, 0); await Task.Delay(200); //2.执行配置的Feeder振动任务 for (int j = 0; j < feederClearWork.VibrationCount; j++) { await ExecuteFeederWorks(Feeder, feederClearWork.FeederWorks, true); //如果料仓停止了,则打开料仓运动 if (!await Feeder.GetOutputStatusAsync()) { await Feeder.OutputAsync((int)feederClearWork.BeforeOutput + 1, 0); } if (cancellationToken != null && cancellationToken.IsCancellationRequested) { await Feeder.StopOutputAsync(); return; } } if (cancellationToken != null && cancellationToken.IsCancellationRequested) { await Feeder.StopOutputAsync(); return; } //3.Feeder振动之后,关闭挡板 await Feeder.StopOutputAsync(); await Task.Delay(300); } //3.Feeder振动之后,关闭挡板 await Feeder.StopOutputAsync(); await Task.Delay(300); } #endregion #region 机器人指令执行 public Dictionary RobotTCPCollection = new Dictionary(); /// /// 机器人TCP接收 /// /// /// /// private async void Management_ReceivedEvent(Guid robotid, object arg2, TouchSocket.Sockets.ReceivedDataEventArgs e) { try { if (!RobotTCPCollection.ContainsKey(robotid)) RobotTCPCollection.Add(robotid, new CancellationTokenSource()); else { if (RobotTCPCollection[robotid].IsCancellationRequested) { RobotTCPCollection[robotid] = new CancellationTokenSource(); } } var robot = await RobotService.GetRobotAsync(robotid); var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == robotid); //从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。 var buffer = e.ByteBlock.Span.ToString(robot.GetEncoding()); SendTaskMessage($"{r.RobotName}Received:{buffer}", MessageLevel.Info); var _ = Task.Run(async () => { try { await ExecuteRemoteCommand(buffer, async result => { if (result.Item1) { try { if (result.Item2.Length > 1) { foreach (var item in result.Item2) { if (robot.ConnectType == TCPConnectType.Client) { if (robot.IsConnected) { await robot.SendAsync($"{item}"); } } else { var cient = (ITcpSessionClient)arg2; if (cient.Online) { await robot.SendAsync(cient, $"{item}"); } } await Task.Delay(10); } } else { if (robot.ConnectType == TCPConnectType.Client) { if (robot.IsConnected) { await robot.SendAsync($"{result.Item2[0]}"); } } else { var cient = (ITcpSessionClient)arg2; if (cient.Online) { await robot.SendAsync(cient, $"{result.Item2[0]}"); } } } } catch (Exception ex) { LogHelper.WriteLogError("TCP发送数据时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } } }, RobotTCPCollection[robotid].Token); } catch (Exception ex) { LogHelper.WriteLogError("处理机器人接收的命令时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } }); } catch (Exception ex) { LogHelper.WriteLogError("处理机器人接收的命令时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } } /// /// 机器人XY模组执行命令 /// /// /// /// private async void Management_TrggerChangeEvent(Guid arg1, object arg2, List arg3) { if (!RobotTCPCollection.ContainsKey(arg1)) RobotTCPCollection.Add(arg1, new CancellationTokenSource()); else { if (RobotTCPCollection[arg1].IsCancellationRequested) { RobotTCPCollection[arg1] = new CancellationTokenSource(); } } var robot = await RobotService.GetRobotAsync(arg1) as XYZ_Platform; var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == arg1); SendTaskMessage($"{r.RobotName}Received:{arg2}", MessageLevel.Info); var _ = Task.Run(async () => { try { await ExecuteRemoteCommand(arg2.ToString(), async result => { if (result.Item1) { try { string[] adrs = new string[arg3.Count]; object[] values = new object[arg3.Count]; values[0] = false; values[1] = false; string[] results = result.Item2[0].Split(','); for (int i = 0; i < arg3.Count; i++) { if (i == 0) { if (results[1] == "OK") { adrs[i] = arg3[i].Name; values[i] = true; adrs[i + 1] = arg3[i + 1].Name; values[i + 1] = false; } else { adrs[i] = arg3[i].Name; values[i] = false; adrs[i + 1] = arg3[i + 1].Name; values[i + 1] = true; } i += 1; continue; } if (i < arg3.Count - 1) { adrs[i] = arg3[i].Name; if (i < results.Length) { float res = 0f; if (float.TryParse(results[i], out res)) { values[i] = results[i]; } else { values[i] = res; } } else { values[i] = 0f; } } } adrs[adrs.Length - 1] = arg3[arg3.Count - 1].Name; values[values.Length - 1] = true; robot.Plc.OpcUaClient.WriteNodes(adrs, values); await Task.Delay(10); } catch (Exception ex) { LogHelper.WriteLogError("回复plc发送数据时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } } }, RobotTCPCollection[arg1].Token); } catch (Exception ex) { LogHelper.WriteLogError("处理XYZ机器人接收的命令时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } }); } #region 处理命令 /// /// 执行命令 /// /// public async Task ExecuteRemoteCommand(string command, Action> callback, CancellationToken cancellationToken) { string[] commandItem = command.Split(','); if (CurrentProduct == null) { if (commandItem[0] == "GetProductName") { callback.Invoke(new Tuple(true, new string[] { $"{commandItem[0]},null" })); return; } } ProcedureModel procedure = null; foreach (var cameraProcedures in CurrentProduct.CameraProcedures) { procedure = cameraProcedures.ProcedureModels.FirstOrDefault(p => p.TriggerCommand == commandItem[0]); if (procedure != null) break; } if (procedure != null) { //相机拍照 await RunProcedure(procedure, commandItem, rst => { if (rst.Item1) { if (rst.Item2.Length > 1) { List buffers = new List(); foreach (var item in rst.Item2) { buffers.Add($"{commandItem[0]},{item}"); } callback.Invoke(new Tuple(rst.Item1, buffers.ToArray())); } else { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2[0]}" })); } } else { callback.Invoke(rst); } }, cancellationToken); return; } switch (commandItem[0]) { case "LoadProduct": await LoadProduct(commandItem[1], rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetProductName": await GetCurrentProductName(rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetProductID": await GetCurrentProductID(rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetSpeed": await GetCurrentProductSpeed(rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetAllRobotPoints": await GetAllRobotPoints(rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetRobotPointsCount": await GetRobotPointsCount(rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetRobotPoint": await GetRobotPoint(int.Parse(commandItem[1]), rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetPalletsCount": await GetPalletsCount(rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetAllPallets": await GetAllPallet(rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetPallet": await GetPallet(commandItem[1], rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetPalletPoint": await GetPalletPoint(commandItem[1], int.Parse(commandItem[2]), rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetToolCount": await GetToolCount(commandItem[1], rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetTool": await GetTool(commandItem[1], int.Parse(commandItem[2]), rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "GetProcedureParam": await GetProcedureParam(commandItem[1], rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "CycleTime": ExecuteCycleTime(commandItem[1], rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "Record": AddProductionRecords(commandItem[1], rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); break; case "Err": SendTaskMessage(commandItem[1], MessageLevel.Error); DatabaseHelper.AppendAlarm(commandItem[1]); break; case "Info": SendTaskMessage(commandItem[1], MessageLevel.Info); break; case "GetGlobalParam": if (commandItem.Length == 2) { GetGlobalParam(commandItem[1], -1, rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); } else if (commandItem.Length == 3) { GetGlobalParam(commandItem[1], int.Parse(commandItem[2]), rst => { callback.Invoke(new Tuple(rst.Item1, new string[] { $"{commandItem[0]},{rst.Item2}" })); }); } break; } } /// /// 运行流程 /// /// 流程对象 /// 触发命令 /// 回调函数 /// 取消任务 /// public async Task RunProcedure(ProcedureModel procedure, string[] items, Action> callback, CancellationToken cancellationToken) { string path = FilePath.ProductsPath + "//" + CurrentProduct.Name + "//" + procedure.CameraName + "//" + procedure.Name + ".cs"; string script = ""; if (File.Exists(path)) { try { script = FileHelper.ReadFile(path); SendTaskMessage(Lang.执行视觉处理脚本, MessageLevel.Debug); var rst1 = await ScriptHelper.ExecuteVisionScriptingAsync(script, _regionManager, _eventAggregator, _container, procedure, items, rst => { callback.Invoke(rst); }); if (!rst1.Item1) { SendTaskMessage(Lang.视觉处理脚本出错, MessageLevel.Error); callback.Invoke(new Tuple(false, new string[] { "ScriptError" })); foreach (var item in rst1.Item2) { SendTaskMessage(item, MessageLevel.Error); } } } catch (Exception ex) { SendTaskMessage(ex.Message, MessageLevel.Error); LogHelper.WriteLogError("执行视觉处理脚本时出错!", ex); callback.Invoke(new Tuple(false, new string[] { "ScriptError" })); } } else { //正常执行模块,每次拍照,会返回所有的结果数据 IVoiceCoilMotorFeeder Feeder = null; if (procedure.FeederId != Guid.Empty) { Feeder = this.FeederService.GetFeeder(procedure.FeederId); await RunProcedureFeeder(Feeder, procedure, items, rst => { callback.Invoke(rst); }, cancellationToken); } else { RunProcedureOther(procedure, items, rst => { callback.Invoke(rst); }, cancellationToken); } } } /// /// Feeder当前Blob的面积,产品-流程-面积 /// public Dictionary> feederCurentBlobArea = new Dictionary>(); /// /// 最后一次feeder拍照出来的点位,流程-点数据 /// public Dictionary>> LastPointsForFeeder = new Dictionary>>(); /// /// 每次拍照得到的点位数据集合,每个流程中包含多个结果输出项,每个结果项中包含多个点位,流程-输出集合-点位队列 /// public Dictionary>> PointCollection = new Dictionary>>(); /// /// Feeder相机引导模式 /// /// feeder /// 流程对象 /// 触发命令 /// 回调函数 /// public async Task RunProcedureFeeder(IVoiceCoilMotorFeeder Feeder, ProcedureModel procedure, string[] items, Action> callback, CancellationToken cancellationToken) { //是否检测上次的点位,相同点位排除 bool IsCheckLastPoints = false; bool IsSingleResultModel = false; int InputVibration = 0; int GetPosCount = 1; Dictionary InputTerminal = null; //触发命令传入的输入终端 if (items.Length >= 2 && string.Equals(items[1], "GetPos")) { //Camera1,GetPos,PosCount,CheckLastPoints if (items.Length >= 4 && string.Equals(items[3], "CheckLastPoints")) { IsCheckLastPoints = true; GetPosCount = int.Parse(items[2]); } ////Camera1,GetPos,CheckLastPoints else if (items.Length >= 3 && string.Equals(items[2], "CheckLastPoints")) { IsCheckLastPoints = true; GetPosCount = 1; } else if (items.Length >= 3 && int.TryParse(items[2], out GetPosCount)) { } else { GetPosCount = 1; } if (GetPosCount <= 0) { GetPosCount = 1; } //进入一次拍照后,,点位数据保存在软件里面,机器人每次跟软件获取一个或者两个点位 ////获取点位数据 if (!PointCollection.ContainsKey(procedure.Id)) { PointCollection.Add(procedure.Id, new Dictionary>()); } IsSingleResultModel = true; } else if (items.Length >= 2 && string.Equals(items[1], "ClearPos")) { //进入一次拍照后,,点位数据保存在软件里面,机器人每次跟软件获取一个或者两个点位 ////清除所有点位数据 if (!PointCollection.ContainsKey(procedure.Id)) { PointCollection.Add(procedure.Id, new Dictionary>()); } PointCollection[procedure.Id].Clear(); IsSingleResultModel = true; callback.Invoke(new Tuple(true, new string[] { "ClearPos" })); return; } else if (items.Length >= 2 && string.Equals(items[1], "CheckLastPoints")) { IsCheckLastPoints = true; } else if (items.Length >= 2 && string.Equals(items[1], "Vibration")) { //先振后拍 InputVibration = 1; } else if (items.Length >= 2 && string.Equals(items[1], "NoVibration")) { //先拍再决定是否要不要振 InputVibration = 2; } else if (items.Length >= 2 && string.Equals(items[1], "SetVisionToolInputs")) { InputTerminal = new Dictionary(); //触发命令传入的输入终端 for (int i = 2; i < items.Length; i += 2) { if (!string.IsNullOrEmpty(items[i]) && items.Length >= i + 2) { InputTerminal.Add(items[i].Trim(), items[i + 1].Trim()); } } if (InputTerminal.Count <= 0) { InputTerminal = null; } } //如果还没有结果项目,则触发拍照,获取结果 if (IsSingleResultModel && PointCollection[procedure.Id].Count > 0) { //遍历所有的结果项 foreach (var item in PointCollection[procedure.Id]) { //如果结果的数量大于等于可获取的点位数量,则直接返回点位数据 if (item.Value != null && item.Value.Count >= GetPosCount) { List poslist = new List(); for (int i = 0; i < GetPosCount; i++) { poslist.Add(item.Value.Dequeue()); } callback.Invoke(new Tuple(true, new string[] { $"OK,{item.Key},{string.Join(",", poslist)}" })); SendTaskMessage($"{item.Key}{Lang.剩余点位数量}:{item.Value.Count}", MessageLevel.Debug); return; } } } if (IsSingleResultModel) { PointCollection[procedure.Id].Clear(); } await ExecuteFeederForPhotoBefore(Feeder, procedure, InputVibration, cancellationToken); if (cancellationToken != null && cancellationToken.IsCancellationRequested) { await Feeder.CloseLightAsync(); return; } DateTime nowtime = DateTime.Now; //多次拍照 for (int i = 0; i < procedure.FeederFailLimit; i++) { if (cancellationToken != null && cancellationToken.IsCancellationRequested) { await Feeder.CloseLightAsync(); return; } SendTaskMessage(Lang.开始执行视觉流程.Replace("{0}", $"{i + 1}").Replace("{1}", procedure.Name), MessageLevel.Info); string photoresult = ExecutePhoto(procedure, InputTerminal, out CogToolBlockTerminalCollection outputCollection); if (!string.IsNullOrEmpty(photoresult)) { //多次拍照,如果拍照次数到达上限,直接返回结果 if (i == procedure.FeederFailLimit - 1) { callback.Invoke(new Tuple(true, new string[] { $"NG,{photoresult}" })); return; } else { continue; } } var BlobTotalArea = (double)outputCollection["BlobTotalArea"].Value; //记录当前的blob面积 Dictionary tuple; if (feederCurentBlobArea.TryGetValue(CurrentProduct.ID, out tuple)) { //如流程当前已经存在此流程的记录 if (tuple.ContainsKey(procedure.Id)) { tuple[procedure.Id] = BlobTotalArea; } else { tuple.Add(procedure.Id, BlobTotalArea); } } else { feederCurentBlobArea.Add(CurrentProduct.ID, new Dictionary()); feederCurentBlobArea[CurrentProduct.ID].Add(procedure.Id, BlobTotalArea); } if (!outputCollection.Contains("Found")) { callback.Invoke(new Tuple(true, new string[] { "NG,NoOut[Found]" })); return; } bool Found = (bool)(outputCollection["Found"].Value); //拍照OK if (Found) { SendTaskMessage(Lang.拍照OK, MessageLevel.Debug); if (procedure.ProcedureOutputs == null) { SendTaskMessage(Lang.视觉流程未配置输出项, MessageLevel.Alarm); callback.Invoke(new Tuple(true, new string[] { "NG,NotOutputs" })); return; } var ResultCollection = GetVisionOutput(procedure, outputCollection, items); if (!ResultCollection.Item1) { callback.Invoke(new Tuple(true, new string[] { ResultCollection.Item2 })); return; } List SendResults = new List(); //将本次点位结果记录至最后一次 if (!LastPointsForFeeder.ContainsKey(Feeder.Id)) { LastPointsForFeeder.Add(Feeder.Id, new List>()); } int PosNum = 0; bool ishavepoint = false; List> pointBuffer = new List>(); foreach (var result in ResultCollection.Item3) { //是点位数据时 if (result.Value.Item1) { ishavepoint = true; //值不为空时 if (result.Value.Item2 != null) { List points = new List(); foreach (var pos in (List>)(result.Value.Item2)) { pointBuffer.Add(pos); //判断是否有重复的点位,防止机器人重复取同一个物料,陷入死循环 if (IsCheckLastPoints) { if (LastPointsForFeeder.ContainsKey(Feeder.Id)) { var repeated = LastPointsForFeeder[Feeder.Id].FirstOrDefault(p => Math.Abs(p[0] - pos[0]) < 1 && Math.Abs(p[1] - pos[1]) < 1); if (repeated != null) continue; } } if (IsSingleResultModel) { PosNum++; //如果当前是软件点位队列存储模式,则,将所有数据存储在队列中 if (!PointCollection[procedure.Id].ContainsKey(result.Key)) { PointCollection[procedure.Id].Add(result.Key, new Queue()); } PointCollection[procedure.Id][result.Key].Enqueue($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}"); } else { //根据当前点位数量决定是否继续输出点位。 PosNum++; if (PosNum >= procedure.OutputPointCountMax) { points.Add($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}"); break; } else { points.Add($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}"); } } } if (IsSingleResultModel) { //如果当前是软件点位队列存储模式,则,将所有数据存储在队列中 if (!PointCollection[procedure.Id].ContainsKey(result.Key)) { PointCollection[procedure.Id].Add(result.Key, new Queue()); } SendTaskMessage($"{result.Key}{Lang.可用的点位个数}:{PointCollection[procedure.Id][result.Key].Count}", MessageLevel.Debug); } else { if (points.Count == 0) { SendResults.Add($"0"); break; } else { SendResults.Add($"{points.Count},{string.Join(",", points)}"); } } } else { SendResults.Add($"0"); } } //其他输出数据时 else { if (result.Value.Item2 != null) { SendResults.Add((string)result.Value.Item2); } else { SendResults.Add($"null"); } } } //将本次点位结果记录至最后一次 LastPointsForFeeder[Feeder.Id] = pointBuffer; if (ishavepoint && PosNum <= 0) { //存在点位输出项,但是点位数量为0 await ExecuteFeederForPhotoLater(Feeder, procedure, BlobTotalArea, cancellationToken); if (cancellationToken != null && cancellationToken.IsCancellationRequested) { await Feeder.CloseLightAsync(); return; } continue; } if (Feeder != null) { await Feeder.CloseLightAsync(); } if (IsSingleResultModel) { //遍历所有的结果项 foreach (var item in PointCollection[procedure.Id]) { //如果结果的数量大于等于可获取的点位数量,则直接返回点位数据 if (item.Value != null && item.Value.Count >= GetPosCount) { List poslist = new List(); for (int j = 0; j < GetPosCount; j++) { poslist.Add(item.Value.Dequeue()); } callback.Invoke(new Tuple(true, new string[] { $"OK,{item.Key},{string.Join(",", poslist)}" })); SendTaskMessage($"{item.Key}{Lang.剩余点位数量}:{item.Value.Count}", MessageLevel.Debug); return; } } } else { callback.Invoke(new Tuple(true, new string[] { $"OK,{string.Join(";", SendResults)}" })); } return; } //拍照NG else { SendTaskMessage(Lang.拍照NG, MessageLevel.Error); await ExecuteFeederForPhotoLater(Feeder, procedure, BlobTotalArea, cancellationToken); continue; } } SendTaskMessage(Lang.多次拍照失败, MessageLevel.Alarm); callback.Invoke(new Tuple(true, new string[] { "NG,NoFind" })); } /// /// 相机拍照,其他模式 /// /// /// /// public void RunProcedureOther(ProcedureModel procedure, string[] items, Action> callback, CancellationToken cancellationToken) { DateTime nowtime = DateTime.Now; Dictionary InputTerminal = null; //触发命令传入的输入终端 if (items.Length >= 2 && string.Equals(items[1], "SetVisionToolInputs")) { InputTerminal = new Dictionary(); //触发命令传入的输入终端 for (int i = 2; i < items.Length; i += 2) { if (!string.IsNullOrEmpty(items[i]) && items.Length >= i + 2) { InputTerminal.Add(items[i].Trim(), items[i + 1].Trim()); } } if (InputTerminal.Count <= 0) { InputTerminal = null; } } for (int i = 0; i < procedure.FeederFailLimit; i++) { if (cancellationToken != null && cancellationToken.IsCancellationRequested) { return; } SendTaskMessage(Lang.开始执行视觉流程.Replace("{0}", $"{i + 1}").Replace("{1}", procedure.Name), MessageLevel.Info); string photoresult = ExecutePhoto(procedure, InputTerminal, out CogToolBlockTerminalCollection outputCollection); if (!string.IsNullOrEmpty(photoresult)) { //多次拍照,如果拍照次数到达上限,直接返回结果 if (i == procedure.FeederFailLimit - 1) { callback.Invoke(new Tuple(true, new string[] { $"NG,{photoresult}" })); return; } else { continue; } } if (!outputCollection.Contains("Found")) { callback.Invoke(new Tuple(true, new string[] { "NG,NoOut[Found]" })); return; } bool Found = (bool)(outputCollection["Found"].Value); //拍照OK if (Found) { SendTaskMessage(Lang.拍照OK, MessageLevel.Debug); if (procedure.ProcedureOutputs == null) { SendTaskMessage(Lang.视觉流程未配置输出项, MessageLevel.Alarm); callback.Invoke(new Tuple(true, new string[] { "NG,NotOutputs" })); return; } var ResultCollection = GetVisionOutput(procedure, outputCollection, items); if (!ResultCollection.Item1) { callback.Invoke(new Tuple(true, new string[] { ResultCollection.Item2 })); return; } List SendResults = new List(); int PosNum = 0; bool ishavepoint = false; foreach (var result in ResultCollection.Item3) { //是点位数据时 if (result.Value.Item1) { ishavepoint = true; //值不为空时 if (result.Value.Item2 != null) { List points = new List(); foreach (var pos in (List>)(result.Value.Item2)) { //根据当前点位数量决定是否继续输出点位。 PosNum++; if (PosNum >= procedure.OutputPointCountMax) { points.Add($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}"); break; } else { points.Add($"{pos[0]:F3},{pos[1]:F3},{pos[2]:F3}"); } } if (points.Count == 0) { SendResults.Add($"0"); break; } else { SendResults.Add($"{points.Count},{string.Join(",", points)}"); } } else { SendResults.Add($"0"); } } //其他输出数据时 else { if (result.Value.Item2 != null) { SendResults.Add((string)result.Value.Item2); } else { SendResults.Add($"null"); } } } if (ishavepoint && PosNum <= 0) { continue; } callback.Invoke(new Tuple(true, new string[] { $"OK,{string.Join(";", SendResults)}" })); return; } //拍照NG else { SendTaskMessage(Lang.拍照NG, MessageLevel.Error); continue; } } SendTaskMessage(Lang.多次拍照失败, MessageLevel.Alarm); callback.Invoke(new Tuple(true, new string[] { "NG,NoFind" })); } #region 相机拍照 /// /// 执行相机拍照 /// /// /// /// public string ExecutePhoto(ProcedureModel procedure, Dictionary InputTerminal, out CogToolBlockTerminalCollection outputCollection) { try { //采集图像 var camera = CameraService.GetCamera(procedure.CameraId); bool succed = camera.SetExposureTime(procedure.ExposureTime); if (!succed) { SendTaskMessage($"相机曝光设置失败!", MessageLevel.Error); } succed = camera.SetGain(procedure.Gain); if (!succed) { SendTaskMessage($"相机增益设置失败!", MessageLevel.Error); } DateTime nowtime = DateTime.Now; LogHelper.WriteLogInfo("开始采集图像"); var image = camera.Grab(); if (image == null) { SendTaskMessage(Lang.图像采集失败, MessageLevel.Error); outputCollection = null; return "CameraError"; } procedure.ToolBlock.Inputs["InputImage"].Value = image; LogHelper.WriteLogInfo($"采集图像完成;用时:{(DateTime.Now - nowtime).Milliseconds}ms"); if (InputTerminal != null) { LogHelper.WriteLogInfo($"为ToolBlock传入输入终端"); foreach (var item in InputTerminal) { if (procedure.ToolBlock.Inputs.Contains(item.Key)) { LogHelper.WriteLogInfo($"传入[{item.Key}]={item.Value}"); procedure.ToolBlock.Inputs[item.Key].Value = item.Value; } else { LogHelper.WriteLogInfo($"创建并传入[{item.Key}]={item.Value}"); procedure.ToolBlock.Inputs.Add(new CogToolBlockTerminal(item.Key, item.Value)); } } } //运行视觉工具 LogHelper.WriteLogInfo("开始运行视觉工具"); procedure.ToolBlock.Run(); if (procedure.ToolBlock.RunStatus.Result == CogToolResultConstants.Accept) { SendTaskMessage(Lang.视觉流程执行耗时.Replace("{0}", procedure.Name).Replace("{1}", $"{procedure.ToolBlock.RunStatus.ProcessingTime:F1}"), MessageLevel.Info); outputCollection = procedure.ToolBlock.Outputs; ICogRecord record = null; foreach (CogToolBlockTerminal item in outputCollection) { if (item.Value is ICogRecord) record = item.Value as ICogRecord; } _eventAggregator.GetEvent().Publish(new ShowRender() { CameraName = procedure.CameraName, Id = procedure.CameraId, Image = (ICogImage)procedure.ToolBlock.Inputs["InputImage"].Value, Graphic = outputCollection["Graphic"].Value as CogGraphicCollection, Record = record, Result = (bool)(outputCollection["Found"].Value), IsShow = true, IsSaveImage = procedure.IsSaveImage, SaveImageModel = procedure.SaveImageModel, SaveImagePathModel = procedure.SaveImagePathModel, SavePath = procedure.SavePath, IsCompress = procedure.IsCompress, }); DatabaseHelper.AddCameraRecords(CurrentProduct.Name, procedure.Name, outputCollection); return string.Empty; } else { SendTaskMessage(Lang.视觉流程执行出错耗时.Replace("{0}", procedure.Name).Replace("{1}", $"{procedure.ToolBlock.RunStatus.ProcessingTime:F1}"), MessageLevel.Error); SendTaskMessage(procedure.ToolBlock.RunStatus.Message, MessageLevel.Error); outputCollection = null; _eventAggregator.GetEvent().Publish(new ShowRender() { CameraName = procedure.CameraName, Id = procedure.CameraId, Image = (ICogImage)procedure.ToolBlock.Inputs["InputImage"].Value, Graphic = new CogGraphicCollection(), Result = false, IsShow = true, IsSaveImage = procedure.IsSaveImage, SaveImageModel = procedure.SaveImageModel, SaveImagePathModel = procedure.SaveImagePathModel, SavePath = procedure.SavePath }); DatabaseHelper.AddCameraRecords(CurrentProduct.Name, procedure.Name, outputCollection); return "ToolBlockError"; } } catch (Exception ex) { SendTaskMessage(ex.Message, MessageLevel.Error); LogHelper.WriteLogError("执行相机取图并执行视觉工具组时出错!", ex); outputCollection = null; return "Error"; } } /// /// 校准转换 /// /// /// /// /// /// public Vector TransformPoint(CalibrationInfo calib, double x, double y, double angle, string[] items, out string message, RobotBrand robotBrand = RobotBrand.Default) { //得到校准矩阵 Matrix matrix = Matrix.Build.DenseOfArray(calib.AffineTransformationMaterial); if (calib.CameraMount == CameraMount.FixedDown || calib.CameraMount == CameraMount.MobileJ2 || calib.CameraMount == CameraMount.MobileJ4) { angle *= -1; } //将像素坐标转换成机器人坐标 var rpos = matrix * Vector.Build.Dense(new double[] { x, y, 1 }); if (calib.CameraMount == Enums.CameraMount.MobileJ4) { if (items.Length < 4) { SendTaskMessage(Lang.J4移动相机校准转换条件错误, MessageLevel.Alarm); message = "NG,DataError"; return null; } //像素直接转换成mm时的点位 double Robot_X, Robot_Y; Robot_X = rpos[0]; Robot_Y = rpos[1]; //机器人当前TOOL 0下的坐标 double curpos_x = 0, curpos_y = 0, curpos_u = 0; curpos_x = double.Parse(items[1]); curpos_y = double.Parse(items[2]); curpos_u = double.Parse(items[3]); //得到旋转矩阵 double angle1 = Math.PI * (curpos_u - calib.MarkPoint.U) / 180; // 创建T矩阵 Matrix rotationMatrix = Matrix.Build.DenseOfArray(new double[,] { { Math.Cos(angle1), -Math.Sin(angle1) }, { Math.Sin(angle1), Math.Cos(angle1) } }); Vector p3 = Vector.Build.Dense(new double[] { Robot_X - calib.CalibPoints[0].Robot.X, Robot_Y - calib.CalibPoints[0].Robot.Y }); Vector phere = Vector.Build.Dense(new double[] { curpos_x, curpos_y }); var cc = rotationMatrix * p3 + phere; rpos = Vector.Build.Dense(new double[] { cc[0], cc[1] }); } else if (calib.CameraMount == Enums.CameraMount.MobileDown_XYPlatform) { if (items.Length < 4) { SendTaskMessage(Lang.移动相机校准转换条件错误, MessageLevel.Alarm); message = "NG,DataError"; return null; } //像素直接转换成mm时的点位 double Robot_X, Robot_Y; Robot_X = rpos[0]; Robot_Y = rpos[1]; //模组当前TOOL 0下的坐标(相当于模组吸嘴坐标) double curpos_x = 0, curpos_y = 0, curpos_u = 0; curpos_x = double.Parse(items[1]); curpos_y = double.Parse(items[2]); curpos_u = double.Parse(items[3]); //得到旋转矩阵 //double angle1 = Math.PI * (curpos_u - calib.MarkPoint.U) / 180; double angle1 = 0; // 创建T矩阵 Matrix rotationMatrix = Matrix.Build.DenseOfArray(new double[,] { { Math.Cos(angle1), -Math.Sin(angle1) }, { Math.Sin(angle1), Math.Cos(angle1) } }); Vector p3 = Vector.Build.Dense(new double[] { Robot_X - calib.CalibPoints[0].Robot.X, Robot_Y - calib.CalibPoints[0].Robot.Y }); Vector phere = Vector.Build.Dense(new double[] { curpos_x, curpos_y }); var cc = rotationMatrix * p3 + phere; rpos = Vector.Build.Dense(new double[] { cc[0], cc[1] });//Mark点在tool0下的的坐标 } else { //当需要直接转换工具坐标时 if (items.Length == 4 && double.TryParse(items[1], out double curposX) && double.TryParse(items[2], out double curposY) && double.TryParse(items[3], out double curposU)) { //根据机器人当前的点位计算工具坐标 ToolCoord tool = new ToolCoord(); if (robotBrand == RobotBrand.Schneider) { curposU *= -1; } tool.ComputeTool(curposX, curposY, curposU, rpos[0], rpos[1]); rpos = Vector.Build.Dense(new double[] { tool.X, tool.Y }); } } message = string.Empty; return Vector.Build.Dense(new double[] { rpos[0], rpos[1], angle }); } /// /// 获取需要输出项 /// /// /// /// /// 是否成功,错误返回内容,<项的名称,<是否是点位数据,值>> public Tuple>> GetVisionOutput(ProcedureModel procedure, CogToolBlockTerminalCollection outputCollection, string[] items) { RobotBrand robotBrand = RobotBrand.Default; var robot = DeviceConfig.Robots.FirstOrDefault(r => r.Id == procedure.RobotId); if (robot != null) { robotBrand = robot.RobotBrand; } List SendResults = new List(); //<输出项的名称,<是否点位,每项输出集合值>> Dictionary> ResultCollection = new Dictionary>(); foreach (var output in procedure.ProcedureOutputs) //遍历配置的所有需要输出项 { //根据输出类型进行区分输出 if (output.ValueType == typeof(string)) { //需要点位转换时,进行点位提取 if (output.IsPointTran) { if (outputCollection[output.Name].Value == null || string.IsNullOrEmpty(outputCollection[output.Name].Value.ToString())) { //如果需要输出的项的值为空,即点位数据为空,则输出点位的个数为0 ResultCollection.Add(output.Name, new Tuple(true, null)); continue; } string[] strpoints = outputCollection[output.Name].Value.ToString().Split(";"); List> currentPointsForFeeder = new List>(); int index = 0; //获取相机校准 var calib = DeviceConfig.Calibrations.FirstOrDefault(cal => cal.Id == procedure.CalibrationId); if (calib == null) { SendTaskMessage(Lang.J4移动相机校准转换条件错误1, MessageLevel.Alarm); return new Tuple>>(false, "NG,NotCalibrations", null); } List SendDtat = new List(); List points = new List(); //遍历所有的输出点位 foreach (var pos in strpoints) { //转换点位-像素转换成机器人绝对坐标 var rpos = TransformPoint(calib, double.Parse(pos.Split(',')[0]), double.Parse(pos.Split(',')[1]), double.Parse(pos.Split(',')[2]), items, out string message, robotBrand); if (rpos == null) { return new Tuple>>(false, message, null); } currentPointsForFeeder.Add(rpos); } ResultCollection[output.Name] = new Tuple(true, currentPointsForFeeder); } //直接输出字符串 else { if (outputCollection[output.Name].Value == null || string.IsNullOrEmpty(outputCollection[output.Name].Value.ToString())) { ResultCollection.Add(output.Name, new Tuple(false, null)); continue; } ResultCollection[output.Name] = new Tuple(false, outputCollection[output.Name].Value.ToString()); } } else { if (outputCollection[output.Name].Value == null) { ResultCollection.Add(output.Name, new Tuple(false, null)); } else { ResultCollection[output.Name] = new Tuple(false, outputCollection[output.Name].Value.ToString()); } } } return new Tuple>>(true, string.Empty, ResultCollection); } /// /// Feeder拍照前,执行Feeder /// /// /// /// public async Task ExecuteFeederForPhotoBefore(IVoiceCoilMotorFeeder Feeder, ProcedureModel procedure, int CompulsoryVibration, CancellationToken cancellationToken) { if (Feeder.FeederBrand == FeederBrand.AFAG) { await Feeder.SetLightValueAsync((ushort)procedure.LightValue); } else { //Feeder光源亮度设置 if (procedure.LightValue > 3) { var light = await Feeder.GetLightBrightnessAsync(); if (procedure.LightValue != light) { await Feeder.SetLightValueAsync((ushort)procedure.LightValue); } //打开光源 await Feeder.OpenLightAsync(); } } if (CompulsoryVibration == 2) { return; } //先振后拍时 if (procedure.VibrationModel == VibrationModel.VibrationPhoto || CompulsoryVibration == 1) { if (procedure.FeederWorks != null && procedure.FeederWorks.FeederWorkList != null) { bool flog = false; Dictionary tuple; if (feederCurentBlobArea.TryGetValue(CurrentProduct.ID, out tuple)) { //首先判断是否是当前流程 if (tuple.TryGetValue(procedure.Id, out double minArea)) { //如果上次的Blob面积小于设定的最小值,则触发供料 if (minArea <= procedure.MinArea) { flog = true; SendTaskMessage(Lang.执行料仓供料, MessageLevel.Info); await ExecuteFeederStockWorks(Feeder, procedure.FeederStockWorks, cancellationToken); await Task.Delay(procedure.WaitFeederStop); SendTaskMessage(Lang.料仓供料结束, MessageLevel.Info); } } } if (!flog) { //执行配置的Feeder振动任务 await ExecuteFeederWorks(Feeder, procedure.FeederWorks, cancellationToken, false); } await Task.Delay(procedure.WaitFeederStop); } } else { if (procedure.LightValue > 3) { await Task.Delay(procedure.WaitPhoto); } Dictionary tuple; if (feederCurentBlobArea.TryGetValue(CurrentProduct.ID, out tuple)) { //首先判断是否是当前流程 if (tuple.TryGetValue(procedure.Id, out double minArea)) { //如果上次的Blob面积小于设定的最小值,则触发供料 if (minArea <= procedure.MinArea) { SendTaskMessage(Lang.执行料仓供料, MessageLevel.Info); await ExecuteFeederStockWorks(Feeder, procedure.FeederStockWorks, cancellationToken); await Task.Delay(procedure.WaitFeederStop); SendTaskMessage(Lang.料仓供料结束, MessageLevel.Info); //执行配置的Feeder振动任务 /*foreach (var feederwork in procedure.FeederWorks.FeederWorkList) { if (feederwork.Function == VibrationFunction.Delay) { await Task.Delay(feederwork.Duration); } else { await Feeder.StartAction((int)feederwork.Function, feederwork.Duration); await Task.Delay(feederwork.Duration); } }*/ await Task.Delay(procedure.WaitFeederStop); } } } } } /// /// Feeder拍照后,执行Feeder /// /// /// /// /// public async Task ExecuteFeederForPhotoLater(IVoiceCoilMotorFeeder Feeder, ProcedureModel procedure, double curentBlobArea, CancellationToken cancellationToken) { //根据斑点面积决定是否料仓供料 if (curentBlobArea <= procedure.MinArea) { SendTaskMessage(Lang.执行料仓供料, MessageLevel.Info); await ExecuteFeederStockWorks(Feeder, procedure.FeederStockWorks, cancellationToken); await Task.Delay(procedure.WaitFeederStop); SendTaskMessage(Lang.料仓供料结束, MessageLevel.Info); } else { if (procedure.FeederWorks != null && procedure.FeederWorks.FeederWorkList != null) { SendTaskMessage(Lang.执行Feeder振动, MessageLevel.Info); //执行配置的Feeder振动任务 await ExecuteFeederWorks(Feeder, procedure.FeederWorks, cancellationToken); await Task.Delay(procedure.WaitFeederStop); SendTaskMessage(Lang.Feeder振动结束, MessageLevel.Info); } } } #endregion /// /// 加载产品 /// /// /// /// private Task LoadProduct(string name, Action> callback) { return Task.Run(() => { ProductModel product = null; product = Products.FirstOrDefault(p => p.Name == name); if (product == null) { if (int.TryParse(name, out int ProductNumber)) { product = Products.FirstOrDefault(p => p.NumberCode == ProductNumber); } else { callback.Invoke(new Tuple(true, "NG,NoProduct")); return; } } LoadProducts(product); callback.Invoke(new Tuple(true, "OK")); }); } /// /// 获取当前产品名称 /// /// /// private Task GetCurrentProductName(Action> callback) { return Task.Run(() => { if (CurrentProduct == null) { callback.Invoke(new Tuple(true, "null")); return; } callback.Invoke(new Tuple(true, CurrentProduct.Name)); }); } /// /// 获取当前产品ID /// /// /// private Task GetCurrentProductID(Action> callback) { return Task.Run(() => { if (CurrentProduct == null) { callback.Invoke(new Tuple(true, "null")); return; } callback.Invoke(new Tuple(true, CurrentProduct.ID.ToString())); }); } /// /// 获取速度 /// /// /// private Task GetCurrentProductSpeed(Action> callback) { return Task.Run(() => { if (CurrentProduct == null) { return; } int power = CurrentProduct.Power ? 1 : 0; int IsUserUpCamera = CurrentProduct.IsUserUpCamera ? 1 : 0; callback.Invoke(new Tuple(true, $"{CurrentProduct.Speed},{CurrentProduct.Accel},{CurrentProduct.Speeds},{CurrentProduct.Accels},{power},{IsUserUpCamera}")); }); } /// /// 获取所有的机器人点位 /// /// /// private Task GetAllRobotPoints(Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.RobotPoint == null) { return; } StringBuilder sb = new StringBuilder(); foreach (var item in CurrentProduct.RobotPoint.Points) { sb.Append($"{item.Number},{item.X.ToString("F3")},{item.Y.ToString("F3")},{item.Z.ToString("F3")},{item.U.ToString("F3")},{item.V.ToString("F3")},{item.W.ToString("F3")},{(int)item.Hand},{item.Local},{item.Tool},{item.RobotId.ToString()},"); } callback.Invoke(new Tuple(true, $"{sb.ToString().TrimEnd(',')}")); }); } /// /// 获取机器人点位数量 /// /// /// private Task GetRobotPointsCount(Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.RobotPoint == null) { return; } if (CurrentProduct.RobotPoint.Points == null) { callback.Invoke(new Tuple(true, $"0")); } else { callback.Invoke(new Tuple(true, $"{CurrentProduct.RobotPoint.Points.Count}")); } }); } /// /// 获取指定编号机器人点位 /// /// /// private Task GetRobotPoint(int number, Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.RobotPoint == null) { return; } var pos = CurrentProduct.RobotPoint.Points.FirstOrDefault(p => p.Number == number); if (pos == null) { callback.Invoke(new Tuple(true, $"null")); } else { callback.Invoke(new Tuple(true, $"{pos.Number},{pos.X.ToString("F3")},{pos.Y.ToString("F3")},{pos.Z.ToString("F3")},{pos.U.ToString("F3")},{pos.V.ToString("F3")},{pos.W.ToString("F3")},{(int)pos.Hand},{pos.Local},{pos.Tool},{pos.RobotId.ToString()}")); } }); } /// /// 获取托盘数量 /// /// /// private Task GetPalletsCount(Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.RobotPoint == null) { return; } if (CurrentProduct.RobotPoint.Pallets == null) { callback.Invoke(new Tuple(true, $"0")); } else { callback.Invoke(new Tuple(true, $"{CurrentProduct.RobotPoint.Pallets.Count}")); } }); } /// /// 获取所有的托盘 /// /// /// private Task GetAllPallet(Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.RobotPoint == null) { return; } StringBuilder sb = new StringBuilder(); foreach (var item in CurrentProduct.RobotPoint.Pallets) { sb.Append($"{item.Name},{item.Row},{item.Column},{(int)item.Arrangement},{item.P0.X.ToString("F3")},{item.P0.Y.ToString("F3")},{item.P0.Z.ToString("F3")},{item.P0.U.ToString("F3")},{item.P0.V.ToString("F3")},{item.P0.W.ToString("F3")},{(int)item.P0.Hand},{item.P0.Local},{item.P0.Tool}," + $"{item.P1.X.ToString("F3")},{item.P1.Y.ToString("F3")},{item.P1.Z.ToString("F3")},{item.P1.U.ToString("F3")},{item.P1.V.ToString("F3")},{item.P1.W.ToString("F3")},{(int)item.P1.Hand},{item.P1.Local},{item.P1.Tool}," + $"{item.P2.X.ToString("F3")},{item.P2.Y.ToString("F3")},{item.P2.Z.ToString("F3")},{item.P2.U.ToString("F3")},{item.P2.V.ToString("F3")},{item.P2.W.ToString("F3")},{(int)item.P2.Hand},{item.P0.Local},{item.P2.Tool},"); } callback.Invoke(new Tuple(true, $"{sb.ToString().TrimEnd(',')}")); }); } /// /// 获取指定名称的托盘 /// /// /// private Task GetPallet(string name, Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.RobotPoint == null) { return; } var pallet = CurrentProduct.RobotPoint.Pallets.FirstOrDefault(p => p.Name == name); if (pallet == null) { callback.Invoke(new Tuple(true, $"null")); } else { callback.Invoke(new Tuple(true, $"{pallet.Name},{pallet.Row},{pallet.Column},{(int)pallet.Arrangement},{pallet.P0.X.ToString("F3")},{pallet.P0.Y.ToString("F3")},{pallet.P0.Z.ToString("F3")},{pallet.P0.U.ToString("F3")},{pallet.P0.V.ToString("F3")},{pallet.P0.W.ToString("F3")},{(int)pallet.P0.Hand},{pallet.P0.Local},{pallet.P0.Tool}," + $"{pallet.P1.X.ToString("F3")},{pallet.P1.Y.ToString("F3")},{pallet.P1.Z.ToString("F3")},{pallet.P1.U.ToString("F3")},{pallet.P1.V.ToString("F3")},{pallet.P1.W.ToString("F3")},{(int)pallet.P1.Hand},{pallet.P1.Local},{pallet.P1.Tool}," + $"{pallet.P2.X.ToString("F3")},{pallet.P2.Y.ToString("F3")},{pallet.P2.Z.ToString("F3")},{pallet.P2.U.ToString("F3")},{pallet.P2.V.ToString("F3")},{pallet.P2.W.ToString("F3")},{(int)pallet.P2.Hand},{pallet.P0.Local},{pallet.P2.Tool}")); } }); } /// /// 获取夹具末端执行器数量 /// /// /// private Task GetToolCount(string command, Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.CameraProcedures == null) { return; } ProcedureModel procedure = null; foreach (var cameraProcedures in CurrentProduct.CameraProcedures) { procedure = cameraProcedures.ProcedureModels.FirstOrDefault(p => p.TriggerCommand == command); if (procedure != null) break; } if (procedure == null || procedure.ToolInfo == null) { callback.Invoke(new Tuple(true, $"0")); return; } callback.Invoke(new Tuple(true, $"{procedure.ToolInfo.Count}")); }); } /// /// 获取指定编号的夹具末端执行器参数 /// /// /// private Task GetTool(string command, int index, Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.CameraProcedures == null) { return; } ProcedureModel procedure = null; foreach (var cameraProcedures in CurrentProduct.CameraProcedures) { procedure = cameraProcedures.ProcedureModels.FirstOrDefault(p => p.TriggerCommand == command); if (procedure != null) break; } if (procedure == null || procedure.ToolInfo == null) { callback.Invoke(new Tuple(true, $"null")); return; } var tool = procedure.ToolInfo[index]; callback.Invoke(new Tuple(true, $"{tool.Tool.Number},{tool.Tool.X.ToString("F3")},{tool.Tool.Y.ToString("F3")},{tool.OffsetX.ToString("F3")},{tool.OffsetY.ToString("F3")},{tool.OffsetZ.ToString("F3")},{tool.OffsetU.ToString("F3")}")); }); } /// /// 获取流程参数 /// /// /// private Task GetProcedureParam(string command, Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.RobotPoint == null) { return; } ProcedureModel procedure = null; foreach (var cameraProcedures in CurrentProduct.CameraProcedures) { procedure = cameraProcedures.ProcedureModels.FirstOrDefault(p => p.TriggerCommand == command); if (procedure != null) break; } if (procedure == null) { callback.Invoke(new Tuple(true, $"null")); return; } callback.Invoke(new Tuple(true, $"{procedure.WaitSuction},{procedure.WaitBlow},{procedure.WaitPhoto}")); }); } /// /// 获取指定名称托盘的指定编号的点坐标 /// /// /// private Task GetPalletPoint(string name, int index, Action> callback) { return Task.Run(() => { if (CurrentProduct == null || CurrentProduct.RobotPoint == null) { return; } var pallet = CurrentProduct.RobotPoint.Pallets.FirstOrDefault(p => p.Name == name); if (pallet == null) { callback.Invoke(new Tuple(true, $"null")); } else { PalletTool palletTool = new PalletTool(pallet); StringBuilder sb = new StringBuilder(); callback.Invoke(new Tuple(true, $"{pallet.Name},{palletTool.Points.Count()},{index},{palletTool.Points[index].X.ToString("F3")},{palletTool.Points[index].Y.ToString("F3")},{palletTool.Points[index].Z.ToString("F3")}," + $"{palletTool.Points[index].U.ToString("F3")},{palletTool.Points[index].V.ToString("F3")},{palletTool.Points[index].W.ToString("F3")},{(int)(palletTool.Points[index].Hand)},{palletTool.Points[index].Local},{palletTool.Points[index].Tool}")); } }); } /// /// 执行Feeder振动集合 /// /// /// /// public async Task ExecuteFeederWorks(IVoiceCoilMotorFeeder feeder, FeederWorks works, CancellationToken cancellationToken, bool isUpdateParame = true) { //执行配置的Feeder振动任务 if (feeder.FeederBrand == FeederBrand.AFAG) { foreach (var feederwork in works.FeederWorkList) { if (feederwork.ConfigIndex < 0) { continue; } if (feederwork.ConfigIndex == 0) { await Task.Delay(feederwork.Duration); } else { ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex); ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity); ((AfagFeeder)feeder).SetDirection(feederwork.Direction); await Task.Delay(feederwork.Duration); ((AfagFeeder)feeder).SetIntensity(0); } if (cancellationToken != null && cancellationToken.IsCancellationRequested) { return; } } } else { foreach (var feederwork in works.FeederWorkList) { if (feederwork.Function == VibrationFunction.Delay) { await Task.Delay(feederwork.Duration); } else { if (feederwork.SingleAction != null && isUpdateParame) { await feeder.SetShakeParametersAsync((int)feederwork.Function, feederwork.SingleAction); } await feeder.StartAction((int)feederwork.Function, feederwork.Duration); await Task.Delay(feederwork.Duration); } if (cancellationToken != null && cancellationToken.IsCancellationRequested) { return; } } } } /// /// 执行Feeder振动集合 /// /// /// /// public async Task ExecuteFeederWorks(IVoiceCoilMotorFeeder feeder, FeederWorks works, bool isUpdateParame = true) { //执行配置的Feeder振动任务 if (feeder.FeederBrand == FeederBrand.AFAG) { foreach (var feederwork in works.FeederWorkList) { if (feederwork.ConfigIndex < 0) { continue; } if (feederwork.ConfigIndex == 0) { await Task.Delay(feederwork.Duration); } else { ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex); ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity); ((AfagFeeder)feeder).SetDirection(feederwork.Direction); await Task.Delay(feederwork.Duration); ((AfagFeeder)feeder).SetIntensity(0); } } } else { foreach (var feederwork in works.FeederWorkList) { if (feederwork.Function == VibrationFunction.Delay) { await Task.Delay(feederwork.Duration); } else { if (feederwork.SingleAction != null && isUpdateParame) { await feeder.SetShakeParametersAsync((int)feederwork.Function, feederwork.SingleAction); } await feeder.StartAction((int)feederwork.Function, feederwork.Duration); await Task.Delay(feederwork.Duration); } } } } /// /// 执行Feeder料仓供料及振动集合 /// /// /// /// public async Task ExecuteFeederStockWorks(IVoiceCoilMotorFeeder feeder, FeederStockWorks works, bool isUpdateParame = true) { List tasks = new List(); if (feeder.FeederBrand == FeederBrand.AFAG) { tasks.Add(Task.Run(() => { if (works != null && works.FeederStockWorkList != null) { //执行配置的料仓振动任务 foreach (var stockwork in works.FeederStockWorkList) { if (stockwork.RegisterIndex == 0) { Task.Delay(stockwork.Duration).Wait(); } else if (stockwork.RegisterIndex == 1) { if (BgModbusTcpCommunicate != null) { BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 1; Thread.Sleep(stockwork.Duration); BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 0; } } } } })); tasks.Add(Task.Run(() => { if (works != null && works.FeederWorkList != null) { //执行配置的Feeder振动任务 foreach (var feederwork in works.FeederWorkList) { if (feederwork.ConfigIndex < 0) { continue; } if (feederwork.ConfigIndex == 0) { Thread.Sleep(feederwork.Duration); } else { ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex); ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity); ((AfagFeeder)feeder).SetDirection(feederwork.Direction); Thread.Sleep(feederwork.Duration); ((AfagFeeder)feeder).SetIntensity(0); } } } })); } else { tasks.Add(Task.Run(() => { if (works != null && works.FeederStockWorkList != null) { //执行配置的料仓振动任务 foreach (var stockwork in works.FeederStockWorkList) { if (stockwork.Function == VibrationFunction.Delay) { Task.Delay(stockwork.Duration).Wait(); } else { feeder.OutputAsync(((int)stockwork.Function) + 1, stockwork.Duration).Wait(); Thread.Sleep(stockwork.Duration); } } } })); tasks.Add(Task.Run(() => { if (works != null && works.FeederWorkList != null) { //执行配置的Feeder振动任务 foreach (var feederwork in works.FeederWorkList) { if (feederwork.Function == VibrationFunction.Delay) { Task.Delay(feederwork.Duration).Wait(); } else { if (feederwork.SingleAction != null && isUpdateParame) { feeder.SetShakeParameters((int)feederwork.Function, feederwork.SingleAction); } feeder.StartAction((int)feederwork.Function, feederwork.Duration).Wait(); Thread.Sleep(feederwork.Duration); } } } })); } await Task.WhenAll(tasks); } /// /// 执行Feeder料仓供料及振动集合 /// /// /// /// public async Task ExecuteFeederStockWorks(IVoiceCoilMotorFeeder feeder, FeederStockWorks works, CancellationToken cancellationToken, bool isUpdateParame = true) { List tasks = new List(); if (feeder.FeederBrand == FeederBrand.AFAG) { tasks.Add(Task.Run(() => { if (works != null && works.FeederStockWorkList != null) { //执行配置的料仓振动任务 foreach (var stockwork in works.FeederStockWorkList) { if (stockwork.RegisterIndex == 0) { LogHelper.WriteLogDebug($"开启延时:{stockwork.Duration}ms"); Thread.Sleep(stockwork.Duration); LogHelper.WriteLogDebug($"延时结束"); } else if (stockwork.RegisterIndex == 1) { if (BgModbusTcpCommunicate != null) { LogHelper.WriteLogDebug($"料仓开始震动:{stockwork.Duration}ms"); BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 1; Thread.Sleep(stockwork.Duration); BgModbusTcpCommunicate.Slave.DataStore.HoldingRegisters[stockwork.Address] = 0; LogHelper.WriteLogDebug($"料仓停止震动:{stockwork.Duration}ms"); } } if (cancellationToken != null && cancellationToken.IsCancellationRequested) { return; } } } })); tasks.Add(Task.Run(() => { if (works != null && works.FeederWorkList != null) { //执行配置的Feeder振动任务 foreach (var feederwork in works.FeederWorkList) { if (feederwork.ConfigIndex < 0) { continue; } if (feederwork.ConfigIndex == 0) { Thread.Sleep(feederwork.Duration); } else { ((AfagFeeder)feeder).LoadConfiguration(feederwork.ConfigIndex); ((AfagFeeder)feeder).SetIntensity(feederwork.Intensity); ((AfagFeeder)feeder).SetDirection(feederwork.Direction); Thread.Sleep(feederwork.Duration); ((AfagFeeder)feeder).SetIntensity(0); } if (cancellationToken != null && cancellationToken.IsCancellationRequested) { return; } } } })); } else { tasks.Add(Task.Run(() => { if (works != null && works.FeederStockWorkList != null) { //执行配置的料仓振动任务 foreach (var stockwork in works.FeederStockWorkList) { if (stockwork.Function == VibrationFunction.Delay) { Task.Delay(stockwork.Duration).Wait(); } else { feeder.OutputAsync(((int)stockwork.Function) + 1, stockwork.Duration).Wait(); Thread.Sleep(stockwork.Duration); } if (cancellationToken != null && cancellationToken.IsCancellationRequested) { return; } } } })); tasks.Add(Task.Run(() => { if (works != null && works.FeederWorkList != null) { //执行配置的Feeder振动任务 foreach (var feederwork in works.FeederWorkList) { if (feederwork.Function == VibrationFunction.Delay) { Task.Delay(feederwork.Duration).Wait(); } else { if (feederwork.SingleAction != null && isUpdateParame) { feeder.SetShakeParameters((int)feederwork.Function, feederwork.SingleAction); } feeder.StartAction((int)feederwork.Function, feederwork.Duration).Wait(); Thread.Sleep(feederwork.Duration); } if (cancellationToken != null && cancellationToken.IsCancellationRequested) { return; } } } })); } await Task.WhenAll(tasks); } /// /// 执行计时器 /// /// /// public void ExecuteCycleTime(string state, Action> callback) { if (int.Parse(state) == 1) { IsStart = true; StartTime = DateTime.Now; callback.Invoke(new Tuple(true, $"1")); } else { IsStart = false; callback.Invoke(new Tuple(true, $"0")); } } /// /// 生产计数 /// /// /// public void AddProductionRecords(string _Content, Action> callback) { DatabaseHelper.AddProductProductionRecords(CurrentProduct.Name, _Content); callback.Invoke(new Tuple(true, _Content)); } /// /// 获取产品的全局参数 /// /// /// /// public void GetGlobalParam(string ParamName, int index, Action> callback) { if (CurrentProduct == null || CurrentProduct.RobotPoint == null) { return; } var pos = CurrentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Name == ParamName); if (pos == null) { callback.Invoke(new Tuple(true, $"null")); } else { switch (index) { case 1: callback.Invoke(new Tuple(true, $"{pos.Name},{pos.Param1}")); break; case 2: callback.Invoke(new Tuple(true, $"{pos.Name},{pos.Param2}")); break; case 3: callback.Invoke(new Tuple(true, $"{pos.Name},{pos.Param3}")); break; case 4: callback.Invoke(new Tuple(true, $"{pos.Name},{pos.Param4}")); break; default: callback.Invoke(new Tuple(true, $"{pos.Name},{pos.Param1},{pos.Param2},{pos.Param3},{pos.Param4}")); break; } } } #endregion #endregion #region 机器人&Feeder&相机连接状态事件 /// /// 机器人发送事件 /// /// /// /// /// private void Management_SendEvent(Guid robotid, object arg2, string arg3) { var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == robotid); SendTaskMessage($"{r.RobotName}Send:{arg3}", MessageLevel.Info); } /// /// 机器人断开连接时 /// /// /// /// /// private void Management_DisconnectedEvent(Guid robotid, object arg2, ClosedEventArgs arg3) { if (RobotTCPCollection.ContainsKey(robotid)) { RobotTCPCollection[robotid].Cancel(); } var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == robotid); if (r.ConnectType == TCPConnectType.Server) { ITcpSessionClient socketClient = arg2 as ITcpSessionClient; SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.断开连接}!", MessageLevel.Error); var robot = this.RobotService.GetRobot(robotid); if (robot.TcpService.Count < 1) { var sta = Status.FirstOrDefault(s => s.ID == robotid); App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{r.RobotName}{Lang.未连接}"; sta.Background = new SolidColorBrush(Colors.Red); })); } } else { SendTaskMessage($"{r.RobotName}{Lang.断开连接}!", MessageLevel.Error); var sta = Status.FirstOrDefault(s => s.ID == robotid); App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{r.RobotName}{Lang.断开连接}"; sta.Background = new SolidColorBrush(Colors.Red); })); } } /// /// 机器人连接成功时 /// /// /// /// /// private void Management_ConnectedEvent(Guid robotid, object arg2, ConnectedEventArgs arg3) { var r = DeviceConfig.Robots.FirstOrDefault(ro => ro.Id == robotid); if (r.ConnectType == TCPConnectType.Server) { ITcpSessionClient socketClient = arg2 as ITcpSessionClient; SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.已连接}!", MessageLevel.Debug); var robot = this.RobotService.GetRobot(robotid); if (robot.TcpService.Count > 0) { var sta = Status.FirstOrDefault(s => s.ID == robotid); App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{r.RobotName}{Lang.已连接}"; sta.Background = new SolidColorBrush(Colors.Green); })); } } else { SendTaskMessage($"{r.RobotName}{Lang.已连接}!", MessageLevel.Debug); var sta = Status.FirstOrDefault(s => s.ID == robotid); App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{r.RobotName}{Lang.已连接}"; sta.Background = new SolidColorBrush(Colors.Green); })); } } /// /// Feeder断开连接时 /// /// /// /// /// private void Management_FeederDisconnectedEvent(Guid feederid, ITcpClient arg2, ClosedEventArgs arg3) { var f = DeviceConfig.Feeders.FirstOrDefault(ro => ro.Id == feederid); var sta = Status.FirstOrDefault(s => s.ID == feederid); App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{f.FeederName}{Lang.未连接}"; sta.Background = new SolidColorBrush(Colors.Red); })); SendTaskMessage($"{f.FeederName}{Lang.断开连接}!", MessageLevel.Error); } /// /// Feeder连接成功时 /// /// /// /// /// private void Management_FeederConnectedEvent(Guid feederid, ITcpClient arg2, ConnectedEventArgs arg3) { var f = DeviceConfig.Feeders.FirstOrDefault(ro => ro.Id == feederid); var sta = Status.FirstOrDefault(s => s.ID == feederid); App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{f.FeederName}{Lang.已连接}"; sta.Background = new SolidColorBrush(Colors.Green); })); SendTaskMessage($"{f.FeederName}{Lang.已连接}!", MessageLevel.Debug); } /// /// 相机连接或者断开时 /// /// /// private void Management_CameraConnectChangedEvent(Guid id, bool state) { var sta = Status.FirstOrDefault(s => s.ID == id); var camera = DeviceConfig.Cameras.FirstOrDefault(c => c.Id == id); if (sta != null && camera != null) { if (state) { App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{camera.CameraName}{Lang.已连接}"; sta.Background = new SolidColorBrush(Colors.Green); })); } else { App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{camera.CameraName}{Lang.断开连接}"; sta.Background = new SolidColorBrush(Colors.Red); })); } } } /// /// PLC连接状态改变事件 /// /// /// private void Management_PlcConnectChangedEvent(object sender, bool isconnected) { IPlc plc = sender as IPlc; var sta = Status.FirstOrDefault(s => s.ID == plc.Id); if (plc.IsConnected) { SendTaskMessage($"{plc.Name}{Lang.已连接}!", MessageLevel.Debug); if (sta == null) return; App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{plc.Name}{Lang.已连接}"; sta.Background = new SolidColorBrush(Colors.Green); })); } else { SendTaskMessage($"{plc.Name}{Lang.断开连接}!", MessageLevel.Error); if (sta == null) return; App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{plc.Name}{Lang.未连接}"; sta.Background = new SolidColorBrush(Colors.Red); })); } } #endregion #region 后台服务器通讯事件 private void BgCommunicate_SendEvent(ITcpSessionClient client, string msg) { SendTaskMessage($"{Lang.服务器}[{client.ServicePort}] Send To {Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info); } public Dictionary BgCommunicateollection = new Dictionary(); private async void BgCommunicate_ReceivedEvent(ITcpSessionClient client, string msg) { try { if (!BgCommunicateollection.ContainsKey(client.Id)) { BgCommunicateollection.Add(client.Id, new CancellationTokenSource()); } SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]ReceiveFor{Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info); Task.Run(async () => { try { await ExecuteRemoteCommand(msg, async result => { try { if (!client.Online) { return; } if (result.Item1) { if (result.Item2.Length > 1) { foreach (var item in result.Item2) { await BgCommunicate.SendAsync(client, $"{item}"); await Task.Delay(10); } } else { await BgCommunicate.SendAsync(client, $"{result.Item2[0]}"); } } } catch (Exception ex) { LogHelper.WriteLogError("后台TCP发送数据时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } }, BgCommunicateollection[client.Id].Token); } catch (Exception ex) { LogHelper.WriteLogError("处理后台服务器接收的命令时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } }); } catch (Exception ex) { LogHelper.WriteLogError("处理后台服务器接收的命令时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } } private void BgCommunicate_DisconnectedEvent(ITcpSessionClient client, ClosedEventArgs e) { SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.断开连接}!", MessageLevel.Alarm); if (BgCommunicateollection.ContainsKey(client.Id)) { BgCommunicateollection[client.Id].Cancel(); } } private void BgCommunicate_ConnectedEvent(ITcpSessionClient client, ConnectedEventArgs e) { SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.连接成功}!", MessageLevel.Debug); } #endregion #region Eip private void DisposeRfidClient() { try { if (BgEipCommunicate != null) { BgEipCommunicate.OnTagDataReceived -= RfidClient_OnTagDataReceived; BgEipCommunicate.OnConnectReceive -= Management_EipConnectChangedEvent; BgEipCommunicate.Dispose(); } UpdateEthernetIpStatus(false); } catch { /* ignore */ } finally { BgEipCommunicate = null; } } private void RfidClient_OnTagDataReceived(object sender, RFIDTagDataEventArgs e) { if (e == null || e.Tags == null || e.Tags.Count == 0) return; foreach (var tag in e.Tags) { lock (_sync) _lastRfidTag = CloneTag(tag); SendTaskMessage("RFID 端口" + tag.Port + " Hex=" + tag.ToHexString(), MessageLevel.Debug); } } private void StopHandshakeLoop() { try { if (_handshakeCts != null) { _handshakeCts.Cancel(); try { if (_handshakeTask != null) _handshakeTask.Wait(2000); } catch (AggregateException) { /* ignore */ } _handshakeCts.Dispose(); } } catch { /* ignore */ } finally { _handshakeCts = null; _handshakeTask = null; } } #endregion #region 方法 private void SendTaskMessage(string msg, MessageLevel level) { _eventAggregator.GetEvent().Publish(new Models.MessageStruct() { Message = msg, level = level }); } private void DoCycleTime(object state) { if (IsStart) { App.Current.Dispatcher.Invoke(new Action(() => { CT = DateTime.Now - StartTime; })); } } private void DoYieldTime(object state) { if (CurrentProduct == null) return; TotalQuantity = DatabaseHelper.GetTotalQuantity(CurrentProduct.Name); TotalQuantityToday = DatabaseHelper.GetTotalQuantityToday(CurrentProduct.Name); CurrentUPH = DatabaseHelper.GetUPH(CurrentProduct.Name); } #region vm启动检测 /// /// 杀死所有VM有关进程 /// public void KillVMProcess() { KillProcess("VisionMasterServerApp"); KillProcess("VisionMaster"); KillProcess("VmModuleProxy.exe"); KillProcess("vServerApp.exe"); } /// /// 杀死指定进程名的进程 /// /// public void KillProcess(string strKillName) { foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses()) { if (p.ProcessName.Contains(strKillName)) { try { p.Kill(); p.WaitForExit(); } catch (Exception ex) { LogHelper.WriteLogError($"杀死进程:{strKillName}时失败!", ex); } } } } #endregion #endregion } }