using AngleSharp.Dom; using AutoMapper; using LogLibrary.Log; using Newtonsoft.Json; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Reflection; using System.Reflection.Metadata; using System.Text; using System.Threading.Tasks; using WIDESEA_Common; using WIDESEA_Core; using WIDESEA_Core.BaseRepository; using WIDESEA_Core.Enums; using WIDESEA_Core.Helper; using WIDESEA_DTO; using WIDESEA_DTO.AGV; using WIDESEA_IStorageBasicRepository; using WIDESEA_IStorageSocketServices; using WIDESEA_IStorageTaskRepository; using WIDESEA_IStorageTaskServices; using WIDESEA_Model.Models; using WIDESEA_Model.Models.AGV; using WIDESEA_StorageTaskRepository; using WIDESEAWCS_BasicInfoRepository; using WIDESEAWCS_Model.Models; using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; namespace WIDESEA_StorageSocketServices { public class WorkTaskService:IWorkTaskService { private readonly IDt_TaskRepository _taskRepository; private readonly IDt_Task_HtyRepository _task_HtyRepository; private readonly IDt_TaskService _taskService; private readonly IMapper _mapper; private readonly IStockInfoRepository _stockInfoRepository; private readonly IUnitOfWorkManage _unitOfWorkManage; private readonly ILocationInfoRepository _locationRepository; private readonly IDt_StationManagerRepository _stationManagerRepository; private readonly LogFactory LogFactory = new LogFactory(); private readonly ISocketClientServices _socket; string url = AppSettings.Configuration["AGVIP"]; public WorkTaskService(IDt_TaskRepository dt_TaskRepository, ISocketClientServices socket, IDt_StationManagerRepository stationManagerRepository,IDt_TaskService taskService, IDt_Task_HtyRepository dt_Task_HtyRepository,ILocationInfoRepository locationRepository, IMapper mapper,IUnitOfWorkManage unitOfWorkManage,IStockInfoRepository stockInfoRepository) { _taskRepository = dt_TaskRepository; _socket = socket; _stationManagerRepository = stationManagerRepository; _taskService = taskService; _task_HtyRepository = dt_Task_HtyRepository; _locationRepository = locationRepository; _mapper = mapper; _unitOfWorkManage = unitOfWorkManage; _stockInfoRepository = stockInfoRepository; } #region 常量 public const byte STX = 2; public const byte ETX = 3; /// /// 作业任务ID 预留 /// public const string JobOrderID = "0000000000000000"; /// /// 优先级 预留 /// public const string priority = "1"; /// /// 托盘个数 预留 /// public const string TrayCnt = "1"; /// /// 设备编号 /// public const string DeviceID = "0000000001"; /// /// 发送不需要回复 /// public const string SendNotReply = "1" + DeviceID + "0"; /// /// 发送需要回复 /// public const string SendandReply = "1" + DeviceID + "1"; /// /// 出库站台启用已分配任务 /// public const string OutBoundStationEnableDistribution = "1000"; /// /// 出库站台启用未分配任务 /// public const string OutBoundStationEnableUndistributed = "1100"; /// /// 出库站台禁用 /// public const string OutBoundStationDisabled = "0000"; /// /// 入库站台个数 /// public const string InBoundStationCount = "1"; /// /// 入库站台状态 /// public const string InBoundStationStatus = "1010001"; /// /// 预留 /// public const string Spare1 = "0000000000000000000000000000000000000000000000000000000000000000000000"; /// /// 出库站台预留 /// public const string OutStationSpare2 = "000000000000"; /// /// 预留 /// public const string Spare3 = "000"; /// /// 托盘号 /// public const string Spare4 = "0000000000"; #endregion #region WMS下发HOST方法 /// /// 设备请求入库 工序101 /// /// 托盘号 /// 入库站台 public void DeviceRequestInbound(HOSTAGVStatus Agvstatus, List outStations, InStationStatus inStation) { try { string str = SendandReply + "101" + _taskRepository.GetSeqNo().Result.ToString() + GetFieldsAsString(Agvstatus) + GetListStringOutStation(outStations) +"1" + GetFieldsAsString(inStation); _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 请求通讯 工序925 /// /// 托盘号 /// 入库站台 public void RequestCommunication() { try { string str = SendandReply + "925" + _taskRepository.GetSeqNo().Result.ToString(); _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 不允许下发任务 工序915 /// /// 命令 /// public void JobReady(string Command) { try { string str = SendNotReply + "915" + _taskRepository.GetSeqNo().Result.ToString() + Command; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 设备状态上报 工序913 /// /// 状态\、R运行 /// public void DeviceStateReport(string Status) { try { string str = SendNotReply + "913" + _taskRepository.GetSeqNo().Result.ToString() + Status; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 设备状态上报 工序917 /// /// 状态\、R运行 /// public void DeviceAutoStatusReport(string Status) { try { string str = SendNotReply + "917" + _taskRepository.GetSeqNo().Result.ToString() + Status; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// /// 作业开始或结束 工序905 /// /// 状态 L指AGV将托盘叉到货叉上、U指AGV将托盘放到库位上 /// 起点 /// 终点 /// 任务类型 I入库、O出库、S站台到站台、R移库 /// 托盘号 public void JobStartOrEnd(string Status, string FormLocation, string ToLocation, string TaskType, string PallteCode) { try { string str = SendandReply + "905" + _taskRepository.GetSeqNo().Result.ToString() + Status + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 托盘动作上报 工序907 /// /// 状态 L指AGV将托盘叉到货叉上、U指AGV将托盘放到库位上 /// 起点 /// 终点 /// 任务类型 I入库、O出库、S站台到站台、R移库 /// 托盘号 /// public void PalletActionReport(string Status, string FormLocation, string ToLocation, string TaskType, string PallteCode) { try { string str = SendNotReply + "907" + _taskRepository.GetSeqNo().Result.ToString() + Status + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 作业完成上报 工序901 /// /// 起点 /// 终点 /// 任务类型 I入库、O出库、S站台到站台、R移库 /// 托盘号 /// public void PalletActionReport(string FormLocation, string ToLocation, string TaskType, string PallteCode) { try { string str = SendandReply + "901" + _taskRepository.GetSeqNo().Result.ToString() + "0" + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 设备站台状态上报 工序936 /// /// 起点 /// 终点 /// 任务类型 I入库、O出库、S站台到站台、R移库 /// 托盘号 /// public void DeviceStationStatusReport(HOSTAGVStatus Agvstatus, List outStations, List inStation) { try { string str = SendNotReply + "936" + _taskRepository.GetSeqNo().Result.ToString() + GetFieldsAsString(Agvstatus) + GetListStringOutStation(outStations) + GetListStringInStation(inStation); _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// /// 重新获取货位信息 工序105 /// /// 起点 /// 终点 /// 任务类型 I入库、O出库、S站台到站台、R移库 /// 托盘号 public void RecreateGetLocation(string FormLocation, string ToLocation, string TaskType, string PallteCode) { try { string str = SendandReply + "105" + _taskRepository.GetSeqNo().Result.ToString() + "R" + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// /// 异常上报 工序985 /// /// 起点 /// 终点 /// 00表示AGV错误、01-08表示站台错误、99表示作业接收前数据错误 public void ErrorReport(string Trouble, string Level, string Location) { try { string str = SendNotReply + "985" + _taskRepository.GetSeqNo().Result.ToString() + Trouble + Level + Location; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 接收HOST工序103回馈响应 工序104 /// /// 0表示OK接收作业、1表示拒绝、9表示作业任务验证有误 /// public void DeviceReceiveJobResponse(string Statues) { try { string str = SendNotReply + "104" + _taskRepository.GetSeqNo().Result.ToString() + Statues; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// /// 空出库 工序107 /// /// 起点 /// 终点 /// 任务类型 I入库、O出库、S站台到站台、R移库 /// 托盘号 public void EmptyOutBound( string FormLocation, string ToLocation, string PallteCode) { try { string str = SendandReply + "107" + _taskRepository.GetSeqNo().Result.ToString() + "1" + JobOrderID + priority + FormLocation + ToLocation + "O" + TrayCnt + PallteCode; _socket.clientSend(MakeStringToByteMsg(str)); } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 回复HOST909工序 工序910 /// /// public void DeviceStatusReportResponse(HOSTAGVStatus AgvStatus,string X,string Y) { string str = SendNotReply + "910" + _taskRepository.GetSeqNo().Result.ToString() + "1" + GetFieldsAsString(AgvStatus)+X+Y; } #endregion #region WMS接收HOST回传方法 /// /// 接收HOST 响应请求 工序102 回复工序101 /// /// public void ReceiveCommandResponse(string message) { var parseMessage = GetParse(message); switch (parseMessage.body.ret) { //OK case "0": //UpdateTaskLocation(parseMessage); InsertWMSTask(parseMessage); Thread.Sleep(500); //915 JobReady("0"); break; //NG case "1": break; //无库位分配 case "2": break; //站台到站台,出库站台未准备好 case "3": break; //非常温工程 case "4": break; case "9": break; default: break; } } /// /// 接收HOST响应AGV作业开始或启动 工序906 回复工序905 /// public void AGVJobStartOrEndResponse(string status, string message) { switch (message) { //OK case "0": if (status == "E") { DeviceStateReport("I"); Thread.Sleep(3000); JobReady("1"); } break; //NG case "1": //调用AGV暂停接口 break; case "9": break; default: break; } } /// /// 数据报告响应 工序902 回复工序901 /// public void DataReportResponse(string message) { Dt_Task task = _taskRepository.QueryFirst(x => x.TaskState == (int)TaskOutStatusEnum.AGV_OutFinish || x.TaskState == (int)TaskRelocationStatusEnum.AGV_RelocationFinish || x.TaskState == (int)TaskInStatusEnum.AGV_InFinish); if (task != null) { switch (task.TaskType) { case (int)TaskInboundTypeEnum.Inbound: //入库 task.TaskState = (int)TaskInStatusEnum.AGV_InFinish; if (message == "0") { JobStartOrEnd("E", task.SourceAddress, task.TargetAddress, "O", task.PalletCode); } _taskService.CompleteInboundTask(task); break; case (int)TaskOutboundTypeEnum.Outbound: //出库 if (message == "0") { JobStartOrEnd("E", task.SourceAddress, task.TargetAddress, "O", task.PalletCode); } task.TaskState = (int)TaskOutStatusEnum.AGV_OutFinish; _taskService.CompleteOutboundTask(task); break; case (int)TaskRelocationTypeEnum.Relocation: if (message == "0") { JobStartOrEnd("E", task.SourceAddress, task.TargetAddress, "O", task.PalletCode); } task.TaskState = (int)TaskRelocationStatusEnum.AGV_RelocationFinish; _taskService.CompleteRelocationboundTask(task); break; case (int)TaskStationTypeEnum.StationToStation: if (message == "0") { JobStartOrEnd("E", task.SourceAddress, task.TargetAddress, "O", task.PalletCode); } task.TaskState = (int)TaskOutStatusEnum.AGV_OutFinish; _taskService.TaskMoveHty(task); break; default: break; } } } /// /// HOST下发出库任务 工序103 /// /// public void HOSTOutBoundTask(string message) { try { var parseMessage = GetParse(message); switch (parseMessage.body.ret) { //OK case "0": var task=InsertWMSTask(parseMessage); _taskRepository.AddData(task); InsertAGVTask(task); Thread.Sleep(500); //915 JobReady("0"); Thread.Sleep(500); //104 DeviceReceiveJobResponse("0"); break; default: break; } } catch (Exception) { DeviceReceiveJobResponse("1"); } } /// /// HOST设备状态获取 工序935 /// /// public void DeviceStationStatusInvite() { try { //回复936 List Instation = _stationManagerRepository.QueryData(x => x.stationType == 1).ToList(); List Outstation = _stationManagerRepository.QueryData(x => x.stationType == 2).ToList(); List outStationStatus = new List(); List inStationStatus = new List(); foreach (var item in Instation) { inStationStatus.Add(new InStationStatus() { StationName = item.stationName, StationEnable = item.stationStatus, IsDistributionTask = item.stationHasTask, PallteCode = "0000000000", }); } foreach (var item in Outstation) { StationStatus station = GetStationStatus(item.stationName); var taskStation = _taskRepository.QueryFirst(x => x.SourceAddress == item.stationName || x.TargetAddress == item.stationName); outStationStatus.Add(new OutStationStatus() { StationName = item.stationName, StationEnable = station.StationEnable == "1" ? "0" : "1", IsDistributionTask = taskStation == null ? "0" : "1", Spare1 = "00" }); //outStationStatus.Add(new OutStationStatus() //{ // StationName = item.stationName, // StationEnable = item.stationStatus, // IsDistributionTask = item.stationHasTask, // Spare1 = "00" //}); } AGVStatusRespone status = GetAGVStatus(); HOSTAGVStatus AgvStatus = new HOSTAGVStatus() { RuntimeStatus = CapitalizeFirstLetter(status.RuntimeStatus), AutoStatus = status.AutoStatus == "MaintenanceMode" ? "1" : "0", Ready = status.AutoStatus == "MaintenanceMode" ? "0" : "1", }; DeviceStationStatusReport(AgvStatus, outStationStatus, inStationStatus); } catch (Exception) { DeviceReceiveJobResponse("1"); } } /// /// 接收HOST重新分配货位 工序106 /// /// public void RecreateGetLocation(string message) { try { var parseMessage = GetParse(message); switch (parseMessage.body.ret) { //OK case "0": UpdateTaskLocation(parseMessage); break; //NG case "1": break; //无库位分配 case "2": break; case "9": break; default: break; } } catch (Exception) { DeviceReceiveJobResponse("1"); } } /// /// HOST空出库响应 工序108 /// /// public void EmptyOutBoundResponse(string message) { if (message != null && message == "0") { DeviceStateReport("I"); Thread.Sleep(500); JobReady("1"); } } /// /// HOST获取设备状态 工序909 /// public void DeviceStatusReportRequest() { HOSTAGVStatus AgvStatus = new HOSTAGVStatus() { RuntimeStatus = "R", AutoStatus = "1", Ready="1" }; string axis = "000000"; DeviceStatusReportResponse(AgvStatus,axis,axis); } #endregion #region 全局方法 /// /// 拼接报文 /// /// /// /// public byte[] MakeStringToByteMsg(string Message) { try { byte[] byt = Encoding.UTF8.GetBytes(Message); // 使用 UTF-8 避免编码问题 LogFactory.GetLog("请求托盘任务").Error(true, BitConverter.ToString(byt)); string checksum = GetCheckSum(byt); string str = Message + checksum; byte[] buffer = Encoding.UTF8.GetBytes(str); byte newFirstByte = 2; // 新的第一位数据 byte newLastByte = 3; // 新的最后一位数据 // 创建一个新数组,长度比原始数组多2 byte[] newArray = new byte[buffer.Length + 2]; // 将新的数据插入到新数组的第一位 newArray[0] = newFirstByte; // 将新的数据插入到新数组的最后一位 newArray[newArray.Length - 1] = newLastByte; // 复制原始数组的所有数据到新数组的中间部分 Array.Copy(buffer, 0, newArray, 1, buffer.Length); return newArray; } catch (Exception ex) { throw new Exception(ex.Message); } } //public bool IsOnline() //{ //} public string GetListStringOutStation(List outStationStatus) { return string.Join("", outStationStatus.Select(status =>$"{status.StationName}{status.StationEnable}{status.IsDistributionTask}{status.Spare1}")); } public string GetListStringInStation(List inStationStatus) { return string.Join("", inStationStatus.Select(status => $"{status.StationName}{status.StationEnable}{status.IsDistributionTask}{status.Spare1}")); } /// /// 将对象值凭拼接 /// /// /// public static string GetFieldsAsString(object obj) { if (obj == null) { return "Object is null"; } StringBuilder builder = new StringBuilder(); Type type = obj.GetType(); foreach (PropertyInfo property in type.GetProperties()) { if (property.CanRead) { object value = property.GetValue(obj); if (value != null) { builder.Append(value); } } } return builder.ToString(); } /// /// 获取CheckSum值 /// /// /// /// public string GetCheckSum(byte[] data) { int sum = 0; try { for (int i = 0; i < data.Length; i++) { sum += data[i]; } string str = sum.ToString(); return str.Substring(str.Length - 2); } catch (Exception ex) { throw new Exception(ex.Message); } } public ParseMessage SubString(string Y) { const string STX = "0x02"; const string ETX = "0x03"; // 检查是否以 STX 开头、ETX 结尾 if (Y.Substring(0, 4) == STX && Y.Substring(Y.Length - 4) == ETX) { string str = Y.Substring(4, Y.Length - 8); byte[] message = Encoding.UTF8.GetBytes(str); int newLength = message.Length - 2; if (newLength < 0) { throw new ArgumentException("数组长度不足,无法去掉最后两位。"); } byte[] newArray = new byte[newLength]; Array.Copy(message, 0, newArray, 0, newLength); string calculatedChecksum = GetCheckSum(newArray); string receivedChecksum = str.Substring(str.Length - 2); if (calculatedChecksum == receivedChecksum) { var x = Encoding.UTF8.GetString(newArray); ParseMessage parseMessage = new ParseMessage() { bDir = x.Substring(0, 1), bObjID = x.Substring(2, 10), bReply = x.Substring(11, 1), bCmdID = x.Substring(12, 3), nSeqNo = x.Substring(15, 5), //Body = x.Substring(21), }; return parseMessage; } else { Console.WriteLine("校验失败!"); return null; } } else { Console.WriteLine("无效报文格式!"); return null; } } #endregion #region 定义对象 #endregion #region 私有方法 private void UpdateTaskLocation(ParseMessage parseMessage) { var task = _taskRepository.QueryFirst(x => x.SeqNo == Convert.ToInt32(parseMessage.nSeqNo) && x.PalletCode.Contains(parseMessage.body.TrayIdList)); if (task != null) { task.TargetAddress = parseMessage.body.ToLocation; task_call agvtask = SqlSugarHelper.DbAGV.Queryable().Where(it => it.d_involed5 == task.TaskNum).First(); _taskRepository.Update(task); SqlSugarHelper.DbAGV.Updateable(agvtask).ExecuteCommand(); } } private Dt_Task InsertWMSTask(ParseMessage parseMessage) { //var task = _taskRepository.QueryFirst(x => x.PalletCode.Contains(parseMessage.body.TrayIdList)); int taskType = 0; int taskState = 0; if (parseMessage.body.JobType == "I") { taskType = (int)TaskInboundTypeEnum.Inbound; taskState = (int)TaskInStatusEnum.InNew; } else if (parseMessage.body.JobType == "O") { taskType = (int)TaskOutboundTypeEnum.Outbound; taskState = (int)TaskOutStatusEnum.OutNew; } else if (parseMessage.body.JobType == "S") { taskType = (int)TaskStationTypeEnum.StationToStation; taskState = (int)TaskOutStatusEnum.OutNew; } else if (parseMessage.body.JobType == "R") { taskType = (int)TaskRelocationTypeEnum.Relocation; taskState = (int)TaskRelocationStatusEnum.RelocationNew; } else { throw new Exception("未知库位"); } return new Dt_Task() { TaskNum = _taskRepository.GetTaskNo().Result, SourceAddress = parseMessage.body.FromLocation, TargetAddress = parseMessage.body.ToLocation, PalletCode = parseMessage.body.TrayIdList.Substring(0, 10), TaskType = taskType, TaskState = taskState, Dispatchertime = DateTime.Now, SeqNo = Convert.ToInt32(parseMessage.nSeqNo), CommandID = Convert.ToInt32(parseMessage.bCmdID) }; } /// /// 添加AGV任务 /// /// 任务对象 /// private int InsertAGVTask(Dt_Task task) { task_call task_Call = new task_call() { d_task_type = task.TaskType == (int)TaskTypeEnum.Inbound ? 1 : 2, d_floor = 1, d_involed1 = task.SourceAddress, d_involed2 = task.TargetAddress, d_involed5 = task.TaskNum, }; return SqlSugarHelper.DbAGV.Insertable(task_Call).ExecuteCommand(); } public ParseMessage GetParse(string x) { return new ParseMessage() { bDir = x.Substring(0, 1), bObjID = x.Substring(2, 10), bReply = x.Substring(11, 1), bCmdID = x.Substring(12, 3), nSeqNo = x.Substring(15, 5), body = new ReceiveBody { ret = x.Substring(21, 1), JobOrderID = x.Substring(22, 16), priority = x.Substring(39, 1), FromLocation = x.Substring(40, 6), ToLocation = x.Substring(47, 6), JobType = x.Substring(54, 1), TrayCnt = x.Substring(55, 1), TrayIdList = x.Substring(56) }, }; } public AGVStatusRespone GetAGVStatus() { string urlnew = url + "/ilns/AGV/getAgvState"; var result = HttpsClient.PostAsync(urlnew, JsonConvert.DeserializeObject>(new { getStatus = "1" }.ToJson())).Result; return JsonConvert.DeserializeObject(result.ToString()); } public StationStatus GetStationStatus(string stationName) { string urlnew = url + "/ilns/strl/getStat"; var result = HttpsClient.PostAsync(urlnew, JsonConvert.DeserializeObject>(new { strlName = stationName }.ToJson())).Result; return JsonConvert.DeserializeObject(result.ToString()); } //public string GetFirstUpper(string Status) //{ // switch (Status) // { // case "Run": // //运行 // return "R"; // case "Idle": // //关机 // return "I"; // case "Trouble": // //故障 // return "T"; // case "Pause": // //暂停 // return "S"; // case "Charge": // //充电 // return "C"; // case "PowerOn": // //开机 // return "P"; // case "PowerOFF": // //关机 // return "O"; // case "MaintenanceMode": // //关机 // return "1"; // case "ControlMode": // //关机 // return "0"; // default: break; // } //} public string CapitalizeFirstLetter(string s) { if (string.IsNullOrEmpty(s)) { return ""; } char firstChar = s[0]; return char.ToUpper(firstChar).ToString(); } #endregion } }