using Cowain.Bake.BLL; using Cowain.Bake.Common; using Cowain.Bake.Common.Core; using Cowain.Bake.Common.Enums; using Cowain.Bake.Common.Interface; using Cowain.Bake.Communication.Interface; using Cowain.Bake.Communication.MOM; using Cowain.Bake.Main.ViewModels; using Cowain.Bake.Main.Views; using Cowain.Bake.Model; using Cowain.Bake.Model.Models; using Cowain.Bake.UI.CsvMap; using HslCommunication; using Newtonsoft.Json; using Prism.Ioc; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows; using Unity; using static Cowain.Bake.Common.Models.MESModel; using JSON = Newtonsoft.Json.JsonConvert; namespace Cowain.Bake.Main.Station { public class LoadingStation : IServerManager { private int _batteryCodeLen = 0; public string Name { get; set; } IPLCDevice PLC { get; set; } readonly List _deviceScann = new List(); public IUnityContainer _unityContainer { get; set; } public LoadingStation(IUnityContainer unityContainer) { _unityContainer = unityContainer; TDeviceConfig config = _unityContainer.Resolve().GetConfig(EDeviceType.PLC, EDeviceName.Loading)[0]; Name = config.Name; SetBatteryCodeLen(); Start(); } public void SetBatteryCodeLen() { _batteryCodeLen = int.Parse(_unityContainer.Resolve().GetValueByParaID(ESysSetup.BatteryCodeLen.ToString())); } bool IsConnectPLC() { if (null == PLC || !PLC.IsConnect) { LogHelper.Instance.Error($"PalletVirtualId:{PLC.Name},PLC为空!"); return false; } return true; } public void Start() { var configScann = _unityContainer.Resolve().GetConfig(EDeviceType.SCANNER).OrderBy(x => x.Id).Take((int)EScanCode.PalletScan2).ToList(); foreach (var item in configScann) { IScanCodeBase device = _unityContainer.Resolve(item.Name); _deviceScann.Add(device); } PLC = _unityContainer.Resolve(Name); //上料PLC } public void Stop() { } /* * 编译器自动在方法入口/出口加 lock(this),简单但锁粒度粗; * 高频调用或重入场景容易成瓶颈,只适合做原型或低并发工具类。 */ //托盘满盘信号 [MethodImpl(MethodImplOptions.Synchronized)] public void ExecuteFullPallet(int curValue, string param, Variable node) { dynamic d = JsonConvert.DeserializeObject(node.Json); LogHelper.Instance.Warn($"LoadingStation:ExecuteFullPallet:开始,线程ID:{System.Threading.Thread.CurrentThread.ManagedThreadId}"); int result ; int batteryQty; _unityContainer.Resolve().SetEvent("开始:" + node.VarDesc); int palletId = _unityContainer.Resolve().GetPalletId((int)EStationType.Loading, int.Parse(param)); TPalletInfo palletInfo = _unityContainer.Resolve().GetPalletInfo(palletId); if (null == palletInfo) //如果当前夹具状态为"满夹具可取",就退出,上位机重启会重复触发! { LogHelper.Instance.Warn($"托盘满信号,上料平台无夹具,异常"); return; } if ((int)EPalletStatus.Advisable == palletInfo.PalletStatus) //如果当前夹具状态为"满夹具可取",就退出,上位机重启会重复触发! { //LogHelper.Instance.Warn($"托盘满信号,当前托盘为满夹具夹,异常"); //return; } try { int debugMode = int.Parse(_unityContainer.Resolve().GetValueByParaID(ESysSetup.DebugMode.ToString())); //0 if (!IsConnectPLC()) return; var resultBatterys = PLC.Read((string)d.ReadVirtualIds); //可以改成直接读PLC var resultPalletVirtualId = PLC.Read((string)d.ReadPalletVirtualId); //这是直接读PLC var lastFlag = PLC.Read((string)d.ReadLastFlag).Content; //这是直接读PLC if (!resultBatterys.IsSuccess || !resultPalletVirtualId.IsSuccess) { LogHelper.Instance.Error($"PalletVirtualId:{resultPalletVirtualId.Content},没有读取电芯信息或托盘信息!"); return; } if (0 == resultPalletVirtualId.Content || 1 == resultPalletVirtualId.Content) { LogHelper.Instance.Error($"托盘满盘信号异常, 托盘虚拟码为【{resultPalletVirtualId.Content}】!", true); return; } //批量更新电芯表 batteryQty = _unityContainer.Resolve().InsertBatch(resultBatterys.Content, resultPalletVirtualId.Content); //add by lsm 20250219 要300耗秒以上 SavePalletInfo(resultPalletVirtualId.Content); if (0 == batteryQty) { //HandyControl.Controls.MessageBox.Error("触发满盘时没有电芯数据!"); //启动上位机时,会触发这个东西。 LogHelper.Instance.Error($"启动上位机时,上料:{resultPalletVirtualId.Content},电芯地址里没有电芯数据!"); return; } if (Global.PALLET_TOTAL_BATTERYS != batteryQty) { LogHelper.Instance.Error($"PalletVirtualId:{resultPalletVirtualId.Content},{batteryQty},上料电芯是{Global.PALLET_TOTAL_BATTERYS}!", false); } result = _unityContainer.Resolve().LoadingUpdatePalletStatus(resultPalletVirtualId.Content, (int)EPalletStatus.Advisable, batteryQty, lastFlag == 1); if (0 == result) { LogHelper.Instance.Error($"PalletVirtualId:{resultPalletVirtualId.Content},修改托盘状态失败!"); } //writeResult = PLC.Write((string)d.WriteResult, true);//回复收到托盘1满盘信号 //if (!writeResult.IsSuccess) //{ // LogHelper.Instance.GetCurrentClassWarn($"{(string)d.WriteResult}:{writeResult.Message}"); //} } catch(Exception ex) { LogHelper.Instance.GetCurrentClassWarn($",{ex.Message}:{ex.StackTrace}"); } _unityContainer.Resolve().SetEvent("结束:" + node.VarDesc); } //假电芯扫码 验证数据库OK public void ExecuteScanDummy(int curValue, string param, Variable node) { dynamic d = JsonConvert.DeserializeObject(node.Json); if (!IsConnectPLC()) return; int virtualId; OperateResult writeResult = null; EScanCode scanName = EScanCode.DummyScan; //(EScanCode)(int.Parse(param)); IScanCodeBase scanDevice = _deviceScann.Find(x => x.Name == scanName.ToString()); HslCommunication.OperateResult scanResult = new HslCommunication.OperateResult() { IsSuccess = false, }; scanResult.IsSuccess = true; scanResult.Content = System.Guid.NewGuid().ToString("N").Substring(0, 12); if (scanResult.IsSuccess) { virtualId = _unityContainer.Resolve().InsertBattery(scanResult.Content, (int)EBatteryStatus.ScanOver, (int)EDummyState.Have, ""); writeResult = PLC.Write((string)d.WriteVirtualId, virtualId); //假电芯虚拟码 if (!writeResult.IsSuccess) { LogHelper.Instance.GetCurrentClassWarn($"写假电芯虚拟码失败,{(string)d.WriteVirtualId}:{writeResult.Message}"); } } writeResult = PLC.Write((string)d.WriteResult, scanResult.IsSuccess ? (Int16)EResult.OK : (Int16)EResult.NG); //假电芯结果 if (!writeResult.IsSuccess) { LogHelper.Instance.GetCurrentClassWarn($"{(string)d.WriteResult}:{writeResult.Message}"); } writeResult = PLC.Write((string)d.WriteAsk, 1); //回复 if (!writeResult.IsSuccess) { LogHelper.Instance.Warn($"ExecuteScanDummy-{(string)d.WriteAsk}:{writeResult.Message}"); } _unityContainer.Resolve().SetEvent("结束:" + node.VarDesc); } //托盘扫码 public void ExecuteScanPallet(int curValue, string param, Variable node) { string msg = ""; dynamic d = JsonConvert.DeserializeObject(node.Json); _unityContainer.Resolve().AddLog("LoadingStation:ExecuteScanPallet:托盘扫码开始", E_LogType.Info.ToString()); string palletCode = ""; int VID = 0; OperateResult writeResult = null; HslCommunication.OperateResult scanResult = new HslCommunication.OperateResult() { IsSuccess = false, }; EScanCode scanName = (EScanCode)((int)EScanCode.PalletScan1 + int.Parse(param) - 1); //param这个就是扫码枪的编号 try { IScanCodeBase scanDevice = _deviceScann.Find(x => x.Name == scanName.ToString()); while (!scanDevice.IsConnect) { msg = $"获取{scanName.GetDescription()}连接异常!"; LogHelper.Instance.Error(msg); _unityContainer.Resolve().AddPromptContent(msg); scanDevice.Connect(); } scanResult = scanDevice.ReadCode(); ////------------------------ //add by lsm //scanResult.IsSuccess = true; //scanResult.Content = System.Guid.NewGuid().ToString("N").Substring(0, 6); // add by lsm测试用 //------------------------ palletCode = scanResult.Content; //夹具扫码异常处理 while (string.IsNullOrEmpty(palletCode)) { Application.Current.Dispatcher.Invoke((Action)(() => { PalletIdInputWindow f = new PalletIdInputWindow("托盘码输入框", "请输入托盘码", "确定输入托盘码?") { WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen, }; f.ShowDialog(); palletCode = f.PalletId; })); } //绑定托盘 VID = _unityContainer.Resolve().BindingPallet(palletCode, (int)EPalletStatus.Loading, (int)EStationType.Loading, int.Parse(param)); //防呆 if (0 == VID) { LogHelper.Instance.Debug($"===ProcBindPallet卡死,{palletCode},{int.Parse(param)},VID:{VID}"); return; //此时会卡死上位机 } LogHelper.Instance.Debug($"===ProcBindPallet,{palletCode},{(int)EPalletStatus.Loading},{int.Parse(param)},VID:{VID}"); if (CurrentWeaterPallet()) { Int16 DummyLocationX = Int16.Parse(_unityContainer.Resolve().GetValueByParaID(ESysSetup.DummyLocationX.ToString())); writeResult = PLC.Write((string)d.WriteDummyX, DummyLocationX); //假电芯行号 if (!writeResult.IsSuccess) { LogHelper.Instance.GetCurrentClassWarn($"假电芯行号:{(string)d.WriteDummyX}:{writeResult.Message},失败"); } Int16 DummyLocationY = Int16.Parse(_unityContainer.Resolve().GetValueByParaID(ESysSetup.DummyLocationY.ToString())); writeResult = PLC.Write((string)d.WriteDummyY, DummyLocationY); //假电芯列号 if (!writeResult.IsSuccess) { LogHelper.Instance.GetCurrentClassWarn($"假电芯列号:{(string)d.WriteDummyY}:{writeResult.Message},失败"); } } writeResult = PLC.Write((string)d.WritePattleVirtualId, VID); //托盘1托盘虚拟码 if (!writeResult.IsSuccess) { LogHelper.Instance.GetCurrentClassWarn($"{(string)d.WritePattleVirtualId}:{writeResult.Message}"); } writeResult = PLC.Write((string)d.WriteResult, 1); //托盘1扫码结果 if (!writeResult.IsSuccess) { LogHelper.Instance.GetCurrentClassWarn($"{(string)d.WriteResult}:{writeResult.Message}"); } writeResult = PLC.Write((string)d.WriteAsk, 1); //回复 if (!writeResult.IsSuccess) { LogHelper.Instance.Warn($"ExecuteScanPallet-{(string)d.WriteAsk}:{writeResult.Message}"); } } catch(Exception ex) { LogHelper.Instance.GetCurrentClassWarn($"ExecuteScanPallet:{ex},{scanResult.Content},{scanResult.Message}"); } _unityContainer.Resolve().DeletePromptContent(); _unityContainer.Resolve().SetEvent("结束:" + node.VarDesc); } /// /// 根据工单判断是否放水含量 /// /// public bool CurrentWeaterPallet() { bool IsWaertPallet = false; var productionInformation = _unityContainer.Resolve().GetCurrentProductInfo(); if (productionInformation.DummyRule == (int)DummyPlaceRule.DEFAULT_EVERY_STOVE_LAYER_PLACED_ONE) { if (SettingProvider.Instance.WaterPallet % 2 == 1) { IsWaertPallet = true; } SettingProvider.Instance.WaterPallet++; } else if (productionInformation.DummyRule == (int)DummyPlaceRule.EVERY_PALLET_PLACED_ONE) { IsWaertPallet = true; } return IsWaertPallet; } //电芯扫码 public async Task ExecutScanBattery(int curValue, string param, Variable node) { dynamic d = JsonConvert.DeserializeObject(node.Json); if (!IsConnectPLC()) return; int scannIndex = 0; OperateResult writeResult = null; int mesIndex = 0; int index = 1; //1..4,第0个索引不用 string[] codeBatterys = new string[Global.ONCE_SCAN_BATTERY + 1]; Int16[] result = new Int16[Global.ONCE_SCAN_BATTERY + 1]; Int16[] ask = new Int16[Global.ONCE_SCAN_BATTERY + 1]; int[] batteryVirtualIds = new int[Global.ONCE_SCAN_BATTERY + 1]; result = Enumerable.Repeat(3, Global.ONCE_SCAN_BATTERY + 1).ToArray(); Array.Clear(ask, 0, Global.ONCE_SCAN_BATTERY + 1); //3:无电芯 Array.Clear(batteryVirtualIds, 0, Global.ONCE_SCAN_BATTERY + 1); List> scannList = new List>(); List mesResultList = new List(); List>> scannTasks = new List>>(); List> mesResultTasks = new List>(); int mesEnable = int.Parse(_unityContainer.Resolve().GetValueByParaID(ESysSetup.MOMEnable.ToString())); int debugMode = int.Parse(_unityContainer.Resolve().GetValueByParaID(ESysSetup.DebugMode.ToString())); //0为测试模式,1为正式模式 Array.Clear(codeBatterys, 0, Global.ONCE_SCAN_BATTERY + 1); //几个条码一起保存 HslCommunication.OperateResult scanResult = new HslCommunication.OperateResult() { IsSuccess = false, }; if (mesEnable == (int)EMOMEnable.Enable && (int)EProductionMode.Debug == debugMode) { LogHelper.Instance.Error($"MOM【在线模式】,设备模式却是【调试模式】,模式不匹配,请修改!", true); return; } try { index = 1; for (EScanCode s = EScanCode.LoadingBatteryScan1; s <= EScanCode.LoadingBatteryScan8; s++, index++) { if (((curValue >> index) & 1) != 1) //第index位不是1,就是不需要扫码 { continue; } ask[(int)s] = 1; IScanCodeBase scanDevice = _deviceScann.Find(x => x.Name == s.ToString()); if (!scanDevice.IsConnect) { string msg = $"获取{s.GetDescription()}失败,请检测线路!"; LogHelper.Instance.Error(msg, true); //弹屏,人为处理好后,再次触发 _unityContainer.Resolve().AddPromptContent(msg); scanDevice.Connect(); --s; //再次获取 continue; } if ((int)EProductionMode.Debug == debugMode) //调试模式 { scanResult.IsSuccess = true; scanResult.Content = System.Guid.NewGuid().ToString("N").Substring(0, _batteryCodeLen); scannList.Add(scanResult); } else { //scanResult = scanDevice.ReadCode(); scannTasks.Add(Task.Run(() => scanDevice.ReadCode())); // 启动并行任务 } } if ((int)EProductionMode.Normal == debugMode) { await Task.WhenAll(scannTasks); //等待所有任务(扫码)完成 foreach (var task in scannTasks) { scannList.Add(task.Result); } } #region 同时请求MOM if (mesEnable == (int)EMOMEnable.Enable)//生产模式,才会请求MES(不是离线就要请求) { foreach (var item in scannList) //四个电芯扫码结果 { if (item.IsSuccess) //可能是三个扫码OK,一个扫码NG,所以只有三个电芯请求到MOM { mesResultTasks.Add(Task.Run(() => _unityContainer.Resolve().GetBatteryStatus(item.Content))); //多线程入库,会有先后顺序的 } } if (0 != mesResultTasks.Count) { MESReturnCmdModel[] tt = await Task.WhenAll(mesResultTasks); // //发送MES,等待所有的MES请求。 foreach (var item in mesResultTasks) //发送MOM,不一定是四个 { mesResultList.Add(item.Result); } } } #endregion index = 1; //1..4,第0个索引不用 for (EScanCode s = EScanCode.LoadingBatteryScan1; s <= EScanCode.LoadingBatteryScan8; s++, index++) { if (((curValue >> index) & 1) != 1) //第index位不是1,就是不需要扫码 { continue; } scanResult = scannList[scannIndex++]; result[index] = scanResult.IsSuccess ? (Int16)EResult.OK : (Int16)EResult.NG; if (scanResult.IsSuccess) //扫码成功 { //长度判断 //if (_batteryCodeLen != scanResult.Content.Length) //{ // result[index] = (Int32)EResult.NG; //长度不对,赋NG // _unityContainer.Resolve().Insert("入站拦截", scanResult.Content, $"要求电芯条码长度为{_batteryCodeLen},实际长度为:{scanResult.Content.Length}"); // continue; //} //0:正常扫码,1:复投扫码 if ("0" == _unityContainer.Resolve().GetValueByParaID(Cowain.Bake.Common.Enums.ESysSetup.ScanCodeMode.ToString())) { if (_unityContainer.Resolve().IsBatteryCodeRepeat(scanResult.Content)) { result[index] = (Int32)EResult.NG; //有相同电芯,赋NG LogHelper.Instance.Warn($"{s.GetDescription()},电芯条码:[{scanResult.Content}]重复扫码,将会排出到NG位;"); //不要弹屏 _unityContainer.Resolve().Insert("入站拦截", scanResult.Content, $"重复扫码,将会排出到NG位"); } } //MES结果判断 if (mesEnable == (int)EMOMEnable.Enable) ////mes-mom 7.电芯状态获取 只有联机才调用MES接口,MES返回成功,失败 { MESReturnCmdModel mesResult = mesResultList[mesIndex++];//_unityContainer.Resolve().MESCellState(scanResult.Content); if (mesResult == null) { result[index] = (int)EResult.NG; _unityContainer.Resolve().Insert("入站拦截", scanResult.Content, "MOM返回信息为空,请判断MOM是否离线,未离线则是MOM回复异常"); continue; } if (mesResult.Info.ResultFlag != EResultFlag.OK.ToString()) { result[index] = (int)EResult.NG; _unityContainer.Resolve().Insert("入站拦截", scanResult.Content, JSON.SerializeObject(mesResult)); continue; } } codeBatterys[index] = scanResult.Content; //MES判断OK的电芯,才放到电芯表中。 } } batteryVirtualIds = _unityContainer.Resolve().InsertBattery(string.Join(",", codeBatterys)).ToArray(); //新加的,四个一起存,并返回虚拟码 //InsertBattery//四个电芯一起存数据库 LogHelper.Instance.Info($"电芯虚拟码写入到PLC:{string.Join(",", batteryVirtualIds)},codes={string.Join(",", codeBatterys)},扫码状态:{string.Join(",", result)}"); writeResult = PLC.Write((string)d.WriteVirtualIds, batteryVirtualIds); //电芯1...4虚拟码 if (!writeResult.IsSuccess) { LogHelper.Instance.Warn($"写虚拟地址-{(string)d.WriteVirtualIds}:{writeResult.Message}"); } //g_U01_HMI.UpLoadFromWcs.iCell_ScanCodeResults writeResult = PLC.Write((string)d.WriteResult, result); //电芯1...8扫码结果 if (!writeResult.IsSuccess) { LogHelper.Instance.Warn($"ExecutScanBattery-{(string)d.WriteResult}:{writeResult.Message}"); } writeResult = PLC.Write((string)d.WriteAsk, ask); //回复 if (!writeResult.IsSuccess) { LogHelper.Instance.Warn($"ExecutScanBattery-{(string)d.WriteAsk}:{writeResult.Message}"); } _unityContainer.Resolve().DeletePromptContent(); } catch(Exception ex) { LogHelper.Instance.Fatal($"ExecutScanBattery-出错-param:{param}:{ex.Message},{index}"); } _unityContainer.Resolve().SetEvent("结束:" + node.VarDesc); } //保存组盘信息 private void SavePalletInfo(int palletVID) { string dateFile; string filePath; try { List batterys = _unityContainer.Resolve().GetBatteryInfos(palletVID); if (0 == batterys.Count) { return; } string path = _unityContainer.Resolve().GetValueByParaID(ESysSetup.DataFilePath.ToString()); path += "\\组盘"; dateFile = DateTime.Now.ToString("yyyyMMdd"); filePath = path + $"\\{dateFile}.csv"; if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } CSVHelper.WriteMap(batterys, filePath); } catch(Exception ex) { LogHelper.Instance.Error($"SavePalletInfo:{ex.Message},{ex.StackTrace}"); } } } }