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.

576 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 Admin.Core.Common;
using Admin.Core.IService;
using Admin.Core.Model;
using Aucma.Core.HwPLc;
using Aucma.Core.Scanner;
using log4net;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Profiling.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Aucma.Core.Palletiz.Business
{
/// <summary>
///分垛入库业务处理
/// </summary>
public class InstoreBusiness
{
#region 单例实现
private static readonly InstoreBusiness lazy = new InstoreBusiness();
public static InstoreBusiness Instance
{
get
{
return lazy;
}
}
#endregion
#region 初始化对象
private static readonly log4net.ILog logHelper = LogManager.GetLogger(typeof(InstoreBusiness));
private readonly IBaseSpaceInfoServices? _baseSpaceInfoServices;
private readonly ICodeBindingRecordServices? _codeBindingServices;
private readonly IRecordInStoreServices? _recordInstoreServices;
#endregion
#region 构造函数
public InstoreBusiness()
{
_baseSpaceInfoServices = App.ServiceProvider.GetService<IBaseSpaceInfoServices>();
_codeBindingServices = App.ServiceProvider.GetService<ICodeBindingRecordServices>();
_recordInstoreServices = App.ServiceProvider.GetService<IRecordInStoreServices>();
MvCodeHelper.HandlePalletizDelegateEvent += ScannerInStore;//注册扫码器扫码后业务处理事件
}
#endregion
#region 变量定义
public readonly List<ScannerModel> allScanners = Appsettings.app<ScannerModel>("ScannerServer").ToList();
public readonly string storeCodeA = Appsettings.app("StoreInfo", "PalletizStoreCodeA");//分垛库A
public readonly string storeCodeB = Appsettings.app("StoreInfo", "PalletizStoreCodeB");//分垛库B
private static bool flagA = true;
private static bool flagB = true;
#endregion
#region 事件
#region 更新提醒信息
/// <summary>
/// 日志事件
/// </summary>
public delegate void LogInStoreInfoDelegate(string message, string color);
public static event LogInStoreInfoDelegate LogInStoreInfoDelegateEvent;
#endregion
#region 更新扫码信息
/// <summary>
/// 刷新扫码信息——图表和表格
/// </summary>
public delegate void ResherStoreInfoDelegate();
public static event ResherStoreInfoDelegate ResherStoreInfoDelegateEvent;
#endregion
#endregion
#region Test
[Obsolete("正式环境下使用ScannerInStore(string SNCode,string IP)")]
public void test()
{
// B240101 8302501416 0001 SN:16160030000000910999
Task.Run(async () =>
{
Thread.Sleep(3000);
await InStore("16160030000000910999", "192.168.1.19");
});
}
#endregion
#region 扫码入库
/// <summary>
/// 扫码入库
/// </summary>
/// <param name="SNCode">成品码</param>
/// <param name="IP">扫码器IP</param>
public void ScannerInStore(string SNCode,string IP)
{
Task.Run(async () =>
{
Thread.Sleep(3000);
await InStore(SNCode, IP);
});
}
#endregion
#region 扫码入库处理
/// <summary>
/// 扫码入库处理
/// </summary>
/// <param name="SNCode">成品码</param>
/// <param name="scannerIp">扫码器ip</param>
/// <returns></returns>
public async Task InStore(string SNCode, string scannerIp)
{
Console.WriteLine($"上位机接收到扫码器传入的成品码:{SNCode};扫码器IP:{scannerIp}");
LogInStoreInfoDelegateEvent?.Invoke($"上位机接收到扫码器传入的成品码:{SNCode};扫码器IP:{scannerIp}","White");
bool plcResult = false;// plc下发结果
List<int> spaceNumList = new List<int>();// 下发plc的货道号
RecordInStore recordInstore = new RecordInStore();// 入库记录
try
{
// 刷新页面
List<ScannerModel> allScanners = Appsettings.app<ScannerModel>("ScannerServer").ToList();
ScannerModel model = allScanners.First(x => x.Ip == scannerIp);
Console.WriteLine($"上位机获取配置的扫码器【IP:{model.ToJson()}》名称:{model.Name}】");
LogInStoreInfoDelegateEvent?.Invoke($"上位机获取配置的扫码器【IP:{model.ToJson()}》名称:{model.Name}】","White");
//1.根据成品码找货道
List<BaseSpaceInfo>? spaceList = GetSpaceBySNCode(SNCode, recordInstore);
Console.WriteLine($"上位机获取配置的货道数量【{spaceList.Count}】");
// 根据货道信息判断下发plc信号
if (spaceList == null || spaceList.Count == 0)
{
logHelper.Error("未找到匹配货道,请手动入库!");
// 刷新页面提示信息
Console.WriteLine("未找到匹配货道,请手动入库!");
LogInStoreInfoDelegateEvent?.Invoke("未找到匹配货道,请手动入库!", "White");
return;
}
// 过滤货道,找到最终需要下发的货道
BaseSpaceInfo finalSpace = FilterSpace(spaceList);
Console.WriteLine($"上位机获取需要下发的货道【货道名称:{finalSpace.SpaceName}》货道库存:{finalSpace.SpaceStock}》货道状态:{finalSpace.SpaceStatus}》货道类型:{finalSpace.SpaceType}》所属仓库:{finalSpace.StoreCode}】");
LogInStoreInfoDelegateEvent?.Invoke($"是否为大产品:【{finalSpace.IsTwoSpace}】", "White");
Console.WriteLine($"是否为大产品:【{finalSpace.IsTwoSpace}】");
LogInStoreInfoDelegateEvent?.Invoke($"下发的货道:【货道名称:{finalSpace.SpaceName}", "White");
// 大产品占两道
if (finalSpace.IsTwoSpace == 1)
{
spaceNumList.Add(int.Parse(finalSpace.SpaceCode.Substring(5, 3)));
spaceNumList.Add(int.Parse(GetOtherSpace(finalSpace, spaceList).SpaceCode.Substring(5, 3)));
plcResult = SendAndAnswerPlc(scannerIp, spaceList[0].RotationRange, spaceNumList);
recordInstore.SpaceCode = finalSpace.SpaceCode;
recordInstore.StoreCode = finalSpace.StoreCode;
// 更新货道信息,大产品last存objId大的储存上一个货道的主键 如货道7,8存8
BaseSpaceInfo otherSpace = GetOtherSpace(finalSpace, spaceList);
Console.WriteLine($"大产品入库信息:【{finalSpace.ToJson()}】【{spaceList.ToJson()}】");
if (otherSpace != null)
{
UpdateSapceList(otherSpace.ObjId, spaceList);
Console.WriteLine("更新货道信息成功!");
LogInStoreInfoDelegateEvent?.Invoke("更新货道信息成功!", "White");
}
}
else
{
// last不等于自己可以先入自己否则入另一条货道
if (IsOddNumber(finalSpace))
{
spaceNumList.Add(int.Parse(finalSpace.SpaceCode.Substring(5, 3)));
spaceNumList.Add(0);
plcResult = SendAndAnswerPlc(scannerIp, finalSpace.RotationRange, spaceNumList);
UpdateSapceList(finalSpace.ObjId, spaceList);
Console.WriteLine("更新货道信息成功!");
LogInStoreInfoDelegateEvent?.Invoke("更新货道信息成功!", "White");
}
else
{
spaceNumList.Add(0);
spaceNumList.Add(int.Parse(finalSpace.SpaceCode.Substring(5, 3)));
plcResult = SendAndAnswerPlc(scannerIp, finalSpace.RotationRange, spaceNumList);//给PLC下传入库信号
UpdateSapceList(finalSpace.ObjId, spaceList);
Console.WriteLine("更新货道信息成功!");
LogInStoreInfoDelegateEvent?.Invoke("更新货道信息成功!", "White");
}
}
if (plcResult == true)
{
// 更新入库记录,刷新界面
#region 添加入库记录
recordInstore.SpaceCode = finalSpace.SpaceCode;
recordInstore.StoreCode = finalSpace.StoreCode;
recordInstore.InStoreAmount = 1;
recordInstore.InStoreTime = DateTime.Now;
recordInstore.CreatedTime = DateTime.Now;
recordInstore.UpdateTime = DateTime.Now;
await _recordInstoreServices.AddAsync(recordInstore);
#endregion
}
else
{
// 界面提示手动入库
LogInStoreInfoDelegateEvent?.Invoke("界面提示手动入库!", "Red");
}
}
catch (Exception ex)
{
logHelper.Error(ex.Message.ToString());
return;
}
}
#region 根据成品码找货道
/// <summary>
/// 根据成品码找货道
/// </summary>
/// <param name="SNCode"></param>
/// <returns></returns>
private List<BaseSpaceInfo>? GetSpaceBySNCode(string SNCode, RecordInStore recordInstore)
{
try
{
CodeBindingRecord bindingRecord = _codeBindingServices.FirstAsync(c => c.ProductCode.Equals(SNCode)).Result;
if (bindingRecord == null) return null;
recordInstore.BarCodeCode = bindingRecord.BoxCode;
recordInstore.MaterialCode = bindingRecord.BoxCode;
recordInstore.MaterialType = bindingRecord.BoxCode.Substring(7, 10);
recordInstore.MaterialName = bindingRecord.BoxName;
string mType = bindingRecord.BoxCode.Substring(7, 10);
List<BaseSpaceInfo> bsInfo = _baseSpaceInfoServices.Query(s => (s.MaterialType.Equals(mType) && (s.StoreCode.Equals(storeCodeA)) || s.StoreCode.Equals(storeCodeB)));
if (bsInfo.Count > 0)
return bsInfo.OrderBy(x => x.ObjId).ToList();
else
return null;
}
catch (Exception ex)
{
logHelper.Error(ex.Message.ToString());
Console.WriteLine(ex.Message.ToString());
return null;
}
}
#endregion
private BaseSpaceInfo FilterSpace(List<BaseSpaceInfo> spaceInfos)
{
BaseSpaceInfo spaceInfo = spaceInfos.First();
if (spaceInfos.Count == 1 || string.IsNullOrEmpty(spaceInfo.LastSpace))
{
return spaceInfo;
}
List<BaseSpaceInfo> spaceList = spaceInfos.Where(s => s.ObjId > int.Parse(spaceInfo.LastSpace)).ToList();
if (spaceList == null || spaceList.Count == 0)
{
return spaceInfo;
}
else
{
return spaceInfo;
}
}
/// <summary>
/// 大产品占据两条货道,根据一条货道找到另一条货道
/// </summary>
/// <param name="spaceInfo"></param>
private BaseSpaceInfo GetOtherSpace(BaseSpaceInfo spaceInfo, List<BaseSpaceInfo> spaceList)
{
try
{
// 找到当前货道匹配的另一条货道
int num = int.Parse(spaceInfo.SpaceCode.Substring(5, 3));
string otherSpaceCode = string.Empty;
if (num % 2 == 0) // 偶数如7,8当前8找7
{
otherSpaceCode = spaceInfo.SpaceCode.Substring(0, 5) + (num - 1).ToString("D3");
}
else
{
otherSpaceCode = spaceInfo.SpaceCode.Substring(0, 5) + (num + 1).ToString("D3");
}
spaceList = spaceList.Where(s => s.SpaceCode.Equals(otherSpaceCode)).ToList();
if (spaceList.Count > 0)
{
return spaceList[0];
}
else
{
return null;
}
}
catch (Exception ex)
{
logHelper.Error(ex.Message.ToString());
Console.WriteLine($"大产品根据一条货道找到另一条货道异常:{ex.Message}");
return null;
}
}
/// <summary>
/// 判断货道编号是否为奇数
/// </summary>
/// <param name="space"></param>
/// <returns></returns>
private bool IsOddNumber(BaseSpaceInfo space)
{
int num = int.Parse(space.SpaceCode.Substring(5, 3));
if (num % 2 == 0)
{
return false;
}
else
{
return true;
}
}
/// <summary>
/// 将该物料的所有货道列表的last修改为上次入库货道objId
/// </summary>
/// <param name="objId"></param>
/// <param name="spaceList"></param>
/// <returns></returns>
private bool UpdateSapceList(int objId, List<BaseSpaceInfo> spaceList)
{
foreach (BaseSpaceInfo space in spaceList)
{
space.LastSpace = objId.ToString();
}
return _baseSpaceInfoServices.UpdateAsync(spaceList).Result;
}
#endregion
#region plc信号下发
#region 下发plc入库信号
/// <summary>
/// 下发plc入库信号
/// </summary>
/// <param name="scannerIp">扫码器ip</param>
/// <param name="range">转向角度</param>
/// <param name="spaceNum">货道号int集合</param>
/// <returns></returns>
private bool SendAndAnswerPlc(string scannerIp, int range, List<int> spaceNum)
{
bool result = false;
try
{
PlcModel obj = GetPlcByScanner(scannerIp);
if (obj != null)
{
if (SendPlc(obj, range, spaceNum))
{
result = WaitAnswerPlc(obj);
}
}
else
{
logHelper.Error("plc未连接");
return false;
}
return result;
}
catch (Exception ex)
{
logHelper.Error(ex.Message.ToString());
return result;
}
}
#endregion
#region 下发plc信号
/// <summary>
/// 下发plc信号
/// </summary>
/// <param name="obj"></param>
/// <param name="range"></param>
/// <param name="spaceNum"></param>
/// <returns></returns>
private bool SendPlc(PlcModel obj, int range, List<int> spaceNum)
{
try
{
bool result = false;
DateTime targetTime = DateTime.Now.AddSeconds(8);
while (true)
{
if (DateTime.Now > targetTime) // plc超最大时限无反馈
{
logHelper.Error("等待plc放行反馈信号超时");
Console.WriteLine("等待plc放行反馈信号超时");
return false;
}
// 应答字允许下发
Console.WriteLine($"读取应答字是否下传成功!【{obj.plc.ReadInt32("D102")}】");
if (obj.plc.ReadInt32("D102") == 1)
{
//obj.plc.WriteInt32("D110", range);//旋转角度暂时不赋值由PLC转向
obj.plc.WriteInt32("D112", spaceNum[0]); //货道号
obj.plc.WriteInt32("D114", spaceNum[1]); //货道号
Console.WriteLine($"plc信号货道信号下发成功!【D112:{spaceNum[0]}】D114:{spaceNum[1]}");
result = true;
break;
}
Thread.Sleep(500);
}
return result;
}
catch (Exception ex)
{
logHelper.Error(ex.Message);
return false;
}
}
#endregion
#region 等待plc信号反馈
/// <summary>
/// 等待plc信号反馈
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
private bool WaitAnswerPlc(PlcModel obj)
{
try
{
bool result = false;
DateTime targetTime = DateTime.Now.AddSeconds(8);
while (true)
{
if (DateTime.Now > targetTime) // plc超最大时限无反馈
{
logHelper.Error("等待plc放行反馈信号超时");
Console.WriteLine("等待plc放行反馈信号超时");
return false;
}
// 应答字允许下发
if (obj.plc.ReadInt32("D102") == 2)
{
result = true;
Console.WriteLine("下发成功!");
break;
}
Thread.Sleep(500);
}
return result;
}
catch (Exception ex)
{
logHelper.Error(ex.Message.ToString());
return false;
}
}
#endregion
#region 根据扫码器ip确定是属于哪个plc
/// <summary>
/// 根据扫码器ip确定是属于哪个plc
/// </summary>
/// <param name="scannerIp"></param>
/// <returns></returns>
private PlcModel GetPlcByScanner(string scannerIp)
{
PlcModel obj = null;
ScannerModel model = allScanners.FirstOrDefault(x => x.Ip == scannerIp);
if (model.Id < 3)
{
obj = PlcHelper.melsecList.FirstOrDefault(d => d.EquipName.Equals("InStoreAPlc"));
}
else
{
obj = PlcHelper.melsecList.FirstOrDefault(d => d.EquipName.Equals("InStoreBPlc"));
}
return obj;
}
#endregion
#endregion
#region 手动写入货道号给PLC数据
/// <summary>
/// 手动写入PLC数据
/// </summary>
/// <returns></returns>
public static bool WritePlc(Models.StackInfoModel tempStackInfo)
{
var obj = PlcHelper.melsecList.FirstOrDefault(d => d.EquipName.Equals("InStorePlc"));
//写入PLC
if (obj.plc.IsConnected)
{
//1、判断是否为大产品、占用两个货道 上位机两条道设置为相同型号
if (tempStackInfo.IsLargeProducts)
{
obj.plc.WriteInt16("D7000", tempStackInfo.BinNo.ToString());//写入货道
obj.plc.WriteInt16("D7100", tempStackInfo.BinNo.ToString());//写入货道
}
else
{
//判断入库货道
if (tempStackInfo.BinNo / 2 == 0)
{
obj.plc.WriteInt16("D7000", tempStackInfo.BinNo.ToString());//写入货道
obj.plc.WriteInt16("D7100", "0");//写入货道
}
else
{
obj.plc.WriteInt16("D7000", "0");//写入货道
obj.plc.WriteInt16("D7100", tempStackInfo.BinNo.ToString());//写入货道
}
}
obj.plc.WriteInt16("D7020", "1");//同时写入应答字
Console.WriteLine("手动写入PLC成功");
do
{
#region 1号区域码垛——A
int responseWord1 = obj.plc.ReadInt16("D7020");//读取1号区域码垛 等待应答字回去
Console.WriteLine($"获取应答信号:{responseWord1}");
if (responseWord1 == 2)
{
//清空货道
obj.plc.WriteInt16("D7000", "0");
obj.plc.WriteInt16("D7100", "0");
flagA = false;
}
#endregion
} while (flagA);
}
//更新货道
return false;
}
private static void GetResposeB()
{
var obj = PlcHelper.melsecList.FirstOrDefault(d => d.EquipName.Equals("InStorePlc"));
//写入PLC
if (obj.plc.IsConnected)
{
do
{
#region 2号区域码垛——B
int responseWord2 = obj.plc.ReadInt16("D7120");//读取1号区域码垛 等待应答字回去
Console.WriteLine("手动写入PLC成功");
if (responseWord2 == 2)
{
obj.plc.WriteInt16("D7000", "0");//清空
}
#endregion
} while (flagA);
}
}
#endregion
}
}