diff --git a/ConsoleStarter/ConsoleStarter.csproj b/ConsoleStarter/ConsoleStarter.csproj index c645c7c..f422989 100644 --- a/ConsoleStarter/ConsoleStarter.csproj +++ b/ConsoleStarter/ConsoleStarter.csproj @@ -9,7 +9,26 @@ + + + + + + + + true + PreserveNewest + PreserveNewest + + + + + + Always + + + diff --git a/ConsoleStarter/log4net.xml b/ConsoleStarter/log4net.xml new file mode 100644 index 0000000..faac71d --- /dev/null +++ b/ConsoleStarter/log4net.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HybirdFrameworkCore/Utils/BitUtls.cs b/HybirdFrameworkCore/Utils/BitUtls.cs index be579f6..b95d795 100644 --- a/HybirdFrameworkCore/Utils/BitUtls.cs +++ b/HybirdFrameworkCore/Utils/BitUtls.cs @@ -4,6 +4,25 @@ namespace HybirdFrameworkCore.Utils; public static class BitUtls { + #region Type Def + + private static readonly Type BOOLEAN = typeof(bool); + private static readonly Type BYTE = typeof(byte); + private static readonly Type SBYTE = typeof(sbyte); + private static readonly Type SHORT = typeof(short); + private static readonly Type USHORT = typeof(ushort); + private static readonly Type INT = typeof(int); + private static readonly Type UINT = typeof(uint); + private static readonly Type LONG = typeof(long); + private static readonly Type ULONG = typeof(ulong); + private static readonly Type FLOAT = typeof(float); + private static readonly Type DOUBLE = typeof(double); + private static readonly Type STRING = typeof(string); + + #endregion + + #region Func + public static string Bytes2BinaryString(byte[] bytes) { return string.Join(" ", bytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0'))); @@ -39,6 +58,10 @@ public static class BitUtls return returnBytes; } + #endregion + + #region Base Func + public static byte Byte2Bit(byte[] bytes, int startBit, int length) { if (length > 8) @@ -46,40 +69,6 @@ public static class BitUtls throw new ArgumentException("length can not be greater then 8!"); } - /* - byte result = 0; - int index = startBit / 8; - byte b = bytes[index]; - - int suffix = startBit % 8; - if (suffix == 0) - { - byte mask = (byte)(255 >> (8 - length)); - result = (byte)(b & mask); - } - else - { - if ((8 - suffix) >= length) - { - byte mask = (byte)(255 >> (8 - length)); - result = (byte)(b & mask); - } - else - { - byte pre = (byte)(b >> suffix); - - int remain = length - (8 - suffix); - byte nextByte = bytes[index + 1]; - - nextByte = (byte)(nextByte << (8 - remain)); - nextByte = (byte)(nextByte >> (8 - remain)); - nextByte = (byte)(nextByte << (8 - suffix)); - - result = (byte)(pre | nextByte); - } - } - - return result;*/ byte[] sub = Sub(bytes, startBit, length); return sub[0]; } @@ -203,4 +192,126 @@ public static class BitUtls return BitConverter.ToInt32(sub, 0); } + + #endregion + + #region Decode Value + + public static object Bytes2Value(byte[] bytes, Type propertyType, int start, int length, double scale, int round, + double offset) + { + if (propertyType == BOOLEAN) + { + return Convert.ChangeType(Byte2Bit(bytes, start, length) == 1, propertyType); + } + else if (propertyType == BYTE || propertyType == SBYTE) + { + return + Convert.ChangeType(Byte2Bit(bytes, start, length) * scale - offset, propertyType); + } + else if (propertyType == USHORT) + { + return + Convert.ChangeType(Byte2UInt16(bytes, start, length) * scale - offset, propertyType); + } + else if (propertyType == SHORT) + { + return + Convert.ChangeType(Byte2Int16(bytes, start, length) * scale - offset, propertyType); + } + else if (propertyType == INT) + { + return + Convert.ChangeType(Byte2Int32(bytes, start, length) * scale - offset, propertyType); + } + else if (propertyType == UINT) + { + return + Convert.ChangeType(Byte2UInt32(bytes, start, length) * scale - offset, propertyType); + } + else if (propertyType == FLOAT) + { + return + Convert.ChangeType(Math.Round(Byte2Float(bytes, start, length, scale) - offset, round), propertyType); + } + else if (propertyType == DOUBLE) + { + return + Convert.ChangeType(Math.Round(Byte2Double(bytes, start, length, scale) - offset, round), + propertyType); + } + else if (propertyType == STRING) + { + return Convert.ChangeType(BytesToHexStr(bytes, start, length), propertyType); + } + + throw new ArgumentException($"参数类型{propertyType}不支持encode!"); + } + + #endregion + + #region encode value + + public static byte[] Value2Bytes(T value, double scale, double offset) + { + if (value == null) + { + return Array.Empty(); + } + + Type propertyType = value.GetType(); + + if (propertyType == BOOLEAN) + { + return BitConverter.GetBytes(Convert.ToBoolean(value)); + } + + if (propertyType == BYTE || propertyType == SBYTE) + { + return new[] { (byte)((Convert.ToByte(value) + offset) / scale) }; + } + + if (propertyType == USHORT) + { + return BitConverter.GetBytes((ushort)((Convert.ToUInt16(value) + offset) / scale)); + } + + if (propertyType == SHORT) + { + return BitConverter.GetBytes((short)((Convert.ToInt16(value) + offset) / scale)); + } + + if (propertyType == INT) + { + return BitConverter.GetBytes((int)((Convert.ToInt32(value) + offset) / scale)); + } + + if (propertyType == UINT) + { + return BitConverter.GetBytes((uint)((Convert.ToUInt32(value) + offset) / scale)); + } + + if (propertyType == FLOAT) + { + return BitConverter.GetBytes((uint)((Convert.ToSingle(value) + offset) / scale)); + } + + if (propertyType == DOUBLE) + { + return BitConverter.GetBytes((UInt64)((Convert.ToDouble(value) + offset) / scale)); + } + + if (propertyType == STRING) + { + string? s = Convert.ToString(value); + if (s != null) + { + return Encoding.ASCII.GetBytes(s); + } + } + + throw new ArgumentException($"参数类型{propertyType}不支持encode!"); + } + + #endregion } \ No newline at end of file diff --git a/HybirdFrameworkCore/Utils/ModelConvert.cs b/HybirdFrameworkCore/Utils/ModelConvert.cs index 0004a15..cb8684c 100644 --- a/HybirdFrameworkCore/Utils/ModelConvert.cs +++ b/HybirdFrameworkCore/Utils/ModelConvert.cs @@ -1,24 +1,23 @@ using System.Collections; using System.Reflection; -using System.Text; using HybirdFrameworkCore.Autofac.Attribute; namespace HybirdFrameworkCore.Utils; public static class ModelConvert { - private static readonly Type BOOLEAN = typeof(bool); - private static readonly Type BYTE = typeof(byte); - private static readonly Type SBYTE = typeof(sbyte); - private static readonly Type SHORT = typeof(short); - private static readonly Type USHORT = typeof(ushort); - private static readonly Type INT = typeof(int); - private static readonly Type UINT = typeof(uint); - private static readonly Type LONG = typeof(long); - private static readonly Type ULONG = typeof(ulong); - private static readonly Type FLOAT = typeof(float); - private static readonly Type DOUBLE = typeof(double); - private static readonly Type STRING = typeof(string); + public static readonly Type BOOLEAN = typeof(bool); + public static readonly Type BYTE = typeof(byte); + public static readonly Type SBYTE = typeof(sbyte); + public static readonly Type SHORT = typeof(short); + public static readonly Type USHORT = typeof(ushort); + public static readonly Type INT = typeof(int); + public static readonly Type UINT = typeof(uint); + public static readonly Type LONG = typeof(long); + public static readonly Type ULONG = typeof(ulong); + public static readonly Type FLOAT = typeof(float); + public static readonly Type DOUBLE = typeof(double); + public static readonly Type STRING = typeof(string); public static T Declode(byte[] bytes) where T : class, new() { @@ -39,62 +38,16 @@ public static class ModelConvert PropertyAttribute? attribute = field.GetCustomAttribute(); if (attribute != null) { - double scale = attribute.Scale; - int length = PropertyReadConstant.Byte == attribute.Type ? attribute.Length * 8 : attribute.Length; int start = attribute.Start; + int length = PropertyReadConstant.Byte == attribute.Type ? attribute.Length * 8 : attribute.Length; + double scale = attribute.Scale; + int round = attribute.Round; double offset = attribute.Offset; Type propertyType = field.PropertyType; - if (propertyType == BOOLEAN) - { - field.SetValue(t, - Convert.ChangeType(BitUtls.Byte2Bit(bytes, start, length) == 1, propertyType), null); - } - - else if (propertyType == BYTE || propertyType == SBYTE) - { - field.SetValue(t, - Convert.ChangeType(BitUtls.Byte2Bit(bytes, start, length) * scale - offset, propertyType), null); - } - else if (propertyType == USHORT) - { - field.SetValue(t, - Convert.ChangeType(BitUtls.Byte2UInt16(bytes, start, length) * scale - offset, propertyType), null); - } - else if (propertyType == SHORT) - { - field.SetValue(t, - Convert.ChangeType(BitUtls.Byte2Int16(bytes, start, length) * scale - offset, propertyType), null); - } - else if (propertyType == INT) - { - field.SetValue(t, - Convert.ChangeType(BitUtls.Byte2Int32(bytes, start, length) * scale - offset, propertyType), null); - } - else if (propertyType == UINT) - { - field.SetValue(t, - Convert.ChangeType(BitUtls.Byte2UInt32(bytes, start, length) * scale - offset, propertyType), null); - } - else if (propertyType == FLOAT) - { - field.SetValue(t, - Convert.ChangeType( - Math.Round(BitUtls.Byte2Float(bytes, start, length, scale) - offset, attribute.Round), - propertyType), null); - } - else if (propertyType == DOUBLE) - { - field.SetValue(t, - Convert.ChangeType( - Math.Round(BitUtls.Byte2Double(bytes, start, length, scale) - offset, attribute.Round), - propertyType), null); - } - else if (propertyType == STRING) - { - field.SetValue(t, Convert.ChangeType(BitUtls.BytesToHexStr(bytes, start, length), propertyType), null); - } + object value = BitUtls.Bytes2Value(bytes, propertyType, start, length, scale, round, offset); + field.SetValue(t, value, null); } } @@ -150,57 +103,8 @@ public static class ModelConvert double offset = attribute.Offset; double scale = attribute.Scale; Type propertyType = field.PropertyType; - object? value = field.GetValue(t); - if (value == null) - { - return Array.Empty(); - } - - if (propertyType == BOOLEAN) - { - return BitConverter.GetBytes((bool)value); - } - - if (propertyType == BYTE || propertyType == SBYTE) - { - return new[] { (byte)(((byte)value + offset) / scale) }; - } - - if (propertyType == USHORT) - { - return BitConverter.GetBytes((ushort)(((ushort)value + offset) / scale)); - } - - if (propertyType == SHORT) - { - return BitConverter.GetBytes((short)(((short)value + offset) / scale)); - } - - if (propertyType == INT) - { - return BitConverter.GetBytes((int)(((int)value + offset) / scale)); - } - if (propertyType == UINT) - { - return BitConverter.GetBytes((uint)(((uint)value + offset) / scale)); - } - - if (propertyType == FLOAT) - { - return BitConverter.GetBytes((uint)(((float)value + offset) / scale)); - } - - if (propertyType == DOUBLE) - { - return BitConverter.GetBytes((UInt64)(((double)value + offset) / scale)); - } - - if (propertyType == STRING) - { - return Encoding.ASCII.GetBytes((string)value); - } - - throw new ArgumentException($"参数类型{propertyType}不支持encode!"); + object? value = field.GetValue(t); + return BitUtls.Value2Bytes(value, scale, offset); } } \ No newline at end of file diff --git a/HybirdFrameworkDriver/ModbusTcpMaster/IModbusProperty.cs b/HybirdFrameworkDriver/ModbusTcpMaster/IModbusProperty.cs new file mode 100644 index 0000000..3ad6bb7 --- /dev/null +++ b/HybirdFrameworkDriver/ModbusTcpMaster/IModbusProperty.cs @@ -0,0 +1,6 @@ +namespace HybirdFrameworkDriver.ModbusTcpMaster; + +public interface IModbusProperty +{ + int GetRegisterNo(); +} \ No newline at end of file diff --git a/HybirdFrameworkDriver/ModbusTcpMaster/ModbusDecoder.cs b/HybirdFrameworkDriver/ModbusTcpMaster/ModbusDecoder.cs new file mode 100644 index 0000000..ac87888 --- /dev/null +++ b/HybirdFrameworkDriver/ModbusTcpMaster/ModbusDecoder.cs @@ -0,0 +1,74 @@ +using HybirdFrameworkCore.Utils; +using log4net; + +namespace HybirdFrameworkDriver.ModbusTcpMaster; + +public static class ModbusDecoder +{ + private static readonly ILog Log = LogManager.GetLogger(typeof(ModbusDecoder)); + + public static T Decode(byte[] bytes) where T : class, new() + { + T t = new T(); + List fields = t.GetType().GetProperties() + .Where(it => it.PropertyType.GetGenericTypeDefinition() == typeof(ModbusProperty<>)) + .Select(p => p.GetValue(t)).ToList(); + + int startRegisterNo = Int32.MaxValue; + foreach (object field in fields) + { + if (field != null) + { + IModbusProperty p = (IModbusProperty)field; + if (startRegisterNo > p.GetRegisterNo()) + { + startRegisterNo = p.GetRegisterNo(); + } + } + } + + foreach (object field in fields) + { + switch (field) + { + case ModbusProperty property: + { + SetPropertyValue(startRegisterNo, property, bytes); + break; + } + case ModbusProperty floatProperty: + { + SetPropertyValue(startRegisterNo, floatProperty, bytes); + break; + } + } + } + + return t; + } + + private static void SetPropertyValue(int startRegisterNo, ModbusProperty field, byte[] bytes) + { + int registerNo = field.RegisterNo; + int start = field.Start; + int length = field.Length; + ModbusDataType type = field.Type; + double scale = field.Scale; + int round = field.Round; + double offset = field.Offset; + + start = (registerNo - startRegisterNo) * 16 + start; + + length = type switch + { + ModbusDataType.Byte => length * 8, + ModbusDataType.Register => length * 16, + _ => length + }; + + Type valueType = typeof(T); + + object value = BitUtls.Bytes2Value(bytes, valueType, start, length, scale, round, offset); + field.Value = (T)value; + } +} \ No newline at end of file diff --git a/HybirdFrameworkDriver/ModbusTcpMaster/ModbusProperty.cs b/HybirdFrameworkDriver/ModbusTcpMaster/ModbusProperty.cs new file mode 100644 index 0000000..b4d8b5c --- /dev/null +++ b/HybirdFrameworkDriver/ModbusTcpMaster/ModbusProperty.cs @@ -0,0 +1,55 @@ +namespace HybirdFrameworkDriver.ModbusTcpMaster; + +public class ModbusProperty : IModbusProperty +{ + /// + /// 寄存器编号 + /// + public int RegisterNo { get; set; } + + /// + /// 数据起始地址 + /// + public int Start { get; set; } + + /// + /// 数据域长度 + /// + public int Length { get; set; } + + /// + /// 数据类型 + /// + public ModbusDataType Type { get; set; } + + public double Scale { get; set; } + public int Round { get; set; } + public double Offset { get; set; } + public T Value { get; set; } + + public ModbusProperty(int registerNo, int start = 0, int length = 1, ModbusDataType type = ModbusDataType.Register, + double scale = 1, + int round = 0, double offset = 0) + { + this.RegisterNo = registerNo; + this.Start = start; + this.Length = length; + this.Type = type; + this.Scale = scale; + this.Round = round; + this.Offset = offset; + } + + + public int GetRegisterNo() + { + return RegisterNo; + } +} + +public enum ModbusDataType +{ + Bit, + Byte, + Register +} \ No newline at end of file diff --git a/HybirdFrameworkDriver/ModbusTcpMaster/ModbusTcpMaster.cs b/HybirdFrameworkDriver/ModbusTcpMaster/ModbusTcpMaster.cs index 7e98461..f0f8a9e 100644 --- a/HybirdFrameworkDriver/ModbusTcpMaster/ModbusTcpMaster.cs +++ b/HybirdFrameworkDriver/ModbusTcpMaster/ModbusTcpMaster.cs @@ -1,16 +1,28 @@ -using log4net; +using System.Collections; +using HslCommunication; +using HslCommunication.ModBus; +using HybirdFrameworkCore.Utils; +using log4net; namespace HybirdFrameworkDriver.ModbusTcpMaster; -public class ModbusTcpMaster +public abstract class ModbusTcpMaster { private static readonly ILog Log = LogManager.GetLogger(typeof(ModbusTcpMaster)); - public string Ip { get; set; } - public int Port { get; set; } + public string Ip { get; set; } = "127.0.0.1"; + + public int Port { get; set; } = 502; + + public int Duration { get; set; } = 1000; + public bool Connected { get; set; } = false; + + private bool StopFlag { get; set; } = false; + + public Action? Action { get; set; } + + private ModbusTcpNet ModbusTcpNet; - public int Duration { get; set; } - public bool Connected { get; set; } ILog GetLog() { @@ -19,6 +31,135 @@ public class ModbusTcpMaster public bool Connect() { + GetLog().Info($"begin to connect {Ip}:{Port}"); + + try + { + if (ModbusTcpNet == null) + { + ModbusTcpNet = new ModbusTcpNet(Ip, Port); + OperateResult result = ModbusTcpNet.ConnectServer(); + if (result.IsSuccess) + { + Connected = true; + GetLog().Info($"connect {Ip}:{Port} success"); + StartListen(); + } + else + { + GetLog().Info($"connect {Ip}:{Port} failed {result.Message}"); + } + } + } + catch (Exception e) + { + GetLog().Error($"connect {Ip}:{Port} exception {e.Message}"); + } + return Connected; } + + public void StartListen() + { + StopListen(); + StopFlag = false; + Thread readThread = new Thread(ReadFunc) + { + IsBackground = true, + Name = $"{Ip}:{Port}-reader-thread" + }; + readThread.Start(); + } + + public void StopListen() + { + StopFlag = true; + Thread.Sleep(500); + } + + private void ReadFunc() + { + while (!StopFlag) + { + try + { + Action?.Invoke(); + Thread.Sleep(Duration); + } + catch (Exception e) + { + Log.Error(e); + } + } + + GetLog().Info("stop listen"); + } + + public byte[]? ReadRegister(int registerNo, int start, int length) + { + start = start % 16 == 0 ? start / 16 : start / 16 + 1; + length = length % 8 == 0 ? length / 8 : length / 8 + 1; + OperateResult result = ModbusTcpNet.Read("x=3;" + (registerNo + start), (ushort)length); + + if (result.IsSuccess) + { + return result.Content; + } + + return null; + } + + public bool WriteValue(ModbusProperty property) + { + T value = property.Value; + if (value == null) + { + GetLog().Warn($"write property{property.RegisterNo} null value"); + return false; + } + + bool result = false; + ModbusDataType dataType = property.Type; + int start = property.Start; + int length = property.Length; + byte[] setValue = BitUtls.Value2Bytes(value, property.Scale, property.Offset); + switch (dataType) + { + case ModbusDataType.Byte: + + start = start % 2 == 0 ? start / 2 : start / 2 + 1; + ModbusTcpNet.Write("x=6;" + start, setValue); + break; + case ModbusDataType.Register: + ModbusTcpNet.Write("x=10;" + start, setValue); + break; + case ModbusDataType.Bit: + + start = start % 16 == 0 ? start / 16 : start / 16 + 1; + length = length % 8 == 0 ? length / 8 : length / 8 + 1; + OperateResult readResult = + ModbusTcpNet.Read("x=3;" + (property.RegisterNo + start), (ushort)length); + + if (readResult.IsSuccess) + { + byte[] bytes = readResult.Content; + BitArray bitArray = new BitArray(bytes); + int index = 0; + for (int i = property.Start % 16; i < property.Length; i++) + { + bitArray[i] = (setValue[index / 8] & (1 << (index % 8))) > 0; + index++; + } + + bitArray.CopyTo(bytes, 0); + + OperateResult write = ModbusTcpNet.Write("x=6;" + (property.RegisterNo + start), bytes); + result = write.IsSuccess; + } + + break; + } + + return result; + } } \ No newline at end of file diff --git a/HybirdFrameworkDriver/ModbusTcpMaster/WaterCoolData.cs b/HybirdFrameworkDriver/ModbusTcpMaster/WaterCoolData.cs new file mode 100644 index 0000000..4d9da83 --- /dev/null +++ b/HybirdFrameworkDriver/ModbusTcpMaster/WaterCoolData.cs @@ -0,0 +1,34 @@ +namespace HybirdFrameworkDriver.ModbusTcpMaster; + +public class WaterCoolData +{ + public ModbusProperty Status { get; set; } = new(40001); + public ModbusProperty Bk1 { get; set; } = new(40002); + + public ModbusProperty CompressorState1 { get; set; } = new(40003, 0, 1, ModbusDataType.Bit); + public ModbusProperty WaterPump { get; set; } = new(40003, 2, 1, ModbusDataType.Bit); + public ModbusProperty ElectricalHeating1 { get; set; } = new(40003, 3, type: ModbusDataType.Bit); + public ModbusProperty ElectricalHeating2 { get; set; } = new(40003, 4, type: ModbusDataType.Bit); + public ModbusProperty CompressorState2 { get; set; } = new(40003, 5, type: ModbusDataType.Bit); + public ModbusProperty DraughtFan1 { get; set; } = new(40003, 6, 1, ModbusDataType.Bit); + public ModbusProperty DraughtFan2 { get; set; } = new(40003, 7, type: ModbusDataType.Bit); + public ModbusProperty DraughtFan3 { get; set; } = new(40003, 8, type: ModbusDataType.Bit); + public ModbusProperty CompressorState3 { get; set; } = new(40003, 9, type: ModbusDataType.Bit); + public ModbusProperty CompressorState4 { get; set; } = new(40003, 10, type: ModbusDataType.Bit); + public ModbusProperty ChargePump { get; set; } = new(40003, 11, type: ModbusDataType.Bit); + + public ModbusProperty Temperature1 { get; set; } = new(40004, scale: 0.1, round: 1); + public ModbusProperty Temperature2 { get; set; } = new(40005, scale: 0.1, round: 1); + public ModbusProperty Temperature3 { get; set; } = new(40006, scale: 0.1, round: 1); + public ModbusProperty Temperature4 { get; set; } = new(40007, scale: 0.1, round: 1); + public ModbusProperty Temperature5 { get; set; } = new(40008, scale: 0.1, round: 1); + public ModbusProperty Temperature6 { get; set; } = new(40009, scale: 0.1, round: 1); + public ModbusProperty WaterDischarge { get; set; } = new(40010, scale: 0.1, round: 1); + public ModbusProperty CompressorTemperature1 { get; set; } = new(40011, scale: 0.1, round: 1); + public ModbusProperty CompressorTemperature2 { get; set; } = new(40012, scale: 0.1, round: 1); + public ModbusProperty CompressorTemperature3 { get; set; } = new(40013, scale: 0.1, round: 1); + public ModbusProperty CompressorTemperature4 { get; set; } = new(40014, scale: 0.1, round: 1); + + public ModbusProperty ElectromagneticStatus1 { get; set; } = new(40023, scale: 0.1, round: 1); + public ModbusProperty ElectromagneticStatus2 { get; set; } = new(40024, scale: 0.1, round: 1); +} \ No newline at end of file diff --git a/WebStarter/Program.cs b/WebStarter/Program.cs index 6299c62..15461db 100644 --- a/WebStarter/Program.cs +++ b/WebStarter/Program.cs @@ -23,6 +23,8 @@ builder.Host.ConfigureContainer(cb => }); return db; }).As().InstancePerLifetimeScope(); + + cb.RegisterModule(new AutofacModuleRegister()); });