# Conflicts:
#	WCS.BLL/Tool/Helper.cs
#	WCS.WebApi/Program.cs
This commit is contained in:
陶坤
2024-06-25 13:46:57 +08:00
176 changed files with 9769 additions and 1826 deletions

View File

@ -274,7 +274,7 @@ namespace WCS.BLL.Tool
public static T GetDataFromHttp<T>(string url, object dataObj, string httpMethod, bool isSaveLog = false)
{
Guid guid = Guid.NewGuid();
var data = JsonConvert.SerializeObject(dataObj);
var data = dataObj == null ? string.Empty : JsonConvert.SerializeObject(dataObj);
try
{
if (isSaveLog)
@ -312,5 +312,47 @@ namespace WCS.BLL.Tool
return default(T);
}
}
public static T GetDataFromHttpShortTime<T>(string url, object dataObj, string httpMethod, bool isSaveLog = false)
{
Guid guid = Guid.NewGuid();
var data = dataObj == null ? string.Empty : JsonConvert.SerializeObject(dataObj);
try
{
if (isSaveLog)
Logs.Write($"【{guid}】开始请求调用接口 url{url} 请求方式:{httpMethod} 数据:{data}", LogsType.Api);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = httpMethod;
request.ContentType = "application/json";
request.Timeout = 2000;
if (!string.IsNullOrEmpty(data))
{
string strContent = data; //参数data
using (StreamWriter dataStream = new StreamWriter(request.GetRequestStream()))
{
dataStream.Write(strContent);
dataStream.Close();
}
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string encoding = response.ContentEncoding;
if (encoding == null || encoding.Length < 1)
{
encoding = "UTF-8"; //默认编码
}
StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(encoding));
string retString = reader.ReadToEnd();
if (isSaveLog)
Logs.Write($"【{guid}】请求调用接口结束 返回数据为{retString}", LogsType.Api);
return JsonConvert.DeserializeObject<T>(retString);
}
catch (Exception ex)
{
Logs.Write($"【{guid}】请求调用遇到异常 异常信息为{ex.Message}");
return default(T);
}
}
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace WCS.BLL.Tool
{
/// <summary>
/// 一个心跳计数器扩展。
/// </summary>
internal static class DependencyExtensions
{
public static readonly DependencyProperty<Timer> HeartbeatTimerProperty =
DependencyProperty<Timer>.Register("HeartbeatTimer", null);
public static bool Ping<TClient>(this TClient client) where TClient : ITcpClientBase
{
try
{
var data = new byte[] { 0x05, 0x00, 0x00, 0x06, 0x78, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00 };
client.Send(data);
return true;
}
catch (Exception ex)
{
Logs.Write($"【{client.IP}:{client.Port}】发送心跳中发现异常:" + ex.Message, LogsType.Info);
if (ex is NotConnectedException)
{
}
}
return false;
}
public static bool Pong<TClient>(this TClient client) where TClient : ITcpClientBase
{
try
{
client.Send(new MyRequestInfo() { DataType = DataType.Pong }.PackageAsBytes());
return true;
}
catch (Exception ex)
{
client.Logger.Exception(ex);
}
return false;
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace WCS.BLL.Tool
{
internal class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugin<ITcpClientBase>, ITcpDisconnectedPlugin<ITcpClientBase>, ITcpReceivedPlugin<ITcpClientBase>
{
private readonly int m_timeTick;
private readonly ILog logger;
[DependencyInject("20000")]
public HeartbeatAndReceivePlugin(int timeTick, ILog logger)
{
this.m_timeTick = 20000;
this.logger = logger;
}
public async Task OnTcpConnected(ITcpClientBase client, ConnectedEventArgs e)
{
if (client is ISocketClient)
{
return;//此处可判断,如果为服务器,则不用使用心跳。
}
if (client.GetValue(DependencyExtensions.HeartbeatTimerProperty) is Timer timer)
{
timer.Dispose();
}
client.SetValue(DependencyExtensions.HeartbeatTimerProperty, new Timer((o) =>
{
client.Ping();
}, null, 0, this.m_timeTick));
await e.InvokeNext();
}
public async Task OnTcpDisconnected(ITcpClientBase client, DisconnectEventArgs e)
{
if (client.GetValue(DependencyExtensions.HeartbeatTimerProperty) is Timer timer)
{
timer.Dispose();
client.SetValue(DependencyExtensions.HeartbeatTimerProperty, null);
}
await e.InvokeNext();
}
public async Task OnTcpReceived(ITcpClientBase client, ReceivedDataEventArgs e)
{
if (e.RequestInfo is MyRequestInfo myRequest)
{
this.logger.Info(myRequest.ToString());
if (myRequest.DataType == DataType.Ping)
{
client.Pong();
}
}
await e.InvokeNext();
}
}
}

View File

@ -83,6 +83,30 @@ namespace WCS.BLL.Tool
return data.ToArray();
}
public static byte[] Query()
{
byte[] data2 = new byte[4];
data2[0] = 0xff;
data2[1] = 0x03;
data2[2] = 0x00;
data2[3] = 0x06;
byte[] senddata2 = Tool.Helper.Crc16(data2, data2.Length, true);
return senddata2;
}
public static byte[] SetId()
{
byte[] data2 = new byte[6];
data2[0] = 0xff;
data2[1] = 0x04;
data2[2] = 0x00;
data2[3] = 0x08;
data2[4] = 0x03;
data2[5] = 0x01;
byte[] senddata2 = Tool.Helper.Crc16(data2, data2.Length, true);
return senddata2;
}
//出库库位灯亮灯
public static byte[] OutstoreLight(List<int> board_id, string lightcolor,int status)
{
@ -200,11 +224,15 @@ namespace WCS.BLL.Tool
//入库警示灯短亮(绿色),蜂鸣器鸣叫一次
public static byte[] InstoreWarnLight(int lightid)
{
byte[] data1 = new byte[8];
byte[] data1 = new byte[8 + 3 * 6];
data1[0] = 0xff;
data1[1] = 0x02;
data1[2] = 0x00;
data1[3] = 0x0a;
data1[4] = (byte)lightid;
data1[5] = 0x03;
data1[6] = 0x02;
for (int i = 0; i < 7; i++)
{
data1[4 + i * 3] = (byte)(lightid + i);
@ -214,7 +242,8 @@ namespace WCS.BLL.Tool
//data1[4] = (byte)lightid;
//data1[5] = 0x03;
//data1[6] = 0x02;
data1[7] = 0x02;
//data1[7] = 0x02;
data1[7] = 0x00;
byte[] senddata1 = Tool.Helper.Crc16(data1, data1.Length, true);
return senddata1;
}

View File

@ -22,18 +22,25 @@ namespace WCS.BLL
/// </summary>
Info,
/// <summary>
/// 警告
/// 启动信息
/// </summary>
Warning,
StartBoot,
/// <summary>
/// 错误
/// 调用外部Api的接口
/// </summary>
Err,
Api,
/// <summary>
/// 数据库错误
/// 指令发送接收
/// </summary>
DbErr,
Api
Instructions,
/// <summary>
/// 指令重发
/// </summary>
InstructionResend,
/// <summary>
/// 出库流程日志
/// </summary>
Outstore,
}
/// <summary>
@ -115,16 +122,6 @@ namespace WCS.BLL
Write($"{contentTitle} {JsonConvert.SerializeObject(content)}", type);
}
/// <summary>
/// 写入日志
/// </summary>
/// <param name="ex">错误</param>
/// <returns>是否写入成功</returns>
public static void Write(Exception ex, LogsType type = LogsType.Err)
{
Write(ex.ToString(), type);
}
/// <summary>
/// 清除日志
/// </summary>

View File

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TouchSocket.Core;
namespace WCS.BLL.Tool
{
internal class MyFixedHeaderDataHandlingAdapter : CustomFixedHeaderDataHandlingAdapter<MyRequestInfo>
{
public override int HeaderLength => 3;
public override bool CanSendRequestInfo => false;
protected override MyRequestInfo GetInstance()
{
return new MyRequestInfo();
}
protected override void PreviewSend(IRequestInfo requestInfo)
{
throw new NotImplementedException();
}
}
internal class MyRequestInfo : IFixedHeaderRequestInfo
{
public DataType DataType { get; set; }
public byte[] Data { get; set; }
public int BodyLength { get; private set; }
public bool OnParsingBody(byte[] body)
{
if (body.Length == this.BodyLength)
{
this.Data = body;
return true;
}
return false;
}
public bool OnParsingHeader(byte[] header)
{
if (header.Length == 3)
{
this.BodyLength = TouchSocketBitConverter.Default.ToUInt16(header, 0) - 1;
this.DataType = (DataType)header[2];
return true;
}
return false;
}
public void Package(ByteBlock byteBlock)
{
byteBlock.Write((ushort)((this.Data == null ? 0 : this.Data.Length) + 1));
byteBlock.Write((byte)this.DataType);
if (this.Data != null)
{
byteBlock.Write(this.Data);
}
}
public byte[] PackageAsBytes()
{
using var byteBlock = new ByteBlock();
this.Package(byteBlock);
return byteBlock.ToArray();
}
public override string ToString()
{
return $"数据类型={this.DataType},数据={(this.Data == null ? "null" : Encoding.UTF8.GetString(this.Data))}";
}
}
internal enum DataType : byte
{
Ping,
Pong,
Data
}
}

View File

@ -2,6 +2,7 @@
using System.Text;
using TouchSocket.Core;
using TouchSocket.Sockets;
using WCS.BLL.Tool;
namespace WCS.BLL
@ -68,6 +69,7 @@ namespace WCS.BLL
{
//配置断线重连
a.UseReconnection(-1, true, 1000);
a.Add<HeartbeatAndReceivePlugin>();
})
.ConfigureContainer(a =>
{
@ -79,36 +81,38 @@ namespace WCS.BLL
tcpClient.Received += (client, e) =>
{
var data = e.ByteBlock.Buffer.Take((int)e.ByteBlock.Length).ToArray();
Logs.Write($"校验发送接收,收到数据" + BitConverter.ToString(data));
var len = data.Length;
for (int index = 0; index < data.Length - PreFixLength; index++)
Task.Run(() =>
{
//协议拆包 通过前缀校验是否为完整数据包
var prefixInData = data.Skip(index).Take(PreFixLength);
var isEqual = prefixInData.SequenceEqual(Prefix);
if (isEqual)
Logs.Write($"【校验发送接收 开始】" + BitConverter.ToString(data), LogsType.InstructionResend);
var len = data.Length;
for (int index = 0; index < data.Length - PreFixLength; index++)
{
var dataTemp = data.Skip(index).Take(PreFixLength + DataLength).ToArray();
if (dataTemp.Length < PreFixLength + DataLength)//拆包后不满足一条指令的长度
//协议拆包 通过前缀校验是否为完整数据包
var prefixInData = data.Skip(index).Take(PreFixLength);
var isEqual = prefixInData.SequenceEqual(Prefix);
if (isEqual)
{
continue;
var dataTemp = data.Skip(index).Take(PreFixLength + DataLength).ToArray();
if (dataTemp.Length < PreFixLength + DataLength)//拆包后不满足一条指令的长度
{
continue;
}
//获取返回指令的板子ID
var boardId = (dataTemp[PreFixLength + 0] << 8) + dataTemp[PreFixLength + 1];
//查询当前板子是否有待验证的指令
var message = new MessageDto();
MessageList.TryGetValue(boardId, out message);
//功能位校验 功能位相同视为已响应指令 删除对应的指令
if (message?.Message[PreFixLength + 2] == dataTemp[PreFixLength + 2])
{
MessageList.TryRemove(boardId, out message);
}
index += (PreFixLength + DataLength - 2);//每次循环index会+1 所以这里-1
}
//获取返回指令的板子ID
var boardId = (data[PreFixLength + 0] << 8) + data[PreFixLength + 1];
//查询当前板子是否有待验证的指令
var message = new MessageDto();
MessageList.TryGetValue(boardId, out message);
//功能位校验 功能位相同视为已响应指令 删除对应的指令
if (message?.Message[PreFixLength + 2] == dataTemp[PreFixLength + 2])
{
MessageList.TryRemove(boardId, out message);
}
index += (PreFixLength + DataLength - 1);//每次循环index会+1 所以这里-1
}
}
Logs.Write($"校验发送接收处理完毕" + BitConverter.ToString(data));
Logs.Write($"【校验发送接收 结束】" + BitConverter.ToString(data), LogsType.InstructionResend);
});
return null;
};
tcpClient.Connected += (client, e) =>
@ -131,27 +135,25 @@ namespace WCS.BLL
try
{
//TODO如果指令已发两次 则取消重发
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
await Task.Delay(100);
if (MessageList.Count > 0)
{
var failedMessage = MessageList.Where(t => t.Value.SendTimes >= 2).ToList();
var failedMessage = MessageList.Where(t => t.Value.LastSendTime < DateTime.Now.AddSeconds(-1))
.ToList();
foreach (var message in failedMessage)
{
Logs.Write(BitConverter.ToString(message.Value.Message) +
"指令未响应");
Logs.Write("【指令重发】" + BitConverter.ToString(message.Value.Message) + "指令超时1s未响应", LogsType.InstructionResend);
}
MessageList.RemoveWhen(t => t.Value.SendTimes >= 3);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
MessageList.RemoveWhen(t => t.Value.SendTimes >= 2);
foreach (var item in MessageList)
{
if (item.Value.LastSendTime < DateTime.Now.AddSeconds(-1))
{
tcpClient.Send(item.Value.Message);
Send(item.Value.Message);
item.Value.SendTimes++;
item.Value.LastSendTime = DateTime.Now;
await Task.Delay(10);
Logs.Write("【指令重发】" + BitConverter.ToString(item.Value.Message) + "已进行重发", LogsType.InstructionResend);
}
}
}
@ -173,22 +175,36 @@ namespace WCS.BLL
}
public void Send(byte[] message)
public void Send(byte[] message, bool IsReSend = false)
{
try
{
var boardId = (message[3] << 8) + message[4];
if (boardId != 2047 && IsReSend == false)
{
MessageList.TryAdd(boardId, new MessageDto()
{
ID = boardId,
Message = message,
SendTimes = 1
});
}
lock (sendLockObject)
{
tcpClient.Send(message);
//TODO MessageList.AddOrUpdate(new Mes)
//发送自带10ms间隔
Thread.Sleep(10);
Task.Run(() =>
{
Logs.Write($"【发送】{BitConverter.ToString(message)}", LogsType.Instructions);
});
//发送自带8ms间隔
Thread.Sleep(8);
}
}
catch (Exception ex)
{
//因异常断连时(网线已经被断了) 尝试重连一次
//因异常断连时(网线已经被断了) 手动重连一次
if (ex is NotConnectedException)
{
Task.Run(() =>
@ -226,18 +242,14 @@ namespace WCS.BLL
public class MessageDto
{
public int ID { get; set; }
/// <summary>
/// 最后一次发送时间
/// </summary>
public DateTime LastSendTime { get; set; } = DateTime.Now;
/// <summary>
/// 发送内容
/// </summary>
public byte[] Message { get; set; }
public bool IsWating { get; set; }
/// <summary>
/// 发送次数
/// </summary>