change-修改出库出库应答字逻辑

foamRearStore
liuwf 12 months ago
parent 2f1305881d
commit 5d2a3ce3ee

Binary file not shown.

@ -42,18 +42,27 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reactive.6.0.0\lib\net472\System.Reactive.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Windows.Threading, Version=3.0.6000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reactive.Windows.Threading.6.0.0\lib\net472\System.Reactive.Windows.Threading.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Windows" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyPlanBusiness.cs" />

@ -11,6 +11,8 @@ 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
{
@ -90,11 +92,20 @@ namespace Aucma.Scada.Business
/// <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>();
@ -102,22 +113,70 @@ namespace Aucma.Scada.Business
_recordInStoreService = registerServices.GetService<IRecordInStoreService>();
_printBarCodeServices = registerServices.GetService<IPrintBarCodeServices>();
_productPlanInfoServices = registerServices.GetService<IProductPlanInfoService>();
taskHandle.InStoreFinsihEvent += FoamTaskFeedback;
taskHandle.InStoreFinsihEvent += InStoreFinish;
taskHandle.InStoreAnswerEvent += InStoreAnswer;
MvCodeHelper.RefreshMaterialCodeStrEvent += InStore;
MvCodeHelper.RefreshLogMessageEvent += PrintLogInfoMessage;
StartPassDown();
//Task.Run(() =>
//{
// Thread.Sleep(6000);
// for (int i = 1; i < 3; i++)
// {
// InStore("F2340600122510190" + i.ToString().PadLeft(2, '0'));
// Thread.Sleep(1000);
// }
//});
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>
@ -132,31 +191,30 @@ namespace Aucma.Scada.Business
#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)
{
//spaceInfo.onRouteAmount += 1; //通过PLC获取货道信息在库、在途数量时不需要修改在途数量
#region 2023-12-15 更新过点数据,插入记录到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);
#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);
}
}
@ -216,20 +274,25 @@ namespace Aucma.Scada.Business
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(5000);
while (true)
{
PassDownFoamTask();
Thread.Sleep(1000);
Thread.Sleep(500);
}
});
}
private bool _isBlocked = false;
private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
/// <summary>
/// 依次获取泡后任务队列进行下发
/// </summary>
@ -237,47 +300,41 @@ namespace Aucma.Scada.Business
/// <param name="e"></param>
private void PassDownFoamTask()
{
#region 出库不需要堵塞整个库
////出库时堵塞入库,这个地方如果有问题的话改为操作StartPassDown将_isBlocked初始值true给while开始出库时设成false结束while出库完成后改为ture重新调用StartPassDown
//if (_isBlocked)
//{
// PrintLogInfoMessage("正在出库,当前入库状态堵塞,出库完成后进行释放");
// _autoResetEvent.WaitOne();
//}
#endregion
//出库时堵塞入库,这个地方如果有问题的话改为操作StartPassDown将_isBlocked初始值true给while开始出库时设成false结束while出库完成后改为ture重新调用StartPassDown
while (_isBlocked)
try
{
PrintLogInfoMessage("正在出库,当前入库状态堵塞,出库完成后进行释放");
_autoResetEvent.WaitOne();
Thread.Sleep(5000);
}
RealTaskInfo taskInfo = GetAwaitSendTask(appConfig.foamStoreCode);
RealTaskInfo taskInfo = _taskInfoService.GetTaskInfoByStoreCode(appConfig.foamStoreCode, 1);
if (taskInfo != null)
{
PrintLogInfoMessage($"下发泡后入库任务:{taskInfo.taskCode};仓库{taskInfo.storeCode};货道:{taskInfo.spaceCode}");
//taskHandle.SendShellTask_InStore(taskInfo);
Thread.Sleep(200);
if (taskHandle.SendFoamTask_InStore(taskInfo))
int result = taskHandle.SendFoamTask_InStore(taskInfo);
if (result==1)
{
PrintLogInfoMessage($"泡后入库任务:{taskInfo.taskCode}下发成功等待PLC执行反馈");
taskInfo.taskStatus = 2;
_taskInfoService.UpdateTaskInfo(taskInfo);
RefreshInStoreTaskEvent?.Invoke(taskInfo);
semaphore.Wait(); //一直堵塞直到信号量释放
//if (semaphore.Wait(TimeSpan.FromSeconds(20)))
//{
// // 收到反馈
// PrintLogInfoMessage("收到反馈,继续执行");
//}
//else
//{
// PrintLogInfoMessage("超时未反馈");
//}
PrintLogInfoMessage($"入库任务:{taskInfo.taskCode};开始执行");
semaphore.Wait(); //一直堵塞直到信号量释放
taskInfo.taskStatus = 2;
PrintLogInfoMessage($"泡后入库任务:{taskInfo.taskCode};执行完成");
_taskInfoService.UpdateTaskInfo(taskInfo);
RefreshInStoreTaskEvent?.Invoke(taskInfo,true);
RefreshInStoreTaskEvent?.Invoke(taskInfo);
}
else if(result==2)
{
PrintLogInfoMessage("泡后入库任务下发失败PLC接收任务未就绪");
}
else
{
PrintLogInfoMessage($"泡后入库任务:{taskInfo.taskCode}下发失败请排除PLC连接");
@ -288,64 +345,47 @@ namespace Aucma.Scada.Business
{
PrintLogInfoMessage("未获取到需要下发的泡后入库任务");
}
Thread.Sleep(3000);
}
/// <summary>
/// 开始出库堵塞入库任务下发
/// </summary>
public void IssueOutTask()
catch (Exception ex)
{
PrintLogInfoMessage("执行出库任务,入库进行堵塞");
_isBlocked = true;
_autoResetEvent.Set();
PrintLogErrorMessage("依次获取入库任务队列进行下发逻辑异常", ex);
}
/// <summary>
/// 出库完成,释放入库任务下发
/// </summary>
public void IssueInTask()
{
PrintLogInfoMessage("出库完成,释放入库");
_isBlocked = false;
_autoResetEvent.Set();
}
/// <summary>
/// 获取待执行的入库任务
/// 入库应答PLC收到入库任务后进行应答
/// </summary>
/// <param name="storeCode"></param>
/// <returns></returns>
private RealTaskInfo GetAwaitSendTask(string storeCode)
{
RealTaskInfo taskInfo = null;
try
/// <param name="taskCode"></param>
private void InStoreAnswer(string storeCode, string taskCode)
{
taskInfo = _taskInfoService.GetTaskInfoByStoreCode(storeCode, 1);
}
catch (Exception ex)
if (storeCode == appConfig.foamStoreCode)
{
PrintLogErrorMessage("获取待执行的入库任务异常", ex);
PrintLogInfoMessage("plc入库应答成功自动释放信号量进行下发新任务");
semaphore.Release();
}
return taskInfo;
}
/// <summary>
/// 泡后执行反馈
/// </summary>
private void FoamTaskFeedback(string taskCode)
{
PrintLogInfoMessage("泡后执行完成,自动释放信号量");
InStoreFinish(taskCode);
semaphore.Release();
///// <summary>
///// 泡后执行反馈
///// </summary>
//private void FoamTaskFeedback(string taskCode)
//{
}
// PrintLogInfoMessage("泡后执行完成,自动释放信号量");
// InStoreFinish(taskCode);
// semaphore.Release();
//}
#endregion
@ -366,6 +406,7 @@ namespace Aucma.Scada.Business
if (spaceInfo != null)
{
spaceInfo.materialType = taskInfo.materialType;
//spaceInfo.spaceStock = spaceInfo.spaceStock + 1;
//spaceInfo.onRouteAmount -= 1;
@ -407,6 +448,7 @@ namespace Aucma.Scada.Business
//清除任务信息
_taskInfoService.DeleteTaskInfo(taskCode, appConfig.foamStoreCode);
}
RefreshInStoreTaskEvent?.Invoke(taskInfo, true);
}
catch (Exception ex)
@ -437,7 +479,7 @@ namespace Aucma.Scada.Business
}
/// <summary>
/// 截条码
/// 截条码类型
/// </summary>
/// <param name="materialCode"></param>
/// <returns></returns>
@ -446,7 +488,7 @@ namespace Aucma.Scada.Business
string result = string.Empty;
if (!string.IsNullOrEmpty(materialCode))
{
result = materialCode.Substring(2, 10);
result = materialCode.Substring(7, 10);
}
return result;
@ -505,7 +547,6 @@ namespace Aucma.Scada.Business
private BaseSpaceInfo GetSpaceInfoByMaterialType(string storeCode, string materialType)
{
BaseSpaceInfo result = null;
try
{
List<BaseSpaceInfo> info = _spaceInfoService.GetBaseSpaceInfosByMaterialType(storeCode, materialType);
@ -522,8 +563,12 @@ namespace Aucma.Scada.Business
item.spaceStatus = spaceInfo.spaceStatus;
}
result = info.Where(x => x.spaceStatus == 1 && x.spaceStock > 0 ? x.spaceCapacity > (x.spaceStock + x.onRouteAmount) : 1 == 1).OrderByDescending(x => x.spaceStock).OrderBy(x => x.spaceCode).First();
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();
}
}
}
}
@ -534,5 +579,52 @@ namespace Aucma.Scada.Business
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
}
}

