using Cognex.VisionPro.ToolBlock; using MathNet.Numerics.LinearAlgebra; using NPOI.SS.Formula.Functions; using OpenCvSharp; using Prism.Events; using Prism.Ioc; using Prism.Mvvm; using Prism.Regions; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Media; using Team.FFFeederService.Interfaces; using TeamAAS_VP.Core.PLCs; using TeamAAS_VP.Core.ScrewDriver; using TeamAAS_VP.Enums; using TeamAAS_VP.Events; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models; using TeamAAS_VP.Resources.Languages; using TouchSocket.Core; using TouchSocket.Sockets; namespace TeamAAS_VP.Core { public class Management : BindableBase { #region 字段 IRegionManager _regionManager; IEventAggregator _eventAggregator; IContainerProvider _container; IConfigService _configService; IRobotService _robotService; ICameraService _cameraService; IFeederService _feederService; IPlcService _plcService; ILightManagerService _lightManagerService; IProductService _productService; ICalibrationService _calibrationService; IRemoteCommandService _remoteCommandService; ISystemDatabaseService _systemDatabaseService; IMesService _mesService; Timer yieldtimer; private DateTime StartTime = DateTime.Now; // Pressure collection /// /// 压力采集信号控制CTS /// private CancellationTokenSource[] _pressureCts = new CancellationTokenSource[10]; /// /// 压力采集超时CTS /// private CancellationTokenSource[] _pressureTimeoutCts = new CancellationTokenSource[10]; /// /// 压力采集CTS /// private CancellationTokenSource[] _pressureLinkedCts = new CancellationTokenSource[10]; /// /// 压力数据集合 /// private List<(DateTime Timestamp, float Value)>[] _pressureSamples = new List<(DateTime, float)>[10]; /// /// 采样间隔毫秒 /// private int _pressureSampleIntervalMs = 50; //采样间隔毫秒 /// /// 超时自动停止秒数 /// private int _pressureMaxDurationSeconds = 30; //超时自动停止秒数 DateTime[] StartCtqTime = new DateTime[3];//开始采集Ctq数据的时间 #endregion #region 属性 private ObservableCollection _Renders; /// /// 显示界面 /// public ObservableCollection Renders { get { return _Renders; } set { SetProperty(ref _Renders, value); } } public BgCommunicate BgCommunicate { get; private set; } public BgModbusTcpCommunicate BgModbusTcpCommunicate { get; private set; } private ObservableCollection _Status = new ObservableCollection(); /// /// 状态栏集合 /// public ObservableCollection Status { get { return _Status; } set { SetProperty(ref _Status, 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); } } private XYD_ScrewDriver _ScrewDriver; public XYD_ScrewDriver ScrewDriver { get { return _ScrewDriver; } set { SetProperty(ref _ScrewDriver, value); } } public Dictionary BackupObject { get; set; } = new Dictionary(); private double _CTtime; /// /// ct /// public double CTtime { get { return _CTtime; } set { SetProperty(ref _CTtime, value); } } private ObservableCollection _dfc = new ObservableCollection(); /// /// CTQ数据集合 /// public ObservableCollection DFC { get { return _dfc; } set { SetProperty(ref _dfc, value); } } #endregion #region 命令 #endregion #region 事件 #endregion public Management(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IConfigService configService, IRobotService robotService, ICameraService cameraService, IFeederService feederService, IPlcService plcService, IProductService productService, ICalibrationService calibrationService, IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService, ILightManagerService lightManagerService, IMesService mesService) { _regionManager = regionManager; _eventAggregator = ea; _container = container; _configService = configService; _robotService = robotService; _cameraService = cameraService; _feederService = feederService; _plcService = plcService; _calibrationService = calibrationService; yieldtimer = new Timer(DoYieldTime, null, 10000, 1000); Renders = new ObservableCollection(); _configService = configService; _productService = productService; _remoteCommandService = remoteCommandService; _systemDatabaseService = systemDatabaseService; _lightManagerService = lightManagerService; _mesService = mesService; InitPressure(); } #region 初始化硬件模块 /// /// 初始化Feeder /// /// public async Task<(bool IsSucceed, string Msg)> InitFeeders() { var feeders = _configService.GetAllFeeders(); StringBuilder sb = new StringBuilder(); foreach (var item in feeders) { _feederService.CreateFeeder(item.Id, item.FeederNo, item.FeederName, item.IP, item.Port, item.FeederBrand); var feeder = _feederService.GetFeeder(item.Id); Status.Add(new StatusInfo(item.Id, item.FeederName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red))); try { feeder.FeederConnectionStatusChanged += Management_FeederConnectionStatusChanged; await feeder.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 (true, ""); } else { return (false, sb.ToString()); } } /// /// 初始化机器人 /// /// public async Task<(bool IsSucceed, string Msg)> InitRobots() { var robots = _configService.GetAllRobots(); StringBuilder sb = new StringBuilder(); foreach (var item in robots) { _robotService.CreateRobot(item.Id, item); try { Status.Add(new StatusInfo(item.Id, item.RobotName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red))); var robot = _robotService.GetRobot(item.Id); robot.ReceivedEvent += Management_ReceivedEvent; robot.SendEvent += Management_SendEvent; robot.ConnectedEvent += Management_ConnectedEvent; robot.DisconnectedEvent += Management_DisconnectedEvent; await robot.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 (true, ""); } else { return (false, sb.ToString()); } } /// /// 初始化PLC /// /// public async Task<(bool IsSucceed, string Msg)> InitPlc() { var plcs = _configService.GetAllPlcs(); StringBuilder sb = new StringBuilder(); foreach (var item in plcs) { try { _plcService.CreatePlc(item.Id, item); var plc = _plcService.GetPlc(item.Id); Status.Add(new StatusInfo(item.Id, item.Name + $"{Lang.未连接}", new SolidColorBrush(Colors.Red))); plc.ConnectChangedEvent += Management_PlcConnectChangedEvent; await plc.ConnectAsync(); //连接PLC //SendTaskMessage($"{item.Name}{Lang.已连接}!", MessageLevel.Debug); } catch (Exception ex) { sb.AppendLine($"{item.Name}:{Lang.断开连接}![{ex.Message}]"); SendTaskMessage($"{item.Name}{Lang.断开连接}!", MessageLevel.Alarm); } } try { await WriteSystemParameterToPlcAsync(_configService.GetSystemConfiguration()); RegisterPlcMonitorAddress(); } catch (Exception ex) { LogHelper.WriteLogError("为PLC写入系统参数和订阅变量时出错", ex); } if (sb.Length == 0) { return (true, ""); } else { return (false, sb.ToString()); } } /// /// 初始化相机 /// /// public async Task<(bool IsSucceed, string Msg)> InitCameras() { var cameras = _configService.GetAllCameras(); StringBuilder sb = new StringBuilder(); foreach (var item in cameras) { try { // Renders.Add(new ShowRender() { Id = item.Id, CameraName = item.CameraName }); _cameraService.CreateCamera(item.Id, item); var camera = _cameraService.GetCamera(item.Id); Status.Add(new StatusInfo(item.Id, item.CameraName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red))); camera.CameraConnectChangedEvent += Management_CameraConnectChangedEvent; try { if (await camera.OpenDeviceAsync()) { SendTaskMessage($"{item.CameraName}{Lang.已连接}!", MessageLevel.Debug); } else { SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm); sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!"); } } catch (Exception ex) { SendTaskMessage($"{item.CameraName}{ex.Message}!", MessageLevel.Alarm); sb.AppendLine($"{item.CameraName}:{ex.Message}!"); } } 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 (true, ""); } else { return (false, sb.ToString()); } } /// /// 初始化光源控制器 /// /// public async Task<(bool IsSucceed, string Msg)> InitLightControllers() { var lightcontrollers = _configService.GetAllLightControllers(); StringBuilder sb = new StringBuilder(); foreach (var item in lightcontrollers) { try { var lishtCtr = await _lightManagerService.RegisterControllerAsync(item.Id, item); //var lightcontroller = _cameraService.GetLightController(item.Id); //await lightcontroller.ConnectAsync(); //连接光源控制器 //SendTaskMessage($"{item.LightControllerName}{Lang.已连接}!", MessageLevel.Debug); } catch (Exception ex) { //sb.AppendLine($"{item.LightControllerName}:{Lang.断开连接}![{ex.Message}]"); //SendTaskMessage($"{item.LightControllerName}{Lang.断开连接}!", MessageLevel.Alarm); } } if (lightcontrollers.Count > 0) { var result = await _lightManagerService.ConnectAllAsync(); if (result) { return (true, ""); } else { return (false, "Light Controller Connect Failed!"); } } return (true, ""); //if (sb.Length == 0) //{ // return (true, ""); //} //else //{ // return (false, sb.ToString()); //} } /// /// 初始化后台TCP服务器通讯 /// public void InitBgTcp() { var config = _configService.GetBgTcp(); try { BgCommunicate = new BgCommunicate(config); BgCommunicate.ConnectedEvent += BgCommunicate_ConnectedEvent; BgCommunicate.DisconnectedEvent += BgCommunicate_DisconnectedEvent; BgCommunicate.ReceivedEvent += BgCommunicate_ReceivedEvent; BgCommunicate.SendEvent += BgCommunicate_SendEvent; BgCommunicate.StartListening(); SendTaskMessage($"{Lang.已开启监听端口}:{config.Port}", MessageLevel.Debug); } catch (Exception ex) { SendTaskMessage($"{Lang.未开启监听端口}:{config.Port}", MessageLevel.Error); LogHelper.WriteLogError("开启后台服务器通讯时出错!", ex); } } /// /// 初始化后台ModbusTCP从站 /// public void InitModbusTcp() { try { var config = _configService.GetBgModbusTcp(); BgModbusTcpCommunicate = new BgModbusTcpCommunicate(config); if (config.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); } } /// /// 初始化后台脚本 /// 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); } } /// /// 初始化电批 /// /// public async Task InitScrewDriver() { try { var screwDriverConfig = _configService.GetScrewDriverConfig(); ScrewDriver = new XYD_ScrewDriver(screwDriverConfig.IPAdress, screwDriverConfig.Port); Status.Add(new StatusInfo(ScrewDriver.Id, "电批未连接", new SolidColorBrush(Colors.Red))); ScrewDriver.ConnectStateChangedEvent += ScrewDriver_ConnectStateChangedEvent; ScrewDriver.SendTimeout = screwDriverConfig.SendTimeout; screwDriverConfig.ReceiveTimeout = screwDriverConfig.ReceiveTimeout; var isConnected = await ScrewDriver.ConnectAsync(); if (isConnected) { SendTaskMessage($"电批已连接", MessageLevel.Debug); } else { SendTaskMessage($"电批连接失败", MessageLevel.Error); } } catch (Exception ex) { SendTaskMessage($"电批连接失败:{ex.Message}", MessageLevel.Alarm); LogHelper.WriteLogError("连接电批时出错!", ex); } } #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); //从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。 var buffer = e.ByteBlock.Span.ToString(robot.GetEncoding()); SendTaskMessage($"{robot.Name}Received:{buffer}", MessageLevel.Info); var _ = _remoteCommandService.ExecuteAsync(buffer, RobotTCPCollection[robotid].Token, async msg => { try { if (robot.ConnectType == TCPConnectType.Client) { if (robot.IsConnected) { await robot.SendAsync($"{msg}"); } } else { var cient = (ITcpSessionClient)arg2; if (cient.Online) { await robot.SendAsync(cient, $"{msg}"); } } } catch (Exception ex) { LogHelper.WriteLogError("TCP发送数据时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } }); } catch (Exception ex) { LogHelper.WriteLogError("处理机器人接收的命令时出错!", ex); SendTaskMessage(ex.Message, MessageLevel.Alarm); } } #endregion #region 机器人&Feeder&相机连接状态事件 /// /// 机器人发送事件 /// /// /// /// /// private void Management_SendEvent(Guid robotid, object arg2, string arg3) { var r = _robotService.GetRobot(robotid); SendTaskMessage($"{r.Name}Send:{arg3}", MessageLevel.Info); } /// /// 机器人断开连接时 /// /// /// /// /// private void Management_DisconnectedEvent(Guid robotid, object arg2, ClosedEventArgs arg3) { if (RobotTCPCollection.ContainsKey(robotid)) { RobotTCPCollection[robotid].Cancel(); } var r = _configService.GetRobot(robotid); if (r.ConnectType == TCPConnectType.Server) { ITcpSessionClient socketClient = arg2 as ITcpSessionClient; SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.断开连接}!", MessageLevel.Error); var robot = _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 = _configService.GetRobot(robotid); if (r.ConnectType == TCPConnectType.Server) { ITcpSessionClient socketClient = arg2 as ITcpSessionClient; SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.已连接}!", MessageLevel.Debug); var robot = _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_FeederConnectionStatusChanged(object sender, bool state) { IFeeder feeder = sender as IFeeder; var sta = Status.FirstOrDefault(s => s.ID == feeder.Id); if (state) { App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{feeder.Name}{Lang.已连接}"; sta.Background = new SolidColorBrush(Colors.Green); })); SendTaskMessage($"{feeder.Name}{Lang.已连接}!", MessageLevel.Debug); } else { App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"{feeder.Name}{Lang.未连接}"; sta.Background = new SolidColorBrush(Colors.Red); })); SendTaskMessage($"{feeder.Name}{Lang.断开连接}!", MessageLevel.Error); } } /// /// 相机连接或者断开时 /// /// /// private void Management_CameraConnectChangedEvent(Guid id, bool state) { var sta = Status.FirstOrDefault(s => s.ID == id); var camera = _configService.GetCamera(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 async 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); })); try { await WriteSystemParameterToPlcAsync(_configService.GetSystemConfiguration()); await WritePositionToPlcAsync(); } catch (Exception ex) { LogHelper.WriteLogError("PLC连接成功时,写入系统参数和点位信息时出错!", ex); } } 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); })); } } /// /// 电批连接状态改变时 /// /// /// private void ScrewDriver_ConnectStateChangedEvent(object screwDevice, bool state) { try { XYD_ScrewDriver screwDriver = screwDevice as XYD_ScrewDriver; if (state) { SendTaskMessage($"电批{Lang.已连接}!", MessageLevel.Debug); var sta = Status.FirstOrDefault(s => s.ID == screwDriver.Id); App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"电批{Lang.已连接}"; sta.Background = new SolidColorBrush(Colors.Green); })); } else { SendTaskMessage($"电批{Lang.未连接}!", MessageLevel.Error); var sta = Status.FirstOrDefault(s => s.ID == screwDriver.Id); App.Current.Dispatcher.Invoke(new Action(() => { sta.Message = $"电批{Lang.未连接}"; sta.Background = new SolidColorBrush(Colors.Red); })); } } catch (Exception ex) { LogHelper.WriteLogError("电批连接状态改变时出错了", ex); } } #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); var _ = _remoteCommandService.ExecuteAsync(msg, BgCommunicateollection[client.Id].Token, async response => { try { if (!client.Online) { return; } await BgCommunicate.SendAsync(client, $"{response}"); } catch (Exception ex) { LogHelper.WriteLogError("后台TCP发送数据时出错!", 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 PLC /// /// 将系统参数写入PLC /// /// /// public async Task WriteSystemParameterToPlcAsync(SystemConfiguration systemConfiguration) { var addressConfig = _configService.GetPlcAddresses(); var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC; if (plc == null || !plc.IsConnected) return; Dictionary nodeValues = new Dictionary(); nodeValues.Add(addressConfig.Out_WorkMode.Address, (Int16)systemConfiguration.WorkMode); nodeValues.Add(addressConfig.Out_PickPointNum.Address, (Int16)systemConfiguration.PickPointNum); nodeValues.Add(addressConfig.Out_DowmCameraNum.Address, (Int16)systemConfiguration.DowmCameraNum); nodeValues.Add(addressConfig.Out_UpCameraPickNum.Address, (Int16)systemConfiguration.UpCameraPickNum); nodeValues.Add(addressConfig.Out_UpCameraPutNum.Address, (Int16)systemConfiguration.UpCameraPutNum); nodeValues.Add(addressConfig.Out_UPCameracheckNum.Address, (Int16)systemConfiguration.UPCameracheckNum); nodeValues.Add(addressConfig.Out_FixedCameraPickNum.Address, (Int16)systemConfiguration.FixedCameraPickNum); nodeValues.Add(addressConfig.Out_FixedCameraPutNum.Address, (Int16)systemConfiguration.FixedCameraPutNum); nodeValues.Add(addressConfig.Out_FixedCameracheckNum.Address, (Int16)systemConfiguration.FixedCameracheckNum); nodeValues.Add(addressConfig.Out_ScrewTeachNum.Address, (Int16)systemConfiguration.ScrewTeachNum); var res = await plc.WriteNodesAsync(nodeValues); } /// /// 注册需要监控的PLC地址 /// public void RegisterPlcMonitorAddress() { var addressConfig = _configService.GetPlcAddresses(); var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC; if (plc == null || !plc.IsConnected) return; //PLC地址数组 List monitorNodeIds = new List() { addressConfig.In_DowmCameraExe.Address, addressConfig.In_UpCameraPickExe.Address, addressConfig.In_UpCameraPutExe.Address, addressConfig.In_UPCameracheckExe.Address, addressConfig.In_FixedCameraPickExe.Address, addressConfig.In_FixedCameraPutExe.Address, addressConfig.In_FixedCameracheckExe.Address, addressConfig.In_RequestPosition.Address, addressConfig.In_ScrewTeachExe.Address, addressConfig.In_QrCodePhotoRequest.Address, addressConfig.In_PickINC.Address, }; for (int i = 0; i < 10; i++) { monitorNodeIds.Add(string.Format(addressConfig.In_MesCheck.Address, i)); } for (int i = 0; i < 7; i++) { monitorNodeIds.Add(string.Format(addressConfig.In_WorkStart.Address, i)); monitorNodeIds.Add(string.Format(addressConfig.In_WorkDone.Address, i)); } //订阅PLC地址变化 plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger); } /// /// 写入点位信息给PLC /// /// public async Task WritePositionToPlcAsync() { var addressConfig = _configService.GetPlcAddresses(); var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC; if (plc == null || !plc.IsConnected) return; var current = _productService.GetCurrentProduct(); if (current == null) return; if (current.PickCameraPoints != null && current.PickCameraPoints.Count > 0) { foreach (var item in current.PickCameraPoints) { await item.WriteToPlcAddress(addressConfig.Out_PickCamera, plc); } } if (current.PickPoints != null && current.PickPoints.Count > 0) { foreach (var item in current.PickPoints) { await item.WriteToPlcAddress(addressConfig.Out_Pick, plc); } } if (current.SecPosCameraPoints != null && current.SecPosCameraPoints.Count > 0) { foreach (var item in current.SecPosCameraPoints) { await item.WriteToPlcAddress(addressConfig.Out_SecPosCamera, plc); } } if (current.ScrewCameraPoints != null && current.ScrewCameraPoints.Count > 0) { foreach (var item in current.ScrewCameraPoints) { await item.WriteToPlcAddress(addressConfig.Out_ScrewCamera, plc); } } if (current.ScrewPoints != null && current.ScrewPoints.Count > 0) { foreach (var item in current.ScrewPoints) { await item.WriteToPlcAddressEx(addressConfig.Out_Screw, plc); } await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)current.ScrewPoints.Count); } if (current.CheckCameraPoints != null && current.CheckCameraPoints.Count > 0) { foreach (var item in current.CheckCameraPoints) { await item.WriteToPlcAddress(addressConfig.Out_CheckCamera, plc); } } if (current.RemovePoints != null && current.RemovePoints.Count > 0) { foreach (var item in current.RemovePoints) { await item.WriteToPlcAddress(addressConfig.Out_RemovePos, plc); } } if (current.PressurePlacePoints != null && current.PressurePlacePoints.Count > 0) { foreach (var item in current.PressurePlacePoints) { await item.WriteToPlcAddress(addressConfig.Out_PressurePlace, plc); } } if (current.IndependentCamerPoints != null && current.IndependentCamerPoints.Count > 0) { foreach (var item in current.IndependentCamerPoints) { await item.WriteToPlcAddress(addressConfig.Out_IndependentCamer, plc); } } if (current.QrCodePoints != null && current.QrCodePoints.Count > 0) { foreach (var item in current.QrCodePoints) { await item.WriteToPlcAddress(addressConfig.Out_QrCodeCamera, plc); } } } public async Task WriteParameterToPlcAsync() { var addressConfig = _configService.GetPlcAddresses(); var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC; if (plc == null || !plc.IsConnected) return; try { if (!File.Exists(FilePath.PressureParamterConfigPath)) { List models = new List(); models.Add(new PressureModel()); models.Add(new PressureModel()); models.Add(new PressureModel()); FileHelper.WriteJsonFile(models, FilePath.PressureParamterConfigPath); } var PressureParam = FileHelper.ReadJsonFile>(FilePath.PressureParamterConfigPath); await plc.WriteNodeAsync(addressConfig.PUT_Down_Time.Address, PressureParam[0].PressureTime); await plc.WriteNodeAsync(addressConfig.PUT_WetOut_Num.Address, PressureParam[0].PressureNum); await plc.WriteNodeAsync(addressConfig.PUT_WetOut_Num_max.Address, PressureParam[0].PressureNumMax); await plc.WriteNodeAsync(addressConfig.Down_Time.Address, PressureParam[1].PressureTime); await plc.WriteNodeAsync(addressConfig.Down_WetOut_Num.Address, PressureParam[1].PressureNum); await plc.WriteNodeAsync(addressConfig.Down_WetOut_Num_max.Address, PressureParam[1].PressureNumMax); } catch (Exception) { } } /// /// 固定向上相机拍照结果 /// OpenCvSharp.Point3f[] DowmCameraResults = new OpenCvSharp.Point3f[10]; /// /// 移动向下相机拍照结果 /// OpenCvSharp.Point3f[] UpCameraPutResults = new OpenCvSharp.Point3f[10]; /// /// 取料移动相机拍照结果 /// OpenCvSharp.Point3f UpCameraPickResult = new OpenCvSharp.Point3f(); /// /// 最终组装位置 /// OpenCvSharp.Point3f PlaceActualCoord = new OpenCvSharp.Point3f(); /// /// 零件SN /// string PartSN = ""; /// /// 产品SN /// string[] ProductMainSN = new string[10];//产品主sn /// /// 工单信息 /// string[] wo = new string[10]; /// /// 复检结果 /// string gapString = ""; /// /// 二次定位相机的工具坐标 /// ToolCoord DownTool = new ToolCoord(); double _FixedUpCameraAngle = 0; double _MoveCameraAngle = 0; bool _LastDowmCameraExe = false; bool _LastScrewTeachExe = false; bool _LastUpCameraPickExe = false; bool _LastUpCameraPutExe = false; bool _LastUPCameracheckExe = false; bool _LastFixedCameraPickExe = false; bool _LastFixedCameraPutExe = false; bool _LastFixedCameracheckExe = false; Int16[] _LastWorkStart = new Int16[7]; Int16 _LastQrCodePhotoRequest = 0; Int16[] _LastWorkDone = new Int16[7]; Int16 _LastRequestPosition = 0; Int16 _LastPickINC = 0; Int16[] _LastMesCheck = new Int16[10]; DateTime[] _LastMesCheckTime = new DateTime[10]; double[] currentPressure = new double[10];//当前压力 /// /// PLC命令触发时 /// /// private async void PlcCommandTrigger((string key, string nodeId, object value) tuple) { try { if (tuple.key != "AutoSensor") { return; } var addressConfig = _configService.GetPlcAddresses(); var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC; if (plc == null || !plc.IsConnected) return; // 获取当前产品 var currentProduct = _productService.GetCurrentProduct(); if (currentProduct == null) { return; } //获取产品编码 _productService.CurrentProductCode = plc.ReadNode(string.Format(addressConfig.In_Code.Address, 0)); //如果产品编码为空,则以当前的时间戳作为编码 if (string.IsNullOrEmpty(_productService.CurrentProductCode)) { _productService.CurrentProductCode = DateTime.Now.ToString("yyyyMMddHHmmssfff"); } bool isDryMode = plc.ReadNode(addressConfig.In_DryMode.Address); // 触发下相机拍照--下相机拍屏幕,预定位 if (addressConfig.In_DowmCameraExe.Address.Contains(tuple.nodeId)) { if (tuple.value is bool state) { if (_LastDowmCameraExe != state) { _LastDowmCameraExe = state; } else { return; } if (state) { SendTaskMessage($"触发下相机拍照...", MessageLevel.Debug); //相机的编号 Int16 cameraIndex = plc.ReadNode(addressConfig.In_DowmCameraNum.Address); SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info); // 获取机器人当前坐标 var robot = _robotService.GetAllRobots().FirstOrDefault(); if (robot == null) { SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } var currentPosition = robot.GetRobotPos(); //如果下相机拍照的次数是1,则判断用时拍照的相机是几个 int downCameraNum = _configService.GetSystemConfiguration().DowmCameraNum; if (downCameraNum == 1) { //如果下相机2的流程id为空,则表示只有一个下相机 if (currentProduct.FixedUpCamera2ProcedureId == Guid.Empty) { //获取固定向上相机1的流程 var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId); if (process == null) { SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } // 执行视觉任务 var _cts = new CancellationTokenSource(); var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, null); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1); return; } if (visionResult.IsSucceed) { //float toolX = (float)(visionResult.X - currentPosition.X); //float toolY = (float)(visionResult.Y - currentPosition.Y); DowmCameraResults[0] = new OpenCvSharp.Point3f((float)visionResult.X, (float)visionResult.Y, (float)visionResult.U); //DownTool = new PointF(toolX, toolY); //以LCD的的上边中心创建工具坐标 DownTool.ComputeTool(currentPosition.X, currentPosition.Y, currentPosition.U, DowmCameraResults[0].X, DowmCameraResults[0].Y); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1); return; } else { if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1); return; } await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } } else { //获取固定向上相机1的流程 var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId); if (process1 == null) { SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } //获取固定向上相机2的流程 var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera2ProcedureId); if (process2 == null) { SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera2ProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } // 执行视觉任务 var _cts = new CancellationTokenSource(); var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, null, _cts.Token, 1, null); var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, null, _cts.Token, 2, null); var visionResults = await Task.WhenAll(task1, task2); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1); return; } if (visionResults[0].IsSucceed && visionResults[1].IsSucceed) { float toolX = (float)(visionResults[0].X + visionResults[1].X) / 2; float toolY = (float)(visionResults[0].Y + visionResults[1].Y) / 2; _FixedUpCameraAngle = Common.CalculateAngleBetweenPointsDegrees(new PointF((float)visionResults[0].X, (float)visionResults[0].Y), new PointF((float)visionResults[1].X, (float)visionResults[1].Y)); SendTaskMessage($"屏幕中心点位:{toolX:F3},{toolY:F4},{_FixedUpCameraAngle:F4}", MessageLevel.Info); SendTaskMessage($"当前机器人点位:{currentPosition.X:F3},{currentPosition.Y:F4},{currentPosition.U:F4}", MessageLevel.Info); DowmCameraResults[0] = new OpenCvSharp.Point3f(toolX, toolY, (float)_FixedUpCameraAngle); //DownTool = new PointF(toolX, toolY); //以LCD的的上边中心创建工具坐标 DownTool.ComputeTool(currentPosition.X, currentPosition.Y, currentPosition.U, DowmCameraResults[0].X, DowmCameraResults[0].Y); SendTaskMessage($"屏幕Tool:{DownTool.X:F3},{DownTool.Y:F4}", MessageLevel.Info); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1); return; } else { await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } } } else if (downCameraNum == 2) { if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1); return; } //如果下相机2的流程id为空,则表示只有一个下相机 if (currentProduct.FixedUpCamera2ProcedureId == Guid.Empty) { //获取固定向上相机1的流程 //var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId); //if (process == null) //{ // SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm); // await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); // return; //} //// 执行视觉任务 //var _cts = new CancellationTokenSource(); //var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token); //if (visionResult.IsSucceed) //{ // float toolX = (float)(visionResult.X - currentPosition.X); // float toolY = (float)(visionResult.Y - currentPosition.Y); // DowmCameraResults[cameraIndex] = new PointF(toolX, toolX); // if (cameraIndex == downCameraNum) // { // //求取多个DowmCameraResults的中心点 // DownTool = new PointF(toolX, toolX); // } // await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1); // return; //} //else //{ // await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); // return; //} //暂时没有应用场景,不支持 await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } else { //获取固定向上相机1的流程 var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId); if (process1 == null) { SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } //获取固定向上相机2的流程 var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera2ProcedureId); if (process2 == null) { SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera2ProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } // 执行视觉任务 var _cts = new CancellationTokenSource(); var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 1, null); var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 2, null); var visionResults = await Task.WhenAll(task1, task2); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1); return; } if (visionResults[0].IsSucceed && visionResults[1].IsSucceed) { float toolX = (float)(visionResults[0].X + visionResults[1].X) / 2; float toolY = (float)(visionResults[0].Y + visionResults[1].Y) / 2; _FixedUpCameraAngle = Common.CalculateAngleBetweenPointsDegrees(new PointF((float)visionResults[0].X, (float)visionResults[0].Y), new PointF((float)visionResults[1].X, (float)visionResults[1].Y)); DowmCameraResults[cameraIndex] = new OpenCvSharp.Point3f(toolX, toolY, (float)_FixedUpCameraAngle); //DownTool = new PointF(toolX, toolY); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1); return; } else { await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } } } else { //暂时没有应用场景,不支持 await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } } else { SendTaskMessage($"触发下相机拍照信号已关闭", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)0); } } } // 触发下相机拍照披头 if (addressConfig.In_ScrewTeachExe.Address.Contains(tuple.nodeId)) { if (tuple.value is bool state) { if (_LastScrewTeachExe != state) { _LastScrewTeachExe = state; } else { return; } if (state) { SendTaskMessage($"触发下相机拍照...", MessageLevel.Debug); //相机的编号 Int16 cameraIndex = plc.ReadNode(addressConfig.In_DowmCameraNum.Address); SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info); // 获取流程 //var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId); //if (process == null) //{ // SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.FixedUpCamera1ProcedureId}", MessageLevel.Alarm); // await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2); // return; //} //// 获取机器人当前坐标 //var robot = _robotService.GetAllRobots().FirstOrDefault(); //if (robot == null) //{ // SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm); // await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2); // return; //} //var currentPosition = robot.GetRobotPos(); //// 执行视觉任务 //var _cts = new CancellationTokenSource(); //var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token); //if (visionResult.IsSucceed) //{ // //披头偏差 // float offsetX = (float)(visionResult.X - currentPosition.X); // float offsetY = (float)(visionResult.Y - currentPosition.Y); // DowmCameraResults[cameraIndex] = new PointF(offsetX, offsetY); // await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)1); // return; //} //else //{ // await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2); // return; //} } else { SendTaskMessage($"触发下相机拍照信号已关闭", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)0); } } } // 触发上相机取料拍照 else if (addressConfig.In_UpCameraPickExe.Address.Contains(tuple.nodeId)) { if (tuple.value is bool state) { if (_LastUpCameraPickExe != state) { _LastUpCameraPickExe = state; } else { return; } if (state) { SendTaskMessage($"触发上相机取料拍照...", MessageLevel.Debug); //相机的编号 Int16 cameraIndex = plc.ReadNode(addressConfig.In_UpCameraPickNum.Address); SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info); // 获取机器人当前坐标 var robot = _robotService.GetAllRobots().FirstOrDefault(); if (robot == null) { SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2); return; } var currentPosition = robot.GetRobotPos(); //如果上相机2的流程id为空,则表示只有一个下相机 if (currentProduct.MoveDownCamer2PickPhotoProcedureId == Guid.Empty) { //获取移动向下相机1的流程 var process = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamer1PickPhotoProcedureId); if (process == null) { SendTaskMessage($"移动向下相机1视觉流程,流程ID:{currentProduct.MoveDownCamer1PickPhotoProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2); return; } // 执行视觉任务 var _cts = new CancellationTokenSource(); var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, null); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1); return; } if (visionResult.IsSucceed) { currentProduct.PickPoints[0].X_Position = (float)visionResult.X; currentProduct.PickPoints[0].Y_Position = (float)visionResult.Y; //currentProduct.PickPoints[0].U_Position += (float)visionResult.U; await currentProduct.ScrewPoints[0].WriteToPlcAddress(addressConfig.Out_Pick, plc); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1); return; } else { await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2); return; } } else { //获取固定向上相机1的流程 var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamer1PickPhotoProcedureId); if (process1 == null) { SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.MoveDownCamer1PickPhotoProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2); return; } //获取固定向上相机2的流程 var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamer2PickPhotoProcedureId); if (process2 == null) { SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.MoveDownCamer2PickPhotoProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2); return; } // 执行视觉任务 var _cts = new CancellationTokenSource(); var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 1, null); var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 2, null); var visionResults = await Task.WhenAll(task1, task2); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1); return; } if (visionResults[0].IsSucceed && visionResults[1].IsSucceed) { float tool1X = (float)(visionResults[0].X - visionResults[0].X); float tool1Y = (float)(visionResults[0].Y - visionResults[0].Y); float tool2X = (float)(visionResults[1].X - visionResults[1].X); float tool2Y = (float)(visionResults[1].Y - visionResults[1].Y); float toolX = (tool1X + tool2X) / 2; float toolY = (tool1Y + tool2Y) / 2; //计算两个点的线角度 currentProduct.PickPoints[0].X_Position = (float)toolX; currentProduct.PickPoints[0].Y_Position = (float)toolY; //currentProduct.PickPoints[0].U_Position += (float)visionResult.U; await currentProduct.ScrewPoints[0].WriteToPlcAddress(addressConfig.Out_Pick, plc); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1); return; } else { await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)2); return; } } } else { SendTaskMessage($"触发上相机取料拍照信号已关闭", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)0); } } } // 上相机锁附/组装拍照 else if (addressConfig.In_UpCameraPutExe.Address.Contains(tuple.nodeId)) { if (tuple.value is bool state) { if (_LastUpCameraPutExe != state) { _LastUpCameraPutExe = state; } else { return; } if (state) { SendTaskMessage($"触发上相机锁附/组装拍照...", MessageLevel.Debug); //相机的编号 Int16 cameraIndex = plc.ReadNode(addressConfig.In_UpCameraPutNum.Address); SendTaskMessage($"编号:{cameraIndex}", MessageLevel.Info); // 获取机器人当前坐标 var robot = _robotService.GetAllRobots().FirstOrDefault(); if (robot == null) { SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2); return; } var currentPosition = robot.GetRobotPos(); //如果上相机2的流程id为空,则表示只有一个上相机 if (currentProduct.MoveDownCamera2LockPhotoProcedureId == Guid.Empty) { //获取移动向下相机1的流程 var process = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamera1LockPhotoProcedureId); if (process == null) { SendTaskMessage($"移动向下相机1视觉流程,流程ID:{currentProduct.MoveDownCamera1LockPhotoProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2); return; } // 执行视觉任务 var _cts = new CancellationTokenSource(); var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, null); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1); return; } if (visionResult.IsSucceed) { UpCameraPutResults[cameraIndex] = new OpenCvSharp.Point3f((float)visionResult.X, (float)visionResult.Y, (float)visionResult.U); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1); return; } else { await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2); return; } } else { //获取移动向下相机1的流程 var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamera1LockPhotoProcedureId); if (process1 == null) { SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.MoveDownCamera1LockPhotoProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2); return; } //获取移动向下相机2的流程 var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCamera2LockPhotoProcedureId); if (process2 == null) { SendTaskMessage($"固定向上相机1视觉流程,流程ID:{currentProduct.MoveDownCamera2LockPhotoProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2); return; } // 执行视觉任务 var _cts = new CancellationTokenSource(); var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 1, null); var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, 2, null); var visionResults = await Task.WhenAll(task1, task2); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1); return; } if (visionResults[0].IsSucceed && visionResults[1].IsSucceed) { float centerX = (float)(visionResults[0].X + visionResults[1].X) / 2; float centerY = (float)(visionResults[0].Y + visionResults[1].Y) / 2; _MoveCameraAngle = Common.CalculateAngleBetweenPointsDegrees(new PointF((float)visionResults[0].X, (float)visionResults[0].Y), new PointF((float)visionResults[1].X, (float)visionResults[1].Y)); UpCameraPutResults[cameraIndex] = new OpenCvSharp.Point3f(centerX, centerY, (float)_MoveCameraAngle); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1); return; } else { await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2); return; } } } else { SendTaskMessage($"触发上相机锁附/组装拍照信号已关闭", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)0); } } } // 上相机检测拍照 else if (addressConfig.In_UPCameracheckExe.Address.Contains(tuple.nodeId)) { if (tuple.value is bool state) { if (_LastUPCameracheckExe != state) { _LastUPCameracheckExe = state; } else { return; } if (state) { SendTaskMessage($"触发上相机检测拍照...", MessageLevel.Debug); //相机的编号 Int16 cameraIndex = plc.ReadNode(addressConfig.In_UPCameracheckNum.Address); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)1); return; } //视觉流程ID Guid procedureId = Guid.Empty; //获取锁付拍照的点位 var pos = currentProduct.CheckCameraPoints.FirstOrDefault(p => p.Number == cameraIndex); if (pos != null) { procedureId = pos.VisionProcessID; } // 获取流程 var process = _productService.GetCurrentProductProcedureModelById(procedureId); if (process == null) { SendTaskMessage(string.Format("未找到点位:{0}的视觉流程:{1}", cameraIndex, procedureId), MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)2); return; } // 执行视觉任务 var _cts = new CancellationTokenSource(); var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, cameraIndex, null); if (visionResult.IsSucceed) { await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)1); SendTaskMessage($"点位{cameraIndex}检测OK", MessageLevel.Debug); } else { await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)2); SendTaskMessage($"点位{cameraIndex}检测NG", MessageLevel.Error); } await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)1); } else { SendTaskMessage($"触发上相机检测拍照信号已关闭", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)0); } } } // 固定相机取料拍照--屏幕拍照,取上边沿中心(防呆作用) else if (addressConfig.In_FixedCameraPickExe.Address.Contains(tuple.nodeId)) { if (tuple.value is bool state) { if (_LastFixedCameraPickExe != state) { _LastFixedCameraPickExe = state; } else { return; } if (state) { SendTaskMessage($"触发固定相机取料拍照...", MessageLevel.Debug); //相机的编号 //Int16 cameraIndex = plc.ReadNode(addressConfig.In_FixedCameraPickNum.Address); if (currentProduct.IndependentCamer1AuxProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } if (currentProduct.IndependentCamer2AuxProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } if (currentProduct.IndependentCamer3AuxProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } if (currentProduct.IndependentCamer4AuxProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } //获取四个独立相机的流程id var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer1AuxProcedureId); var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer2AuxProcedureId); var process3 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer3AuxProcedureId); var process4 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer4AuxProcedureId); // 执行视觉任务 var _cts = new CancellationTokenSource(); var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, null, _cts.Token, 1, null); var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, null, _cts.Token, 2, null); var task3 = _remoteCommandService.ExecutePhotoGetSinglePoint(process3, null, null, _cts.Token, 3, null); var task4 = _remoteCommandService.ExecutePhotoGetSinglePoint(process4, null, null, _cts.Token, 4, null); var visionResults = await Task.WhenAll(task1, task2, task3, task4); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)1); return; } if (visionResults[0].IsSucceed && visionResults[1].IsSucceed && visionResults[2].IsSucceed && visionResults[3].IsSucceed) { Vector p1 = Vector.Build.Dense(new double[] { visionResults[0].X, visionResults[0].Y }); Vector p2 = Vector.Build.Dense(new double[] { visionResults[1].X, visionResults[1].Y }); Vector p3 = Vector.Build.Dense(new double[] { visionResults[2].X, visionResults[2].Y }); Vector p4 = Vector.Build.Dense(new double[] { visionResults[3].X, visionResults[3].Y }); //var center = RectangleCenterCalculator.CalculateByDirectMethod(new List> { p1, p2, p3, p4 }); var center = RectangleCenterCalculator.CalculateOptimalCenter(new List> { p1, p2, p3, p4 });//0215修改,不考虑点顺序 //UpCameraPutResults[1].X = (float)center.Center[0]; //UpCameraPutResults[1].Y = (float)center.Center[1]; //UpCameraPutResults[1].Z = (float)center.MajorAxisAngle; UpCameraPutResults[1].X = ((float)center.TopLeft[0] + (float)center.TopRight[0]) / 2; UpCameraPutResults[1].Y = ((float)center.TopLeft[1] + (float)center.TopRight[1]) / 2; UpCameraPutResults[1].Z = (float)center.MajorAxisAngle; SendTaskMessage($"LCD拍照结果:X={UpCameraPutResults[1].X},Y={UpCameraPutResults[1].Y},Angle={UpCameraPutResults[1].Z}", MessageLevel.Info); var dist1 = RectangleCenterCalculator.Distance(center.TopLeft, center.TopRight);//长1 var dist2 = RectangleCenterCalculator.Distance(center.TopRight, center.BottomRight);//宽1 var dist3 = RectangleCenterCalculator.Distance(center.BottomLeft, center.BottomRight);//长2 var dist4 = RectangleCenterCalculator.Distance(center.TopLeft, center.BottomLeft);//宽2 LogHelper.WriteLogInfo($"dist1={dist1};dist2={dist2};dist3={dist3};dist4={dist4};"); if (currentProduct.GlobalParamListCollection.Count >= 4) { var param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 2); if (param != null)//长 { var length = Double.Parse(param.Param1); var limtDown = Double.Parse(param.Param2); var LimtUp = Double.Parse(param.Param3); if (!(((length + limtDown) <= dist1) && (dist1 <= (length + LimtUp)) && (((length + limtDown) <= dist3) && (dist3 <= (length + LimtUp))))) { SendTaskMessage($"屏幕长度超范围:长1={dist1},长2={dist3},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } SendTaskMessage($"屏幕长度防呆:长1={dist1},长2={dist3},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Info); } param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 3); if (param != null)//宽 { var length = Double.Parse(param.Param1); var limtDown = Double.Parse(param.Param2); var LimtUp = Double.Parse(param.Param3); if (!(((length + limtDown) <= dist2) && (dist2 <= (length + LimtUp)) && (((length + limtDown) <= dist4) && (dist4 <= (length + LimtUp))))) { SendTaskMessage($"屏幕宽度超范围:宽1={dist2},宽2={dist4},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } SendTaskMessage($"屏幕宽度防呆:宽1={dist2},宽2={dist4},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Info); } if (currentProduct.GlobalParamListCollection.Count >= 7) { param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 4); if (param != null)//X防呆 { var Limt_val = Double.Parse(param.Param1); var limtDown = Double.Parse(param.Param2); var LimtUp = Double.Parse(param.Param3); var dist = UpCameraPutResults[1].X - UpCameraPutResults[0].X; if (!(((Limt_val + limtDown) <= dist) && (dist <= (Limt_val + LimtUp)))) { SendTaskMessage($"贴合X防呆超限:X实际差值={dist},X允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info); await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } SendTaskMessage($"贴合X防呆:X实际差值={dist},X允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info); } param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 5); if (param != null)//Y防呆 { var Limt_val = Double.Parse(param.Param1); var limtDown = Double.Parse(param.Param2); var LimtUp = Double.Parse(param.Param3); var dist = UpCameraPutResults[1].Y - UpCameraPutResults[0].Y; if (!(((Limt_val + limtDown) <= dist) && (dist <= (Limt_val + LimtUp)))) { SendTaskMessage($"贴合Y防呆超限:Y实际差值={dist},Y允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info); await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } SendTaskMessage($"贴合Y防呆:Y实际差值={dist},Y允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info); } param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 6); if (param != null)//U防呆 { var Limt_val = Double.Parse(param.Param1); var limtDown = Double.Parse(param.Param2); var LimtUp = Double.Parse(param.Param3); var dist = UpCameraPutResults[1].Z - UpCameraPutResults[0].Z; if (!(((Limt_val + limtDown) <= dist) && (dist <= (Limt_val + LimtUp)))) { SendTaskMessage($"贴合U防呆超限:U实际差值={dist},X允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info); await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } SendTaskMessage($"贴合U防呆:U实际差值={dist},X允许差值={Limt_val},下限={limtDown},上限={LimtUp}", MessageLevel.Info); } } } } else { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2); return; } await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)1); } else { SendTaskMessage($"触发固定相机取料拍照信号已关闭", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)0); } } } // 固定相机锁附/组装拍照--外壳拍照 else if (addressConfig.In_FixedCameraPutExe.Address.Contains(tuple.nodeId)) { if (tuple.value is bool state) { if (_LastFixedCameraPutExe != state) { _LastFixedCameraPutExe = state; } else { return; } if (state) { SendTaskMessage($"触发固定相机锁附/组装拍照...", MessageLevel.Debug); //相机的编号 //Int16 cameraIndex = plc.ReadNode(addressConfig.In_FixedCameraPutNum.Address); if (currentProduct.IndependentCamer1ProductProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2); return; } if (currentProduct.IndependentCamer2ProductProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2); return; } if (currentProduct.IndependentCamer3ProductProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2); return; } if (currentProduct.IndependentCamer4ProductProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2); return; } //获取四个独立相机的流程id var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer1ProductProcedureId); var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer2ProductProcedureId); var process3 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer3ProductProcedureId); var process4 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer4ProductProcedureId); // 执行视觉任务 var _cts = new CancellationTokenSource(); var task1 = _remoteCommandService.ExecutePhotoGetSinglePoint(process1, null, null, _cts.Token, 1, null); var task2 = _remoteCommandService.ExecutePhotoGetSinglePoint(process2, null, null, _cts.Token, 2, null); var task3 = _remoteCommandService.ExecutePhotoGetSinglePoint(process3, null, null, _cts.Token, 3, null); var task4 = _remoteCommandService.ExecutePhotoGetSinglePoint(process4, null, null, _cts.Token, 4, null); var visionResults = await Task.WhenAll(task1, task2, task3, task4); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)1); return; } if (visionResults[0].IsSucceed && visionResults[1].IsSucceed && visionResults[2].IsSucceed && visionResults[3].IsSucceed) { Vector p1 = Vector.Build.Dense(new double[] { visionResults[0].X, visionResults[0].Y }); Vector p2 = Vector.Build.Dense(new double[] { visionResults[1].X, visionResults[1].Y }); Vector p3 = Vector.Build.Dense(new double[] { visionResults[2].X, visionResults[2].Y }); Vector p4 = Vector.Build.Dense(new double[] { visionResults[3].X, visionResults[3].Y }); //var center = RectangleCenterCalculator.CalculateByDirectMethod(new List> { p1, p2, p3, p4 }); var center = RectangleCenterCalculator.CalculateOptimalCenter(new List> { p1, p2, p3, p4 });//0215修改,不考虑点顺序 //UpCameraPutResults[0].X = (float)center.Center[0]; //UpCameraPutResults[0].Y = (float)center.Center[1]; UpCameraPutResults[0].X = ((float)center.TopLeft[0] + (float)center.TopRight[0]) / 2; UpCameraPutResults[0].Y = ((float)center.TopLeft[1] + (float)center.TopRight[1]) / 2; UpCameraPutResults[0].Z = (float)center.MajorAxisAngle; SendTaskMessage($"产品拍照结果:X={UpCameraPutResults[0].X},Y={UpCameraPutResults[0].Y},Angle={UpCameraPutResults[0].Z}", MessageLevel.Info); var dist1 = RectangleCenterCalculator.Distance(center.TopLeft, center.TopRight);//长1 var dist2 = RectangleCenterCalculator.Distance(center.TopRight, center.BottomRight);//宽1 var dist3 = RectangleCenterCalculator.Distance(center.BottomLeft, center.BottomRight);//长2 var dist4 = RectangleCenterCalculator.Distance(center.TopLeft, center.BottomLeft);//宽2 LogHelper.WriteLogInfo($"dist1={dist1};dist2={dist2};dist3={dist3};dist4={dist4};"); if (currentProduct.GlobalParamListCollection.Count >= 4) { var param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 0); if (param != null)//长 { var length = Double.Parse(param.Param1); var limtDown = Double.Parse(param.Param2); var LimtUp = Double.Parse(param.Param3); if (!(((length + limtDown) <= dist1) && (dist1 <= (length + LimtUp)) && (((length + limtDown) <= dist3) && (dist3 <= (length + LimtUp))))) { SendTaskMessage($"外壳长度超范围:长1={dist1},长2={dist3},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2); return; } SendTaskMessage($"外壳长度防呆:长1={dist1},长2={dist3},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Info); } param = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Number == 1); if (param != null)//宽 { var length = Double.Parse(param.Param1); var limtDown = Double.Parse(param.Param2); var LimtUp = Double.Parse(param.Param3); if (!(((length + limtDown) <= dist2) && (dist2 <= (length + LimtUp)) && (((length + limtDown) <= dist4) && (dist4 <= (length + LimtUp))))) { SendTaskMessage($"外壳宽度超范围:宽1={dist2},宽2={dist4},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2); return; } SendTaskMessage($"外壳宽度防呆:宽1={dist2},宽2={dist4},para1={length},para2={limtDown},para3={LimtUp}", MessageLevel.Info); } } } else { await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)2); return; } await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)1); } else { SendTaskMessage($"触发固定相机锁附/组装拍照信号已关闭", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)0); } } } // 固定相机检测拍照 else if (addressConfig.In_FixedCameracheckExe.Address.Contains(tuple.nodeId)) { if (tuple.value is bool state) { if (_LastFixedCameracheckExe != state) { _LastFixedCameracheckExe = state; } else { return; } if (state) { SendTaskMessage($"触发固定相机检测拍照...", MessageLevel.Debug); //相机的编号 //Int16 cameraIndex = plc.ReadNode(addressConfig.In_FixedCameracheckNum.Address); if (currentProduct.IndependentCamer1CheckProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2); return; } if (currentProduct.IndependentCamer2CheckProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2); return; } if (currentProduct.IndependentCamer3CheckProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2); return; } if (currentProduct.IndependentCamer4CheckProcedureId == Guid.Empty) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2); return; } //获取四个独立相机的流程id var process1 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer1CheckProcedureId); var process2 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer2CheckProcedureId); var process3 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer3CheckProcedureId); var process4 = _productService.GetCurrentProductProcedureModelById(currentProduct.IndependentCamer4CheckProcedureId); // 执行视觉任务(复检结果gap) var _cts = new CancellationTokenSource(); var task1 = _remoteCommandService.ExecutePhotoGetDistance(process1, null, null, _cts.Token, 1, null); var task2 = _remoteCommandService.ExecutePhotoGetDistance(process2, null, null, _cts.Token, 2, null); var task3 = _remoteCommandService.ExecutePhotoGetDistance(process3, null, null, _cts.Token, 3, null); var task4 = _remoteCommandService.ExecutePhotoGetDistance(process4, null, null, _cts.Token, 4, null); var visionResults = await Task.WhenAll(task1, task2, task3, task4); if (isDryMode) { await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)1); return; } if (visionResults[0].IsSucceed && visionResults[1].IsSucceed && visionResults[2].IsSucceed && visionResults[3].IsSucceed) { SendTaskMessage($"固定相机检测OK", MessageLevel.Debug); SendTaskMessage($"流程1结果:{visionResults[0].distance}", MessageLevel.Debug); SendTaskMessage($"流程2结果:{visionResults[1].distance}", MessageLevel.Debug); SendTaskMessage($"流程3结果:{visionResults[2].distance}", MessageLevel.Debug); SendTaskMessage($"流程4结果:{visionResults[3].distance}", MessageLevel.Debug); gapString = $"{visionResults[0].distance.Split(',')[1]},{visionResults[1].distance.Split(',')[1]},{visionResults[2].distance.Split(',')[1]},{visionResults[3].distance.Split(',')[1]}"; await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)1); return; } else { string res1="", res2 = "", res3 = "", res4 = ""; if(visionResults[0].IsSucceed) { res1 = visionResults[0].distance.Split(',')[1]; } if (visionResults[1].IsSucceed) { res2 = visionResults[1].distance.Split(',')[1]; } if (visionResults[2].IsSucceed) { res3 = visionResults[2].distance.Split(',')[1]; } if (visionResults[3].IsSucceed) { res4 = visionResults[3].distance.Split(',')[1]; } SendTaskMessage($"固定相机检测NG", MessageLevel.Error); gapString = $"{res1},{res2},{res3},{res4}"; await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)2); return; } } else { SendTaskMessage($"触发固定相机检测拍照信号已关闭", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)0); } } } // 多相机坐标合并 else if (addressConfig.In_RequestPosition.Address.Contains(tuple.nodeId)) { if (tuple.value is Int16 state) { if (_LastRequestPosition != state) { _LastRequestPosition = state; } else { return; } if (state == 1) { SendTaskMessage($"收到多相机坐标合并请求,开始合并...", MessageLevel.Debug); if (isDryMode)//空跑模式 { var point1 = currentProduct.ScrewPoints[0].Clone(); SendTaskMessage($"空跑模式点:1,{point1.X_Position:F3},{point1.Y_Position:F4},{point1.U_Position:F4}", MessageLevel.Info); await point1.WriteToPlcAddress(addressConfig.Out_Screw, plc); await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)1); await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)1); return; } // 获取机器人当前坐标 var robot = _robotService.GetAllRobots().FirstOrDefault(); if (robot == null) { SendTaskMessage($"未找到机器人,无法执行上相机锁附/组装拍照视觉任务", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } var currentPosition = robot.GetRobotPos(); //已经通过下相机拍屏幕两角获取--DownTool //以LCD的中线创建工具坐标 //ToolCoord toolCoord = new ToolCoord(); //toolCoord.ComputeTool(currentPosition.X, currentPosition.Y, currentPosition.U, UpCameraPutResults[1].X, UpCameraPutResults[1].Y); //添加新的补偿算法--20260213 double offset_X = 0, offset_Y = 0, offset_U = 0; var current = _productService.GetCurrentProduct(); if (current.ScrewPoints == null || current.ScrewPoints.Count <= 0) { SendTaskMessage($"未找到任务贴合补偿参数", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2); return; } offset_X = current.ScrewPoints[0].X_Offset; offset_Y = current.ScrewPoints[0].Y_Offset; offset_U = current.ScrewPoints[0].U_Offset; SendTaskMessage($"放料补偿:X={offset_X},Y={offset_Y},U={offset_U}", MessageLevel.Info); //利用补偿偏差算出,补偿后的壳体中心,p_new ToolCoord toolCoord2 = new ToolCoord(); toolCoord2.SetTool(offset_X, offset_Y); var p_new_Vector = Vector.Build.Dense(new double[] { UpCameraPutResults[0].X, UpCameraPutResults[0].Y }); var p_new = toolCoord2.GetToolnCoord(p_new_Vector, UpCameraPutResults[0].Z); SendTaskMessage($"放料原点:1,{UpCameraPutResults[0].X:F3},{UpCameraPutResults[0].Y:F4},{UpCameraPutResults[0].Z:F4}", MessageLevel.Info); SendTaskMessage($"放料补偿点:2,{p_new[0]:F3},{p_new[1]:F4},{UpCameraPutResults[0].Z:F4}", MessageLevel.Info); //计算放料角度--带入下相机的角度 double angle = currentPosition.U - DowmCameraResults[0].Z + UpCameraPutResults[0].Z + offset_U; //将产品的中心点坐标转换为Tool0下的坐标 var point = currentProduct.ScrewPoints[0].Clone(); //var pn = Vector.Build.Dense(new double[] { UpCameraPutResults[0].X, UpCameraPutResults[0].Y }); var pn = p_new; //var p0 = toolCoord.GetTool0Coord(pn, angle); var p0 = DownTool.GetTool0Coord(pn, angle);//下相机拍照tool,动态Tool point.X_Position = (float)p0[0]; point.Y_Position = (float)p0[1]; point.U_Position = (float)angle; PlaceActualCoord = new OpenCvSharp.Point3f(point.X_Position, point.Y_Position, point.U_Position); //SendTaskMessage($"点:1,{point.X_Position:F3},{point.Y_Position:F4},{point.U_Position:F4}", MessageLevel.Info); SendTaskMessage($"目标点:3,{point.X_Position:F3},{point.Y_Position:F4},{point.U_Position:F4}", MessageLevel.Info); await point.WriteToPlcAddressEx2(addressConfig.Out_Screw, plc);//补偿参数提前代入计算,直接发最终点位 await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)1); await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)1); //原计算方法--20260213 ////计算放料角度 //double angle = currentPosition.U - UpCameraPutResults[1].Z + UpCameraPutResults[0].Z; ////将产品的中心点坐标转换为Tool0下的坐标 //var point = currentProduct.ScrewPoints[0].Clone(); //var pn = Vector.Build.Dense(new double[] { UpCameraPutResults[0].X, UpCameraPutResults[0].Y }); //var p0 = toolCoord.GetTool0Coord(pn, angle); //point.X_Position = (float)p0[0]; //point.Y_Position = (float)p0[1]; //point.U_Position = (float)angle; //PlaceActualCoord = new OpenCvSharp.Point3f(point.X_Position, point.Y_Position, point.U_Position); //SendTaskMessage($"点:1,{point.X_Position:F3},{point.Y_Position:F4},{point.U_Position:F4}", MessageLevel.Info); //await point.WriteToPlcAddress(addressConfig.Out_Screw, plc); //await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)1); //await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)1); } else { SendTaskMessage($"多相机坐标合并请求已结束或被取消,state={state}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)0); } } } // 螺丝供料器螺丝递减 else if (addressConfig.In_PickINC.Address.Contains(tuple.nodeId)) { if (tuple.value is Int16 state) { if (_LastPickINC != state) { _LastPickINC = state; } else { return; } if (state != 0) { SendTaskMessage($"螺丝供料器...", MessageLevel.Info); await plc.WriteNodeAsync(addressConfig.In_PickINC.Address, (Int16)0); } } } // PLC请求二维码拍照 else if (addressConfig.In_QrCodePhotoRequest.Address.Contains(tuple.nodeId)) { if (tuple.value is Int16 state) { if (_LastQrCodePhotoRequest != state) { _LastQrCodePhotoRequest = state; } else { return; } if (state == 1) { //相机的编号 //Int16 cameraIndex = plc.ReadNode(addressConfig.In_QrCodeCamera_ScanNum.Address); //SendTaskMessage(string.Format("扫码编号", cameraIndex), MessageLevel.Info); Guid qrCodeProcedureId = currentProduct.QrCodeProcedureId; //视觉流程ID var qrCodeProcess = _productService.GetCurrentProductProcedureModelById(qrCodeProcedureId); if (qrCodeProcess == null) { SendTaskMessage($"未找到二维码视觉流程,流程ID: {qrCodeProcedureId}", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2); return; } SendTaskMessage($"开始执行二维码扫码视觉流程", MessageLevel.Debug); var _cts = new CancellationTokenSource(); await _remoteCommandService.SetLightBeforePhoto(qrCodeProcess); var qrCodeResult = _remoteCommandService.ExecutePhoto(1, qrCodeProcess, out CogToolBlockTerminalCollection outputCollection); if (qrCodeResult && outputCollection.Contains("QR")) { PartSN = outputCollection["QR"].Value as string; if (!string.IsNullOrEmpty(PartSN)) { await plc.WriteNodeAsync(addressConfig.Out_PartCode.Address, PartSN); SendTaskMessage($"二维码扫码成功:{PartSN}", MessageLevel.Info); await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)1); } else { PartSN = ""; SendTaskMessage("二维码扫码失败", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2); return; } } else { PartSN = ""; SendTaskMessage("二维码扫码失败", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2); return; } } else { SendTaskMessage("二维码拍照点位发送信号已关闭", MessageLevel.Alarm); await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)0); } } } else { // 过站检查 for (int i = 0; i < 10; i++) { if (string.Format(addressConfig.In_MesCheck.Address, i).Contains(tuple.nodeId)) { if (tuple.value is Int16 state) { if (_LastMesCheck[i] != state) { _LastMesCheck[i] = state; } else { return; } var code = plc.ReadNode(string.Format(addressConfig.In_Code.Address, i)); string comp = PartSN; SendTaskMessage($"产品sn:{code},辅料码:{comp}", MessageLevel.Info); //询问mes if (state == 1) { // 这里缺少从MES获取产品配方的内容,需要补充 _LastMesCheckTime[i] = (await _mesService.GetServerTimeAsync(i, code, 2)).Now; //获取当前工位的配置 var stationConfig = _configService.GetDeviceInfo(i); if (stationConfig.EnableDoubleMes) { SendTaskMessage($"收到{i}:{stationConfig.DeviceName}过站检查请求(当站是否生产)(当前需要双MES验证)...", MessageLevel.Debug); LogHelper.WriteLogMes($"【mes开始QUERY】"); SendTaskMessage($"mes开始QUERY", MessageLevel.Info); (bool isSuccess, string response) = await _mesService.StationGetExAsync(i, code, "", 2);//询问mes if (isSuccess) { LogHelper.WriteLogMes($"询问mes成功"); SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug); string ProductMainSN_Temp = ""; if (response != "MES功能未启用!") { string[] item = response.Split('\n'); //获取过度件sn string snItem = item.FirstOrDefault(f => f.Contains("sn=")); if (snItem != null) { ProductMainSN_Temp = snItem.Replace("sn=", "").Trim(); SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info); } } (bool isSuccess2, string response2) = await _mesService.StationGetAsync(i, ProductMainSN_Temp, "", 2);//询问mes if (isSuccess2) { LogHelper.WriteLogMes($"第二次询问mes成功"); SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug); if (response2 != "MES功能未启用!") { string[] item = response2.Split('\n'); //获取产品主sn和wo用于后续上传数据使用 string snItem = item.FirstOrDefault(f => f.Contains("sn=")); if (snItem != null) { ProductMainSN[i] = snItem.Replace("sn=", "").Trim(); if (i == 0) { _productService.CurrentProductCode = ProductMainSN[i]; } SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info); } string woItem = item.FirstOrDefault(f => f.Contains("wo=")); if (woItem != null) { wo[i] = woItem.Replace("wo=", "").Trim(); SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info); } } //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改 await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变 await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1); } else { LogHelper.WriteLogMes($"第二次询问mes失败"); SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error); //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改 await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变 await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2); } } else { LogHelper.WriteLogMes($"询问mes失败"); SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error); //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改 await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变 await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2); } LogHelper.WriteLogMes($"【mes结束QUERY】"); SendTaskMessage($"mes结束QUERY", MessageLevel.Info); } else { SendTaskMessage($"收到{i}:{stationConfig.DeviceName}过站检查请求(当站是否生产)(当前不需要双MES验证)...", MessageLevel.Debug); LogHelper.WriteLogMes($"【mes开始QUERY】"); SendTaskMessage($"mes开始QUERY", MessageLevel.Info); (bool isSuccess, string response) = await _mesService.StationGetAsync(i, code, "", 2);//询问mes if (isSuccess) { if (response != "MES功能未启用!") { string[] item = response.Split('\n'); //获取产品主sn和wo用于后续上传数据使用 string snItem = item.FirstOrDefault(f => f.Contains("sn=")); if (snItem != null) { ProductMainSN[i] = snItem.Replace("sn=", "").Trim(); if (i == 0) { _productService.CurrentProductCode = ProductMainSN[i]; } SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info); } string woItem = item.FirstOrDefault(f => f.Contains("wo=")); if (woItem != null) { wo[i] = woItem.Replace("wo=", "").Trim(); SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info); } } else { ProductMainSN[i] = DateTime.Now.ToString(); if (i == 0) { _productService.CurrentProductCode = ProductMainSN[i]; } } LogHelper.WriteLogMes($"询问mes成功"); SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug); //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改 await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变 await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1); } else { LogHelper.WriteLogMes($"询问mes失败"); SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error); //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改 await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变 await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2); } LogHelper.WriteLogMes($"【mes结束QUERY】"); SendTaskMessage($"mes结束QUERY", MessageLevel.Info); } } // 上传本站数据 else if (state == 2) { DFC.Clear(); //获取当前工位的配置 var stationConfig = _configService.GetDeviceInfo(i); SendTaskMessage($"收到{i}:{stationConfig.DeviceName}上传本站数据请求...", MessageLevel.Debug); //从服务器中获取当前时间 var serverTimeResult = await _mesService.GetServerTimeAsync(i, code, 2); double ct = serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds; if (i == 0) { CTtime = ct; } //从PLC获取零件编号 PartSN = plc.ReadNode(addressConfig.In_PartCode.Address); comp = PartSN; //读取产品结果 Int16 resultIndex = plc.ReadNode(string.Format(addressConfig.In_Result.Address, i)); resultIndex = 1; //读取压力值 float pressure = plc.ReadNode(string.Format(addressConfig.In_PressureValue.Address, i)); AssemblyRecord assemblyRecord = new AssemblyRecord(); assemblyRecord.ProductSN = code; assemblyRecord.RecipeName = currentProduct.Name; assemblyRecord.PartSN = PartSN; assemblyRecord.AssemblyDurationSeconds = ct; assemblyRecord.AssemblyPressure = pressure; assemblyRecord.DeviceId = i; assemblyRecord.DeviceName = stationConfig.DeviceName; assemblyRecord.OperatorName = _systemDatabaseService.GetCurrentUser().UserName; assemblyRecord.Success = resultIndex == 1 ? true : false; assemblyRecord.DownCameraCoord1 = ""; assemblyRecord.DownCameraCoord2 = ""; assemblyRecord.DownCameraCoord3 = ""; assemblyRecord.DownCameraCoord4 = ""; assemblyRecord.PickMoveCameraCoord = ""; assemblyRecord.PlaceMoveCameraCoord1 = ""; assemblyRecord.PlaceMoveCameraCoord2 = ""; assemblyRecord.PlaceMoveCameraCoord3 = ""; assemblyRecord.PlaceMoveCameraCoord4 = ""; assemblyRecord.PlaceActualCoord = ""; //目前只记录当前站 if (i == 0) { await _systemDatabaseService.RecordProductionAsync(new ProductionRecord() { ProductName = currentProduct.Name, ProductCode = code, DeviceId = i, DeviceName = "", Category = resultIndex == 1 ? "OK" : "NG", Quantity = 1, Remark = $"TotalSeconds:{ct.ToString("F2")}", UserName = _systemDatabaseService.GetCurrentUser().UserName, }); assemblyRecord.PlaceMoveCameraCoord1 = $"{UpCameraPutResults[0].X:F3},{UpCameraPutResults[0].Y:F3},{UpCameraPutResults[0].Z:F3}"; assemblyRecord.PlaceMoveCameraCoord2 = $"{UpCameraPutResults[1].X:F3},{UpCameraPutResults[1].Y:F3},{UpCameraPutResults[1].Z:F3}"; assemblyRecord.PlaceActualCoord = $"{PlaceActualCoord.X:F3},{PlaceActualCoord.Y:F3},{PlaceActualCoord.Z:F3}"; App.Current.Dispatcher.Invoke(() => { _eventAggregator.GetEvent().Publish(assemblyRecord); }); } await _systemDatabaseService.RecordAssemblyAsync(assemblyRecord); var pressureParam = FileHelper.ReadJsonFile>(FilePath.PressureParamterConfigPath); var p1 = pressureParam[0];//组装压力 var p2 = pressureParam[1];//保压压力 LogHelper.WriteLogMes($"【mes开始ADD】"); SendTaskMessage($"mes开始ADD", MessageLevel.Info); Dictionary data = new Dictionary(); string gap = ""; string press = ""; string presstime = ""; string assypress = ""; if (i == 0) { gap = gapString; try { assypress = currentPressure[0].ToString("F2"); if (currentPressure[0] >= p1.PressureDownLimit && currentPressure[0] <= p1.PressureUpLimit) { resultIndex = 1; } else { resultIndex = 2; } } catch (Exception ex) { assypress = "0"; resultIndex = 2; } data.Add("gap", gap); data.Add("assy_pressure", assypress); SendTaskMessage($"gap:{gap},组装压力:{assypress}", MessageLevel.Info); #region 数据 bool assyPresRes; if (currentPressure[0] >= p1.PressureDownLimit && currentPressure[0] <= p1.PressureUpLimit) { assyPresRes = true; } else assyPresRes = false; DFC.Clear(); int itemIndex = 0; var gaps = gap.Split(','); foreach (var item in gaps) { itemIndex++; DFC.Add(new DataFormCtq { PPID = ProductMainSN[i], PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME, PROCESS_START_TIME = _LastMesCheckTime[i].ToString(), CTQ_NAME = $"Gap{itemIndex}", CTQ_VALUE = item, CTQ_LSL = "", CTQ_USL = "", PROCESS_END_TIME = serverTimeResult.Now.ToString(), SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME, COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE, REVISION = stationConfig.DataInfo.REVISION, WO = wo[i], MFG_LOT = stationConfig.DataInfo.MFG_LOT, MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE, PROCESS_OUTCOME = "PASS", PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID, CTQ_UNIT_OF_MEASURE = "mm", ERROR_MESSAGE = "" }); } DFC.Add(new DataFormCtq { PPID = ProductMainSN[i], PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME, PROCESS_START_TIME = _LastMesCheckTime[i].ToString(), CTQ_NAME = "Assy_Pressure", CTQ_VALUE = assypress, CTQ_LSL = p1.PressureDownLimit.ToString("F3"), CTQ_USL = p1.PressureUpLimit.ToString("F3"), PROCESS_END_TIME = serverTimeResult.Now.ToString(), SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME, COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE, REVISION = stationConfig.DataInfo.REVISION, WO = wo[i], MFG_LOT = stationConfig.DataInfo.MFG_LOT, MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE, PROCESS_OUTCOME = assyPresRes ? "PASS" : "FAIL", PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID, CTQ_UNIT_OF_MEASURE = "kg", ERROR_MESSAGE = assyPresRes ? "" : "Assy_Pressure OOS" }); #endregion } else if (i == 1) { try { press = currentPressure[1].ToString("F2"); if (currentPressure[1] >= p2.PressureDownLimit && currentPressure[1] <= p2.PressureUpLimit) { resultIndex = 1; } else { resultIndex = 2; } } catch (Exception ex) { press = "0"; resultIndex = 2; } presstime = p2.PressureTime.ToString(); data.Add("pressure", press); data.Add("pressure_time", presstime); SendTaskMessage($"保压压力:{press},保压时间:{presstime}", MessageLevel.Info); #region 数据 bool presRes; if (currentPressure[1] >= p2.PressureDownLimit && currentPressure[1] <= p2.PressureUpLimit) { presRes = true; } else presRes = false; DFC.Clear(); DFC.Add(new DataFormCtq { PPID = ProductMainSN[i], PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME, PROCESS_START_TIME = _LastMesCheckTime[i].ToString(), CTQ_NAME = "Pressure", CTQ_VALUE = press, CTQ_LSL = p2.PressureDownLimit.ToString("F3"), CTQ_USL = p2.PressureUpLimit.ToString("F3"), PROCESS_END_TIME = serverTimeResult.Now.ToString(), SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME, COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE, REVISION = stationConfig.DataInfo.REVISION, WO = wo[i], MFG_LOT = stationConfig.DataInfo.MFG_LOT, MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE, PROCESS_OUTCOME = presRes ? "PASS" : "FAIL", PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID, CTQ_UNIT_OF_MEASURE = "kg", ERROR_MESSAGE = presRes ? "" : "Assy_Pressure OOS" }); DFC.Add(new DataFormCtq { PPID = ProductMainSN[i], PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME, PROCESS_START_TIME = _LastMesCheckTime[i].ToString(), CTQ_NAME = "Pressure_time", CTQ_VALUE = Math.Round(double.Parse(presstime), 3).ToString(), CTQ_LSL = p2.PressureTime.ToString("F3"), CTQ_USL = p2.PressureTime.ToString("F3"), PROCESS_END_TIME = serverTimeResult.Now.ToString(), SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME, COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE, REVISION = stationConfig.DataInfo.REVISION, WO = stationConfig.DataInfo.WO, MFG_LOT = stationConfig.DataInfo.MFG_LOT, MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE, PROCESS_OUTCOME = "PASS", PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID, CTQ_UNIT_OF_MEASURE = "s", ERROR_MESSAGE = "" }); #endregion } //gap-复检gap参数, press-保压站实际压力值, presstime-保压站保压时间, assypress-组装站实际压力值 (bool isSuccess, string response) = await _mesService.SubmitGetAsync(i, code, comp, resultIndex == 1 ? true : false, data, _LastMesCheckTime[i], serverTimeResult.Now); if (isSuccess) { LogHelper.WriteLogMes($"上传mes成功"); SendTaskMessage($"上传mes成功", MessageLevel.Debug); } else { LogHelper.WriteLogMes($"上传mes失败"); SendTaskMessage($"上传mes失败", MessageLevel.Error); } await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3); //是否保存当前的产品记录至CSV? if (stationConfig.SaveCsv) { //判断锁付路径字符串是否为空,如果不为空则保存,否则不保存 if (!string.IsNullOrEmpty(stationConfig.SaveCsvPath)) { bool recordResult = RecordStationResult(i, DFC, ProductMainSN[i], serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath); if (recordResult) { SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存成功", MessageLevel.Debug); } else { SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存失败", MessageLevel.Error); } } else { SendTaskMessage($"{i}:{stationConfig.DeviceName}工位未配置保存路径,无法保存产品记录", MessageLevel.Error); } } else { SendTaskMessage($"{i}:{stationConfig.DeviceName}工位未配置保存产品记录", MessageLevel.Debug); } LogHelper.WriteLogMes($"【mes结束ADD】"); SendTaskMessage($"mes结束ADD", MessageLevel.Info); return; } else { SendTaskMessage($"{i}过站检查信号:state={state},处理为默认状态", MessageLevel.Alarm); await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)0); return; } } } } //开始/停止 组装/锁附 for (int i = 0; i < 7; i++) { //开始组装 if (string.Format(addressConfig.In_WorkStart.Address, i).Contains(tuple.nodeId)) { if (tuple.value is Int16 state) { if (_LastWorkStart[i] != state) { _LastWorkStart[i] = state; } else { return; } var code = plc.ReadNode(string.Format(addressConfig.In_Code.Address, i)); if (state == 1) { //SendTaskMessage($"收到开始组装信号[{i}]...", MessageLevel.Debug); SendTaskMessage($"收到开始保压信号[{i}]...", MessageLevel.Debug); // 开始采集压力值 StartPressureCollection(currentProduct.Name, code, i); StartCtqTime[i] = DateTime.Now; } else { SendTaskMessage($"关闭开始保压信号[{i}]...", MessageLevel.Debug); } } } //停止组装 else if (string.Format(addressConfig.In_WorkDone.Address, i).Contains(tuple.nodeId)) { if (tuple.value is Int16 state) { if (_LastWorkDone[i] != state) { _LastWorkDone[i] = state; } else { return; } if (state == 1) { SendTaskMessage($"收到停止压力信号[{i}]...", MessageLevel.Debug); //读取压力值 //float pressure = plc.ReadNode(addressConfig.In_PressureValue.Address); //读取压力值 float pressure = plc.ReadNode(string.Format(addressConfig.In_PressureValue.Address, i)); currentPressure[i] = pressure; SendTaskMessage($"[{i}]压力:{pressure}", MessageLevel.Info); //await Task.Delay(60); await plc.WriteNodeAsync(addressConfig.In_WorkDone.Address, (Int16)0); var code = plc.ReadNode(string.Format(addressConfig.In_Code.Address, i)); // 停止采集压力并保存 await StopAndSavePressureCollectionAsync(currentProduct.Name, code, i); } else { SendTaskMessage($"停止压力信号[{i}]已关闭", MessageLevel.Alarm); } } } } } } catch (Exception ex) { SendTaskMessage(ex.Message, MessageLevel.Error); LogHelper.WriteLogError("PLC命令执行时出错!", ex); } } #endregion #region 方法 private void SendTaskMessage(string msg, MessageLevel level) { App.Current.Dispatcher.Invoke(() => { _eventAggregator.GetEvent().Publish(new Models.MessageStruct() { Message = msg, level = level }); }); } #region Pressure collection helpers public void InitPressure() { for (int i = 0; i < _pressureSamples.Length; i++) { _pressureSamples[i] = new List<(DateTime, float)>(); } for (int i = 0; i < _pressureCts.Length; i++) { _pressureCts[i] = new CancellationTokenSource(); } for (int i = 0; i < _pressureTimeoutCts.Length; i++) { _pressureTimeoutCts[i] = new CancellationTokenSource(); } for (int i = 0; i < _pressureLinkedCts.Length; i++) { _pressureLinkedCts[i] = new CancellationTokenSource(); } } /// /// 开始采集压力值 /// /// 配方名称 /// 产品编号 /// 编号 private void StartPressureCollection(string productName, string productCode, int Index) { try { // cancel existing if any //_pressureCts[Index]?.Cancel(); //_pressureTimeoutCts[Index]?.Cancel(); //_pressureLinkedCts[Index]?.Dispose(); _pressureSamples[Index]?.Clear(); _pressureCts[Index] = new CancellationTokenSource(); _pressureTimeoutCts[Index] = new CancellationTokenSource(); // timeout after configured seconds _pressureTimeoutCts[Index].CancelAfter(TimeSpan.FromSeconds(_pressureMaxDurationSeconds)); // link tokens so either stop or timeout cancels _pressureLinkedCts[Index] = CancellationTokenSource.CreateLinkedTokenSource(_pressureCts[Index].Token, _pressureTimeoutCts[Index].Token); var token = _pressureLinkedCts[Index].Token; var addressConfig = _configService.GetPlcAddresses(); var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC; if (plc == null || !plc.IsConnected) return; Task.Run(async () => { SendTaskMessage($"Pressure collection started for position {Index}", MessageLevel.Debug); while (!token.IsCancellationRequested) { try { float pressure = plc.ReadNode(string.Format(addressConfig.In_PressureValue.Address, Index)); lock (_pressureSamples[Index]) { _pressureSamples[Index].Add((DateTime.Now, pressure)); } } catch (Exception ex) { LogHelper.WriteLogError("采集压力值时出错", ex); } await Task.Delay(_pressureSampleIntervalMs); } // if cancelled due to timeout if (_pressureTimeoutCts[Index].IsCancellationRequested && !_pressureCts[Index].IsCancellationRequested) { SendTaskMessage("Pressure collection timed out, saving collected data.", MessageLevel.Alarm); await StopAndSavePressureCollectionAsync(productName, productCode, Index); } }, token); } catch (Exception ex) { LogHelper.WriteLogError("StartPressureCollection error", ex); SendTaskMessage($"压力采集时出错:{ex.Message}", MessageLevel.Error); } } /// /// 停止采集并保存压力数据 /// /// /// /// /// private async Task StopAndSavePressureCollectionAsync(string productName, string productCode, int Index) { try { // cancel sampling //_pressureCts[Index]?.Cancel(); // allow worker to observe cancel await Task.Delay(50); List<(DateTime Timestamp, float Value)> snapshot; lock (_pressureSamples[Index]) { snapshot = new List<(DateTime, float)>(_pressureSamples[Index]); } if (snapshot.Count == 0) { SendTaskMessage("No pressure samples collected.", MessageLevel.Info); return; } // save to file under configured path try { //获取系统参数 var systemConfig = _configService.GetSystemConfiguration(); if (systemConfig.PressureSensorConfig == null || systemConfig.PressureSensorConfig.Count <= 0) { SendTaskMessage("当前没有配置任何压力传感器!", MessageLevel.Alarm); return; } //获取当前压力传感器配置 var pressureSensorConfig = systemConfig.PressureSensorConfig.FirstOrDefault(p => p.SensorId == Index); if (pressureSensorConfig == null) { SendTaskMessage($"当前压力传感器配置不存在!- {Index}", MessageLevel.Alarm); return; } //PressureFinishNotification : Prism.Events.PubSubEvent<(bool IsSuccess, int Index, (DateTime Timestamp, float Value)[] values,string SavePath )> string basePath = pressureSensorConfig?.PressureCurvePath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); string datePath = DateTime.Now.ToString("yyyy-MM") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\"; string dirPath = Path.Combine(basePath, datePath, productName ?? "UnknownProduct", productCode ?? DateTime.Now.ToString("yyyyMMddHHmmssfff")); if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); string csvfileName = $"pressure-{productCode}-{Index}-{DateTime.Now.ToString("HHmmss")}.csv"; string csvPath = Path.Combine(dirPath, csvfileName); string pngfileName = $"pressure-{productCode}-{Index}-{DateTime.Now.ToString("HHmmss")}.png"; string pngPath = Path.Combine(dirPath, pngfileName); if (!pressureSensorConfig.IsSavePressureCurve) { csvPath = string.Empty; pngPath = string.Empty; } App.Current.Dispatcher.Invoke(() => { _eventAggregator.GetEvent().Publish((true, Index, snapshot.ToArray(), pngPath)); }); //判断是否保存锁付曲线 if (pressureSensorConfig.IsSavePressureCurve) { if (!string.IsNullOrEmpty(csvPath)) { var sb = new StringBuilder(); sb.AppendLine("Timestamp,Value"); foreach (var s in snapshot) { sb.AppendLine($"{s.Timestamp:O},{s.Value}"); } File.WriteAllText(csvPath, sb.ToString()); SendTaskMessage($"Pressure samples saved to {csvPath}", MessageLevel.Debug); } } } catch (Exception ex) { LogHelper.WriteLogError("保存压力采样文件时出错", ex); SendTaskMessage("保存压力采样文件时出错: " + ex.Message, MessageLevel.Error); } } catch (Exception ex) { LogHelper.WriteLogError("StopAndSavePressureCollectionAsync error", ex); } finally { try { _pressureLinkedCts[Index]?.Cancel(); _pressureLinkedCts[Index]?.Dispose(); _pressureTimeoutCts[Index]?.Dispose(); _pressureCts[Index]?.Dispose(); _pressureSamples[Index]?.Clear(); } catch { } } } #endregion private async void DoYieldTime(object state) { try { //获取当前产品 var currentproduct = _productService.GetCurrentProduct(); if (currentproduct == null) return; TotalQuantity = await _systemDatabaseService.GetOverallProductionAsync(currentproduct.Name); TotalQuantityToday = await _systemDatabaseService.GetTodayProductionAsync(currentproduct.Name); CurrentUPH = await _systemDatabaseService.GetLastHourProductionAsync(currentproduct.Name); } catch (Exception) { } } #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 /// /// 记录工位的结果,其他工位要存储的数据项按照键值对集合的形式传入,键为数据项名称,值为数据项的值 /// /// /// /// /// /// /// /// /// private bool RecordStationResult(int stationIndex, IEnumerable dfcs, string productMainSN, double ct, DateTime timestamp, string savePath, bool isFirstEnter = true) { try { //获取当前工位的配置 var stationConfig = _configService.GetDeviceInfo(stationIndex); if (stationConfig == null) return false; //如果保存路径为空,则不保存 if (string.IsNullOrEmpty(savePath)) return false; string fullPath = savePath; //如果保存路径是一个文件路径,则直接保存,否则按照工位和日期进行保存 if (File.Exists(savePath) || Path.HasExtension(savePath)) { fullPath = savePath; //如果路径不存在,则创建 // 创建目录 string directory = Path.GetDirectoryName(fullPath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } } else { //结果保存至CSV文件,文件名格式:savePath\yyyy-MM\工位编号_工位名称_yyyy-MM-dd.csv string datePath = timestamp.ToString("yyyy-MM") + "\\"; string dirPath = Path.Combine(savePath, datePath); if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); string fileName = $"{stationIndex}_{stationConfig.DeviceName}_{timestamp.ToString("yyyy-MM-dd")}.csv"; fullPath = Path.Combine(dirPath, fileName); } StringBuilder sb = new StringBuilder(); if (!File.Exists(fullPath)) { sb.Append("PPID,PROCESS_NAME,PROCESS_START_TIME,CTQ_NAME,CTQ_VALUE,CTQ_LSL,CTQ_USL,PROCESS_END_TIME,SUPPLIER_NAME,COMMODITY_TYPE,REVISION,WO#,MFG_LOT#," + "MFG_ASSY_LINE,PROCESS_OUTCOME,PROCESS_MACHINE_ID,CTQ_UNIT_OF_MEASURE,ERROR_MESSAGE"); sb.AppendLine(); } if (dfcs == null || dfcs.Count() == 0) { return false; } foreach (var dfc in dfcs) { sb.Append($"{dfc.PPID},{dfc.PROCESS_NAME},{dfc.PROCESS_START_TIME},{dfc.CTQ_NAME},{dfc.CTQ_VALUE},{dfc.CTQ_LSL},{dfc.CTQ_USL},{dfc.PROCESS_END_TIME},{dfc.SUPPLIER_NAME},{dfc.COMMODITY_TYPE},{dfc.REVISION},{dfc.WO},{dfc.MFG_LOT},{dfc.MFG_ASSY_LINE},{dfc.PROCESS_OUTCOME},{dfc.PROCESS_MACHINE_ID},{dfc.CTQ_UNIT_OF_MEASURE},{dfc.ERROR_MESSAGE}"); sb.AppendLine(); } // 使用 StreamWriter 以追加模式打开文件 StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8); sw.Write(sb.ToString()); sw.Close(); sw.Dispose(); return true; } catch (Exception ex) { if (!isFirstEnter) { return false; } LogHelper.WriteLogError("记录其他工位结果时出错!", ex); //我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录 //生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv string datePath = timestamp.ToString("yyyy-MM") + "\\"; string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp"); string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv"; RecordStationResult(stationIndex, dfcs, productMainSN, ct, timestamp, tempFileName, false); return false; } } #endregion } }