master
smartwyy 7 months ago
parent 000d0ade8e
commit 584602cd7f

@ -9,7 +9,26 @@
<ItemGroup>
<ProjectReference Include="..\HybirdFrameworkCore\HybirdFrameworkCore.csproj" />
<ProjectReference Include="..\HybirdFrameworkDriver\HybirdFrameworkDriver.csproj" />
<ProjectReference Include="..\HybirdFrameworkEntity\HybirdFrameworkEntity.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="log4net" Version="2.0.15" />
</ItemGroup>
<ItemGroup>
<Content Update="log4net.xml">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Update="log4net.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<log4net>
<!-- 控制台日志配置 -->
<appender name="Console" type="log4net.Appender.ConsoleAppender">
<!-- 日志输出格式 -->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %5level [%thread] (%file:%line) - %message%newline" />
</layout>
</appender>
<!-- 文件存储日志配置 -->
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<!-- 保存文件的名称 -->
<file value="logs\Console.log" />
<appendToFile value="true" />
<!-- 文件的编码方式 -->
<param name="Encoding" value="UTF-8"/>
<!-- 每个文件的大小 -->
<maximumFileSize value="100MB" />
<!-- 保存文件数量 -->
<maxSizeRollBackups value="2" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<!-- 日志输出格式 -->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level %thread %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="Console" />
<appender-ref ref="RollingFile" />
</root>
</log4net>

@ -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>(T value, double scale, double offset)
{
if (value == null)
{
return Array.Empty<byte>();
}
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
}

@ -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<T>(byte[] bytes) where T : class, new()
{
@ -39,62 +38,16 @@ public static class ModelConvert
PropertyAttribute? attribute = field.GetCustomAttribute<PropertyAttribute>();
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<byte>();
}
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);
}
}

@ -0,0 +1,6 @@
namespace HybirdFrameworkDriver.ModbusTcpMaster;
public interface IModbusProperty
{
int GetRegisterNo();
}

@ -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<T>(byte[] bytes) where T : class, new()
{
T t = new T();
List<object?> 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<byte> property:
{
SetPropertyValue(startRegisterNo, property, bytes);
break;
}
case ModbusProperty<float> floatProperty:
{
SetPropertyValue(startRegisterNo, floatProperty, bytes);
break;
}
}
}
return t;
}
private static void SetPropertyValue<T>(int startRegisterNo, ModbusProperty<T> 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;
}
}

@ -0,0 +1,55 @@
namespace HybirdFrameworkDriver.ModbusTcpMaster;
public class ModbusProperty<T> : IModbusProperty
{
/// <summary>
/// 寄存器编号
/// </summary>
public int RegisterNo { get; set; }
/// <summary>
/// 数据起始地址
/// </summary>
public int Start { get; set; }
/// <summary>
/// 数据域长度
/// </summary>
public int Length { get; set; }
/// <summary>
/// 数据类型
/// </summary>
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
}

@ -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<byte[]> result = ModbusTcpNet.Read("x=3;" + (registerNo + start), (ushort)length);
if (result.IsSuccess)
{
return result.Content;
}
return null;
}
public bool WriteValue<T>(ModbusProperty<T> 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<byte[]> 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;
}
}

@ -0,0 +1,34 @@
namespace HybirdFrameworkDriver.ModbusTcpMaster;
public class WaterCoolData
{
public ModbusProperty<byte> Status { get; set; } = new(40001);
public ModbusProperty<byte> Bk1 { get; set; } = new(40002);
public ModbusProperty<bool> CompressorState1 { get; set; } = new(40003, 0, 1, ModbusDataType.Bit);
public ModbusProperty<bool> WaterPump { get; set; } = new(40003, 2, 1, ModbusDataType.Bit);
public ModbusProperty<bool> ElectricalHeating1 { get; set; } = new(40003, 3, type: ModbusDataType.Bit);
public ModbusProperty<bool> ElectricalHeating2 { get; set; } = new(40003, 4, type: ModbusDataType.Bit);
public ModbusProperty<bool> CompressorState2 { get; set; } = new(40003, 5, type: ModbusDataType.Bit);
public ModbusProperty<bool> DraughtFan1 { get; set; } = new(40003, 6, 1, ModbusDataType.Bit);
public ModbusProperty<bool> DraughtFan2 { get; set; } = new(40003, 7, type: ModbusDataType.Bit);
public ModbusProperty<bool> DraughtFan3 { get; set; } = new(40003, 8, type: ModbusDataType.Bit);
public ModbusProperty<bool> CompressorState3 { get; set; } = new(40003, 9, type: ModbusDataType.Bit);
public ModbusProperty<bool> CompressorState4 { get; set; } = new(40003, 10, type: ModbusDataType.Bit);
public ModbusProperty<bool> ChargePump { get; set; } = new(40003, 11, type: ModbusDataType.Bit);
public ModbusProperty<float> Temperature1 { get; set; } = new(40004, scale: 0.1, round: 1);
public ModbusProperty<float> Temperature2 { get; set; } = new(40005, scale: 0.1, round: 1);
public ModbusProperty<float> Temperature3 { get; set; } = new(40006, scale: 0.1, round: 1);
public ModbusProperty<float> Temperature4 { get; set; } = new(40007, scale: 0.1, round: 1);
public ModbusProperty<float> Temperature5 { get; set; } = new(40008, scale: 0.1, round: 1);
public ModbusProperty<float> Temperature6 { get; set; } = new(40009, scale: 0.1, round: 1);
public ModbusProperty<float> WaterDischarge { get; set; } = new(40010, scale: 0.1, round: 1);
public ModbusProperty<float> CompressorTemperature1 { get; set; } = new(40011, scale: 0.1, round: 1);
public ModbusProperty<float> CompressorTemperature2 { get; set; } = new(40012, scale: 0.1, round: 1);
public ModbusProperty<float> CompressorTemperature3 { get; set; } = new(40013, scale: 0.1, round: 1);
public ModbusProperty<float> CompressorTemperature4 { get; set; } = new(40014, scale: 0.1, round: 1);
public ModbusProperty<byte> ElectromagneticStatus1 { get; set; } = new(40023, scale: 0.1, round: 1);
public ModbusProperty<byte> ElectromagneticStatus2 { get; set; } = new(40024, scale: 0.1, round: 1);
}

@ -24,6 +24,8 @@ builder.Host.ConfigureContainer<ContainerBuilder>(cb =>
return db;
}).As<ISqlSugarClient>().InstancePerLifetimeScope();
cb.RegisterModule(new AutofacModuleRegister());
});

Loading…
Cancel
Save