@ -2,6 +2,7 @@
using HighWayIot.Config;
using HighWayIot.Log4net;
using HighWayIot.Plc;
using HighWayIot.Repository.service;
using System;
using System.Collections.Generic;
using System.Threading;
@ -35,6 +36,14 @@ namespace Aucma.Scada.Business
private PlcPool _pool = PlcPool.Instance;
private PlcSpaceConfig spaceConfig = PlcSpaceConfig.Instance;
private RegisterServices registerServices = RegisterServices.Instance;
/// <summary>
/// 实时任务
/// </summary>
private IRealTaskInfoService _taskInfoService;
#endregion
#region 私有变量
@ -48,6 +57,15 @@ namespace Aucma.Scada.Business
#endregion
#region 委托事件
/// <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>
@ -58,115 +76,127 @@ namespace Aucma.Scada.Business
private InStoreTaskHandle()
{
_taskInfoService = registerServices.GetService<IRealTaskInfoService>();
_plcDictionary = _pool.GetAll();
List<RealTaskInfo> taskList = _taskInfoService.GetTaskInfosForInstore(appConfig.foamStoreCode, appConfig.instoreTaskType, 2);
RealReadFinish();
}
#region 泡后入库任务下发处理
public bool SendFoamTask_InStore(RealTaskInfo taskInfo)
#region 判断入库是否完成
/// <summary>
/// 实时读取入库完成信号
/// </summary>
private void RealReadFinish()
{
bool result = false;
try
Task.Run(() =>
{
IPlc _plc = _plcDictionary[taskInfo.storeCode];
if (_plc != null)
while (true)
{
if (_plc.IsConnected)
if (_plcDictionary.Count > 0)
{
IPlc _plc = _plcDictionary[appConfig.foamStoreCode];
if (_plc != null && _plc.IsConnected)
{
//写入货道号
_plc.writeStringByAddress(plcConfig.in_foam_spaceCode, taskInfo.spaceCode);
//写入应答字
_plc.writeInt32ByAddress(plcConfig.in_foam_answer, 1);
//写入任务号
_plc.writeStringByAddress(plcConfig.in_foam_task, taskInfo.taskCode);
//写入完成后读取应答字进行复位
ReadShellAnswer_InStore(taskInfo.taskCode);
result = true;
}
else
List<RealTaskInfo> taskList = _taskInfoService.GetTaskInfosForInstore(appConfig.foamStoreCode, appConfig.instoreTaskType, 2);
if (taskList != null && taskList.Count > 0)
{
logHelper.Info($"仓库{taskInfo.storeCode}PLC未连接");
foreach (RealTaskInfo taskInfo in taskList)
{
SpaceAddress spaceAddress = spaceConfig.GetSpaceAddress(appConfig.foamStoreCode, taskInfo.spaceCode);
JudgeIsFinish(taskInfo, _plc, spaceAddress);
}
}
}
else
{
logHelper.Info($"PLC信息为空,通过{taskInfo.storeCode}未获取到该仓库对应的PLC信息");
logHelper.Info($"PLC信息为空或连接失败,通过{appConfig.foamStoreCode}未获取到该仓库对应的PLC信息");
}
}
catch (Exception ex)
{
logHelper.Error("泡后入库任务下发异常", ex);
}
return result;
Thread.Sleep(1000);
};
});
}
/// <summary>
/// 读取泡后入库应答
/// 判断该任务对应的货道是否有完成信号
/// </summary>
private void ReadShellAnswer_InStore(string taskCode)
/// <param name="taskInfo"></param>
/// <returns></returns>
public void JudgeIsFinish(RealTaskInfo taskInfo,IPlc _plc,SpaceAddress spaceAddress)
{
lock (string.Empty)
//读取入库完成反馈信号
if (_plc.readInt32ByAddress(spaceAddress.inStoreFinish) == 1)
{
bool isFlag = true;
IPlc _plc = _plcDictionary[appConfig.foamStoreCode];
try
_plc.writeInt32ByAddress(spaceAddress.inStoreFinish, 0);
InStoreFinsihEvent(taskInfo.taskCode);
}
}
#endregion
#region 泡后入库任务下发处理
public int SendFoamTask_InStore(RealTaskInfo taskInfo)
{
Task.Run(() =>
int result = 0;
try
{
IPlc _plc = _plcDictionary[taskInfo.storeCode];
if (_plc != null)
{
if (_plc.IsConnected)
{
do
{
//读取PLC应答字为2时上位机清空写入的入库内容
if (_plc.readInt32ByAddress(plcConfig.in_foam_answer) == 2)
if (_plc.readInt32ByAddress(plcConfig.in_foam_answer) == 1)
{
logHelper.Info("泡后入库应答字为1货道号:" + plcConfig.in_foam_spaceCode + ";写" + short.Parse(taskInfo.spaceCode.Substring(5, 1)));
//写入货道号
_plc.writeStringByAddress(plcConfig.in_foam_spaceCode, string.Empty);
_plc.writeInt32ByAddress(plcConfig.in_foam_spaceCode, short.Parse(taskInfo.spaceCode.Substring(5, 1)));
//写入应答字
_plc.writeInt32ByAddress(plcConfig.in_foam_answer, 0);
// _plc.writeInt32ByAddress(plcConfig.in_foam_answer, 1);
//写入任务号
_plc.writeStringByAddress(plcConfig.in_foam_task, string.Empty);
isFlag = false;
// _plc.writeStringByAddress(plcConfig.in_foam_task, taskInfo.taskCode);
//写入完成后读取应答字进行复位
ReadAnswer_InStore(taskInfo);
ReadShellFinish_InStore(taskCode);
result = 1;
}
else
{
result = 2;
logHelper.Info("应答字为2下发新任务plc未就绪");
}
Thread.Sleep(1000);
} while (isFlag);
}
else
{
logHelper.Info($"仓库{appConfig.foamStoreCode}PLC未连接");
logHelper.Info($"仓库{taskInfo.storeCode}PLC未连接");
}
}
else
{
logHelper.Info($"PLC信息为空通过{appConfig.foamStoreCode}未获取到该仓库对应的PLC信息");
logHelper.Info($"PLC信息为空通过{taskInfo.storeCode}未获取到该仓库对应的PLC信息");
}
});
}
catch (Exception ex)
{
logHelper.Error("读取泡后入库应答字异常", ex);
}
logHelper.Error("泡后入库任务下发异常", ex);
}
return result;
}
/// <summary>
/// 读取泡后入库完成
/// 读取泡后入库应答
/// </summary>
private void ReadShellFinish_InStore(string taskCode)
private void ReadAnswer_InStore(RealTaskInfo taskInfo)
{
lock (string.Empty)
{
bool isFlag = true;
IPlc _plc = _plcDictionary[appConfig.foamStoreCode];
foamTaskCode = taskCode;
try
{
Task.Run(() =>
@ -177,19 +207,21 @@ namespace Aucma.Scada.Business
{
do
{
//读取PLC入库任务完成
if (_plc.readInt32ByAddress(plcConfig.in_foam_finish) == 1)
//读取PLC应答字为2时上位机清空写入的入库内容
if (_plc.readInt32ByAddress(plcConfig.in_foam_answer) == 2)
{
_plc.writeInt32ByAddress(plcConfig.in_foam_finish, 0);
//string taskCode = _plc.readStringByAddress(plcConfig.out_foam_task, 10);
InStoreFinsihEvent?.Invoke(taskCode);
logHelper.PlcLog("入库应答字为2货道号:" + plcConfig.in_foam_spaceCode + ";复位写0");
//写入货道号
_plc.writeInt32ByAddress(plcConfig.in_foam_spaceCode, 0);
//写入应答字
// _plc.writeInt32ByAddress(plcConfig.in_foam_answer, 0);
isFlag = false;
InStoreAnswerEvent?.Invoke(appConfig.foamStoreCode, taskInfo.taskCode);
}
Thread.Sleep(1000);
Thread.Sleep(500);
} while (isFlag);
}
else
@ -205,7 +237,7 @@ namespace Aucma.Scada.Business
}
catch (Exception ex)
{
logHelper.Error("读取泡后入库完成异常", ex);
logHelper.Error("读取泡后入库应答字异常", ex);
}
}
}
@ -227,11 +259,70 @@ namespace Aucma.Scada.Business
{
spaceInfo.spaceStock = _plc.readInt32ByAddress(spaceAddress.onStore);
spaceInfo.onRouteAmount = _plc.readInt32ByAddress(spaceAddress.onRoute);
spaceInfo.spaceStatus = _plc.readInt32ByAddress(spaceAddress.spaceStatus);
// 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)
// {
// if (_plc.IsConnected)
// {
// 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($"仓库{appConfig.foamStoreCode}PLC未连接");
// }
// }
// else
// {
// logHelper.Info($"PLC信息为空通过{appConfig.foamStoreCode}未获取到该仓库对应的PLC信息");
// }
// });
// }
// catch (Exception ex)
// {
// logHelper.Error("读取泡后入库完成异常", ex);
// }
// }
//}
#endregion
}
}

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using static Aucma.Scada.Business.OutStoreTaskHandle;
namespace Aucma.Scada.Business
{
@ -113,7 +114,10 @@ namespace Aucma.Scada.Business
_recordOutStoreService = registerServices.GetService<IRecordOutStoreService>();
_recordProductfinishService = registerServices.GetService<IRecordProductfinishService>();
assemblyPlanBusiness.NextPassExecutePlanInfoEvent += PlanHandle;
taskHandleBusiness.OutStoreFinsihEvent += TaskFeedback;
taskHandleBusiness.OutStoreAnswerEvent += OutStoreAnswer;
taskHandleBusiness.OutStoreFinsihEvent += OutStoreFinish;
StartPassDown();
}
@ -129,8 +133,13 @@ namespace Aucma.Scada.Business
{
if (planInfo != null)
{
var bomInfo = _bomInfoService.GetChildenBomInfoByMaterialCode(planInfo.materialCode, appConfig.foamMaterialType);
// var bomInfo = _bomInfoService.GetChildenBomInfoByMaterialCode(planInfo.materialCode, appConfig.foamMaterialType);
var bomInfo = _bomInfoService.GetBomInfoByMaterialCode(planInfo.materialCode);
if (bomInfo==null)
{
PrintLogInfoMessage($"物料:{planInfo.materialCode}获取Bom信息为空");
return;
}
for (int i = 0; i < planInfo.planAmount - planInfo.completeAmount; i++)
{
string taskCode = System.Guid.NewGuid().ToString("N").Substring(0, 10);
@ -161,7 +170,16 @@ namespace Aucma.Scada.Business
{
PrintLogInfoMessage($"匹配货道:{spaceInfo.spaceName}");
// RefreshScanMateriaCodeEvent?.Invoke(materiaclCode, materialType, spaceInfo.spaceName, storeCode); //刷新界面扫码信息
CreateOutStoreTask(spaceInfo, planCode, taskCode); //创建出库任务
bool result = CreateOutStoreTask(spaceInfo, planCode, taskCode); //创建出库任务
if (result)
{
PrintLogInfoMessage("出库任务创建成功");
}
else
{
PrintLogInfoMessage("出库任务创建失败");
}
}
else
{
@ -188,7 +206,6 @@ namespace Aucma.Scada.Business
BaseSpaceDetail spaceDetail = GetSpaceDetailFirstOrderByCreatTime(spaceInfo);
if (spaceDetail != null)
{
#region 出库任务赋值
RealTaskInfo realTaskInfo = new RealTaskInfo();
realTaskInfo.planCode = planCode;
@ -205,6 +222,7 @@ namespace Aucma.Scada.Business
realTaskInfo.createTime = DateTime.Now;
#endregion
result = _taskInfoService.AddTaskInfo(realTaskInfo);
if (result)
{
@ -277,8 +295,8 @@ namespace Aucma.Scada.Business
//{
// spaceInfo.spaceStock = spaceInfo.spaceStock + 1;
//}
spaceInfo.outRouteAmount += 1;
_spaceInfoService.UpdateSpaceInfo(spaceInfo);
// spaceInfo.outRouteAmount += 1;
// _spaceInfoService.UpdateSpaceInfo(spaceInfo);
}
#region 轮询获取出库任务下发至PLC等待PLC执行反馈完成后再次下发
@ -288,6 +306,7 @@ namespace Aucma.Scada.Business
private int taskAmount = 2;
private void StartPassDown()
{
Task.Run(() =>
@ -295,7 +314,7 @@ namespace Aucma.Scada.Business
while (true)
{
PassDownTaskInfo();
Thread.Sleep(1000);
Thread.Sleep(500);
}
});
@ -308,66 +327,51 @@ namespace Aucma.Scada.Business
/// <param name="e"></param>
private void PassDownTaskInfo()
{
string taskCode = string.Empty;
string executePlanCode = string.Empty;
int iFlag = 0;
completedTasks = 0;
try
{
//获取待执行的出库任务下发至PLC,并将任务状态改为执行中
var taskInfoList = GetAwaitSendTask();
RealTaskInfo taskInfo = _taskInfoService.GetTaskInfoByStoreCode(appConfig.foamStoreCode, appConfig.outstoreTaskType);
if (taskInfoList.Count > 0)
if (taskInfo != null)
{
taskAmount = taskInfoList.Count; //下发的任务数量默认2泡后、内胆泡后、内胆均执行完成后才会释放信号量
foreach (var item in taskInfoList)
PrintLogInfoMessage($"下发泡后出库任务:{taskInfo.taskCode};仓库{taskInfo.storeCode};货道:{taskInfo.spaceCode}");
int result = taskHandleBusiness.SendFoamTask_OutStore(taskInfo);
if (result==1)
{
taskCode = item.taskCode;
executePlanCode = item.planCode;
if (taskHandleBusiness.SendFoamTask_OutStore(item))
PrintLogInfoMessage($"泡后出库任务:{taskInfo.taskCode}下发成功等待PLC执行反馈");
semaphore.Wait();//一直堵塞直到信号量释放
PrintLogInfoMessage($"泡后出库任务:{taskInfo.taskCode};开始执行");
taskInfo.taskStatus = 2;
_taskInfoService.UpdateTaskInfo(taskInfo);
RefreshScanMateriaCodeEvent?.Invoke(taskInfo.materialCode, taskInfo.materialType, taskInfo.spaceName, taskInfo.storeCode);
}
else if (result == 2)
{
PrintLogInfoMessage($"下发泡后出库任务:{item.taskCode};仓库{item.storeCode};货道:{item.spaceCode}等待PLC执行反馈");
item.taskStatus = 2;
iFlag++;
PrintLogInfoMessage("泡后出库任务下发失败PLC接收任务未就绪");
}
else
{
PrintLogInfoMessage($"泡后出库任务:{item.taskCode}下发失败请排除PLC连接");
continue;
PrintLogInfoMessage($"泡后出库任务:{taskInfo.taskCode}下发失败请排除PLC连接");
}
_taskInfoService.UpdateTaskInfo(item);
RefreshScanMateriaCodeEvent?.Invoke(item.materialCode, item.materialType, item.spaceName, item.storeCode);
}
if (iFlag == taskInfoList.Count)
else
{
inStoreBusiness.IssueOutTask();
semaphore.Wait(); //一直堵塞直到信号量释放
inStoreBusiness.IssueInTask();
PrintLogInfoMessage($"出库任务:{taskCode};执行完成");
UpdatePlanInfo(executePlanCode);
RefreshStoreStockEvent?.Invoke();
PrintLogInfoMessage("未获取到需要下发的出库任务");
}
}
else
catch (Exception ex)
{
PrintLogInfoMessage("未获取到需要下发的出库任务");
PrintLogErrorMessage("下传出库任务逻辑处理异常", ex);
}
Thread.Sleep(3000);
}
#endregion
/// <summary>
/// 获取待执行的出库任务
/// 获取所有待执行的出库任务
/// </summary>
/// <returns></returns>
private List<RealTaskInfo> GetAwaitSendTask()
@ -375,20 +379,7 @@ namespace Aucma.Scada.Business
List<RealTaskInfo> taskInfos = new List<RealTaskInfo>();
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);
}
taskInfos = _taskInfoService.GetAllTaskInfoByStoreCode(appConfig.foamStoreCode, 2);
}
catch (Exception ex)
{
@ -397,28 +388,30 @@ namespace Aucma.Scada.Business
return taskInfos;
}
#region PLC出库应答反馈
/// <summary>
/// PLC任务执行反馈
/// 出库应答
/// </summary>
/// <param name="storeCode"></param>
/// <param name="taskCode"></param>
private void TaskFeedback(string storeCode, string taskCode)
private void OutStoreAnswer(string storeCode, string taskCode)
{
FoamTaskFeedback(taskCode);
PrintLogInfoMessage("出库应答成功,自动释放信号量,进行下发新任务");
semaphore.Release();
}
/// <summary>
/// 泡后执行反馈
/// 出库完成
/// </summary>
private void FoamTaskFeedback(string taskCode)
/// <param name="storeCode"></param>
/// <param name="taskCode"></param>
private void OutStoreFinish(string storeCode, string taskCode)
{
Interlocked.Increment(ref completedTasks);
CheckCompletedTasks();
PrintLogInfoMessage("泡后执行完成,自动释放信号量");
PrintLogInfoMessage($"出库任务:{taskCode};执行完成");
OutStoreFinish(taskCode, appConfig.foamStoreCode);
OutStoreFinishHandle(taskCode, appConfig.foamStoreCode);
}
/// <summary>
@ -442,7 +435,7 @@ namespace Aucma.Scada.Business
/// <param name="storeCode"></param>
/// <param name="spaceCode"></param>
/// <param name="materialType"></param>
private void OutStoreFinish(string taskCode, string storeCode)
private void OutStoreFinishHandle(string taskCode, string storeCode)
{
try
{
@ -453,24 +446,22 @@ namespace Aucma.Scada.Business
if (spaceInfo != null)
{
// taskHandleBusiness.WritePlc(spaceInfo.storeCode, spaceInfo.spaceCode);
//读取PLC获取货道信息存放数量、在途数量
spaceInfo.spaceStock -= 1;
spaceInfo.outRouteAmount -= 1;
#region Add By wenjy 2023-10-30 13:44:00 通过PLC获取货道信息
var item = taskHandleBusiness.ReadSpaceInfoByPlc(spaceInfo);
spaceInfo.spaceStock = item.spaceStock;
spaceInfo.onRouteAmount = item.onRouteAmount;
spaceInfo.spaceStatus = item.spaceStatus;
//spaceInfo.spaceStatus = item.spaceStatus;
#endregion
if (spaceInfo.spaceStock == 0)
{
spaceInfo.materialType = string.Empty;
}
_spaceInfoService.UpdateSpaceInfo(spaceInfo);
//读取PLC获取物料类型进行绑定
_spaceInfoService.UpdateSpaceInfo(spaceInfo);
#region 添加出库记录
RecordOutstore recordOutstore = new RecordOutstore();
@ -484,6 +475,12 @@ namespace Aucma.Scada.Business
_recordOutStoreService.InsertReocrdOutStoreService(recordOutstore);
#endregion
}
//更新计划信息
UpdatePlanInfo(taskInfo.planCode);
RefreshStoreStockEvent?.Invoke();
//清除任务信息
_taskInfoService.DeleteTaskInfo(taskCode, storeCode);
@ -500,6 +497,7 @@ namespace Aucma.Scada.Business
}
}
/// <summary>
/// 更新计划信息
/// </summary>
@ -571,7 +569,7 @@ namespace Aucma.Scada.Business
/// </summary>
/// <param name="taskCode"></param>
/// <returns></returns>
public bool DeleteTaskInfoByTaskCode(string taskCode)
public bool DeleteTaskInfoByTaskCode(string taskCode, bool isFlag)
{
bool result = false;
var info = _taskInfoService.GetTaskInfosByTaskCode(taskCode);
@ -579,21 +577,24 @@ namespace Aucma.Scada.Business
{
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)
{
if (!isFlag)
{
spaceDetailInfo.isFlag = 0;
_spaceDetailService.UpdateSpaceDetail(spaceDetailInfo);
}
else
{
_spaceDetailService.DeleteSpaceDetailByMaterialCode(spaceDetailInfo.materialCode);
}
}
}
}
}
@ -614,7 +615,7 @@ namespace Aucma.Scada.Business
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);
result = this.CreateOutStoreTask(spaceInfo, System.Guid.NewGuid().ToString("N").Substring(0, 10), System.Guid.NewGuid().ToString("N").Substring(0, 10), 1);
}
else
{
@ -701,10 +702,21 @@ namespace Aucma.Scada.Business
var spaceInfo = taskHandleBusiness.ReadSpaceInfoByPlc(item);
item.spaceStock = spaceInfo.spaceStock;
item.onRouteAmount = spaceInfo.onRouteAmount;
item.spaceStatus = spaceInfo.spaceStatus;
// item.spaceStatus = spaceInfo.spaceStatus;
}
result = info.Where(x => x.spaceStatus == 1 && x.spaceStock > 0).OrderBy(x => x.spaceStock).OrderBy(x => x.spaceCode).First();
//result = info.Where(x => x.spaceStatus == 1 && x.spaceStock > 0).OrderBy(x => x.spaceStock).OrderBy(x => x.spaceCode).First();
var list = info.Where(x => x.spaceStatus == 1 && x.spaceStock > 0).ToList();
if (list.Count > 0)
{
result = info.OrderBy(x => x.spaceStock).OrderBy(x => x.spaceCode).First();
}
else
{
PrintLogInfoMessage("未获取到匹配的货道,请排查货道库存及货道状态是否可用");
}
}
}
@ -716,5 +728,5 @@ namespace Aucma.Scada.Business
return result;
}
}
}
} }

