mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Further implementation
- added new parsing method
This commit is contained in:
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
using MewtocolNet.RegisterBuilding;
|
||||
using System.Collections.Generic;
|
||||
using MewtocolNet.Registers;
|
||||
|
||||
namespace Examples;
|
||||
|
||||
@@ -46,7 +47,7 @@ public class ExampleScenarios {
|
||||
while (interf.IsConnected) {
|
||||
|
||||
//flip the bool register each tick and wait for it to be registered
|
||||
await interf.SetRegisterAsync(nameof(registers.TestBool1), !registers.TestBool1);
|
||||
//await interf.SetRegisterAsync(nameof(registers.TestBool1), !registers.TestBool1);
|
||||
|
||||
Console.Title = $"Polling Paused: {interf.PollingPaused}, " +
|
||||
$"Poller active: {interf.PollerActive}, " +
|
||||
@@ -167,7 +168,7 @@ public class ExampleScenarios {
|
||||
await interf.ConnectAsync();
|
||||
|
||||
//use the async method to make sure the cycling is stopped
|
||||
await interf.SetRegisterAsync(nameof(registers.StartCyclePLC), false);
|
||||
//await interf.SetRegisterAsync(nameof(registers.StartCyclePLC), false);
|
||||
|
||||
await Task.Delay(5000);
|
||||
|
||||
@@ -182,4 +183,49 @@ public class ExampleScenarios {
|
||||
|
||||
}
|
||||
|
||||
[Scenario("Read register test")]
|
||||
public async Task RunReadTest () {
|
||||
|
||||
Console.WriteLine("Starting auto enums and bitwise");
|
||||
|
||||
//setting up a new PLC interface and register collection
|
||||
MewtocolInterface interf = new MewtocolInterface("192.168.115.210").WithPoller();
|
||||
|
||||
//auto add all built registers to the interface
|
||||
var builder = RegBuilder.ForInterface(interf);
|
||||
var r0reg = builder.FromPlcRegName("R0").Build();
|
||||
builder.FromPlcRegName("R1").Build();
|
||||
builder.FromPlcRegName("R1F").Build();
|
||||
builder.FromPlcRegName("R101A").Build();
|
||||
|
||||
var shortReg = builder.FromPlcRegName("DT35").AsPlcType(PlcVarType.INT).Build();
|
||||
builder.FromPlcRegName("DDT36").AsPlcType(PlcVarType.DINT).Build();
|
||||
|
||||
//builder.FromPlcRegName("DDT38").AsPlcType(PlcVarType.TIME).Build();
|
||||
//builder.FromPlcRegName("DT40").AsPlcType(PlcVarType.STRING).Build();
|
||||
|
||||
//connect
|
||||
await interf.ConnectAsync();
|
||||
|
||||
//var res = await interf.SendCommandAsync("%01#RCSR000F");
|
||||
|
||||
while(true) {
|
||||
|
||||
await interf.SetRegisterAsync(r0reg, !(bool)r0reg.Value);
|
||||
await interf.SetRegisterAsync(shortReg, (short)new Random().Next(0, 100));
|
||||
|
||||
foreach (var reg in interf.Registers) {
|
||||
|
||||
Console.WriteLine($"Register {reg.GetRegisterPLCName()} val: {reg.Value}");
|
||||
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,9 +14,6 @@ class Program {
|
||||
|
||||
static void Main(string[] args) {
|
||||
|
||||
RegBuilder.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build();
|
||||
var res = RegBuilder.FromPlcRegName("DT100").AsPlcType(PlcVarType.INT).Build();
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (s,e) => {
|
||||
Console.WriteLine(e.ExceptionObject.ToString());
|
||||
};
|
||||
|
||||
@@ -47,10 +47,10 @@ namespace Examples {
|
||||
public BitArray TestBitRegister { get; private set; }
|
||||
|
||||
//corresponds to a DT1204 as a 16bit word/int takes the bit at index 9 and writes it back as a boolean
|
||||
[Register(1204, 9, BitCount.B16)]
|
||||
[Register(1204, BitCount.B16, 9)]
|
||||
public bool BitValue { get; private set; }
|
||||
|
||||
[Register(1204, 5, BitCount.B16)]
|
||||
[Register(1204, BitCount.B16, 5)]
|
||||
public bool FillTest { get; private set; }
|
||||
|
||||
//corresponds to a DT7012 - DT7013 as a 32bit time value that gets parsed as a timespan (TIME)
|
||||
|
||||
@@ -53,52 +53,52 @@ namespace Examples {
|
||||
|
||||
//you can also extract single bits from DT503
|
||||
|
||||
[Register(503, 0, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 0)]
|
||||
public bool BitValue0 { get; private set; }
|
||||
|
||||
[Register(503, 1, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 1)]
|
||||
public bool BitValue1 { get; private set; }
|
||||
|
||||
[Register(503, 2, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 2)]
|
||||
public bool BitValue2 { get; private set; }
|
||||
|
||||
[Register(503, 3, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 3)]
|
||||
public bool BitValue3 { get; private set; }
|
||||
|
||||
[Register(503, 4, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 4)]
|
||||
public bool BitValue4 { get; private set; }
|
||||
|
||||
[Register(503, 5, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 5)]
|
||||
public bool BitValue5 { get; private set; }
|
||||
|
||||
[Register(503, 6, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 6)]
|
||||
public bool BitValue6 { get; private set; }
|
||||
|
||||
[Register(503, 7, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 7)]
|
||||
public bool BitValue7 { get; private set; }
|
||||
|
||||
[Register(503, 8, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 8)]
|
||||
public bool BitValue8 { get; private set; }
|
||||
|
||||
[Register(503, 9, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 9)]
|
||||
public bool BitValue9 { get; private set; }
|
||||
|
||||
[Register(503, 10, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 10)]
|
||||
public bool BitValue10 { get; private set; }
|
||||
|
||||
[Register(503, 11, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 11)]
|
||||
public bool BitValue11 { get; private set; }
|
||||
|
||||
[Register(503, 12, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 12)]
|
||||
public bool BitValue12 { get; private set; }
|
||||
|
||||
[Register(503, 13, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 13)]
|
||||
public bool BitValue13 { get; private set; }
|
||||
|
||||
[Register(503, 14, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 14)]
|
||||
public bool BitValue14 { get; private set; }
|
||||
|
||||
[Register(503, 15, BitCount.B16)]
|
||||
[Register(503, BitCount.B16, 15)]
|
||||
public bool BitValue15 { get; private set; }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
using MewtocolNet.Logging;
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.Logging;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet {
|
||||
namespace MewtocolNet
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The PLC com interface class
|
||||
@@ -118,61 +122,21 @@ namespace MewtocolNet {
|
||||
|
||||
var reg = Registers[iteration];
|
||||
|
||||
if (reg is NRegister<short> shortReg) {
|
||||
var lastVal = shortReg.Value;
|
||||
var readout = (await ReadNumRegister(shortReg)).Register.Value;
|
||||
if(reg.IsAllowedRegisterGenericType()) {
|
||||
|
||||
var lastVal = reg.Value;
|
||||
|
||||
var rwReg = (IRegisterInternal)reg;
|
||||
|
||||
var readout = await rwReg.ReadAsync(this);
|
||||
|
||||
if (lastVal != readout) {
|
||||
InvokeRegisterChanged(shortReg);
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<ushort> ushortReg) {
|
||||
var lastVal = ushortReg.Value;
|
||||
var readout = (await ReadNumRegister(ushortReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
InvokeRegisterChanged(ushortReg);
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<int> intReg) {
|
||||
var lastVal = intReg.Value;
|
||||
var readout = (await ReadNumRegister(intReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
InvokeRegisterChanged(intReg);
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<uint> uintReg) {
|
||||
var lastVal = uintReg.Value;
|
||||
var readout = (await ReadNumRegister(uintReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
InvokeRegisterChanged(uintReg);
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<float> floatReg) {
|
||||
var lastVal = floatReg.Value;
|
||||
var readout = (await ReadNumRegister(floatReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
InvokeRegisterChanged(floatReg);
|
||||
}
|
||||
}
|
||||
if (reg is NRegister<TimeSpan> tsReg) {
|
||||
var lastVal = tsReg.Value;
|
||||
var readout = (await ReadNumRegister(tsReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
InvokeRegisterChanged(tsReg);
|
||||
}
|
||||
}
|
||||
if (reg is BRegister boolReg) {
|
||||
var lastVal = boolReg.Value;
|
||||
var readout = (await ReadBoolRegister(boolReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
InvokeRegisterChanged(boolReg);
|
||||
}
|
||||
}
|
||||
if (reg is SRegister stringReg) {
|
||||
var lastVal = stringReg.Value;
|
||||
var readout = (await ReadStringRegister(stringReg)).Register.Value;
|
||||
if (lastVal != readout) {
|
||||
InvokeRegisterChanged(stringReg);
|
||||
|
||||
rwReg.SetValueFromPLC(readout);
|
||||
InvokeRegisterChanged(reg);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
iteration++;
|
||||
@@ -194,67 +158,218 @@ namespace MewtocolNet {
|
||||
|
||||
internal void PropertyRegisterWasSet(string propName, object value) {
|
||||
|
||||
SetRegister(propName, value);
|
||||
_ = SetRegisterAsync(GetRegister(propName), value);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register Colleciton adding
|
||||
|
||||
#region Register Collection
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a register collection object to
|
||||
/// the interface that can be updated automatically.
|
||||
/// <para/>
|
||||
/// Just create a class inheriting from <see cref="RegisterCollectionBase"/>
|
||||
/// and assert some propertys with the custom <see cref="RegisterAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="collection">A collection inherting the <see cref="RegisterCollectionBase"/> class</param>
|
||||
public MewtocolInterface WithRegisterCollection(RegisterCollectionBase collection) {
|
||||
|
||||
collection.PLCInterface = this;
|
||||
|
||||
var props = collection.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in props) {
|
||||
|
||||
var attributes = prop.GetCustomAttributes(true);
|
||||
|
||||
string propName = prop.Name;
|
||||
foreach (var attr in attributes) {
|
||||
|
||||
if (attr is RegisterAttribute cAttribute && prop.PropertyType.IsAllowedPlcCastingType()) {
|
||||
|
||||
var dotnetType = prop.PropertyType;
|
||||
|
||||
AddRegister(new RegisterBuildInfo {
|
||||
memoryAddress = cAttribute.MemoryArea,
|
||||
specialAddress = cAttribute.SpecialAddress,
|
||||
memorySizeBytes = cAttribute.ByteLength,
|
||||
registerType = cAttribute.RegisterType,
|
||||
dotnetCastType = dotnetType,
|
||||
collectionType = collection.GetType(),
|
||||
name = prop.Name,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RegisterChanged += (reg) => {
|
||||
|
||||
//register is used bitwise
|
||||
if (reg.IsUsedBitwise()) {
|
||||
|
||||
for (int i = 0; i < props.Length; i++) {
|
||||
|
||||
var prop = props[i];
|
||||
var bitWiseFound = prop.GetCustomAttributes(true)
|
||||
.FirstOrDefault(y => y.GetType() == typeof(RegisterAttribute) && ((RegisterAttribute)y).MemoryArea == reg.MemoryAddress);
|
||||
|
||||
if (bitWiseFound != null) {
|
||||
|
||||
var casted = (RegisterAttribute)bitWiseFound;
|
||||
var bitIndex = casted.AssignedBitIndex;
|
||||
|
||||
BitArray bitAr = null;
|
||||
|
||||
if (reg is NumberRegister<short> reg16) {
|
||||
var bytes = BitConverter.GetBytes((short)reg16.Value);
|
||||
bitAr = new BitArray(bytes);
|
||||
} else if (reg is NumberRegister<int> reg32) {
|
||||
var bytes = BitConverter.GetBytes((int)reg32.Value);
|
||||
bitAr = new BitArray(bytes);
|
||||
}
|
||||
|
||||
if (bitAr != null && bitIndex < bitAr.Length && bitIndex >= 0) {
|
||||
|
||||
//set the specific bit index if needed
|
||||
prop.SetValue(collection, bitAr[bitIndex]);
|
||||
collection.TriggerPropertyChanged(prop.Name);
|
||||
|
||||
} else if (bitAr != null) {
|
||||
|
||||
//set the specific bit array if needed
|
||||
prop.SetValue(collection, bitAr);
|
||||
collection.TriggerPropertyChanged(prop.Name);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//updating normal properties
|
||||
var foundToUpdate = props.FirstOrDefault(x => x.Name == reg.Name);
|
||||
|
||||
if (foundToUpdate != null) {
|
||||
|
||||
var foundAttributes = foundToUpdate.GetCustomAttributes(true);
|
||||
var foundAttr = foundAttributes.FirstOrDefault(x => x.GetType() == typeof(RegisterAttribute));
|
||||
|
||||
if (foundAttr == null)
|
||||
return;
|
||||
|
||||
var registerAttr = (RegisterAttribute)foundAttr;
|
||||
|
||||
//check if bit parse mode
|
||||
if (registerAttr.AssignedBitIndex == -1) {
|
||||
|
||||
HashSet<Type> NumericTypes = new HashSet<Type> {
|
||||
typeof(bool),
|
||||
typeof(short),
|
||||
typeof(ushort),
|
||||
typeof(int),
|
||||
typeof(uint),
|
||||
typeof(float),
|
||||
typeof(TimeSpan),
|
||||
typeof(string)
|
||||
};
|
||||
|
||||
var regValue = ((IRegister)reg).Value;
|
||||
|
||||
if (NumericTypes.Any(x => foundToUpdate.PropertyType == x)) {
|
||||
foundToUpdate.SetValue(collection, regValue);
|
||||
}
|
||||
|
||||
if (foundToUpdate.PropertyType.IsEnum) {
|
||||
foundToUpdate.SetValue(collection, regValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
collection.TriggerPropertyChanged(foundToUpdate.Name);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (collection != null)
|
||||
collection.OnInterfaceLinked(this);
|
||||
|
||||
Connected += (i) => {
|
||||
if (collection != null)
|
||||
collection.OnInterfaceLinkedAndOnline(this);
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register Adding
|
||||
|
||||
//Internal register adding for auto register collection building
|
||||
internal void AddRegister<T>(Type _colType, int _address, PropertyInfo boundProp, int _length = 1, bool _isBitwise = false, Type _enumType = null) {
|
||||
internal void AddRegister (RegisterBuildInfo buildInfo) {
|
||||
|
||||
Type regType = typeof(T);
|
||||
var builtRegister = buildInfo.Build();
|
||||
|
||||
if (regType != typeof(string) && _length != 1) {
|
||||
throw new NotSupportedException($"_lenght parameter only allowed for register of type string");
|
||||
}
|
||||
//is bitwise and the register list already contains that area register
|
||||
if(builtRegister.IsUsedBitwise() && CheckDuplicateRegister(builtRegister, out var existing)) {
|
||||
|
||||
if (Registers.Any(x => x.MemoryAddress == _address) && _isBitwise) {
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
IRegister reg = null;
|
||||
if (CheckDuplicateRegister(builtRegister))
|
||||
throw MewtocolException.DupeRegister(builtRegister);
|
||||
|
||||
string propName = boundProp.Name;
|
||||
if(CheckDuplicateNameRegister(builtRegister))
|
||||
throw MewtocolException.DupeNameRegister(builtRegister);
|
||||
|
||||
//rename the property name to prevent duplicate names in case of a bitwise prop
|
||||
if (_isBitwise && regType == typeof(short))
|
||||
propName = $"Auto_Bitwise_DT{_address}";
|
||||
Registers.Add(builtRegister);
|
||||
|
||||
if (_isBitwise && regType == typeof(int))
|
||||
propName = $"Auto_Bitwise_DDT{_address}";
|
||||
|
||||
if (regType == typeof(short)) {
|
||||
reg = new NRegister<short>(_address, propName, _isBitwise, _enumType).WithCollectionType(_colType);
|
||||
} else if (regType == typeof(ushort)) {
|
||||
reg = new NRegister<ushort>(_address, propName).WithCollectionType(_colType);
|
||||
} else if (regType == typeof(int)) {
|
||||
reg = new NRegister<int>(_address, propName, _isBitwise, _enumType).WithCollectionType(_colType);
|
||||
} else if (regType == typeof(uint)) {
|
||||
reg = new NRegister<uint>(_address, propName).WithCollectionType(_colType);
|
||||
} else if (regType == typeof(float)) {
|
||||
reg = new NRegister<float>(_address, propName).WithCollectionType(_colType);
|
||||
} else if (regType == typeof(string)) {
|
||||
reg = new SRegister(_address, _length, propName).WithCollectionType(_colType);
|
||||
} else if (regType == typeof(TimeSpan)) {
|
||||
reg = new NRegister<TimeSpan>(_address, propName).WithCollectionType(_colType);
|
||||
} else if (regType == typeof(bool)) {
|
||||
reg = new BRegister(IOType.R, 0x0, _address, propName).WithCollectionType(_colType);
|
||||
}
|
||||
|
||||
if (reg == null) {
|
||||
throw new NotSupportedException($"The type {regType} is not allowed for Registers \n" +
|
||||
$"Allowed are: short, ushort, int, uint, float and string");
|
||||
public void AddRegister(IRegister register) {
|
||||
|
||||
if (CheckDuplicateRegister(register))
|
||||
throw MewtocolException.DupeRegister(register);
|
||||
|
||||
if (CheckDuplicateNameRegister(register))
|
||||
throw MewtocolException.DupeNameRegister(register);
|
||||
|
||||
Registers.Add(register);
|
||||
|
||||
}
|
||||
|
||||
if (Registers.Any(x => x.GetRegisterPLCName() == reg.GetRegisterPLCName()) && !_isBitwise) {
|
||||
throw new NotSupportedException($"Cannot add a register multiple times, " +
|
||||
$"make sure that all register attributes or AddRegister assignments have different adresses.");
|
||||
private bool CheckDuplicateRegister (IRegister instance, out IRegister foundDupe) {
|
||||
|
||||
foundDupe = Registers.FirstOrDefault(x => x.CompareIsDuplicate(instance));
|
||||
|
||||
return Registers.Contains(instance) || foundDupe != null;
|
||||
|
||||
}
|
||||
|
||||
Registers.Add(reg);
|
||||
private bool CheckDuplicateRegister(IRegister instance) {
|
||||
|
||||
var foundDupe = Registers.FirstOrDefault(x => x.CompareIsDuplicate(instance));
|
||||
|
||||
return Registers.Contains(instance) || foundDupe != null;
|
||||
|
||||
}
|
||||
|
||||
private bool CheckDuplicateNameRegister(IRegister instance) {
|
||||
|
||||
return Registers.Any(x => x.CompareIsNameDuplicate(instance));
|
||||
|
||||
}
|
||||
|
||||
|
||||
34
MewtocolNet/Exceptions/MewtocolException.cs
Normal file
34
MewtocolNet/Exceptions/MewtocolException.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MewtocolNet.Exceptions {
|
||||
|
||||
[Serializable]
|
||||
public class MewtocolException : Exception {
|
||||
|
||||
public MewtocolException() { }
|
||||
|
||||
public MewtocolException(string message) : base(message) { }
|
||||
|
||||
public MewtocolException(string message, Exception inner) : base(message, inner) { }
|
||||
|
||||
protected MewtocolException(
|
||||
System.Runtime.Serialization.SerializationInfo info,
|
||||
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
|
||||
|
||||
public static MewtocolException DupeRegister (IRegister register) {
|
||||
|
||||
return new MewtocolException($"The mewtocol interface already contains this register: {register.GetRegisterPLCName()}");
|
||||
|
||||
}
|
||||
|
||||
public static MewtocolException DupeNameRegister (IRegister register) {
|
||||
|
||||
return new MewtocolException($"The mewtocol interface registers already contains a register with the name: {register.Name}");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
|
||||
18
MewtocolNet/IRegisterInternal.cs
Normal file
18
MewtocolNet/IRegisterInternal.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet {
|
||||
internal interface IRegisterInternal {
|
||||
|
||||
void WithCollectionType(Type colType);
|
||||
|
||||
void SetValueFromPLC(object value);
|
||||
|
||||
Task<object> ReadAsync(MewtocolInterface interf);
|
||||
|
||||
Task<bool> WriteAsync(MewtocolInterface interf, object data);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -109,17 +109,17 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
internal static string BuildDTString(this string _inString, short _stringReservedSize) {
|
||||
internal static string BuildDTString (this byte[] inBytes, short reservedSize) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
//clamp string lenght
|
||||
if (_inString.Length > _stringReservedSize) {
|
||||
_inString = _inString.Substring(0, _stringReservedSize);
|
||||
if (inBytes.Length > reservedSize) {
|
||||
inBytes = inBytes.Take(reservedSize).ToArray();
|
||||
}
|
||||
|
||||
//actual string content
|
||||
var hexstring = _inString.GetAsciiHexFromString();
|
||||
var hexstring = inBytes.ToHexString();
|
||||
|
||||
var sizeBytes = BitConverter.GetBytes((short)(hexstring.Length / 2)).ToHexString();
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
var reservedSizeBytes = BitConverter.GetBytes(_stringReservedSize).ToHexString();
|
||||
var reservedSizeBytes = BitConverter.GetBytes(reservedSize).ToHexString();
|
||||
|
||||
//reserved string count bytes
|
||||
sb.Append(reservedSizeBytes);
|
||||
@@ -159,8 +159,10 @@ namespace MewtocolNet {
|
||||
}
|
||||
|
||||
internal static string GetAsciiHexFromString(this string input) {
|
||||
|
||||
var bytes = new ASCIIEncoding().GetBytes(input);
|
||||
return bytes.ToHexString();
|
||||
|
||||
}
|
||||
|
||||
internal static byte[] HexStringToByteArray(this string hex) {
|
||||
@@ -265,6 +267,22 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
internal static bool CompareIsDuplicate (this IRegister reg1, IRegister compare) {
|
||||
|
||||
bool valCompare = reg1.RegisterType == compare.RegisterType &&
|
||||
reg1.MemoryAddress == compare.MemoryAddress &&
|
||||
reg1.GetSpecialAddress() == compare.GetSpecialAddress();
|
||||
|
||||
return valCompare;
|
||||
|
||||
}
|
||||
|
||||
internal static bool CompareIsNameDuplicate(this IRegister reg1, IRegister compare) {
|
||||
|
||||
return ( reg1.Name != null || compare.Name != null) && reg1.Name == compare.Name;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -308,7 +308,6 @@ namespace MewtocolNet {
|
||||
public MewtocolInterface WithPoller() {
|
||||
|
||||
usePoller = true;
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
@@ -407,301 +406,10 @@ namespace MewtocolNet {
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register Collection
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a register collection object to
|
||||
/// the interface that can be updated automatically.
|
||||
/// <para/>
|
||||
/// Just create a class inheriting from <see cref="RegisterCollectionBase"/>
|
||||
/// and assert some propertys with the custom <see cref="RegisterAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="collection">A collection inherting the <see cref="RegisterCollectionBase"/> class</param>
|
||||
public MewtocolInterface WithRegisterCollection(RegisterCollectionBase collection) {
|
||||
|
||||
collection.PLCInterface = this;
|
||||
|
||||
var props = collection.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in props) {
|
||||
|
||||
var attributes = prop.GetCustomAttributes(true);
|
||||
|
||||
string propName = prop.Name;
|
||||
foreach (var attr in attributes) {
|
||||
|
||||
if (attr is RegisterAttribute cAttribute) {
|
||||
|
||||
if (prop.PropertyType == typeof(bool) && cAttribute.AssignedBitIndex == -1) {
|
||||
|
||||
//add bool register non bit assgined
|
||||
Registers.Add(new BRegister((IOType)(int)cAttribute.RegisterType, cAttribute.SpecialAddress, cAttribute.MemoryArea, _name: propName).WithCollectionType(collection.GetType()));
|
||||
|
||||
}
|
||||
|
||||
if (prop.PropertyType == typeof(short)) {
|
||||
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, prop);
|
||||
}
|
||||
|
||||
if (prop.PropertyType == typeof(ushort)) {
|
||||
AddRegister<ushort>(collection.GetType(), cAttribute.MemoryArea, prop);
|
||||
}
|
||||
|
||||
if (prop.PropertyType == typeof(int)) {
|
||||
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, prop);
|
||||
}
|
||||
|
||||
if (prop.PropertyType == typeof(uint)) {
|
||||
AddRegister<uint>(collection.GetType(), cAttribute.MemoryArea, prop);
|
||||
}
|
||||
|
||||
if (prop.PropertyType == typeof(float)) {
|
||||
AddRegister<float>(collection.GetType(), cAttribute.MemoryArea, prop);
|
||||
}
|
||||
|
||||
if (prop.PropertyType == typeof(string)) {
|
||||
AddRegister<string>(collection.GetType(), cAttribute.MemoryArea, prop, cAttribute.StringLength);
|
||||
}
|
||||
|
||||
if (prop.PropertyType.IsEnum) {
|
||||
|
||||
if (cAttribute.BitCount == BitCount.B16) {
|
||||
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, prop, _enumType: prop.PropertyType);
|
||||
} else {
|
||||
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, prop, _enumType: prop.PropertyType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//read number as bit array
|
||||
if (prop.PropertyType == typeof(BitArray)) {
|
||||
|
||||
if (cAttribute.BitCount == BitCount.B16) {
|
||||
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true);
|
||||
} else {
|
||||
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//read number as bit array by invdividual properties
|
||||
if (prop.PropertyType == typeof(bool) && cAttribute.AssignedBitIndex != -1) {
|
||||
|
||||
//var bitwiseCount = Registers.Count(x => x.Value.isUsedBitwise);
|
||||
|
||||
if (cAttribute.BitCount == BitCount.B16) {
|
||||
AddRegister<short>(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true);
|
||||
} else {
|
||||
AddRegister<int>(collection.GetType(), cAttribute.MemoryArea, prop, _isBitwise: true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (prop.PropertyType == typeof(TimeSpan)) {
|
||||
AddRegister<TimeSpan>(collection.GetType(), cAttribute.MemoryArea, prop);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RegisterChanged += (reg) => {
|
||||
|
||||
//register is used bitwise
|
||||
if (reg.IsUsedBitwise()) {
|
||||
|
||||
for (int i = 0; i < props.Length; i++) {
|
||||
|
||||
var prop = props[i];
|
||||
var bitWiseFound = prop.GetCustomAttributes(true)
|
||||
.FirstOrDefault(y => y.GetType() == typeof(RegisterAttribute) && ((RegisterAttribute)y).MemoryArea == reg.MemoryAddress);
|
||||
|
||||
if (bitWiseFound != null) {
|
||||
|
||||
var casted = (RegisterAttribute)bitWiseFound;
|
||||
var bitIndex = casted.AssignedBitIndex;
|
||||
|
||||
BitArray bitAr = null;
|
||||
|
||||
if (reg is NRegister<short> reg16) {
|
||||
var bytes = BitConverter.GetBytes((short)reg16.Value);
|
||||
bitAr = new BitArray(bytes);
|
||||
} else if (reg is NRegister<int> reg32) {
|
||||
var bytes = BitConverter.GetBytes((int)reg32.Value);
|
||||
bitAr = new BitArray(bytes);
|
||||
}
|
||||
|
||||
if (bitAr != null && bitIndex < bitAr.Length && bitIndex >= 0) {
|
||||
|
||||
//set the specific bit index if needed
|
||||
prop.SetValue(collection, bitAr[bitIndex]);
|
||||
collection.TriggerPropertyChanged(prop.Name);
|
||||
|
||||
} else if (bitAr != null) {
|
||||
|
||||
//set the specific bit array if needed
|
||||
prop.SetValue(collection, bitAr);
|
||||
collection.TriggerPropertyChanged(prop.Name);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//updating normal properties
|
||||
var foundToUpdate = props.FirstOrDefault(x => x.Name == reg.Name);
|
||||
|
||||
if (foundToUpdate != null) {
|
||||
|
||||
var foundAttributes = foundToUpdate.GetCustomAttributes(true);
|
||||
var foundAttr = foundAttributes.FirstOrDefault(x => x.GetType() == typeof(RegisterAttribute));
|
||||
|
||||
if (foundAttr == null)
|
||||
return;
|
||||
|
||||
var registerAttr = (RegisterAttribute)foundAttr;
|
||||
|
||||
//check if bit parse mode
|
||||
if (registerAttr.AssignedBitIndex == -1) {
|
||||
|
||||
HashSet<Type> NumericTypes = new HashSet<Type> {
|
||||
typeof(bool),
|
||||
typeof(short),
|
||||
typeof(ushort),
|
||||
typeof(int),
|
||||
typeof(uint),
|
||||
typeof(float),
|
||||
typeof(TimeSpan),
|
||||
typeof(string)
|
||||
};
|
||||
|
||||
var regValue = ((IRegister)reg).Value;
|
||||
|
||||
if (NumericTypes.Any(x => foundToUpdate.PropertyType == x)) {
|
||||
foundToUpdate.SetValue(collection, regValue);
|
||||
}
|
||||
|
||||
if (foundToUpdate.PropertyType.IsEnum) {
|
||||
foundToUpdate.SetValue(collection, regValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
collection.TriggerPropertyChanged(foundToUpdate.Name);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (collection != null)
|
||||
collection.OnInterfaceLinked(this);
|
||||
|
||||
Connected += (i) => {
|
||||
if (collection != null)
|
||||
collection.OnInterfaceLinkedAndOnline(this);
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Register Writing
|
||||
|
||||
/// <summary>
|
||||
/// Sets a register in the PLCs memory
|
||||
/// </summary>
|
||||
/// <param name="registerName">The name the register was given to or a property name from the RegisterCollection class</param>
|
||||
/// <param name="value">The value to write to the register</param>
|
||||
public void SetRegister(string registerName, object value) {
|
||||
|
||||
var foundRegister = GetAllRegisters().FirstOrDefault(x => x.Name == registerName);
|
||||
|
||||
if (foundRegister == null) {
|
||||
throw new Exception($"Register with the name {registerName} was not found");
|
||||
}
|
||||
|
||||
_ = SetRegisterAsync(registerName, value);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a register in the PLCs memory asynchronously, returns the result status from the PLC
|
||||
/// </summary>
|
||||
/// <param name="registerName">The name the register was given to or a property name from the RegisterCollection class</param>
|
||||
/// <param name="value">The value to write to the register</param>
|
||||
public async Task<bool> SetRegisterAsync(string registerName, object value) {
|
||||
|
||||
var foundRegister = GetAllRegisters().FirstOrDefault(x => x.Name == registerName);
|
||||
|
||||
if (foundRegister == null) {
|
||||
throw new Exception($"Register with the name {registerName} was not found");
|
||||
}
|
||||
|
||||
if (foundRegister.GetType() == typeof(BRegister)) {
|
||||
|
||||
return await WriteBoolRegister((BRegister)foundRegister, (bool)value);
|
||||
|
||||
}
|
||||
|
||||
if (foundRegister.GetType() == typeof(NRegister<short>)) {
|
||||
|
||||
return await WriteNumRegister((NRegister<short>)foundRegister, (short)value);
|
||||
|
||||
}
|
||||
|
||||
if (foundRegister.GetType() == typeof(NRegister<ushort>)) {
|
||||
|
||||
return await WriteNumRegister((NRegister<ushort>)foundRegister, (ushort)value);
|
||||
|
||||
}
|
||||
|
||||
if (foundRegister.GetType() == typeof(NRegister<int>)) {
|
||||
|
||||
return await WriteNumRegister((NRegister<int>)foundRegister, (int)value);
|
||||
|
||||
}
|
||||
|
||||
if (foundRegister.GetType() == typeof(NRegister<uint>)) {
|
||||
|
||||
return await WriteNumRegister((NRegister<uint>)foundRegister, (uint)value);
|
||||
|
||||
}
|
||||
|
||||
if (foundRegister.GetType() == typeof(NRegister<float>)) {
|
||||
|
||||
return await WriteNumRegister((NRegister<float>)foundRegister, (float)value);
|
||||
|
||||
}
|
||||
|
||||
if (foundRegister.GetType() == typeof(NRegister<TimeSpan>)) {
|
||||
|
||||
return await WriteNumRegister((NRegister<TimeSpan>)foundRegister, (TimeSpan)value);
|
||||
|
||||
}
|
||||
|
||||
if (foundRegister.GetType() == typeof(SRegister)) {
|
||||
|
||||
return await WriteStringRegister((SRegister)foundRegister, (string)value);
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Low level command handling
|
||||
|
||||
/// <summary>
|
||||
/// Calculates checksum and sends a command to the PLC then awaits results
|
||||
/// Calculates the checksum automatically and sends a command to the PLC then awaits results
|
||||
/// </summary>
|
||||
/// <param name="_msg">MEWTOCOL Formatted request string ex: %01#RT</param>
|
||||
/// <returns>Returns the result</returns>
|
||||
|
||||
@@ -4,7 +4,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet {
|
||||
@@ -155,244 +157,110 @@ namespace MewtocolNet {
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bool register reading / writing
|
||||
#region Raw register reading / writing
|
||||
|
||||
/// <summary>
|
||||
/// Reads the given boolean register from the PLC
|
||||
/// </summary>
|
||||
/// <param name="_toRead">The register to read</param>
|
||||
public async Task<BRegisterResult> ReadBoolRegister(BRegister _toRead) {
|
||||
internal async Task<byte[]> ReadRawRegisterAsync (IRegister _toRead) {
|
||||
|
||||
//returns a byte array 1 long and with the byte beeing 0 or 1
|
||||
if (_toRead.GetType() == typeof(BoolRegister)) {
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#RCS{_toRead.BuildMewtocolQuery()}";
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
|
||||
if (!result.Success) {
|
||||
return new BRegisterResult {
|
||||
Result = result,
|
||||
Register = _toRead
|
||||
};
|
||||
}
|
||||
|
||||
var resultBool = result.Response.ParseRCSingleBit();
|
||||
if (resultBool != null) {
|
||||
_toRead.SetValueFromPLC(resultBool.Value);
|
||||
}
|
||||
|
||||
var finalRes = new BRegisterResult {
|
||||
Result = result,
|
||||
Register = _toRead
|
||||
};
|
||||
|
||||
return finalRes;
|
||||
return resultBool.Value ? new byte[] { 1 } : new byte[] { 0 };
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the given bool register on the PLC
|
||||
/// </summary>
|
||||
/// <param name="_toWrite">The register to write to</param>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <returns>The success state of the write operation</returns>
|
||||
public async Task<bool> WriteBoolRegister(BRegister _toWrite, bool value) {
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolQuery()}{(value ? "1" : "0")}";
|
||||
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
|
||||
return result.Success && result.Response.StartsWith($"%{GetStationNumber()}$WC");
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Number register reading / writing
|
||||
|
||||
/// <summary>
|
||||
/// Reads the given numeric register from the PLC
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of number (short, ushort, int, uint, float)</typeparam>
|
||||
/// <param name="_toRead">The register to read</param>
|
||||
/// <returns>A result with the given NumberRegister containing the readback value and a result struct</returns>
|
||||
public async Task<NRegisterResult<T>> ReadNumRegister<T>(NRegister<T> _toRead) {
|
||||
|
||||
Type numType = typeof(T);
|
||||
//returns a byte array 2 bytes or 4 bytes long depending on the data size
|
||||
if (_toRead.GetType().GetGenericTypeDefinition() == typeof(NumberRegister<>)) {
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}";
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
|
||||
var failedResult = new NRegisterResult<T> {
|
||||
Result = result,
|
||||
Register = _toRead
|
||||
};
|
||||
if (!result.Success)
|
||||
throw new Exception($"Failed to load the byte data for: {_toRead}");
|
||||
|
||||
if (!result.Success || string.IsNullOrEmpty(result.Response)) {
|
||||
return failedResult;
|
||||
}
|
||||
if(_toRead.RegisterType == RegisterType.DT) {
|
||||
|
||||
if (numType == typeof(short)) {
|
||||
|
||||
var resultBytes = result.Response.ParseDTByteString(4).ReverseByteOrder();
|
||||
if (resultBytes == null) return failedResult;
|
||||
var val = short.Parse(resultBytes, NumberStyles.HexNumber);
|
||||
_toRead.SetValueFromPLC(val);
|
||||
|
||||
} else if (numType == typeof(ushort)) {
|
||||
|
||||
var resultBytes = result.Response.ParseDTByteString(4).ReverseByteOrder();
|
||||
if (resultBytes == null) return failedResult;
|
||||
var val = ushort.Parse(resultBytes, NumberStyles.HexNumber);
|
||||
_toRead.SetValueFromPLC(val);
|
||||
|
||||
} else if (numType == typeof(int)) {
|
||||
|
||||
var resultBytes = result.Response.ParseDTByteString(8).ReverseByteOrder();
|
||||
if (resultBytes == null) return failedResult;
|
||||
var val = int.Parse(resultBytes, NumberStyles.HexNumber);
|
||||
_toRead.SetValueFromPLC(val);
|
||||
|
||||
} else if (numType == typeof(uint)) {
|
||||
|
||||
var resultBytes = result.Response.ParseDTByteString(8).ReverseByteOrder();
|
||||
if (resultBytes == null) return failedResult;
|
||||
var val = uint.Parse(resultBytes, NumberStyles.HexNumber);
|
||||
_toRead.SetValueFromPLC(val);
|
||||
|
||||
} else if (numType == typeof(float)) {
|
||||
|
||||
var resultBytes = result.Response.ParseDTByteString(8).ReverseByteOrder();
|
||||
if (resultBytes == null) return failedResult;
|
||||
//convert to unsigned int first
|
||||
var val = uint.Parse(resultBytes, NumberStyles.HexNumber);
|
||||
|
||||
byte[] floatVals = BitConverter.GetBytes(val);
|
||||
float finalFloat = BitConverter.ToSingle(floatVals, 0);
|
||||
|
||||
_toRead.SetValueFromPLC(finalFloat);
|
||||
|
||||
} else if (numType == typeof(TimeSpan)) {
|
||||
|
||||
var resultBytes = result.Response.ParseDTByteString(8).ReverseByteOrder();
|
||||
if (resultBytes == null) return failedResult;
|
||||
//convert to unsigned int first
|
||||
var vallong = long.Parse(resultBytes, NumberStyles.HexNumber);
|
||||
var valMillis = vallong * 10;
|
||||
var ts = TimeSpan.FromMilliseconds(valMillis);
|
||||
|
||||
//minmax writable / readable value is 10ms
|
||||
_toRead.SetValueFromPLC(ts);
|
||||
|
||||
}
|
||||
|
||||
var finalRes = new NRegisterResult<T> {
|
||||
Result = result,
|
||||
Register = _toRead
|
||||
};
|
||||
|
||||
return finalRes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the given numeric register from the PLC
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of number (short, ushort, int, uint, float)</typeparam>
|
||||
/// <param name="_toWrite">The register to write</param>
|
||||
/// <param name="_value">The value to write</param>
|
||||
/// <returns>The success state of the write operation</returns>
|
||||
public async Task<bool> WriteNumRegister<T>(NRegister<T> _toWrite, T _value) {
|
||||
|
||||
byte[] toWriteVal;
|
||||
Type numType = typeof(T);
|
||||
|
||||
if (numType == typeof(short)) {
|
||||
toWriteVal = BitConverter.GetBytes(Convert.ToInt16(_value));
|
||||
} else if (numType == typeof(ushort)) {
|
||||
toWriteVal = BitConverter.GetBytes(Convert.ToUInt16(_value));
|
||||
} else if (numType == typeof(int)) {
|
||||
toWriteVal = BitConverter.GetBytes(Convert.ToInt32(_value));
|
||||
} else if (numType == typeof(uint)) {
|
||||
toWriteVal = BitConverter.GetBytes(Convert.ToUInt32(_value));
|
||||
} else if (numType == typeof(float)) {
|
||||
|
||||
var fl = _value as float?;
|
||||
if (fl == null)
|
||||
throw new NullReferenceException("Float cannot be null");
|
||||
|
||||
toWriteVal = BitConverter.GetBytes(fl.Value);
|
||||
|
||||
} else if (numType == typeof(TimeSpan)) {
|
||||
|
||||
var fl = _value as TimeSpan?;
|
||||
if (fl == null)
|
||||
throw new NullReferenceException("Timespan cannot be null");
|
||||
|
||||
var tLong = (uint)(fl.Value.TotalMilliseconds / 10);
|
||||
toWriteVal = BitConverter.GetBytes(tLong);
|
||||
return result.Response.ParseDTByteString(4).HexStringToByteArray();
|
||||
|
||||
} else {
|
||||
toWriteVal = null;
|
||||
|
||||
return result.Response.ParseDTByteString(8).HexStringToByteArray();
|
||||
|
||||
}
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolQuery()}{toWriteVal.ToHexString()}";
|
||||
}
|
||||
|
||||
//returns a byte array with variable size
|
||||
if (_toRead.GetType() == typeof(BytesRegister<>)) {
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}";
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
|
||||
return result.Success && result.Response.StartsWith($"%{GetStationNumber()}$WD");
|
||||
if (!result.Success)
|
||||
throw new Exception($"Failed to load the byte data for: {_toRead}");
|
||||
|
||||
return result.Response.ParseDTString().ReverseByteOrder().HexStringToByteArray();
|
||||
|
||||
}
|
||||
|
||||
throw new Exception($"Failed to load the byte data for: {_toRead}");
|
||||
|
||||
}
|
||||
|
||||
internal async Task<bool> WriteRawRegisterAsync (IRegister _toWrite, byte[] data) {
|
||||
|
||||
//returns a byte array 1 long and with the byte beeing 0 or 1
|
||||
if (_toWrite.GetType() == typeof(BoolRegister)) {
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#WCS{_toWrite.BuildMewtocolQuery()}{(data[0] == 1 ? "1" : "0")}";
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
return result.Success;
|
||||
|
||||
}
|
||||
|
||||
//returns a byte array 2 bytes or 4 bytes long depending on the data size
|
||||
if (_toWrite.GetType().GetGenericTypeDefinition() == typeof(NumberRegister<>)) {
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#WD{_toWrite.BuildMewtocolQuery()}{data.ToHexString()}";
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
return result.Success;
|
||||
|
||||
}
|
||||
|
||||
//returns a byte array with variable size
|
||||
if (_toWrite.GetType() == typeof(BytesRegister<>)) {
|
||||
|
||||
//string stationNum = GetStationNumber();
|
||||
//string dataString = gotBytes.BuildDTString(_toWrite.ReservedSize);
|
||||
//string dataArea = _toWrite.BuildCustomIdent(dataString.Length / 4);
|
||||
|
||||
//string requeststring = $"%{stationNum}#WD{dataArea}{dataString}";
|
||||
|
||||
//var result = await SendCommandAsync(requeststring);
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String register reading / writing
|
||||
#region Register reading / writing
|
||||
|
||||
//string is build up like this
|
||||
//04 00 04 00 53 50 33 35 13
|
||||
//0, 1 = reserved size
|
||||
//1, 2 = current size
|
||||
//3,4,5,6 = ASCII encoded chars (SP35)
|
||||
//7,8 = checksum
|
||||
public async Task<bool> SetRegisterAsync (IRegister register, object value) {
|
||||
|
||||
/// <summary>
|
||||
/// Reads back the value of a string register
|
||||
/// </summary>
|
||||
/// <param name="_toRead">The register to read</param>
|
||||
/// <param name="_stationNumber">The station number of the PLC</param>
|
||||
/// <returns></returns>
|
||||
public async Task<SRegisterResult> ReadStringRegister(SRegister _toRead, int _stationNumber = 1) {
|
||||
var internalReg = (IRegisterInternal)register;
|
||||
|
||||
string requeststring = $"%{GetStationNumber()}#RD{_toRead.BuildMewtocolQuery()}";
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
if (result.Success)
|
||||
_toRead.SetValueFromPLC(result.Response.ParseDTString());
|
||||
return new SRegisterResult {
|
||||
Result = result,
|
||||
Register = _toRead
|
||||
};
|
||||
}
|
||||
return await internalReg.WriteAsync(this, value);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a string to a string register
|
||||
/// </summary>
|
||||
/// <param name="_toWrite">The register to write</param>
|
||||
/// <param name="_value">The value to write, if the strings length is longer than the cap size it gets trimmed to the max char length</param>
|
||||
/// <param name="_stationNumber">The station number of the PLC</param>
|
||||
/// <returns>The success state of the write operation</returns>
|
||||
public async Task<bool> WriteStringRegister(SRegister _toWrite, string _value, int _stationNumber = 1) {
|
||||
|
||||
if (_value == null) _value = "";
|
||||
if (_value.Length > _toWrite.ReservedSize) {
|
||||
throw new ArgumentException("Write string size cannot be longer than reserved string size");
|
||||
}
|
||||
|
||||
string stationNum = GetStationNumber();
|
||||
string dataString = _value.BuildDTString(_toWrite.ReservedSize);
|
||||
string dataArea = _toWrite.BuildCustomIdent(dataString.Length / 4);
|
||||
|
||||
string requeststring = $"%{stationNum}#WD{dataArea}{dataString}";
|
||||
|
||||
var result = await SendCommandAsync(requeststring);
|
||||
|
||||
|
||||
return result.Success && result.Response.StartsWith($"%{GetStationNumber()}$WD");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -11,7 +11,9 @@ namespace MewtocolNet {
|
||||
UDINT,
|
||||
REAL,
|
||||
TIME,
|
||||
STRING
|
||||
STRING,
|
||||
WORD,
|
||||
DWORD
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MewtocolNet {
|
||||
internal static class PlcVarTypeConversions {
|
||||
|
||||
static Dictionary<PlcVarType, Type> dictTypeConv = new Dictionary<PlcVarType, Type> {
|
||||
|
||||
{ PlcVarType.BOOL, typeof(bool) },
|
||||
{ PlcVarType.INT, typeof(short) },
|
||||
{ PlcVarType.UINT, typeof(ushort) },
|
||||
{ PlcVarType.DINT, typeof(int) },
|
||||
{ PlcVarType.UDINT, typeof(uint) },
|
||||
{ PlcVarType.REAL, typeof(float) },
|
||||
{ PlcVarType.TIME, typeof(TimeSpan) },
|
||||
{ PlcVarType.STRING, typeof(string) },
|
||||
|
||||
};
|
||||
|
||||
static Dictionary<PlcVarType, Type> dictRegisterConv = new Dictionary<PlcVarType, Type> {
|
||||
|
||||
{ PlcVarType.BOOL, typeof(BRegister) },
|
||||
{ PlcVarType.INT, typeof(NRegister<short>) },
|
||||
{ PlcVarType.UINT, typeof(NRegister<ushort>) },
|
||||
{ PlcVarType.DINT, typeof(NRegister<int>) },
|
||||
{ PlcVarType.UDINT, typeof(NRegister<uint>) },
|
||||
{ PlcVarType.REAL, typeof(NRegister<float>) },
|
||||
{ PlcVarType.TIME, typeof(NRegister<TimeSpan>) },
|
||||
{ PlcVarType.STRING, typeof(SRegister) },
|
||||
|
||||
};
|
||||
|
||||
internal static bool IsAllowedPlcCastingType <T> () {
|
||||
|
||||
var inversed = dictTypeConv.ToDictionary((i) => i.Value, (i) => i.Key);
|
||||
|
||||
return inversed.ContainsKey(typeof(T));
|
||||
|
||||
}
|
||||
|
||||
internal static bool IsAllowedPlcCastingType (this Type type) {
|
||||
|
||||
var inversed = dictTypeConv.ToDictionary((i) => i.Value, (i) => i.Key);
|
||||
|
||||
return inversed.ContainsKey(type);
|
||||
|
||||
}
|
||||
|
||||
internal static Type ToDotnetType (this PlcVarType type) {
|
||||
|
||||
if(dictTypeConv.ContainsKey(type)) {
|
||||
|
||||
return dictTypeConv[type];
|
||||
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"The PlcVarType: '{type}' is not supported");
|
||||
|
||||
}
|
||||
|
||||
internal static PlcVarType ToPlcVarType (this Type type) {
|
||||
|
||||
var inversed = dictTypeConv.ToDictionary((i) => i.Value, (i) => i.Key);
|
||||
|
||||
if (inversed.ContainsKey(type)) {
|
||||
|
||||
return inversed[type];
|
||||
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"The Dotnet Type: '{type}' is not supported");
|
||||
|
||||
}
|
||||
|
||||
internal static Type ToRegisterType (this PlcVarType type) {
|
||||
|
||||
if (dictRegisterConv.ContainsKey(type)) {
|
||||
|
||||
return dictRegisterConv[type];
|
||||
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"The PlcVarType: '{type}' is not supported");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,10 +8,12 @@ namespace MewtocolNet.RegisterAttributes {
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||
public class RegisterAttribute : Attribute {
|
||||
|
||||
internal int MemoryArea;
|
||||
internal int StringLength;
|
||||
internal RegisterType RegisterType;
|
||||
internal RegisterType? RegisterType;
|
||||
|
||||
internal int MemoryArea = 0;
|
||||
internal int ByteLength = 2;
|
||||
internal byte SpecialAddress = 0x0;
|
||||
|
||||
internal BitCount BitCount;
|
||||
internal int AssignedBitIndex = -1;
|
||||
|
||||
@@ -19,11 +21,36 @@ namespace MewtocolNet.RegisterAttributes {
|
||||
/// Attribute for string type or numeric registers
|
||||
/// </summary>
|
||||
/// <param name="memoryArea">The area in the plcs memory</param>
|
||||
/// <param name="stringLength">The max string length in the plc</param>
|
||||
public RegisterAttribute(int memoryArea, int stringLength = 1) {
|
||||
public RegisterAttribute(int memoryArea) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
StringLength = stringLength;
|
||||
|
||||
}
|
||||
|
||||
public RegisterAttribute(int memoryArea, int byteLength) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
ByteLength = byteLength;
|
||||
|
||||
}
|
||||
|
||||
public RegisterAttribute(int memoryArea, BitCount bitCount) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
BitCount = bitCount;
|
||||
AssignedBitIndex = 0;
|
||||
|
||||
RegisterType = BitCount == BitCount.B16 ? MewtocolNet.RegisterType.DT : MewtocolNet.RegisterType.DDT;
|
||||
|
||||
}
|
||||
|
||||
public RegisterAttribute(int memoryArea, BitCount bitCount, int bitIndex) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
BitCount = bitCount;
|
||||
AssignedBitIndex = bitIndex;
|
||||
|
||||
RegisterType = BitCount == BitCount.B16 ? MewtocolNet.RegisterType.DT : MewtocolNet.RegisterType.DDT;
|
||||
|
||||
}
|
||||
|
||||
@@ -49,42 +76,6 @@ namespace MewtocolNet.RegisterAttributes {
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attribute to read numeric registers as bitwise
|
||||
/// </summary>
|
||||
/// <param name="memoryArea">The area in the plcs memory</param>
|
||||
/// <param name="bitcount">The number of bits to parse</param>
|
||||
public RegisterAttribute(int memoryArea, BitCount bitcount) {
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
StringLength = 0;
|
||||
BitCount = bitcount;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attribute to read numeric registers as bitwise
|
||||
/// </summary>
|
||||
/// <param name="memoryArea">The area in the plcs memory</param>
|
||||
/// <param name="bitcount">The number of bits to parse</param>
|
||||
/// <param name="assignBit">The index of the bit that gets linked to the bool</param>
|
||||
public RegisterAttribute(int memoryArea, uint assignBit, BitCount bitcount) {
|
||||
|
||||
if (assignBit > 15 && bitcount == BitCount.B16) {
|
||||
throw new NotSupportedException("The assignBit parameter cannot be greater than 15 in a 16 bit var");
|
||||
}
|
||||
|
||||
if (assignBit > 31 && bitcount == BitCount.B32) {
|
||||
throw new NotSupportedException("The assignBit parameter cannot be greater than 31 in a 32 bit var");
|
||||
}
|
||||
|
||||
MemoryArea = memoryArea;
|
||||
StringLength = 0;
|
||||
BitCount = bitcount;
|
||||
AssignedBitIndex = (int)assignBit;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
118
MewtocolNet/RegisterBuildInfo.cs
Normal file
118
MewtocolNet/RegisterBuildInfo.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MewtocolNet
|
||||
{
|
||||
|
||||
internal struct RegisterBuildInfo {
|
||||
|
||||
internal string name;
|
||||
internal int memoryAddress;
|
||||
internal int memorySizeBytes;
|
||||
internal byte? specialAddress;
|
||||
|
||||
internal RegisterType? registerType;
|
||||
internal Type dotnetCastType;
|
||||
internal Type collectionType;
|
||||
|
||||
internal IRegister Build () {
|
||||
|
||||
RegisterType regType = registerType ?? dotnetCastType.ToRegisterTypeDefault();
|
||||
|
||||
PlcVarType plcType = dotnetCastType.ToPlcVarType();
|
||||
Type registerClassType = plcType.GetDefaultPlcVarType();
|
||||
|
||||
if (regType.IsNumericDTDDT() && (dotnetCastType == typeof(bool) || dotnetCastType == typeof(BitArray))) {
|
||||
|
||||
//-------------------------------------------
|
||||
//as numeric register with boolean bit target
|
||||
|
||||
var type = typeof(NumberRegister<BitArray>);
|
||||
|
||||
var areaAddr = memoryAddress;
|
||||
|
||||
//create a new bregister instance
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance;
|
||||
|
||||
//int _adress, string _name = null, bool isBitwise = false, Type _enumType = null
|
||||
var parameters = new object[] { areaAddr, name, true, null };
|
||||
var instance = (IRegister)Activator.CreateInstance(type, flags, null, parameters, null);
|
||||
|
||||
if (collectionType != null)
|
||||
((IRegisterInternal)instance).WithCollectionType(collectionType);
|
||||
|
||||
return instance;
|
||||
|
||||
} else if (regType.IsNumericDTDDT()) {
|
||||
|
||||
//-------------------------------------------
|
||||
//as numeric register
|
||||
|
||||
var type = plcType.GetDefaultPlcVarType();
|
||||
|
||||
var areaAddr = memoryAddress;
|
||||
|
||||
//create a new bregister instance
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance;
|
||||
|
||||
//int _adress, string _name = null, bool isBitwise = false, Type _enumType = null
|
||||
var parameters = new object[] { areaAddr, name, false, null };
|
||||
var instance = (IRegister)Activator.CreateInstance(type, flags, null, parameters, null);
|
||||
|
||||
if(collectionType != null)
|
||||
((IRegisterInternal)instance).WithCollectionType(collectionType);
|
||||
|
||||
return instance;
|
||||
|
||||
}
|
||||
|
||||
if (regType.IsBoolean()) {
|
||||
|
||||
//-------------------------------------------
|
||||
//as boolean register
|
||||
|
||||
var io = (IOType)(int)regType;
|
||||
var spAddr = specialAddress;
|
||||
var areaAddr = memoryAddress;
|
||||
|
||||
//create a new bregister instance
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance;
|
||||
var parameters = new object[] { io, spAddr.Value, areaAddr, name };
|
||||
var instance = (BoolRegister)Activator.CreateInstance(typeof(BoolRegister), flags, null, parameters, null);
|
||||
|
||||
if (collectionType != null)
|
||||
((IRegisterInternal)instance).WithCollectionType(collectionType);
|
||||
|
||||
return instance;
|
||||
|
||||
}
|
||||
|
||||
if(regType == RegisterType.DT_RANGE) {
|
||||
|
||||
//-------------------------------------------
|
||||
//as byte range register
|
||||
|
||||
var type = plcType.GetDefaultPlcVarType();
|
||||
|
||||
//create a new bregister instance
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance;
|
||||
//int _adress, int _reservedSize, string _name = null
|
||||
var parameters = new object[] { memoryAddress, memorySizeBytes, name };
|
||||
var instance = (IRegister)Activator.CreateInstance(type, flags, null, parameters, null);
|
||||
|
||||
if (collectionType != null)
|
||||
((IRegisterInternal)instance).WithCollectionType(collectionType);
|
||||
|
||||
return instance;
|
||||
|
||||
}
|
||||
|
||||
throw new Exception("Failed to build register");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MewtocolNet.RegisterBuilding {
|
||||
namespace MewtocolNet.RegisterBuilding
|
||||
{
|
||||
|
||||
public static class FinalizerExtensions {
|
||||
|
||||
@@ -29,7 +31,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
step.dotnetVarType = typeof(bool);
|
||||
|
||||
} else if (isTypeNotDefined && step.RegType == RegisterType.DT_START) {
|
||||
} else if (isTypeNotDefined && step.RegType == RegisterType.DT_RANGE) {
|
||||
|
||||
step.dotnetVarType = typeof(string);
|
||||
|
||||
@@ -37,58 +39,31 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
if(step.plcVarType != null) {
|
||||
|
||||
step.dotnetVarType = step.plcVarType.Value.ToDotnetType();
|
||||
step.dotnetVarType = step.plcVarType.Value.GetDefaultDotnetType();
|
||||
|
||||
}
|
||||
|
||||
//as numeric register
|
||||
if (step.RegType.IsNumericDTDDT()) {
|
||||
var builtReg = new RegisterBuildInfo {
|
||||
|
||||
if(step.plcVarType == null && step.dotnetVarType != null) {
|
||||
name = step.Name,
|
||||
specialAddress = step.SpecialAddress,
|
||||
memoryAddress = step.MemAddress,
|
||||
registerType = step.RegType,
|
||||
dotnetCastType = step.dotnetVarType,
|
||||
|
||||
step.plcVarType = step.dotnetVarType.ToPlcVarType();
|
||||
}.Build();
|
||||
|
||||
step.AddToRegisterList(builtReg);
|
||||
|
||||
return builtReg;
|
||||
|
||||
}
|
||||
|
||||
var type = step.plcVarType.Value.ToRegisterType();
|
||||
private static void AddToRegisterList (this RegisterBuilderStep step, IRegister instance) {
|
||||
|
||||
var areaAddr = step.MemAddress;
|
||||
var name = step.Name;
|
||||
if (step.forInterface == null) return;
|
||||
|
||||
//create a new bregister instance
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance;
|
||||
|
||||
//int _adress, string _name = null, bool isBitwise = false, Type _enumType = null
|
||||
var parameters = new object[] { areaAddr, name, false, null };
|
||||
var instance = (IRegister)Activator.CreateInstance(type, flags, null, parameters, null);
|
||||
|
||||
return instance;
|
||||
|
||||
}
|
||||
|
||||
if (step.RegType.IsBoolean()) {
|
||||
|
||||
var io = (IOType)(int)step.RegType;
|
||||
var spAddr = step.SpecialAddress;
|
||||
var areaAddr = step.MemAddress;
|
||||
var name = step.Name;
|
||||
|
||||
//create a new bregister instance
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance;
|
||||
var parameters = new object[] { io, spAddr.Value, areaAddr, name };
|
||||
var instance = (BRegister)Activator.CreateInstance(typeof(BRegister), flags, null, parameters, null);
|
||||
|
||||
return instance;
|
||||
|
||||
}
|
||||
|
||||
if (step.dotnetVarType != null) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
throw new Exception("Failed to build register");
|
||||
step.forInterface.AddRegister(instance);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
/// <summary>
|
||||
/// Contains useful tools for register creation
|
||||
/// </summary>
|
||||
public static class RegBuilder {
|
||||
public class RegBuilder {
|
||||
|
||||
internal MewtocolInterface forInterface = null;
|
||||
|
||||
//methods to test the input string on
|
||||
private static List<Func<string, ParseResult>> parseMethods = new List<Func<string, ParseResult>>() {
|
||||
@@ -19,7 +21,18 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
};
|
||||
|
||||
public static RegisterBuilderStep FromPlcRegName (string plcAddrName, string name = null) {
|
||||
public static RegBuilder ForInterface (MewtocolInterface interf) {
|
||||
|
||||
var rb = new RegBuilder();
|
||||
rb.forInterface = interf;
|
||||
return rb;
|
||||
|
||||
}
|
||||
|
||||
public static RegBuilder Factory { get; private set; } = new RegBuilder();
|
||||
|
||||
|
||||
public RegisterBuilderStep FromPlcRegName (string plcAddrName, string name = null) {
|
||||
|
||||
foreach (var method in parseMethods) {
|
||||
|
||||
@@ -31,7 +44,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
res.stepData.Name = name;
|
||||
|
||||
res.stepData.OriginalInput = plcAddrName;
|
||||
|
||||
res.stepData.forInterface = forInterface;
|
||||
return res.stepData;
|
||||
|
||||
} else if(res.state == ParseResultState.FailedHard) {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
public class RegisterBuilderStep {
|
||||
|
||||
internal MewtocolInterface forInterface;
|
||||
|
||||
internal bool wasCasted = false;
|
||||
|
||||
internal string OriginalInput;
|
||||
@@ -15,9 +18,9 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
internal PlcVarType? plcVarType;
|
||||
internal Type dotnetVarType;
|
||||
|
||||
public RegisterBuilderStep () => throw new NotSupportedException("Cant make a new instance of RegisterBuilderStep, use the builder pattern");
|
||||
public RegisterBuilderStep() => throw new NotSupportedException("Cant make a new instance of RegisterBuilderStep, use the builder pattern");
|
||||
|
||||
internal RegisterBuilderStep (RegisterType regType, int memAddr) {
|
||||
internal RegisterBuilderStep(RegisterType regType, int memAddr) {
|
||||
|
||||
RegType = regType;
|
||||
MemAddress = memAddr;
|
||||
@@ -32,7 +35,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
public RegisterBuilderStep AsPlcType (PlcVarType varType) {
|
||||
public RegisterBuilderStep AsPlcType(PlcVarType varType) {
|
||||
|
||||
dotnetVarType = null;
|
||||
plcVarType = varType;
|
||||
@@ -43,9 +46,9 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
public RegisterBuilderStep AsType<T> () {
|
||||
public RegisterBuilderStep AsType<T>() {
|
||||
|
||||
if(!typeof(T).IsAllowedPlcCastingType()) {
|
||||
if (!typeof(T).IsAllowedPlcCastingType()) {
|
||||
|
||||
throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC type casting");
|
||||
|
||||
@@ -60,7 +63,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
internal RegisterBuilderStep AutoType () {
|
||||
internal RegisterBuilderStep AutoType() {
|
||||
|
||||
switch (RegType) {
|
||||
case RegisterType.X:
|
||||
@@ -74,7 +77,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
case RegisterType.DDT:
|
||||
dotnetVarType = typeof(int);
|
||||
break;
|
||||
case RegisterType.DT_START:
|
||||
case RegisterType.DT_RANGE:
|
||||
dotnetVarType = typeof(string);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace MewtocolNet {
|
||||
using System;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
/// <summary>
|
||||
/// The register prefixed type
|
||||
@@ -26,9 +28,9 @@
|
||||
/// </summary>
|
||||
DDT = 4,
|
||||
/// <summary>
|
||||
/// Start area of a byte sequence longer than 2 words
|
||||
/// Area of a byte sequence longer than 2 words
|
||||
/// </summary>
|
||||
DT_START = 5,
|
||||
DT_RANGE = 5,
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet.Registers {
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing a boolean
|
||||
/// </summary>
|
||||
public class BRegister : IRegister, INotifyPropertyChanged {
|
||||
public class BoolRegister : IRegister, IRegisterInternal, INotifyPropertyChanged {
|
||||
|
||||
/// <summary>
|
||||
/// Gets called whenever the value was changed
|
||||
@@ -62,7 +63,7 @@ namespace MewtocolNet.Registers {
|
||||
/// <param name="_name">The custom name</param>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public BRegister(IOType _io, byte _spAddress = 0x0, int _areaAdress = 0, string _name = null) {
|
||||
public BoolRegister(IOType _io, byte _spAddress = 0x0, int _areaAdress = 0, string _name = null) {
|
||||
|
||||
if (_areaAdress < 0)
|
||||
throw new NotSupportedException("The area address cant be negative");
|
||||
@@ -84,36 +85,33 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
}
|
||||
|
||||
internal BRegister WithCollectionType(Type colType) {
|
||||
|
||||
collectionType = colType;
|
||||
return this;
|
||||
|
||||
}
|
||||
public void WithCollectionType (Type colType) => collectionType = colType;
|
||||
|
||||
public byte? GetSpecialAddress() => SpecialAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the register area name
|
||||
/// Builds the register area name for the mewtocol protocol
|
||||
/// </summary>
|
||||
public string BuildMewtocolQuery() {
|
||||
|
||||
//build area code from register type
|
||||
StringBuilder asciistring = new StringBuilder(RegisterType.ToString());
|
||||
//(R|X|Y)(area add [3] + special add [1])
|
||||
StringBuilder asciistring = new StringBuilder();
|
||||
|
||||
string memPadded = MemoryAddress.ToString().PadLeft(4, '0');
|
||||
string prefix = RegisterType.ToString();
|
||||
string mem = MemoryAddress.ToString();
|
||||
string sp = SpecialAddress.ToString("X1");
|
||||
|
||||
asciistring.Append(memPadded);
|
||||
asciistring.Append(prefix);
|
||||
asciistring.Append(mem.PadLeft(3, '0'));
|
||||
asciistring.Append(sp);
|
||||
|
||||
return asciistring.ToString();
|
||||
|
||||
}
|
||||
|
||||
internal void SetValueFromPLC(bool val) {
|
||||
public void SetValueFromPLC(object val) {
|
||||
|
||||
lastValue = val;
|
||||
lastValue = (bool)val;
|
||||
TriggerChangedEvnt(this);
|
||||
TriggerNotifyChange();
|
||||
|
||||
@@ -183,6 +181,19 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
}
|
||||
|
||||
public async Task<object> ReadAsync (MewtocolInterface interf) {
|
||||
|
||||
var read = await interf.ReadRawRegisterAsync(this);
|
||||
return PlcValueParser.Parse<bool>(read);
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> WriteAsync (MewtocolInterface interf, object data) {
|
||||
|
||||
return await interf.WriteRawRegisterAsync(this, PlcValueParser.Encode((bool)data));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Result for a boolean register
|
||||
/// </summary>
|
||||
public class BRegisterResult {
|
||||
public class BoolRegisterResult {
|
||||
|
||||
/// <summary>
|
||||
/// The command result
|
||||
@@ -13,7 +13,7 @@
|
||||
/// <summary>
|
||||
/// The used register
|
||||
/// </summary>
|
||||
public BRegister Register { get; set; }
|
||||
public BoolRegister Register { get; set; }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet.Registers {
|
||||
|
||||
/// <summary>
|
||||
/// Defines a register containing a string
|
||||
/// </summary>
|
||||
public class SRegister : IRegister {
|
||||
public class BytesRegister<T> : IRegister, IRegisterInternal {
|
||||
|
||||
/// <summary>
|
||||
/// Gets called whenever the value was changed
|
||||
@@ -58,30 +60,25 @@ namespace MewtocolNet.Registers {
|
||||
/// <summary>
|
||||
/// Defines a register containing a string
|
||||
/// </summary>
|
||||
public SRegister(int _adress, int _reservedStringSize, string _name = null) {
|
||||
public BytesRegister(int _adress, int _reservedSize, string _name = null) {
|
||||
|
||||
if (_adress > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999");
|
||||
name = _name;
|
||||
memoryAdress = _adress;
|
||||
ReservedSize = (short)_reservedStringSize;
|
||||
ReservedSize = (short)_reservedSize;
|
||||
|
||||
//calc mem length
|
||||
var wordsize = (double)_reservedStringSize / 2;
|
||||
var wordsize = (double)_reservedSize / 2;
|
||||
if (wordsize % 2 != 0) {
|
||||
wordsize++;
|
||||
}
|
||||
|
||||
RegisterType = RegisterType.DT_START;
|
||||
RegisterType = RegisterType.DT_RANGE;
|
||||
|
||||
memoryLength = (int)Math.Round(wordsize + 1);
|
||||
}
|
||||
|
||||
internal SRegister WithCollectionType (Type colType) {
|
||||
|
||||
collectionType = colType;
|
||||
return this;
|
||||
|
||||
}
|
||||
public void WithCollectionType(Type colType) => collectionType = colType;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the register identifier for the mewotocol protocol
|
||||
@@ -115,9 +112,9 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
public bool IsUsedBitwise() => false;
|
||||
|
||||
internal void SetValueFromPLC(string val) {
|
||||
public void SetValueFromPLC(object val) {
|
||||
|
||||
lastValue = val;
|
||||
lastValue = (string)val;
|
||||
|
||||
TriggerChangedEvnt(this);
|
||||
TriggerNotifyChange();
|
||||
@@ -146,6 +143,19 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
public string ToString(bool additional) => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
|
||||
|
||||
public async Task<object> ReadAsync(MewtocolInterface interf) {
|
||||
|
||||
var read = await interf.ReadRawRegisterAsync(this);
|
||||
return PlcValueParser.Parse<T>(read);
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> WriteAsync(MewtocolInterface interf, object data) {
|
||||
|
||||
return await interf.WriteRawRegisterAsync(this, (byte[])data);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// The results of a string register operation
|
||||
/// </summary>
|
||||
public class SRegisterResult {
|
||||
public class BytesRegisterResult<T> {
|
||||
|
||||
/// <summary>
|
||||
/// The command result
|
||||
@@ -12,7 +12,7 @@
|
||||
/// <summary>
|
||||
/// The register definition used
|
||||
/// </summary>
|
||||
public SRegister Register { get; set; }
|
||||
public BytesRegister<T> Register { get; set; }
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet.Registers {
|
||||
|
||||
@@ -10,7 +11,7 @@ namespace MewtocolNet.Registers {
|
||||
/// Defines a register containing a number
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the numeric value</typeparam>
|
||||
public class NRegister<T> : IRegister {
|
||||
public class NumberRegister<T> : IRegister, IRegisterInternal {
|
||||
|
||||
/// <summary>
|
||||
/// Gets called whenever the value was changed
|
||||
@@ -65,7 +66,7 @@ namespace MewtocolNet.Registers {
|
||||
/// </summary>
|
||||
/// <param name="_adress">Memory start adress max 99999</param>
|
||||
/// <param name="_name">Name of the register</param>
|
||||
public NRegister (int _adress, string _name = null) {
|
||||
public NumberRegister (int _adress, string _name = null) {
|
||||
|
||||
if (_adress > 99999)
|
||||
throw new NotSupportedException("Memory adresses cant be greater than 99999");
|
||||
@@ -98,7 +99,7 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
}
|
||||
|
||||
public NRegister (int _adress, string _name = null, bool isBitwise = false, Type _enumType = null) {
|
||||
public NumberRegister (int _adress, string _name = null, bool isBitwise = false, Type _enumType = null) {
|
||||
|
||||
if (_adress > 99999) throw new NotSupportedException("Memory adresses cant be greater than 99999");
|
||||
memoryAdress = _adress;
|
||||
@@ -132,14 +133,9 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
}
|
||||
|
||||
internal NRegister<T> WithCollectionType(Type colType) {
|
||||
public void WithCollectionType(Type colType) => collectionType = colType;
|
||||
|
||||
collectionType = colType;
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
internal void SetValueFromPLC(object val) {
|
||||
public void SetValueFromPLC(object val) {
|
||||
|
||||
lastValue = (T)val;
|
||||
TriggerChangedEvnt(this);
|
||||
@@ -214,7 +210,7 @@ namespace MewtocolNet.Registers {
|
||||
/// <returns>A bitarray</returns>
|
||||
public BitArray GetBitwise() {
|
||||
|
||||
if (this is NRegister<short> shortReg) {
|
||||
if (this is NumberRegister<short> shortReg) {
|
||||
|
||||
var bytes = BitConverter.GetBytes((short)Value);
|
||||
BitArray bitAr = new BitArray(bytes);
|
||||
@@ -222,7 +218,7 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
}
|
||||
|
||||
if (this is NRegister<int> intReg) {
|
||||
if (this is NumberRegister<int> intReg) {
|
||||
|
||||
var bytes = BitConverter.GetBytes((int)Value);
|
||||
BitArray bitAr = new BitArray(bytes);
|
||||
@@ -291,6 +287,19 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
public string ToString(bool additional) => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
|
||||
|
||||
public async Task<object> ReadAsync(MewtocolInterface interf) {
|
||||
|
||||
var read = await interf.ReadRawRegisterAsync(this);
|
||||
return PlcValueParser.Parse<T>(read);
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> WriteAsync(MewtocolInterface interf, object data) {
|
||||
|
||||
return await interf.WriteRawRegisterAsync(this, PlcValueParser.Encode((T)data));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
/// Result for a read/write operation
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the numeric value</typeparam>
|
||||
public class NRegisterResult<T> {
|
||||
public class NumberRegisterResult<T> {
|
||||
|
||||
/// <summary>
|
||||
/// Command result
|
||||
@@ -14,7 +14,7 @@
|
||||
/// <summary>
|
||||
/// The used register
|
||||
/// </summary>
|
||||
public NRegister<T> Register { get; set; }
|
||||
public NumberRegister<T> Register { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Trys to get the value of there is one
|
||||
124
MewtocolNet/TypeConversion/Conversions.cs
Normal file
124
MewtocolNet/TypeConversion/Conversions.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace MewtocolNet.TypeConversion {
|
||||
|
||||
internal static class Conversions {
|
||||
|
||||
internal static Dictionary<PlcVarType, RegisterType> dictPlcTypeToRegisterType = new Dictionary<PlcVarType, RegisterType> {
|
||||
|
||||
{ PlcVarType.BOOL, RegisterType.R },
|
||||
{ PlcVarType.INT, RegisterType.DT },
|
||||
{ PlcVarType.UINT, RegisterType.DT },
|
||||
{ PlcVarType.DINT, RegisterType.DDT },
|
||||
{ PlcVarType.UDINT, RegisterType.DDT },
|
||||
{ PlcVarType.REAL, RegisterType.DDT },
|
||||
{ PlcVarType.TIME, RegisterType.DDT },
|
||||
{ PlcVarType.WORD, RegisterType.DT },
|
||||
{ PlcVarType.DWORD, RegisterType.DDT },
|
||||
{ PlcVarType.STRING, RegisterType.DT_RANGE },
|
||||
|
||||
};
|
||||
|
||||
internal static List<IPlcTypeConverter> items = new List<IPlcTypeConverter> {
|
||||
|
||||
new PlcTypeConversion<bool>(RegisterType.R) {
|
||||
HoldingRegisterType = typeof(BoolRegister),
|
||||
PlcVarType = PlcVarType.BOOL,
|
||||
FromRaw = bytes => {
|
||||
|
||||
return (bool)(bytes[0] == 1);
|
||||
},
|
||||
ToRaw = value => {
|
||||
|
||||
return new byte[] { (byte)(value ? 1 : 0) };
|
||||
|
||||
},
|
||||
},
|
||||
new PlcTypeConversion<bool>(RegisterType.X) {
|
||||
HoldingRegisterType = typeof(BoolRegister),
|
||||
PlcVarType = PlcVarType.BOOL,
|
||||
FromRaw = bytes => {
|
||||
|
||||
return bytes[0] == 1;
|
||||
},
|
||||
ToRaw = value => {
|
||||
|
||||
return new byte[] { (byte)(value ? 1 : 0) };
|
||||
|
||||
},
|
||||
},
|
||||
new PlcTypeConversion<bool>(RegisterType.Y) {
|
||||
HoldingRegisterType = typeof(BoolRegister),
|
||||
PlcVarType = PlcVarType.BOOL,
|
||||
FromRaw = bytes => {
|
||||
|
||||
return bytes[0] == 1;
|
||||
},
|
||||
ToRaw = value => {
|
||||
|
||||
return new byte[] { (byte)(value ? 1 : 0) };
|
||||
|
||||
},
|
||||
},
|
||||
new PlcTypeConversion<short>(RegisterType.DT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<short>),
|
||||
PlcVarType = PlcVarType.INT,
|
||||
FromRaw = bytes => {
|
||||
|
||||
return BitConverter.ToInt16(bytes, 0);
|
||||
},
|
||||
ToRaw = value => {
|
||||
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
},
|
||||
},
|
||||
new PlcTypeConversion<ushort>(RegisterType.DT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<ushort>),
|
||||
PlcVarType = PlcVarType.UINT,
|
||||
FromRaw = bytes => {
|
||||
|
||||
return BitConverter.ToUInt16(bytes, 0);
|
||||
},
|
||||
ToRaw = value => {
|
||||
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
},
|
||||
},
|
||||
new PlcTypeConversion<int>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<int>),
|
||||
PlcVarType = PlcVarType.DINT,
|
||||
FromRaw = bytes => {
|
||||
|
||||
return BitConverter.ToInt32(bytes, 0);
|
||||
},
|
||||
ToRaw = value => {
|
||||
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
},
|
||||
},
|
||||
new PlcTypeConversion<uint>(RegisterType.DDT) {
|
||||
HoldingRegisterType = typeof(NumberRegister<uint>),
|
||||
PlcVarType = PlcVarType.UDINT,
|
||||
FromRaw = bytes => {
|
||||
|
||||
return BitConverter.ToUInt32(bytes, 0);
|
||||
},
|
||||
ToRaw = value => {
|
||||
|
||||
return BitConverter.GetBytes(value);
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
21
MewtocolNet/TypeConversion/IPlcTypeConverter.cs
Normal file
21
MewtocolNet/TypeConversion/IPlcTypeConverter.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
internal interface IPlcTypeConverter {
|
||||
|
||||
object FromRawData(byte[] data);
|
||||
|
||||
byte[] ToRawData(object value);
|
||||
|
||||
Type GetDotnetType();
|
||||
|
||||
Type GetHoldingRegisterType();
|
||||
|
||||
RegisterType GetPlcRegisterType();
|
||||
|
||||
PlcVarType GetPlcVarType();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
42
MewtocolNet/TypeConversion/PlcTypeConversion.cs
Normal file
42
MewtocolNet/TypeConversion/PlcTypeConversion.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
internal class PlcTypeConversion<T> : IPlcTypeConverter {
|
||||
|
||||
public Type MainType { get; private set; }
|
||||
|
||||
public RegisterType PlcType { get; private set; }
|
||||
|
||||
public PlcVarType PlcVarType { get; set; }
|
||||
|
||||
public Type HoldingRegisterType { get; set; }
|
||||
|
||||
public Func<byte[], T> FromRaw { get; set; }
|
||||
|
||||
public Func<T, byte[]> ToRaw { get; set; }
|
||||
|
||||
public PlcTypeConversion(RegisterType plcType) {
|
||||
|
||||
MainType = typeof(T);
|
||||
PlcType = plcType;
|
||||
|
||||
}
|
||||
|
||||
public Type GetDotnetType() => MainType;
|
||||
|
||||
public Type GetHoldingRegisterType() => HoldingRegisterType;
|
||||
|
||||
public RegisterType GetPlcRegisterType() => PlcType;
|
||||
|
||||
public PlcVarType GetPlcVarType() => PlcVarType;
|
||||
|
||||
public object FromRawData(byte[] data) => FromRaw.Invoke(data);
|
||||
|
||||
public byte[] ToRawData(object value) => ToRaw.Invoke((T)value);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
55
MewtocolNet/TypeConversion/PlcValueParser.cs
Normal file
55
MewtocolNet/TypeConversion/PlcValueParser.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolNet.TypeConversion;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
internal static class PlcValueParser {
|
||||
|
||||
private static List<IPlcTypeConverter> conversions => Conversions.items;
|
||||
|
||||
public static T Parse<T>(byte[] bytes) {
|
||||
|
||||
var converter = conversions.FirstOrDefault(x => x.GetDotnetType() == typeof(T));
|
||||
|
||||
if (converter == null)
|
||||
throw new MewtocolException($"A converter for the dotnet type {typeof(T)} doesn't exist");
|
||||
|
||||
return (T)converter.FromRawData(bytes);
|
||||
|
||||
}
|
||||
|
||||
public static byte[] Encode <T>(T value) {
|
||||
|
||||
var converter = conversions.FirstOrDefault(x => x.GetDotnetType() == typeof(T));
|
||||
|
||||
if (converter == null)
|
||||
throw new MewtocolException($"A converter for the dotnet type {typeof(T)} doesn't exist");
|
||||
|
||||
return converter.ToRawData(value);
|
||||
|
||||
}
|
||||
|
||||
public static List<Type> GetAllowDotnetTypes () => conversions.Select(x => x.GetDotnetType()).ToList();
|
||||
|
||||
public static List<Type> GetAllowRegisterTypes () => conversions.Select(x => x.GetHoldingRegisterType()).ToList();
|
||||
|
||||
public static RegisterType? GetDefaultRegisterType (Type type) =>
|
||||
conversions.FirstOrDefault(x => x.GetDotnetType() == type)?.GetPlcRegisterType();
|
||||
|
||||
public static Type GetDefaultPlcVarType (this PlcVarType type) =>
|
||||
conversions.FirstOrDefault(x => x.GetPlcVarType() == type)?.GetHoldingRegisterType();
|
||||
|
||||
public static Type GetDefaultDotnetType (this PlcVarType type) =>
|
||||
conversions.FirstOrDefault(x => x.GetPlcVarType() == type)?.GetDotnetType();
|
||||
|
||||
public static PlcVarType? GetDefaultPlcVarType (this Type type) =>
|
||||
conversions.FirstOrDefault(x => x.GetDotnetType() == type)?.GetPlcVarType();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
65
MewtocolNet/TypeConversion/PlcVarTypeConversions.cs
Normal file
65
MewtocolNet/TypeConversion/PlcVarTypeConversions.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolNet.TypeConversion;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
internal static class PlcVarTypeConversions {
|
||||
|
||||
static List<Type> allowedCastingTypes = PlcValueParser.GetAllowDotnetTypes();
|
||||
|
||||
static List<Type> allowedGenericRegisters = PlcValueParser.GetAllowRegisterTypes();
|
||||
|
||||
internal static bool IsAllowedRegisterGenericType(this IRegister register) {
|
||||
|
||||
return allowedGenericRegisters.Contains(register.GetType());
|
||||
|
||||
}
|
||||
|
||||
internal static bool IsAllowedPlcCastingType<T>() {
|
||||
|
||||
return allowedCastingTypes.Contains(typeof(T));
|
||||
|
||||
}
|
||||
|
||||
internal static bool IsAllowedPlcCastingType(this Type type) {
|
||||
|
||||
return allowedCastingTypes.Contains(type);
|
||||
|
||||
}
|
||||
|
||||
internal static RegisterType ToRegisterTypeDefault(this Type type) {
|
||||
|
||||
var found = PlcValueParser.GetDefaultRegisterType(type);
|
||||
|
||||
if (found != null) {
|
||||
|
||||
return found.Value;
|
||||
|
||||
}
|
||||
|
||||
throw new MewtocolException("No default register type found");
|
||||
|
||||
}
|
||||
|
||||
internal static PlcVarType ToPlcVarType (this Type type) {
|
||||
|
||||
var found = type.GetDefaultPlcVarType().Value;
|
||||
|
||||
if (found != null) {
|
||||
|
||||
return found;
|
||||
|
||||
}
|
||||
|
||||
throw new MewtocolException("No default plcvar type found");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,7 @@ namespace MewtocolTests {
|
||||
|
||||
//corresponds to a R100 boolean register in the PLC
|
||||
//can also be written as R1000 because the last one is a special address
|
||||
[Register(IOType.R, 100, spAdress: 0)]
|
||||
[Register(IOType.R, memoryArea: 85, spAdress: 0)]
|
||||
public bool TestBool1 { get; private set; }
|
||||
|
||||
//corresponds to a XD input of the PLC
|
||||
@@ -60,10 +60,10 @@ namespace MewtocolTests {
|
||||
public BitArray TestBitRegister32 { get; private set; }
|
||||
|
||||
//corresponds to a DT1204 as a 16bit word/int takes the bit at index 9 and writes it back as a boolean
|
||||
[Register(1204, 9, BitCount.B16)]
|
||||
[Register(1204, BitCount.B16, 9)]
|
||||
public bool BitValue { get; private set; }
|
||||
|
||||
[Register(1204, 5, BitCount.B16)]
|
||||
[Register(1204, BitCount.B32, 5)]
|
||||
public bool FillTest { get; private set; }
|
||||
|
||||
//corresponds to a DT7012 - DT7013 as a 32bit time value that gets parsed as a timespan (TIME)
|
||||
@@ -115,7 +115,7 @@ namespace MewtocolTests {
|
||||
var register = interf.GetRegister(nameof(TestRegisterCollection.TestBool1));
|
||||
|
||||
//test generic properties
|
||||
TestBasicGeneration(register, nameof(TestRegisterCollection.TestBool1), false, 100, "R100");
|
||||
TestBasicGeneration(register, nameof(TestRegisterCollection.TestBool1), false, 85, "R85");
|
||||
|
||||
}
|
||||
|
||||
@@ -208,8 +208,8 @@ namespace MewtocolTests {
|
||||
//test generic properties
|
||||
TestBasicGeneration(register, nameof(TestRegisterCollection.TestString2), null!, 7005, "DT7005");
|
||||
|
||||
Assert.Equal(5, ((SRegister)register).ReservedSize);
|
||||
Assert.Equal(4, ((SRegister)register).MemoryLength);
|
||||
Assert.Equal(5, ((BytesRegister<string>)register).ReservedSize);
|
||||
Assert.Equal(4, ((BytesRegister<string>)register).MemoryLength);
|
||||
|
||||
}
|
||||
|
||||
@@ -224,8 +224,8 @@ namespace MewtocolTests {
|
||||
//test generic properties
|
||||
TestBasicGeneration(register, "Auto_Bitwise_DT7010", (short)0, 7010, "DT7010");
|
||||
|
||||
Assert.True(((NRegister<short>)register).isUsedBitwise);
|
||||
Assert.Equal(0, ((NRegister<short>)register).MemoryLength);
|
||||
Assert.True(((NumberRegister<short>)register).isUsedBitwise);
|
||||
Assert.Equal(0, ((NumberRegister<short>)register).MemoryLength);
|
||||
|
||||
}
|
||||
|
||||
@@ -240,8 +240,8 @@ namespace MewtocolTests {
|
||||
//test generic properties
|
||||
TestBasicGeneration(register, "Auto_Bitwise_DDT8010", (int)0, 8010, "DDT8010");
|
||||
|
||||
Assert.True(((NRegister<int>)register).isUsedBitwise);
|
||||
Assert.Equal(1, ((NRegister<int>)register).MemoryLength);
|
||||
Assert.True(((NumberRegister<int>)register).isUsedBitwise);
|
||||
Assert.Equal(1, ((NumberRegister<int>)register).MemoryLength);
|
||||
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ namespace MewtocolTests {
|
||||
//test generic properties
|
||||
TestBasicGeneration(register, "Auto_Bitwise_DT1204", (short)0, 1204, "DT1204");
|
||||
|
||||
Assert.True(((NRegister<short>)register).isUsedBitwise);
|
||||
Assert.True(((NumberRegister<short>)register).isUsedBitwise);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,32 +16,32 @@ public class TestRegisterBuilder {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as BRegister List (Phyiscal Outputs)")]
|
||||
[Fact(DisplayName = "Parsing as Bool Register List (Phyiscal Outputs)")]
|
||||
public void TestParsingBRegisterY() {
|
||||
|
||||
var tests = new Dictionary<string, IRegister>() {
|
||||
|
||||
{"Y0", new BRegister(IOType.Y)},
|
||||
{"Y1", new BRegister(IOType.Y, 0x1)},
|
||||
{"Y2", new BRegister(IOType.Y, 0x2)},
|
||||
{"Y3", new BRegister(IOType.Y, 0x3)},
|
||||
{"Y4", new BRegister(IOType.Y, 0x4)},
|
||||
{"Y5", new BRegister(IOType.Y, 0x5)},
|
||||
{"Y6", new BRegister(IOType.Y, 0x6)},
|
||||
{"Y7", new BRegister(IOType.Y, 0x7)},
|
||||
{"Y8", new BRegister(IOType.Y, 0x8)},
|
||||
{"Y9", new BRegister(IOType.Y, 0x9)},
|
||||
{"Y0", new BoolRegister(IOType.Y)},
|
||||
{"Y1", new BoolRegister(IOType.Y, 0x1)},
|
||||
{"Y2", new BoolRegister(IOType.Y, 0x2)},
|
||||
{"Y3", new BoolRegister(IOType.Y, 0x3)},
|
||||
{"Y4", new BoolRegister(IOType.Y, 0x4)},
|
||||
{"Y5", new BoolRegister(IOType.Y, 0x5)},
|
||||
{"Y6", new BoolRegister(IOType.Y, 0x6)},
|
||||
{"Y7", new BoolRegister(IOType.Y, 0x7)},
|
||||
{"Y8", new BoolRegister(IOType.Y, 0x8)},
|
||||
{"Y9", new BoolRegister(IOType.Y, 0x9)},
|
||||
|
||||
{"YA", new BRegister(IOType.Y, 0xA)},
|
||||
{"YB", new BRegister(IOType.Y, 0xB)},
|
||||
{"YC", new BRegister(IOType.Y, 0xC)},
|
||||
{"YD", new BRegister(IOType.Y, 0xD)},
|
||||
{"YE", new BRegister(IOType.Y, 0xE)},
|
||||
{"YF", new BRegister(IOType.Y, 0xF)},
|
||||
{"YA", new BoolRegister(IOType.Y, 0xA)},
|
||||
{"YB", new BoolRegister(IOType.Y, 0xB)},
|
||||
{"YC", new BoolRegister(IOType.Y, 0xC)},
|
||||
{"YD", new BoolRegister(IOType.Y, 0xD)},
|
||||
{"YE", new BoolRegister(IOType.Y, 0xE)},
|
||||
{"YF", new BoolRegister(IOType.Y, 0xF)},
|
||||
|
||||
{"Y1A", new BRegister(IOType.Y, 0xA, 1)},
|
||||
{"Y10B", new BRegister(IOType.Y, 0xB, 10)},
|
||||
{"Y109C", new BRegister(IOType.Y, 0xC, 109)},
|
||||
{"Y1A", new BoolRegister(IOType.Y, 0xA, 1)},
|
||||
{"Y10B", new BoolRegister(IOType.Y, 0xB, 10)},
|
||||
{"Y109C", new BoolRegister(IOType.Y, 0xC, 109)},
|
||||
|
||||
};
|
||||
|
||||
@@ -49,32 +49,32 @@ public class TestRegisterBuilder {
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as BRegister List (Phyiscal Inputs)")]
|
||||
[Fact(DisplayName = "Parsing as Bool Register List (Phyiscal Inputs)")]
|
||||
public void TestParsingBRegisterX() {
|
||||
|
||||
var tests = new Dictionary<string, IRegister>() {
|
||||
|
||||
{"X0", new BRegister(IOType.X)},
|
||||
{"X1", new BRegister(IOType.X, 0x1)},
|
||||
{"X2", new BRegister(IOType.X, 0x2)},
|
||||
{"X3", new BRegister(IOType.X, 0x3)},
|
||||
{"X4", new BRegister(IOType.X, 0x4)},
|
||||
{"X5", new BRegister(IOType.X, 0x5)},
|
||||
{"X6", new BRegister(IOType.X, 0x6)},
|
||||
{"X7", new BRegister(IOType.X, 0x7)},
|
||||
{"X8", new BRegister(IOType.X, 0x8)},
|
||||
{"X9", new BRegister(IOType.X, 0x9)},
|
||||
{"X0", new BoolRegister(IOType.X)},
|
||||
{"X1", new BoolRegister(IOType.X, 0x1)},
|
||||
{"X2", new BoolRegister(IOType.X, 0x2)},
|
||||
{"X3", new BoolRegister(IOType.X, 0x3)},
|
||||
{"X4", new BoolRegister(IOType.X, 0x4)},
|
||||
{"X5", new BoolRegister(IOType.X, 0x5)},
|
||||
{"X6", new BoolRegister(IOType.X, 0x6)},
|
||||
{"X7", new BoolRegister(IOType.X, 0x7)},
|
||||
{"X8", new BoolRegister(IOType.X, 0x8)},
|
||||
{"X9", new BoolRegister(IOType.X, 0x9)},
|
||||
|
||||
{"XA", new BRegister(IOType.X, 0xA)},
|
||||
{"XB", new BRegister(IOType.X, 0xB)},
|
||||
{"XC", new BRegister(IOType.X, 0xC)},
|
||||
{"XD", new BRegister(IOType.X, 0xD)},
|
||||
{"XE", new BRegister(IOType.X, 0xE)},
|
||||
{"XF", new BRegister(IOType.X, 0xF)},
|
||||
{"XA", new BoolRegister(IOType.X, 0xA)},
|
||||
{"XB", new BoolRegister(IOType.X, 0xB)},
|
||||
{"XC", new BoolRegister(IOType.X, 0xC)},
|
||||
{"XD", new BoolRegister(IOType.X, 0xD)},
|
||||
{"XE", new BoolRegister(IOType.X, 0xE)},
|
||||
{"XF", new BoolRegister(IOType.X, 0xF)},
|
||||
|
||||
{"X1A", new BRegister(IOType.X, 0xA, 1)},
|
||||
{"X10B", new BRegister(IOType.X, 0xB, 10)},
|
||||
{"X109C", new BRegister(IOType.X, 0xC, 109)},
|
||||
{"X1A", new BoolRegister(IOType.X, 0xA, 1)},
|
||||
{"X10B", new BoolRegister(IOType.X, 0xB, 10)},
|
||||
{"X109C", new BoolRegister(IOType.X, 0xC, 109)},
|
||||
|
||||
};
|
||||
|
||||
@@ -82,35 +82,35 @@ public class TestRegisterBuilder {
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as BRegister List (Internal Relay)")]
|
||||
[Fact(DisplayName = "Parsing as Bool Register List (Internal Relay)")]
|
||||
public void TestParsingBRegisterR() {
|
||||
|
||||
var tests = new Dictionary<string, IRegister>() {
|
||||
|
||||
{"R0", new BRegister(IOType.R)},
|
||||
{"R1", new BRegister(IOType.R, 0x1)},
|
||||
{"R2", new BRegister(IOType.R, 0x2)},
|
||||
{"R3", new BRegister(IOType.R, 0x3)},
|
||||
{"R4", new BRegister(IOType.R, 0x4)},
|
||||
{"R5", new BRegister(IOType.R, 0x5)},
|
||||
{"R6", new BRegister(IOType.R, 0x6)},
|
||||
{"R7", new BRegister(IOType.R, 0x7)},
|
||||
{"R8", new BRegister(IOType.R, 0x8)},
|
||||
{"R9", new BRegister(IOType.R, 0x9)},
|
||||
{"R0", new BoolRegister(IOType.R)},
|
||||
{"R1", new BoolRegister(IOType.R, 0x1)},
|
||||
{"R2", new BoolRegister(IOType.R, 0x2)},
|
||||
{"R3", new BoolRegister(IOType.R, 0x3)},
|
||||
{"R4", new BoolRegister(IOType.R, 0x4)},
|
||||
{"R5", new BoolRegister(IOType.R, 0x5)},
|
||||
{"R6", new BoolRegister(IOType.R, 0x6)},
|
||||
{"R7", new BoolRegister(IOType.R, 0x7)},
|
||||
{"R8", new BoolRegister(IOType.R, 0x8)},
|
||||
{"R9", new BoolRegister(IOType.R, 0x9)},
|
||||
|
||||
{"RA", new BRegister(IOType.R, 0xA)},
|
||||
{"RB", new BRegister(IOType.R, 0xB)},
|
||||
{"RC", new BRegister(IOType.R, 0xC)},
|
||||
{"RD", new BRegister(IOType.R, 0xD)},
|
||||
{"RE", new BRegister(IOType.R, 0xE)},
|
||||
{"RF", new BRegister(IOType.R, 0xF)},
|
||||
{"RA", new BoolRegister(IOType.R, 0xA)},
|
||||
{"RB", new BoolRegister(IOType.R, 0xB)},
|
||||
{"RC", new BoolRegister(IOType.R, 0xC)},
|
||||
{"RD", new BoolRegister(IOType.R, 0xD)},
|
||||
{"RE", new BoolRegister(IOType.R, 0xE)},
|
||||
{"RF", new BoolRegister(IOType.R, 0xF)},
|
||||
|
||||
{"R1A", new BRegister(IOType.R, 0xA, 1)},
|
||||
{"R10B", new BRegister(IOType.R, 0xB, 10)},
|
||||
{"R109C", new BRegister(IOType.R, 0xC, 109)},
|
||||
{"R1000", new BRegister(IOType.R, 0x0, 100)},
|
||||
{"R511", new BRegister(IOType.R, 0x0, 511)},
|
||||
{"R511A", new BRegister(IOType.R, 0xA, 511)},
|
||||
{"R1A", new BoolRegister(IOType.R, 0xA, 1)},
|
||||
{"R10B", new BoolRegister(IOType.R, 0xB, 10)},
|
||||
{"R109C", new BoolRegister(IOType.R, 0xC, 109)},
|
||||
{"R1000", new BoolRegister(IOType.R, 0x0, 100)},
|
||||
{"R511", new BoolRegister(IOType.R, 0x0, 511)},
|
||||
{"R511A", new BoolRegister(IOType.R, 0xA, 511)},
|
||||
|
||||
};
|
||||
|
||||
@@ -126,7 +126,7 @@ public class TestRegisterBuilder {
|
||||
|
||||
output.WriteLine($"Expected: {item.Key}");
|
||||
|
||||
var built = RegBuilder.FromPlcRegName(item.Key).AsPlcType(PlcVarType.BOOL).Build();
|
||||
var built = RegBuilder.Factory.FromPlcRegName(item.Key).AsPlcType(PlcVarType.BOOL).Build();
|
||||
|
||||
output.WriteLine($"{(built?.ToString(true) ?? "null")}\n");
|
||||
Assert.Equivalent(item.Value, built);
|
||||
@@ -141,63 +141,75 @@ public class TestRegisterBuilder {
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as BRegister (Casted)")]
|
||||
[Fact(DisplayName = "Parsing as Bool Register (Casted)")]
|
||||
public void TestRegisterBuildingBoolCasted () {
|
||||
|
||||
var expect = new BRegister(IOType.R, 0x1, 0);
|
||||
var expect2 = new BRegister(IOType.Y, 0xA, 103);
|
||||
var expect = new BoolRegister(IOType.R, 0x1, 0);
|
||||
var expect2 = new BoolRegister(IOType.Y, 0xA, 103);
|
||||
|
||||
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").AsPlcType(PlcVarType.BOOL).Build());
|
||||
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").AsType<bool>().Build());
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").AsPlcType(PlcVarType.BOOL).Build());
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").AsType<bool>().Build());
|
||||
|
||||
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").AsPlcType(PlcVarType.BOOL).Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").AsType<bool>().Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").AsPlcType(PlcVarType.BOOL).Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").AsType<bool>().Build());
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as BRegister (Auto)")]
|
||||
[Fact(DisplayName = "Parsing as Bool Register (Auto)")]
|
||||
public void TestRegisterBuildingBoolAuto () {
|
||||
|
||||
var expect = new BRegister(IOType.R, 0x1, 0);
|
||||
var expect2 = new BRegister(IOType.Y, 0xA, 103);
|
||||
var expect = new BoolRegister(IOType.R, 0x1, 0);
|
||||
var expect2 = new BoolRegister(IOType.Y, 0xA, 103);
|
||||
|
||||
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").Build());
|
||||
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").Build());
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").Build());
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("R1").Build());
|
||||
|
||||
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("Y103A").Build());
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as NRegister (Casted)")]
|
||||
[Fact(DisplayName = "Parsing as Number Register (Casted)")]
|
||||
public void TestRegisterBuildingNumericCasted() {
|
||||
|
||||
var expect = new NRegister<short>(303, null);
|
||||
var expect2 = new NRegister<int>(10002, null);
|
||||
var expect3 = new NRegister<TimeSpan>(400, null);
|
||||
var expect = new NumberRegister<short>(303, null);
|
||||
var expect2 = new NumberRegister<int>(10002, null);
|
||||
var expect3 = new NumberRegister<TimeSpan>(400, null);
|
||||
//var expect4 = new NRegister<TimeSpan>(103, null, true);
|
||||
|
||||
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build());
|
||||
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("DT303").AsType<short>().Build());
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build());
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsType<short>().Build());
|
||||
|
||||
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("DDT10002").AsPlcType(PlcVarType.DINT).Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("DDT10002").AsType<int>().Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").AsPlcType(PlcVarType.DINT).Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").AsType<int>().Build());
|
||||
|
||||
Assert.Equivalent(expect3, RegBuilder.FromPlcRegName("DDT400").AsPlcType(PlcVarType.TIME).Build());
|
||||
Assert.Equivalent(expect3, RegBuilder.FromPlcRegName("DDT400").AsType<TimeSpan>().Build());
|
||||
Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT400").AsPlcType(PlcVarType.TIME).Build());
|
||||
Assert.Equivalent(expect3, RegBuilder.Factory.FromPlcRegName("DDT400").AsType<TimeSpan>().Build());
|
||||
|
||||
//Assert.Equivalent(expect4, RegBuilder.FromPlcRegName("DT103").AsType<BitArray>().Build());
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as NRegister (Auto)")]
|
||||
[Fact(DisplayName = "Parsing as Number Register (Auto)")]
|
||||
public void TestRegisterBuildingNumericAuto() {
|
||||
|
||||
var expect = new NRegister<short>(303, null);
|
||||
var expect2 = new NRegister<int>(10002, null);
|
||||
var expect = new NumberRegister<short>(303, null);
|
||||
var expect2 = new NumberRegister<int>(10002, null);
|
||||
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.Factory.FromPlcRegName("DDT10002").Build());
|
||||
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Parsing as Bytes Register (Casted)")]
|
||||
public void TestRegisterBuildingByteRangeCasted() {
|
||||
|
||||
var expect = new BytesRegister<byte[]>(303, 5);
|
||||
|
||||
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsPlcType(PlcVarType.INT).Build());
|
||||
Assert.Equivalent(expect, RegBuilder.Factory.FromPlcRegName("DT303").AsType<short>().Build());
|
||||
|
||||
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("DT303").Build());
|
||||
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("DDT10002").Build());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ namespace MewtocolTests {
|
||||
public void NumericRegisterMewtocolIdentifiers() {
|
||||
|
||||
List<IRegister> registers = new List<IRegister> {
|
||||
new NRegister<short>(50, _name: null),
|
||||
new NRegister<ushort>(50, _name: null),
|
||||
new NRegister<int>(50, _name : null),
|
||||
new NRegister<uint>(50, _name : null),
|
||||
new NRegister<float>(50, _name : null),
|
||||
new NRegister<TimeSpan>(50, _name : null),
|
||||
new NumberRegister<short>(50, _name: null),
|
||||
new NumberRegister<ushort>(50, _name: null),
|
||||
new NumberRegister<int>(50, _name : null),
|
||||
new NumberRegister<uint>(50, _name : null),
|
||||
new NumberRegister<float>(50, _name : null),
|
||||
new NumberRegister<TimeSpan>(50, _name : null),
|
||||
};
|
||||
|
||||
List<string> expectedIdents = new List<string> {
|
||||
@@ -51,23 +51,23 @@ namespace MewtocolTests {
|
||||
|
||||
List<IRegister> registers = new List<IRegister> {
|
||||
//numeric ones
|
||||
new NRegister<short>(50, _name: null),
|
||||
new NRegister<ushort>(60, _name : null),
|
||||
new NRegister<int>(70, _name : null),
|
||||
new NRegister<uint>(80, _name : null),
|
||||
new NRegister<float>(90, _name : null),
|
||||
new NRegister<TimeSpan>(100, _name : null),
|
||||
new NumberRegister<short>(50, _name: null),
|
||||
new NumberRegister<ushort>(60, _name : null),
|
||||
new NumberRegister<int>(70, _name : null),
|
||||
new NumberRegister<uint>(80, _name : null),
|
||||
new NumberRegister<float>(90, _name : null),
|
||||
new NumberRegister<TimeSpan>(100, _name : null),
|
||||
|
||||
//boolean
|
||||
new BRegister(IOType.R, 0, 100),
|
||||
new BRegister(IOType.R, 0, 0),
|
||||
new BRegister(IOType.X, 5),
|
||||
new BRegister(IOType.X, 0xA),
|
||||
new BRegister(IOType.X, 0xF, 109),
|
||||
new BRegister(IOType.Y, 0xC, 75),
|
||||
new BoolRegister(IOType.R, 0, 100),
|
||||
new BoolRegister(IOType.R, 0, 0),
|
||||
new BoolRegister(IOType.X, 5),
|
||||
new BoolRegister(IOType.X, 0xA),
|
||||
new BoolRegister(IOType.X, 0xF, 109),
|
||||
new BoolRegister(IOType.Y, 0xC, 75),
|
||||
|
||||
//string
|
||||
new SRegister(999, 5),
|
||||
new BytesRegister<string>(999, 5),
|
||||
};
|
||||
|
||||
List<string> expcectedIdents = new List<string> {
|
||||
@@ -110,7 +110,7 @@ namespace MewtocolTests {
|
||||
|
||||
var ex = Assert.Throws<NotSupportedException>(() => {
|
||||
|
||||
new NRegister<short>(100000, _name: null);
|
||||
new NumberRegister<short>(100000, _name: null);
|
||||
|
||||
});
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace MewtocolTests {
|
||||
|
||||
var ex1 = Assert.Throws<NotSupportedException>(() => {
|
||||
|
||||
new BRegister(IOType.R, _areaAdress: 512);
|
||||
new BoolRegister(IOType.R, _areaAdress: 512);
|
||||
|
||||
});
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace MewtocolTests {
|
||||
|
||||
var ex2 = Assert.Throws<NotSupportedException>(() => {
|
||||
|
||||
new BRegister(IOType.X, _areaAdress: 110);
|
||||
new BoolRegister(IOType.X, _areaAdress: 110);
|
||||
|
||||
});
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace MewtocolTests {
|
||||
|
||||
var ex3 = Assert.Throws<NotSupportedException>(() => {
|
||||
|
||||
new SRegister(100000, 5);
|
||||
new BytesRegister<string>(100000, 5);
|
||||
|
||||
});
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace MewtocolTests {
|
||||
|
||||
var ex = Assert.Throws<NotSupportedException>(() => {
|
||||
|
||||
new NRegister<double>(100, _name: null);
|
||||
new NumberRegister<double>(100, _name: null);
|
||||
|
||||
});
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -2,10 +2,20 @@
|
||||
<ProjectConfiguration CompactMode="1">
|
||||
<PaneContents>
|
||||
<Pane-1 PaneBaseID="410" PaneInstanceNumber="1">
|
||||
<Contents BinaryData="B21A0000789CE599516F9B3010C7FB39F684E843DF164C429B484D251A688794940A58BA699A22065E6A0D6C844DD3EC43ED33CE86A66D68B384AA04A4BD4418B8BB3FF7E3EC33F9F3E1E0E07442306224BD4138240B6944E2C40FD884847028035972835B18FB43B9D7E5C7B7646105044F115C0C65453E3B1D91288B315D1D481683B18543782F2E4B166630C57E74E5C7DC991542CCD04F045359D22334C7311F73C760D0EB2A3DEE9DF90C05372864B73CB2C6EDF58C91E7E34FD00F61FACC96EB33EF139F070C45C0CE6B3A4059C7D48F32B8260128BDBE76D22F4950C1604D423E7E9304B52CE1E25A0FC31452BA838CFE7A22FA6FCD43B72C829316D63BC05095750DF9F84D227A2F4444BEC8C21451F42382F95BB3FDDD28C9019BE4D851F8E0F829FC56895A59A2B74C60830A3B8F355638C8FD16E75CF41BE1792E292F4DFE10D4C29729C912FA60EDC080A4A194A779283BE6C8760C591A93E05711403243C4FCFCC1B89CEB14DE3DD6B670B732B43C73224B1EBCE7B23D48D9392111288C5DB6CCADD5BE88B7D1C8F96CBEB85F72633F65793DBA308201430417934E3E1296361636E264C9392F21C3704CD75D4570789295B5102A38392E9B8DECC9C4BCF2B6DF37D69F5C4F75677639B6CFF5F1563BEFEBB5B9323BB7EDD70D3A0595DAE8185912A1C067B00A21A55E3C86E1B50E90616D08543720513E164E32F6C5A882E8421FBB3557514950D3841A2B21BE0480E336958FD71F0C5A85A6A9DAE164BA6A9BC818DE89A28056B1696C62BB88885F15CFC7BD00EAB60A90636E30A81B90CB52DE325602747454F7D4C6F168ADC2E37A8E7575F94DFBDE4C77809803E78832B153DE9D13383E54CA1D562DB040BBBAB89B3CEB7BE6243661330E6A81289CF1D656157BC0D6B17A21EBBF64256ACA4371A5DD9077A8D03D2C4B406D151ECF9A988DE0317116B7ABA7D3DA35C935D3D151C1A652B3B097BA012DFB8E5063D94806A249E42FC54748716D73E6E7B3C568194428E0AB920B196F1EAA6D60D57C459A153FE0E1A8EE054A6B595F5EE3FA245954340D16A63065302CFC6C42BA0BB6B2CA7742F3BE3476CAFDBFF2DD59FB7BEAEC2F8D4435CD">
|
||||
<Contents BinaryData="B31A0000789CE599D16EDA301486FB1CBB8AD28BDE8D38900252A94449DA4582A64A22BA699A509678D45A6247B133CA5E6AAF383B296D49CB80AA2196768362E03FE7E77C9CF818FE7C383A3A9B108C18C96E118EC8421991240D423621111CA84055BCF00E26C140EDB4F9F51D59D821C1530417035553CFCF4624CE134C57178ACD6062E308DE8B97151B3398E120BE0E121ECC8E2066E8078299AA0C6334C7095FF3C0A0DF696B1D1E9D050C85B72862773CB3C1F5C39C91E7EB4F308860F64CCBFD59F769C013462261EB351FA0EA631AC4395CB300B44ECFE8F62A1674D05FB350ACDF6441AF5AB8BC1946510629DDC1466FBD10BDB7D6A15D35C1490BF50E30746DDD43B17E9389CE0B137120AA3045147D8F61F1ADD9FEDDA8D8019BEC3871F410F829FD568B46D5A2BF4C61830E5B8F3D560628E296CF79E837C2F3C252D19AFC43501B5F65244FE983DA8521C922A528F34075AD91E39AAA3226E1CF328162458805C507E3766E32F8EBB1B745B895D0F6AD89AAF8F09EDBF621651784C4A0147B6C59A8F59EC8B74974391C7BD60B81E22541C68A86F4600C4386082EEF3AC54A481D2C34E2C94A74DE43A6E95A9EB74AE1F22A6B6B2974D03DADCA46CE64625DFBDBDF371E3E859E0EDDD9D5D8B9188EB7EAFC2F37D64A76E138AF0B5A2596DAF098791AA33060701F445ABD784CD3970E90696F48543720D13F364E73F6D994AC8B2A869A26D4580BF13D009CCAD43E7EAFDF970A4D53BDC3C9B47599C8987E57D380546C1ABBB15DC624D817CFC783006A4B05C8B53608EA06E4B18CCF8C7B013A39A9FBD6C6F11852E1F17CD7BEBEFA6A7C6B663A40CC85734499382AEFCE099C1E6BD509AB165840AE29EEB6A8FA81398953D88C835A200A677CB4D5C521503A562F6CFD97AC444FF928D9EB34E41F6BF400DB12D0A5C2E3DB13AB113C16CE13B9663A43AE9B5C33131D156CF61A160ED23740B2DF116A6C1BC544348D83A5F81552BCB6B9F2F3D962B40C6314F25DC9838C0F0FFB1D60F562479A950FE0E1AAEE0DCA906C2EAF717F526C2A86061B5398311895713621DD055BD5E53BA1795F1A3BD5FE5FF56EADFD3F75FE17D00A35F9">
|
||||
<FilterHistory/>
|
||||
</Contents>
|
||||
</Pane-1>
|
||||
<Summary PaneCount="1"/>
|
||||
<Pane-2 PaneBaseID="410" PaneInstanceNumber="4">
|
||||
<Contents BinaryData="480F0000789CED97ED6EDA3014867B07BB052B3740BEF8A8542AA524ED224183928C6ABF901B7B602DB1516206F4EA6727D036860E8658C70FFEE504BFEF797D9E9880FBE5EAEA66C028E12C7F2214B105E8B16C06133E6008773543035132C519EC6AB625AEA76CE1278C8E085E74355DBBBDE9B1749ED16273017C8E339F22BC941F039F729C53983EC24C98F908534E7E109C6BC049C98466A216C6C6B56DE9B670E79093E489203E159D9B42EFCC397B5F7FC510E1FC9D56E4F39633281A22D9B0B12B87A1E618C1748E6B110CDDEE34DB1D2582695CD72294F551114C35C2FDD04128C74571408C4E7D109D63E760A9210469A93E0086A9D73394F55121ECAD102994531891823CA7B87C6AF63F1B4A1CE3A338418AD6C66FEDF7466CAA11E3D50CFFC7848DD733561994BED5BD88BC103A29239547536CA2F0E943CEE6B362AD0E71C27204CA3177B5D0EB05A1AB813E4B7E560D80870887E5C6449C618E7FBD9E6D69B711FAB137D0408C9722F664FC5C6D7633A610A770655456115F955E664776FFC8220EBF795BEB4194C19C97A733C2294E3861B4FA0A2A2BA90CA8D4C89B8AB93850AE1B7A51B4E910EA357FD368B7544D2F180CBCC778FFBABEF3E63B72C2F1433FB873FA7B75F1F7A1B791DD05C16E41A302F489A0CC3303655C40ED06659D1BA8FB0BA9DDA4EC7323A51BCE85D5644CD6AC945DEE01D4DE7E959D148F1B5BCDB3A2A3CEE773E0A0351DF72FF15856EB1FF311805A6705686B44A72304FC42FE9EF46981738E51E52317ED0270082435E589D09C96C641B3FFD3BC1BB53FCDB7BF01DF64C269">
|
||||
<FilterHistory/>
|
||||
</Contents>
|
||||
</Pane-2>
|
||||
<Pane-3 PaneBaseID="410" PaneInstanceNumber="5">
|
||||
<Contents BinaryData="0A070000789CC595D16E9B3014867BB5BD86C50B2410B252A9A94413D6218512014BB5ABCAC5678935B023E32C499F7EB6296DA1E912659576C731FEFFF3F91C1FF8F4F9ECEC32E28C4A2EEE28237C83C6BC5CE15C469CC0C8B22D94E64B28F1C87207EA79C93761CED99CC26664F5ADABCB312FD625AB9A07144A28434660AB5FA39049100C17B7B85466210126E94F0AC2427E4117AC54B132B62FDC41DF55EE124B9ADF5122972AF350E9FDB5E4AFE36F800988575AC5176C575825243A616F1F87DDE598E3620D2D04BBEF7AC373AF83E0D8172D04139F84E07411BECE7C420454D511185EBB10DEA97518742154A7B5FA886638FD3683894F8270DF40145857614E2BFA5080B93587EF4607C77E0F272EC893F14BFA8388C32E62B65BC17F24EC3DCF586D607CEBB5943E52B630486634D521AA90DD08BE5E554FEA04722E0832651E5949308E938985A63CFF55274001A1129B83299C9980DFCFB3ADED1A619805918532D82AECC5FD437DD8A64C091478E7D456A9DC192FC7D3D9DFB3C892EFC19BFD282DB190663A5328209794B3FA136422AD8C99D6E8C58EB91AA8C92409D2B4C990D82D7FC73EFFD2D58CE3280A6EB3C3FBA6FE8BEFDC4FEE6FA6F1B53F3DA8CB7ECC8246761DC7FB05BDBA41FFD0281456FA4684AC022181D43E7AD3BEF21FD3A22EE507F5E563BB7154EDFF56EF5EEBB777F507221839D5">
|
||||
<FilterHistory/>
|
||||
</Contents>
|
||||
</Pane-3>
|
||||
<Summary PaneCount="3"/>
|
||||
</PaneContents>
|
||||
</ProjectConfiguration>
|
||||
|
||||
Reference in New Issue
Block a user