using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace AMWD.Protocols.Modbus.Common { /// /// Custom extensions for s. /// public static class ModbusExtensions { /// /// Converts a into a value. /// /// The Modbus object. /// The objects bool value. /// when the object is null. 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; } /// /// Converts multiple s into a value. /// /// The list of Modbus objects. /// The number of registers to use. /// The first index to use. /// The encoding used to convert the text. (Default: ) /// Indicates whehter the taken registers should be reversed. /// Indicates whether to reverse high and low byte per register. /// The objects text value. /// when the list is null. /// when the list is too short or the list contains mixed/incompatible objects. /// when the is too high. public static string GetString(this IEnumerable 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; } /// /// Converts a value to a . /// /// The bool value. /// The coil address. /// The coil. public static Coil ToCoil(this bool value, ushort address) { return new Coil { Address = address, Value = value }; } /// /// Converts a value to a . /// /// The bool value. /// The register address. /// The register. public static HoldingRegister ToRegister(this bool value, ushort address) { return new HoldingRegister { Address = address, Value = (ushort)(value ? 1 : 0) }; } /// /// Converts a value to a . /// /// The text. /// The address of the text. /// The encoding used to convert the text. (Default: ) /// Indicates whehter the taken registers should be reversed. /// Indicates whether to reverse high and low byte per register. /// The registers. /// when the text is null. public static IEnumerable 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; } } } }