@ -2,8 +2,10 @@
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;
@ -27,6 +29,15 @@ namespace Aucma.Scada.Business
#region 对象引用
/// <summary>
/// PLC应答
/// </summary>
/// <param name="storeCode"></param>
/// <param name="taskCode"></param>
public delegate void OutStoreAnswer(string storeCode, string taskCode);
public event OutStoreAnswer OutStoreAnswerEvent;
private LogHelper logHelper = LogHelper.Instance;
private AppConfig appConfig = AppConfig.Instance;
@ -36,6 +47,13 @@ namespace Aucma.Scada.Business
private PlcPool _pool = PlcPool.Instance;
private PlcSpaceConfig spaceConfig = PlcSpaceConfig.Instance;
private RegisterServices registerServices = RegisterServices.Instance;
private IRealTaskInfoService _taskInfoService;
#endregion
#region 私有变量
@ -44,15 +62,8 @@ namespace Aucma.Scada.Business
/// </summary>
private Dictionary<string, IPlc> _plcDictionary = new Dictionary<string, IPlc>();
/// <summary>
/// 泡后任务编号,PLC反馈后进行赋值
/// </summary>
private string shellTaskCode = string.Empty;
/// <summary>
/// 内胆任务编号,PLC反馈后进行赋值
/// </summary>
private string linerTaskCode = string.Empty;
#endregion
#region 委托事件
@ -69,122 +80,132 @@ namespace Aucma.Scada.Business
private OutStoreTaskHandle()
{
_plcDictionary = _pool.GetAll();
_taskInfoService = registerServices.GetService<IRealTaskInfoService>();
RealReadFinish();
}
#region 泡后出库任务下发处理
#region 出库完成
/// <summary>
/// 泡后出库任务下发
/// 实时读取出库完成信号
/// </summary>
/// <param name="taskInfo"></param>
public bool SendFoamTask_OutStore(RealTaskInfo taskInfo)
private void RealReadFinish()
{
bool result = false;
try
Task.Run(() =>
{
IPlc _plc = _plcDictionary[taskInfo.storeCode];
if (_plc != null)
while (true)
{
if (_plc.IsConnected)
RealTaskInfo task = GetTaskInfoByTaskStatus(appConfig.foamStoreCode).OrderBy(x => x.createTime).FirstOrDefault();
if(task != null)
{
//写入货道号
_plc.writeStringByAddress(plcConfig.out_foam_spaceCode, taskInfo.spaceCode);
//写入出库数量
_plc.writeInt32ByAddress(plcConfig.out_foam_amount, taskInfo.planAmount);
//写入应答字
_plc.writeInt32ByAddress(plcConfig.out_foam_answer, 1);
//写入任务号
_plc.writeStringByAddress(plcConfig.out_foam_task, taskInfo.taskCode);
//写入完成后读取应答字进行复位
ReadShellAnswer_OutStore(taskInfo.taskCode);
if (_plcDictionary.Count > 0)
{
IPlc _plc = _plcDictionary[appConfig.foamStoreCode];
if (_plc != null && _plc.IsConnected)
{
//出库完成
if (_plc.readInt32ByAddress(plcConfig.out_foam_finish) == 1)
{
_plc.writeInt32ByAddress(plcConfig.out_foam_finish, 0);
OutStoreFinsihEvent?.Invoke(appConfig.foamStoreCode, task.taskCode);
result = true;
}
}
else
{
logHelper.Info($"仓库{taskInfo.storeCode}PLC未连接");
logHelper.Info($"PLC信息为空或连接失败通过{appConfig.foamStoreCode}未获取到该仓库对应的PLC信息");
}
}
else
{
logHelper.Info($"PLC信息为空通过{taskInfo.storeCode}未获取到该仓库对应的PLC信息");
}
Thread.Sleep(500);
};
});
}
/// <summary>
/// 根据任务状态获取执行中的出库任务
/// </summary>
/// <param name="storeCode"></param>
/// <param name="taskStatus"></param>
private List<RealTaskInfo> GetTaskInfoByTaskStatus(string storeCode, int taskStatus = 2)
{
List<RealTaskInfo> result = null;
try
{
result = _taskInfoService.GetTaskInfosByTaskStatus(new string[] { storeCode }, appConfig.outstoreTaskType, taskStatus);
}
catch (Exception ex)
{
logHelper.Error("泡后出库任务下发异常", ex);
logHelper.Error("根据任务状态获取执行中的任务异常", ex);
}
return result;
}
#endregion
#region 泡后出库任务下发处理
/// <summary>
/// 读取泡后出库应答
/// 泡后出库任务下发
/// </summary>
private void ReadShellAnswer_OutStore(string taskCode)
{
lock (string.Empty)
/// <param name="taskInfo"></param>
public int SendFoamTask_OutStore(RealTaskInfo taskInfo)
{
bool isFlag = true;
IPlc _plc = _plcDictionary[appConfig.foamStoreCode];
int result = 0;
try
{
Task.Run(() =>
{
IPlc _plc = _plcDictionary[taskInfo.storeCode];
if (_plc != null)
{
if (_plc.IsConnected)
{
do
{
//读取PLC应答字为2时上位机清空写入的出库内容
if (_plc.readInt32ByAddress(plcConfig.out_foam_answer) == 2)
if (_plc.readInt32ByAddress(plcConfig.out_foam_answer) == 1)
{
logHelper.Info("泡后出库应答字为1货道号:" + plcConfig.out_foam_spaceCode + ";写" + short.Parse(taskInfo.spaceCode.Substring(5, 1)));
//写入货道号
_plc.writeStringByAddress(plcConfig.out_foam_spaceCode, string.Empty);
_plc.writeInt32ByAddress(plcConfig.out_foam_spaceCode, short.Parse(taskInfo.spaceCode.Substring(5, 1)));
//写入出库数量
_plc.writeInt32ByAddress(plcConfig.out_foam_amount, 0);
//写入应答字
_plc.writeInt32ByAddress(plcConfig.out_foam_answer, 0);
//写入任务号
_plc.writeStringByAddress(plcConfig.out_foam_task, string.Empty);
isFlag = false;
_plc.writeInt32ByAddress(plcConfig.out_foam_amount, taskInfo.planAmount);
ReadShellFinish_OutStore(taskCode);
}
//写入完成后读取应答字进行复位
ReadAnswer_OutStore(taskInfo);
Thread.Sleep(1000);
} while (isFlag);
result = 1;
}
else
{
logHelper.Info($"仓库{appConfig.foamStoreCode}PLC未连接");
result = 2;
logHelper.Info("应答字为2下发新任务plc未就绪");
}
}
else
{
logHelper.Info($"PLC信息为空通过{appConfig.foamStoreCode}未获取到该仓库对应的PLC信息");
logHelper.Info($"仓库{taskInfo.storeCode}PLC未连接");
}
});
}
catch (Exception ex)
else
{
logHelper.Error("读取泡后出库应答字异常", ex);
logHelper.Info($"PLC信息为空通过{taskInfo.storeCode}未获取到该仓库对应的PLC信息");
}
}
catch (Exception ex)
{
logHelper.Error("泡后出库任务下发异常", ex);
}
return result;
}
/// <summary>
/// 读取泡后出库完成
/// 读取泡后出库应答
/// </summary>
private void ReadShellFinish_OutStore(string taskCode)
private void ReadAnswer_OutStore(RealTaskInfo taskInfo)
{
lock (string.Empty)
{
bool isFlag = true;
IPlc _plc = _plcDictionary[appConfig.foamStoreCode];
shellTaskCode = taskCode;
try
{
Task.Run(() =>
@ -195,19 +216,21 @@ namespace Aucma.Scada.Business
{
do
{
//读取PLC出库任务完成
if (_plc.readInt32ByAddress(plcConfig.out_foam_finish) == 1)
//读取PLC应答字为2时上位机清空写入的出库内容
if (_plc.readInt32ByAddress(plcConfig.out_foam_answer) == 2)
{
_plc.writeInt32ByAddress(plcConfig.out_foam_finish, 0);
//string taskCode = _plc.readStringByAddress(plcConfig.out_foam_task, 10);
OutStoreFinsihEvent?.Invoke(appConfig.foamStoreCode, taskCode);
logHelper.Info("出库应答字为2货道号:" + plcConfig.out_foam_spaceCode + ";复位写0");
//写入货道号
_plc.writeInt32ByAddress(plcConfig.out_foam_spaceCode, 0);
//写入出库数量
_plc.writeInt32ByAddress(plcConfig.out_foam_amount, 0);
isFlag = false;
OutStoreAnswerEvent?.Invoke(appConfig.foamStoreCode, taskInfo.taskCode);
}
Thread.Sleep(1000);
Thread.Sleep(500);
} while (isFlag);
}
else
@ -223,10 +246,12 @@ namespace Aucma.Scada.Business
}
catch (Exception ex)
{
logHelper.Error("读取泡后出库出库完成异常", ex);
logHelper.Error("读取泡后出库应答字异常", ex);
}
}
}
#endregion
/// <summary>
@ -245,11 +270,68 @@ namespace Aucma.Scada.Business
{
spaceInfo.spaceStock = _plc.readInt32ByAddress(spaceAddress.onStore);
spaceInfo.onRouteAmount = _plc.readInt32ByAddress(spaceAddress.onRoute);
spaceInfo.spaceStatus = _plc.readInt32ByAddress(spaceAddress.spaceStatus);
// spaceInfo.spaceStatus = _plc.readInt32ByAddress(spaceAddress.spaceStatus);
}
}
return spaceInfo;
}
///// <summary>
///// 读取泡后出库完成
///// </summary>
//private void ReadFinish_OutStore(RealTaskInfo taskInfo)
//{
// lock (string.Empty)
// {
// bool isFlag = true;
// IPlc _plc = _plcDictionary[appConfig.foamStoreCode];
// try
// {
// if (_plc != null)
// {
// if (_plc.IsConnected)
// {
// do
// {
// //读取PLC出库任务完成
// if (_plc.readInt32ByAddress(plcConfig.out_foam_finish) == 1)
// {
// _plc.writeInt32ByAddress(plcConfig.out_foam_finish, 0);
// OutStoreFinsihEvent?.Invoke(appConfig.foamStoreCode, taskInfo.taskCode);
// isFlag = false;
// }
// Thread.Sleep(1000);
// } while (isFlag);
// }
// else
// {
// logHelper.Info($"仓库{appConfig.foamStoreCode}PLC未连接");
// }
// }
// else
// {
// logHelper.Info($"PLC信息为空通过{appConfig.foamStoreCode}未获取到该仓库对应的PLC信息");
// }
// }
// catch (Exception ex)
// {
// logHelper.Error("读取箱壳出库出库完成异常", ex);
// }
// }
//}
}
}

