using Aucma.Core.Scanner; using Aucma.Scada.HikRobot; using Aucma.Scada.Model.domain; using Aucma.Scada.Model.dto; using HighWayIot.Config; using HighWayIot.Log4net; using HighWayIot.Repository.service; using HighWayIot.TouchSocket; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Timers; using static Aucma.Core.Scanner.MvCodeHelper; using static Aucma.Scada.Business.InStoreTaskHandle; namespace Aucma.Scada.Business { /// /// 入库业务逻辑 /// public sealed class InStoreBusiness { #region 单例实现 private static readonly Lazy lazy = new Lazy(() => new InStoreBusiness()); public static InStoreBusiness Instance { get { return lazy.Value; } } #endregion #region 对象引用 private LogHelper logHelper = LogHelper.Instance; private AppConfig appConfig = AppConfig.Instance; private RegisterServices registerServices = RegisterServices.Instance; private InStoreTaskHandle taskHandle = InStoreTaskHandle.Instance; #endregion #region 接口引用 /// /// 货道信息 /// private IBaseSpaceInfoService _spaceInfoService; /// /// 实时任务 /// private IRealTaskInfoService _taskInfoService; private IBaseSpaceDetailService _baseSpaceDetailService; private IBaseBomInfoService _baseBomInfoService; private IRecordInStoreService _recordInStoreService; private IPrintBarCodeServices _printBarCodeServices; private IProductPlanInfoService _productPlanInfoServices; // 过点数据表 private IMaterialCompletionServices _iMaterialCompletionServices; #endregion #region 委托事件 /// /// 初始化入库任务 /// /// public delegate void RefreshInStoreTask(RealTaskInfo taskInfos,bool isFinsih = false); public event RefreshInStoreTask RefreshInStoreTaskEvent; /// /// 扫码信息刷新 /// /// /// /// /// public delegate void RefreshScanMateriaCode(string materialCode, string materialName, string spaceName, string materialType); public event RefreshScanMateriaCode RefreshScanMateriaCodeEvent; /// /// 日志信息刷新 /// /// public delegate void RefreshLogMessage(string message); public event RefreshLogMessage RefreshLogMessageEvent; /// /// 出库状态检测 /// System.Timers.Timer timer = new System.Timers.Timer(1000 * 5); #endregion #region 自定义变量 #endregion private InStoreBusiness() { _spaceInfoService = registerServices.GetService(); _taskInfoService = registerServices.GetService(); _baseSpaceDetailService = registerServices.GetService(); _baseBomInfoService = registerServices.GetService(); _recordInStoreService = registerServices.GetService(); _printBarCodeServices = registerServices.GetService(); _iMaterialCompletionServices = registerServices.GetService(); _productPlanInfoServices = registerServices.GetService(); taskHandle.InStoreFinsihEvent += InStoreFinish; taskHandle.InStoreAnswerEvent += InStoreAnswer; taskHandle.InStoreFinsihBySpaceCodeEvent += InStoreAllBySpaceCode; // MvCodeHelper.RefreshMaterialCodeStrEvent += InStore; // MvCodeHelper.RefreshLogMessageEvent += PrintLogInfoMessage; TouchSocketBusiness.MessageNoReadEvent += MessageNoRead; TouchSocketBusiness.RefreshMaterialCodeStrEvent += InStore; StartPassDown(); //Task.Run(() => //{ // Thread.Sleep(3000); // for (int i = 1; i < 2; i++) // { // // 8302501181 8302500586 9 // InStore("B2401018302500586001" + i); // Thread.Sleep(1000); // } // InStore("B2401018302501181001" + 1); // Thread.Sleep(1000); // InStore("B2401018302500889001" + 1); //}); } #region delete 2024-01-04出库时堵塞入库 ,不需要堵塞整个库 private bool _isBlocked = false; private AutoResetEvent _autoResetEvent = new AutoResetEvent(false); /// /// 出库状态检测(出库时不能入库) /// public void ListenOutStatus() { timer.Elapsed += new System.Timers.ElapsedEventHandler(RefreshOutStatus); timer.AutoReset = true; timer.Enabled = true; timer.Start(); } public Semaphore outSemaphore = new Semaphore(1, 1); public void RefreshOutStatus(object source, System.Timers.ElapsedEventArgs e) { outSemaphore.WaitOne(); try { List outTasks = _taskInfoService.GetAllTaskInfoByStoreCode(appConfig.foamStoreCode, 2); if (outTasks == null || outTasks.Count==0) { //没有正在出库的任务,释放信号量,可以入库 _isBlocked = false; _autoResetEvent.Set(); } else { _isBlocked = true; } }catch(Exception ex) { Console.WriteLine(ex.ToString()); } finally { outSemaphore.Release(); } } #endregion /// /// 入库 /// /// public void InStore(string materialCode) { try { // plc报警信息清除 taskHandle.SendPlcWarnInfo(0); PrintLogInfoMessage($"扫码成功,物料码:{materialCode}"); string materialType = SubStringMaterialCode(materialCode); // 加防呆,判断任务表是否有任务了 #region Delete By wenjy 2023-10-30 11:41:00 取消通过数据库获取货道数量、在途量,改为通过PLC获取货道信息 // var spaceInfo = _spaceInfoService.InStoreGetSpaceInfoByMaterialType(appConfig.foamStoreCode, materialType); #endregion // 筛选货道的时候,不能选择正在出库的货道 var spaceInfo = GetSpaceInfoByMaterialType(appConfig.foamStoreCode, materialType); if (spaceInfo != null) { PrintLogInfoMessage($"匹配货道:{spaceInfo.spaceName}"); spaceInfo.materialType = materialType; spaceInfo.typeNameA = GetMaterialName(materialType); // 入库禁止出库 spaceInfo.outStoreFlag = 2; RefreshScanMateriaCodeEvent?.Invoke(materialCode, GetMaterialName(materialType), spaceInfo.spaceName, appConfig.foamStoreCode); //刷新界面扫码信息 var result = CreateInStoreTask(spaceInfo, materialCode); //创建入库任务 if (result) { _spaceInfoService.UpdateSpaceInfo(spaceInfo); } } else { //报警停线 PrintLogInfoMessage($"物料码:{materialCode};未匹配到可用货道"); // 下发plc报警信息 taskHandle.SendPlcWarnInfo(2); } } catch (Exception ex) { PrintLogErrorMessage("入库业务异常", ex); } } /// /// 根据指定货道异常入库 /// public void ExceptionInstore(string spaceCode) { BaseSpaceInfo space = _spaceInfoService.GetSpaceInfoBySpaceCode(appConfig.foamStoreCode,spaceCode); if(space != null) { CreateInStoreTask(space, ""); } } /// /// 创建入库任务 /// /// private bool CreateInStoreTask(BaseSpaceInfo spaceInfo, string materialCode) { bool result = false; #region 任务赋值 RealTaskInfo realTaskInfo = new RealTaskInfo(); realTaskInfo.taskType = 1; realTaskInfo.taskCode = System.Guid.NewGuid().ToString("N").Substring(0, 6); realTaskInfo.storeCode = spaceInfo.storeCode; realTaskInfo.spaceCode = spaceInfo.spaceCode; realTaskInfo.materialType = spaceInfo.materialType; realTaskInfo.materialCode = materialCode; realTaskInfo.planAmount = 1; realTaskInfo.taskStatus = 1; realTaskInfo.createTime = DateTime.Now; #endregion result = _taskInfoService.AddTaskInfo(realTaskInfo); if (result) { PrintLogInfoMessage("入库任务创建成功"); RefreshInStoreTaskEvent?.Invoke(realTaskInfo); result = true; } else { PrintLogInfoMessage("入库任务创建失败"); } return result; } #region 轮询获取入库任务下发至PLC,等待PLC执行反馈,完成后再次下发 private SemaphoreSlim semaphore = new SemaphoreSlim(0); private void StartPassDown() { ////筛选货道的时候,不能选择正在出库的货道 //List outTasks = _taskInfoService.GetTaskInfosByTaskStatus(new string[] { appConfig.foamStoreCode }, 1, 2); ////筛选info集合中spaceCode不等于outTasks集合spaceCode的列表 //if (outTasks.Count > 0) //{ // list = info.Where(i => !outTasks.Any(o => o.spaceCode == i.spaceCode)).ToList(); //} Task.Run(() => { Thread.Sleep(2000); while (true) { PassDownFoamTask(); Thread.Sleep(500); } }); } /// /// 依次获取泡后任务队列进行下发 /// /// /// private void PassDownFoamTask() { try { RealTaskInfo taskInfo = _taskInfoService.GetTaskInfoByStoreCode(appConfig.foamStoreCode, 1); if (taskInfo != null) { PrintLogInfoMessage($"下发泡后入库任务:{taskInfo.taskCode};仓库{taskInfo.storeCode};货道:{taskInfo.spaceCode}"); int result = taskHandle.SendFoamTask_InStore(taskInfo); if (result==1) { PrintLogInfoMessage($"泡后入库任务:{taskInfo.taskCode};下发成功,等待PLC执行反馈"); semaphore.Wait(); //一直堵塞直到信号量释放 PrintLogInfoMessage($"入库任务:{taskInfo.taskCode};开始执行"); taskInfo.taskStatus = 2; _taskInfoService.UpdateTaskInfo(taskInfo); RefreshInStoreTaskEvent?.Invoke(taskInfo); } else if(result==2) { PrintLogInfoMessage("泡后入库任务下发失败,PLC接收任务未就绪"); } else { PrintLogInfoMessage($"泡后入库任务:{taskInfo.taskCode};下发失败,请排除PLC连接"); } } else { logHelper.Info("未获取到需要下发的泡后入库任务"); // PrintLogInfoMessage("未获取到需要下发的泡后入库任务"); } } catch (Exception ex) { PrintLogErrorMessage("依次获取入库任务队列进行下发逻辑异常", ex); } } /// /// 入库应答,PLC收到入库任务后进行应答 /// /// /// private void InStoreAnswer(string storeCode, string taskCode) { if (storeCode == appConfig.foamStoreCode) { PrintLogInfoMessage("plc入库应答成功,自动释放信号量,进行下发新任务"); semaphore.Release(); } } ///// ///// 泡后执行反馈 ///// //private void FoamTaskFeedback(string taskCode) //{ // PrintLogInfoMessage("泡后执行完成,自动释放信号量"); // InStoreFinish(taskCode); // semaphore.Release(); //} #endregion /// /// 在途为0后,根据货道完成该货道所有入库任务 /// /// public void InStoreAllBySpaceCode(string spaceCode) { try { List list = _taskInfoService.GetAllInstoreTaskByStoreCode(appConfig.foamStoreCode, spaceCode, 1, 2); if (list != null && list.Count > 0) { foreach (RealTaskInfo task in list) { InStoreFinish(task.taskCode); } } } catch (Exception ex) { logHelper.Info("InStoreAllBySpaceCode异常:"+ex.Message.ToString()); } } /// /// 入库完成 /// /// /// /// public void InStoreFinish(string taskCode) { try { var taskInfo = _taskInfoService.GetTaskInfoByTaskCode(taskCode, appConfig.foamStoreCode); if (taskInfo != null) { #region 添加入库记录 RecordInstore recordInstore = new RecordInstore(); recordInstore.storeCode = taskInfo.storeCode; recordInstore.spaceCode = taskInfo.spaceCode; recordInstore.materialCode = taskInfo.materialCode; recordInstore.materialType = taskInfo.materialType; recordInstore.materialName = GetMaterialName(taskInfo.materialType); recordInstore.inStoreAmount = 1; recordInstore.inStoreTime = DateTime.Now; recordInstore.barcodeCode = taskInfo.materialCode; _recordInStoreService.InsertRecordInStore(recordInstore); #endregion #region 更新过点数据,插入记录到MATERIAL_COMPLETION表 if (!string.IsNullOrEmpty(taskInfo.materialCode)) { PrintBarCode print = _printBarCodeServices.query(taskInfo.materialCode); if (print != null) { string planCode = _productPlanInfoServices.GetPlanCode(print.OrderCode, appConfig.stationCode); MaterialCompletion completion = new MaterialCompletion(); completion.OrderCode = print.OrderCode; completion.MaterialBarcode = taskInfo.materialCode; completion.MaterialCode = print.MaterialCode; completion.MaterialName = print.MaterialName; completion.StationName = appConfig.stationCode; completion.CompleteDate = DateTime.Now; completion.planCode = planCode; completion.ProductLineCode = "CX_02"; _iMaterialCompletionServices.Add(completion); } } #endregion #region 添加货道明细,取消记录库存明细 BaseSpaceDetail spaceDetail = new BaseSpaceDetail(); spaceDetail.materialType = taskInfo.materialType; spaceDetail.materialCode = taskInfo.materialCode; spaceDetail.materialName = GetMaterialName(taskInfo.materialType); spaceDetail.storeCode = appConfig.foamStoreCode; spaceDetail.spaceCode = taskInfo.spaceCode; spaceDetail.materialAmount = 1; spaceDetail.createTime = DateTime.Now; _baseSpaceDetailService.InsertSpaceDetail(spaceDetail); #endregion //清除任务信息 _taskInfoService.DeleteTaskInfo(taskCode, appConfig.foamStoreCode); } RefreshInStoreTaskEvent?.Invoke(taskInfo, true); } catch (Exception ex) { PrintLogErrorMessage("入库完成逻辑处理异常", ex); } } /// /// 日志输出,界面刷新同时记录文件 /// /// private void PrintLogInfoMessage(string message) { RefreshLogMessageEvent?.Invoke(message); logHelper.Info(message); } /// /// 未扫描上条码 /// /// private void MessageNoRead() { PrintLogInfoMessage("入库扫码器未扫上条码"); // 下发plc报警信号 taskHandle.SendPlcWarnInfo(1); } /// /// 异常日志输出 /// /// /// private void PrintLogErrorMessage(string message, Exception ex = null) { RefreshLogMessageEvent?.Invoke(message); logHelper.Error(message, ex); } /// /// 截条码类型 /// /// /// private string SubStringMaterialCode(string materialCode) { string result = string.Empty; if (!string.IsNullOrEmpty(materialCode)) { result = materialCode.Substring(7, 10); } return result; } public List GetInStoreTask() { return _taskInfoService.GetTaskInfosByStoreCode(new string[] { appConfig.foamStoreCode }, 1); } /// /// 通过BOM获取物料名称 /// /// /// public string GetMaterialName(string materialType) { string materialName = string.Empty; var info = _baseBomInfoService.GetBomInfoByMaterialCode(materialType); if (info != null) { materialName = info.materialName; } return materialName; } /// /// 库存统计 /// /// public List GetFoamStoreStock() { List groups = null; try { var info = _spaceInfoService.GetSpaceInfosByStoreCode(appConfig.foamStoreCode); if (info != null) { groups = info.GroupBy(x => x.materialType).Select(y => new FoamStoreStockDto() { materialType = y.Key, storeStock = y.Sum(bs => bs.spaceStock) }).ToList(); } }catch(Exception ex) { PrintLogErrorMessage("库存统计异常", ex); } return groups; } /// 通过PLC读取货道信息(在途数量、在库数量、货道状态) /// /// /// /// private BaseSpaceInfo GetSpaceInfoByMaterialType(string storeCode, string materialType) { BaseSpaceInfo result = null; try { List info = _spaceInfoService.GetBaseSpaceInfosByMaterialType(storeCode, materialType,1); if (info != null) { if (info.Count > 0) { foreach (BaseSpaceInfo item in info) { var spaceInfo = taskHandle.ReadSpaceInfoByPlc(item); item.spaceStock = spaceInfo.spaceStock; item.onRouteAmount = spaceInfo.onRouteAmount; item.spaceStatus = spaceInfo.spaceStatus; } var list = info.Where(x => x.spaceStatus == 1 && x.spaceStock > 0 ? x.spaceCapacity > (x.spaceStock + x.onRouteAmount) : 1 == 1).ToList(); if(list==null || list.Count == 0) { list = _spaceInfoService.GetEmptySpaceInfo(appConfig.foamStoreCode); } if (list.Count > 0) { result = list.OrderByDescending(x => x.spaceStock).First(); } } } } catch (Exception ex) { PrintLogErrorMessage("货道信息plc读取异常", ex); } return result; } #region delete 2024-01-04 入库引用方法(修改入库逻辑) /// /// 开始出库堵塞入库任务下发 /// public void IssueOutTask() { PrintLogInfoMessage("执行出库任务,入库进行堵塞"); _isBlocked = true; _autoResetEvent.Set(); } /// /// 出库完成,释放入库任务下发 /// public void IssueInTask() { PrintLogInfoMessage("出库完成,释放入库"); _isBlocked = false; _autoResetEvent.Set(); } /// /// 获取待执行的入库任务 /// /// /// private RealTaskInfo GetAwaitSendTask(string storeCode) { RealTaskInfo taskInfo = null; try { taskInfo = _taskInfoService.GetTaskInfoByStoreCode(storeCode, 1); } catch (Exception ex) { PrintLogErrorMessage("获取待执行的入库任务异常", ex); } return taskInfo; } #endregion public BaseSpaceInfo GetSpaceInfo(string spaceCode) { return _spaceInfoService.GetSpaceInfoBySpaceCode(appConfig.foamStoreCode,spaceCode); } } }