mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Add Dword / Word
This commit is contained in:
117
MewtocolNet/CustomTypes/DWord.cs
Normal file
117
MewtocolNet/CustomTypes/DWord.cs
Normal file
@@ -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 {
|
||||
|
||||
/// <summary>
|
||||
/// A DWord is a 16 bit value of 2 bytes
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bit value at the given position
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
7
MewtocolNet/CustomTypes/MewtocolExtensionType.cs
Normal file
7
MewtocolNet/CustomTypes/MewtocolExtensionType.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace MewtocolNet {
|
||||
|
||||
internal interface MewtocolExtensionTypeDT { }
|
||||
|
||||
internal interface MewtocolExtensionTypeDDT { }
|
||||
|
||||
}
|
||||
117
MewtocolNet/CustomTypes/Word.cs
Normal file
117
MewtocolNet/CustomTypes/Word.cs
Normal file
@@ -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 {
|
||||
|
||||
/// <summary>
|
||||
/// A word is a 16 bit value of 2 bytes
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bit value at the given position
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Type> 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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -290,20 +290,7 @@ namespace MewtocolNet {
|
||||
|
||||
internal void InsertRegistersToMemoryStack (List<BaseRegister> 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);
|
||||
|
||||
|
||||
@@ -362,13 +362,14 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
/// <item><term><see cref="bool"/></term><description>Boolean R/X/Y registers</description></item>
|
||||
/// <item><term><see cref="short"/></term><description>16 bit signed integer</description></item>
|
||||
/// <item><term><see cref="ushort"/></term><description>16 bit un-signed integer</description></item>
|
||||
/// <item><term><see cref="Word"/></term><description>16 bit word (2 bytes)</description></item>
|
||||
/// <item><term><see cref="int"/></term><description>32 bit signed integer</description></item>
|
||||
/// <item><term><see cref="uint"/></term><description>32 bit un-signed integer</description></item>
|
||||
/// <item><term><see cref="DWord"/></term><description>32 bit word (4 bytes)</description></item>
|
||||
/// <item><term><see cref="float"/></term><description>32 bit floating point</description></item>
|
||||
/// <item><term><see cref="TimeSpan"/></term><description>32 bit time from <see cref="PlcVarType.TIME"/> interpreted as <see cref="TimeSpan"/></description></item>
|
||||
/// <item><term><see cref="Enum"/></term><description>16 or 32 bit enums</description></item>
|
||||
/// <item><term><see cref="Enum"/></term><description>16 or 32 bit enums, also supports flags</description></item>
|
||||
/// <item><term><see cref="string"/></term><description>String of chars, the interface will automatically get the length</description></item>
|
||||
/// <item><term><see cref="BitArray"/></term><description>As an array of bits</description></item>
|
||||
/// <item><term><see cref="byte[]"/></term><description>As an array of bytes</description></item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
107
MewtocolNet/Registers/ArrayRegister.cs
Normal file
107
MewtocolNet/Registers/ArrayRegister.cs
Normal file
@@ -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 {
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing a string
|
||||
/// </summary>
|
||||
public class ArrayRegister : BaseRegister {
|
||||
|
||||
internal uint addressLength;
|
||||
|
||||
/// <summary>
|
||||
/// The rgisters memory length
|
||||
/// </summary>
|
||||
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("-");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetRegisterString() => "DT";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override uint GetRegisterAddressLen() => AddressLength;
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,8 +19,10 @@ namespace MewtocolNet.Registers {
|
||||
//links to
|
||||
internal RegisterCollection containedCollection;
|
||||
internal MewtocolInterface attachedInterface;
|
||||
internal List<RegisterPropTarget> boundToProps = new List<RegisterPropTarget>();
|
||||
|
||||
internal List<RegisterPropTarget> boundProperties = new List<RegisterPropTarget>();
|
||||
|
||||
internal Type underlyingSystemType;
|
||||
internal IMemoryArea underlyingMemory;
|
||||
internal object lastValue = null;
|
||||
internal string name;
|
||||
@@ -40,7 +42,7 @@ namespace MewtocolNet.Registers {
|
||||
public object Value => lastValue;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public RegisterType RegisterType { get; protected set; }
|
||||
public RegisterType RegisterType { get; internal set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
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<RegisterPropTarget> propInfos) {
|
||||
|
||||
foreach (var item in propInfos)
|
||||
boundProperties.Add(item);
|
||||
|
||||
}
|
||||
|
||||
#region Read / Write
|
||||
|
||||
@@ -86,10 +95,6 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
public virtual Task<bool> WriteAsync(object data) => throw new NotImplementedException();
|
||||
|
||||
internal virtual Task<bool> WriteToAnonymousAsync (object value) => throw new NotImplementedException();
|
||||
|
||||
internal virtual Task<object> ReadFromAnonymousAsync () => throw new NotImplementedException();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Default accessors
|
||||
@@ -147,18 +152,36 @@ namespace MewtocolNet.Registers {
|
||||
else successfulWrites++;
|
||||
}
|
||||
|
||||
internal virtual bool IsSameAddressAndType (BaseRegister toCompare) {
|
||||
|
||||
return this.MemoryAddress == toCompare.MemoryAddress &&
|
||||
this.RegisterType == toCompare.RegisterType &&
|
||||
this.GetRegisterAddressLen() == toCompare.GetRegisterAddressLen() &&
|
||||
this.GetSpecialAddress() == toCompare.GetSpecialAddress();
|
||||
|
||||
}
|
||||
|
||||
internal virtual bool IsSameAddress (BaseRegister toCompare) {
|
||||
|
||||
return (this.MemoryAddress == toCompare.MemoryAddress) &&
|
||||
(this.GetRegisterAddressLen() == toCompare.GetRegisterAddressLen()) &&
|
||||
(this.GetSpecialAddress() == toCompare.GetSpecialAddress());
|
||||
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(GetMewName());
|
||||
if(Name != null) sb.Append($" ({Name})");
|
||||
sb.Append($" [{this.GetType().Name}({underlyingSystemType.Name})]");
|
||||
if (Value != null) sb.Append($" Val: {GetValueString()}");
|
||||
|
||||
return sb.ToString();
|
||||
|
||||
}
|
||||
|
||||
public virtual string ToString(bool additional) {
|
||||
public virtual string ToString (bool additional) {
|
||||
|
||||
if (!additional) return this.ToString();
|
||||
|
||||
@@ -166,16 +189,30 @@ namespace MewtocolNet.Registers {
|
||||
sb.AppendLine($"MewName: {GetMewName()}");
|
||||
sb.AppendLine($"Name: {Name ?? "Not named"}");
|
||||
sb.AppendLine($"Value: {GetValueString()}");
|
||||
sb.AppendLine($"Perf. Reads: {successfulReads}, Writes: {successfulWrites}");
|
||||
sb.AppendLine($"Register Type: {RegisterType}");
|
||||
sb.AppendLine($"Address: {GetRegisterWordRangeString()}");
|
||||
if(this is StringRegister sr) sb.AppendLine($"Reserved: {sr.ReservedSize}, Used: {sr.UsedSize}");
|
||||
|
||||
return sb.ToString();
|
||||
|
||||
}
|
||||
|
||||
public virtual string Explain () {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine($"MewName: {GetMewName()}");
|
||||
sb.AppendLine($"Name: {Name ?? "Not named"}");
|
||||
sb.AppendLine($"Value: {GetValueString()}");
|
||||
sb.AppendLine($"Perf. Reads: {successfulReads}, Writes: {successfulWrites}");
|
||||
sb.AppendLine($"Register Type: {RegisterType}");
|
||||
sb.AppendLine($"Underlying System Type: {underlyingSystemType}");
|
||||
sb.AppendLine($"Address: {GetRegisterWordRangeString()}");
|
||||
if (this is StringRegister sr) sb.AppendLine($"Reserved: {sr.ReservedSize}, Used: {sr.UsedSize}");
|
||||
if (GetSpecialAddress() != null) sb.AppendLine($"SPAddress: {GetSpecialAddress():X1}");
|
||||
if (GetType().IsGenericType) sb.AppendLine($"Type: NumberRegister<{GetType().GenericTypeArguments[0]}>");
|
||||
else sb.AppendLine($"Type: {GetType()}");
|
||||
if(containedCollection != null) sb.AppendLine($"In collection: {containedCollection.GetType()}");
|
||||
if(boundToProps != null && boundToProps.Count != 0)
|
||||
sb.AppendLine($"Bound props: {string.Join(",", boundToProps)}");
|
||||
if (containedCollection != null) sb.AppendLine($"In collection: {containedCollection.GetType()}");
|
||||
if (boundProperties != null && boundProperties.Count > 0) sb.AppendLine($"Bound props: {string.Join(", ", boundProperties)}");
|
||||
else sb.AppendLine("No bound properties");
|
||||
|
||||
return sb.ToString();
|
||||
|
||||
|
||||
@@ -54,57 +54,6 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
}
|
||||
|
||||
#region Read / Write
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<bool> WriteAsync(object data) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
internal override async Task<bool> WriteToAnonymousAsync (object value) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var station = attachedInterface.GetStationNumber();
|
||||
string reqStr = $"%{station}#WCS{BuildMewtocolQuery()}{((bool)value ? "1" : "0")}";
|
||||
var res = await attachedInterface.SendCommandAsync(reqStr);
|
||||
|
||||
return res.Success;
|
||||
|
||||
}
|
||||
|
||||
internal override async Task<object> ReadFromAnonymousAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var station = attachedInterface.GetStationNumber();
|
||||
string requeststring = $"%{station}#RCS{BuildMewtocolQuery()}";
|
||||
var result = await attachedInterface.SendCommandAsync(requeststring);
|
||||
if (!result.Success) return null;
|
||||
|
||||
return result.Response.ParseRCSingleBit();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override byte? GetSpecialAddress() => SpecialAddress;
|
||||
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
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 {
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing a string
|
||||
/// </summary>
|
||||
public class BytesRegister : BaseRegister {
|
||||
|
||||
internal uint addressLength;
|
||||
/// <summary>
|
||||
/// The rgisters memory length
|
||||
/// </summary>
|
||||
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 BytesRegister() =>
|
||||
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
||||
|
||||
internal BytesRegister(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 BytesRegister(uint _address, ushort _reservedBitSize, string _name = null) {
|
||||
|
||||
name = _name;
|
||||
memoryAddress = _address;
|
||||
ReservedBytesSize = (uint)Math.Max(1, _reservedBitSize / 8);
|
||||
ReservedBitSize = _reservedBitSize;
|
||||
|
||||
//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("-");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetValueFromPLC (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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetRegisterString() => "DT";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override uint GetRegisterAddressLen() => AddressLength;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var res = await underlyingMemory.ReadRegisterAsync(this);
|
||||
if (!res) return null;
|
||||
|
||||
var bytes = underlyingMemory.GetUnderlyingBytes(this);
|
||||
|
||||
return SetValueFromBytes(bytes);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<bool> WriteAsync(object data) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
byte[] encoded;
|
||||
|
||||
if (ReservedBitSize != null) {
|
||||
encoded = PlcValueParser.Encode(this, (BitArray)data);
|
||||
} else {
|
||||
encoded = PlcValueParser.Encode(this, (byte[])data);
|
||||
}
|
||||
|
||||
var res = await underlyingMemory.WriteRegisterAsync(this, encoded);
|
||||
if (res) {
|
||||
AddSuccessWrite();
|
||||
SetValueFromPLC(data);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
internal override object SetValueFromBytes(byte[] bytes) {
|
||||
|
||||
AddSuccessRead();
|
||||
|
||||
object parsed;
|
||||
if (ReservedBitSize != null) {
|
||||
parsed = PlcValueParser.Parse<BitArray>(this, bytes);
|
||||
} else {
|
||||
parsed = PlcValueParser.Parse<byte[]>(this, bytes);
|
||||
}
|
||||
|
||||
SetValueFromPLC(parsed);
|
||||
return parsed;
|
||||
|
||||
}
|
||||
|
||||
internal override async Task<bool> WriteToAnonymousAsync(object value) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
return await attachedInterface.WriteByteRange((int)MemoryAddress, (byte[])value);
|
||||
|
||||
}
|
||||
|
||||
internal override async Task<object> ReadFromAnonymousAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2, false);
|
||||
if (res == null) return null;
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,8 +24,6 @@ namespace MewtocolNet {
|
||||
|
||||
// setters
|
||||
|
||||
void SetValueFromPLC(object value);
|
||||
|
||||
void ClearValue();
|
||||
|
||||
// Accessors
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -62,21 +63,6 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetValueFromPLC(object val) {
|
||||
|
||||
if (lastValue?.ToString() != val?.ToString()) {
|
||||
|
||||
if (val != null) lastValue = (T)val;
|
||||
else lastValue = null;
|
||||
|
||||
TriggerNotifyChange();
|
||||
attachedInterface.InvokeRegisterChanged(this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string BuildMewtocolQuery() {
|
||||
|
||||
@@ -108,7 +94,13 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
return $"{Value} [{((TimeSpan)Value).ToPlcTime()}]";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (Value != null && typeof(T) == typeof(Word)) {
|
||||
|
||||
return $"{Value} [{((Word)Value).ToStringBitsPlc()}]";
|
||||
|
||||
}
|
||||
|
||||
if (Value != null && typeof(T).IsEnum) {
|
||||
|
||||
@@ -126,36 +118,43 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override uint GetRegisterAddressLen() => (uint)(RegisterType == RegisterType.DT ? 1 : 2);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<bool> WriteAsync (object value) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var encoded = PlcValueParser.Encode(this, (T)value);
|
||||
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
||||
|
||||
if(res) {
|
||||
|
||||
//find the underlying memory
|
||||
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
|
||||
.FirstOrDefault(x => x.IsSameAddressAndType(this));
|
||||
|
||||
if (matchingReg != null)
|
||||
matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, encoded);
|
||||
|
||||
AddSuccessWrite();
|
||||
UpdateHoldingValue(value);
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var res = await underlyingMemory.ReadRegisterAsync(this);
|
||||
if (!res) return null;
|
||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2, false);
|
||||
if (res == null) return null;
|
||||
|
||||
var bytes = underlyingMemory.GetUnderlyingBytes(this);
|
||||
|
||||
return SetValueFromBytes(bytes);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<bool> WriteAsync(object data) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var encoded = PlcValueParser.Encode(this, (T)data);
|
||||
var res = await underlyingMemory.WriteRegisterAsync(this, encoded);
|
||||
|
||||
if (res) {
|
||||
AddSuccessWrite();
|
||||
SetValueFromPLC(data);
|
||||
}
|
||||
|
||||
return res;
|
||||
return SetValueFromBytes(res);
|
||||
|
||||
}
|
||||
|
||||
@@ -164,30 +163,22 @@ namespace MewtocolNet.Registers {
|
||||
AddSuccessRead();
|
||||
|
||||
var parsed = PlcValueParser.Parse<T>(this, bytes);
|
||||
SetValueFromPLC(parsed);
|
||||
UpdateHoldingValue(parsed);
|
||||
return parsed;
|
||||
|
||||
}
|
||||
|
||||
internal override async Task<bool> WriteToAnonymousAsync (object value) {
|
||||
internal override void UpdateHoldingValue(object val) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
if (lastValue?.ToString() != val?.ToString()) {
|
||||
|
||||
var encoded = PlcValueParser.Encode(this, (T)value);
|
||||
return await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
||||
if (val != null) lastValue = (T)val;
|
||||
else lastValue = null;
|
||||
|
||||
}
|
||||
TriggerNotifyChange();
|
||||
attachedInterface.InvokeRegisterChanged(this);
|
||||
|
||||
internal override async Task<object> ReadFromAnonymousAsync () {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2, false);
|
||||
if (res == null) return null;
|
||||
|
||||
return PlcValueParser.Parse<T>(this, res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -51,21 +51,7 @@ namespace MewtocolNet.Registers {
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetValueString() => $"'{Value}'";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetValueFromPLC (object val) {
|
||||
|
||||
if (val == null || !val.Equals(lastValue)) {
|
||||
|
||||
lastValue = (string)val;
|
||||
|
||||
TriggerNotifyChange();
|
||||
attachedInterface.InvokeRegisterChanged(this);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public override string GetValueString() => Value == null ? "null" : $"'{Value}'";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetRegisterString() => "DT";
|
||||
@@ -97,76 +83,17 @@ namespace MewtocolNet.Registers {
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
internal override void UpdateHoldingValue(object val) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
if ((val == null && lastValue != null) || val != lastValue) {
|
||||
|
||||
if (!isCalibratedFromPlc) await CalibrateFromPLC();
|
||||
lastValue = val;
|
||||
|
||||
var res = await underlyingMemory.ReadRegisterAsync(this);
|
||||
if (!res) return null;
|
||||
TriggerNotifyChange();
|
||||
attachedInterface.InvokeRegisterChanged(this);
|
||||
|
||||
var bytes = underlyingMemory.GetUnderlyingBytes(this);
|
||||
|
||||
return SetValueFromBytes(bytes);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<bool> WriteAsync(object data) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
if (!isCalibratedFromPlc) await CalibrateFromPLC();
|
||||
|
||||
var encoded = PlcValueParser.Encode(this, (string)data);
|
||||
var res = await underlyingMemory.WriteRegisterAsync(this, encoded);
|
||||
|
||||
if (res) {
|
||||
AddSuccessWrite();
|
||||
SetValueFromPLC(data);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
internal override object SetValueFromBytes(byte[] bytes) {
|
||||
|
||||
AddSuccessRead();
|
||||
|
||||
var parsed = PlcValueParser.Parse<string>(this, bytes);
|
||||
SetValueFromPLC(parsed);
|
||||
return parsed;
|
||||
|
||||
}
|
||||
|
||||
internal override async Task<bool> WriteToAnonymousAsync(object value) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
if (!isCalibratedFromPlc) await CalibrateFromPLC();
|
||||
|
||||
var encoded = PlcValueParser.Encode(this, (string)value);
|
||||
return await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
||||
|
||||
}
|
||||
|
||||
internal override async Task<object> ReadFromAnonymousAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
if (!isCalibratedFromPlc) await CalibrateFromPLC();
|
||||
|
||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
||||
if (res == null) return null;
|
||||
|
||||
return PlcValueParser.Parse<string>(this, res);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,12 +72,12 @@ namespace MewtocolNet.TypeConversion {
|
||||
ToRaw = (reg, value) => BitConverter.GetBytes(value),
|
||||
},
|
||||
|
||||
//default ushort DT conversion
|
||||
new PlcTypeConversion<ushort>(RegisterType.DT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<ushort>),
|
||||
//default Word DT conversion
|
||||
new PlcTypeConversion<Word>(RegisterType.DT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<Word>),
|
||||
PlcVarType = PlcVarType.WORD,
|
||||
FromRaw = (reg, bytes) => BitConverter.ToUInt16(bytes, 0),
|
||||
ToRaw = (reg, value) => BitConverter.GetBytes(value),
|
||||
FromRaw = (reg, bytes) => new Word(bytes),
|
||||
ToRaw = (reg, value) => value.ToByteArray(),
|
||||
},
|
||||
|
||||
//default int DDT conversion
|
||||
@@ -96,12 +96,12 @@ namespace MewtocolNet.TypeConversion {
|
||||
ToRaw = (reg, value) => BitConverter.GetBytes(value),
|
||||
},
|
||||
|
||||
//default uint DDT conversion
|
||||
new PlcTypeConversion<uint>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<uint>),
|
||||
//default DWord DDT conversion
|
||||
new PlcTypeConversion<DWord>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<DWord>),
|
||||
PlcVarType = PlcVarType.DWORD,
|
||||
FromRaw = (reg, bytes) => BitConverter.ToUInt32(bytes, 0),
|
||||
ToRaw = (reg, value) => BitConverter.GetBytes(value),
|
||||
FromRaw = (reg, bytes) => new DWord(bytes),
|
||||
ToRaw = (reg, value) => value.ToByteArray(),
|
||||
},
|
||||
|
||||
//default float DDT conversion
|
||||
@@ -134,7 +134,7 @@ namespace MewtocolNet.TypeConversion {
|
||||
|
||||
//default byte array DT Range conversion, direct pass through
|
||||
new PlcTypeConversion<byte[]>(RegisterType.DT_BYTE_RANGE) {
|
||||
HoldingRegisterType = typeof(BytesRegister),
|
||||
HoldingRegisterType = typeof(ArrayRegister),
|
||||
FromRaw = (reg, bytes) => bytes,
|
||||
ToRaw = (reg, value) => value,
|
||||
},
|
||||
@@ -182,37 +182,6 @@ namespace MewtocolNet.TypeConversion {
|
||||
},
|
||||
},
|
||||
|
||||
//default bit array <=> byte array conversion
|
||||
new PlcTypeConversion<BitArray>(RegisterType.DT_BYTE_RANGE) {
|
||||
HoldingRegisterType = typeof(BytesRegister),
|
||||
FromRaw = (reg, bytes) => {
|
||||
|
||||
var byteReg = (BytesRegister)reg;
|
||||
|
||||
BitArray bitAr = new BitArray(bytes);
|
||||
bitAr.Length = (int)byteReg.ReservedBitSize;
|
||||
|
||||
return bitAr;
|
||||
|
||||
},
|
||||
ToRaw = (reg, value) => {
|
||||
|
||||
byte[] ret = new byte[(value.Length - 1) / 8 + 1];
|
||||
value.CopyTo(ret, 0);
|
||||
|
||||
if(ret.Length % 2 != 0) {
|
||||
|
||||
var lst = ret.ToList();
|
||||
lst.Add(0);
|
||||
ret = lst.ToArray();
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
internal byte[] underlyingBytes = new byte[2];
|
||||
|
||||
internal List<BaseRegister> linkedRegisters = new List<BaseRegister>();
|
||||
internal List<BaseRegister> linkedRegisters = new List<BaseRegister>();
|
||||
|
||||
internal Dictionary<BaseRegister, List<BaseRegister>> crossRegisterBindings = new Dictionary<BaseRegister, List<BaseRegister>>();
|
||||
|
||||
public ulong AddressStart => addressStart;
|
||||
public ulong AddressEnd => addressEnd;
|
||||
@@ -40,7 +42,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
//copy old bytes to new array
|
||||
var offset = (int)(oldFrom - addFrom) * 2;
|
||||
oldUnderlying.CopyTo(oldUnderlying, offset);
|
||||
oldUnderlying.CopyTo(underlyingBytes, offset);
|
||||
|
||||
addressStart = addFrom;
|
||||
addressEnd = addTo;
|
||||
@@ -61,18 +63,6 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> ReadRegisterAsync (BaseRegister reg) {
|
||||
|
||||
return await RequestByteReadAsync(reg.MemoryAddress, reg.MemoryAddress + reg.GetRegisterAddressLen() - 1);
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> WriteRegisterAsync (BaseRegister reg, byte[] bytes) {
|
||||
|
||||
return await RequestByteWriteAsync(reg.MemoryAddress, bytes);
|
||||
|
||||
}
|
||||
|
||||
internal async Task<bool> RequestByteReadAsync (ulong addStart, ulong addEnd) {
|
||||
|
||||
await CheckDynamicallySizedRegistersAsync();
|
||||
@@ -93,24 +83,6 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
}
|
||||
|
||||
internal async Task<bool> RequestByteWriteAsync(ulong addStart, byte[] bytes) {
|
||||
|
||||
var station = mewInterface.GetStationNumber();
|
||||
var addEnd = addStart + ((ulong)bytes.Length / 2) - 1;
|
||||
|
||||
string requeststring = $"%{station}#WD{GetMewtocolIdent(addStart, addEnd)}{bytes.ToHexString()}";
|
||||
var result = await mewInterface.SendCommandAsync(requeststring);
|
||||
|
||||
if (result.Success) {
|
||||
|
||||
SetUnderlyingBytes(bytes, addStart);
|
||||
|
||||
}
|
||||
|
||||
return result.Success;
|
||||
|
||||
}
|
||||
|
||||
public byte[] GetUnderlyingBytes(BaseRegister reg) {
|
||||
|
||||
int byteLen = (int)(reg.GetRegisterAddressLen() * 2);
|
||||
|
||||
@@ -7,9 +7,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
byte[] GetUnderlyingBytes(BaseRegister reg);
|
||||
|
||||
Task<bool> ReadRegisterAsync(BaseRegister reg);
|
||||
|
||||
Task<bool> WriteRegisterAsync(BaseRegister reg, byte[] bytes);
|
||||
void SetUnderlyingBytes(BaseRegister reg, byte[] bytes);
|
||||
|
||||
void UpdateAreaRegisterValues();
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolNet.Helpers;
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolNet.SetupClasses;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -44,22 +47,69 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
}
|
||||
|
||||
internal bool LinkRegister (BaseRegister reg) {
|
||||
internal void LinkRegisters (List<BaseRegister> registers = null) {
|
||||
|
||||
TestPollLevelExistence(reg);
|
||||
//for self calling
|
||||
if (registers == null) registers = GetAllRegisters().ToList();
|
||||
|
||||
//pre combine
|
||||
var groupedByAdd = registers
|
||||
.GroupBy(x => new {
|
||||
x.MemoryAddress,
|
||||
len = x.GetRegisterAddressLen(),
|
||||
spadd = x.GetSpecialAddress(),
|
||||
});
|
||||
|
||||
var filteredRegisters = new List<BaseRegister>();
|
||||
var propertyLookupTable = new Dictionary<PropertyInfo, BaseRegister>();
|
||||
|
||||
foreach (var addressGroup in groupedByAdd) {
|
||||
|
||||
var ordered = addressGroup.OrderBy(x => x.pollLevel);
|
||||
var highestPollLevel = ordered.Max(x => x.pollLevel);
|
||||
|
||||
var distinctByUnderlyingType =
|
||||
ordered.GroupBy(x => x.underlyingSystemType).ToList();
|
||||
|
||||
foreach (var underlyingTypeGroup in distinctByUnderlyingType) {
|
||||
|
||||
foreach (var register in underlyingTypeGroup) {
|
||||
|
||||
register.pollLevel = highestPollLevel;
|
||||
|
||||
var alreadyAdded = filteredRegisters
|
||||
.FirstOrDefault(x => x.underlyingSystemType == register.underlyingSystemType);
|
||||
|
||||
if(alreadyAdded == null) {
|
||||
filteredRegisters.Add(register);
|
||||
} else {
|
||||
alreadyAdded.WithBoundProperties(register.boundProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch (reg.RegisterType) {
|
||||
case RegisterType.X:
|
||||
case RegisterType.Y:
|
||||
case RegisterType.R:
|
||||
return AddWRArea(reg);
|
||||
case RegisterType.DT:
|
||||
case RegisterType.DDT:
|
||||
case RegisterType.DT_BYTE_RANGE:
|
||||
return AddDTArea(reg);
|
||||
}
|
||||
|
||||
return false;
|
||||
foreach (var reg in filteredRegisters) {
|
||||
|
||||
TestPollLevelExistence(reg);
|
||||
|
||||
switch (reg.RegisterType) {
|
||||
case RegisterType.X:
|
||||
case RegisterType.Y:
|
||||
case RegisterType.R:
|
||||
AddToWRArea(reg);
|
||||
break;
|
||||
case RegisterType.DT:
|
||||
case RegisterType.DDT:
|
||||
case RegisterType.DT_BYTE_RANGE:
|
||||
AddToDTArea(reg);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -88,7 +138,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
}
|
||||
|
||||
private bool AddWRArea (BaseRegister insertReg) {
|
||||
private bool AddToWRArea (BaseRegister insertReg) {
|
||||
|
||||
var pollLevelFound = pollLevels.FirstOrDefault(x => x.level == insertReg.pollLevel);
|
||||
|
||||
@@ -115,9 +165,6 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
if(existingLinkedRegister != null) {
|
||||
|
||||
foreach (var prop in insertReg.boundToProps)
|
||||
existingLinkedRegister.WithBoundProperty(prop);
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
@@ -147,7 +194,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
}
|
||||
|
||||
private bool AddDTArea (BaseRegister insertReg) {
|
||||
private void AddToDTArea (BaseRegister insertReg) {
|
||||
|
||||
uint regInsAddStart = insertReg.MemoryAddress;
|
||||
uint regInsAddEnd = insertReg.MemoryAddress + insertReg.GetRegisterAddressLen() - 1;
|
||||
@@ -159,26 +206,13 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
foreach (var dtArea in dataAreas) {
|
||||
|
||||
bool matchingAddress = regInsAddStart >= dtArea.AddressStart &&
|
||||
regInsAddEnd <= dtArea.addressEnd;
|
||||
bool addressInsideArea = regInsAddStart >= dtArea.AddressStart &&
|
||||
regInsAddEnd <= dtArea.AddressEnd;
|
||||
|
||||
//found matching
|
||||
if (matchingAddress) {
|
||||
|
||||
//check if the area has registers linked that are overlapping (not matching)
|
||||
var foundDupe = dtArea.linkedRegisters
|
||||
.FirstOrDefault(x => x.CompareIsDuplicateNonCast(insertReg, allowByteRegDupes));
|
||||
|
||||
if (foundDupe != null) {
|
||||
throw new NotSupportedException(
|
||||
message: $"Can't have registers of different types at the same referenced plc address: " +
|
||||
$"{insertReg.PLCAddressName} ({insertReg.GetType()}) <=> " +
|
||||
$"{foundDupe.PLCAddressName} ({foundDupe.GetType()})"
|
||||
);
|
||||
}
|
||||
if (addressInsideArea) {
|
||||
|
||||
//found an area that is already existing where the register can fit into
|
||||
targetArea = dtArea;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -208,7 +242,6 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
//expand the boundaries for the area to include the new adjacent area
|
||||
dtArea.BoundaryUdpdate(addrFrom: regInsAddStart);
|
||||
|
||||
targetArea = dtArea;
|
||||
break;
|
||||
|
||||
@@ -218,6 +251,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
}
|
||||
|
||||
//create a new area
|
||||
if (targetArea == null) {
|
||||
|
||||
targetArea = new DTArea(mewInterface) {
|
||||
@@ -227,45 +261,27 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
};
|
||||
|
||||
targetArea.BoundaryUdpdate();
|
||||
|
||||
dataAreas.Add(targetArea);
|
||||
|
||||
}
|
||||
|
||||
insertReg.underlyingMemory = targetArea;
|
||||
|
||||
var existingLinkedRegister = targetArea.linkedRegisters
|
||||
.FirstOrDefault(x => x.CompareIsDuplicate(insertReg));
|
||||
|
||||
if (existingLinkedRegister != null) {
|
||||
|
||||
foreach (var prop in insertReg.boundToProps)
|
||||
existingLinkedRegister.WithBoundProperty(prop);
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
targetArea.linkedRegisters.Add(insertReg);
|
||||
return true;
|
||||
|
||||
if (insertReg.name == null) {
|
||||
insertReg.name = $"auto_{Guid.NewGuid().ToString("N")}";
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine($"Adding linked register: {insertReg}");
|
||||
targetArea.linkedRegisters.Add(insertReg);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
internal void MergeAndSizeDataAreas () {
|
||||
|
||||
//merge gaps that the algorithm didn't catch be rerunning the register attachment
|
||||
|
||||
foreach (var pLevel in pollLevels) {
|
||||
|
||||
var allDataAreaRegisters = pLevel.dataAreas.SelectMany(x => x.linkedRegisters).ToList();
|
||||
var dataAreas = new List<DTArea>(allDataAreaRegisters.Capacity);
|
||||
|
||||
foreach (var reg in allDataAreaRegisters)
|
||||
AddDTArea(reg);
|
||||
|
||||
}
|
||||
LinkRegisters();
|
||||
|
||||
}
|
||||
|
||||
@@ -309,7 +325,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
}
|
||||
|
||||
//update registers in poll level
|
||||
foreach (var dtArea in pollLevel.dataAreas) {
|
||||
foreach (var dtArea in pollLevel.dataAreas.ToArray()) {
|
||||
|
||||
//set the whole memory area at once
|
||||
await dtArea.RequestByteReadAsync(dtArea.AddressStart, dtArea.AddressEnd);
|
||||
@@ -346,7 +362,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
sb.AppendLine($"Optimization distance: {maxOptimizationDistance}");
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine($"---- DT Area ----");
|
||||
sb.AppendLine($"---- DT Areas: ----");
|
||||
|
||||
foreach (var area in pollLevel.dataAreas) {
|
||||
|
||||
@@ -358,7 +374,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
foreach (var reg in area.linkedRegisters) {
|
||||
|
||||
sb.AppendLine($"{reg.ToString(true)}");
|
||||
sb.AppendLine($"{reg.Explain()}");
|
||||
|
||||
}
|
||||
|
||||
@@ -372,7 +388,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
foreach (var reg in area.linkedRegisters) {
|
||||
|
||||
sb.AppendLine($"{reg.ToString(true)}");
|
||||
sb.AppendLine($"{reg.Explain()}");
|
||||
|
||||
}
|
||||
|
||||
@@ -386,7 +402,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
foreach (var reg in area.linkedRegisters) {
|
||||
|
||||
sb.AppendLine($"{reg.ToString(true)}");
|
||||
sb.AppendLine($"{reg.Explain()}");
|
||||
|
||||
}
|
||||
|
||||
@@ -400,7 +416,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
foreach (var reg in area.linkedRegisters) {
|
||||
|
||||
sb.AppendLine($"{reg.ToString(true)}");
|
||||
sb.AppendLine($"{reg.Explain()}");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
|
||||
|
||||
}
|
||||
public void SetUnderlyingBytes(BaseRegister reg, byte[] bytes) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public byte[] GetUnderlyingBytes(BaseRegister reg) {
|
||||
|
||||
Reference in New Issue
Block a user