@ -61,6 +61,7 @@ namespace Aucma.Scada.Business
services.AddSingleton<IRecordInStoreService, RecordInStoreServiceImpl>();
services.AddSingleton<IRecordOutStoreService, RecordOutStoreServiceImpl>();
services.AddSingleton<IRecordProductfinishService, RecordProductfinishServiceImpl>();
services.AddSingleton<IPrintBarCodeServices, PrintBarCodeServicesImpl>();
}
public T GetService<T>()

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>System.Reactive.Windows.Threading</name>
</assembly>
<members>
</members>
</doc>

File diff suppressed because it is too large Load Diff

@ -1 +1 @@
0d0ae30db0cb44b31c0ab9092cac8bfadb5631ad
a8efbbac1222f58683c22a85ac49ead5747df755

@ -122,3 +122,7 @@ E:\c#\AUCMA\aucma.scada\foam\Aucma.Scada.Business\bin\Debug\System.Runtime.Compi
E:\c#\AUCMA\aucma.scada\foam\Aucma.Scada.Business\bin\Debug\System.Threading.Tasks.Extensions.xml
E:\c#\AUCMA\aucma.scada\foam\Aucma.Scada.Business\bin\Debug\Newtonsoft.Json.xml
E:\c#\AUCMA\aucma.scada\foam\Aucma.Scada.Business\obj\Debug\Aucma.Scada.Business.csproj.CopyComplete
E:\c#\AUCMA\aucma.scada\foam\Aucma.Scada.Business\bin\Debug\System.Reactive.dll
E:\c#\AUCMA\aucma.scada\foam\Aucma.Scada.Business\bin\Debug\System.Reactive.Windows.Threading.dll
E:\c#\AUCMA\aucma.scada\foam\Aucma.Scada.Business\bin\Debug\System.Reactive.xml
E:\c#\AUCMA\aucma.scada\foam\Aucma.Scada.Business\bin\Debug\System.Reactive.Windows.Threading.xml

