using Aucma.Scada.Model.domain;
using HighWayIot.Config;
using HighWayIot.Log4net;
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 OutStoreBusiness
{
#region 单例实现
private static readonly Lazy lazy = new Lazy(() => new OutStoreBusiness());
public static OutStoreBusiness Instance
{
get
{
return lazy.Value;
}
}
#endregion
#region 对象引用
private LogHelper logHelper = LogHelper.Instance;
private AppConfig appConfig = AppConfig.Instance;
private RegisterServices registerServices = RegisterServices.Instance;
private AssemblyPlanBusiness assemblyPlanBusiness = AssemblyPlanBusiness.Instance;
private OutStoreTaskHandle taskHandleBusiness = OutStoreTaskHandle.Instance;
#endregion
#region 接口引用
///
/// 货道信息
///
private IBaseSpaceInfoService _spaceInfoService;
///
/// 实时任务
///
private IRealTaskInfoService _taskInfoService;
///
/// BOM信息
///
private IBaseBomInfoService _bomInfoService;
///
/// 货道明细
///
private IBaseSpaceDetailService _spaceDetailService;
private IExecutePlanInfoService _executePlanInfoService;
private IProductPlanInfoService _productPlanInfoService;
private IRecordOutStoreService _recordOutStoreService;
private IRecordProductfinishService _recordProductfinishService;
#endregion
#region 委托事件
///
/// 初始化出库任务
///
///
public delegate void RefreshOutStoreTask(RealTaskInfo taskInfos);
public event RefreshOutStoreTask RefreshOutStoreTaskEvent;
///
/// 扫码信息刷新
///
///
///
///
///
public delegate void RefreshScanMateriaCode(string materialCode, string materialName, string spaceName, string storeCode);
public event RefreshScanMateriaCode RefreshScanMateriaCodeEvent;
///
/// 日志信息刷新
///
///
public delegate void RefreshLogMessage(string message);
public event RefreshLogMessage RefreshLogMessageEvent;
public delegate void RefreshAssemblyPlanInit();
public event RefreshAssemblyPlanInit RefreshAssemblyPlanInitEvent;
public delegate void RefreshStoreStock();
public event RefreshStoreStock RefreshStoreStockEvent;
#endregion
private OutStoreBusiness()
{
_spaceInfoService = registerServices.GetService();
_taskInfoService = registerServices.GetService();
_bomInfoService = registerServices.GetService();
_spaceDetailService = registerServices.GetService();
_executePlanInfoService = registerServices.GetService();
_productPlanInfoService = registerServices.GetService();
_recordOutStoreService = registerServices.GetService();
_recordProductfinishService = registerServices.GetService();
assemblyPlanBusiness.NextPassExecutePlanInfoEvent += PlanHandle;
taskHandleBusiness.OutStoreFinsihEvent += TaskFeedback;
StartPassDown();
}
///
/// 接收下达的组装计划,根据BOM获取需要出库的泡后、内胆物料信息
///
///
private void PlanHandle(ExecutePlanInfo planInfo)
{
lock (string.Empty)
{
Task.Run(() =>
{
if (planInfo != null)
{
var bomInfo = _bomInfoService.GetChildenBomInfoByMaterialCode(planInfo.materialCode, appConfig.foamMaterialType);
for (int i = 0; i < planInfo.planAmount - planInfo.completeAmount; i++)
{
string taskCode = System.Guid.NewGuid().ToString("N").Substring(0,10);
OutStore(appConfig.foamStoreCode, bomInfo, planInfo.executePlanCode, taskCode);
Thread.Sleep(500);
}
}
});
}
}
///
/// 解析计划创建出库任务
///
///
///
///
private void OutStore(string storeCode, BaseBomInfo bomInfo, string planCode, string taskCode)
{
try
{
PrintLogInfoMessage($"收到出库计划,物料码:{bomInfo.materialCode}");
BaseSpaceInfo spaceInfo = _spaceInfoService.OutStoreGetSpaceInfoByMaterialCode(storeCode, bomInfo.materialCode);
if (spaceInfo != null)
{
PrintLogInfoMessage($"匹配货道:{spaceInfo.spaceName}");
// RefreshScanMateriaCodeEvent?.Invoke(materiaclCode, materialType, spaceInfo.spaceName, storeCode); //刷新界面扫码信息
CreateOutStoreTask(spaceInfo, planCode, taskCode); //创建出库任务
}
else
{
//报警停线
PrintLogInfoMessage($"{storeCode};仓库内未获取到{bomInfo.materialCode}相匹配的物料及货道");
}
}
catch (Exception ex)
{
PrintLogErrorMessage("出库业务异常", ex);
}
}
///
/// 创建出库任务
///
///
private bool CreateOutStoreTask(BaseSpaceInfo spaceInfo, string planCode, string taksCode, int taskModel = 0)
{
bool result = false;
try
{
//获取出库的货道明细物料信息
BaseSpaceDetail spaceDetail = GetSpaceDetailFirstOrderByCreatTime(spaceInfo);
if (spaceDetail != null)
{
#region 出库任务赋值
RealTaskInfo realTaskInfo = new RealTaskInfo();
realTaskInfo.planCode = planCode;
realTaskInfo.taskType = 2;
realTaskInfo.taskCode = taksCode;
realTaskInfo.taskModel = taskModel;
realTaskInfo.storeCode = spaceInfo.storeCode;
realTaskInfo.spaceCode = spaceInfo.spaceCode;
realTaskInfo.spaceName = spaceInfo.spaceName;
realTaskInfo.materialType = spaceDetail.materialType;
realTaskInfo.materialCode = spaceDetail.materialCode;
realTaskInfo.planAmount = 1;
realTaskInfo.taskStatus = 1;
realTaskInfo.createTime = DateTime.Now;
#endregion
result = _taskInfoService.AddTaskInfo(realTaskInfo);
if (result)
{
PrintLogInfoMessage("出库任务创建成功");
RefreshOutStoreTaskEvent?.Invoke(realTaskInfo);
UpdateSpaceAndDetial(spaceInfo, spaceDetail);
}
else
{
PrintLogInfoMessage("出库任务创建失败");
}
}
else
{
PrintLogInfoMessage("出库任务创建失败,货道明细为空");
}
}
catch (Exception ex)
{
PrintLogErrorMessage("出库任务创建异常", ex);
}
return result;
}
///
/// 根据创建时间获取第一个货道明细
///
///
///
private BaseSpaceDetail GetSpaceDetailFirstOrderByCreatTime(BaseSpaceInfo spaceInfo)
{
BaseSpaceDetail spaceDetail = null;
try
{
List spaceDetails = _spaceDetailService.GetSpaceDetailsBySpaceCode(spaceInfo.storeCode, spaceInfo.spaceCode);
if (spaceDetails.Count > 0)
{
spaceDetails = spaceDetails.Where(x => x.isFlag != 1).ToList();
if (spaceDetails.Count > 0)
{
spaceDetail = spaceDetails.OrderBy(x => x.createTime).First();
}
}
}
catch (Exception ex)
{
PrintLogErrorMessage("获取货道明细信息异常", ex);
}
return spaceDetail;
}
///
/// 任务创建完成后修改货道信息及货道明细
///
///
///
private void UpdateSpaceAndDetial(BaseSpaceInfo spaceInfo, BaseSpaceDetail spaceDetail, int detailIsFlag = 1, bool stockFlag = true)
{
//任务创建完成后修改货道库存、货道明细修改物料标识物料不可用
spaceDetail.isFlag = detailIsFlag;
var result = _spaceDetailService.UpdateSpaceDetail(spaceDetail);
//if (stockFlag)
//{
// spaceInfo.spaceStock = spaceInfo.spaceStock > 0 ? spaceInfo.spaceStock - 1 : 0;
//}
//else
//{
// spaceInfo.spaceStock = spaceInfo.spaceStock + 1;
//}
spaceInfo.outRouteAmount += 1;
_spaceInfoService.UpdateSpaceInfo(spaceInfo);
}
#region 轮询获取出库任务下发至PLC,等待PLC执行反馈,完成后再次下发
private SemaphoreSlim semaphore = new SemaphoreSlim(0);
private int completedTasks = 0;
private int taskAmount = 2;
private void StartPassDown()
{
Task.Run(() =>
{
while (true)
{
PassDownTaskInfo();
Thread.Sleep(1000);
}
});
}
///
/// 依次获取任务队列进行下发
///
///
///
private void PassDownTaskInfo()
{
string taskCode = string.Empty;
string executePlanCode = string.Empty;
int iFlag = 0;
completedTasks = 0;
//获取待执行的出库任务下发至PLC,并将任务状态改为执行中
var taskInfoList = GetAwaitSendTask();
if (taskInfoList.Count > 0)
{
taskAmount = taskInfoList.Count; //下发的任务数量,默认2(泡后、内胆),泡后、内胆均执行完成后才会释放信号量
foreach (var item in taskInfoList)
{
taskCode = item.taskCode;
executePlanCode = item.planCode;
if (taskHandleBusiness.SendFoamTask_OutStore(item))
{
PrintLogInfoMessage($"下发泡后出库任务:{item.taskCode};仓库{item.storeCode};货道:{item.spaceCode};等待PLC执行反馈");
item.taskStatus = 2;
iFlag++;
}
else
{
PrintLogInfoMessage($"泡后出库任务:{item.taskCode};下发失败,请排除PLC连接");
continue;
}
_taskInfoService.UpdateTaskInfo(item);
RefreshScanMateriaCodeEvent?.Invoke(item.materialCode, item.materialType, item.spaceName, item.storeCode);
}
if (iFlag == taskInfoList.Count)
{
semaphore.Wait(); //一直堵塞直到信号量释放
PrintLogInfoMessage($"出库任务:{taskCode};执行完成");
UpdatePlanInfo(executePlanCode);
RefreshStoreStockEvent?.Invoke();
}
}
else
{
PrintLogInfoMessage("未获取到需要下发的出库任务");
}
Thread.Sleep(3000);
}
///
/// 获取待执行的出库任务
///
///
private List GetAwaitSendTask()
{
List taskInfos = new List();
try
{
RealTaskInfo foamTaskInfo = _taskInfoService.GetTaskInfoByStoreCode(appConfig.foamStoreCode, 2);
if (foamTaskInfo != null)
{
taskInfos.Add(foamTaskInfo);
//获取与泡后任务匹配的内胆任务
RealTaskInfo linerTaskInfo = _taskInfoService.GetTaskInfoByTaskCode(foamTaskInfo.taskCode, appConfig.linerStoreCode);
if (linerTaskInfo != null) taskInfos.Add(linerTaskInfo);
}
else
{
RealTaskInfo linerInfo = _taskInfoService.GetTaskInfoByStoreCode(appConfig.linerStoreCode, 2);
if (linerInfo != null) taskInfos.Add(linerInfo);
}
}
catch (Exception ex)
{
PrintLogErrorMessage("获取待执行的出库任务异常", ex);
}
return taskInfos;
}
///
/// PLC任务执行反馈
///
///
///
private void TaskFeedback(string storeCode, string taskCode)
{
FoamTaskFeedback(taskCode);
}
///
/// 泡后执行反馈
///
private void FoamTaskFeedback(string taskCode)
{
Interlocked.Increment(ref completedTasks);
CheckCompletedTasks();
PrintLogInfoMessage("泡后执行完成,自动释放信号量");
OutStoreFinish(taskCode, appConfig.foamStoreCode);
}
///
/// 信号量释放,根据任务完成数量,执行完成后进行释放
///
private void CheckCompletedTasks()
{
if (completedTasks == taskAmount)
{
// 释放信号量
semaphore.Release();
}
}
#endregion
///
/// 出库完成
///
///
///
///
private void OutStoreFinish(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)
{
//读取PLC获取货道信息:存放数量、在途数量
//spaceInfo.materialType = taskInfo.materialType;
spaceInfo.spaceStock -= 1;
spaceInfo.outRouteAmount -= 1;
if (spaceInfo.spaceStock == 0)
{
spaceInfo.materialType = string.Empty;
}
_spaceInfoService.UpdateSpaceInfo(spaceInfo);
//读取PLC获取物料类型进行绑定
#region 添加出库记录
RecordOutstore recordOutstore = new RecordOutstore();
recordOutstore.storeCode = taskInfo.storeCode;
recordOutstore.spaceCode = taskInfo.spaceCode;
recordOutstore.materialCode = taskInfo.materialCode;
recordOutstore.materialType = taskInfo.materialType;
recordOutstore.materialName = GetMaterialName(taskInfo.materialType);
recordOutstore.outStoreAmount = 1;
recordOutstore.outStoreTime = DateTime.Now;
_recordOutStoreService.InsertReocrdOutStoreService(recordOutstore);
#endregion
}
//清除任务信息
_taskInfoService.DeleteTaskInfo(taskCode, storeCode);
_spaceDetailService.DeleteSpaceDetailByMaterialCode(taskInfo.materialCode);
//刷新界面
RefreshScanMateriaCodeEvent?.Invoke(string.Empty, string.Empty, string.Empty, taskInfo.storeCode);
}
}
catch (Exception ex)
{
PrintLogErrorMessage("出库完成逻辑处理异常", ex);
}
}
///
/// 更新计划信息
///
///
private void UpdatePlanInfo(string planCode)
{
try
{
var executeInfo = _executePlanInfoService.GetExecutePlanInfoByPlanCode(planCode);
if (executeInfo != null)
{
executeInfo.completeAmount += 1;
if (executeInfo.completeAmount == executeInfo.planAmount)
{
executeInfo.executeStatus = 3;
}
_executePlanInfoService.UpdateExecutePlanInfo(executeInfo);
var productInfo = _productPlanInfoService.GetProductPlanByPlanCode(executeInfo.productPlanCode);
if (productInfo != null)
{
productInfo.completeAmount += 1;
if (productInfo.completeAmount == productInfo.planAmount)
{
productInfo.endTime = DateTime.Now;
}
_productPlanInfoService.UpdateProductPlanInfo(productInfo);
}
#region 添加完成记录
RecordProductfinish productfinish = new RecordProductfinish();
productfinish.planCode = executeInfo.executePlanCode;
productfinish.productlineCode = appConfig.stationCode;
productfinish.materialCode = executeInfo.materialCode;
productfinish.completeAmount = 1; //目前一个箱体一个任务下发给plc
productfinish.recordTime = DateTime.Now;
_recordProductfinishService.InsertRecordProductfinish(productfinish);
#endregion
}
RefreshAssemblyPlanInitEvent?.Invoke();
}
catch (Exception ex)
{
PrintLogErrorMessage("计划信息更新异常", ex);
}
}
///
/// 获取出库任务
///
///
public List GetOutStoreTask()
{
var taskInfos = _taskInfoService.GetTaskInfosByStoreCode(new string[] { appConfig.foamStoreCode}, appConfig.outstoreTaskType);
return taskInfos;
}
///
/// 通过任务编号删除任务
///
/// 任务删除后是否需要还原库存,如果出库完成后减少库存则不需要
///
///
///
///
public bool DeleteTaskInfoByTaskCode(string taskCode)
{
bool result = false;
var info = _taskInfoService.GetTaskInfosByTaskCode(taskCode);
if (info != null)
{
foreach (var taskInfo in info)
{
if (taskInfo.taskStatus == 2)
{
PrintLogInfoMessage("任务正在执行中不运行删除");
continue;
}
result = _taskInfoService.DeleteTaskInfoById(taskInfo.objId);
if (result)
{
var spaceDetailInfo = _spaceDetailService.GetSpaceDetailByMaterialCode(taskInfo.materialCode);
if (spaceDetailInfo != null)
{
spaceDetailInfo.isFlag = 0;
_spaceDetailService.UpdateSpaceDetail(spaceDetailInfo);
}
}
}
}
return result;
}
///
/// 根据货道手动出一个
///
///
///
///
public bool OutOnlyOneBySpaceCode(string storeCode, string spaceCode)
{
bool result = false;
try
{
BaseSpaceInfo spaceInfo = _spaceInfoService.GetSpaceInfoBySpaceCode(storeCode, spaceCode);
if (spaceInfo.spaceStock > 0)
{
result = this.CreateOutStoreTask(spaceInfo, System.Guid.NewGuid().ToString("N"), DateTime.Now.ToString("HH:mm:ss"), 1);
}
else
{
PrintLogInfoMessage($"仓库:{storeCode};货道:{spaceCode};出一个失败:库存不足");
}
}
catch (Exception ex)
{
logHelper.Error("根据货道出一个异常", ex);
}
return result;
}
///
/// 日志输出,界面刷新同时记录文件
///
///
private void PrintLogInfoMessage(string message)
{
RefreshLogMessageEvent?.Invoke(message);
logHelper.Info(message);
}
///
/// 异常日志输出
///
///
///
private void PrintLogErrorMessage(string message, Exception ex = null)
{
RefreshLogMessageEvent?.Invoke(message);
logHelper.Error(message, ex);
}
///
/// 手动释放信号量
///
///
private void GetAllRelese(Semaphore sph)
{
bool res = sph.WaitOne(1, false);
if (res)
{
GetAllRelese(sph);
}
}
///
/// 通过BOM获取物料名称
///
///
///
public string GetMaterialName(string materialType)
{
string materialName = string.Empty;
var info = _bomInfoService.GetBomInfoByMaterialCode(materialType);
if (info != null)
{
materialName = info.materialName;
}
return materialName;
}
}
}