|
|
|
|
using Ems.CollectService.Analysis;
|
|
|
|
|
using Ems.CollectService.Common;
|
|
|
|
|
using Ems.CollectService.Entity;
|
|
|
|
|
using Ems.CollectService.Redis;
|
|
|
|
|
using Ems.CollectService.SqlSugarCore;
|
|
|
|
|
using NetTaste;
|
|
|
|
|
using NLog;
|
|
|
|
|
using SqlSugar;
|
|
|
|
|
using StackExchange.Redis;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using TouchSocket.Core;
|
|
|
|
|
using TouchSocket.Sockets;
|
|
|
|
|
|
|
|
|
|
namespace Ems.CollectService.TouchSocket
|
|
|
|
|
{
|
|
|
|
|
public sealed class TcpServer
|
|
|
|
|
{
|
|
|
|
|
private static readonly Lazy<TcpServer> lazy = new Lazy<TcpServer>(() => new TcpServer());
|
|
|
|
|
|
|
|
|
|
private Logger logger = LogManager.GetCurrentClassLogger();
|
|
|
|
|
|
|
|
|
|
private TcpService service = new TcpService();
|
|
|
|
|
|
|
|
|
|
private StringChange stringChange = StringChange.Instance;
|
|
|
|
|
|
|
|
|
|
private BufferAnalysis bufferAnalysis = BufferAnalysis.Instance;
|
|
|
|
|
|
|
|
|
|
private MsgUtil msgUtil = MsgUtil.Instance;
|
|
|
|
|
|
|
|
|
|
private JsonChange jsonChange = JsonChange.Instance;
|
|
|
|
|
|
|
|
|
|
//private SqlSugarClient baseService = SqlGenerator.GetMySqlInstance();
|
|
|
|
|
|
|
|
|
|
//private ConnectionMultiplexer conn = RedisHelper.RedisConn;
|
|
|
|
|
public static TcpServer Instance
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return lazy.Value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TcpServer() { }
|
|
|
|
|
|
|
|
|
|
public void Init(int serverPort)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
service.Connecting = (client, e) => { //有客户端正在连接
|
|
|
|
|
logger.Info($"客户端{client.IP}正在接入服务");
|
|
|
|
|
};
|
|
|
|
|
service.Connected = (client, e) => { //有客户端成功连接
|
|
|
|
|
logger.Trace($"客户端{client.IP}接入服务成功,Id:{client.ID}");
|
|
|
|
|
};
|
|
|
|
|
service.Disconnected = (client, e) => { //有客户端断开连接
|
|
|
|
|
logger.Trace($"客户端{client.IP}断开连接,Id:{client.ID}");
|
|
|
|
|
if (client.ID.Length >= 4)
|
|
|
|
|
{
|
|
|
|
|
string clientId = string.Format("E{0}", client.ID.ToString().Substring(2, client.ID.ToString().Length - 2));
|
|
|
|
|
bufferAnalysis.UpdateCollectDeviceOnLineState(clientId, 0);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
service.Received = (client, byteBlock, requestInfo) =>
|
|
|
|
|
{
|
|
|
|
|
if (requestInfo is MyFixedHeaderRequestInfo myRequestInfo)
|
|
|
|
|
{
|
|
|
|
|
string body = Encoding.UTF8.GetString(myRequestInfo.Body, 0, myRequestInfo.Body.Length);
|
|
|
|
|
|
|
|
|
|
bufferMemory(client, myRequestInfo);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
service.Setup(new TouchSocketConfig()//载入配置
|
|
|
|
|
.SetListenIPHosts(new IPHost[] { new IPHost($"0.0.0.0:{serverPort}") })//同时监听两个地址
|
|
|
|
|
.SetDataHandlingAdapter(() => { return new MyFixedHeaderCustomDataHandlingAdapter(); })//配置适配器
|
|
|
|
|
.ConfigureContainer(a =>//容器的配置顺序应该在最前面
|
|
|
|
|
{
|
|
|
|
|
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
|
|
|
|
})
|
|
|
|
|
.ConfigurePlugins(a =>
|
|
|
|
|
{
|
|
|
|
|
//a.Add<CheckClearPlugin>().SetDuration(new TimeSpan(0, 0, 0, 5, 0));
|
|
|
|
|
|
|
|
|
|
}))
|
|
|
|
|
.Start();//启动
|
|
|
|
|
ReadMeterTask(); //点抄指令下发
|
|
|
|
|
logger.Info($"采集服务启动成功,监听端口:{serverPort}");
|
|
|
|
|
}
|
|
|
|
|
catch(Exception ex)
|
|
|
|
|
{
|
|
|
|
|
logger.Error($"采集服务启动异常:{ex}");
|
|
|
|
|
throw new Exception(ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 缓存器
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="clientId"></param>
|
|
|
|
|
/// <param name="requestInfo"></param>
|
|
|
|
|
private void bufferMemory(SocketClient client, MyFixedHeaderRequestInfo requestInfo)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string header = stringChange.bytesToHexStr(requestInfo.Header, requestInfo.Header.Length);
|
|
|
|
|
string body = stringChange.bytesToHexStr(requestInfo.Body, requestInfo.BodyLength);
|
|
|
|
|
|
|
|
|
|
byte[] buffer = new byte[requestInfo.Header.Length + requestInfo.Body.Length];
|
|
|
|
|
Array.Copy(requestInfo.Header, 0, buffer, 0, requestInfo.Header.Length);
|
|
|
|
|
Array.Copy(requestInfo.Body, 0, buffer, requestInfo.Header.Length, requestInfo.Body.Length);
|
|
|
|
|
|
|
|
|
|
//var db = conn.GetDatabase();
|
|
|
|
|
switch (requestInfo.Header[7])
|
|
|
|
|
{
|
|
|
|
|
case 0x21:
|
|
|
|
|
//登录指令 =>回复登录指令、下发校时指令
|
|
|
|
|
//db.StringSet("LoginMessage:" + clientId, body, TimeSpan.FromMinutes(10));
|
|
|
|
|
logger.Info($"{client.ID}登录指令,Header:{header};Body:{body}");
|
|
|
|
|
bufferAnalysis.LoginMessageHandling(client, buffer, requestInfo.Header.Length + requestInfo.Body.Length);
|
|
|
|
|
break;
|
|
|
|
|
case 0X24:
|
|
|
|
|
//心跳指令
|
|
|
|
|
//db.StringSet("HeartMessage:" + clientId, body, TimeSpan.FromMinutes(10));
|
|
|
|
|
logger.Info($"{client.ID}心跳指令,Header:{header};Body:{body}");
|
|
|
|
|
bufferAnalysis.HeartMessageHandlingAsync(client, buffer, requestInfo.Header.Length + requestInfo.Body.Length);
|
|
|
|
|
break;
|
|
|
|
|
case 131:
|
|
|
|
|
//电表数据
|
|
|
|
|
//db.StringSet("DnbMessage:" + clientId, body, TimeSpan.FromMinutes(10));
|
|
|
|
|
logger.Info($"{client.ID}电表数据,Header:{header};Body:{body}");
|
|
|
|
|
bufferAnalysis.EDataMessageHandlingAsync(client, buffer, requestInfo.Header.Length + requestInfo.Body.Length, 1);
|
|
|
|
|
break;
|
|
|
|
|
case 132:
|
|
|
|
|
//水、蒸汽、压缩空气表数据(流量类型)
|
|
|
|
|
//db.StringSet("FlowMessage:" + clientId, body, TimeSpan.FromMinutes(10));
|
|
|
|
|
logger.Info($"{client.ID}水表数据,Header:{header};Body:{body}");
|
|
|
|
|
bufferAnalysis.SDataMessageHandling(client, buffer, requestInfo.Header.Length + requestInfo.Body.Length, 1);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 147:
|
|
|
|
|
//电表点抄
|
|
|
|
|
//db.StringSet("DnbMessage:" + clientId, body, TimeSpan.FromMinutes(10));
|
|
|
|
|
logger.Info($"{client.ID}电表点抄,Header:{header};Body:{body}");
|
|
|
|
|
bufferAnalysis.EDataMessageHandlingAsync(client, buffer, requestInfo.Header.Length + requestInfo.Body.Length, 2);
|
|
|
|
|
break;
|
|
|
|
|
case 148:
|
|
|
|
|
//水表点抄
|
|
|
|
|
//db.StringSet("FlowMessage:" + clientId, body, TimeSpan.FromMinutes(10));
|
|
|
|
|
logger.Info($"{client.ID}水表点抄,Header:{header};Body:{body}");
|
|
|
|
|
bufferAnalysis.SDataMessageHandling(client, buffer, requestInfo.Header.Length + requestInfo.Body.Length, 2);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
logger.Info($"{client.ID}指令未匹配到对应类型;");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch(Exception ex)
|
|
|
|
|
{
|
|
|
|
|
logger.Error("缓存器异常", ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 点抄指令下发
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void ReadMeterTask()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var t = Task.Run(async delegate
|
|
|
|
|
{
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
//SysConfig sysConfig = baseService.Queryable<SysConfig>().InSingle(14);
|
|
|
|
|
SysConfig sysConfig = SqlSugarHelper.Db.Queryable<SysConfig>().InSingle(14);
|
|
|
|
|
if (sysConfig != null)
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(sysConfig.ConfigValue))
|
|
|
|
|
{
|
|
|
|
|
logger.Warn($"通过参数配置获取到点抄表号:{sysConfig.ConfigValue}");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string[] clientIds = sysConfig.ConfigValue.Split('+');
|
|
|
|
|
|
|
|
|
|
foreach (string item in clientIds)
|
|
|
|
|
{
|
|
|
|
|
string clientId = FormatCollectDeviceId(item.Substring(0, 5));
|
|
|
|
|
SocketClient[] socketClients = service.GetClients();
|
|
|
|
|
|
|
|
|
|
foreach (SocketClient socketClient in socketClients)
|
|
|
|
|
{
|
|
|
|
|
logger.Warn($"客户端信息打印:{socketClient.ID};{socketClient.IP}");
|
|
|
|
|
|
|
|
|
|
byte[] buffer = new byte[14];
|
|
|
|
|
byte[] collectBuffer = msgUtil.HexStrTorbytes(clientId.Substring(2, 4));
|
|
|
|
|
|
|
|
|
|
string monitorId = Convert.ToString(msgUtil.ParseToInt(item.Substring(6, 2)), 16).PadLeft(2, '0');
|
|
|
|
|
monitorId += Convert.ToString(msgUtil.ParseToInt(item.Substring(8, 2)), 16).PadLeft(2, '0');
|
|
|
|
|
|
|
|
|
|
byte[] monitorBuffer = msgUtil.HexToString(monitorId);
|
|
|
|
|
|
|
|
|
|
Array.Copy(new byte[] { 0x68, 0x45 }, 0, buffer, 0, 2);
|
|
|
|
|
Array.Copy(collectBuffer, 0, buffer, 2, collectBuffer.Length);
|
|
|
|
|
Array.Copy(new byte[] { 0x00, 0xBF, 0x68, 0x06, 0x00, 0x02 }, 0, buffer, 4, 6);
|
|
|
|
|
Array.Copy(monitorBuffer, 0, buffer, 10, monitorBuffer.Length);
|
|
|
|
|
|
|
|
|
|
/*buffer[10] = Encoding.UTF8.GetBytes(Convert.ToString(msgUtil.ParseToInt(sysConfig.ConfigValue.Substring(6, 2)), 16))[0];
|
|
|
|
|
buffer[11] = 0x00;*/
|
|
|
|
|
|
|
|
|
|
byte csCheck = msgUtil.Check_CS(buffer);
|
|
|
|
|
buffer[12] = csCheck;
|
|
|
|
|
buffer[13] = 0x16;
|
|
|
|
|
|
|
|
|
|
socketClient.Send(buffer);
|
|
|
|
|
logger.Warn($"向客户端:{socketClient.ID};发送点抄指令:{msgUtil.bytesToHexStr(buffer, (int)buffer.Length)}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await Task.Delay(100 * 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sysConfig.ConfigValue = string.Empty;
|
|
|
|
|
|
|
|
|
|
int result = SqlSugarHelper.Db.Updateable<SysConfig>().SetColumns(it => new SysConfig() { ConfigValue = string.Empty, UpdateTime = DateTime.Now }).Where(it => it.ConfigId == 14).ExecuteCommand();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Warn("参数配置信息获取为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await Task.Delay(1000 * 2);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
logger.Error($"点抄数据异常:{ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 集中器编号格式化=>ClientID
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="monitorId"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private string FormatCollectDeviceId(string deviceId)
|
|
|
|
|
{
|
|
|
|
|
byte sensorid1 = Convert.ToByte(deviceId.Substring(1, 2));
|
|
|
|
|
byte sensorid2 = Convert.ToByte(deviceId.Substring(3, 2));
|
|
|
|
|
byte[] flag = Encoding.Default.GetBytes(deviceId.Substring(0, 1));
|
|
|
|
|
string collectDeviceId = Convert.ToInt32(flag[0].ToString("x")) + deviceId.Substring(1);
|
|
|
|
|
return collectDeviceId;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|