@ -3,6 +3,9 @@
<package id="Microsoft.Bcl.AsyncInterfaces" version="7.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.DependencyInjection" version="7.0.0" targetFramework="net48" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="7.0.0" targetFramework="net48" />
<package id="System.Reactive" version="6.0.0" targetFramework="net48" />
<package id="System.Reactive.Windows.Threading" version="6.0.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.3" targetFramework="net48" />
<package id="System.Threading" version="4.3.0" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
</packages>

@ -210,11 +210,11 @@
Foreground="#FFFFFF" >
<!--resourceStyle 399行修改选中字体颜色-->
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding taskCode}" Header="任务编号" Width="100" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
<DataGridTextColumn Binding="{Binding materialCode}" Header="物料编码" Width="*" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
<DataGridTextColumn Binding="{Binding taskCode}" Header="任务编号" Width="0.4*" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
<DataGridTextColumn Binding="{Binding materialCode}" Header="物料编码" Width="1.6*" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
<DataGridTextColumn Binding="{Binding materialType}" Header="物料类型" Width="*" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
<DataGridTextColumn Binding="{Binding spaceCode}" Header="入库货道" Width="*" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
<DataGridTextColumn Binding="{Binding taskStatus,Converter={StaticResource TaskStatusConverter}}" Header="入库状态" Width="*" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
<DataGridTextColumn Binding="{Binding spaceCode}" Header="入库货道" Width="0.5*" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
<DataGridTextColumn Binding="{Binding taskStatus,Converter={StaticResource TaskStatusConverter}}" Header="入库状态" Width="0.5*" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
<DataGridTextColumn Binding="{Binding createTime,StringFormat=\{0:MM月dd日 HH:mm\}}" Header="任务时间" Width="*" IsReadOnly="True" ElementStyle="{StaticResource DataGridTextColumnCenterSytle}"/>
</DataGrid.Columns>
</DataGrid>

