You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Aucma.Scada/Aucma.Scada.Business/InStoreBusiness.cs

632 lines
23 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using static Aucma.Scada.Business.InStoreTaskHandle;
namespace Aucma.Scada.Business
{
/// <summary>
/// 入库业务逻辑
/// </summary>
public sealed class InStoreBusiness
{
#region 单例实现
private static readonly Lazy<InStoreBusiness> lazy = new Lazy<InStoreBusiness>(() => 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 接口引用
/// <summary>
/// 货道信息
/// </summary>
private IBaseSpaceInfoService _spaceInfoService;
/// <summary>
/// 实时任务
/// </summary>
private IRealTaskInfoService _taskInfoService;
private IBaseSpaceDetailService _baseSpaceDetailService;
private IBaseBomInfoService _baseBomInfoService;
private IRecordInStoreService _recordInStoreService;
private IPrintBarCodeServices _printBarCodeServices;
private IProductPlanInfoService _productPlanInfoServices;
// 过点数据表
private IMaterialCompletionServices _iMaterialCompletionServices;
#endregion
#region 委托事件
/// <summary>
/// 初始化入库任务
/// </summary>
/// <param name="message"></param>
public delegate void RefreshInStoreTask(RealTaskInfo taskInfos,bool isFinsih = false);
public event RefreshInStoreTask RefreshInStoreTaskEvent;
/// <summary>
/// 扫码信息刷新
/// </summary>
/// <param name="materialCode"></param>
/// <param name="materialName"></param>
/// <param name="spaceName"></param>
/// <param name="materialType"></param>
public delegate void RefreshScanMateriaCode(string materialCode, string materialName, string spaceName, string materialType);
public event RefreshScanMateriaCode RefreshScanMateriaCodeEvent;
/// <summary>
/// 日志信息刷新
/// </summary>
/// <param name="message"></param>
public delegate void RefreshLogMessage(string message);
public event RefreshLogMessage RefreshLogMessageEvent;
/// <summary>
/// 出库状态检测
/// </summary>
System.Timers.Timer timer = new System.Timers.Timer(1000 * 5);
#endregion
#region 自定义变量
#endregion
private InStoreBusiness()
{
_spaceInfoService = registerServices.GetService<IBaseSpaceInfoService>();
_taskInfoService = registerServices.GetService<IRealTaskInfoService>();
_baseSpaceDetailService = registerServices.GetService<IBaseSpaceDetailService>();
_baseBomInfoService = registerServices.GetService<IBaseBomInfoService>();
_recordInStoreService = registerServices.GetService<IRecordInStoreService>();
_printBarCodeServices = registerServices.GetService<IPrintBarCodeServices>();
_productPlanInfoServices = registerServices.GetService<IProductPlanInfoService>();
taskHandle.InStoreFinsihEvent += InStoreFinish;
taskHandle.InStoreAnswerEvent += InStoreAnswer;
MvCodeHelper.RefreshMaterialCodeStrEvent += InStore;
MvCodeHelper.RefreshLogMessageEvent += PrintLogInfoMessage;
StartPassDown();
//Task.Run(() =>
//{
// Thread.Sleep(6000);
// for (int i = 1; i < 2; i++)
// {
// InStore("B24010181060282920010");
// Thread.Sleep(1000);
// }
//});
}
#region delete 2024-01-04出库时堵塞入库 ,不需要堵塞整个库
private bool _isBlocked = false;
private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
/// <summary>
/// 出库状态检测(出库时不能入库)
/// </summary>
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<RealTaskInfo> 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
/// <summary>
/// 入库
/// </summary>
/// <param name="materialType"></param>
private void InStore(string materialCode)
{
try
{
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;
RefreshScanMateriaCodeEvent?.Invoke(materialCode, GetMaterialName(materialType), spaceInfo.spaceName, appConfig.foamStoreCode); //刷新界面扫码信息
var result = CreateInStoreTask(spaceInfo, materialCode); //创建入库任务
if (result)
{
#region delete/20240105/正式生产启用 更新过点数据,插入记录到MATERIAL_COMPLETION表
//PrintBarCode print = _printBarCodeServices.query(materialCode);
//string planCode = _productPlanInfoServices.GetPlanCode(print.OrderCode, appConfig.stationCode);
//MaterialCompletion completion = new MaterialCompletion();
//completion.OrderCode = print.OrderCode;
//completion.MaterialBarcode = materialCode;
//completion.MaterialCode = print.MaterialCode;
//completion.MaterialName = print.MaterialName;
//completion.StationName = appConfig.stationCode;
//completion.CompleteDate = DateTime.Now;
//completion.planCode = planCode;
//_iMaterialCompletionServices.Add(completion);
#endregion
_spaceInfoService.UpdateSpaceInfo(spaceInfo);
}
}
else
{
//报警停线
PrintLogInfoMessage($"物料码:{materialCode};未匹配到可用货道");
}
}
catch (Exception ex)
{
PrintLogErrorMessage("入库业务异常", ex);
}
}
/// <summary>
/// 创建入库任务
/// </summary>
/// <param name="spaceInfo"></param>
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<RealTaskInfo> 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);
}
});
}
/// <summary>
/// 依次获取泡后任务队列进行下发
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void PassDownFoamTask()
{
#region 出库不需要堵塞整个库
////出库时堵塞入库,这个地方如果有问题的话改为操作StartPassDown将_isBlocked初始值true给while开始出库时设成false结束while出库完成后改为ture重新调用StartPassDown
//if (_isBlocked)
//{
// PrintLogInfoMessage("正在出库,当前入库状态堵塞,出库完成后进行释放");
// _autoResetEvent.WaitOne();
//}
#endregion
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
{
PrintLogInfoMessage("未获取到需要下发的泡后入库任务");
}
}
catch (Exception ex)
{
PrintLogErrorMessage("依次获取入库任务队列进行下发逻辑异常", ex);
}
}
/// <summary>
/// 入库应答PLC收到入库任务后进行应答
/// </summary>
/// <param name="storeCode"></param>
/// <param name="taskCode"></param>
private void InStoreAnswer(string storeCode, string taskCode)
{
if (storeCode == appConfig.foamStoreCode)
{
PrintLogInfoMessage("plc入库应答成功自动释放信号量进行下发新任务");
semaphore.Release();
}
}
///// <summary>
///// 泡后执行反馈
///// </summary>
//private void FoamTaskFeedback(string taskCode)
//{
// PrintLogInfoMessage("泡后执行完成,自动释放信号量");
// InStoreFinish(taskCode);
// semaphore.Release();
//}
#endregion
/// <summary>
/// 入库完成
/// </summary>
/// <param name="storeCode"></param>
/// <param name="spaceCode"></param>
/// <param name="materialType"></param>
private void InStoreFinish(string taskCode)
{
try
{
var taskInfo = _taskInfoService.GetTaskInfoByTaskCode(taskCode, appConfig.foamStoreCode);
if (taskInfo != null)
{
var spaceInfo = _spaceInfoService.GetSpaceInfoBySpaceCode(taskInfo.storeCode, taskInfo.spaceCode);
if (spaceInfo != null)
{
spaceInfo.materialType = taskInfo.materialType;
//spaceInfo.spaceStock = spaceInfo.spaceStock + 1;
//spaceInfo.onRouteAmount -= 1;
//读取PLC获取货道信息存放数量、在途数量,
#region Add By wenjy 2023-10-30 13:44:00 通过PLC获取货道信息
var item = taskHandle.ReadSpaceInfoByPlc(spaceInfo);
spaceInfo.spaceStock = item.spaceStock;
spaceInfo.onRouteAmount = item.onRouteAmount;
spaceInfo.spaceStatus = item.spaceStatus;
#endregion
_spaceInfoService.UpdateSpaceInfo(spaceInfo);
#region 添加货道明细
BaseSpaceDetail spaceDetail = new BaseSpaceDetail();
spaceDetail.materialType = taskInfo.materialType;
spaceDetail.materialCode = taskInfo.materialCode;
spaceDetail.materialName = GetMaterialName(taskInfo.materialType);
spaceDetail.storeCode = spaceInfo.storeCode;
spaceDetail.spaceCode = spaceInfo.spaceCode;
spaceDetail.materialAmount = 1;
_baseSpaceDetailService.InsertSpaceDetail(spaceDetail);
#endregion
#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
}
//清除任务信息
_taskInfoService.DeleteTaskInfo(taskCode, appConfig.foamStoreCode);
}
RefreshInStoreTaskEvent?.Invoke(taskInfo, true);
}
catch (Exception ex)
{
PrintLogErrorMessage("入库完成逻辑处理异常", ex);
}
}
/// <summary>
/// 日志输出,界面刷新同时记录文件
/// </summary>
/// <param name="message"></param>
private void PrintLogInfoMessage(string message)
{
RefreshLogMessageEvent?.Invoke(message);
logHelper.Info(message);
}
/// <summary>
/// 异常日志输出
/// </summary>
/// <param name="message"></param>
/// <param name="ex"></param>
private void PrintLogErrorMessage(string message, Exception ex = null)
{
RefreshLogMessageEvent?.Invoke(message);
logHelper.Error(message, ex);
}
/// <summary>
/// 截条码类型
/// </summary>
/// <param name="materialCode"></param>
/// <returns></returns>
private string SubStringMaterialCode(string materialCode)
{
string result = string.Empty;
if (!string.IsNullOrEmpty(materialCode))
{
result = materialCode.Substring(7, 10);
}
return result;
}
public List<RealTaskInfo> GetInStoreTask()
{
return _taskInfoService.GetTaskInfosByStoreCode(new string[] { appConfig.foamStoreCode }, 1);
}
/// <summary>
/// 通过BOM获取物料名称
/// </summary>
/// <param name="materialType"></param>
/// <returns></returns>
public string GetMaterialName(string materialType)
{
string materialName = string.Empty;
var info = _baseBomInfoService.GetBomInfoByMaterialCode(materialType);
if (info != null)
{
materialName = info.materialName;
}
return materialName;
}
/// <summary>
/// 库存统计
/// </summary>
/// <returns></returns>
public List<FoamStoreStockDto> GetFoamStoreStock()
{
List<FoamStoreStockDto> 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读取货道信息在途数量、在库数量、货道状态)
/// </summary>
/// <param name="storeCode"></param>
/// <param name="materialType"></param>
/// <returns></returns>
private BaseSpaceInfo GetSpaceInfoByMaterialType(string storeCode, string materialType)
{
BaseSpaceInfo result = null;
try
{
List<BaseSpaceInfo> info = _spaceInfoService.GetBaseSpaceInfosByMaterialType(storeCode, materialType);
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.Count > 0)
{
result = list.OrderByDescending(x => x.spaceStock).OrderBy(x => x.spaceCode).First();
}
}
}
}
catch (Exception ex)
{
PrintLogErrorMessage("货道信息读取异常", ex);
}
return result;
}
#region delete 2024-01-04 入库引用方法(修改入库逻辑)
/// <summary>
/// 开始出库堵塞入库任务下发
/// </summary>
public void IssueOutTask()
{
PrintLogInfoMessage("执行出库任务,入库进行堵塞");
_isBlocked = true;
_autoResetEvent.Set();
}
/// <summary>
/// 出库完成,释放入库任务下发
/// </summary>
public void IssueInTask()
{
PrintLogInfoMessage("出库完成,释放入库");
_isBlocked = false;
_autoResetEvent.Set();
}
/// <summary>
/// 获取待执行的入库任务
/// </summary>
/// <param name="storeCode"></param>
/// <returns></returns>
private RealTaskInfo GetAwaitSendTask(string storeCode)
{
RealTaskInfo taskInfo = null;
try
{
taskInfo = _taskInfoService.GetTaskInfoByStoreCode(storeCode, 1);
}
catch (Exception ex)
{
PrintLogErrorMessage("获取待执行的入库任务异常", ex);
}
return taskInfo;
}
#endregion
}
}