From ec8b7037c060e5570a361a24d0e42fb9ad857343 Mon Sep 17 00:00:00 2001 From: smartwyy <645583145@qq.com> Date: Tue, 14 May 2024 22:06:03 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A9=B1=E5=8A=A8=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ConsoleStarter/ConsoleStarter.csproj | 2 + ConsoleStarter/ExportDb.cs | 62 +++++++ ConsoleStarter/Program.cs | 42 +---- .../Autofac/AutofacModuleRegister.cs | 36 ++-- HybirdFrameworkDriver/Common/IToBytes.cs | 6 + .../ModbusTcpMaster/ModbusTcpMaster.cs | 23 ++- .../{TcpServer => Session}/ChannelUtils.cs | 2 +- .../{TcpServer => Session}/IoSession.cs | 15 +- .../Session/ModbusSession.cs | 37 +++++ HybirdFrameworkDriver/Session/SessionMgr.cs | 154 ++++++++++++++++++ .../TcpClient/ClientListenerHandler.cs | 73 +++++++++ HybirdFrameworkDriver/TcpClient/TcpClient.cs | 148 +++++++++++++++++ HybirdFrameworkDriver/TcpServer/IDecoder.cs | 5 - HybirdFrameworkDriver/TcpServer/IEncoder.cs | 6 - .../TcpServer/IMsgHandler.cs | 7 - .../TcpServer/ServerListenerHandler.cs | 10 +- HybirdFrameworkDriver/TcpServer/SessionMgr.cs | 39 ----- .../TcpServer/{Server.cs => TcpServer.cs} | 64 ++++++-- .../HybirdFrameworkRepository.deps.json | 68 ++++++++ .../Charger/Client/ChargerClient.cs | 29 ++++ .../Charger/Client/ChargerServer.cs | 14 ++ .../Charger/Client/ClientMgr.cs | 29 ++++ .../Charger/Codec/Decoder.cs | 8 +- .../Charger/Codec/Encoder.cs | 10 +- .../Charger/Common/ChargerConst.cs | 6 + .../Charger/Handler/AuthResHandler.cs | 29 ++++ .../Charger/Handler/Cmd106Handler.cs | 15 -- .../Charger/Handler/IBaseHandler.cs | 7 + .../Charger/Handler/IoSessionHandler.cs | 14 -- .../Charger/Handler/LoginHandler.cs | 42 +++++ HybirdFrameworkServices/Charger/Msg/APCI.cs | 46 ++++++ HybirdFrameworkServices/Charger/Msg/ASDU.cs | 75 +++++++++ .../Charger/Msg/Charger/Req/Login.cs | 88 ++++++++++ .../Charger/Msg/Charger/Resp/AuthRes.cs | 35 ++++ HybirdFrameworkServices/Charger/Msg/Cmd106.cs | 9 - .../Charger/Msg/Host/Req/Auth.cs | 57 +++++++ .../Charger/Msg/Host/Resp/LogSignMessage.cs | 89 ++++++++++ WebStarter/Program.cs | 3 - WinFormStarter/Form2.cs | 10 +- ...rosoft.Extensions.Logging.Abstractions.dll | Bin 52616 -> 62064 bytes 40 files changed, 1208 insertions(+), 206 deletions(-) create mode 100644 ConsoleStarter/ExportDb.cs create mode 100644 HybirdFrameworkDriver/Common/IToBytes.cs rename HybirdFrameworkDriver/{TcpServer => Session}/ChannelUtils.cs (94%) rename HybirdFrameworkDriver/{TcpServer => Session}/IoSession.cs (61%) create mode 100644 HybirdFrameworkDriver/Session/ModbusSession.cs create mode 100644 HybirdFrameworkDriver/Session/SessionMgr.cs create mode 100644 HybirdFrameworkDriver/TcpClient/ClientListenerHandler.cs create mode 100644 HybirdFrameworkDriver/TcpClient/TcpClient.cs delete mode 100644 HybirdFrameworkDriver/TcpServer/IDecoder.cs delete mode 100644 HybirdFrameworkDriver/TcpServer/IEncoder.cs delete mode 100644 HybirdFrameworkDriver/TcpServer/IMsgHandler.cs delete mode 100644 HybirdFrameworkDriver/TcpServer/SessionMgr.cs rename HybirdFrameworkDriver/TcpServer/{Server.cs => TcpServer.cs} (62%) create mode 100644 HybirdFrameworkServices/Charger/Client/ChargerClient.cs create mode 100644 HybirdFrameworkServices/Charger/Client/ChargerServer.cs create mode 100644 HybirdFrameworkServices/Charger/Client/ClientMgr.cs create mode 100644 HybirdFrameworkServices/Charger/Common/ChargerConst.cs create mode 100644 HybirdFrameworkServices/Charger/Handler/AuthResHandler.cs delete mode 100644 HybirdFrameworkServices/Charger/Handler/Cmd106Handler.cs create mode 100644 HybirdFrameworkServices/Charger/Handler/IBaseHandler.cs delete mode 100644 HybirdFrameworkServices/Charger/Handler/IoSessionHandler.cs create mode 100644 HybirdFrameworkServices/Charger/Handler/LoginHandler.cs create mode 100644 HybirdFrameworkServices/Charger/Msg/APCI.cs create mode 100644 HybirdFrameworkServices/Charger/Msg/ASDU.cs create mode 100644 HybirdFrameworkServices/Charger/Msg/Charger/Req/Login.cs create mode 100644 HybirdFrameworkServices/Charger/Msg/Charger/Resp/AuthRes.cs delete mode 100644 HybirdFrameworkServices/Charger/Msg/Cmd106.cs create mode 100644 HybirdFrameworkServices/Charger/Msg/Host/Req/Auth.cs create mode 100644 HybirdFrameworkServices/Charger/Msg/Host/Resp/LogSignMessage.cs diff --git a/ConsoleStarter/ConsoleStarter.csproj b/ConsoleStarter/ConsoleStarter.csproj index f422989..8ac72a1 100644 --- a/ConsoleStarter/ConsoleStarter.csproj +++ b/ConsoleStarter/ConsoleStarter.csproj @@ -15,6 +15,8 @@ + + diff --git a/ConsoleStarter/ExportDb.cs b/ConsoleStarter/ExportDb.cs new file mode 100644 index 0000000..f2524ed --- /dev/null +++ b/ConsoleStarter/ExportDb.cs @@ -0,0 +1,62 @@ +using log4net; +using SqlSugar; + +namespace ConsoleStarter; + +public class ExportDb +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(ExportDb)); + + private SqlSugarClient Db = new(new ConnectionConfig + { + ConnectionString = + "server=106.12.36.89;Database=chassis_track_swap0;Uid=remote_user;Pwd=Rszn123;Charset=utf8;", + DbType = DbType.MySql, + IsAutoCloseConnection = true, + InitKeyType = InitKeyType.Attribute + }); + + private static readonly string[] UsedTable = new[] + { + "t_bs_charging_bin_info", + "t_bs_cloud_charge_model_recv_record", + "t_bs_cloud_elec_price_recv_record", + "t_bs_eqm_fault_base_info", + "t_bs_net_cloud_param_info", + "t_bs_net_eqm_param_info", + "t_bs_station_config_info", + "t_bs_station_elec_price_info", + "t_bs_station_info", + "t_cb_amt_order_info", + "t_cb_station_order_batt_log_info", + "t_cb_station_order_sended_log", + "t_cb_station_order_state_log", + "t_fl_repaired_info", + "t_fl_un_repair_info", + "t_rm_charger_record_report", + "t_ss_authority_to_role", + "t_ss_button_info", + "t_ss_menu_info", + "t_ss_role_info", + "t_ss_user_info", + "t_ss_user_to_role" + }; + + public void Export() + { + List tableInfoList = Db.DbMaintenance.GetTableInfoList(false); + foreach (DbTableInfo tableInfo in tableInfoList) + { + if (UsedTable.Contains(tableInfo.Name)) + { + Log.Info($"{tableInfo.Name}:{tableInfo.Description}"); + + List columnInfos = Db.DbMaintenance.GetColumnInfosByTableName(tableInfo.Name, false); + foreach (DbColumnInfo columnInfo in columnInfos) + { + Log.Info($" {columnInfo.DbColumnName}:{columnInfo.ColumnDescription}"); + } + } + } + } +} \ No newline at end of file diff --git a/ConsoleStarter/Program.cs b/ConsoleStarter/Program.cs index 0b92eeb..689e389 100644 --- a/ConsoleStarter/Program.cs +++ b/ConsoleStarter/Program.cs @@ -1,49 +1,23 @@ // See https://aka.ms/new-console-template for more information using System.Diagnostics; -using System.IO.Pipes; -using HslCommunication.Core; +using ConsoleStarter; using HybirdFrameworkCore.Autofac.Attribute; using HybirdFrameworkCore.Utils; using HybirdFrameworkDriver.ModbusTcpMaster; +using log4net.Config; internal class Program { public static void Main(string[] args) { - ModbusTcpMaster master = new ModbusTcpMaster() - { - Ip = "192.168.1.5", - //默认DataFormat.ABCD - //DataFormat = DataFormat.ABCD, - ReadAction = ReadFunc - }; - - bool connected = master.Connect(); - Debug.Assert(connected, "连接modbus server 失败"); - - #region 测试写 - WaterCoolData coolData = new WaterCoolData(); - - //写入ushort - coolData.ushortType.Value = 18; - bool writeResult4 = master.WriteValue(coolData.ushortType); - Debug.Assert(writeResult4, "写入失败"); - //写入byte 0-8 byte[1] - coolData.Status.Value = 12; - bool writeResult1 = master.WriteValue(coolData.Status); - Debug.Assert(writeResult1, "写入失败"); - //写入bit - coolData.DraughtFan1.Value = true; - bool writeResult3 = master.WriteValue(coolData.DraughtFan1); - Debug.Assert(writeResult3, "写入失败"); + XmlConfigurator.ConfigureAndWatch(new FileInfo(AppDomain.CurrentDomain.BaseDirectory + @"\log4net.xml")); + ExportDb exportDb = new ExportDb(); + exportDb.Export(); + } - coolData.Temperature1.Value = 12.7f; - bool writeResult5 = master.WriteValue(coolData.Temperature1); - Debug.Assert(writeResult5, "写入失败"); + #region test - #endregion 测试写 - } private static WaterCoolData coolData = new WaterCoolData(); private static void ReadFunc(ModbusTcpMaster master) { @@ -156,6 +130,8 @@ internal class Program Console.WriteLine(BitUtls.BytesToHexStr(bytes)); } + + #endregion } public struct EncodeData diff --git a/HybirdFrameworkCore/Autofac/AutofacModuleRegister.cs b/HybirdFrameworkCore/Autofac/AutofacModuleRegister.cs index 52e89c1..60a7306 100644 --- a/HybirdFrameworkCore/Autofac/AutofacModuleRegister.cs +++ b/HybirdFrameworkCore/Autofac/AutofacModuleRegister.cs @@ -8,7 +8,7 @@ namespace HybirdFrameworkCore.Autofac { public class AutofacModuleRegister : Module { - private readonly ILog Log = LogManager.GetLogger(typeof(AutofacModuleRegister)); + private static readonly ILog Log = LogManager.GetLogger(typeof(AutofacModuleRegister)); protected override void Load(ContainerBuilder builder) { @@ -68,7 +68,7 @@ namespace HybirdFrameworkCore.Autofac if (defaultList.Count > 0) { - builder.RegisterTypes(defaultList.ToArray()).SingleInstance() + builder.RegisterTypes(defaultList.ToArray()).AsSelf().SingleInstance() .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); //支持属性注入依赖重复 } @@ -83,39 +83,27 @@ namespace HybirdFrameworkCore.Autofac } else { - Type? iInterface = type.GetInterface("HybirdFrameworkDriver.TcpServer.IMsgHandler"); - if (iInterface != null) - { - builder.RegisterType(type).As(iInterface).InstancePerDependency() + builder.RegisterType(type).InstancePerDependency() .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); } + } + } - iInterface = type.GetInterface("HybirdFrameworkDriver.TcpServer.IDecoder"); - if (iInterface != null) + if (instancePerLifetimeScopeList.Count > 0) { - builder.RegisterType(type).As(iInterface).InstancePerDependency() - .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); + builder.RegisterTypes(instancePerLifetimeScopeList.ToArray()).InstancePerMatchingLifetimeScope() + .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); //支持属性注入依赖重复 + } } - iInterface = type.GetInterface("HybirdFrameworkDriver.TcpServer.IEncoder"); + private void RegisterToInterface(String interfaceName, Type type, ContainerBuilder builder) + { + Type? iInterface = type.GetInterface(interfaceName); if (iInterface != null) { builder.RegisterType(type).As(iInterface).InstancePerDependency() .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); } - - - builder.RegisterType(type).InstancePerDependency() - .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); - } - } - } - - if (instancePerLifetimeScopeList.Count > 0) - { - builder.RegisterTypes(instancePerLifetimeScopeList.ToArray()).InstancePerMatchingLifetimeScope() - .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); //支持属性注入依赖重复 - } } } } \ No newline at end of file diff --git a/HybirdFrameworkDriver/Common/IToBytes.cs b/HybirdFrameworkDriver/Common/IToBytes.cs new file mode 100644 index 0000000..8d13ce3 --- /dev/null +++ b/HybirdFrameworkDriver/Common/IToBytes.cs @@ -0,0 +1,6 @@ +namespace HybirdFrameworkDriver.Common; + +public interface IToBytes +{ + byte[] ToBytes(); +} \ No newline at end of file diff --git a/HybirdFrameworkDriver/ModbusTcpMaster/ModbusTcpMaster.cs b/HybirdFrameworkDriver/ModbusTcpMaster/ModbusTcpMaster.cs index ef60ba5..cacd12e 100644 --- a/HybirdFrameworkDriver/ModbusTcpMaster/ModbusTcpMaster.cs +++ b/HybirdFrameworkDriver/ModbusTcpMaster/ModbusTcpMaster.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections; -using HslCommunication; +using HslCommunication; using HslCommunication.Core; using HslCommunication.ModBus; using HybirdFrameworkCore.Utils; @@ -12,8 +10,8 @@ public class ModbusTcpMaster { private static readonly ILog Log = LogManager.GetLogger(typeof(ModbusTcpMaster)); - // public string Ip { get; set; } = "127.0.0.1"; - public string Ip { get; set; } = "192.168.1.5"; + public string Ip { get; set; } = "127.0.0.1"; + public int Port { get; set; } = 502; public static DataFormat DataFormat { get; set; } = DataFormat.ABCD; @@ -21,6 +19,8 @@ public class ModbusTcpMaster public int Duration { get; set; } = 1000; public bool Connected { get; set; } = false; + public string connectId { get; set; } + public delegate void MyReadAction(ModbusTcpMaster str); public MyReadAction? ReadAction { get; set; } @@ -29,7 +29,6 @@ public class ModbusTcpMaster private ModbusTcpNet ModbusTcpNet; - ILog GetLog() { return Log; @@ -46,6 +45,7 @@ public class ModbusTcpMaster ModbusTcpNet = new ModbusTcpNet(Ip, Port); ModbusTcpNet.DataFormat = DataFormat; OperateResult result = ModbusTcpNet.ConnectServer(); + connectId = ModbusTcpNet.ConnectionId; if (result.IsSuccess) { Connected = true; @@ -185,15 +185,16 @@ public class ModbusTcpMaster else preWriteCont[i - 1] = setValue[i]; } + break; case DataFormat.BADC: Array.Copy(setValue, preWriteCont, setValue.Length); break; } + operateResult = ModbusTcpNet.Write("x=16;" + (registerNo), preWriteCont); result = operateResult.IsSuccess; } - } else if (setValue.Length == length * 2) { @@ -209,21 +210,26 @@ public class ModbusTcpMaster else preWriteCont[i - 1] = setValue[i]; } + break; case DataFormat.BADC: //Array.Copy(setValue, preWriteCont, setValue.Length); break; } + operateResult = ModbusTcpNet.Write("x=16;" + (registerNo), preWriteCont); result = operateResult.IsSuccess; } + break; case ModbusDataType.Bit: result = WriteRegisterOneBit(registerNo, start, Convert.ToBoolean(value)); break; } + return result; } + /// /// 写寄存器中的一个bit /// @@ -253,6 +259,7 @@ public class ModbusTcpMaster mask = (ushort)(~(1 << location)); registerValue1 &= mask; } + OperateResult writeResult = ModbusTcpNet.Write("x=6;" + addr, registerValue1); if (writeResult.IsSuccess) { @@ -260,7 +267,7 @@ public class ModbusTcpMaster } } } + return result; } - } \ No newline at end of file diff --git a/HybirdFrameworkDriver/TcpServer/ChannelUtils.cs b/HybirdFrameworkDriver/Session/ChannelUtils.cs similarity index 94% rename from HybirdFrameworkDriver/TcpServer/ChannelUtils.cs rename to HybirdFrameworkDriver/Session/ChannelUtils.cs index 0bdc145..d228136 100644 --- a/HybirdFrameworkDriver/TcpServer/ChannelUtils.cs +++ b/HybirdFrameworkDriver/Session/ChannelUtils.cs @@ -1,7 +1,7 @@ using DotNetty.Common.Utilities; using DotNetty.Transport.Channels; -namespace HybirdFrameworkDriver.TcpServer; +namespace HybirdFrameworkDriver.Session; public static class ChannelUtils { diff --git a/HybirdFrameworkDriver/TcpServer/IoSession.cs b/HybirdFrameworkDriver/Session/IoSession.cs similarity index 61% rename from HybirdFrameworkDriver/TcpServer/IoSession.cs rename to HybirdFrameworkDriver/Session/IoSession.cs index ef2a2d8..2cd6edb 100644 --- a/HybirdFrameworkDriver/TcpServer/IoSession.cs +++ b/HybirdFrameworkDriver/Session/IoSession.cs @@ -1,25 +1,30 @@ -using DotNetty.Buffers; +using System.Collections.Concurrent; +using DotNetty.Buffers; using DotNetty.Transport.Channels; using log4net; -namespace HybirdFrameworkDriver.TcpServer; - +namespace HybirdFrameworkDriver.Session; public class IoSession { - private readonly ILog Log = LogManager.GetLogger(typeof(IoSession)); + private static readonly ILog Log = LogManager.GetLogger(typeof(IoSession)); - private IChannel Channel { get; } + public IChannel Channel { get; } private String IpAddr { get; } public String Key { get; set; } + private bool Reconnected { get; set; } + //业务数据 + public ConcurrentDictionary BusinessMap { get; } = new ConcurrentDictionary(); + public IoSession(IChannel channel) { this.Channel = channel; this.IpAddr = ChannelUtils.GetIp(channel); } + public void Send(IByteBuffer buffer) { Channel.WriteAndFlushAsync(buffer); diff --git a/HybirdFrameworkDriver/Session/ModbusSession.cs b/HybirdFrameworkDriver/Session/ModbusSession.cs new file mode 100644 index 0000000..3c39c70 --- /dev/null +++ b/HybirdFrameworkDriver/Session/ModbusSession.cs @@ -0,0 +1,37 @@ +using System.Collections.Concurrent; +using DotNetty.Buffers; +using DotNetty.Transport.Channels; +using HybirdFrameworkDriver.ModbusTcpMaster; +using log4net; + +namespace HybirdFrameworkDriver.Session; + + +public class ModbusSession +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(ModbusSession)); + + public ModbusTcpMaster.ModbusTcpMaster ModbusTcpMaster; + private String IpAddr { get; } + public String Key { get; set; } + + public ConcurrentDictionary BusinessMap { get;set; } + public ModbusSession(ModbusTcpMaster.ModbusTcpMaster modbusTcpMaster) + { + this.ModbusTcpMaster = modbusTcpMaster; + this.IpAddr = modbusTcpMaster.Ip; + this.Key = modbusTcpMaster.connectId; + } + + + public bool Write(ModbusProperty property) + { + return ModbusTcpMaster.WriteValue(property); + } + public byte[]? Read(int registerNo, int length) + { + return ModbusTcpMaster.BatchRead(registerNo,length); + } + + +} \ No newline at end of file diff --git a/HybirdFrameworkDriver/Session/SessionMgr.cs b/HybirdFrameworkDriver/Session/SessionMgr.cs new file mode 100644 index 0000000..b256b32 --- /dev/null +++ b/HybirdFrameworkDriver/Session/SessionMgr.cs @@ -0,0 +1,154 @@ +using System.Collections.Concurrent; +using DotNetty.Buffers; +using DotNetty.Transport.Channels; +using log4net; + +namespace HybirdFrameworkDriver.Session; + +public class SessionMgr +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(SessionMgr)); + + private static readonly ConcurrentDictionary Dictionary = + new ConcurrentDictionary(); + + private static readonly ConcurrentDictionary ModbusDictionary = + new ConcurrentDictionary(); + + public ConcurrentDictionary? BusinessMap { get; set; } + + public static IoSession? GetSession(string key) + { + if (Dictionary.ContainsKey(key)) + { + IoSession? value; + Dictionary.TryGetValue(key, out value); + + return value; + } + + return null; + } + + public static List GetSessionList() + { + return Dictionary.Values.ToList(); + } + + public static ModbusSession GetModbusSession(string key) + { + if (ModbusDictionary.ContainsKey(key)) + { + ModbusSession value; + ModbusDictionary.TryGetValue(key, out value); + + return value; + } + + return null; + } + + public static void RegisterSession(IChannel channel, IoSession ioSession) + { + var oldKey = ioSession.Key; + if (oldKey != null) + { + IoSession? session; + Dictionary.Remove(oldKey, out session); + } + + ChannelUtils.AddChannelSession(channel, ioSession); + ioSession.Key = channel.Id.ToString(); + Dictionary.AddOrUpdate(channel.Id.ToString(), ioSession, (k, oldSession) => ioSession); + } + + + public static void RegisterModbusSession(string key, ModbusSession ioSession) + { + var oldKey = ioSession.Key; + if (oldKey != null) + { + ModbusSession? session; + ModbusDictionary.Remove(oldKey, out session); + } + + ioSession.Key = key; + ModbusDictionary.AddOrUpdate(key, ioSession, (k, oldSession) => ioSession); + } + + public static void UnregisterSession(IChannel channel) + { + IoSession session = ChannelUtils.GetSessionBy(channel); + IoSession? outSession; + Dictionary.Remove(session.Key, out outSession); + session.Close(); + } + + + public static void Broadcast(IByteBuffer buffer, ConcurrentDictionary dictionary) + { + foreach (IoSession session in dictionary.Values) + { + session.Send(buffer); + } + } + + public static Object GetAttr(IoSession session, String key) + { + Object? obj; + session.BusinessMap.TryGetValue(key, out obj); + return obj; + } + + public static void SetAttr(IoSession session, String key, Object obj) + { + session.BusinessMap.TryAdd(key, obj); + } + + public static Object GetAttrByKey(String? key, String mapKey) + { + if (!string.IsNullOrEmpty(key)) + { + Dictionary.TryGetValue(key, out IoSession? session); + + if (session != null) + { + session.BusinessMap.TryGetValue(mapKey, out Object? obj); + + if (obj != null) + { + return (int)obj; + } + } + } + + return 0; + } + + public static void SetAttrModbus(ModbusSession session, String key, Object obj) + { + if (session.BusinessMap == null) + { + session.BusinessMap = new ConcurrentDictionary(); + } + + session.BusinessMap.TryAdd(key, obj); + } + + public static Object GetAttrModbus(String key, String mapKey) + { + ModbusDictionary.TryGetValue(key, out ModbusSession? session); + + if (session != null) + { + session.BusinessMap.TryGetValue(mapKey, out Object? obj); + + if (obj != null) + { + return (int)obj; + } + } + + return 0; + } +} \ No newline at end of file diff --git a/HybirdFrameworkDriver/TcpClient/ClientListenerHandler.cs b/HybirdFrameworkDriver/TcpClient/ClientListenerHandler.cs new file mode 100644 index 0000000..7a934aa --- /dev/null +++ b/HybirdFrameworkDriver/TcpClient/ClientListenerHandler.cs @@ -0,0 +1,73 @@ +using System.Net; +using Autofac; +using DotNetty.Codecs; +using DotNetty.Handlers.Timeout; +using DotNetty.Transport.Channels; +using HybirdFrameworkCore.Autofac; +using HybirdFrameworkDriver.Session; +using log4net; + +namespace HybirdFrameworkDriver.TcpClient; + +public class ClientListenerHandler : ChannelHandlerAdapter where TH : IChannelHandler + where TD : ByteToMessageDecoder, new() + where TE : ChannelHandlerAdapter, new() +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(ClientListenerHandler)); + + public override void ChannelRegistered(IChannelHandlerContext context) + { + base.ChannelRegistered(context); + Log.Info("register " + context.Channel); + } + + public override void ChannelUnregistered(IChannelHandlerContext context) + { + base.ChannelUnregistered(context); + Log.Info("unregister " + context.Channel); + } + + public override void ChannelActive(IChannelHandlerContext context) + { + base.ChannelActive(context); + SessionMgr.RegisterSession(context.Channel, new IoSession(context.Channel)); + Log.Info("active " + context.Channel); + } + + public override void ChannelInactive(IChannelHandlerContext context) + { + base.ChannelInactive(context); + var ioSession = SessionMgr.GetSession(context.Channel.Id.ToString()); + SessionMgr.UnregisterSession(context.Channel); + Log.Info("inactive " + context.Channel); + //处理重连 + + TcpClient tcpClient = AppInfo.Container.Resolve>(); + IPEndPoint channelRemoteAddress = (IPEndPoint)ioSession.Channel.RemoteAddress; + tcpClient.InitBootstrap(channelRemoteAddress.Address.ToString(), channelRemoteAddress.Port); + tcpClient.Connect(); + } + + public override void UserEventTriggered(IChannelHandlerContext context, object evt) + { + if (evt is IdleStateEvent) + { + if (context.Channel.Open) + { + context.Channel.CloseAsync(); + context.Channel.CloseCompletion.Wait(); + Log.Info($"channel {context.Channel.Id} timeout close"); + } + } + + base.UserEventTriggered(context, evt); + Log.Info("UserEventTriggered " + context.Channel); + } + + public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) + { + base.ExceptionCaught(context, exception); + context.Channel.CloseAsync(); + Log.Info("exception " + context.Channel); + } +} \ No newline at end of file diff --git a/HybirdFrameworkDriver/TcpClient/TcpClient.cs b/HybirdFrameworkDriver/TcpClient/TcpClient.cs new file mode 100644 index 0000000..08820da --- /dev/null +++ b/HybirdFrameworkDriver/TcpClient/TcpClient.cs @@ -0,0 +1,148 @@ +using System.Net; +using System.Reflection; +using Autofac; +using Autofac.Core; +using DotNetty.Codecs; +using DotNetty.Handlers.Timeout; +using DotNetty.Transport.Bootstrapping; +using DotNetty.Transport.Channels; +using DotNetty.Transport.Channels.Sockets; +using HybirdFrameworkCore.Autofac; +using HybirdFrameworkCore.Autofac.Attribute; +using log4net; + +namespace HybirdFrameworkDriver.TcpClient; + +public class TcpClient where TH : IChannelHandler where TD: ByteToMessageDecoder,new() where TE: ChannelHandlerAdapter, new() +{ + private Bootstrap? _bootstrap; + + public IChannel Channel { get; set; } + + public bool Connected { get; set; } = false; + + public string Host { get; set; } + public int Port { get; set; } + + private static readonly ILog Log = LogManager.GetLogger(typeof(TcpClient)); + + public void InitBootstrap(string host, int port) + { + Host = host; + Port = port; + _bootstrap = new Bootstrap(); + _bootstrap + .Group(new MultithreadEventLoopGroup()) + .Channel() + .Option(ChannelOption.TcpNodelay, true) + .Handler(new ActionChannelInitializer(channel => + { + var clientListenerHandler = new ClientListenerHandler(); + + IChannelPipeline pipeline = channel.Pipeline; + // 监听器 + pipeline.AddLast(clientListenerHandler); + pipeline.AddLast("idleStateHandler", new IdleStateHandler(30, 0, 0)); // 触发读取超时 + + + // 可以添加编解码器等 + ResolveDecode(pipeline); + ResolveEncode(pipeline); + + ResolveHandler(pipeline); + })); + } + + + private void ResolveEncode(IChannelPipeline pipeline) + { + pipeline.AddLast(new TE()); + } + + private void ResolveDecode(IChannelPipeline pipeline) + { + pipeline.AddLast(new TD()); + } + + private void ResolveHandler(IChannelPipeline pipeline) + { + + List list = new List(); + + foreach (IComponentRegistration reg in AppInfo.Container.ComponentRegistry.Registrations) + { + foreach (Service service in reg.Services) + { + if (service is TypedService ts) + { + if (MatchHandlers(ts)) + { + list.Add(ts.ServiceType); + } + } + } + } + + List handlers = new List(); + foreach (var type in list) + { + object resolve = AppInfo.Container.Resolve(type); + handlers.Add((TH) resolve); + } + + handlers.Sort((handler, msgHandler) => + { + OrderAttribute? orderAttribute1 = handler.GetType().GetCustomAttribute(); + OrderAttribute? orderAttribute2 = msgHandler.GetType().GetCustomAttribute(); + int h1Order = orderAttribute1?.Order ?? 0; + int h2Order = orderAttribute2?.Order ?? 0; + return h1Order.CompareTo(h2Order); + }); + foreach (var msgHandler in handlers) + { + pipeline.AddLast((IChannelHandler)msgHandler); + } + } + + private bool MatchHandlers(TypedService ts) + { + Type[] interfaces = ts.ServiceType.GetInterfaces(); + if (interfaces.Length > 0) + { + foreach (Type type in interfaces) + { + if (type == typeof(TH)) + { + return true; + } + } + } + + return false; + } + + + public void Connect() + { + Connected = false; + int num = 1; + while (!Connected) + { + Task task = _bootstrap!.ConnectAsync(new IPEndPoint(IPAddress.Parse(Host), Port)); + + Channel = task.Result; + Connected = Channel.Open; + + if (Connected) + { + break; + } + + Thread.Sleep(5000); + num++; + } + + } + + +} \ No newline at end of file diff --git a/HybirdFrameworkDriver/TcpServer/IDecoder.cs b/HybirdFrameworkDriver/TcpServer/IDecoder.cs deleted file mode 100644 index 65c6d68..0000000 --- a/HybirdFrameworkDriver/TcpServer/IDecoder.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace HybirdFrameworkDriver.TcpServer; - -public interface IDecoder -{ -} \ No newline at end of file diff --git a/HybirdFrameworkDriver/TcpServer/IEncoder.cs b/HybirdFrameworkDriver/TcpServer/IEncoder.cs deleted file mode 100644 index 9cbfde5..0000000 --- a/HybirdFrameworkDriver/TcpServer/IEncoder.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace HybirdFrameworkDriver.TcpServer; - -public interface IEncoder -{ - -} \ No newline at end of file diff --git a/HybirdFrameworkDriver/TcpServer/IMsgHandler.cs b/HybirdFrameworkDriver/TcpServer/IMsgHandler.cs deleted file mode 100644 index ef24d8b..0000000 --- a/HybirdFrameworkDriver/TcpServer/IMsgHandler.cs +++ /dev/null @@ -1,7 +0,0 @@ -using DotNetty.Transport.Channels; - -namespace HybirdFrameworkDriver.TcpServer; - -public interface IMsgHandler -{ -} \ No newline at end of file diff --git a/HybirdFrameworkDriver/TcpServer/ServerListenerHandler.cs b/HybirdFrameworkDriver/TcpServer/ServerListenerHandler.cs index 82d6f06..4fb6914 100644 --- a/HybirdFrameworkDriver/TcpServer/ServerListenerHandler.cs +++ b/HybirdFrameworkDriver/TcpServer/ServerListenerHandler.cs @@ -1,14 +1,18 @@ -using DotNetty.Transport.Channels; +using DotNetty.Codecs; +using DotNetty.Transport.Channels; using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkDriver.Session; using log4net; namespace HybirdFrameworkDriver.TcpServer { [Scope("InstancePerDependency")] - public class ServerListenerHandler : ChannelHandlerAdapter + public class ServerListenerHandler : ChannelHandlerAdapter where TH : IChannelHandler + where TD : ByteToMessageDecoder, new() + where TE : ChannelHandlerAdapter, new() { - private readonly ILog Log = LogManager.GetLogger(typeof(ServerListenerHandler)); + private static readonly ILog Log = LogManager.GetLogger(typeof(ServerListenerHandler)); public override void ChannelRegistered(IChannelHandlerContext context) { diff --git a/HybirdFrameworkDriver/TcpServer/SessionMgr.cs b/HybirdFrameworkDriver/TcpServer/SessionMgr.cs deleted file mode 100644 index 6bf8b0e..0000000 --- a/HybirdFrameworkDriver/TcpServer/SessionMgr.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Concurrent; -using DotNetty.Buffers; -using DotNetty.Transport.Channels; -using log4net; - -namespace HybirdFrameworkDriver.TcpServer; - -public class SessionMgr -{ - private static readonly ILog Log = LogManager.GetLogger(typeof(SessionMgr)); - - private static readonly ConcurrentDictionary Dictionary = - new ConcurrentDictionary(); - - public static void RegisterSession(IChannel channel, IoSession ioSession) - { - ioSession.Key = channel.Id.ToString(); - IoSession? session; - Dictionary.Remove(ioSession.Key, out session); - ChannelUtils.AddChannelSession(channel, ioSession); - Dictionary.AddOrUpdate(channel.Id.ToString(), ioSession, (k, oldSession) => ioSession); - } - - public static void UnregisterSession(IChannel channel) - { - IoSession session = ChannelUtils.GetSessionBy(channel); - IoSession? outSession; - Dictionary.Remove(session.Key, out outSession); - session.Close(); - } - - public static void Broadcast(IByteBuffer buffer) - { - foreach (IoSession session in Dictionary.Values) - { - session.Send(buffer); - } - } -} \ No newline at end of file diff --git a/HybirdFrameworkDriver/TcpServer/Server.cs b/HybirdFrameworkDriver/TcpServer/TcpServer.cs similarity index 62% rename from HybirdFrameworkDriver/TcpServer/Server.cs rename to HybirdFrameworkDriver/TcpServer/TcpServer.cs index 35a83b2..9e83895 100644 --- a/HybirdFrameworkDriver/TcpServer/Server.cs +++ b/HybirdFrameworkDriver/TcpServer/TcpServer.cs @@ -1,5 +1,6 @@ using System.Reflection; using Autofac; +using Autofac.Core; using DotNetty.Codecs; using DotNetty.Handlers.Logging; using DotNetty.Handlers.Timeout; @@ -15,10 +16,9 @@ namespace HybirdFrameworkDriver.TcpServer /// /// netty server /// - [Scope("InstancePerDependency")] - public class Server : IDisposable + public class TcpServer : IDisposable where TH : IChannelHandler where TD: ByteToMessageDecoder,new() where TE: ChannelHandlerAdapter, new() { - private readonly ILog Log = LogManager.GetLogger(typeof(Server)); + private static readonly ILog Log = LogManager.GetLogger(typeof(TcpServer)); static MultithreadEventLoopGroup? bossGroup; static MultithreadEventLoopGroup? workerGroup; @@ -26,7 +26,7 @@ namespace HybirdFrameworkDriver.TcpServer private int _port = 9000; - public Server() + public TcpServer() { bossGroup = new MultithreadEventLoopGroup(); workerGroup = new MultithreadEventLoopGroup(); @@ -38,7 +38,7 @@ namespace HybirdFrameworkDriver.TcpServer .Handler(new LoggingHandler()) .ChildHandler(new ActionChannelInitializer(channel => { - var serverListenerHandler = new ServerListenerHandler(); + var serverListenerHandler = new ServerListenerHandler(); IChannelPipeline pipeline = channel.Pipeline; pipeline.AddLast(new LoggingHandler("")); pipeline.AddLast(serverListenerHandler); @@ -53,21 +53,40 @@ namespace HybirdFrameworkDriver.TcpServer private void ResolveEncode(IChannelPipeline pipeline) { - IEncoder resolve = AppInfo.Container.Resolve(); - pipeline.AddLast((MessageToByteEncoder)resolve); + pipeline.AddLast(new TE()); } private void ResolveDecode(IChannelPipeline pipeline) { - IDecoder resolve = AppInfo.Container.Resolve(); - pipeline.AddLast((ByteToMessageDecoder)resolve); + pipeline.AddLast(new TD()); } private void ResolveHandler(IChannelPipeline pipeline) { - IEnumerable handlers = AppInfo.Container.Resolve>(); - IMsgHandler[] msgHandlers = handlers.ToArray(); - Array.Sort(msgHandlers, (handler, msgHandler) => + List list = new List(); + + foreach (IComponentRegistration reg in AppInfo.Container.ComponentRegistry.Registrations) + { + foreach (Service service in reg.Services) + { + if (service is TypedService ts) + { + if (MatchHandlers(ts)) + { + list.Add(ts.ServiceType); + } + } + } + } + + List handlers = new List(); + foreach (var type in list) + { + object resolve = AppInfo.Container.Resolve(type); + handlers.Add((TH) resolve); + } + + handlers.Sort((handler, msgHandler) => { OrderAttribute? orderAttribute1 = handler.GetType().GetCustomAttribute(); OrderAttribute? orderAttribute2 = msgHandler.GetType().GetCustomAttribute(); @@ -75,13 +94,28 @@ namespace HybirdFrameworkDriver.TcpServer int h2Order = orderAttribute2?.Order ?? 0; return h1Order.CompareTo(h2Order); }); - foreach (var msgHandler in msgHandlers) + foreach (var msgHandler in handlers) { - pipeline.AddLast((ChannelHandlerAdapter)msgHandler); + pipeline.AddLast((IChannelHandler)msgHandler); } } + + private bool MatchHandlers(TypedService ts) + { + Type[] interfaces = ts.ServiceType.GetInterfaces(); + if (interfaces.Length > 0) + { + foreach (Type type in interfaces) + { + if (type == typeof(TH)) + { + return true; + } + } + } - + return false; + } public void Start(int port) { diff --git a/HybirdFrameworkRepository/bin/Debug/net6.0/HybirdFrameworkRepository.deps.json b/HybirdFrameworkRepository/bin/Debug/net6.0/HybirdFrameworkRepository.deps.json index e543d05..3377398 100644 --- a/HybirdFrameworkRepository/bin/Debug/net6.0/HybirdFrameworkRepository.deps.json +++ b/HybirdFrameworkRepository/bin/Debug/net6.0/HybirdFrameworkRepository.deps.json @@ -208,6 +208,14 @@ } } }, + "Microsoft.Extensions.Logging.Abstractions/6.0.0": { + "runtime": { + "lib/net6.0/Microsoft.Extensions.Logging.Abstractions.dll": { + "assemblyVersion": "6.0.0.0", + "fileVersion": "6.0.21.52210" + } + } + }, "Microsoft.Extensions.Primitives/7.0.0": { "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -347,6 +355,17 @@ } } }, + "Pipelines.Sockets.Unofficial/2.2.8": { + "dependencies": { + "System.IO.Pipelines": "5.0.1" + }, + "runtime": { + "lib/net5.0/Pipelines.Sockets.Unofficial.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "2.2.8.1080" + } + } + }, "SQLitePCLRaw.bundle_e_sqlite3/2.1.4": { "dependencies": { "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", @@ -551,6 +570,18 @@ } } }, + "StackExchange.Redis/2.7.33": { + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + }, + "runtime": { + "lib/net6.0/StackExchange.Redis.dll": { + "assemblyVersion": "2.0.0.0", + "fileVersion": "2.7.33.41805" + } + } + }, "System.Collections/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "5.0.0", @@ -703,6 +734,14 @@ "System.Threading.Tasks": "4.3.0" } }, + "System.IO.Pipelines/5.0.1": { + "runtime": { + "lib/netcoreapp3.0/System.IO.Pipelines.dll": { + "assemblyVersion": "5.0.0.1", + "fileVersion": "5.0.120.57516" + } + } + }, "System.Memory/4.5.3": {}, "System.Reflection/4.3.0": { "dependencies": { @@ -895,6 +934,7 @@ "Microsoft.Extensions.Configuration.Abstractions": "7.0.0", "Microsoft.Extensions.Configuration.Json": "7.0.0", "Newtonsoft.Json": "13.0.3", + "StackExchange.Redis": "2.7.33", "log4net": "2.0.15" }, "runtime": { @@ -1022,6 +1062,13 @@ "path": "microsoft.extensions.filesystemglobbing/7.0.0", "hashPath": "microsoft.extensions.filesystemglobbing.7.0.0.nupkg.sha512" }, + "Microsoft.Extensions.Logging.Abstractions/6.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==", + "path": "microsoft.extensions.logging.abstractions/6.0.0", + "hashPath": "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512" + }, "Microsoft.Extensions.Primitives/7.0.0": { "type": "package", "serviceable": true, @@ -1127,6 +1174,13 @@ "path": "oracle.manageddataaccess.core/3.21.100", "hashPath": "oracle.manageddataaccess.core.3.21.100.nupkg.sha512" }, + "Pipelines.Sockets.Unofficial/2.2.8": { + "type": "package", + "serviceable": true, + "sha512": "sha512-zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", + "path": "pipelines.sockets.unofficial/2.2.8", + "hashPath": "pipelines.sockets.unofficial.2.2.8.nupkg.sha512" + }, "SQLitePCLRaw.bundle_e_sqlite3/2.1.4": { "type": "package", "serviceable": true, @@ -1183,6 +1237,13 @@ "path": "sqlsugarcore.kdbndp/7.4.0", "hashPath": "sqlsugarcore.kdbndp.7.4.0.nupkg.sha512" }, + "StackExchange.Redis/2.7.33": { + "type": "package", + "serviceable": true, + "sha512": "sha512-2kCX5fvhEE824a4Ab5Imyi8DRuGuTxyklXV01kegkRpsWJcPmO6+GAQ+HegKxvXAxlXZ8yaRspvWJ8t3mMClfQ==", + "path": "stackexchange.redis/2.7.33", + "hashPath": "stackexchange.redis.2.7.33.nupkg.sha512" + }, "System.Collections/4.3.0": { "type": "package", "serviceable": true, @@ -1260,6 +1321,13 @@ "path": "system.io/4.3.0", "hashPath": "system.io.4.3.0.nupkg.sha512" }, + "System.IO.Pipelines/5.0.1": { + "type": "package", + "serviceable": true, + "sha512": "sha512-qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==", + "path": "system.io.pipelines/5.0.1", + "hashPath": "system.io.pipelines.5.0.1.nupkg.sha512" + }, "System.Memory/4.5.3": { "type": "package", "serviceable": true, diff --git a/HybirdFrameworkServices/Charger/Client/ChargerClient.cs b/HybirdFrameworkServices/Charger/Client/ChargerClient.cs new file mode 100644 index 0000000..0de0f3d --- /dev/null +++ b/HybirdFrameworkServices/Charger/Client/ChargerClient.cs @@ -0,0 +1,29 @@ +using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkDriver.Session; +using HybirdFrameworkDriver.TcpClient; +using HybirdFrameworkServices.Charger.Codec; +using HybirdFrameworkServices.Charger.Handler; + +namespace HybirdFrameworkServices.Charger.Client; + +/// +/// 示例程序 +/// +[Scope("InstancePerDependency")] +public class ChargerClient : TcpClient +{ + public void SessionAttr(int sn, int fEqmTypeNo, string eqmCode, string destAddr) + { + IoSession? ioSession = SessionMgr.GetSession(this.Channel.Id.ToString()); + if (ioSession == null) + { + ioSession = new IoSession(this.Channel); + SessionMgr.RegisterSession(this.Channel, ioSession); + } + + SessionMgr.SetAttr(ioSession, "charger_sn", sn); + SessionMgr.SetAttr(ioSession, "eqm_type_no", fEqmTypeNo); + SessionMgr.SetAttr(ioSession, "eqm_code", eqmCode); + SessionMgr.SetAttr(ioSession, "dest_addr", destAddr); + } +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Client/ChargerServer.cs b/HybirdFrameworkServices/Charger/Client/ChargerServer.cs new file mode 100644 index 0000000..66bf615 --- /dev/null +++ b/HybirdFrameworkServices/Charger/Client/ChargerServer.cs @@ -0,0 +1,14 @@ +using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkDriver.TcpServer; +using HybirdFrameworkServices.Charger.Codec; +using HybirdFrameworkServices.Charger.Handler; + +namespace HybirdFrameworkServices.Charger.Client; + +/// +/// 示例程序 +/// +[Scope("InstancePerDependency")] +public class ChargerServer : TcpServer +{ +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Client/ClientMgr.cs b/HybirdFrameworkServices/Charger/Client/ClientMgr.cs new file mode 100644 index 0000000..dcd493f --- /dev/null +++ b/HybirdFrameworkServices/Charger/Client/ClientMgr.cs @@ -0,0 +1,29 @@ +using System.Text; +using Autofac; +using HybirdFrameworkCore.Autofac; +using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkServices.Charger.Msg.Host.Req; + +namespace HybirdFrameworkServices.Charger.Client; + +/// +/// 示例程序 +/// +[Scope("SingleInstance")] +public class ClientMgr +{ + public void InitClient() + { + Auth auth = new Auth(1, Encoding.UTF8.GetBytes("ddddddddd"), 1); + auth.DestAddr = new byte[] { 0x01, 0x02, 0x03 }; + + ChargerClient chargerClient2 = AppInfo.Container.Resolve(); + chargerClient2.InitBootstrap("127.0.0.1", 9998); + chargerClient2.Connect(); + chargerClient2.SessionAttr(12, 12, "12", "2"); + chargerClient2.Channel.WriteAndFlushAsync(auth); + + ChargerServer chargerServer = AppInfo.Container.Resolve(); + chargerServer.Start(9000); + } +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Codec/Decoder.cs b/HybirdFrameworkServices/Charger/Codec/Decoder.cs index ee1e1ea..bb230cc 100644 --- a/HybirdFrameworkServices/Charger/Codec/Decoder.cs +++ b/HybirdFrameworkServices/Charger/Codec/Decoder.cs @@ -1,16 +1,14 @@ using DotNetty.Buffers; using DotNetty.Codecs; using DotNetty.Transport.Channels; -using HybirdFrameworkCore.Autofac.Attribute; -using HybirdFrameworkDriver.TcpServer; +using HybirdFrameworkServices.Charger.Msg.Charger.Req; namespace HybirdFrameworkServices.Charger.Codec; -[Scope("InstancePerDependency")] -public class Decoder : ByteToMessageDecoder, IDecoder +public class Decoder : ByteToMessageDecoder { protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List output) { - output.Add(new IoSession(context.Channel)); + output.Add(new Login()); } } \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Codec/Encoder.cs b/HybirdFrameworkServices/Charger/Codec/Encoder.cs index a59d3b2..9aa7817 100644 --- a/HybirdFrameworkServices/Charger/Codec/Encoder.cs +++ b/HybirdFrameworkServices/Charger/Codec/Encoder.cs @@ -1,16 +1,14 @@ using DotNetty.Buffers; using DotNetty.Codecs; using DotNetty.Transport.Channels; -using HybirdFrameworkCore.Autofac.Attribute; -using HybirdFrameworkDriver.TcpServer; +using HybirdFrameworkDriver.Common; namespace HybirdFrameworkServices.Charger.Codec; -[Scope("InstancePerDependency")] -public class Encoder : MessageToByteEncoder, IEncoder +public class Encoder : MessageToByteEncoder { - protected override void Encode(IChannelHandlerContext context, byte[] message, IByteBuffer output) + protected override void Encode(IChannelHandlerContext context, IToBytes obj, IByteBuffer output) { - output.WriteBytes(message); + output.WriteBytes(obj.ToBytes()); } } \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Common/ChargerConst.cs b/HybirdFrameworkServices/Charger/Common/ChargerConst.cs new file mode 100644 index 0000000..2592e03 --- /dev/null +++ b/HybirdFrameworkServices/Charger/Common/ChargerConst.cs @@ -0,0 +1,6 @@ +namespace HybirdFrameworkServices.Charger.Common; + +public class ChargerConst +{ + public static readonly byte[] ApciStartChar = { 0x68/* ,0xEE*/}; +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Handler/AuthResHandler.cs b/HybirdFrameworkServices/Charger/Handler/AuthResHandler.cs new file mode 100644 index 0000000..a83b8ac --- /dev/null +++ b/HybirdFrameworkServices/Charger/Handler/AuthResHandler.cs @@ -0,0 +1,29 @@ +using DotNetty.Transport.Channels; +using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkDriver.Session; +using HybirdFrameworkServices.Charger.Msg.Charger.Resp; +using log4net; + +namespace HybirdFrameworkServices.Charger.Handler +{ + /// + /// 接收到鉴权帧 + /// + /// 1,保存日志到log + /// 2,从SessionMgr中取目的地址,解析后写入ChargerManager + /// 3,保存鉴权状态和充电状态 + /// + /// + [Order(8)] + [Scope("InstancePerDependency")] + public class AuthResHandler : SimpleChannelInboundHandler, IBaseHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(AuthResHandler)); + + protected override void ChannelRead0(IChannelHandlerContext ctx, AuthRes msg) + { + int sn = (int)SessionMgr.GetAttrByKey(ctx.Channel.Id.ToString(), "charger_sn"); + Log.Info($"receive {msg} from {sn}"); + } + } +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Handler/Cmd106Handler.cs b/HybirdFrameworkServices/Charger/Handler/Cmd106Handler.cs deleted file mode 100644 index ccdec7e..0000000 --- a/HybirdFrameworkServices/Charger/Handler/Cmd106Handler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using DotNetty.Transport.Channels; -using HybirdFrameworkCore.Autofac.Attribute; -using HybirdFrameworkDriver.TcpServer; -using HybirdFrameworkServices.Charger.Msg; - -namespace HybirdFrameworkServices.Charger.Handler; - -[Order(9)] -[Scope("InstancePerDependency")] -public class Cmd106Handler : SimpleChannelInboundHandler, IMsgHandler -{ - protected override void ChannelRead0(IChannelHandlerContext ctx, Cmd106 msg) - { - } -} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Handler/IBaseHandler.cs b/HybirdFrameworkServices/Charger/Handler/IBaseHandler.cs new file mode 100644 index 0000000..69f8216 --- /dev/null +++ b/HybirdFrameworkServices/Charger/Handler/IBaseHandler.cs @@ -0,0 +1,7 @@ +using DotNetty.Transport.Channels; + +namespace HybirdFrameworkServices.Charger.Handler; + +public interface IBaseHandler : IChannelHandler +{ +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Handler/IoSessionHandler.cs b/HybirdFrameworkServices/Charger/Handler/IoSessionHandler.cs deleted file mode 100644 index 91990bd..0000000 --- a/HybirdFrameworkServices/Charger/Handler/IoSessionHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -using DotNetty.Transport.Channels; -using HybirdFrameworkCore.Autofac.Attribute; -using HybirdFrameworkDriver.TcpServer; - -namespace HybirdFrameworkServices.Charger.Handler; - -[Order(8)] -[Scope("InstancePerDependency")] -public class IoSessionHandler : SimpleChannelInboundHandler, IMsgHandler -{ - protected override void ChannelRead0(IChannelHandlerContext ctx, IoSession msg) - { - } -} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Handler/LoginHandler.cs b/HybirdFrameworkServices/Charger/Handler/LoginHandler.cs new file mode 100644 index 0000000..3b7e227 --- /dev/null +++ b/HybirdFrameworkServices/Charger/Handler/LoginHandler.cs @@ -0,0 +1,42 @@ +using DotNetty.Transport.Channels; +using HybirdFrameworkCore.Autofac.Attribute; +using HybirdFrameworkDriver.Session; +using HybirdFrameworkServices.Charger.Msg.Charger.Req; +using HybirdFrameworkServices.Charger.Msg.Host.Resp; +using log4net; + +namespace HybirdFrameworkServices.Charger.Handler +{ + /// + /// 3.3.3 充放电机登陆签到 + /// 监控平台应答充电设备登录签到报文 + /// + /// 1,保存日志到log + /// 2,回复签到应答 + /// 3,保存签到应答日志 + /// + /// + [Order(8)] + [Scope("InstancePerDependency")] + public class LoginHandler : SimpleChannelInboundHandler,IBaseHandler + { + private static readonly ILog Log = LogManager.GetLogger(typeof(LoginHandler)); + + protected override void ChannelRead0(IChannelHandlerContext ctx, Login msg) + { + msg.ConnProtocolVersion = msg.ConnProtocolVersion0 + "." + msg.ConnProtocolVersion1 + "." + + msg.ConnProtocolVersion2; + msg.ControllerHardwareVersion = msg.ControllerHardwareVersion0 + "." + msg.ControllerHardwareVersion1 + + "." + msg.ControllerHardwareVersion2; + msg.ControllerSoftwareVersion = msg.ControllerSoftwareVersion0 + "." + msg.ControllerSoftwareVersion1 + + "." + msg.ControllerSoftwareVersion2; + int sn = (int)SessionMgr.GetAttrByKey(ctx.Channel.Id.ToString(), "charger_sn"); + Log.Info($"receive {msg} from {sn}"); + + + LogSignMessage logSignMessage = new LogSignMessage(0); + logSignMessage.DestAddr = new byte[] { 0x01, 0x02, 0x03 }; + ctx.Channel.WriteAndFlushAsync(logSignMessage); + } + } +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Msg/APCI.cs b/HybirdFrameworkServices/Charger/Msg/APCI.cs new file mode 100644 index 0000000..514d299 --- /dev/null +++ b/HybirdFrameworkServices/Charger/Msg/APCI.cs @@ -0,0 +1,46 @@ +using HybirdFrameworkDriver.Common; +using HybirdFrameworkServices.Charger.Common; + +namespace HybirdFrameworkServices.Charger.Msg +{ + public abstract class APCI : IToBytes + { + /// + /// 报文长度 + /// + public UInt16 PackLen { get; set; } + + /// + /// 控制域 + /// + public UInt32 CtlArea { get; set; } + + /// + /// 目标地址 + /// + public byte[] DestAddr { get; set; } + + /// + /// 源地址 + /// + public UInt32 SrcAddr { get; set; } + + + public byte[] ToBytes() + { + byte[] bodyBytes = GetBytes(); + List list = new List(); + list.AddRange(ChargerConst.ApciStartChar); + list.AddRange(BitConverter.GetBytes(bodyBytes.Length + 12)); + list.AddRange(BitConverter.GetBytes(CtlArea)); + list.AddRange(DestAddr); + list.AddRange(BitConverter.GetBytes(SrcAddr)); + + list.AddRange(bodyBytes); + + return list.ToArray(); + } + + public abstract byte[] GetBytes(); + } +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Msg/ASDU.cs b/HybirdFrameworkServices/Charger/Msg/ASDU.cs new file mode 100644 index 0000000..55dfae0 --- /dev/null +++ b/HybirdFrameworkServices/Charger/Msg/ASDU.cs @@ -0,0 +1,75 @@ +using DotNetty.Buffers; +using HybirdFrameworkCore.Utils; +using HybirdFrameworkServices.Charger.Common; + +namespace HybirdFrameworkServices.Charger.Msg +{ + public class ASDU : APCI + { + /// + /// 帧类型号 + /// + public byte FrameTypeNo { get; set; } + + /// + /// 信息体个数 + /// + public byte MsgBodyCount { get; set; } + + /// + /// 传送原因-3.1.3.2 + /// + public UInt16 TransReason { get; set; } + + /// + /// 公共地址 + /// + public UInt16 PublicAddr { get; set; } + + /// + /// 信息体地址-3个字节 + /// + public byte[]? MsgBodyAddr { get; set; } + + + public override byte[] GetBytes() + { + List list = new List(); + list.Add(FrameTypeNo); + list.Add(MsgBodyCount); + list.AddRange(BitConverter.GetBytes(TransReason)); + list.AddRange(BitConverter.GetBytes(PublicAddr)); + list.AddRange(MsgBodyAddr); + + list.AddRange(ModelConvert.Encode(this)); + return list.ToArray(); + } + + + public static void ParseHeader(IByteBuffer byteBuffer, ASDU asdu) + { + int start = ChargerConst.ApciStartChar.Length - 1; + + asdu.PackLen = byteBuffer.GetUnsignedShortLE(start + 1); + asdu.CtlArea = byteBuffer.GetUnsignedInt(start + 3); + asdu.DestAddr = new[] + { + byteBuffer.GetByte(start + 7), + byteBuffer.GetByte(start + 8), + byteBuffer.GetByte(start + 9), + byteBuffer.GetByte(start + 10), + }; + asdu.SrcAddr = byteBuffer.GetUnsignedInt(start + 11); + asdu.FrameTypeNo = byteBuffer.GetByte(start + 15); + asdu.MsgBodyCount = byteBuffer.GetByte(start + 16); + asdu.TransReason = byteBuffer.GetUnsignedShortLE(start + 17); + asdu.PublicAddr = byteBuffer.GetUnsignedShortLE(start + 19); + asdu.MsgBodyAddr = new[] + { + byteBuffer.GetByte(start + 21), + byteBuffer.GetByte(start + 22), + byteBuffer.GetByte(start + 23), + }; + } + } +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Msg/Charger/Req/Login.cs b/HybirdFrameworkServices/Charger/Msg/Charger/Req/Login.cs new file mode 100644 index 0000000..d3ddbec --- /dev/null +++ b/HybirdFrameworkServices/Charger/Msg/Charger/Req/Login.cs @@ -0,0 +1,88 @@ +using HybirdFrameworkCore.Autofac.Attribute; + +namespace HybirdFrameworkServices.Charger.Msg.Charger.Req +{ + /// + /// 3.3.3 充放电机登陆签到 + /// + public class Login : ASDU + { + /// + /// 记录类型 + /// + [Property(0, 8)] + public byte RecordType { get; set; } + /// + /// 监控网关编号 + /// + [Property(8, 16)] + public UInt16 GatewayNo { get; set; } + /// + /// 设备属性 + /// + [Property(24, 8)] + public byte EquipType { get; set; } + /// + /// 通讯协议版本 + /// + [Property(32, 8)] + public byte ConnProtocolVersion0 { get; set; } + [Property(40, 8)] + public byte ConnProtocolVersion1 { get; set; } + [Property(48, 8)] + public byte ConnProtocolVersion2 { get; set; } + public string ConnProtocolVersion { get; set; } + /// + /// 充电控制器硬件版本号 + /// + [Property(56, 8)] + public byte ControllerHardwareVersion0 { get; set; } + [Property(64, 8)] + public byte ControllerHardwareVersion1 { get; set; } + [Property(72, 8)] + public byte ControllerHardwareVersion2 { get; set; } + public string ControllerHardwareVersion { get; set; } + /// + /// 充电控制器软件版本 + /// + [Property(80, 8)] + public byte ControllerSoftwareVersion0 { get; set; } + [Property(88, 8)] + public byte ControllerSoftwareVersion1 { get; set; } + [Property(96, 8)] + public byte ControllerSoftwareVersion2 { get; set; } + public string ControllerSoftwareVersion { get; set; } + /// + /// 充电枪口数目 + /// + [Property(104, 8)] + public byte GunNum { get; set; } + /// + /// 充电模块数目 + /// + [Property(112, 8)] + public byte GunModuleNum { get; set; } + /// + /// 额定功率 + /// + [Property(128, 16)] + public ushort RatedPower { get; set; } + /// + /// 当前功率 + /// + [Property(136, 8)] + public byte CurrentPower { get; set; } + /// + /// 当前速率 + /// + [Property(144, 8)] + public byte CurrentSpeed { get; set; } + /// + /// 分流器量程 + /// + [Property(152, 16)] + public ushort DiverterRange { get; set; } + + + } +} diff --git a/HybirdFrameworkServices/Charger/Msg/Charger/Resp/AuthRes.cs b/HybirdFrameworkServices/Charger/Msg/Charger/Resp/AuthRes.cs new file mode 100644 index 0000000..f8ef764 --- /dev/null +++ b/HybirdFrameworkServices/Charger/Msg/Charger/Resp/AuthRes.cs @@ -0,0 +1,35 @@ +using HybirdFrameworkCore.Autofac.Attribute; + +namespace HybirdFrameworkServices.Charger.Msg.Charger.Resp +{ + /// + /// 3.3.2 充放电机应答鉴权认证 + /// + public class AuthRes : ASDU + + { + /// + /// 记录类型 + /// + [Property(0, 8)] + public byte RecordType { get; set; } + /// + /// 连接序号 + /// + [Property(8, 16)] + public ushort ConnSeq { get; set; } + + /// + /// 鉴权结果 + /// + [Property(24, 8)] + public byte AuthResult { get; set; } + + /// + /// 失败原因 + /// + [Property(32, 8)] + public byte FailReason { get; set; } + + } +} diff --git a/HybirdFrameworkServices/Charger/Msg/Cmd106.cs b/HybirdFrameworkServices/Charger/Msg/Cmd106.cs deleted file mode 100644 index c0f3ecd..0000000 --- a/HybirdFrameworkServices/Charger/Msg/Cmd106.cs +++ /dev/null @@ -1,9 +0,0 @@ -using HybirdFrameworkCore.Autofac.Attribute; - -namespace HybirdFrameworkServices.Charger.Msg; - -[Scope("InstancePerDependency")] -public class Cmd106 -{ - -} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Msg/Host/Req/Auth.cs b/HybirdFrameworkServices/Charger/Msg/Host/Req/Auth.cs new file mode 100644 index 0000000..b522b8f --- /dev/null +++ b/HybirdFrameworkServices/Charger/Msg/Host/Req/Auth.cs @@ -0,0 +1,57 @@ +using HybirdFrameworkCore.Autofac.Attribute; + +namespace HybirdFrameworkServices.Charger.Msg.Host.Req +{ + /// + /// 3.3.1 监控平台鉴权认证 + /// + public class Auth : ASDU + { + /// + /// 记录类型 + /// + public byte RecordType { get; set; } + /// + /// 客户端类型.1站控 2本地控制器 3测试客户端 4TCU模式 0未知设备 + /// + [Property(0, 8)] + public byte ClientType { get; set; } + + /// + /// 连接序号 + /// + [Property(8, 16)] + public ushort ConnSeq { get; set; } + + /// + /// 鉴权码字节数组 + /// + [Property(24, 64)] + public byte[] AuthCodes { get; set; } + + /// + /// 鉴码KEY + /// + [Property(88, 8)] + public byte AuthCodeKey { get; set; } + + public Auth(ushort connseq, byte[] authcodes, byte authcodekey) + { + PackLen = 0; + CtlArea = 0; + SrcAddr = 0; + + FrameTypeNo = 45; + MsgBodyCount = 1; + TransReason = 3; + PublicAddr = 0; + MsgBodyAddr = new byte[] { 0, 0, 0 }; + + RecordType = 24; + ClientType = 1; + ConnSeq = connseq; + AuthCodes = authcodes; + AuthCodeKey = authcodekey; + } + } +} \ No newline at end of file diff --git a/HybirdFrameworkServices/Charger/Msg/Host/Resp/LogSignMessage.cs b/HybirdFrameworkServices/Charger/Msg/Host/Resp/LogSignMessage.cs new file mode 100644 index 0000000..5fe2b00 --- /dev/null +++ b/HybirdFrameworkServices/Charger/Msg/Host/Resp/LogSignMessage.cs @@ -0,0 +1,89 @@ +using HybirdFrameworkCore.Autofac.Attribute; + +namespace HybirdFrameworkServices.Charger.Msg.Host.Resp +{ + /// + /// 3.3.4 监控平台应答充电设备登录签到报文 + /// + public class LogSignMessage : ASDU + { + [Property(0, 8)] public byte RecordType { get; set; } + + /// + /// 结果 + /// 0:成功/确认 + ///1:失败-平台处理该消息失败 + ///2:消息有误-消息校验错误/消息长度有误 + ///4:该设备编码在系统没有找到 + ///5:该设备编码在系统中异常,可能存在冲突 + ///6:充电控制器数目不对 + ///255:其它错误 + /// + [Property(8, 8)] + public byte Result { get; set; } + + /// + /// 年 + /// + [Property(16, 16)] + public ushort TimeYear { get; set; } + + /// + /// 月 + /// + [Property(32, 8)] + public byte TimeMonth { get; set; } + + /// + /// 日 + /// + [Property(40, 8)] + public byte TimeDay { get; set; } + + /// + /// 时 + /// + [Property(48, 8)] + public byte TimeHour { get; set; } + + /// + /// 分 + /// + [Property(56, 8)] + public byte TimeMinute { get; set; } + + /// + /// 秒 + /// + [Property(64, 8)] + public byte TimeSecond { get; set; } + + /// + /// 保留:1位, 默认0xFF + /// + [Property(72, 8)] + public byte Time { get; set; } + + public LogSignMessage(byte result) + { + Result = result; + + CtlArea = 0; + SrcAddr = 0; + FrameTypeNo = 45; + MsgBodyCount = 1; + TransReason = 4; + PublicAddr = 0; + MsgBodyAddr = new byte[] { 0, 0, 0 }; + RecordType = 12; + + DateTime dtime = DateTime.Now; + TimeYear = Convert.ToUInt16(dtime.Year.ToString()); + TimeMonth = Convert.ToByte(dtime.Month); + TimeDay = Convert.ToByte(dtime.Day); + TimeHour = Convert.ToByte(dtime.Hour); + TimeMinute = Convert.ToByte(dtime.Minute); + TimeSecond = Convert.ToByte(dtime.Second); + } + } +} \ No newline at end of file diff --git a/WebStarter/Program.cs b/WebStarter/Program.cs index 5fe1276..8e62897 100644 --- a/WebStarter/Program.cs +++ b/WebStarter/Program.cs @@ -65,7 +65,4 @@ app.MapControllers(); AppInfo.Container = app.Services.GetAutofacRoot(); -Server server = AppInfo.Container.Resolve(); -server.Start(9000); - app.Run(); \ No newline at end of file diff --git a/WinFormStarter/Form2.cs b/WinFormStarter/Form2.cs index 317e02a..ef85d94 100644 --- a/WinFormStarter/Form2.cs +++ b/WinFormStarter/Form2.cs @@ -1,15 +1,9 @@ -using HybirdFrameworkDriver.TcpServer; - -namespace WinFormStarter; +namespace WinFormStarter; public partial class Form2 : Form { - - private Server _server; - public Form2(Server server) + public Form2() { - _server = server; InitializeComponent(); - _server.Start(9000); } } \ No newline at end of file diff --git a/WinFormStarter/bin/Debug/net6.0-windows/Microsoft.Extensions.Logging.Abstractions.dll b/WinFormStarter/bin/Debug/net6.0-windows/Microsoft.Extensions.Logging.Abstractions.dll index 2c87f798a33608d70f582b601ec528347ec93a4c..bb27a2fcaa36c520c62d59c12ed48aad23ad088a 100644 GIT binary patch literal 62064 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 C7uFO2 literal 52616 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 m&j?Mt!wB>qY#Ip?lK%mA?#&zk