@ -115,7 +115,8 @@
<Grid Margin="3,3">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="2*"/>
<!--<RowDefinition Height="2*"/>-->
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0">
@ -156,8 +157,8 @@
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<!--<RowDefinition/>
<RowDefinition/>-->
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
@ -176,7 +177,7 @@
<TextBlock Text="{Binding materialType}" FontSize="16" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Button>
</Border>
<Border Grid.Column="0" Grid.Row="1" BorderBrush="White" BorderThickness="1" >
<!--<Border Grid.Column="0" Grid.Row="1" BorderBrush="White" BorderThickness="1" >
<TextBlock Text="型号2" FontSize="18" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
<Border Grid.Column="1" Grid.Row="1" BorderBrush="White" BorderThickness="1">
@ -191,7 +192,7 @@
<Button Command="{Binding DataContext.SubmitCommand3, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding Text, ElementName=spaceCodeText}" Background="Transparent">
<TextBlock Text="{Binding typeCodeC}" FontSize="16" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Button>
</Border>
</Border>-->
</Grid>
</Border>

@ -4,7 +4,7 @@ mesConnStr=Data Source=10.100.72.20/ORCLCDB;User ID=c##aucma_mes;Password=aucma
scadaConnStr=Data Source=10.100.72.20/ORCLCDB;User ID=c##aucma_scada;Password=aucma
#¹¤Î»±àºÅ
stationCode=1005
stationCode=1006
#¹¤Î»Ãû³Æ
stationName=Åݺó¿â
@ -16,7 +16,7 @@ linerStoreCode=NDJCK-001
foamStoreCode=FPJCK-001
#ÅݺóÎïÁÏÀàÐͱàºÅ
foamMaterialType=400
foamMaterialType=200
#Èë¿âÈÎÎñÀàÐͱàºÅ
instoreTaskType=1

