using LogLibrary.Log;
|
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Logging;
|
using Newtonsoft.Json;
|
using System.Net;
|
using System.Net.Sockets;
|
using System.Reflection;
|
using System.Text;
|
using WIDESEA_Common;
|
using WIDESEA_Core;
|
using WIDESEA_Core.Enums;
|
using WIDESEA_Core.Helper;
|
using WIDESEA_DTO.AGV;
|
using WIDESEA_DTO;
|
using WIDESEA_IStorageBasicRepository;
|
using WIDESEA_IStorageSocketServices;
|
using WIDESEA_IStorageTaskRepository;
|
using WIDESEA_Model.Models.AGV;
|
using WIDESEA_Model.Models;
|
using WIDESEAWCS_BasicInfoRepository;
|
using WIDESEAWCS_Model.Models;
|
using WIDESEA_Core.BaseRepository;
|
using AutoMapper;
|
using Mapster;
|
using AngleSharp.Dom;
|
using Masuit.Tools.Security;
|
using WIDESEA_IStorageTaskServices;
|
using System.Threading.Tasks;
|
using Microsoft.Extensions.DependencyInjection;
|
using WIDESEA_IBusinessesRepository;
|
using WIDESEA_Repository;
|
|
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;
|
private readonly IDt_TaskService _taskService;
|
private readonly IDt_HostLogRepository _hostLogRepository;
|
private bool _IsOnline=false;
|
|
|
public SocketClientService(IDt_TaskService TaskService, IDt_TaskRepository TaskRepository, IDt_StationManagerRepository stationManagerRepository, ILogger<SocketClientService> logger, ILocationInfoRepository locationInfoRepository,IDt_HostLogRepository hostLogRepository)
|
{
|
BaseDal = TaskRepository;
|
_stationManagerRepository = stationManagerRepository;
|
_logger = logger;
|
_taskService = TaskService;
|
_locationRepository = locationInfoRepository;
|
_hostLogRepository = hostLogRepository;
|
#region
|
//if (App.RootServices != null)
|
//{
|
// IDt_TaskRepository? BaseDal = App.RootServices.CreateScope().ServiceProvider.GetService(typeof(IDt_TaskRepository)) as IDt_TaskRepository;
|
// IDt_TaskService? _taskService = App.RootServices.CreateScope().ServiceProvider.GetService(typeof(IDt_TaskService)) as IDt_TaskService;
|
// ILocationInfoRepository? _locationRepository = App.RootServices.CreateScope().ServiceProvider.GetService(typeof(ILocationInfoRepository)) as ILocationInfoRepository;
|
// IDt_StationManagerRepository? _stationManagerRepository = App.RootServices.CreateScope().ServiceProvider.GetService(typeof(IDt_StationManagerRepository)) as IDt_StationManagerRepository;
|
//}
|
#endregion
|
}
|
|
public void ConnectServer(string IP, int Port)
|
{
|
try
|
{
|
// 检查是否已连接,避免重复创建
|
if (socket != null && socket.Connected)
|
{
|
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连接成功");
|
_IsOnline = true;
|
RequestCommunication();
|
DeviceStateReport("I");
|
|
//开启一个新的线程不停的接收服务端发来的信息
|
Thread th = new Thread(Receive);
|
th.IsBackground = true;
|
th.Start();
|
return;
|
}
|
catch (Exception ex)
|
{
|
_IsOnline = false;
|
ConsoleHelper.WriteErrorLine($"Socket连接失败{ex.Message}");
|
HandleDisconnection();
|
}
|
}
|
public bool Socketonline()
|
{
|
return _IsOnline;
|
}
|
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, $"无效报文格式:{JsonConvert.SerializeObject(Y)}");
|
}
|
}
|
catch
|
{
|
_IsOnline = false;
|
HandleDisconnection();
|
|
}
|
}
|
}
|
|
private void ProcessCommand(string cmdId, string x)
|
{
|
switch (cmdId)
|
{
|
case "102":
|
ReceiveCommandResponse(x);
|
break;
|
case "906":
|
AGVJobStartOrEndResponse(x.Substring(20, 1), x.Substring(21, 1));
|
break;
|
case "902":
|
DataReportResponse(x.Substring(20, 1));
|
break;
|
case "935":
|
DeviceStationStatusInvite(x.Substring(15, 5));
|
break;
|
case "103":
|
HOSTOutBoundTask(x);
|
break;
|
case "106":
|
RecreateGetLocation(x);
|
break;
|
case "108":
|
EmptyOutBoundResponse(x.Substring(20, 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);
|
}
|
public void clientSend(byte[] buffer)
|
{
|
var Y = Encoding.UTF8.GetString(buffer);
|
LogFactory.GetLog("Socket发送数据").Info(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 Restocking = "2100";
|
|
|
public const string EmptyOutBoundconst = "2101";
|
|
/// <summary>
|
/// 托盘个数 预留
|
/// </summary>
|
public const string TrayCnt = "1";
|
|
/// <summary>
|
/// 设备编号
|
/// </summary>
|
public const string DeviceID = "0013130010";
|
/// <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("D5") + GetFieldsAsString(Agvstatus) + GetListStringOutStation(outStations) + OutStationSpare2 + "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("D5");
|
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("D5") + 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("D5") + 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("D5") + 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("D5") + 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("D5") + 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("D5") + "0" + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode;
|
Dt_HostLog hostLog = new Dt_HostLog()
|
{
|
CommandID = 901,
|
Count=0,
|
Messgae=str,
|
};
|
_hostLogRepository.AddData(hostLog);
|
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, string sGetSeqNo)
|
{
|
try
|
{
|
var agvstatus = GetFieldsAsString(Agvstatus);
|
var outstation = GetListStringOutStation(outStations);
|
var instation = GetListStringInStation(inStation);
|
string str = SendNotReply + "936" + sGetSeqNo + agvstatus + outstation + OutStationSpare2 + "2" + 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("D5") + "R" + JobOrderID + priority + FormLocation + ToLocation + TaskType + TrayCnt + PallteCode;
|
Dt_HostLog hostLog = new Dt_HostLog()
|
{
|
CommandID = 105,
|
Count = 0,
|
Messgae = str,
|
};
|
_hostLogRepository.AddData(hostLog);
|
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("D5") + 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("D5") + 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("D5") + "1" + JobOrderID + priority + FormLocation + ToLocation + "O" + TrayCnt + PallteCode;
|
Dt_HostLog hostLog = new Dt_HostLog()
|
{
|
CommandID = 107,
|
Count = 0,
|
Messgae = str,
|
};
|
_hostLogRepository.AddData(hostLog);
|
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("D5") + GetFieldsAsString(AgvStatus) + X + Y;
|
clientSend(MakeStringToByteMsg(str));
|
}
|
|
#endregion
|
|
#region WMS接收HOST回传方法
|
|
/// <summary>
|
/// 接收HOST 响应请求 工序102 回复工序101
|
/// </summary>
|
/// <param name="message"></param>
|
public void ReceiveCommandResponse(string message)
|
{
|
var parseMessage = GetParse(message);
|
var lcationEnd = parseMessage.body.ToLocation;
|
switch (parseMessage.body.ret)
|
{
|
//OK
|
case "0":
|
//var location = _locationRepository.QueryFirst(x => x.LocationCode == lcationEnd);
|
var location = SqlSugarHelper.DbWMS.Queryable<DtLocationInfo>().Where(x => x.LocationCode == parseMessage.body.ToLocation).First();
|
if (location == null)
|
{
|
return;
|
}
|
if (location.LocationStatus == (int)LocationEnum.Free)
|
{
|
var task = InsertWMSTask(parseMessage);
|
Thread.Sleep(500);
|
InsertAGVTask(task);
|
location.LocationStatus = 1;
|
//SqlSugarHelper.DbWMS.Insertable(task).ExecuteCommand();
|
SqlSugarHelper.DbWMS.Updateable(location).ExecuteCommand();
|
BaseDal.AddData(task);
|
//915
|
JobReady("0");
|
return;
|
}
|
else
|
{
|
ErrorReport(Restocking, "A", "00");
|
Thread.Sleep(500);
|
RecreateGetLocation(parseMessage.body.FromLocation, parseMessage.body.ToLocation, parseMessage.body.JobType, parseMessage.body.TrayIdList.Substring(0, 10));
|
//todo货位有货重复入库
|
return;
|
}
|
//NG
|
case "1":
|
break;
|
//无库位分配
|
case "2":
|
break;
|
//站台到站台,出库站台未准备好
|
case "3":
|
break;
|
//非常温工程
|
case "4":
|
break;
|
|
case "9":
|
break;
|
default:
|
break;
|
}
|
|
LogFactory.GetLog("Host102回复WMS101").InfoFormat(true, $"请求参数:{JsonConvert.SerializeObject(parseMessage)}{Environment.NewLine}", "");
|
}
|
|
/// <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;
|
}
|
|
LogFactory.GetLog("Host906回复WMS905").InfoFormat(true, $"请求参数:{JsonConvert.SerializeObject(status)}{Environment.NewLine}{JsonConvert.SerializeObject(message)}{Environment.NewLine}", "");
|
}
|
|
/// <summary>
|
/// 数据报告响应 工序902 回复工序901
|
/// </summary>
|
public void DataReportResponse(string message)
|
{
|
LogFactory.GetLog("Host902回复WMS901").InfoFormat(true, $"请求参数:{JsonConvert.SerializeObject(message)}{Environment.NewLine}", "");
|
#region
|
//Dt_Task task = BaseDal.QueryFirst(x => (x.TaskState == (int)TaskOutStatusEnum.AGV_OutFinish || x.TaskState == (int)TaskRelocationStatusEnum.AGV_RelocationFinish || x.TaskState == (int)TaskInStatusEnum.AGV_InFinish) && x.Roadway != "PDA");
|
//if (task != null)
|
//{
|
// switch (task.TaskType)
|
// {
|
// case 200:
|
// //入库
|
// if (message == "0")
|
// {
|
// JobStartOrEnd("E", task.Remark, task.TargetAddress, "O", task.PalletCode);
|
// }
|
// break;
|
// case 100:
|
// //出库
|
// if (message == "0")
|
// {
|
// JobStartOrEnd("E", task.SourceAddress, task.Remark, "O", task.PalletCode);
|
// }
|
// break;
|
// case 300:
|
// if (message == "0")
|
// {
|
// JobStartOrEnd("E", task.SourceAddress, task.TargetAddress, "O", task.PalletCode);
|
// }
|
// _taskService.CompleteRelocationboundTask(task);
|
// break;
|
// case 400:
|
// if (message == "0")
|
// {
|
// JobStartOrEnd("E", task.Remark, task.HostName, "O", task.PalletCode);
|
// }
|
// break;
|
// default:
|
// break;
|
// }
|
//}
|
#endregion
|
}
|
/// <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 location = SqlSugarHelper.DbWMS.Queryable<DtLocationInfo>().Where(x => x.LocationCode == parseMessage.body.FromLocation).First();
|
if (location == null)
|
{
|
return;
|
}
|
if (location.LocationStatus == (int)LocationEnum.InStock)
|
{
|
Dt_Task task = InsertWMSTask(parseMessage);
|
//BaseDal.AddData(task);
|
location.LocationStatus = 1;
|
SqlSugarHelper.DbWMS.Insertable(task).ExecuteCommand();
|
SqlSugarHelper.DbWMS.Updateable(location).ExecuteCommand();
|
InsertAGVTask(task);
|
//Thread.Sleep(500);
|
//915
|
JobReady("0");
|
Thread.Sleep(500);
|
//104
|
DeviceReceiveJobResponse("0");
|
}
|
else
|
{
|
ErrorReport(EmptyOutBoundconst, "A", "00");
|
Thread.Sleep(500);
|
EmptyOutBound(parseMessage.body.FromLocation, parseMessage.body.ToLocation, parseMessage.body.TrayIdList.Substring(0, 10));
|
return;
|
}
|
|
break;
|
default:
|
break;
|
}
|
LogFactory.GetLog("Host103").InfoFormat(true, $"请求参数:{JsonConvert.SerializeObject(parseMessage)}{Environment.NewLine}", "");
|
|
}
|
catch (Exception ex)
|
{
|
DeviceReceiveJobResponse("1");
|
}
|
|
}
|
|
/// <summary>
|
/// HOST设备状态获取 工序935
|
/// </summary>
|
/// <param name="message"></param>
|
public void DeviceStationStatusInvite(string sSeqNo)
|
{
|
try
|
{
|
//回复936
|
|
StationStatus stationIn = GetStationStatus("B002");
|
StationStatus stationOut = GetStationStatus("B001");
|
List<OutStationStatus> outStationStatus = GetOutStationStatus(stationOut);
|
List<InStationStatus> inStationStatus = GetInStationStatus(stationIn);
|
|
#region
|
//inStationStatus.Add(new InStationStatus()
|
//{
|
// StationName = "02",
|
// StationEnable = stationIn.WorkstationO == "1" ? "0" : "1",
|
// IsDistributionTask = "0",
|
// PallteCode = "0000000000",
|
//});
|
//inStationStatus.Add(new InStationStatus()
|
//{
|
// StationName = "01",
|
// StationEnable = stationIn.WorkstationT == "1" ? "0" : "1",
|
// IsDistributionTask = "0",
|
// PallteCode = "0000000000",
|
//});
|
//var taskOutStationO = BaseDal.QueryFirst(x => x.SourceAddress == "B001::1" || x.TargetAddress == "B001::1");
|
//var taskOutStationT = BaseDal.QueryFirst(x => x.SourceAddress == "B001::2" || x.TargetAddress == "B001::2");
|
//outStationStatus.Add(new OutStationStatus()
|
//{
|
// StationName = "04",
|
// StationEnable = stationOut.WorkstationO == "1" ? "0" : "1",
|
// IsDistributionTask = taskOutStationO == null ? "0" : "1",
|
// Spare1 = "00"
|
//});
|
//outStationStatus.Add(new OutStationStatus()
|
//{
|
// StationName = "03",
|
// StationEnable = stationOut.WorkstationT == "1" ? "0" : "1",
|
// IsDistributionTask = taskOutStationT == null ? "0" : "1",
|
// Spare1 = "00"
|
//});
|
#endregion
|
AGVStatusRespone status = GetAGVStatus();
|
HOSTAGVStatus AgvStatus = new HOSTAGVStatus()
|
{
|
RuntimeStatus = CapitalizeFirstLetter(status.RuntimeStatus),
|
AutoStatus = status.AutoStatus == "MaintenanceMode" ? "1" : "0",
|
//Ready = status.AutoStatus == "MaintenanceMode" ? "0" : "1",
|
};
|
if (status.RuntimeStatus == "Idle" && status.AutoStatus == "ControlMode")
|
{
|
AgvStatus.Ready = "1";
|
}
|
else
|
{
|
AgvStatus.Ready = "0";
|
}
|
DeviceStationStatusReport(AgvStatus, outStationStatus, inStationStatus, sSeqNo);
|
}
|
catch (Exception)
|
{
|
DeviceReceiveJobResponse("1");
|
}
|
|
LogFactory.GetLog("Host935").InfoFormat(true, $"请求参数:{JsonConvert.SerializeObject(sSeqNo)}{Environment.NewLine}", "");
|
}
|
|
/// <summary>
|
/// 接收HOST重新分配货位 工序106
|
/// </summary>
|
/// <param name="message"></param>
|
public void RecreateGetLocation(string message)
|
{
|
try
|
{
|
var parseMessage = GetParse(message);
|
var lcationEnd = parseMessage.body.ToLocation;
|
switch (parseMessage.body.ret)
|
{
|
//OK
|
case "0":
|
var location = SqlSugarHelper.DbWMS.Queryable<DtLocationInfo>().Where(x => x.LocationCode == parseMessage.body.ToLocation).First();
|
if (location == null)
|
{
|
ErrorReport("2102", "A", "00");
|
Thread.Sleep(500);
|
RecreateGetLocation(parseMessage.body.FromLocation, parseMessage.body.ToLocation, parseMessage.body.JobType, parseMessage.body.TrayIdList.Substring(0, 10));
|
return;
|
}
|
if (location.LocationStatus == (int)LocationEnum.Free)
|
{
|
var task = SqlSugarHelper.DbWMS.Queryable<Dt_Task>().Where(x => x.PalletCode == parseMessage.body.TrayIdList.Substring(0, 10)).First();
|
if (task == null)
|
{
|
location.LocationStatus = (int)LocationEnum.Lock;
|
var tasknew = InsertWMSTask(parseMessage);
|
InsertAGVTask(tasknew);
|
BaseDal.AddData(tasknew);
|
SqlSugarHelper.DbWMS.Updateable(location);
|
Thread.Sleep(500);
|
//915
|
JobReady("0");
|
}
|
else
|
{
|
var Agvtask = SqlSugarHelper.DbAGV.Queryable<task_call>().Where(x => x.d_involed5 == task.TaskNum.ToString()).First();
|
location.LocationStatus = (int)LocationEnum.Lock;
|
task.TargetAddress = lcationEnd;
|
Agvtask.d_involed2 = lcationEnd;
|
SqlSugarHelper.DbWMS.Updateable(task);
|
SqlSugarHelper.DbWMS.Updateable(location);
|
SqlSugarHelper.DbAGV.Updateable(Agvtask);
|
Thread.Sleep(500);
|
//915
|
JobReady("0");
|
}
|
return;
|
}
|
else
|
{
|
ErrorReport(Restocking, "A", "00");
|
Thread.Sleep(500);
|
RecreateGetLocation(parseMessage.body.FromLocation, parseMessage.body.ToLocation, parseMessage.body.JobType, parseMessage.body.TrayIdList.Substring(0, 10));
|
return;
|
}
|
//NG
|
case "1":
|
break;
|
//无库位分配
|
case "2":
|
break;
|
case "9":
|
break;
|
default:
|
break;
|
}
|
|
LogFactory.GetLog("Host106重新分配库位").InfoFormat(true, $"请求参数:{JsonConvert.SerializeObject(parseMessage)}{Environment.NewLine}", "");
|
}
|
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(2000);
|
JobReady("1");
|
}
|
LogFactory.GetLog("Host108空出库响应").InfoFormat(true, $"请求参数:{JsonConvert.SerializeObject(message)}{Environment.NewLine}", "");
|
}
|
|
/// <summary>
|
/// HOST获取设备状态 工序909
|
/// </summary>
|
public void DeviceStatusReportRequest()
|
{
|
AGVStatusRespone status = GetAGVStatus();
|
|
HOSTAGVStatus AgvStatus = new HOSTAGVStatus()
|
{
|
RuntimeStatus = CapitalizeFirstLetter(status.RuntimeStatus),
|
AutoStatus = status.AutoStatus == "MaintenanceMode" ? "1" : "0",
|
|
};
|
if (status.RuntimeStatus == "Idle" && status.AutoStatus == "ControlMode")
|
{
|
AgvStatus.Ready = "1";
|
}
|
else
|
{
|
AgvStatus.Ready = "0";
|
}
|
string axis = "000000";
|
|
DeviceStatusReportResponse(AgvStatus, axis, axis);
|
|
LogFactory.GetLog("Host心跳").InfoFormat(true, $"请求参数:{JsonConvert.SerializeObject(AgvStatus)}{Environment.NewLine}{JsonConvert.SerializeObject(status)}{Environment.NewLine}", "");
|
}
|
#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 避免编码问题
|
|
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);
|
|
//var parseMessage = GetParse(str);
|
LogFactory.GetLog("Socket发送报文").Info(true, str);
|
return newArray;
|
}
|
catch (Exception ex)
|
{
|
throw new Exception(ex.Message);
|
}
|
}
|
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.IsHasPallte}{status.Spare1}{status.StationPallteCount}{status.PallteCode}{status.Spare2}"));
|
}
|
/// <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 List<OutStationStatus> GetOutStationStatus(StationStatus stationOut)
|
{
|
List<OutStationStatus> outStationStatus = new List<OutStationStatus>();
|
var taskOutStationO = BaseDal.QueryFirst(x => x.SourceAddress == "B001::1" || x.TargetAddress == "B001::1");
|
var taskOutStationT = BaseDal.QueryFirst(x => x.SourceAddress == "B001::2" || x.TargetAddress == "B001::2");
|
outStationStatus.Add(new OutStationStatus()
|
{
|
StationName = "04",
|
StationEnable = stationOut.WorkstationO == "1" ? "0" : "1",
|
IsDistributionTask = taskOutStationO == null ? "0" : "1",
|
Spare1 = "00"
|
});
|
outStationStatus.Add(new OutStationStatus()
|
{
|
StationName = "03",
|
StationEnable = stationOut.WorkstationT == "1" ? "0" : "1",
|
IsDistributionTask = taskOutStationT == null ? "0" : "1",
|
Spare1 = "00"
|
});
|
return outStationStatus;
|
}
|
|
public List<InStationStatus> GetInStationStatus(StationStatus stationIn)
|
{
|
List<InStationStatus> inStationStatus = new List<InStationStatus>();
|
var taskOutStationO = BaseDal.QueryFirst(x => x.SourceAddress == "B002::1" || x.TargetAddress == "B002::1");
|
var taskOutStationT = BaseDal.QueryFirst(x => x.SourceAddress == "B002::2" || x.TargetAddress == "B002::2");
|
inStationStatus.Add(new InStationStatus()
|
{
|
StationName = "02",
|
StationEnable = stationIn.WorkstationO == "1" ? "0" : "1",
|
IsDistributionTask = taskOutStationO == null ? "0" : "1",
|
PallteCode = "0000000000",
|
});
|
inStationStatus.Add(new InStationStatus()
|
{
|
StationName = "01",
|
StationEnable = stationIn.WorkstationT == "1" ? "0" : "1",
|
IsDistributionTask = taskOutStationT == null ? "0" : "1",
|
PallteCode = "0000000000",
|
});
|
return inStationStatus;
|
}
|
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 Dt_Task InsertWMSTask(ParseMessage parseMessage)
|
{
|
int taskType = 0;
|
int taskState = 0;
|
Dt_Task task = new Dt_Task();
|
if (parseMessage.body.JobType == "I")
|
{
|
Dt_StationManager Instation = _stationManagerRepository.QueryFirst(x => x.HostName == parseMessage.body.FromLocation.Substring(4, 2));
|
task.SourceAddress = Instation.stationName;
|
task.TargetAddress = parseMessage.body.ToLocation;
|
task.CurrentAddress = Instation.stationName;
|
task.NextAddress = parseMessage.body.ToLocation;
|
taskType = (int)TaskInboundTypeEnum.Inbound;
|
taskState = (int)TaskInStatusEnum.InNew;
|
task.Remark = parseMessage.body.FromLocation;
|
|
}
|
else if (parseMessage.body.JobType == "O")
|
{
|
task.SourceAddress = parseMessage.body.FromLocation;
|
Dt_StationManager Instation = _stationManagerRepository.QueryFirst(x => x.HostName == parseMessage.body.ToLocation.Substring(4, 2));
|
task.TargetAddress = Instation.stationName;
|
task.CurrentAddress = parseMessage.body.FromLocation;
|
task.NextAddress = Instation.stationName;
|
taskType = (int)TaskOutboundTypeEnum.Outbound;
|
taskState = (int)TaskOutStatusEnum.OutNew;
|
task.Remark = parseMessage.body.ToLocation;
|
}
|
else if (parseMessage.body.JobType == "S")
|
{
|
Dt_StationManager stationone = _stationManagerRepository.QueryFirst(x => x.HostName == parseMessage.body.FromLocation.Substring(4, 2));
|
Dt_StationManager stationtwo = _stationManagerRepository.QueryFirst(x => x.HostName == parseMessage.body.ToLocation.Substring(4, 2));
|
taskType = (int)TaskStationTypeEnum.StationToStation;
|
taskState = (int)TaskOutStatusEnum.OutNew;
|
task.SourceAddress = stationone.stationName;
|
task.TargetAddress = stationtwo.stationName;
|
task.CurrentAddress = stationone.stationName;
|
task.NextAddress = stationtwo.stationName;
|
task.Remark = parseMessage.body.FromLocation;
|
task.HostName = parseMessage.body.ToLocation;
|
task.HostName = parseMessage.body.ToLocation;
|
}
|
else if (parseMessage.body.JobType == "R")
|
{
|
taskType = (int)TaskRelocationTypeEnum.Relocation;
|
taskState = (int)TaskRelocationStatusEnum.RelocationNew;
|
|
task.SourceAddress = parseMessage.body.FromLocation;
|
task.TargetAddress = parseMessage.body.ToLocation;
|
task.CurrentAddress = parseMessage.body.FromLocation;
|
task.NextAddress = parseMessage.body.ToLocation;
|
}
|
else
|
{
|
throw new Exception("未知库位");
|
}
|
|
|
task.TaskNum = BaseDal.GetTaskNo().Result;
|
task.PalletCode = parseMessage.body.TrayIdList.Substring(0, 10);
|
task.TaskType = taskType;
|
task.TaskState = taskState;
|
task.Dispatchertime = DateTime.Now;
|
task.Grade = 1;
|
task.SeqNo = Convert.ToInt32(parseMessage.nSeqNo);
|
task.CommandID = Convert.ToInt32(parseMessage.bCmdID);
|
return task;
|
|
}
|
/// <summary>
|
/// 添加AGV任务
|
/// </summary>
|
/// <param name="task">任务对象</param>
|
/// <returns></returns>
|
private int InsertAGVTask(Dt_Task task)
|
{
|
string SourceAddress = string.Empty;
|
string TargetAddress = string.Empty;
|
if (task.TaskType == (int)TaskInboundTypeEnum.Inbound)
|
{
|
SourceAddress = task.SourceAddress;
|
TargetAddress = InsertHyphenEveryTwoChars(task.TargetAddress);
|
}
|
else if (task.TaskType == (int)TaskOutboundTypeEnum.Outbound)
|
{
|
SourceAddress = InsertHyphenEveryTwoChars(task.SourceAddress);
|
TargetAddress = task.TargetAddress;
|
}
|
else if (task.TaskType == (int)TaskRelocationTypeEnum.Relocation)
|
{
|
TargetAddress = InsertHyphenEveryTwoChars(task.TargetAddress);
|
SourceAddress = InsertHyphenEveryTwoChars(task.SourceAddress);
|
}
|
else if (task.TaskType == (int)TaskStationTypeEnum.StationToStation)
|
{
|
TargetAddress = task.TargetAddress;
|
SourceAddress = task.SourceAddress;
|
}
|
task_call task_Call = new task_call()
|
{
|
d_task_type = task.TaskType == (int)TaskTypeEnum.Inbound ? "1" : "2",
|
d_floor = "1",
|
d_involed1 = SourceAddress,
|
d_involed2 = TargetAddress,
|
d_involed5 = task.TaskNum.ToString(),
|
};
|
|
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(20, 1),
|
JobOrderID = x.Substring(21, 16),
|
priority = x.Substring(37, 1),
|
FromLocation = x.Substring(38, 6),
|
ToLocation = x.Substring(44, 6),
|
JobType = x.Substring(50, 1),
|
TrayCnt = x.Substring(51, 1),
|
TrayIdList = x.Substring(52)
|
},
|
};
|
}
|
public AGVStatusRespone GetAGVStatus()
|
{
|
string urlnew = url + "/ilns/AGV/getState";
|
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/ctrl/getState";
|
var result = HttpsClient.PostAsync(urlnew, JsonConvert.DeserializeObject<Dictionary<string, object>>(new { ctrlName = "B001" }.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();
|
}
|
public string InsertHyphenEveryTwoChars(string input)
|
{
|
if (string.IsNullOrEmpty(input))
|
{
|
return input;
|
}
|
|
StringBuilder result = new StringBuilder();
|
|
for (int i = 0; i < input.Length; i += 2)
|
{
|
// 添加两个字符
|
result.Append(input.Substring(i, 2));
|
|
// 如果不是最后两个字符,添加一个连字符
|
if (i + 2 < input.Length)
|
{
|
result.Append('-');
|
}
|
}
|
|
return result.ToString();
|
}
|
|
#endregion
|
}
|
}
|