using LogLibrary.Log;
|
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Logging;
|
using MimeKit;
|
using Newtonsoft.Json;
|
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
|
using Org.BouncyCastle.Tls;
|
using System;
|
using System.Net;
|
using System.Net.Sockets;
|
using System.Reflection;
|
using System.Text;
|
using WIDESEA_Common;
|
using WIDESEA_Core;
|
using WIDESEA_Core.BaseServices;
|
using WIDESEA_Core.Enums;
|
using WIDESEA_Core.Helper;
|
using WIDESEA_Core.Middlewares;
|
using WIDESEA_DTO.AGV;
|
using WIDESEA_DTO;
|
using WIDESEA_IServices;
|
using WIDESEA_IStorageBasicRepository;
|
using WIDESEA_IStorageSocketServices;
|
using WIDESEA_IStorageTaskRepository;
|
using WIDESEA_IStorageTaskServices;
|
using WIDESEA_Model.Models.AGV;
|
using WIDESEA_Model.Models;
|
using WIDESEAWCS_BasicInfoRepository;
|
//using static MailKit.Telemetry;
|
using WIDESEAWCS_Model.Models;
|
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
|
using WIDESEA_Core.BaseRepository;
|
using AutoMapper;
|
using Mapster;
|
|
namespace WIDESEA_StorageSocketServices
|
{
|
public class SocketClientService : ISocketClientServices
|
{
|
System.Net.Sockets.Socket socket;
|
const byte STX = 2;
|
const byte ETX = 3;
|
private readonly ILogger<SocketClientService> _logger;
|
private readonly LogFactory LogFactory = new LogFactory();
|
//private readonly IDt_TaskService _taskService;
|
private readonly IDt_StationManagerRepository _stationManagerRepository;
|
private readonly IDt_TaskRepository BaseDal;
|
Connection connection = AppSettings.Configuration.GetSection("Connection").Get<Connection>();
|
string url = AppSettings.Configuration["AGVIP"];
|
private readonly IUnitOfWorkManage _unitOfWorkManage;
|
private readonly IStockInfoRepository _stockInfoRepository;
|
private readonly IDt_Task_HtyRepository _task_HtyRepository;
|
private readonly IMapper _mapper;
|
private readonly ILocationInfoRepository _locationRepository;
|
|
|
public SocketClientService(/*IDt_TaskService taskService*/IDt_TaskRepository TaskRepository, IDt_StationManagerRepository stationManagerRepository, ILogger<SocketClientService> logger)
|
{
|
//_taskService = taskService;
|
BaseDal= TaskRepository;
|
_stationManagerRepository= stationManagerRepository;
|
_logger = logger;
|
}
|
|
public void ConnectServer(string IP, int Port)
|
{
|
try
|
{
|
// 检查是否已连接,避免重复创建
|
if (socket != null && socket.Connected)
|
{
|
//Console.WriteLine("Socket已连接,无需重复创建");
|
ConsoleHelper.WriteErrorLine($"Socket已处于连接状态");
|
return;
|
}
|
//创建负责通信的socket
|
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
IPAddress ip = IPAddress.Parse(IP);
|
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(Port));
|
|
//获得要连接的远程服务器应用程序的IP地址和端口号
|
socket.Connect(point);
|
|
_logger.LogInformation("Socket连接成功");
|
ConsoleHelper.WriteSuccessLine("Socket连接成功");
|
|
RequestCommunication();
|
DeviceStateReport("I");
|
|
//开启一个新的线程不停的接收服务端发来的信息
|
Thread th = new Thread(Receive);
|
th.IsBackground = true;
|
th.Start();
|
ConsoleHelper.WriteErrorLine($"Socket启动");
|
return;
|
}
|
catch (Exception ex)
|
{
|
ConsoleHelper.WriteErrorLine($"Socket连接失败{ex.Message}");
|
HandleDisconnection();
|
}
|
}
|
void Receive()
|
{
|
while (true)
|
{
|
try
|
{
|
byte[] buffer = new byte[1024]; // 接收缓冲区
|
int bytesReceived = socket.Receive(buffer); // 接收数据
|
byte[] receivedData = new byte[bytesReceived];
|
|
Array.Copy(buffer, receivedData, bytesReceived); // 复制有效数据
|
var Y = Encoding.UTF8.GetString(receivedData);
|
|
// 检查是否以 STX 开头、ETX 结尾
|
if (receivedData[0] == STX && receivedData[receivedData.Length - 1] == ETX)
|
{
|
int newLength = receivedData.Length - 2;
|
int newCheckSumLength = receivedData.Length - 3;
|
|
if (newLength < 0)
|
{
|
throw new ArgumentException("数组长度不足,无法去掉最后两位。");
|
}
|
|
byte[] newArray = new byte[newLength];
|
|
Array.Copy(receivedData, 1, newArray, 0, newLength);
|
string calculatedChecksum = GetCheckSumone(newArray);
|
|
var str = Encoding.UTF8.GetString(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),
|
};
|
ProcessCommand(parseMessage.bCmdID, x);
|
}
|
else
|
{
|
LogFactory.GetLog("Socket接收数据").Error(true, "校验失败");
|
}
|
}
|
else
|
{
|
LogFactory.GetLog("Socket接收数据").Error(true, "无效报文格式");
|
}
|
}
|
catch
|
{
|
HandleDisconnection();
|
|
}
|
}
|
}
|
|
private void ProcessCommand(string cmdId, string x)
|
{
|
switch (cmdId)
|
{
|
case "102":
|
ReceiveCommandResponse(x);
|
break;
|
case "906":
|
AGVJobStartOrEndResponse(x.Substring(21, 1),x.Substring(22,1));
|
break;
|
case "902":
|
DataReportResponse(x.Substring(21, 1));
|
break;
|
case "935":
|
DeviceStationStatusInvite();
|
break;
|
case "103":
|
HOSTOutBoundTask(x);
|
break;
|
case "106":
|
RecreateGetLocation(x);
|
break;
|
case "108":
|
RecreateGetLocation(x.Substring(21, 1));
|
break;
|
case "909":
|
DeviceStatusReportRequest();
|
break;
|
default:
|
break;
|
}
|
}
|
|
private void HandleDisconnection()
|
{
|
int attempts = 5;
|
do
|
{
|
string message = "连接已断开..." + '\n';
|
message += "等待5秒后重新连接" + '\n';
|
Console.WriteLine(message);
|
|
// 关闭当前连接
|
try
|
{
|
socket?.Close();
|
}
|
catch { }
|
|
// 等待5秒
|
Thread.Sleep(5000);
|
|
// 尝试重新连接
|
ConnectServer(connection.IP, connection.Port);
|
attempts--;
|
} while (!socket.Connected && attempts > 0);
|
}
|
|
//private void CheckTimeout(object state)
|
//{
|
// TimeSpan elapsed = DateTime.Now - _lastActivityTime;
|
// if (elapsed.TotalSeconds > 180) // 3分钟超时
|
// {
|
// Console.WriteLine("超时未收到数据,触发909命令并尝试重新连接...");
|
|
// // 触发909命令
|
// _workTaskService.DeviceStatusReportRequest();
|
|
// // 尝试重新连接
|
// HandleDisconnection();
|
// }
|
//}
|
|
public void clientSend(byte[] buffer)
|
{
|
var Y = Encoding.UTF8.GetString(buffer);
|
LogFactory.GetLog("Socket发送数据").Error(true, Y);
|
socket.Send(buffer);
|
}
|
|
public string GetCheckSumone(byte[] x)
|
{
|
int sum = 0;
|
try
|
{
|
for (int i = 0; i < x.Length-2; i++)
|
{
|
sum += x[i];
|
}
|
string str = sum.ToString();
|
return str.Substring(str.Length - 2);
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
}
|
|
|
#region 常量
|
//public const byte STX = 2;
|
//public const byte ETX = 3;
|
|
/// <summary>
|
/// 作业任务ID 预留
|
/// </summary>
|
public const string JobOrderID = "0000000000000000";
|
|
/// <summary>
|
/// 优先级 预留
|
/// </summary>
|
public const string priority = "1";
|
|
/// <summary>
|
/// 托盘个数 预留
|
/// </summary>
|
public const string TrayCnt = "1";
|
|
/// <summary>
|
/// 设备编号
|
/// </summary>
|
public const string DeviceID = "0000000001";
|
/// <summary>
|
/// 发送不需要回复
|
/// </summary>
|
public const string SendNotReply = "1" + DeviceID + "0";
|
/// <summary>
|
/// 发送需要回复
|
/// </summary>
|
public const string SendandReply = "1" + DeviceID + "1";
|
/// <summary>
|
/// 出库站台启用已分配任务
|
/// </summary>
|
public const string OutBoundStationEnableDistribution = "1000";
|
/// <summary>
|
/// 出库站台启用未分配任务
|
/// </summary>
|
public const string OutBoundStationEnableUndistributed = "1100";
|
/// <summary>
|
/// 出库站台禁用
|
/// </summary>
|
public const string OutBoundStationDisabled = "0000";
|
/// <summary>
|
/// 入库站台个数
|
/// </summary>
|
public const string InBoundStationCount = "1";
|
|
/// <summary>
|
/// 入库站台状态
|
/// </summary>
|
public const string InBoundStationStatus = "1010001";
|
/// <summary>
|
/// 预留
|
/// </summary>
|
public const string Spare1 = "0000000000000000000000000000000000000000000000000000000000000000000000";
|
|
/// <summary>
|
/// 出库站台预留
|
/// </summary>
|
public const string OutStationSpare2 = "000000000000";
|
|
/// <summary>
|
/// 预留
|
/// </summary>
|
public const string Spare3 = "000";
|
|
/// <summary>
|
/// 托盘号
|
/// </summary>
|
public const string Spare4 = "0000000000";
|
|
#endregion
|
|
#region WMS下发HOST方法
|
|
/// <summary>
|
/// 设备请求入库 工序101
|
/// </summary>
|
/// <param name="PallteCode">托盘号</param>
|
/// <param name="InStation">入库站台</param>
|
public void DeviceRequestInbound(HOSTAGVStatus Agvstatus, List<OutStationStatus> outStations, InStationStatus inStation)
|
{
|
try
|
{
|
string str = SendandReply + "101" + BaseDal.GetSeqNo().Result.ToString() + GetFieldsAsString(Agvstatus) + GetListStringOutStation(outStations) + "1" + GetFieldsAsString(inStation);
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
}
|
|
/// <summary>
|
/// 请求通讯 工序925
|
/// </summary>
|
/// <param name="PallteCode">托盘号</param>
|
/// <param name="InStation">入库站台</param>
|
public void RequestCommunication()
|
{
|
try
|
{
|
string str = SendandReply + "925" + BaseDal.GetSeqNo().Result.ToString();
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
}
|
|
/// <summary>
|
/// 不允许下发任务 工序915
|
/// </summary>
|
/// <param name="Command">命令</param>
|
/// <returns></returns>
|
public void JobReady(string Command)
|
{
|
try
|
{
|
string str = SendNotReply + "915" + BaseDal.GetSeqNo().Result.ToString() + Command;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// <summary>
|
/// 设备状态上报 工序913
|
/// </summary>
|
/// <param name="state">状态\、R运行</param>
|
/// <returns></returns>
|
public void DeviceStateReport(string Status)
|
{
|
try
|
{
|
string str = SendNotReply + "913" + BaseDal.GetSeqNo().Result.ToString() + Status;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// <summary>
|
/// 设备状态上报 工序917
|
/// </summary>
|
/// <param name="state">状态\、R运行</param>
|
/// <returns></returns>
|
public void DeviceAutoStatusReport(string Status)
|
{
|
try
|
{
|
string str = SendNotReply + "917" + BaseDal.GetSeqNo().Result.ToString() + Status;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// /// <summary>
|
/// 作业开始或结束 工序905
|
/// </summary>
|
/// <param name="Status">状态 L指AGV将托盘叉到货叉上、U指AGV将托盘放到库位上</param>
|
/// <param name="FormLocation">起点</param>
|
/// <param name="ToLocation">终点</param>
|
/// <param name="TaskType">任务类型 I入库、O出库、S站台到站台、R移库</param>
|
/// <param name="PallteCode">托盘号</param>
|
public void JobStartOrEnd(string Status, string FormLocation, string ToLocation, string TaskType, string PallteCode)
|
{
|
try
|
{
|
string str = SendandReply + "905" + BaseDal.GetSeqNo().Result.ToString() + Status + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// <summary>
|
/// 托盘动作上报 工序907
|
/// </summary>
|
/// <param name="Status">状态 L指AGV将托盘叉到货叉上、U指AGV将托盘放到库位上</param>
|
/// <param name="FormLocation">起点</param>
|
/// <param name="ToLocation">终点</param>
|
/// <param name="TaskType">任务类型 I入库、O出库、S站台到站台、R移库</param>
|
/// <param name="PallteCode">托盘号</param>
|
/// <returns></returns>
|
public void PalletActionReport(string Status, string FormLocation, string ToLocation, string TaskType, string PallteCode)
|
{
|
try
|
{
|
string str = SendNotReply + "907" + BaseDal.GetSeqNo().Result.ToString() + Status + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// <summary>
|
/// 作业完成上报 工序901
|
/// </summary>
|
/// <param name="FormLocation">起点</param>
|
/// <param name="ToLocation">终点</param>
|
/// <param name="TaskType">任务类型 I入库、O出库、S站台到站台、R移库</param>
|
/// <param name="PallteCode">托盘号</param>
|
/// <returns></returns>
|
public void PalletActionReport(string FormLocation, string ToLocation, string TaskType, string PallteCode)
|
{
|
try
|
{
|
string str = SendandReply + "901" + BaseDal.GetSeqNo().Result.ToString() + "0" + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// <summary>
|
/// 设备站台状态上报 工序936
|
/// </summary>
|
/// <param name="FormLocation">起点</param>
|
/// <param name="ToLocation">终点</param>
|
/// <param name="TaskType">任务类型 I入库、O出库、S站台到站台、R移库</param>
|
/// <param name="PallteCode">托盘号</param>
|
/// <returns></returns>
|
public void DeviceStationStatusReport(HOSTAGVStatus Agvstatus, List<OutStationStatus> outStations, List<InStationStatus> inStation)
|
{
|
try
|
{
|
string str = SendNotReply + "936" + BaseDal.GetSeqNo().Result.ToString() + GetFieldsAsString(Agvstatus) + GetListStringOutStation(outStations) + GetListStringInStation(inStation);
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// /// <summary>
|
/// 重新获取货位信息 工序105
|
/// </summary>
|
/// <param name="FormLocation">起点</param>
|
/// <param name="ToLocation">终点</param>
|
/// <param name="TaskType">任务类型 I入库、O出库、S站台到站台、R移库</param>
|
/// <param name="PallteCode">托盘号</param>
|
public void RecreateGetLocation(string FormLocation, string ToLocation, string TaskType, string PallteCode)
|
{
|
try
|
{
|
string str = SendandReply + "105" + BaseDal.GetSeqNo().Result.ToString() + "R" + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// /// <summary>
|
/// 异常上报 工序985
|
/// </summary>
|
/// <param name="FormLocation">起点</param>
|
/// <param name="ToLocation">终点</param>
|
/// <param name="Location">00表示AGV错误、01-08表示站台错误、99表示作业接收前数据错误</param>
|
public void ErrorReport(string Trouble, string Level, string Location)
|
{
|
try
|
{
|
string str = SendNotReply + "985" + BaseDal.GetSeqNo().Result.ToString() + Trouble + Level + Location;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// <summary>
|
/// 接收HOST工序103回馈响应 工序104
|
/// </summary>
|
/// <param name="Statues">0表示OK接收作业、1表示拒绝、9表示作业任务验证有误</param>
|
/// <returns></returns>
|
public void DeviceReceiveJobResponse(string Statues)
|
{
|
try
|
{
|
string str = SendNotReply + "104" + BaseDal.GetSeqNo().Result.ToString() + Statues;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// /// <summary>
|
/// 空出库 工序107
|
/// </summary>
|
/// <param name="FormLocation">起点</param>
|
/// <param name="ToLocation">终点</param>
|
/// <param name="TaskType">任务类型 I入库、O出库、S站台到站台、R移库</param>
|
/// <param name="PallteCode">托盘号</param>
|
public void EmptyOutBound(string FormLocation, string ToLocation, string PallteCode)
|
{
|
try
|
{
|
string str = SendandReply + "107" + BaseDal.GetSeqNo().Result.ToString() + "1" + JobOrderID + priority + FormLocation + ToLocation + "O" + TrayCnt + PallteCode;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
|
}
|
|
/// <summary>
|
/// 回复HOST909工序 工序910
|
/// </summary>
|
/// <param name="AgvStatus"></param>
|
public void DeviceStatusReportResponse(HOSTAGVStatus AgvStatus, string X, string Y)
|
{
|
string str = SendNotReply + "910" + BaseDal.GetSeqNo().Result.ToString() + "1" + GetFieldsAsString(AgvStatus) + X + Y;
|
}
|
|
#endregion
|
|
#region WMS接收HOST回传方法
|
|
/// <summary>
|
/// 接收HOST 响应请求 工序102 回复工序101
|
/// </summary>
|
/// <param name="message"></param>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 接收HOST响应AGV作业开始或启动 工序906 回复工序905
|
/// </summary>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 数据报告响应 工序902 回复工序901
|
/// </summary>
|
public void DataReportResponse(string message)
|
{
|
Dt_Task task = BaseDal.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);
|
}
|
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;
|
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;
|
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;
|
TaskMoveHty(task);
|
break;
|
default:
|
break;
|
}
|
|
}
|
}
|
/// <summary>
|
/// HOST下发出库任务 工序103
|
/// </summary>
|
/// <param name="message"></param>
|
public void HOSTOutBoundTask(string message)
|
{
|
try
|
{
|
var parseMessage = GetParse(message);
|
switch (parseMessage.body.ret)
|
{
|
//OK
|
case "0":
|
var task = InsertWMSTask(parseMessage);
|
BaseDal.AddData(task);
|
InsertAGVTask(task);
|
Thread.Sleep(500);
|
//915
|
JobReady("0");
|
Thread.Sleep(500);
|
//104
|
DeviceReceiveJobResponse("0");
|
break;
|
default:
|
break;
|
}
|
}
|
catch (Exception)
|
{
|
DeviceReceiveJobResponse("1");
|
}
|
|
}
|
|
/// <summary>
|
/// HOST设备状态获取 工序935
|
/// </summary>
|
/// <param name="message"></param>
|
public void DeviceStationStatusInvite()
|
{
|
try
|
{
|
//回复936
|
List<Dt_StationManager> Instation = _stationManagerRepository.QueryData(x => x.stationType == 1).ToList();
|
List<Dt_StationManager> Outstation = _stationManagerRepository.QueryData(x => x.stationType == 2).ToList();
|
List<OutStationStatus> outStationStatus = new List<OutStationStatus>();
|
List<InStationStatus> inStationStatus = new List<InStationStatus>();
|
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 = BaseDal.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");
|
}
|
|
}
|
|
/// <summary>
|
/// 接收HOST重新分配货位 工序106
|
/// </summary>
|
/// <param name="message"></param>
|
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");
|
}
|
|
}
|
|
/// <summary>
|
/// HOST空出库响应 工序108
|
/// </summary>
|
/// <param name="message"></param>
|
public void EmptyOutBoundResponse(string message)
|
{
|
if (message != null && message == "0")
|
{
|
DeviceStateReport("I");
|
Thread.Sleep(500);
|
JobReady("1");
|
|
}
|
}
|
|
/// <summary>
|
/// HOST获取设备状态 工序909
|
/// </summary>
|
public void DeviceStatusReportRequest()
|
{
|
HOSTAGVStatus AgvStatus = new HOSTAGVStatus()
|
{
|
RuntimeStatus = "R",
|
AutoStatus = "1",
|
Ready = "1"
|
};
|
string axis = "000000";
|
DeviceStatusReportResponse(AgvStatus, axis, axis);
|
}
|
#endregion
|
|
#region 全局方法
|
/// <summary>
|
/// 拼接报文
|
/// </summary>
|
/// <param name="Message"></param>
|
/// <returns></returns>
|
/// <exception cref="Exception"></exception>
|
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> outStationStatus)
|
{
|
return string.Join("", outStationStatus.Select(status => $"{status.StationName}{status.StationEnable}{status.IsDistributionTask}{status.Spare1}"));
|
}
|
public string GetListStringInStation(List<InStationStatus> inStationStatus)
|
{
|
return string.Join("", inStationStatus.Select(status => $"{status.StationName}{status.StationEnable}{status.IsDistributionTask}{status.Spare1}"));
|
}
|
/// <summary>
|
/// 将对象值凭拼接
|
/// </summary>
|
/// <param name="obj"></param>
|
/// <returns></returns>
|
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();
|
}
|
/// <summary>
|
/// 获取CheckSum值
|
/// </summary>
|
/// <param name="data"></param>
|
/// <returns></returns>
|
/// <exception cref="Exception"></exception>
|
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 = BaseDal.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<task_call>().Where(it => it.d_involed5 == task.TaskNum).First();
|
BaseDal.Update(task);
|
SqlSugarHelper.DbAGV.Updateable(agvtask).ExecuteCommand();
|
}
|
|
}
|
|
private Dt_Task InsertWMSTask(ParseMessage parseMessage)
|
{
|
//var task = BaseDal.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 = BaseDal.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)
|
};
|
|
}
|
/// <summary>
|
/// 添加AGV任务
|
/// </summary>
|
/// <param name="task">任务对象</param>
|
/// <returns></returns>
|
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<Dictionary<string, object>>(new { getStatus = "1" }.ToJson())).Result;
|
|
return JsonConvert.DeserializeObject<AGVStatusRespone>(result.ToString());
|
}
|
|
public StationStatus GetStationStatus(string stationName)
|
{
|
string urlnew = url + "/ilns/strl/getStat";
|
var result = HttpsClient.PostAsync(urlnew, JsonConvert.DeserializeObject<Dictionary<string, object>>(new { strlName = stationName }.ToJson())).Result;
|
return JsonConvert.DeserializeObject<StationStatus>(result.ToString());
|
}
|
public string CapitalizeFirstLetter(string s)
|
{
|
if (string.IsNullOrEmpty(s))
|
{
|
return "";
|
}
|
|
char firstChar = s[0];
|
return char.ToUpper(firstChar).ToString();
|
}
|
|
#region 外部接口方法
|
|
public WebResponseContent CompleteInboundTask(Dt_Task task)
|
{
|
WebResponseContent content = new WebResponseContent();
|
try
|
{
|
_unitOfWorkManage.BeginTran();
|
var locationInf = _locationRepository.QueryFirst(x => x.LocationCode == task.TargetAddress);
|
locationInf.LocationStatus = (int)LocationEnum.InStock;
|
var stock = new DtStockInfo()
|
{
|
PalletCode = task.PalletCode,
|
LocationCode = task.TargetAddress,
|
CreateDate = DateTime.Now,
|
Creater = "system",
|
LocationId = locationInf.Id,
|
};
|
_stockInfoRepository.AddData(stock);
|
_locationRepository.UpdateData(locationInf);
|
TaskMoveHty(task);
|
_unitOfWorkManage.CommitTran();
|
return content.OK();
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
return content.Error(ex.Message);
|
}
|
}
|
|
public WebResponseContent CompleteOutboundTask(Dt_Task task)
|
{
|
WebResponseContent content = new WebResponseContent();
|
try
|
{
|
_unitOfWorkManage.BeginTran();
|
var locationInf = _locationRepository.QueryFirst(x => x.LocationCode == task.TargetAddress);
|
locationInf.LocationStatus = (int)LocationEnum.Free;
|
var stock = _stockInfoRepository.QueryFirst(x => x.PalletCode == task.PalletCode);
|
|
DtStockInfo_Hty stockInfo_Hty = stock.Adapt<DtStockInfo_Hty>();
|
stockInfo_Hty.ModifyDate = DateTime.Now;
|
|
AddStockInfoHty(stockInfo_Hty);
|
|
_stockInfoRepository.DeleteData(stock);
|
_locationRepository.UpdateData(locationInf);
|
|
TaskMoveHty(task);
|
|
_unitOfWorkManage.CommitTran();
|
return content.OK();
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
return content.Error(ex.Message);
|
}
|
}
|
|
public WebResponseContent CompleteRelocationboundTask(Dt_Task task)
|
{
|
WebResponseContent content = new WebResponseContent();
|
try
|
{
|
_unitOfWorkManage.BeginTran();
|
var locationInf = _locationRepository.QueryFirst(x => x.LocationCode == task.TargetAddress);
|
var location = _locationRepository.QueryFirst(x => x.LocationCode == task.SourceAddress);
|
locationInf.LocationStatus = (int)LocationEnum.InStock;
|
location.LocationStatus = (int)LocationEnum.Free;
|
|
var stock = _stockInfoRepository.QueryFirst(x => x.PalletCode == task.PalletCode);
|
|
stock.LocationCode = locationInf.LocationCode;
|
stock.LocationId = locationInf.Id;
|
|
|
_stockInfoRepository.UpdateData(stock);
|
_locationRepository.UpdateData(locationInf);
|
_locationRepository.UpdateData(location);
|
TaskMoveHty(task);
|
_unitOfWorkManage.CommitTran();
|
return content.OK();
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
return content.Error(ex.Message);
|
}
|
}
|
public WebResponseContent TaskMoveHty(Dt_Task task)
|
{
|
WebResponseContent content = new WebResponseContent();
|
try
|
{
|
_unitOfWorkManage.BeginTran();
|
var taskHtyNG = CreateHistoricalTask(task);
|
var isTaskHtyAdd = _task_HtyRepository.AddData(taskHtyNG) > 0;
|
var isTaskDelete = Delete(task.TaskId);
|
var AgvTask = SqlSugarHelper.DbAGV.Queryable<task_call>().Where(x => x.d_involed5 == task.TaskNum).First();
|
if (AgvTask != null)
|
{
|
SqlSugarHelper.DbAGV.Deleteable(AgvTask).ExecuteCommand();
|
}
|
_unitOfWorkManage.CommitTran();
|
return content.OK();
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
return content.Error(ex.Message);
|
}
|
}
|
|
#endregion 外部接口方法
|
|
#region private 内部方法
|
|
/// <summary>
|
/// 创建历史任务记录
|
/// </summary>
|
/// <param name="task"></param>
|
/// <returns></returns>
|
public Dt_Task_Hty CreateHistoricalTask(Dt_Task task, bool isHand = false)
|
{
|
task.CurrentAddress = task.NextAddress;
|
|
// 创建历史任务
|
var taskHty = _mapper.Map<Dt_Task_Hty>(task);
|
taskHty.FinishTime = DateTime.Now;
|
taskHty.TaskId = 0;
|
taskHty.OperateType = isHand ? (int)OperateTypeEnum.人工删除 : App.User.UserName != null ? (int)OperateTypeEnum.人工完成 : (int)OperateTypeEnum.自动完成;
|
taskHty.SourceId = task.TaskId;
|
if (isHand)
|
{
|
taskHty.Creater = App.User.UserName != null ? App.User.UserName : "System";
|
}
|
return taskHty;
|
}
|
/// <summary>
|
/// 删除一个任务
|
/// </summary>
|
/// <param name="id">任务ID</param>
|
/// <returns>是否删除成功</returns>
|
public bool Delete(int id)
|
{
|
return BaseDal.Delete(id);
|
}
|
private void AddStockInfoHty(DtStockInfo_Hty dtStock)
|
{
|
var isStockAdd = SqlSugarHelper.DbWMS.InsertNav(dtStock).IncludesAllFirstLayer().ExecuteCommand();
|
if (!isStockAdd)
|
{
|
throw new Exception("库存历史信息添加失败");
|
}
|
}
|
|
#endregion private 内部方法
|
#endregion
|
}
|
}
|