@ -5,13 +5,11 @@
[foam_inStore_address]
入库货道号=D7000
入库应答字=D7010
入库完成=D7020
入库任务号=
[foam_outStore_address]
出库货道号=D7100
出库数量=D7110
出库应答字=D7130
出库完成=D7120
备用字=D7140
出库任务号=

@ -1,65 +1,46 @@
#泡后库货道信息
#泡后库货道信息 #是否已满=D7221 #货道状态=D7231 仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
[FPJCK-001_PH_001]
在库数量=D7201
在途数量=D7211
是否已满=D7221
货道状态=D7231
仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
入库完成=D7021
出库完成=D7240
[FPJCK-001_PH_002]
在库数量=D7202
在途数量=D7212
是否已满=D7222
货道状态=D7232
仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
入库完成=D7022
出库完成=D7240
[FPJCK-001_PH_003]
在库数量=D7203
在途数量=D7213
是否已满=D7223
货道状态=D7233
仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
入库完成=D7023
出库完成=D7240
[FPJCK-001_PH_004]
在库数量=D7204
在途数量=D7214
是否已满=D7224
货道状态=D7234
仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
入库完成=D7024
出库完成=D7240
[FPJCK-001_PH_005]
在库数量=D7205
在途数量=D7215
是否已满=D7225
货道状态=D7235
仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
入库完成=D7025
出库完成=D7240
[FPJCK-001_PH_006]
在库数量=D7206
在途数量=D7216
是否已满=D7226
货道状态=D7236
仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
入库完成=D7026
出库完成=D7240
[FPJCK-001_PH_007]
在库数量=D7207
在途数量=D7217
是否已满=D7227
货道状态=D7237
仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
入库完成=D7027
出库完成=D7240
[FPJCK-001_PH_008]
在库数量=D7208
在途数量=D7218
是否已满=D7228
货道状态=D7238
仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
入库完成=D7028
出库完成=D7240
[FPJCK-001_PH_009]
在库数量=D7209
在途数量=D7219
是否已满=D7229
货道状态=D7239
仓库状态=D7300 #设备状态字,0未启动状态,1已自动运行,2维修调试状态,禁止启动
入库完成=D7029
出库完成=D7240

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\..\Page\InStoreInfo\InStoreInfoControl.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "923C14CB4FB6F417B25F28806A661E0FB09C6F5B05C089A67BFCA9872CC2BE38"
#pragma checksum "..\..\..\..\Page\InStoreInfo\InStoreInfoControl.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "7012E740612AEB51281394C4CADCE95451CE8202499FB91074E1297E31AD081D"
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\..\Page\InStoreInfo\InStoreInfoControl.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "923C14CB4FB6F417B25F28806A661E0FB09C6F5B05C089A67BFCA9872CC2BE38"
#pragma checksum "..\..\..\..\Page\InStoreInfo\InStoreInfoControl.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "7012E740612AEB51281394C4CADCE95451CE8202499FB91074E1297E31AD081D"
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\..\Page\InventoryInfo\BomFoamRearInventory.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "6DBBE6696A0F37C12B41EA00B251E2EAF9CF479CD952FC1F63C5660880A0FBC2"
#pragma checksum "..\..\..\..\Page\InventoryInfo\BomFoamRearInventory.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "43D95CFBDF80A87CAE8F4743F718368517E5C4F5064091D0BED61F628153B897"
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\..\Page\InventoryInfo\BomFoamRearInventory.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "6DBBE6696A0F37C12B41EA00B251E2EAF9CF479CD952FC1F63C5660880A0FBC2"
#pragma checksum "..\..\..\..\Page\InventoryInfo\BomFoamRearInventory.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "43D95CFBDF80A87CAE8F4743F718368517E5C4F5064091D0BED61F628153B897"
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。

