Implementation of the basic functionallity
This commit is contained in:
14
AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs
Normal file
14
AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace AMWD.Protocols.Modbus.Common
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
internal static class ArrayExtensions
|
||||
{
|
||||
public static void SwapNetworkOrder(this byte[] bytes)
|
||||
{
|
||||
if (BitConverter.IsLittleEndian)
|
||||
Array.Reverse(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
AMWD.Protocols.Modbus.Common/Extensions/EnumExtensions.cs
Normal file
30
AMWD.Protocols.Modbus.Common/Extensions/EnumExtensions.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace System
|
||||
{
|
||||
// ================================================================================================================================== //
|
||||
// Source: https://git.am-wd.de/am.wd/common/-/blob/d4b390ad911ce302cc371bb2121fa9c31db1674a/AMWD.Common/Extensions/EnumExtensions.cs //
|
||||
// ================================================================================================================================== //
|
||||
[Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
internal static class EnumExtensions
|
||||
{
|
||||
private static IEnumerable<TAttribute> GetAttributes<TAttribute>(this Enum value)
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
var fieldInfo = value.GetType().GetField(value.ToString());
|
||||
if (fieldInfo == null)
|
||||
return Array.Empty<TAttribute>();
|
||||
|
||||
return fieldInfo.GetCustomAttributes(typeof(TAttribute), inherit: false).Cast<TAttribute>();
|
||||
}
|
||||
|
||||
private static TAttribute GetAttribute<TAttribute>(this Enum value)
|
||||
where TAttribute : Attribute
|
||||
=> value.GetAttributes<TAttribute>().FirstOrDefault();
|
||||
|
||||
public static string GetDescription(this Enum value)
|
||||
=> value.GetAttribute<DescriptionAttribute>()?.Description ?? value.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AMWD.Protocols.Modbus.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom extensions for <see cref="ModbusObject"/>s.
|
||||
/// </summary>
|
||||
public static class ModbusDecimalExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts multiple <see cref="ModbusObject"/>s into a <see cref="float"/> value.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of Modbus objects.</param>
|
||||
/// <param name="startIndex">The first index to use.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The objects float value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the list is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the list is too short or the list contains mixed/incompatible objects.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">when the <paramref name="startIndex"/> is too high.</exception>
|
||||
public static float GetSingle(this IEnumerable<ModbusObject> list, int startIndex = 0, bool reverseRegisterOrder = false)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(list);
|
||||
#else
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
#endif
|
||||
|
||||
int count = list.Count();
|
||||
if (count < 2)
|
||||
throw new ArgumentException("At least two registers required", nameof(list));
|
||||
|
||||
if (startIndex < 0 || startIndex + 2 > count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
|
||||
if (!list.All(o => o.Type == ModbusObjectType.HoldingRegister) && !list.All(o => o.Type == ModbusObjectType.InputRegister))
|
||||
throw new ArgumentException("Mixed object typs found", nameof(list));
|
||||
|
||||
var registers = list.OrderBy(o => o.Address).Skip(startIndex).Take(2).ToArray();
|
||||
if (reverseRegisterOrder)
|
||||
Array.Reverse(registers);
|
||||
|
||||
byte[] blob = new byte[registers.Length * 2];
|
||||
for (int i = 0; i < registers.Length; i++)
|
||||
{
|
||||
blob[i * 2] = registers[i].HighByte;
|
||||
blob[i * 2 + 1] = registers[i].LowByte;
|
||||
}
|
||||
|
||||
blob.SwapNetworkOrder();
|
||||
return BitConverter.ToSingle(blob, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts multiple <see cref="ModbusObject"/>s into a <see cref="double"/> value.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of Modbus objects.</param>
|
||||
/// <param name="startIndex">The first index to use.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The objects double value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the list is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the list is too short or the list contains mixed/incompatible objects.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">when the <paramref name="startIndex"/> is too high.</exception>
|
||||
public static double GetDouble(this IEnumerable<ModbusObject> list, int startIndex = 0, bool reverseRegisterOrder = false)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(list);
|
||||
#else
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
#endif
|
||||
|
||||
int count = list.Count();
|
||||
if (count < 4)
|
||||
throw new ArgumentException("At least four registers required", nameof(list));
|
||||
|
||||
if (startIndex < 0 || startIndex + 4 > count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
|
||||
if (!list.All(o => o.Type == ModbusObjectType.HoldingRegister) && !list.All(o => o.Type == ModbusObjectType.InputRegister))
|
||||
throw new ArgumentException("Mixed object typs found", nameof(list));
|
||||
|
||||
var registers = list.OrderBy(o => o.Address).Skip(startIndex).Take(4).ToArray();
|
||||
if (reverseRegisterOrder)
|
||||
Array.Reverse(registers);
|
||||
|
||||
byte[] blob = new byte[registers.Length * 2];
|
||||
for (int i = 0; i < registers.Length; i++)
|
||||
{
|
||||
blob[i * 2] = registers[i].HighByte;
|
||||
blob[i * 2 + 1] = registers[i].LowByte;
|
||||
}
|
||||
|
||||
blob.SwapNetworkOrder();
|
||||
return BitConverter.ToDouble(blob, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="float"/> value to a list of <see cref="HoldingRegister"/>s.
|
||||
/// </summary>
|
||||
/// <param name="value">The float value.</param>
|
||||
/// <param name="address">The first register address.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The list of registers.</returns>
|
||||
public static IEnumerable<HoldingRegister> ToRegister(this float value, ushort address, bool reverseRegisterOrder = false)
|
||||
{
|
||||
byte[] blob = BitConverter.GetBytes(value);
|
||||
blob.SwapNetworkOrder();
|
||||
|
||||
int numRegisters = blob.Length / 2;
|
||||
for (int i = 0; i < numRegisters; i++)
|
||||
{
|
||||
int addr = reverseRegisterOrder
|
||||
? address + numRegisters - 1 - i
|
||||
: address + i;
|
||||
|
||||
yield return new HoldingRegister
|
||||
{
|
||||
Address = (ushort)addr,
|
||||
HighByte = blob[i * 2],
|
||||
LowByte = blob[i * 2 + 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="double"/> value to a list of <see cref="HoldingRegister"/>s.
|
||||
/// </summary>
|
||||
/// <param name="value">The double value.</param>
|
||||
/// <param name="address">The first register address.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The list of registers.</returns>
|
||||
public static IEnumerable<HoldingRegister> ToRegister(this double value, ushort address, bool reverseRegisterOrder = false)
|
||||
{
|
||||
byte[] blob = BitConverter.GetBytes(value);
|
||||
blob.SwapNetworkOrder();
|
||||
|
||||
int numRegisters = blob.Length / 2;
|
||||
for (int i = 0; i < numRegisters; i++)
|
||||
{
|
||||
int addr = reverseRegisterOrder
|
||||
? address + numRegisters - 1 - i
|
||||
: address + i;
|
||||
|
||||
yield return new HoldingRegister
|
||||
{
|
||||
Address = (ushort)addr,
|
||||
HighByte = blob[i * 2],
|
||||
LowByte = blob[i * 2 + 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
175
AMWD.Protocols.Modbus.Common/Extensions/ModbusExtensions.cs
Normal file
175
AMWD.Protocols.Modbus.Common/Extensions/ModbusExtensions.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace AMWD.Protocols.Modbus.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom extensions for <see cref="ModbusObject"/>s.
|
||||
/// </summary>
|
||||
public static class ModbusExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ModbusObject"/> into a <see cref="bool"/> value.
|
||||
/// </summary>
|
||||
/// <param name="obj">The Modbus object.</param>
|
||||
/// <returns>The objects bool value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the object is null.</exception>
|
||||
public static bool GetBoolean(this ModbusObject obj)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(obj);
|
||||
#else
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
#endif
|
||||
|
||||
if (obj is Coil coil)
|
||||
return coil.Value;
|
||||
|
||||
if (obj is DiscreteInput discreteInput)
|
||||
return discreteInput.Value;
|
||||
|
||||
return obj.HighByte > 0 || obj.LowByte > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts multiple <see cref="ModbusObject"/>s into a <see cref="string"/> value.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of Modbus objects.</param>
|
||||
/// <param name="length">The number of registers to use.</param>
|
||||
/// <param name="startIndex">The first index to use.</param>
|
||||
/// <param name="encoding">The encoding used to convert the text. (Default: <see cref="Encoding.ASCII"/>)</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <param name="reverseByteOrderPerRegister">Indicates whether to reverse high and low byte per register.</param>
|
||||
/// <returns>The objects text value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the list is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the list is too short or the list contains mixed/incompatible objects.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">when the <paramref name="startIndex"/> is too high.</exception>
|
||||
public static string GetString(this IEnumerable<ModbusObject> list, int length, int startIndex = 0, Encoding encoding = null, bool reverseRegisterOrder = false, bool reverseByteOrderPerRegister = false)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(list);
|
||||
#else
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
#endif
|
||||
|
||||
int count = list.Count();
|
||||
if (count < length)
|
||||
throw new ArgumentException($"At least {length} registers required", nameof(list));
|
||||
|
||||
if (startIndex < 0 || startIndex + length > count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
|
||||
if (!list.All(o => o.Type == ModbusObjectType.HoldingRegister) && !list.All(o => o.Type == ModbusObjectType.InputRegister))
|
||||
throw new ArgumentException("Mixed object types found", nameof(list));
|
||||
|
||||
var registers = list.OrderBy(o => o.Address).Skip(startIndex).Take(length).ToArray();
|
||||
if (reverseRegisterOrder)
|
||||
Array.Reverse(registers);
|
||||
|
||||
byte[] blob = new byte[registers.Length * 2];
|
||||
for (int i = 0; i < registers.Length; i++)
|
||||
{
|
||||
blob[i * 2] = reverseByteOrderPerRegister
|
||||
? registers[i].LowByte
|
||||
: registers[i].HighByte;
|
||||
|
||||
blob[i * 2 + 1] = reverseByteOrderPerRegister
|
||||
? registers[i].HighByte
|
||||
: registers[i].LowByte;
|
||||
}
|
||||
|
||||
string text = (encoding ?? Encoding.ASCII).GetString(blob).Trim([' ', '\t', '\0', '\r', '\n']);
|
||||
int nullIndex = text.IndexOf('\0');
|
||||
|
||||
if (nullIndex > 0)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
return text[..nullIndex];
|
||||
#else
|
||||
return text.Substring(0, nullIndex);
|
||||
#endif
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="bool"/> value to a <see cref="Coil"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The bool value.</param>
|
||||
/// <param name="address">The coil address.</param>
|
||||
/// <returns>The coil.</returns>
|
||||
public static Coil ToCoil(this bool value, ushort address)
|
||||
{
|
||||
return new Coil
|
||||
{
|
||||
Address = address,
|
||||
Value = value
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="bool"/> value to a <see cref="HoldingRegister"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The bool value.</param>
|
||||
/// <param name="address">The register address.</param>
|
||||
/// <returns>The register.</returns>
|
||||
public static HoldingRegister ToRegister(this bool value, ushort address)
|
||||
{
|
||||
return new HoldingRegister
|
||||
{
|
||||
Address = address,
|
||||
Value = (ushort)(value ? 1 : 0)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="string"/> value to a <see cref="HoldingRegister"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The text.</param>
|
||||
/// <param name="address">The address of the text.</param>
|
||||
/// <param name="encoding">The encoding used to convert the text. (Default: <see cref="Encoding.ASCII"/>)</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <param name="reverseByteOrderPerRegister">Indicates whether to reverse high and low byte per register.</param>
|
||||
/// <returns>The registers.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the text is null.</exception>
|
||||
public static IEnumerable<HoldingRegister> ToRegisters(this string value, ushort address, Encoding encoding = null, bool reverseRegisterOrder = false, bool reverseByteOrderPerRegister = false)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
#else
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
#endif
|
||||
|
||||
byte[] blob = (encoding ?? Encoding.ASCII).GetBytes(value);
|
||||
int numRegisters = (int)Math.Ceiling(blob.Length / 2.0);
|
||||
|
||||
for (int i = 0; i < numRegisters; i++)
|
||||
{
|
||||
int addr = reverseRegisterOrder
|
||||
? address + numRegisters - 1 - i
|
||||
: address + i;
|
||||
|
||||
var register = new HoldingRegister
|
||||
{
|
||||
Address = (ushort)addr,
|
||||
|
||||
HighByte = reverseByteOrderPerRegister
|
||||
? (i * 2 + 1 < blob.Length ? blob[i * 2 + 1] : (byte)0)
|
||||
: blob[i * 2],
|
||||
|
||||
LowByte = reverseByteOrderPerRegister
|
||||
? blob[i * 2]
|
||||
: (i * 2 + 1 < blob.Length ? blob[i * 2 + 1] : (byte)0)
|
||||
};
|
||||
|
||||
yield return register;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AMWD.Protocols.Modbus.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom extensions for <see cref="ModbusObject"/>s.
|
||||
/// </summary>
|
||||
public static class ModbusSignedExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ModbusObject"/> into a <see cref="sbyte"/> value.
|
||||
/// </summary>
|
||||
/// <param name="obj">The Modbus object.</param>
|
||||
/// <returns>The objects signed byte value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the object is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the wrong types are provided.</exception>
|
||||
public static sbyte GetSByte(this ModbusObject obj)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(obj);
|
||||
#else
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
#endif
|
||||
|
||||
if (obj is HoldingRegister holdingRegister)
|
||||
return (sbyte)holdingRegister.Value;
|
||||
|
||||
if (obj is InputRegister inputRegister)
|
||||
return (sbyte)inputRegister.Value;
|
||||
|
||||
throw new ArgumentException($"The object type '{obj.GetType()}' is invalid", nameof(obj));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ModbusObject"/> into a <see cref="short"/> value.
|
||||
/// </summary>
|
||||
/// <param name="obj">The Modbus object.</param>
|
||||
/// <returns>The objects short value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the object is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the wrong types are provided.</exception>
|
||||
public static short GetInt16(this ModbusObject obj)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(obj);
|
||||
#else
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
#endif
|
||||
|
||||
if (obj is HoldingRegister holdingRegister)
|
||||
return (short)holdingRegister.Value;
|
||||
|
||||
if (obj is InputRegister inputRegister)
|
||||
return (short)inputRegister.Value;
|
||||
|
||||
throw new ArgumentException($"The object type '{obj.GetType()}' is invalid", nameof(obj));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts multiple <see cref="ModbusObject"/>s into a <see cref="int"/> value.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of Modbus objects.</param>
|
||||
/// <param name="startIndex">The first index to use.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The objects int value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the list is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the list is too short or the list contains mixed/incompatible objects.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">when the <paramref name="startIndex"/> is too high.</exception>
|
||||
public static int GetInt32(this IEnumerable<ModbusObject> list, int startIndex = 0, bool reverseRegisterOrder = false)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(list);
|
||||
#else
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
#endif
|
||||
|
||||
int count = list.Count();
|
||||
if (count < 2)
|
||||
throw new ArgumentException("At least two registers required", nameof(list));
|
||||
|
||||
if (startIndex < 0 || startIndex + 2 > count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
|
||||
if (!list.All(o => o.Type == ModbusObjectType.HoldingRegister) && !list.All(o => o.Type == ModbusObjectType.InputRegister))
|
||||
throw new ArgumentException("Mixed object typs found", nameof(list));
|
||||
|
||||
var registers = list.OrderBy(o => o.Address).Skip(startIndex).Take(2).ToArray();
|
||||
if (reverseRegisterOrder)
|
||||
Array.Reverse(registers);
|
||||
|
||||
byte[] blob = new byte[registers.Length * 2];
|
||||
for (int i = 0; i < registers.Length; i++)
|
||||
{
|
||||
blob[i * 2] = registers[i].HighByte;
|
||||
blob[i * 2 + 1] = registers[i].LowByte;
|
||||
}
|
||||
|
||||
blob.SwapNetworkOrder();
|
||||
return BitConverter.ToInt32(blob, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts multiple <see cref="ModbusObject"/>s into a <see cref="long"/> value.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of Modbus objects.</param>
|
||||
/// <param name="startIndex">The first index to use.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The objects long value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the list is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the list is too short or the list contains mixed/incompatible objects.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">when the <paramref name="startIndex"/> is too high.</exception>
|
||||
public static long GetInt64(this IEnumerable<ModbusObject> list, int startIndex = 0, bool reverseRegisterOrder = false)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(list);
|
||||
#else
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
#endif
|
||||
|
||||
int count = list.Count();
|
||||
if (count < 4)
|
||||
throw new ArgumentException("At least four registers required", nameof(list));
|
||||
|
||||
if (startIndex < 0 || startIndex + 4 > count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
|
||||
if (!list.All(o => o.Type == ModbusObjectType.HoldingRegister) && !list.All(o => o.Type == ModbusObjectType.InputRegister))
|
||||
throw new ArgumentException("Mixed object typs found", nameof(list));
|
||||
|
||||
var registers = list.OrderBy(o => o.Address).Skip(startIndex).Take(4).ToArray();
|
||||
if (reverseRegisterOrder)
|
||||
Array.Reverse(registers);
|
||||
|
||||
byte[] blob = new byte[registers.Length * 2];
|
||||
for (int i = 0; i < registers.Length; i++)
|
||||
{
|
||||
blob[i * 2] = registers[i].HighByte;
|
||||
blob[i * 2 + 1] = registers[i].LowByte;
|
||||
}
|
||||
|
||||
blob.SwapNetworkOrder();
|
||||
return BitConverter.ToInt64(blob, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="sbyte"/> value to a <see cref="HoldingRegister"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The signed byte value.</param>
|
||||
/// <param name="address">The register address.</param>
|
||||
/// <returns>The register.</returns>
|
||||
public static HoldingRegister ToRegister(this sbyte value, ushort address)
|
||||
{
|
||||
return new HoldingRegister
|
||||
{
|
||||
Address = address,
|
||||
LowByte = (byte)value
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="short"/> value to a <see cref="HoldingRegister"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The short value.</param>
|
||||
/// <param name="address">The register address.</param>
|
||||
/// <returns>The register.</returns>
|
||||
public static HoldingRegister ToRegister(this short value, ushort address)
|
||||
{
|
||||
byte[] blob = BitConverter.GetBytes(value);
|
||||
blob.SwapNetworkOrder();
|
||||
|
||||
return new HoldingRegister
|
||||
{
|
||||
Address = address,
|
||||
HighByte = blob[0],
|
||||
LowByte = blob[1]
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="int"/> value to a list of <see cref="HoldingRegister"/>s.
|
||||
/// </summary>
|
||||
/// <param name="value">The int value.</param>
|
||||
/// <param name="address">The first register address.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The list of registers.</returns>
|
||||
public static IEnumerable<HoldingRegister> ToRegister(this int value, ushort address, bool reverseRegisterOrder = false)
|
||||
{
|
||||
byte[] blob = BitConverter.GetBytes(value);
|
||||
blob.SwapNetworkOrder();
|
||||
|
||||
int numRegisters = blob.Length / 2;
|
||||
for (int i = 0; i < numRegisters; i++)
|
||||
{
|
||||
int addr = reverseRegisterOrder
|
||||
? address + numRegisters - 1 - i
|
||||
: address + i;
|
||||
|
||||
yield return new HoldingRegister
|
||||
{
|
||||
Address = (ushort)addr,
|
||||
HighByte = blob[i * 2],
|
||||
LowByte = blob[i * 2 + 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="long"/> value to a list of <see cref="HoldingRegister"/>s.
|
||||
/// </summary>
|
||||
/// <param name="value">The long value.</param>
|
||||
/// <param name="address">The first register address.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The list of registers.</returns>
|
||||
public static IEnumerable<HoldingRegister> ToRegister(this long value, ushort address, bool reverseRegisterOrder = false)
|
||||
{
|
||||
byte[] blob = BitConverter.GetBytes(value);
|
||||
blob.SwapNetworkOrder();
|
||||
|
||||
int numRegisters = blob.Length / 2;
|
||||
for (int i = 0; i < numRegisters; i++)
|
||||
{
|
||||
int addr = reverseRegisterOrder
|
||||
? address + numRegisters - 1 - i
|
||||
: address + i;
|
||||
|
||||
yield return new HoldingRegister
|
||||
{
|
||||
Address = (ushort)addr,
|
||||
HighByte = blob[i * 2],
|
||||
LowByte = blob[i * 2 + 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AMWD.Protocols.Modbus.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom extensions for <see cref="ModbusObject"/>s.
|
||||
/// </summary>
|
||||
public static class ModbusUnsignedExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ModbusObject"/> into a <see cref="byte"/> value.
|
||||
/// </summary>
|
||||
/// <param name="obj">The Modbus object.</param>
|
||||
/// <returns>The objects byte value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the object is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the wrong types are provided.</exception>
|
||||
public static byte GetByte(this ModbusObject obj)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(obj);
|
||||
#else
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
#endif
|
||||
|
||||
if (obj is HoldingRegister holdingRegister)
|
||||
return (byte)holdingRegister.Value;
|
||||
|
||||
if (obj is InputRegister inputRegister)
|
||||
return (byte)inputRegister.Value;
|
||||
|
||||
throw new ArgumentException($"The object type '{obj.GetType()}' is invalid", nameof(obj));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ModbusObject"/> into a <see cref="ushort"/> value.
|
||||
/// </summary>
|
||||
/// <param name="obj">The Modbus object.</param>
|
||||
/// <returns>The objects unsigned short value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the object is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the wrong types are provided.</exception>
|
||||
public static ushort GetUInt16(this ModbusObject obj)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(obj);
|
||||
#else
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
#endif
|
||||
|
||||
if (obj is HoldingRegister holdingRegister)
|
||||
return holdingRegister.Value;
|
||||
|
||||
if (obj is InputRegister inputRegister)
|
||||
return inputRegister.Value;
|
||||
|
||||
throw new ArgumentException($"The object type '{obj.GetType()}' is invalid", nameof(obj));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts multiple <see cref="ModbusObject"/>s into a <see cref="uint"/> value.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of Modbus objects.</param>
|
||||
/// <param name="startIndex">The first index to use.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The objects unsigned int value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the list is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the list is too short or the list contains mixed/incompatible objects.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">when the <paramref name="startIndex"/> is too high.</exception>
|
||||
public static uint GetUInt32(this IEnumerable<ModbusObject> list, int startIndex = 0, bool reverseRegisterOrder = false)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(list);
|
||||
#else
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
#endif
|
||||
|
||||
int count = list.Count();
|
||||
if (count < 2)
|
||||
throw new ArgumentException("At least two registers required", nameof(list));
|
||||
|
||||
if (startIndex < 0 || startIndex + 2 > count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
|
||||
if (!list.All(o => o.Type == ModbusObjectType.HoldingRegister) && !list.All(o => o.Type == ModbusObjectType.InputRegister))
|
||||
throw new ArgumentException("Mixed object typs found", nameof(list));
|
||||
|
||||
var registers = list.OrderBy(o => o.Address).Skip(startIndex).Take(2).ToArray();
|
||||
if (reverseRegisterOrder)
|
||||
Array.Reverse(registers);
|
||||
|
||||
byte[] blob = new byte[registers.Length * 2];
|
||||
for (int i = 0; i < registers.Length; i++)
|
||||
{
|
||||
blob[i * 2] = registers[i].HighByte;
|
||||
blob[i * 2 + 1] = registers[i].LowByte;
|
||||
}
|
||||
|
||||
blob.SwapNetworkOrder();
|
||||
return BitConverter.ToUInt32(blob, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts multiple <see cref="ModbusObject"/>s into a <see cref="ulong"/> value.
|
||||
/// </summary>
|
||||
/// <param name="list">The list of Modbus objects.</param>
|
||||
/// <param name="startIndex">The first index to use.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The objects unsigned long value.</returns>
|
||||
/// <exception cref="ArgumentNullException">when the list is null.</exception>
|
||||
/// <exception cref="ArgumentException">when the list is too short or the list contains mixed/incompatible objects.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">when the <paramref name="startIndex"/> is too high.</exception>
|
||||
public static ulong GetUInt64(this IEnumerable<ModbusObject> list, int startIndex = 0, bool reverseRegisterOrder = false)
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(list);
|
||||
#else
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
#endif
|
||||
|
||||
int count = list.Count();
|
||||
if (count < 4)
|
||||
throw new ArgumentException("At least four registers required", nameof(list));
|
||||
|
||||
if (startIndex < 0 || startIndex + 4 > count)
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex));
|
||||
|
||||
if (!list.All(o => o.Type == ModbusObjectType.HoldingRegister) && !list.All(o => o.Type == ModbusObjectType.InputRegister))
|
||||
throw new ArgumentException("Mixed object typs found", nameof(list));
|
||||
|
||||
var registers = list.OrderBy(o => o.Address).Skip(startIndex).Take(4).ToArray();
|
||||
if (reverseRegisterOrder)
|
||||
Array.Reverse(registers);
|
||||
|
||||
byte[] blob = new byte[registers.Length * 2];
|
||||
for (int i = 0; i < registers.Length; i++)
|
||||
{
|
||||
blob[i * 2] = registers[i].HighByte;
|
||||
blob[i * 2 + 1] = registers[i].LowByte;
|
||||
}
|
||||
|
||||
blob.SwapNetworkOrder();
|
||||
return BitConverter.ToUInt64(blob, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="byte"/> value to a <see cref="HoldingRegister"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The byte value.</param>
|
||||
/// <param name="address">The register address.</param>
|
||||
/// <returns>The register.</returns>
|
||||
public static HoldingRegister ToRegister(this byte value, ushort address)
|
||||
{
|
||||
return new HoldingRegister
|
||||
{
|
||||
Address = address,
|
||||
LowByte = value
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ushort"/> value to a <see cref="HoldingRegister"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The unsigned short value.</param>
|
||||
/// <param name="address">The register address.</param>
|
||||
/// <returns>The register.</returns>
|
||||
public static HoldingRegister ToRegister(this ushort value, ushort address)
|
||||
{
|
||||
byte[] blob = BitConverter.GetBytes(value);
|
||||
blob.SwapNetworkOrder();
|
||||
|
||||
return new HoldingRegister
|
||||
{
|
||||
Address = address,
|
||||
HighByte = blob[0],
|
||||
LowByte = blob[1]
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="uint"/> value to a list of <see cref="HoldingRegister"/>s.
|
||||
/// </summary>
|
||||
/// <param name="value">The unsigned int value.</param>
|
||||
/// <param name="address">The first register address.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The list of registers.</returns>
|
||||
public static IEnumerable<HoldingRegister> ToRegister(this uint value, ushort address, bool reverseRegisterOrder = false)
|
||||
{
|
||||
byte[] blob = BitConverter.GetBytes(value);
|
||||
blob.SwapNetworkOrder();
|
||||
|
||||
int numRegisters = blob.Length / 2;
|
||||
for (int i = 0; i < numRegisters; i++)
|
||||
{
|
||||
int addr = reverseRegisterOrder
|
||||
? address + numRegisters - 1 - i
|
||||
: address + i;
|
||||
|
||||
yield return new HoldingRegister
|
||||
{
|
||||
Address = (ushort)addr,
|
||||
HighByte = blob[i * 2],
|
||||
LowByte = blob[i * 2 + 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="ulong"/> value to a list of <see cref="HoldingRegister"/>s.
|
||||
/// </summary>
|
||||
/// <param name="value">The unsigned long value.</param>
|
||||
/// <param name="address">The first register address.</param>
|
||||
/// <param name="reverseRegisterOrder">Indicates whehter the taken registers should be reversed.</param>
|
||||
/// <returns>The list of registers.</returns>
|
||||
public static IEnumerable<HoldingRegister> ToRegister(this ulong value, ushort address, bool reverseRegisterOrder = false)
|
||||
{
|
||||
byte[] blob = BitConverter.GetBytes(value);
|
||||
blob.SwapNetworkOrder();
|
||||
|
||||
int numRegisters = blob.Length / 2;
|
||||
for (int i = 0; i < numRegisters; i++)
|
||||
{
|
||||
int addr = reverseRegisterOrder
|
||||
? address + numRegisters - 1 - i
|
||||
: address + i;
|
||||
|
||||
yield return new HoldingRegister
|
||||
{
|
||||
Address = (ushort)addr,
|
||||
HighByte = blob[i * 2],
|
||||
LowByte = blob[i * 2 + 1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user