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/InStoreTaskHandle.cs

591 lines
22 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.Scada.Model.domain;
using HighWayIot.Config;
using HighWayIot.Log4net;
using HighWayIot.Plc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Aucma.Scada.Business
{
/// <summary>
/// 入库任务处理
/// </summary>
internal 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;
#endregion
#region 私有变量
private Dictionary<string, IPlc> _plcDictionary = new Dictionary<string, IPlc>();
private Dictionary<string, int> shellKeyValuePairs = new Dictionary<string, int>();
private Dictionary<string, int> linerKeyValuePairs = new Dictionary<string, int>();
/// <summary>
/// 箱壳任务编号,PLC反馈后进行赋值
/// </summary>
private string shellTaskCode = string.Empty;
/// <summary>
/// 内胆任务编号,PLC反馈后进行赋值
/// </summary>
private string linerTaskCode = string.Empty;
/// <summary>
/// 已下传的任务信息
/// </summary>
private List<RealTaskInfo> shellTaskInfos = new List<RealTaskInfo>();
private List<RealTaskInfo> linerTaskInfos = new List<RealTaskInfo>();
#endregion
#region 委托事件
/// <summary>
/// 入库完成
/// </summary>
/// <param name="storeCode"></param>
/// <param name="taskCode"></param>
public delegate void InStoreFinsih(string storeCode, string taskCode);
public event InStoreFinsih InStoreFinsihEvent;
/// <summary>
/// 入库应答PLC收到下发的入库任务后进行应答
/// </summary>
/// <param name="storeCode"></param>
/// <param name="taskCode"></param>
public delegate void InStoreAnswer(string storeCode, string taskCode);
public event InStoreAnswer InStoreAnswerEvent;
#endregion
private InStoreTaskHandle()
{
_plcDictionary = _pool.GetAll();
RealReadPlcSpace();
}
#region 箱壳入库任务下发处理
public bool SendShellTask_InStore(RealTaskInfo taskInfo)
{
bool result = false;
try
{
IPlc _plc = _plcDictionary[taskInfo.storeCode];
if (_plc != null)
{
if (_plc.IsConnected)
{
//写入货道号
_plc.writeStringByAddress(plcConfig.in_shell_spaceCode, taskInfo.spaceCode);
//写入应答字
_plc.writeInt32ByAddress(plcConfig.in_shell_answer, 1);
//写入任务号
_plc.writeStringByAddress(plcConfig.in_shell_task, taskInfo.taskCode);
//写入完成后读取应答字进行复位
ReadShellAnswer_InStore(taskInfo);
result = true;
}
else
{
logHelper.Info($"仓库{taskInfo.storeCode}PLC未连接");
}
}
else
{
logHelper.Info($"PLC信息为空通过{taskInfo.storeCode}未获取到该仓库对应的PLC信息");
}
}
catch (Exception ex)
{
logHelper.Error("箱壳入库任务下发异常", ex);
}
return result;
}
/// <summary>
/// 读取箱壳入库应答
/// </summary>
private void ReadShellAnswer_InStore(RealTaskInfo taskInfo)
{
lock (string.Empty)
{
bool isFlag = true;
IPlc _plc = _plcDictionary[appConfig.shellStoreCode];
try
{
Task.Run(() =>
{
if (_plc != null)
{
if (_plc.IsConnected)
{
do
{
//读取PLC应答字为2时上位机清空写入的入库内容
if (_plc.readInt32ByAddress(plcConfig.in_shell_answer) == 2)
{
//写入货道号
_plc.writeStringByAddress(plcConfig.in_shell_spaceCode, string.Empty);
//写入应答字
_plc.writeInt32ByAddress(plcConfig.in_shell_answer, 0);
//写入任务号
_plc.writeStringByAddress(plcConfig.in_shell_task, string.Empty);
isFlag = false;
WritePlc(taskInfo.storeCode, taskInfo.spaceCode, false);
//ReadShellFinish_InStore(taskCode);
InStoreAnswerEvent?.Invoke(appConfig.shellStoreCode, taskInfo.taskCode);
shellTaskInfos.Add(taskInfo);
}
Thread.Sleep(1000);
} while (isFlag);
}
else
{
logHelper.Info($"仓库{appConfig.shellStoreCode}PLC未连接");
}
}
else
{
logHelper.Info($"PLC信息为空通过{appConfig.shellStoreCode}未获取到该仓库对应的PLC信息");
}
});
}
catch (Exception ex)
{
logHelper.Error("读取箱壳入库应答字异常", ex);
}
}
}
#endregion
#region 内胆入库任务处理
/// <summary>
/// 内胆入库任务下发
/// </summary>
/// <param name="taskInfo"></param>
public bool SendLinerTask_InStore(RealTaskInfo taskInfo)
{
bool result = false;
try
{
IPlc _plc = _plcDictionary[appConfig.linerStoreCode];
if (_plc != null)
{
if (_plc.IsConnected)
{
//写入货道号
_plc.writeStringByAddress(plcConfig.in_liner_spaceCode, taskInfo.spaceCode);
//写入应答字
_plc.writeInt32ByAddress(plcConfig.in_liner_answer, 1);
//写入任务号
_plc.writeStringByAddress(plcConfig.in_liner_task, taskInfo.taskCode);
//写入完成后读取应答字进行复位
ReadLinerAnswer_InStore(taskInfo);
result = true;
}
else
{
logHelper.Info($"仓库{taskInfo.storeCode}PLC未连接");
}
}
else
{
logHelper.Info($"PLC信息为空通过{taskInfo.storeCode}未获取到该仓库对应的PLC信息");
}
}
catch (Exception ex)
{
logHelper.Error("内胆入库任务下发异常", ex);
}
return result;
}
/// <summary>
/// 读取内胆入库应答
/// </summary>
private void ReadLinerAnswer_InStore(RealTaskInfo taskInfo)
{
lock (string.Empty)
{
bool isFlag = true;
IPlc _plc = _plcDictionary[appConfig.linerStoreCode];
try
{
Task.Run(() =>
{
if (_plc != null)
{
if (_plc.IsConnected)
{
do
{
//读取PLC应答字为2时上位机清空写入的入库内容
if (_plc.readInt32ByAddress(plcConfig.in_liner_answer) == 2)
{
//写入货道号
_plc.writeStringByAddress(plcConfig.in_liner_spaceCode, string.Empty);
//写入应答字
_plc.writeInt32ByAddress(plcConfig.in_liner_answer, 0);
//写入任务号
_plc.writeStringByAddress(plcConfig.in_liner_task, string.Empty);
isFlag = false;
//ReadLinerFinish_InStore(taskCode);
WritePlc(taskInfo.storeCode, taskInfo.spaceCode, false);
InStoreAnswerEvent?.Invoke(appConfig.linerStoreCode, taskInfo.taskCode);
linerTaskInfos.Add(taskInfo);
}
Thread.Sleep(1000);
} while (isFlag);
}
else
{
logHelper.Info($"仓库{appConfig.linerStoreCode}PLC未连接");
}
}
else
{
logHelper.Info($"PLC信息为空通过{appConfig.linerStoreCode}未获取到该仓库对应的PLC信息");
}
});
}
catch (Exception ex)
{
logHelper.Error("读取内胆入库应答字异常", ex);
}
}
}
#endregion
#region 读取PLC入库完成 Delete By Wenjy 2023-11-08 15:05:00经讨论入库完成改为监测在途数量变化
/// <summary>
/// 读取箱壳入库完成
/// </summary>
/*private void ReadShellFinish_InStore(string taskCode)
{
lock (string.Empty)
{
bool isFlag = true;
IPlc _plc = _plcDictionary[appConfig.shellStoreCode];
shellTaskCode = taskCode;
try
{
Task.Run(() =>
{
if (_plc != null)
{
if (_plc.IsConnected)
{
do
{
//读取PLC入库任务完成
if (_plc.readInt32ByAddress(plcConfig.in_shell_finish) == 1)
{
_plc.writeInt32ByAddress(plcConfig.in_shell_finish, 0);
//string taskCode = _plc.readStringByAddress(plcConfig.out_shell_task, 10);
InStoreFinsihEvent?.Invoke(appConfig.shellStoreCode, taskCode);
isFlag = false;
}
Thread.Sleep(1000);
} while (isFlag);
}
else
{
logHelper.Info($"仓库{appConfig.shellStoreCode}PLC未连接");
}
}
else
{
logHelper.Info($"PLC信息为空通过{appConfig.shellStoreCode}未获取到该仓库对应的PLC信息");
}
});
}
catch (Exception ex)
{
logHelper.Error("读取箱壳入库完成异常", ex);
}
}
}*/
/// <summary>
/// 读取内胆入库完成
/// </summary>
/*private void ReadLinerFinish_InStore(string taskCode)
{
lock (string.Empty)
{
try
{
Task.Run(() =>
{
bool isFlag = true;
IPlc _plc = _plcDictionary[appConfig.linerStoreCode];
if (_plc != null)
{
if (_plc.IsConnected)
{
do
{
//读取PLC入库任务完成
if (_plc.readInt32ByAddress(plcConfig.in_liner_finish) == 1)
{
_plc.writeInt32ByAddress(plcConfig.in_liner_finish, 0);
//string taskCode = _plc.readStringByAddress(plcConfig.in_liner_task, 10);
InStoreFinsihEvent?.Invoke(appConfig.linerStoreCode, taskCode);
isFlag = false;
}
Thread.Sleep(1000);
} while (isFlag);
}
else
{
logHelper.Info($"仓库{appConfig.linerStoreCode}PLC未连接");
}
}
else
{
logHelper.Info($"PLC信息为空通过{appConfig.linerStoreCode}未获取到该仓库对应的PLC信息");
}
});
}
catch (Exception ex)
{
logHelper.Error("读取内胆入库完成异常", ex);
}
}
}*/
#endregion
#region 监测PLC在途数量变化完成入库任务
private void RealReadPlcSpace()
{
Thread.Sleep(5000);
Task.Run(() =>
{
while (true)
{
RealReadShellPlcSpace();
Thread.Sleep(500);
}
});
Task.Run(() =>
{
while (true)
{
RealReadLinerPlcSpace();
Thread.Sleep(500);
}
});
}
/// <summary>
/// 读取箱壳已下发任务的货道信息读取后将货道编号及在途数量写入Dictionary进行比较在途数减少则入库完成
/// </summary>
private void RealReadShellPlcSpace()
{
if (shellTaskInfos != null)
{
List<string> spaceCodes = shellTaskInfos.Select(x => x.spaceCode).Distinct().ToList();
for (int i = 0; i < spaceCodes.Count; i++)
{
string spaceCode = spaceCodes[i];
BaseSpaceInfo spaceInfo = new BaseSpaceInfo() { storeCode = appConfig.shellStoreCode, spaceCode = spaceCode };
spaceInfo = ReadSpaceInfoByPlc(spaceInfo);
if (shellKeyValuePairs.ContainsKey(spaceInfo.spaceCode))
{
shellKeyValuePairs.TryGetValue(spaceInfo.spaceCode, out int value);
//判断前次读取的数据和当前数据,如果前次数据大于当前数据则代表入库完成,然后筛选任务中对应货道的首个任务进行完成
//如果前次数据不大于当前数据则更新字典中存放的数据
if (value > spaceInfo.onRouteAmount)
{
//筛选任务
var list = shellTaskInfos.Where(x => x.spaceCode == spaceInfo.spaceCode).ToList();
if (list.Count > 0)
{
RealTaskInfo taskInfo = list.OrderBy(x => x.createTime).First();
InStoreFinsihEvent?.Invoke(taskInfo.storeCode, taskInfo.taskCode);
shellTaskInfos.Remove(taskInfo);
}
shellKeyValuePairs.Remove(spaceInfo.spaceCode);
}
else
{
shellKeyValuePairs[spaceInfo.spaceCode] = spaceInfo.onRouteAmount;
}
}
else
{
shellKeyValuePairs.Add(spaceInfo.spaceCode, spaceInfo.onRouteAmount);
}
}
}
}
/// <summary>
/// 读取内胆已下发任务的货道信息读取后将货道编号及在途数量写入Dictionary进行比较在途数减少则入库完成
/// </summary>
private void RealReadLinerPlcSpace()
{
if (linerTaskInfos != null)
{
List<string> spaceCodes = linerTaskInfos.Select(x => x.spaceCode).Distinct().ToList();
for (int i = 0; i < spaceCodes.Count; i++)
{
string spaceCode = spaceCodes[i];
BaseSpaceInfo spaceInfo = new BaseSpaceInfo() { storeCode = appConfig.linerStoreCode, spaceCode = spaceCode };
spaceInfo = ReadSpaceInfoByPlc(spaceInfo);
if (linerKeyValuePairs.ContainsKey(spaceInfo.spaceCode))
{
linerKeyValuePairs.TryGetValue(spaceInfo.spaceCode, out int value);
//判断前次读取的数据和当前数据,如果前次数据大于当前数据则代表入库完成,然后筛选任务中对应货道的首个任务进行完成
//如果前次数据不大于当前数据则更新字典中存放的数据
if (value > spaceInfo.onRouteAmount)
{
//筛选任务
var list = linerTaskInfos.Where(x => x.spaceCode == spaceInfo.spaceCode).ToList();
if (list.Count > 0)
{
RealTaskInfo taskInfo = list.OrderBy(x => x.createTime).First();
InStoreFinsihEvent?.Invoke(taskInfo.storeCode, taskInfo.taskCode);
linerTaskInfos.Remove(taskInfo);
}
linerKeyValuePairs.Remove(spaceInfo.spaceCode);
}
else
{
linerKeyValuePairs[spaceInfo.spaceCode] = spaceInfo.onRouteAmount;
}
}
else
{
linerKeyValuePairs.Add(spaceInfo.spaceCode, spaceInfo.onRouteAmount);
}
}
}
}
#endregion
/// <summary>
/// 通过PLC获取货道信息
/// </summary>
/// <param name="spaceInfo"></param>
/// <returns></returns>
public BaseSpaceInfo ReadSpaceInfoByPlc(BaseSpaceInfo spaceInfo)
{
var spaceAddress = spaceConfig.GetSpaceAddress(spaceInfo.storeCode, spaceInfo.spaceCode);
IPlc _plc = _plcDictionary[spaceInfo.storeCode];
if (_plc != null)
{
if (_plc.IsConnected)
{
spaceInfo.spaceStock = _plc.readInt32ByAddress(spaceAddress.onStore);
spaceInfo.onRouteAmount = _plc.readInt32ByAddress(spaceAddress.onRoute);
spaceInfo.spaceStatus = _plc.readInt32ByAddress(spaceAddress.spaceStatus);
}
}
return spaceInfo;
}
/// <summary>
/// 测试方法,联调时不用
/// </summary>
/// <param name="storeCode"></param>
/// <param name="spaceCode"></param>
/// <param name="flag"></param>
public void WritePlc(string storeCode, string spaceCode, bool flag)
{
var spaceAddress = spaceConfig.GetSpaceAddress(storeCode, spaceCode);
IPlc _plc = _plcDictionary[storeCode];
if (_plc != null)
{
if (_plc.IsConnected)
{
var spaceStock = _plc.readInt32ByAddress(spaceAddress.onStore);
var onRouteAmount = _plc.readInt32ByAddress(spaceAddress.onRoute);
if (flag)
{
_plc.writeInt32ByAddress(spaceAddress.onStore, spaceStock + 1);
//_plc.writeInt32ByAddress(spaceAddress.onRoute, onRouteAmount - 1);
}
else
{
_plc.writeInt32ByAddress(spaceAddress.onRoute, onRouteAmount + 1);
}
}
}
}
}
}