@ -292,15 +292,23 @@ namespace Aucma.Scada.UI.viewModel.OutStoreInfo
private void DeleteTaskInfo(object obj)
{
string taskCode = obj as string;
if (outStoreBusiness.DeleteTaskInfoByTaskCode(taskCode))
MessageBoxResult result = MessageBox.Show("货物是否已出库?", "出库确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
if (outStoreBusiness.DeleteTaskInfoByTaskCode(taskCode, true))
{
MessageBox.Show("任务删除成功", "提示", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK, MessageBoxOptions.DefaultDesktopOnly);
Query();
}
}
if (outStoreBusiness.DeleteTaskInfoByTaskCode(taskCode, false))
{
MessageBox.Show("任务删除成功");
MessageBox.Show("任务删除成功", "提示", MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK, MessageBoxOptions.DefaultDesktopOnly);
Query();
}
else
{
MessageBox.Show("任务删除失败");
MessageBox.Show("任务删除失败", "提示", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK, MessageBoxOptions.DefaultDesktopOnly);
}
}

@ -68,20 +68,20 @@ namespace HighWayIot.Config
/// <summary>
/// 泡后——入库任务号
/// </summary>
public string in_foam_task
{
get { return iniHelper.IniReadValue("foam_inStore_address", "入库任务号"); }
set { iniHelper.IniWriteValue("foam_inStore_address", "入库任务号", value); }
}
//public string in_foam_task
//{
// get { return iniHelper.IniReadValue("foam_inStore_address", "入库任务号"); }
// set { iniHelper.IniWriteValue("foam_inStore_address", "入库任务号", value); }
//}
/// <summary>
/// 泡后——入库完成
/// </summary>
public string in_foam_finish
{
get { return iniHelper.IniReadValue("foam_inStore_address", "入库完成"); }
set { iniHelper.IniWriteValue("foam_inStore_address", "入库完成", value); }
}
//public string in_foam_finish
//{
// get { return iniHelper.IniReadValue("foam_inStore_address", "入库完成"); }
// set { iniHelper.IniWriteValue("foam_inStore_address", "入库完成", value); }
//}
#endregion

@ -32,13 +32,15 @@ namespace HighWayIot.Config
SpaceAddress spaceAddress = new SpaceAddress();
spaceAddress.onStore = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "在库数量");
spaceAddress.onRoute = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "在途数量");
spaceAddress.isFull = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "是否已满");
spaceAddress.spaceStatus = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "货道状态");
spaceAddress.storeStatus = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "仓库状态");
spaceAddress.alarmInfo = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "报警信息");
spaceAddress.inStoreFinish = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "入库完成");
spaceAddress.outStoreFinish = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "出库完成");
//spaceAddress.isFull = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "是否已满");
//spaceAddress.spaceStatus = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "货道状态");
//spaceAddress.storeStatus = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "仓库状态");
//spaceAddress.alarmInfo = iniHelper.IniReadValue($"{storeCode}_{spaceCode}", "报警信息");
return spaceAddress;
}
}
public class SpaceAddress
@ -47,15 +49,18 @@ namespace HighWayIot.Config
public string onRoute { get; set; }
public string isFull { get; set; }
public string inStoreFinish { get; set; }
public string spaceStatus { get; set; }
public string outStoreFinish { get; set; }
//public string isFull { get; set; }
public string storeStatus { get; set; }
//public string spaceStatus { get; set; }
//public string storeStatus { get; set; }
//public string alarmInfo { get; set; }
public string alarmInfo { get; set; }
public string outStoreFinish { get; set; }
}
}

@ -47,6 +47,13 @@ namespace HighWayIot.Repository.service
/// <returns></returns>
RealTaskInfo GetTaskInfoByStoreCode(string storeCode, int taskType);
/// <summary>
/// 通过仓库编号获取所有待执行的任务信息
/// </summary>
/// <param name="storeCode"></param>
/// <returns></returns>
List<RealTaskInfo> GetAllTaskInfoByStoreCode(string storeCode, int taskType);
/// <summary>
/// 通过任务号获取任务信息
/// </summary>
@ -70,6 +77,14 @@ namespace HighWayIot.Repository.service
/// <param name="taskStatus">0返回所有状态1-待执行2-执行中3-已完成</param>
/// <returns></returns>
List<RealTaskInfo> GetTaskInfosByTaskStatus(string[] storeCode, int taskType, int taskStatus);
/// <summary>
/// 获取指定状态的货道去重的按时间排序任务列表
/// </summary>
/// <param name="storeCode"></param>
/// <param name="taskType"></param>
/// <param name="taskStatus"></param>
/// <returns></returns>
List<RealTaskInfo> GetTaskInfosForInstore(string storeCode, int taskType, int taskStatus);
List<RealTaskInfo> GetTaskInfosByTaskCode(string taskCode);

@ -13,7 +13,7 @@ namespace HighWayIot.Repository.service.Impl
public PrintBarCode query(string code)
{
return _repository.GetFirst(x=>x.MaterialBarcode==code);
return _repository.GetFirst(x => x.MaterialBarcode.Equals(code));
}
}
}

@ -20,7 +20,16 @@ namespace HighWayIot.Repository.service.Impl
/// </summary>
public string GetPlanCode(string orderCode, string station)
{
return _mesRepository.GetFirst(x => x.orderCode == orderCode && x.productLineCode == station).planCode;
var aa = _mesRepository.GetList();
var plan = _mesRepository.GetFirst(x => x.orderCode == orderCode && x.productLineCode == station);
if(plan == null)
{
return "";
}
else
{
return plan.planCode;
}
}
/// <summary>

@ -139,6 +139,27 @@ namespace HighWayIot.Repository.service.Impl
return taskInfo;
}
/// <summary>
/// 通过仓库编号获取所有待执行的任务信息
/// </summary>
/// <param name="storeCode"></param>
/// <returns></returns>
public List<RealTaskInfo> GetAllTaskInfoByStoreCode(string storeCode, int taskType)
{
List<RealTaskInfo> taskInfo = null;
try
{
taskInfo = _mesRepository.GetList(x => x.storeCode == storeCode && x.taskStatus == 1 && x.taskType == taskType);
}
catch (Exception ex)
{
logHelper.Error("通过仓库编号获取待执行的任务信息异常", ex);
}
return taskInfo;
}
/// <summary>
/// 根据任务号获取任务信息
/// </summary>
@ -208,6 +229,41 @@ namespace HighWayIot.Repository.service.Impl
return taskInfos;
}
/// <summary>
/// 获取指定状态的货道去重的按时间排序任务列表
/// </summary>
/// <param name="storeCode"></param>
/// <param name="taskType"></param>
/// <param name="taskStatus"></param>
/// <returns></returns>
public List<RealTaskInfo> GetTaskInfosForInstore(string storeCode, int taskType, int taskStatus)
{
List<RealTaskInfo> realTaskInfos = null;
List<RealTaskInfo> filteredList = null;
try
{
Expression<Func<RealTaskInfo, bool>> exp = s1 => true;
exp = exp.And(x => x.taskType == taskType && storeCode.Contains(x.storeCode));
exp = exp.And(x => x.taskStatus == taskStatus);
realTaskInfos = _mesRepository.GetList(exp);
if (realTaskInfos == null || realTaskInfos.Count == 0) return null;
filteredList = realTaskInfos.GroupBy(rti => rti.spaceCode) // 按照 spaceCode 进行分组
.Select(group => group.OrderBy(rti => rti.createTime).First())
.ToList();
}
catch (Exception ex)
{
logHelper.Error("获取指定状态的任务信息异常", ex);
}
return filteredList;
}
public List<RealTaskInfo> GetTaskInfosByTaskCode(string taskCode)
{
List<RealTaskInfo> taskInfos = null;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save