diff --git a/MewtocolNet/CustomTypes/DWord.cs b/MewtocolNet/CustomTypes/DWord.cs
new file mode 100644
index 0000000..2bef2bd
--- /dev/null
+++ b/MewtocolNet/CustomTypes/DWord.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace MewtocolNet {
+
+ ///
+ /// A DWord is a 16 bit value of 2 bytes
+ ///
+ public struct DWord : MewtocolExtensionTypeDDT {
+
+ private int bitLength;
+
+ internal uint value;
+
+ public uint Value {
+ get => value;
+ set {
+ this.value = value;
+ }
+ }
+
+ public DWord(uint bytes) {
+ value = bytes;
+ bitLength = Marshal.SizeOf(value) * 8;
+ }
+ public DWord(byte[] bytes) {
+ bytes = bytes.Take(4).ToArray();
+ value = BitConverter.ToUInt32(bytes, 0);
+ bitLength = Marshal.SizeOf(value) * 8;
+ }
+
+ //operations
+
+ public static DWord operator -(DWord a, DWord b) => new DWord() {
+ value = (ushort)(a.value - b.value)
+ };
+
+ public static DWord operator +(DWord a, DWord b) => new DWord() {
+ value = (ushort)(a.value + b.value)
+ };
+
+ public static DWord operator *(DWord a, DWord b) => new DWord() {
+ value = (ushort)(a.value * b.value)
+ };
+
+ public static DWord operator /(DWord a, DWord b) => new DWord() {
+ value = (ushort)(a.value / b.value)
+ };
+
+ public static bool operator ==(DWord a, DWord b) => a.value == b.value;
+
+ public static bool operator !=(DWord a, DWord b) => a.value != b.value;
+
+ ///
+ /// Gets or sets the bit value at the given position
+ ///
+ public bool this[int bitIndex] {
+ get {
+ if (bitIndex > bitLength - 1)
+ throw new IndexOutOfRangeException($"The DWord bit index was out of range ({bitIndex}/{bitLength - 1})");
+
+ return (value & (1 << bitIndex)) != 0;
+ }
+ set {
+ if (bitIndex > bitLength - 1)
+ throw new IndexOutOfRangeException($"The DWord bit index was out of range ({bitIndex}/{bitLength - 1})");
+
+ int mask = 1 << bitIndex;
+ this.value = value ? this.value |= (uint)mask : this.value &= (uint)~mask;
+ }
+ }
+
+ public void ClearBits () => this.value = 0;
+
+ public override bool Equals(object obj) {
+
+ if ((obj == null) || !this.GetType().Equals(obj.GetType())) {
+ return false;
+ } else {
+ return (DWord)obj == this;
+ }
+
+ }
+
+ public override int GetHashCode() => (int)value;
+
+ public byte[] ToByteArray() => BitConverter.GetBytes(value);
+
+ //string ops
+
+ public override string ToString() => $"0x{value.ToString("X8")}";
+
+ public string ToStringBits () {
+
+ return Convert.ToString(value, 2).PadLeft(bitLength, '0');
+
+ }
+
+ public string ToStringBitsPlc () {
+
+ var parts = Convert.ToString(value, 2)
+ .PadLeft(Marshal.SizeOf(value) * 8, '0')
+ .SplitInParts(4);
+
+ return string.Join("_", parts);
+
+ }
+
+ }
+
+}
diff --git a/MewtocolNet/CustomTypes/MewtocolExtensionType.cs b/MewtocolNet/CustomTypes/MewtocolExtensionType.cs
new file mode 100644
index 0000000..85cb76c
--- /dev/null
+++ b/MewtocolNet/CustomTypes/MewtocolExtensionType.cs
@@ -0,0 +1,7 @@
+namespace MewtocolNet {
+
+ internal interface MewtocolExtensionTypeDT { }
+
+ internal interface MewtocolExtensionTypeDDT { }
+
+}
diff --git a/MewtocolNet/CustomTypes/Word.cs b/MewtocolNet/CustomTypes/Word.cs
new file mode 100644
index 0000000..b544e51
--- /dev/null
+++ b/MewtocolNet/CustomTypes/Word.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace MewtocolNet {
+
+ ///
+ /// A word is a 16 bit value of 2 bytes
+ ///
+ public struct Word : MewtocolExtensionTypeDT {
+
+ private int bitLength;
+
+ internal ushort value;
+
+ public ushort Value {
+ get => value;
+ set {
+ this.value = value;
+ }
+ }
+
+ public Word(ushort bytes) {
+ value = bytes;
+ bitLength = Marshal.SizeOf(value) * 8;
+ }
+ public Word(byte[] bytes) {
+ bytes = bytes.Take(2).ToArray();
+ value = BitConverter.ToUInt16(bytes, 0);
+ bitLength = Marshal.SizeOf(value) * 8;
+ }
+
+ //operations
+
+ public static Word operator -(Word a, Word b) => new Word() {
+ value = (ushort)(a.value - b.value)
+ };
+
+ public static Word operator +(Word a, Word b) => new Word() {
+ value = (ushort)(a.value + b.value)
+ };
+
+ public static Word operator *(Word a, Word b) => new Word() {
+ value = (ushort)(a.value * b.value)
+ };
+
+ public static Word operator /(Word a, Word b) => new Word() {
+ value = (ushort)(a.value / b.value)
+ };
+
+ public static bool operator ==(Word a, Word b) => a.value == b.value;
+
+ public static bool operator !=(Word a, Word b) => a.value != b.value;
+
+ ///
+ /// Gets or sets the bit value at the given position
+ ///
+ public bool this[int bitIndex] {
+ get {
+ if (bitIndex > bitLength - 1)
+ throw new IndexOutOfRangeException($"The word bit index was out of range ({bitIndex}/{bitLength - 1})");
+
+ return (value & (1 << bitIndex)) != 0;
+ }
+ set {
+ if (bitIndex > bitLength - 1)
+ throw new IndexOutOfRangeException($"The word bit index was out of range ({bitIndex}/{bitLength - 1})");
+
+ int mask = 1 << bitIndex;
+ this.value = value ? this.value |= (ushort)mask : this.value &= (ushort)~mask;
+ }
+ }
+
+ public void ClearBits () => this.value = 0;
+
+ public override bool Equals(object obj) {
+
+ if ((obj == null) || !this.GetType().Equals(obj.GetType())) {
+ return false;
+ } else {
+ return (Word)obj == this;
+ }
+
+ }
+
+ public override int GetHashCode() => (int)value;
+
+ public byte[] ToByteArray() => BitConverter.GetBytes(value);
+
+ //string ops
+
+ public override string ToString() => $"0x{value.ToString("X4")}";
+
+ public string ToStringBits () {
+
+ return Convert.ToString(value, 2).PadLeft(bitLength, '0');
+
+ }
+
+ public string ToStringBitsPlc () {
+
+ var parts = Convert.ToString(value, 2)
+ .PadLeft(Marshal.SizeOf(value) * 8, '0')
+ .SplitInParts(4);
+
+ return string.Join("_", parts);
+
+ }
+
+ }
+
+}
diff --git a/MewtocolNet/Helpers/MewtocolHelpers.cs b/MewtocolNet/Helpers/MewtocolHelpers.cs
index 2191b05..493f44d 100644
--- a/MewtocolNet/Helpers/MewtocolHelpers.cs
+++ b/MewtocolNet/Helpers/MewtocolHelpers.cs
@@ -5,8 +5,10 @@ using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
+using static MewtocolNet.RegisterBuilding.RBuild;
namespace MewtocolNet {
@@ -252,14 +254,18 @@ namespace MewtocolNet {
}
- internal static bool CompareIsDuplicateNonCast (this BaseRegister reg1, BaseRegister compare, bool ingnoreByteRegisters = true) {
+ internal static bool CompareIsDuplicateNonCast (this BaseRegister toInsert, BaseRegister compare, List allowOverlappingTypes) {
- if (ingnoreByteRegisters && (compare.GetType() == typeof(BytesRegister) || reg1.GetType() == typeof(BytesRegister))) return false;
+ foreach (var type in allowOverlappingTypes) {
- bool valCompare = reg1.GetType() != compare.GetType() &&
- reg1.MemoryAddress == compare.MemoryAddress &&
- reg1.GetRegisterAddressLen() == compare.GetRegisterAddressLen() &&
- reg1.GetSpecialAddress() == compare.GetSpecialAddress();
+ if (toInsert.GetType() == type) return false;
+
+ }
+
+ bool valCompare = toInsert.GetType() != compare.GetType() &&
+ toInsert.MemoryAddress == compare.MemoryAddress &&
+ toInsert.GetRegisterAddressLen() == compare.GetRegisterAddressLen() &&
+ toInsert.GetSpecialAddress() == compare.GetSpecialAddress();
return valCompare;
diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs
index 147ee10..5abde05 100644
--- a/MewtocolNet/MewtocolInterface.cs
+++ b/MewtocolNet/MewtocolInterface.cs
@@ -156,10 +156,11 @@ namespace MewtocolNet {
private void OnRegisterChanged(IRegister o) {
- var asInternal = (IRegisterInternal)o;
+ var asInternal = (BaseRegister)o;
Logger.Log($"{asInternal.GetMewName()} " +
$"{(o.Name != null ? $"({o.Name}) " : "")}" +
+ $"{asInternal.underlyingSystemType} " +
$"changed to \"{asInternal.GetValueString()}\"", LogLevel.Change, this);
OnRegisterChangedUpdateProps((IRegisterInternal)o);
diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
index 1c80a4f..28dc91e 100644
--- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
+++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
@@ -290,20 +290,7 @@ namespace MewtocolNet {
internal void InsertRegistersToMemoryStack (List registers) {
- //order by address
- registers = registers.OrderBy(x => x.GetSpecialAddress()).ToList();
- registers = registers.OrderBy(x => x.MemoryAddress).ToList();
-
- //link to memory manager
- for (int i = 0, j = 0; i < registers.Count; i++) {
-
- BaseRegister reg = registers[i];
- reg.name = $"auto_prop_register_{j + 1}";
-
- //link the memory area to the register
- if (memoryManager.LinkRegister(reg)) j++;
-
- }
+ memoryManager.LinkRegisters(registers);
}
@@ -408,7 +395,7 @@ namespace MewtocolNet {
}
- internal void InvokeRegisterChanged(IRegister reg) {
+ internal void InvokeRegisterChanged(BaseRegister reg) {
RegisterChanged?.Invoke(reg);
diff --git a/MewtocolNet/RegisterBuilding/RBuild.cs b/MewtocolNet/RegisterBuilding/RBuild.cs
index 7547560..28087e0 100644
--- a/MewtocolNet/RegisterBuilding/RBuild.cs
+++ b/MewtocolNet/RegisterBuilding/RBuild.cs
@@ -362,13 +362,14 @@ namespace MewtocolNet.RegisterBuilding {
/// - Boolean R/X/Y registers
/// - 16 bit signed integer
/// - 16 bit un-signed integer
+ /// - 16 bit word (2 bytes)
/// - 32 bit signed integer
/// - 32 bit un-signed integer
+ /// - 32 bit word (4 bytes)
/// - 32 bit floating point
/// - 32 bit time from interpreted as
- /// - 16 or 32 bit enums
+ /// - 16 or 32 bit enums, also supports flags
/// - String of chars, the interface will automatically get the length
- /// - As an array of bits
/// - As an array of bytes
///
///
@@ -611,7 +612,7 @@ namespace MewtocolNet.RegisterBuilding {
var assembler = new RegisterAssembler(attachedPLC);
var tempRegister = assembler.Assemble(reg.Data);
- return await tempRegister.WriteToAnonymousAsync(value);
+ return await tempRegister.WriteAsync(value);
}
@@ -619,7 +620,7 @@ namespace MewtocolNet.RegisterBuilding {
var assembler = new RegisterAssembler(attachedPLC);
var tempRegister = assembler.Assemble(reg.Data);
- return await tempRegister.WriteToAnonymousAsync(value);
+ return await tempRegister.WriteAsync(value);
}
@@ -627,7 +628,7 @@ namespace MewtocolNet.RegisterBuilding {
var assembler = new RegisterAssembler(attachedPLC);
var tempRegister = assembler.Assemble(reg.Data);
- return await tempRegister.ReadFromAnonymousAsync();
+ return await tempRegister.ReadAsync();
}
@@ -635,7 +636,7 @@ namespace MewtocolNet.RegisterBuilding {
var assembler = new RegisterAssembler(attachedPLC);
var tempRegister = assembler.Assemble(reg.Data);
- return (T)await tempRegister.ReadFromAnonymousAsync();
+ return (T)await tempRegister.ReadAsync();
}
diff --git a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
index 86d8e4b..8accdce 100644
--- a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
+++ b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
@@ -3,6 +3,7 @@ using MewtocolNet.RegisterAttributes;
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
+using System.Data;
using System.Reflection;
using System.Runtime.InteropServices;
using static MewtocolNet.RegisterBuilding.RBuild;
@@ -49,9 +50,9 @@ namespace MewtocolNet.RegisterBuilding {
//as numeric register with enum target
var underlying = Enum.GetUnderlyingType(data.dotnetVarType);
- var enuSize = Marshal.SizeOf(underlying);
+ int numericSize = Marshal.SizeOf(underlying);
- if (enuSize > 4)
+ if (numericSize > 4)
throw new NotSupportedException("Enums not based on 16 or 32 bit numbers are not supported");
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
@@ -61,6 +62,8 @@ namespace MewtocolNet.RegisterBuilding {
var parameters = new object[] { data.memAddress, data.name };
var instance = (BaseRegister)constr.Invoke(parameters);
+ instance.RegisterType = numericSize > 2 ? RegisterType.DDT : RegisterType.DT;
+
generatedInstance = instance;
} else if (registerClassType.IsGenericType) {
@@ -74,23 +77,31 @@ namespace MewtocolNet.RegisterBuilding {
//int _adress, Type _enumType = null, string _name = null
var parameters = new object[] { data.memAddress, data.name };
var instance = (BaseRegister)Activator.CreateInstance(registerClassType, flags, null, parameters, null);
- instance.pollLevel = data.pollLevel;
+
+ int numericSize = 0;
+ bool isExtensionTypeDT = typeof(MewtocolExtensionTypeDT).IsAssignableFrom(data.dotnetVarType);
+ bool isExtensionTypeDDT = typeof(MewtocolExtensionTypeDDT).IsAssignableFrom(data.dotnetVarType);
+
+ if (data.dotnetVarType.Namespace == "System") {
+ numericSize = Marshal.SizeOf(data.dotnetVarType);
+ } else if(isExtensionTypeDT) {
+ numericSize = 2;
+ } else if(isExtensionTypeDDT) {
+ numericSize = 4;
+ } else {
+ throw new NotSupportedException($"The type {data.dotnetVarType} is not supported for NumberRegisters");
+ }
+
+ instance.RegisterType = numericSize > 2 ? RegisterType.DDT : RegisterType.DT;
+
generatedInstance = instance;
- } else if (registerClassType == typeof(BytesRegister) && data.byteSize != null) {
+ } else if (registerClassType == typeof(ArrayRegister) && data.byteSize != null) {
//-------------------------------------------
//as byte range register
- BytesRegister instance = new BytesRegister(data.memAddress, (uint)data.byteSize, data.name);
- generatedInstance = instance;
-
- } else if (registerClassType == typeof(BytesRegister) && data.bitSize != null) {
-
- //-------------------------------------------
- //as bit range register
-
- BytesRegister instance = new BytesRegister(data.memAddress, (ushort)data.bitSize, data.name);
+ ArrayRegister instance = new ArrayRegister(data.memAddress, (uint)data.byteSize, data.name);
generatedInstance = instance;
} else if (registerClassType == typeof(StringRegister)) {
@@ -125,8 +136,13 @@ namespace MewtocolNet.RegisterBuilding {
if (collectionTarget != null)
generatedInstance.WithRegisterCollection(collectionTarget);
- generatedInstance.attachedInterface = onInterface;
+ if (data.boundProperty != null)
+ generatedInstance.WithBoundProperty(new RegisterPropTarget {
+ BoundProperty = data.boundProperty,
+ });
+ generatedInstance.attachedInterface = onInterface;
+ generatedInstance.underlyingSystemType = data.dotnetVarType;
generatedInstance.pollLevel = data.pollLevel;
return generatedInstance;
diff --git a/MewtocolNet/Registers/ArrayRegister.cs b/MewtocolNet/Registers/ArrayRegister.cs
new file mode 100644
index 0000000..04facc4
--- /dev/null
+++ b/MewtocolNet/Registers/ArrayRegister.cs
@@ -0,0 +1,107 @@
+using MewtocolNet.Exceptions;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MewtocolNet.Registers {
+
+ ///
+ /// Defines a register containing a string
+ ///
+ public class ArrayRegister : BaseRegister {
+
+ internal uint addressLength;
+
+ ///
+ /// The rgisters memory length
+ ///
+ public uint AddressLength => addressLength;
+
+ internal uint ReservedBytesSize { get; set; }
+
+ internal ushort? ReservedBitSize { get; set; }
+
+ [Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
+ public ArrayRegister() =>
+ throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
+
+ internal ArrayRegister(uint _address, uint _reservedByteSize, string _name = null) {
+
+ name = _name;
+ memoryAddress = _address;
+ ReservedBytesSize = _reservedByteSize;
+
+ //calc mem length
+ //because one register is always 1 word (2 bytes) long, if the bytecount is uneven we get the trailing word too
+ var byteSize = ReservedBytesSize;
+ if (ReservedBytesSize % 2 != 0) byteSize++;
+
+ RegisterType = RegisterType.DT_BYTE_RANGE;
+ addressLength = Math.Max((byteSize / 2), 1);
+
+ CheckAddressOverflow(memoryAddress, addressLength);
+
+ lastValue = null;
+
+ }
+
+ public override string GetValueString() {
+
+ if (Value == null) return "null";
+
+ if(Value != null && Value is BitArray bitArr) {
+
+ return bitArr.ToBitString();
+
+ } else {
+
+ return ((byte[])Value).ToHexString("-");
+
+ }
+
+ }
+
+ ///
+ public override string BuildMewtocolQuery() {
+
+ StringBuilder asciistring = new StringBuilder("D");
+
+ asciistring.Append(MemoryAddress.ToString().PadLeft(5, '0'));
+ asciistring.Append((MemoryAddress + AddressLength - 1).ToString().PadLeft(5, '0'));
+
+ return asciistring.ToString();
+ }
+
+ ///
+ public override string GetRegisterString() => "DT";
+
+ ///
+ public override uint GetRegisterAddressLen() => AddressLength;
+
+ ///
+ internal override void UpdateHoldingValue(object val) {
+
+ bool changeTriggerBitArr = val is BitArray bitArr &&
+ lastValue is BitArray bitArr2 &&
+ (bitArr.ToBitString() != bitArr2.ToBitString());
+
+ bool changeTriggerGeneral = (lastValue?.ToString() != val?.ToString());
+
+ if (changeTriggerBitArr || changeTriggerGeneral) {
+
+ lastValue = val;
+
+ TriggerNotifyChange();
+ attachedInterface.InvokeRegisterChanged(this);
+
+ }
+
+ }
+
+ }
+
+}
diff --git a/MewtocolNet/Registers/BaseRegister.cs b/MewtocolNet/Registers/BaseRegister.cs
index ac36db9..853454a 100644
--- a/MewtocolNet/Registers/BaseRegister.cs
+++ b/MewtocolNet/Registers/BaseRegister.cs
@@ -19,8 +19,10 @@ namespace MewtocolNet.Registers {
//links to
internal RegisterCollection containedCollection;
internal MewtocolInterface attachedInterface;
- internal List boundToProps = new List();
+ internal List boundProperties = new List();
+
+ internal Type underlyingSystemType;
internal IMemoryArea underlyingMemory;
internal object lastValue = null;
internal string name;
@@ -40,7 +42,7 @@ namespace MewtocolNet.Registers {
public object Value => lastValue;
///
- public RegisterType RegisterType { get; protected set; }
+ public RegisterType RegisterType { get; internal set; }
///
public string Name => name;
@@ -59,9 +61,9 @@ namespace MewtocolNet.Registers {
#endregion
- public virtual void ClearValue() => SetValueFromPLC(null);
+ public virtual void ClearValue() => UpdateHoldingValue(null);
- public virtual void SetValueFromPLC(object val) {
+ internal virtual void UpdateHoldingValue(object val) {
if(lastValue?.ToString() != val?.ToString()) {
@@ -78,7 +80,14 @@ namespace MewtocolNet.Registers {
internal void WithRegisterCollection (RegisterCollection collection) => containedCollection = collection;
- internal void WithBoundProperty(RegisterPropTarget propInfo) => boundToProps.Add(propInfo);
+ internal void WithBoundProperty(RegisterPropTarget propInfo) => boundProperties.Add(propInfo);
+
+ internal void WithBoundProperties(IEnumerable propInfos) {
+
+ foreach (var item in propInfos)
+ boundProperties.Add(item);
+
+ }
#region Read / Write
@@ -86,10 +95,6 @@ namespace MewtocolNet.Registers {
public virtual Task WriteAsync(object data) => throw new NotImplementedException();
- internal virtual Task WriteToAnonymousAsync (object value) => throw new NotImplementedException();
-
- internal virtual Task