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 RegisterServices registerServices = RegisterServices.Instance; private JsonChange json = JsonChange.Instance; /// /// 货道信息 /// private IBaseSpaceInfoService _spaceInfoService; /// /// 已下传的任务信息 /// private List foamRearTaskInfos; /// /// 实时任务 /// private IRealTaskInfoService _taskInfoService; #endregion #region 私有变量 private static Dictionary _plcDictionary = new Dictionary(); private Dictionary foamRearKeyValuePairs = new Dictionary(); /// /// 泡后任务编号,PLC反馈后进行赋值 /// private string foamTaskCode = string.Empty; #endregion #region 委托事件 /// /// 实时库存刷新 /// /// public delegate void RefreshFoamStock(); public event RefreshFoamStock RefreshFoamStockEvent; /// /// 入库应答,PLC收到下发的入库任务后进行应答 /// /// /// public delegate void InStoreAnswer(string storeCode, string taskCode); public event InStoreAnswer InStoreAnswerEvent; /// /// 入库完成 /// /// public delegate void InStoreFinsih(string taskCode); public event InStoreFinsih InStoreFinsihEvent; /// /// 在途清0,完成货道任务 /// /// public delegate void InStoreFinsihBySpaceCode(string spaceCode); public event InStoreFinsihBySpaceCode InStoreFinsihBySpaceCodeEvent; #endregion private InStoreTaskHandle() { _taskInfoService = registerServices.GetService(); _spaceInfoService = registerServices.GetService(); _plcDictionary = _pool.GetAll(); // 程序启动查询数据库 foamRearTaskInfos = _taskInfoService.GetTaskInfosForInstore(appConfig.foamStoreCode, appConfig.instoreTaskType, 2); foamRearTaskInfos = new List(); RealReadPlcSpace(); SendHeart(); RealUpdateSpaceInfoByPlc(); // RealReadFinish(); change-入库完成信号改回监测在途数 // test(); } /// /// 实时读取plc更新数据库货道数量及在途 /// private void RealUpdateSpaceInfoByPlc() { Task.Run(() => { Thread.Sleep(2000); while (true) { List spaceList = _spaceInfoService.GetSpaceInfosByStoreCode(appConfig.foamStoreCode); if (spaceList != null && spaceList.Count > 0) { foreach (BaseSpaceInfo spaceInfo in spaceList) { int Stock = spaceInfo.spaceStock; int OnAmount = spaceInfo.onRouteAmount; ReadSpaceInfoByPlc(spaceInfo); if (spaceInfo.onRouteAmount == 0) { //自动完成该货道的所有入库任务 InStoreFinsihBySpaceCodeEvent?.Invoke(spaceInfo.spaceCode); //该货道没有入库任务了,重置货道出库标识,可以再次出库 spaceInfo.outStoreFlag = 1; _spaceInfoService.UpdateSpaceInfo(spaceInfo); RefreshFoamStockEvent?.Invoke(); // 删除多余入库任务 } if (spaceInfo.spaceStock == 0 && spaceInfo.onRouteAmount == 0) { // 清空型号供新型号使用 spaceInfo.materialType = null; spaceInfo.typeNameA = null; //重置货道,可以再次入库 spaceInfo.inStoreFlag = 1; spaceInfo.outStoreFlag = 1; _spaceInfoService.UpdateSpaceInfo(spaceInfo); RefreshFoamStockEvent?.Invoke(); } // 更新库存 if (Stock != spaceInfo.spaceStock || OnAmount != spaceInfo.onRouteAmount) { _spaceInfoService.UpdateSpaceInfo(spaceInfo); RefreshFoamStockEvent?.Invoke(); } } } Thread.Sleep(3000); } }); } /// /// 心跳 /// private void SendHeart() { Task.Run(() => { try { Thread.Sleep(3000); while (true) { Console.Write(_plcDictionary.Count); _plcDictionary = _pool.GetAll(); if (_plcDictionary.Count == 0) { Thread.Sleep(5000); continue; } IPlc _plc = _plcDictionary[appConfig.foamStoreCode]; if (_plc != null) { _plc.writeInt32ByAddress("D7020", 1); Thread.Sleep(1000); _plc.writeInt32ByAddress("D7020", 0); Thread.Sleep(1000); } } } catch (Exception ex) { logHelper.Error(ex.Message.ToString()); } }); } #region 判断入库是否完成 delete20240224-入库完成信号改回监测在途数 /// /// 实时读取入库完成信号 /// private void RealReadFinish() { Task.Run(() => { Thread.Sleep(2000); while (true) { if (_plcDictionary.Count > 0) { IPlc _plc = _plcDictionary[appConfig.foamStoreCode]; if (_plc != null) { List taskList = _taskInfoService.GetTaskInfosForInstore(appConfig.foamStoreCode, appConfig.instoreTaskType, 2); if (taskList != null && taskList.Count > 0) { foreach (RealTaskInfo taskInfo in taskList) { SpaceAddress spaceAddress = spaceConfig.GetSpaceAddress(appConfig.foamStoreCode, taskInfo.spaceCode); JudgeIsFinish(taskInfo, _plc, spaceAddress); } } } else { logHelper.Info($"PLC信息为空或连接失败,通过{appConfig.foamStoreCode}未获取到该仓库对应的PLC信息"); } } Thread.Sleep(1000); }; }); } /// /// 判断该任务对应的货道是否有完成信号 /// /// /// public void JudgeIsFinish(RealTaskInfo taskInfo, IPlc _plc, SpaceAddress spaceAddress) { // 测试方法,模拟plc,正式启用删除 // _plc.writeInt32ByAddress(spaceAddress.inStoreFinish, 1); //读取入库完成反馈信号 if (_plc.readInt32ByAddress(spaceAddress.inStoreFinish) == 1) { _plc.writeInt32ByAddress(spaceAddress.inStoreFinish, 0); InStoreFinsihEvent(taskInfo.taskCode); } } #endregion #region 监测PLC在途数量变化,完成入库任务 private void RealReadPlcSpace() { Thread.Sleep(5000); Task.Run(() => { while (true) { RealReadShellPlcSpace(); Thread.Sleep(500); } }); } /// /// 读取箱壳已下发任务的货道信息,读取后将货道编号及在途数量写入Dictionary进行比较,在途数减少则入库完成 /// private void RealReadShellPlcSpace() { if (foamRearTaskInfos != null && foamRearTaskInfos.Count > 0) { List spaceCodes = foamRearTaskInfos.Select(x => x.spaceCode).Distinct().ToList(); for (int i = 0; i < spaceCodes.Count; i++) { string spaceCode = spaceCodes[i]; BaseSpaceInfo spaceInfo = new BaseSpaceInfo() { storeCode = appConfig.foamStoreCode, spaceCode = spaceCode }; spaceInfo = ReadSpaceInfoByPlc(spaceInfo); if (foamRearKeyValuePairs.ContainsKey(spaceInfo.spaceCode)) { foamRearKeyValuePairs.TryGetValue(spaceInfo.spaceCode, out int value); //判断前次读取的数据和当前数据,如果前次数据大于当前数据则代表入库完成,然后筛选任务中对应货道的首个任务进行完成 //如果前次数据不大于当前数据则更新字典中存放的数据 if (value > spaceInfo.onRouteAmount) { //筛选任务 var list = foamRearTaskInfos.Where(x => x.spaceCode == spaceInfo.spaceCode).ToList(); if (list.Count > 0) { RealTaskInfo taskInfo = list.OrderBy(x => x.createTime).First(); InStoreFinsihEvent?.Invoke(taskInfo.taskCode); foamRearTaskInfos.Remove(taskInfo); } foamRearKeyValuePairs.Remove(spaceInfo.spaceCode); } else { foamRearKeyValuePairs[spaceInfo.spaceCode] = spaceInfo.onRouteAmount; } } else { foamRearKeyValuePairs.Add(spaceInfo.spaceCode, spaceInfo.onRouteAmount); } } } } #endregion #region 泡后入库任务下发处理 public int SendFoamTask_InStore(RealTaskInfo taskInfo) { int result = 0; try { IPlc _plc = _plcDictionary[taskInfo.storeCode]; if (_plc != null) { if (_plc.readInt32ByAddress(plcConfig.in_foam_answer) == 1) { logHelper.Info("泡后入库应答字为1,货道号:" + plcConfig.in_foam_spaceCode + ";写" + short.Parse(taskInfo.spaceCode.Substring(5, 1))); //写入货道号 _plc.writeInt32ByAddress(plcConfig.in_foam_spaceCode, short.Parse(taskInfo.spaceCode.Substring(5, 1))); //写入应答字 // _plc.writeInt32ByAddress(plcConfig.in_foam_answer, 1); //写入任务号 // _plc.writeStringByAddress(plcConfig.in_foam_task, taskInfo.taskCode); //写入完成后读取应答字进行复位 ReadAnswer_InStore(taskInfo); result = 1; } else { result = 2; logHelper.Info("应答字为2,下发新任务plc未就绪"); } } else { logHelper.Info($"PLC信息为空,通过{taskInfo.storeCode}未获取到该仓库对应的PLC信息"); } } catch (Exception ex) { logHelper.Error("泡后入库任务下发异常", ex); } return result; } /// /// 读取泡后入库应答 /// private void ReadAnswer_InStore(RealTaskInfo taskInfo) { lock (string.Empty) { bool isFlag = true; IPlc _plc = _plcDictionary[appConfig.foamStoreCode]; try { Task.Run(() => { if (_plc != null) { do { // 测试方法,模拟plc,正式启用删除 // _plc.writeInt32ByAddress(plcConfig.in_foam_answer, 2); //读取PLC应答字为2时,上位机清空写入的入库内容 if (_plc.readInt32ByAddress(plcConfig.in_foam_answer) == 2) { logHelper.PlcLog("入库应答字为2,货道号:" + plcConfig.in_foam_spaceCode + ";复位写0"); //写入货道号 _plc.writeInt32ByAddress(plcConfig.in_foam_spaceCode, 0); //写入应答字 // _plc.writeInt32ByAddress(plcConfig.in_foam_answer, 0); isFlag = false; foamRearTaskInfos.Add(taskInfo); InStoreAnswerEvent?.Invoke(appConfig.foamStoreCode, taskInfo.taskCode); } Thread.Sleep(500); } while (isFlag); } else { logHelper.Info($"PLC信息为空,通过{appConfig.foamStoreCode}未获取到该仓库对应的PLC信息"); } }); } catch (Exception ex) { logHelper.Error("读取泡后入库应答字异常", ex); } } } #endregion /// /// 0正常,清除plc报警信息,1-扫码失败;2-库满,未匹配到可用货道 /// /// #region 泡后库plc报警信息 public void SendPlcWarnInfo(short flag) { try { IPlc _plc = _plcDictionary[appConfig.foamStoreCode]; if (_plc != null) { _plc.writeInt32ByAddress("D7018", flag); } } catch (Exception ex) { logHelper.Error(ex.Message.ToString()); } } #endregion /// /// 通过PLC获取货道信息 /// /// /// public BaseSpaceInfo ReadSpaceInfoByPlc(BaseSpaceInfo spaceInfo) { var spaceAddress = spaceConfig.GetSpaceAddress(spaceInfo.storeCode, spaceInfo.spaceCode); _plcDictionary = _pool.GetAll(); if (_plcDictionary.Count > 0) { 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; } #region 2023-01-04delete 入库逻辑修改,删除引用方法 /// /// 读取泡后入库完成 /// //private void ReadShellFinish_InStore(string taskCode) //{ // lock (string.Empty) // { // bool isFlag = true; // IPlc _plc = _plcDictionary[appConfig.foamStoreCode]; // foamTaskCode = taskCode; // try // { // Task.Run(() => // { // if (_plc != null) // { // // do // { // //读取PLC入库任务完成 // if (_plc.readInt32ByAddress(plcConfig.in_foam_finish) == 1) // { // _plc.writeInt32ByAddress(plcConfig.in_foam_finish, 0); // //string taskCode = _plc.readStringByAddress(plcConfig.out_foam_task, 10); // InStoreFinsihEvent?.Invoke(taskCode); // isFlag = false; // } // Thread.Sleep(1000); // } while (isFlag); // // } // else // { // logHelper.Info($"PLC信息为空,通过{appConfig.foamStoreCode}未获取到该仓库对应的PLC信息"); // } // }); // } // catch (Exception ex) // { // logHelper.Error("读取泡后入库完成异常", ex); // } // } //} #endregion } }