using Aucma.Scada.Model.domain; using HighWayIot.Common; using HighWayIot.Config; using HighWayIot.Log4net; using HighWayIot.Plc; using HighWayIot.Repository.service; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace Aucma.Scada.Business { /// /// 入库任务处理 /// public sealed class InStoreTaskHandle { #region 单例实现 private static readonly Lazy lazy = new Lazy(() => new InStoreTaskHandle()); public static InStoreTaskHandle Instance { get { return lazy.Value; } } #endregion #region 对象引用 private LogHelper logHelper = LogHelper.Instance; private AppConfig appConfig = AppConfig.Instance; private PlcConfig plcConfig = PlcConfig.Instance; private PlcPool _pool = PlcPool.Instance; private PlcSpaceConfig spaceConfig = PlcSpaceConfig.Instance; /// /// 货道信息 /// private IBaseSpaceInfoService _spaceInfoService; #endregion #region 私有变量 private Dictionary _plcDictionary = new Dictionary(); private Dictionary shellKeyValuePairs = new Dictionary(); private Dictionary linerKeyValuePairs = new Dictionary(); /// /// 箱壳任务编号,PLC反馈后进行赋值 /// private string shellTaskCode = string.Empty; /// /// 内胆任务编号,PLC反馈后进行赋值 /// private string linerTaskCode = string.Empty; /// /// 已下传的任务信息 /// // private List shellTaskInfos = new List(); // private List linerTaskInfos = new List(); #endregion #region 委托事件 /// /// 箱壳实时库存刷新 /// /// public delegate void RefreshShellStock(); public event RefreshShellStock RefreshShellStockEvent; /// /// 内胆实时库存刷新 /// /// public delegate void RefreshLinerStock(); public event RefreshLinerStock RefreshLinerStockEvent; /// /// 入库完成 /// /// /// public delegate void InStoreFinsih(string storeCode, string taskCode); public event InStoreFinsih InStoreFinsihEvent; /// /// 入库应答,PLC收到下发的入库任务后进行应答 /// /// /// public delegate void InStoreAnswer(string storeCode, string taskCode); public event InStoreAnswer InStoreAnswerEvent; #endregion /// /// 实时任务 /// private IRealTaskInfoService _taskInfoService; private RegisterServices registerServices = RegisterServices.Instance; private InStoreTaskHandle() { _taskInfoService = registerServices.GetService(); _spaceInfoService = registerServices.GetService(); _plcDictionary = _pool.GetAll(); // RealReadFinish(); RealUpdateSpaceInfoByPlc(); } /// /// 实时读取plc更新数据库货道数量及在途 /// private void RealUpdateSpaceInfoByPlc() { Task.Run(() => { // List spaceAddressList = spaceConfig.GetAllSpaceAddress(spaceList); Thread.Sleep(2000); while (true) { List spaceList = _spaceInfoService.GetSpaceInfosByStoreCode(appConfig.shellStoreCode, appConfig.linerStoreCode); if (spaceList != null && spaceList.Count > 0) { foreach (BaseSpaceInfo spaceInfo in spaceList) { int Stock = spaceInfo.spaceStock; int OnAmount = spaceInfo.onRouteAmount; //BaseSpaceInfo tempSpace = new BaseSpaceInfo(); //tempSpace.storeCode = spaceInfo.storeCode; //tempSpace.spaceCode = spaceInfo.spaceCode; ReadSpaceInfoByPlc(spaceInfo); //if (spaceInfo.spaceStock == 0 && spaceInfo.onRouteAmount == 0) //{ // // 清空型号供新型号使用 // spaceInfo.materialType = null; // spaceInfo.typeNameA = null; // _spaceInfoService.UpdateSpaceInfo(spaceInfo); // RefreshShellStockEvent?.Invoke(); // RefreshLinerStockEvent?.Invoke(); //} // 更新库存 if (Stock != spaceInfo.spaceStock || OnAmount != spaceInfo.onRouteAmount) { // Console.WriteLine("RealUpdateSpaceInfoByPlc1 " + JsonChange.Instance.ModeToJson(spaceInfo)); // spaceInfo.spaceStock = tempSpace.spaceStock; // spaceInfo.onRouteAmount = tempSpace.onRouteAmount; _spaceInfoService.UpdateSpaceInfo(spaceInfo); RefreshShellStockEvent?.Invoke(); RefreshLinerStockEvent?.Invoke(); } } } Thread.Sleep(3000); } }); } #region 20240221取消入库完成信号 判断箱壳,内胆入库是否完成 /// /// 实时读取箱壳,内胆入库完成信号 /// private void RealReadFinish() { // 箱壳线程 Task.Run(() => { try { while (true) { if (_plcDictionary.Count > 0) { IPlc _plc = _plcDictionary[appConfig.shellStoreCode]; if (_plc != null) { List taskList = _taskInfoService.GetTaskInfosForInstore(appConfig.shellStoreCode, appConfig.instoreTaskType, 2); if (taskList != null && taskList.Count > 0) { foreach (RealTaskInfo taskInfo in taskList) { SpaceAddress spaceAddress = spaceConfig.GetSpaceAddress(appConfig.shellStoreCode, taskInfo.spaceCode); JudgeIsFinish(taskInfo, _plc, spaceAddress, appConfig.shellStoreCode); } } } else { logHelper.Info($"PLC信息为空或连接失败,通过{appConfig.shellStoreCode}未获取到该仓库对应的PLC信息"); } } Thread.Sleep(1000); }; } catch (Exception ex) { logHelper.Error(ex.Message.ToString()); } }); //内胆线程 Task.Run(() => { try { while (true) { if (_plcDictionary.Count > 0) { IPlc _plc = _plcDictionary[appConfig.linerStoreCode]; if (_plc != null) { // 修改这个方法 List taskList = _taskInfoService.GetTaskInfosForInstore(appConfig.linerStoreCode, appConfig.instoreTaskType, 2); if (taskList != null && taskList.Count > 0) { foreach (RealTaskInfo taskInfo in taskList) { SpaceAddress spaceAddress = spaceConfig.GetSpaceAddress(appConfig.linerStoreCode, taskInfo.spaceCode); JudgeIsFinish(taskInfo, _plc, spaceAddress, appConfig.linerStoreCode); } } } else { logHelper.Info($"PLC信息为空或连接失败,通过{appConfig.linerStoreCode}未获取到该仓库对应的PLC信息"); } } Thread.Sleep(1000); }; } catch (Exception ex) { logHelper.Error(ex.Message.ToString()); } }); } /// /// 判断该任务对应的货道是否有完成信号 /// /// /// public void JudgeIsFinish(RealTaskInfo taskInfo, IPlc _plc, SpaceAddress spaceAddress,string storeCode) { //读取入库完成反馈信号 if (_plc.readInt32ByAddress(spaceAddress.inStoreFinish) == 1) { _plc.writeInt32ByAddress(spaceAddress.inStoreFinish, 0); InStoreFinsihEvent?.Invoke(storeCode, taskInfo.taskCode); } } #endregion /// /// 读箱壳应答字是否为1,判断空板使用 /// /// /// public int ReadShellAnswer() { int result = 0; try { IPlc _plc = _plcDictionary[appConfig.shellStoreCode]; if (_plc != null) { result = _plc.readInt32ByAddress(plcConfig.in_shell_answer); } } catch (Exception ex) { logHelper.Error("读取箱壳应答字异常:"+ ex.Message.ToString()); } return result; } /// /// 读内胆应答字是否为1,判断空板使用 /// /// /// public int ReadLinerAnswer() { int result = 0; try { IPlc _plc = _plcDictionary[appConfig.linerStoreCode]; if (_plc != null) { result = _plc.readInt32ByAddress(plcConfig.in_liner_answer); } } catch (Exception ex) { logHelper.Error("读取内胆应答字异常:" + ex.Message.ToString()); } return result; } #region 箱壳入库任务下发处理 public int SendShellTask_InStore(RealTaskInfo taskInfo) { int result = 0; try { IPlc _plc = _plcDictionary[taskInfo.storeCode]; if (_plc != null) { if (_plc.readInt32ByAddress(plcConfig.in_shell_answer) == 1) { Console.WriteLine(plcConfig.in_shell_answer + " 应答字读 1"); logHelper.Info("箱壳入库应答字为1,货道号:" + plcConfig.in_shell_spaceCode + ";写" + short.Parse(taskInfo.spaceCode.Substring(5, 1))); //写入货道号 _plc.writeInt32ByAddress(plcConfig.in_shell_spaceCode, short.Parse(taskInfo.spaceCode.Substring(5, 1))); Console.WriteLine(plcConfig.in_shell_spaceCode + " 货道号写 "+ short.Parse(taskInfo.spaceCode.Substring(5, 1))); //写入完成后读取应答字进行复位 ReadShellAnswer_InStore(taskInfo); //写入应答字 // _plc.writeInt32ByAddress(plcConfig.in_shell_answer, 1); //写入任务号 // _plc.writeStringByAddress(plcConfig.in_shell_task, taskInfo.taskCode); result = 1; } else { result = 2; logHelper.PlcLog("应答字为2,下发新任务plc未就绪"); } } else { logHelper.Info($"PLC信息为空,通过{taskInfo.storeCode}未获取到该仓库对应的PLC信息"); } } catch (Exception ex) { logHelper.Error("箱壳入库任务下发异常", ex); } return result; } /// /// 读取箱壳入库应答 /// private void ReadShellAnswer_InStore(RealTaskInfo taskInfo) { lock (string.Empty) { bool isFlag = true; IPlc _plc = _plcDictionary[appConfig.shellStoreCode]; try { Task.Run(() => { if (_plc != null) { do { //读取PLC应答字为2时,上位机清空写入的入库内容 if (_plc.readInt32ByAddress(plcConfig.in_shell_answer) == 2) { Console.WriteLine(plcConfig.in_shell_answer + " 应答字读 2" ); logHelper.PlcLog("箱壳入库应答字为2,货道号:" + plcConfig.in_shell_spaceCode + ";复位写0"); //写入货道号 _plc.writeInt32ByAddress(plcConfig.in_shell_spaceCode, 0); Console.WriteLine(plcConfig.in_shell_spaceCode + " 货道号写 0"); //写入应答字 // _plc.writeInt32ByAddress(plcConfig.in_foam_answer, 0); isFlag = false; InStoreAnswerEvent?.Invoke(appConfig.shellStoreCode, taskInfo.taskCode); // shellTaskInfos.Add(taskInfo); } Thread.Sleep(500); } while (isFlag); } else { logHelper.Info($"PLC信息为空,通过{appConfig.shellStoreCode}未获取到该仓库对应的PLC信息"); } }); } catch (Exception ex) { logHelper.Error("读取箱壳入库应答字异常", ex); } } } #endregion #region 内胆入库任务处理 /// /// 内胆入库任务下发 /// /// public int SendLinerTask_InStore(RealTaskInfo taskInfo) { int result = 0; try { IPlc _plc = _plcDictionary[appConfig.linerStoreCode]; if (_plc != null) { if (_plc.readInt32ByAddress(plcConfig.in_liner_answer) == 1) { logHelper.PlcLog("内胆入库应答字为1,货道号:" + plcConfig.in_liner_spaceCode + ";写" + short.Parse(taskInfo.spaceCode.Substring(5, 1))); //写入货道号 _plc.writeInt32ByAddress(plcConfig.in_liner_spaceCode, short.Parse(taskInfo.spaceCode.Substring(5, 1))); //写入应答字 // _plc.writeInt32ByAddress(plcConfig.in_foam_answer, 1); //写入任务号 // _plc.writeStringByAddress(plcConfig.in_foam_task, taskInfo.taskCode); //写入完成后读取应答字进行复位 ReadLinerAnswer_InStore(taskInfo); result = 1; } else { result = 2; logHelper.PlcLog("内胆应答字为2,下发新任务plc未就绪"); } } else { logHelper.Info($"PLC信息为空,通过{taskInfo.storeCode}未获取到该仓库对应的PLC信息"); } } catch (Exception ex) { logHelper.Error("内胆入库任务下发异常", ex); } return result; } /// /// 读取内胆入库应答 /// private void ReadLinerAnswer_InStore(RealTaskInfo taskInfo) { lock (string.Empty) { bool isFlag = true; IPlc _plc = _plcDictionary[appConfig.linerStoreCode]; try { Task.Run(() => { if (_plc != null) { do { //读取PLC应答字为2时,上位机清空写入的入库内容 if (_plc.readInt32ByAddress(plcConfig.in_liner_answer) == 2) { logHelper.PlcLog("内胆入库应答字为2,货道号:" + plcConfig.in_liner_spaceCode + ";复位写0"); //写入货道号 _plc.writeInt32ByAddress(plcConfig.in_liner_spaceCode, 0); //写入应答字 // _plc.writeInt32ByAddress(plcConfig.in_foam_answer, 0); isFlag = false; InStoreAnswerEvent?.Invoke(appConfig.linerStoreCode, taskInfo.taskCode); //ReadLinerFinish_InStore(taskCode); // WritePlc(taskInfo.storeCode, taskInfo.spaceCode, false); // linerTaskInfos.Add(taskInfo); } Thread.Sleep(1000); } while (isFlag); } else { logHelper.Info($"PLC信息为空,通过{appConfig.linerStoreCode}未获取到该仓库对应的PLC信息"); } }); } catch (Exception ex) { logHelper.Error("读取内胆入库应答字异常", ex); } } } #endregion #region 读取PLC入库完成 Delete By Wenjy 2023-11-08 15:05:00,经讨论入库完成改为监测在途数量变化 /// /// 读取箱壳入库完成 /// /*private void ReadShellFinish_InStore(string taskCode) { lock (string.Empty) { bool isFlag = true; IPlc _plc = _plcDictionary[appConfig.shellStoreCode]; shellTaskCode = taskCode; try { Task.Run(() => { if (_plc != null) { do { //读取PLC入库任务完成 if (_plc.readInt32ByAddress(plcConfig.in_shell_finish) == 1) { _plc.writeInt32ByAddress(plcConfig.in_shell_finish, 0); //string taskCode = _plc.readStringByAddress(plcConfig.out_shell_task, 10); InStoreFinsihEvent?.Invoke(appConfig.shellStoreCode, taskCode); isFlag = false; } Thread.Sleep(1000); } while (isFlag); } else { logHelper.Info($"PLC信息为空,通过{appConfig.shellStoreCode}未获取到该仓库对应的PLC信息"); } }); } catch (Exception ex) { logHelper.Error("读取箱壳入库完成异常", ex); } } }*/ /// /// 读取内胆入库完成 /// /*private void ReadLinerFinish_InStore(string taskCode) { lock (string.Empty) { try { Task.Run(() => { bool isFlag = true; IPlc _plc = _plcDictionary[appConfig.linerStoreCode]; if (_plc != null) { do { //读取PLC入库任务完成 if (_plc.readInt32ByAddress(plcConfig.in_liner_finish) == 1) { _plc.writeInt32ByAddress(plcConfig.in_liner_finish, 0); //string taskCode = _plc.readStringByAddress(plcConfig.in_liner_task, 10); InStoreFinsihEvent?.Invoke(appConfig.linerStoreCode, taskCode); isFlag = false; } Thread.Sleep(1000); } while (isFlag); } else { logHelper.Info($"PLC信息为空,通过{appConfig.linerStoreCode}未获取到该仓库对应的PLC信息"); } }); } catch (Exception ex) { logHelper.Error("读取内胆入库完成异常", ex); } } }*/ #endregion #region 监测PLC在途数量变化,完成入库任务 delete by liuwf 2024-01-06,入库完成改成根据每个货道完成信号完成任务 //private void RealReadPlcSpace() //{ // Thread.Sleep(1000); // Task.Run(() => // { // while (true) // { // RealReadShellPlcSpace(); // Thread.Sleep(500); // } // }); // Task.Run(() => // { // while (true) // { // RealReadLinerPlcSpace(); // Thread.Sleep(500); // } // }); //} ///// ///// 读取箱壳已下发任务的货道信息,读取后将货道编号及在途数量写入Dictionary进行比较,在途数减少则入库完成 ///// //private void RealReadShellPlcSpace() //{ // if (shellTaskInfos != null) // { // List spaceCodes = shellTaskInfos.Select(x => x.spaceCode).Distinct().ToList(); // for (int i = 0; i < spaceCodes.Count; i++) // { // string spaceCode = spaceCodes[i]; // BaseSpaceInfo spaceInfo = new BaseSpaceInfo() { storeCode = appConfig.shellStoreCode, spaceCode = spaceCode }; // spaceInfo = ReadSpaceInfoByPlc(spaceInfo); // if (shellKeyValuePairs.ContainsKey(spaceInfo.spaceCode)) // { // shellKeyValuePairs.TryGetValue(spaceInfo.spaceCode, out int value); // //判断前次读取的数据和当前数据,如果前次数据大于当前数据则代表入库完成,然后筛选任务中对应货道的首个任务进行完成 // //如果前次数据不大于当前数据则更新字典中存放的数据 // if (value > spaceInfo.onRouteAmount) // { // //筛选任务 // var list = shellTaskInfos.Where(x => x.spaceCode == spaceInfo.spaceCode).ToList(); // if (list.Count > 0) // { // RealTaskInfo taskInfo = list.OrderBy(x => x.createTime).First(); // InStoreFinsihEvent?.Invoke(taskInfo.storeCode, taskInfo.taskCode); // shellTaskInfos.Remove(taskInfo); // } // shellKeyValuePairs.Remove(spaceInfo.spaceCode); // } // else // { // shellKeyValuePairs[spaceInfo.spaceCode] = spaceInfo.onRouteAmount; // } // } // else // { // shellKeyValuePairs.Add(spaceInfo.spaceCode, spaceInfo.onRouteAmount); // } // } // } //} ///// ///// 读取内胆已下发任务的货道信息,读取后将货道编号及在途数量写入Dictionary进行比较,在途数减少则入库完成 ///// //private void RealReadLinerPlcSpace() //{ // if (linerTaskInfos != null) // { // List spaceCodes = linerTaskInfos.Select(x => x.spaceCode).Distinct().ToList(); // for (int i = 0; i < spaceCodes.Count; i++) // { // string spaceCode = spaceCodes[i]; // BaseSpaceInfo spaceInfo = new BaseSpaceInfo() { storeCode = appConfig.linerStoreCode, spaceCode = spaceCode }; // spaceInfo = ReadSpaceInfoByPlc(spaceInfo); // if (linerKeyValuePairs.ContainsKey(spaceInfo.spaceCode)) // { // linerKeyValuePairs.TryGetValue(spaceInfo.spaceCode, out int value); // //判断前次读取的数据和当前数据,如果前次数据大于当前数据则代表入库完成,然后筛选任务中对应货道的首个任务进行完成 // //如果前次数据不大于当前数据则更新字典中存放的数据 // if (value > spaceInfo.onRouteAmount) // { // //筛选任务 // var list = linerTaskInfos.Where(x => x.spaceCode == spaceInfo.spaceCode).ToList(); // if (list.Count > 0) // { // RealTaskInfo taskInfo = list.OrderBy(x => x.createTime).First(); // InStoreFinsihEvent?.Invoke(taskInfo.storeCode, taskInfo.taskCode); // linerTaskInfos.Remove(taskInfo); // } // linerKeyValuePairs.Remove(spaceInfo.spaceCode); // } // else // { // linerKeyValuePairs[spaceInfo.spaceCode] = spaceInfo.onRouteAmount; // } // } // else // { // linerKeyValuePairs.Add(spaceInfo.spaceCode, spaceInfo.onRouteAmount); // } // } // } //} #endregion /// /// 通过PLC获取货道信息 /// /// /// public BaseSpaceInfo ReadSpaceInfoByPlc(BaseSpaceInfo spaceInfo) { try { var spaceAddress = spaceConfig.GetSpaceAddress(spaceInfo.storeCode, spaceInfo.spaceCode); IPlc _plc = _plcDictionary[spaceInfo.storeCode]; if (_plc != null) { spaceInfo.spaceStock = _plc.readInt32ByAddress(spaceAddress.onStore); spaceInfo.onRouteAmount = _plc.readInt32ByAddress(spaceAddress.onRoute); // spaceInfo.spaceStatus = _plc.readInt32ByAddress(spaceAddress.spaceStatus); } return spaceInfo; } catch (Exception ex) { logHelper.Error(ex.Message.ToString()); return spaceInfo; } } /// /// 测试方法,联调时不用 /// /// /// /// public void WritePlc(string storeCode, string spaceCode, bool flag) { var spaceAddress = spaceConfig.GetSpaceAddress(storeCode, spaceCode); IPlc _plc = _plcDictionary[storeCode]; if (_plc != null) { var spaceStock = _plc.readInt32ByAddress(spaceAddress.onStore); var onRouteAmount = _plc.readInt32ByAddress(spaceAddress.onRoute); if (flag) { _plc.writeInt32ByAddress(spaceAddress.onStore, spaceStock + 1); //_plc.writeInt32ByAddress(spaceAddress.onRoute, onRouteAmount - 1); } else { _plc.writeInt32ByAddress(spaceAddress.onRoute, onRouteAmount + 1); } } } } }