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

788 lines
30 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 HighWayIot.Common;
using HighWayIot.Config;
using HighWayIot.Log4net;
using HighWayIot.Repository.service;
using HighWayIot.TouchSocket;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
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 _recordInStore;
private IPrintBarCodeServices _printBarCodeServices;
// 过点数据表
private IMaterialCompletionServices _iMaterialCompletionServices;
private IProductPlanInfoService _productPlanInfoServices;
#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;
#endregion
#region 变量定义
private int shellNoReadFlag = 0;
private int linerNoReadFlag = 0;
private string shell_materialCodeStr = string.Empty;
private string liner_materialCodeStr = string.Empty;
#endregion
private InStoreBusiness()
{
_spaceInfoService = registerServices.GetService<IBaseSpaceInfoService>();
_taskInfoService = registerServices.GetService<IRealTaskInfoService>();
_baseSpaceDetailService = registerServices.GetService<IBaseSpaceDetailService>();
_baseBomInfoService = registerServices.GetService<IBaseBomInfoService>();
_recordInStore = registerServices.GetService<IRecordInStoreService>();
_printBarCodeServices = registerServices.GetService<IPrintBarCodeServices>();
_productPlanInfoServices = registerServices.GetService<IProductPlanInfoService>();
_iMaterialCompletionServices = registerServices.GetService<IMaterialCompletionServices>();
// taskHandle.InStoreFinsihEvent += InStoreFinish;
taskHandle.InStoreAnswerEvent += InStoreAnswer;
taskHandle.BareBoardHandleEvent += BareBoardInStore;
// MvCodeHelper.MessageNoReadEvent += MessageNoRead;
// MvCodeHelper.RefreshMaterialCodeStrEvent += InStore;
TouchSocketBusiness.RefreshMaterialCodeStrEvent += InStore;
// MvCodeHelper.RefreshLogMessageEvent += PrintLogInfoMessage;
StartPassDown();
}
#region 入库请求逻辑处理,创建入库任务,包含空板入库
/// <summary>
/// 入库
/// </summary>
/// <param name="storeCode"></param>
/// <param name="materialType"></param>
public void InStore(string materialCode, string scannerIp)
{
Task.Run(() =>
{
try
{
string storeCode = string.Empty;
if (appConfig.shellHikRobotIp.Equals(scannerIp))
{
storeCode = appConfig.shellStoreCode;
}
else
{
storeCode = appConfig.linerStoreCode;
}
taskHandle.ClearSpaceCode(storeCode);
PrintLogInfoMessage($"扫码成功,物料码:{materialCode}");
if (storeCode == appConfig.shellStoreCode)
{
shell_materialCodeStr = materialCode;
}
else
{
liner_materialCodeStr = materialCode;
}
string materialType = SubStringMaterialCode(materialCode);
var spaceInfo = GetSpaceInfoByMaterialType(storeCode, materialType);
if (spaceInfo != null)
{
PrintLogInfoMessage($"匹配货道:{spaceInfo.spaceName}");
spaceInfo.materialType = materialType;
spaceInfo.typeNameA = GetMaterialName(materialType);
RefreshScanMateriaCodeEvent?.Invoke(materialCode, GetMaterialName(materialType), spaceInfo.spaceName, storeCode); //刷新界面扫码信息
// 创建任务前先判断该仓库是否存在未下发任务,如果有,不再重复创建任务
RealTaskInfo taskInfo = _taskInfoService.GetTaskInfoByStoreCode(storeCode, 1);
if (taskInfo != null)
{
logHelper.Info($"仓库{storeCode}存在未下发任务,不再重复创建任务");
return;
}
var result = CreateInStoreTask(spaceInfo, materialCode); //创建入库任务
if (result)
{
taskHandle.ClearSpaceCode(storeCode);
#region 2023-12-15 更新过点数据,插入记录到MATERIAL_COMPLETION表
PrintBarCode print = _printBarCodeServices.query(materialCode);
if (print != null)
{
MaterialCompletion completion = new MaterialCompletion();
completion.OrderCode = print.OrderCode;
completion.MaterialBarcode = materialCode;
completion.MaterialCode = print.MaterialCode;
completion.MaterialName = print.MaterialName;
// 过点工位,与强哥确认 内胆存1003箱壳1002
if (materialCode.Substring(0, 1) == "L")
{
completion.StationName = "1003";
}
else
{
completion.StationName = appConfig.stationCode;
}
completion.CompleteDate = DateTime.Now;
completion.ProductLineCode = "CX_02";
completion.IsDownLine = 0;
_iMaterialCompletionServices.Add(completion);
}
#endregion
_spaceInfoService.UpdateSpaceInfo(spaceInfo);
}
}
else
{
//报警
PrintLogInfoMessage($"物料码:{materialCode};未匹配到可用货道");
BareBoardInStore(storeCode, false);
}
}
catch (Exception ex)
{
PrintLogErrorMessage("入库业务异常", ex);
}
});
}
private string last_shell_materialCodeStr = string.Empty;
private string last_liner_materialCodeStr = string.Empty;
/// <summary>
/// 空板入库逻辑
/// </summary>
/// <param name="storeCode"></param>
private void BareBoardInStore(string storeCode, bool isFlag = true)
{
try
{
// 创建空板任务前先判断是否存在未下发任务,如果有,不再重复创建任务
RealTaskInfo taskInfo = _taskInfoService.GetTaskInfoByStoreCode(storeCode, 1);
if (taskInfo != null) return;
taskHandle.ClearSpaceCode(storeCode);
string typeStr = isFlag ? "空板入库请求" : "读取失败自动进入异常道";
string logStr = storeCode == appConfig.shellStoreCode ? "收到箱壳" : "收到内胆";
PrintLogInfoMessage(logStr + typeStr + ";筛选异常货道创建入库任务");
//获取异常货道
var spaceList = _spaceInfoService.GetSpaceInfosByStoreCode(storeCode);
if (spaceList != null)
{
var unusualspaceList = spaceList.Where(x => x.unusualFlag == 2).ToList();
BaseSpaceInfo spaceInfo = GetSpaceInfosByPlc(unusualspaceList, true); //判断货道库存,不存在合适的获取空白货道
if (spaceInfo != null)
{
CreateInStoreTask(spaceInfo, null);
}
else
{
string str = storeCode == appConfig.shellStoreCode ? "箱壳异常入库" : "内胆异常入库";
PrintLogInfoMessage(str + "; 未获取到可用的异常货道");
List<BaseSpaceInfo> emptySpaceInfo = _spaceInfoService.GetEmptySpaceInfo(storeCode);
if (emptySpaceInfo != null && emptySpaceInfo.Count > 0)
{
BaseSpaceInfo empty_space = GetSpaceInfosByPlc(emptySpaceInfo, true);
if (empty_space != null)
{
CreateInStoreTask(empty_space, null);
empty_space.unusualFlag = 2;
_spaceInfoService.UpdateSpaceInfo(empty_space);
PrintLogInfoMessage($"{str}; 占用货道{empty_space.spaceName};为异常道");
}
}
else
{
BaseSpaceInfo zhanYong_space = GetSpaceInfosByPlc(spaceList, true);
if (zhanYong_space != null)
{
CreateInStoreTask(zhanYong_space, null);
PrintLogInfoMessage($"{str}; 没有异常道可分配,占用{zhanYong_space.spaceName};存放异常物料");
}
}
}
}
else
{
string str = storeCode == appConfig.shellStoreCode ? "箱壳异常入库" : "内胆异常入库";
PrintLogInfoMessage(str + ";系统内未设置异常货道");
}
}
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;
}
#endregion
#region 轮询获取入库任务下发至PLC等待PLC执行反馈完成后再次下发
private SemaphoreSlim shellSemaphore = new SemaphoreSlim(0);
private SemaphoreSlim linerSemaphore = new SemaphoreSlim(0);
private void StartPassDown1()
{
Thread shellThread = new Thread(() =>
{
while (true)
{
Console.WriteLine("箱壳入库线程任务下发");
PassDownShellTask();
Thread.Sleep(2000);
}
});
Thread linerThread = new Thread(() =>
{
while (true)
{
Console.WriteLine("内胆入库线程任务下发");
PassDownLinerTask();
Thread.Sleep(2000);
}
});
shellThread.Start();
linerThread.Start();
}
/// <summary>
/// 任务下发
/// </summary>
private void StartPassDown()
{
Task.Run(() =>
{
while (true)
{
Console.WriteLine("箱壳入库线程任务下发");
PassDownShellTask();
Thread.Sleep(2000);
}
});
Task.Run(() =>
{
while (true)
{
Console.WriteLine("内胆入库线程任务下发");
PassDownLinerTask();
Thread.Sleep(2000);
}
});
}
/// <summary>
/// 依次获取箱壳任务队列进行下发
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void PassDownShellTask()
{
try
{
lock (string.Empty)
{
RealTaskInfo taskInfo = _taskInfoService.GetTaskInfoByStoreCode(appConfig.shellStoreCode, appConfig.instoreTaskType);
if (taskInfo != null)
{
shellNoReadFlag = 1;
int result = taskHandle.SendShellTask_InStore(taskInfo);
if (result == 1)
{
PrintLogInfoMessage($"箱壳入库任务:{taskInfo.taskCode}下发成功等待PLC执行反馈");
Console.WriteLine("箱壳信号量锁住等待释放");
// shellSemaphore.Wait(); //一直堵塞直到信号量释放
Console.WriteLine("箱壳信号量释放");
shellNoReadFlag = 0;
PrintLogInfoMessage($"箱壳入库任务:{taskInfo.taskCode};开始执行");
taskInfo.taskStatus = 2;
_taskInfoService.UpdateTaskInfo(taskInfo);
RefreshInStoreTaskEvent?.Invoke(taskInfo);
}
else if (result == 2)
{
PrintLogInfoMessage("箱壳入库任务下发失败PLC接收任务未就绪");
}
else
{
PrintLogInfoMessage($"箱壳入库任务:{taskInfo.taskCode}下发失败请排除PLC连接");
}
}
}
}
catch (Exception ex)
{
PrintLogErrorMessage("依次获取箱壳任务队列进行下发逻辑异常", ex);
}
}
/// <summary>
/// 依次获取内胆任务队列进行下发
/// </summary>
private void PassDownLinerTask()
{
try
{
lock (string.Empty)
{
RealTaskInfo taskInfo = _taskInfoService.GetTaskInfoByStoreCode(appConfig.linerStoreCode, appConfig.instoreTaskType);
if (taskInfo != null)
{
linerNoReadFlag = 1;
int result = taskHandle.SendLinerTask_InStore(taskInfo);
if (result == 1)
{
PrintLogInfoMessage($"内胆入库任务:{taskInfo.taskCode}下发成功等待PLC执行反馈");
Console.WriteLine("内胆信号量锁住等待释放");
// linerSemaphore.Wait(); //一直堵塞直到信号量释放
Console.WriteLine("内胆信号量释放");
linerNoReadFlag = 0;
PrintLogInfoMessage($"内胆入库任务:{taskInfo.taskCode};开始执行");
taskInfo.taskStatus = 2;
_taskInfoService.UpdateTaskInfo(taskInfo);
RefreshInStoreTaskEvent?.Invoke(taskInfo);
}
else if (result == 2)
{
PrintLogInfoMessage("内胆入库任务下发失败PLC接收任务未就绪");
}
else
{
PrintLogInfoMessage($"内胆入库任务:{taskInfo.taskCode}下发失败请排除PLC连接");
}
}
}
}
catch (Exception ex)
{
PrintLogErrorMessage("依次获取内胆任务队列进行下发逻辑异常", ex);
}
}
#endregion
#region PLC应答逻辑应答后完成入库任务
/// <summary>
/// 入库应答PLC收到入库任务后进行应答
/// </summary>
/// <param name="storeCode"></param>
/// <param name="taskCode"></param>
private void InStoreAnswer(string storeCode, string taskCode)
{
if (storeCode == appConfig.shellStoreCode)
{
PrintLogInfoMessage("箱壳应答成功,自动释放信号量,进行下发新任务");
// shellSemaphore.Release();
PrintLogInfoMessage($"箱壳任务:{taskCode};执行完成");
InStoreFinishHandle(taskCode, appConfig.shellStoreCode);
}
else
{
PrintLogInfoMessage("内胆应答成功,自动释放信号量,进行下发新任务");
// linerSemaphore.Release();
PrintLogInfoMessage($"内胆任务:{taskCode};执行完成");
InStoreFinishHandle(taskCode, appConfig.linerStoreCode);
}
}
/// <summary>
/// 入库完成逻辑处理
/// </summary>
/// <param name="storeCode"></param>
/// <param name="spaceCode"></param>
/// <param name="materialType"></param>
private void InStoreFinishHandle(string taskCode, string storeCode)
{
try
{
var taskInfo = _taskInfoService.GetTaskInfoByTaskCode(taskCode, storeCode);
if (taskInfo != null)
{
var spaceInfo = _spaceInfoService.GetSpaceInfoBySpaceCode(taskInfo.storeCode, taskInfo.spaceCode);
if (spaceInfo != null)
{
#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;
spaceDetail.createTime = DateTime.Now;
spaceDetail.detailCode = System.Guid.NewGuid().ToString("N").Substring(0, 10);
_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;
_recordInStore.InsertRecordInStore(recordInstore);
#endregion
}
//清除任务信息
_taskInfoService.DeleteTaskInfo(taskCode, storeCode);
}
RefreshInStoreTaskEvent?.Invoke(taskInfo, true);
}
catch (Exception ex)
{
PrintLogErrorMessage("入库完成逻辑处理异常", ex);
}
}
#endregion
#region 通过PLC读取货道信息在库、在途、货道状态
/// <summary>
/// 通过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)
{
info = info.Where(x => x.unusualFlag != 2).ToList();
result = GetSpaceInfosByPlc(info);
if (result == null)
{
var list = _spaceInfoService.GetEmptySpaceInfo(storeCode);
result = GetSpaceInfosByPlc(list);
}
}
}
}
catch (Exception ex)
{
PrintLogErrorMessage("货道信息读取异常", ex);
}
return result;
}
/// <summary>
/// 读取PLC货道信息
/// isFlag 是否空板
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
private BaseSpaceInfo GetSpaceInfosByPlc(List<BaseSpaceInfo> info, bool isFlag = true)
{
BaseSpaceInfo result = null;
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;
PrintLogInfoMessage($"通过PLC读取货道信息货道{spaceInfo.spaceName};在库数量:{item.spaceStock};在途数量:{item.onRouteAmount};货道状态:{item.spaceStatus}");
}
if (isFlag)
{
var list = info.Where(x => x.spaceStock > 0 ? x.spaceCapacity > (x.spaceStock + x.onRouteAmount) : 1 == 1 && x.spaceStatus == 1).ToList();
if (list.Count > 0)
{
result = list.OrderByDescending(x => x.spaceStock).First();
}
}
else
{
var list = info.Where(x => x.spaceStock > 0 ? x.spaceCapacity > (x.spaceStock + x.onRouteAmount) : 1 == 1 && x.spaceStatus == 4).ToList();
if (list.Count > 0)
{
result = list.OrderByDescending(x => x.spaceStock).First();
}
}
}
}
return result;
}
#endregion
/// <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;
}
/// <summary>
/// 获取已创建的所有入库任务
/// </summary>
/// <returns></returns>
public List<RealTaskInfo> GetInStoreTask()
{
return _taskInfoService.GetTaskInfosByStoreCode(new string[] { appConfig.shellStoreCode, appConfig.linerStoreCode }, appConfig.instoreTaskType);
}
/// <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<BaseSpaceInfo> GetStock()
{
List<BaseSpaceInfo> infos = _spaceInfoService.GetSpaceInfosByTwoStoreCode(appConfig.shellStoreCode, appConfig.linerStoreCode);
return infos;
}
/// <summary>
/// 通过任务编号删除任务
///
/// 任务删除后是否需要还原库存,如果出库完成后减少库存则不需要
///
/// </summary>
/// <param name="taskCode"></param>
/// <returns></returns>
public bool DeleteTaskInfoByTaskCode(string taskCode, bool isFlag)
{
bool result = false;
var info = _taskInfoService.GetTaskInfosByTaskCode(taskCode);
if (info != null && info.Count > 0)
{
RealTaskInfo taskInfo = info[0];
result = _taskInfoService.DeleteTaskInfoById(taskInfo.objId);
}
return result;
}
#region 日志输出
/// <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);
}
#endregion
/// <summary>
/// 通过货道号查找货道
/// </summary>
/// <returns></returns>
public BaseSpaceInfo getSpaceBySpaceCode(string storeCode,string spaceCode)
{
return _spaceInfoService.GetSpaceInfoBySpaceCode(storeCode,spaceCode);
}
}
}