|
|
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
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 入库任务处理
|
|
|
/// </summary>
|
|
|
public sealed class InStoreTaskHandle
|
|
|
{
|
|
|
#region 单例实现
|
|
|
private static readonly Lazy<InStoreTaskHandle> lazy = new Lazy<InStoreTaskHandle>(() => 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;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 货道信息
|
|
|
/// </summary>
|
|
|
private IBaseSpaceInfoService _spaceInfoService;
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 已下传的任务信息
|
|
|
/// </summary>
|
|
|
private List<RealTaskInfo> foamRearTaskInfos;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 实时任务
|
|
|
/// </summary>
|
|
|
private IRealTaskInfoService _taskInfoService;
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region 私有变量
|
|
|
private static Dictionary<string, IPlc> _plcDictionary = new Dictionary<string, IPlc>();
|
|
|
private Dictionary<string, int> foamRearKeyValuePairs = new Dictionary<string, int>();
|
|
|
/// <summary>
|
|
|
/// 泡后任务编号,PLC反馈后进行赋值
|
|
|
/// </summary>
|
|
|
private string foamTaskCode = string.Empty;
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region 委托事件
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 实时库存刷新
|
|
|
/// </summary>
|
|
|
/// <param name="message"></param>
|
|
|
public delegate void RefreshFoamStock();
|
|
|
public event RefreshFoamStock RefreshFoamStockEvent;
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 入库应答,PLC收到下发的入库任务后进行应答
|
|
|
/// </summary>
|
|
|
/// <param name="storeCode"></param>
|
|
|
/// <param name="taskCode"></param>
|
|
|
public delegate void InStoreAnswer(string storeCode, string taskCode);
|
|
|
public event InStoreAnswer InStoreAnswerEvent;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 入库完成
|
|
|
/// </summary>
|
|
|
/// <param name="taskCode"></param>
|
|
|
public delegate void InStoreFinsih(string taskCode);
|
|
|
public event InStoreFinsih InStoreFinsihEvent;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 在途清0,完成货道任务
|
|
|
/// </summary>
|
|
|
/// <param name="taskCode"></param>
|
|
|
public delegate void InStoreFinsihBySpaceCode(string spaceCode);
|
|
|
public event InStoreFinsihBySpaceCode InStoreFinsihBySpaceCodeEvent;
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
private InStoreTaskHandle()
|
|
|
{
|
|
|
_taskInfoService = registerServices.GetService<IRealTaskInfoService>();
|
|
|
_spaceInfoService = registerServices.GetService<IBaseSpaceInfoService>();
|
|
|
_plcDictionary = _pool.GetAll();
|
|
|
// 程序启动查询数据库
|
|
|
foamRearTaskInfos = _taskInfoService.GetTaskInfosForInstore(appConfig.foamStoreCode, appConfig.instoreTaskType, 2);
|
|
|
foamRearTaskInfos = new List<RealTaskInfo>();
|
|
|
|
|
|
RealReadPlcSpace();
|
|
|
SendHeart();
|
|
|
RealUpdateSpaceInfoByPlc();
|
|
|
// RealReadFinish(); change-入库完成信号改回监测在途数
|
|
|
// test();
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 实时读取plc更新数据库货道数量及在途
|
|
|
/// </summary>
|
|
|
private void RealUpdateSpaceInfoByPlc()
|
|
|
{
|
|
|
Task.Run(() =>
|
|
|
{
|
|
|
|
|
|
Thread.Sleep(2000);
|
|
|
while (true)
|
|
|
{
|
|
|
|
|
|
List<BaseSpaceInfo> 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 心跳
|
|
|
/// </summary>
|
|
|
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-入库完成信号改回监测在途数
|
|
|
/// <summary>
|
|
|
/// 实时读取入库完成信号
|
|
|
/// </summary>
|
|
|
private void RealReadFinish()
|
|
|
{
|
|
|
|
|
|
Task.Run(() =>
|
|
|
{
|
|
|
Thread.Sleep(2000);
|
|
|
while (true)
|
|
|
{
|
|
|
if (_plcDictionary.Count > 0)
|
|
|
{
|
|
|
IPlc _plc = _plcDictionary[appConfig.foamStoreCode];
|
|
|
if (_plc != null)
|
|
|
{
|
|
|
|
|
|
List<RealTaskInfo> 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);
|
|
|
};
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 判断该任务对应的货道是否有完成信号
|
|
|
/// </summary>
|
|
|
/// <param name="taskInfo"></param>
|
|
|
/// <returns></returns>
|
|
|
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);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 读取箱壳已下发任务的货道信息,读取后将货道编号及在途数量写入Dictionary进行比较,在途数减少则入库完成
|
|
|
/// </summary>
|
|
|
private void RealReadShellPlcSpace()
|
|
|
{
|
|
|
if (foamRearTaskInfos != null && foamRearTaskInfos.Count > 0)
|
|
|
{
|
|
|
|
|
|
List<string> 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;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 读取泡后入库应答
|
|
|
/// </summary>
|
|
|
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
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 0正常,清除plc报警信息,1-扫码失败;2-库满,未匹配到可用货道
|
|
|
/// </summary>
|
|
|
/// <param name="flag"></param>
|
|
|
#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
|
|
|
/// <summary>
|
|
|
/// 通过PLC获取货道信息
|
|
|
/// </summary>
|
|
|
/// <param name="spaceInfo"></param>
|
|
|
/// <returns></returns>
|
|
|
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 入库逻辑修改,删除引用方法
|
|
|
|
|
|
/// <summary>
|
|
|
/// 读取泡后入库完成
|
|
|
/// </summary>
|
|
|
//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
|
|
|
}
|
|
|
}
|
|
|
|