Added register builder for booleans and numerics

This commit is contained in:
Felix Weiß
2023-06-25 22:45:07 +02:00
parent f5f1e3bea9
commit b48f86d23d
23 changed files with 821 additions and 132 deletions

View File

@@ -4,6 +4,8 @@ using System;
using System.Reflection;
using System.Threading.Tasks;
using System.Collections;
using MewtocolNet.RegisterBuilding;
using System.Collections.Generic;
namespace Examples;

View File

@@ -1,4 +1,6 @@
using System;
using MewtocolNet.RegisterBuilding;
using MewtocolNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -12,6 +14,9 @@ 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());
};

View File

@@ -1,5 +1,4 @@
using MewtocolNet.PLCEnums;
using System;
using System;
namespace MewtocolNet {

View File

@@ -12,6 +12,11 @@ namespace MewtocolNet {
/// </summary>
event Action<object> ValueChanged;
/// <summary>
/// Type of the underlying register
/// </summary>
RegisterType RegisterType { get; }
/// <summary>
/// The name of the register
/// </summary>
@@ -27,6 +32,12 @@ namespace MewtocolNet {
/// </summary>
int MemoryAddress { get; }
/// <summary>
/// Gets the special address of the register or -1 if it has none
/// </summary>
/// <returns></returns>
byte? GetSpecialAddress();
/// <summary>
/// Indicates if the register is processed bitwise
/// </summary>
@@ -93,6 +104,11 @@ namespace MewtocolNet {
/// </summary>
string ToString();
/// <summary>
/// Builds a readable string with all important register informations and additional infos
/// </summary>
string ToString(bool detailed);
}
}

View File

@@ -239,7 +239,7 @@ namespace MewtocolNet {
}
/// <summary>
/// Checks if the register type is non numeric
/// Checks if the register type is boolean
/// </summary>
internal static bool IsBoolean (this RegisterType type) {
@@ -247,6 +247,15 @@ namespace MewtocolNet {
}
/// <summary>
/// Checks if the register type numeric
/// </summary>
internal static bool IsNumericDTDDT (this RegisterType type) {
return type == RegisterType.DT || type == RegisterType.DDT;
}
/// <summary>
/// Checks if the register type is an physical in or output of the plc
/// </summary>

View File

@@ -1,5 +1,4 @@
using MewtocolNet.Logging;
using MewtocolNet.PLCEnums;
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;

View File

@@ -1,4 +1,4 @@
namespace MewtocolNet.PLCEnums {
namespace MewtocolNet {
/// <summary>
/// CPU type of the PLC

View File

@@ -1,4 +1,4 @@
namespace MewtocolNet.PLCEnums {
namespace MewtocolNet {
/// <summary>
/// CPU type of the PLC

View File

@@ -0,0 +1,18 @@
using System.Text;
namespace MewtocolNet {
public enum PlcVarType {
BOOL,
INT,
UINT,
DINT,
UDINT,
REAL,
TIME,
STRING
}
}

View File

@@ -0,0 +1,91 @@
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");
}
}
}

View File

@@ -0,0 +1,97 @@
using MewtocolNet.Registers;
using System;
using System.Reflection;
namespace MewtocolNet.RegisterBuilding {
public static class FinalizerExtensions {
public static IRegister Build (this RegisterBuilderStep step) {
//if no casting method in builder was called => autocast the type from the RegisterType
if (!step.wasCasted)
step.AutoType();
bool isBoolean = step.RegType.IsBoolean();
bool isTypeNotDefined = step.plcVarType == null && step.dotnetVarType == null;
//fallbacks if no casting builder was given
if (isTypeNotDefined && step.RegType == RegisterType.DT) {
step.dotnetVarType = typeof(short);
}
if (isTypeNotDefined && step.RegType == RegisterType.DDT) {
step.dotnetVarType = typeof(int);
} else if (isTypeNotDefined && isBoolean) {
step.dotnetVarType = typeof(bool);
} else if (isTypeNotDefined && step.RegType == RegisterType.DT_START) {
step.dotnetVarType = typeof(string);
}
if(step.plcVarType != null) {
step.dotnetVarType = step.plcVarType.Value.ToDotnetType();
}
//as numeric register
if (step.RegType.IsNumericDTDDT()) {
if(step.plcVarType == null && step.dotnetVarType != null) {
step.plcVarType = step.dotnetVarType.ToPlcVarType();
}
var type = step.plcVarType.Value.ToRegisterType();
var areaAddr = step.MemAddress;
var name = step.Name;
//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");
}
}
}

View File

@@ -0,0 +1,12 @@
namespace MewtocolNet.RegisterBuilding {
internal struct ParseResult {
public ParseResultState state;
public string hardFailReason;
public RegisterBuilderStep stepData;
}
}

View File

@@ -0,0 +1,19 @@
namespace MewtocolNet.RegisterBuilding {
internal enum ParseResultState {
/// <summary>
/// The parse try failed at the intial regex match
/// </summary>
FailedSoft,
/// <summary>
/// The parse try failed at the afer- regex match
/// </summary>
FailedHard,
/// <summary>
/// The parse try did work
/// </summary>
Success,
}
}

View File

@@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using System.Text.RegularExpressions;
namespace MewtocolNet.RegisterBuilding {
/// <summary>
/// Contains useful tools for register creation
/// </summary>
public static class RegBuilder {
//methods to test the input string on
private static List<Func<string, ParseResult>> parseMethods = new List<Func<string, ParseResult>>() {
(x) => TryBuildBoolean(x),
(x) => TryBuildNumericBased(x),
};
public static RegisterBuilderStep FromPlcRegName (string plcAddrName, string name = null) {
foreach (var method in parseMethods) {
var res = method.Invoke(plcAddrName);
if(res.state == ParseResultState.Success) {
if (!string.IsNullOrEmpty(name))
res.stepData.Name = name;
res.stepData.OriginalInput = plcAddrName;
return res.stepData;
} else if(res.state == ParseResultState.FailedHard) {
throw new Exception(res.hardFailReason);
}
}
throw new Exception("Wrong input format");
}
//bool registers
private static ParseResult TryBuildBoolean (string plcAddrName) {
//regex to find special register values
var patternBool = new Regex(@"(?<prefix>X|Y|R)(?<area>[0-9]{0,3})(?<special>(?:[0-9]|[A-F]){1})?");
var match = patternBool.Match(plcAddrName);
if (!match.Success)
return new ParseResult {
state = ParseResultState.FailedSoft
};
string prefix = match.Groups["prefix"].Value;
string area = match.Groups["area"].Value;
string special = match.Groups["special"].Value;
IOType regType;
int areaAdd = 0;
byte specialAdd = 0x0;
//try cast the prefix
if(!Enum.TryParse(prefix, out regType)) {
return new ParseResult {
state = ParseResultState.FailedHard,
hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for boolean registers"
};
}
if(!string.IsNullOrEmpty(area) && !int.TryParse(area, out areaAdd) ) {
return new ParseResult {
state = ParseResultState.FailedHard,
hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong"
};
}
//special address not given
if(string.IsNullOrEmpty(special) && !string.IsNullOrEmpty(area)) {
var isAreaInt = int.TryParse(area, NumberStyles.Number, CultureInfo.InvariantCulture, out var areaInt);
if (isAreaInt && areaInt >= 0 && areaInt <= 9) {
//area address is actually meant as special address but 0-9
specialAdd = (byte)areaInt;
areaAdd = 0;
} else if (isAreaInt && areaInt > 9) {
//area adress is meant to be the actual area address
areaAdd = areaInt;
specialAdd = 0;
} else {
return new ParseResult {
state = ParseResultState.FailedHard,
hardFailReason = $"Cannot parse '{plcAddrName}', the special address: '{special}' is wrong 1",
};
}
} else {
//special address parsed as hex num
if (!byte.TryParse(special, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out specialAdd)) {
return new ParseResult {
state = ParseResultState.FailedHard,
hardFailReason = $"Cannot parse '{plcAddrName}', the special address: '{special}' is wrong 2",
};
}
}
return new ParseResult {
state = ParseResultState.Success,
stepData = new RegisterBuilderStep ((RegisterType)(int)regType, areaAdd, specialAdd),
};
}
// one to two word registers
private static ParseResult TryBuildNumericBased (string plcAddrName) {
var patternByte = new Regex(@"(?<prefix>DT|DDT)(?<area>[0-9]{1,5})");
var match = patternByte.Match(plcAddrName);
if (!match.Success)
return new ParseResult {
state = ParseResultState.FailedSoft
};
string prefix = match.Groups["prefix"].Value;
string area = match.Groups["area"].Value;
RegisterType regType;
int areaAdd = 0;
//try cast the prefix
if (!Enum.TryParse(prefix, out regType)) {
return new ParseResult {
state = ParseResultState.FailedHard,
hardFailReason = $"Cannot parse '{plcAddrName}', the prefix is not allowed for numeric registers"
};
}
if (!string.IsNullOrEmpty(area) && !int.TryParse(area, out areaAdd)) {
return new ParseResult {
state = ParseResultState.FailedHard,
hardFailReason = $"Cannot parse '{plcAddrName}', the area address: '{area}' is wrong"
};
}
return new ParseResult {
state = ParseResultState.Success,
stepData = new RegisterBuilderStep(regType, areaAdd),
};
}
}
}

View File

@@ -1,24 +0,0 @@
namespace MewtocolNet.RegisterBuilding {
/// <summary>
/// Contains useful tools for register creation
/// </summary>
public static class RegisterBuilder {
/// <summary>
/// Parses a register from its PLC name
/// </summary>
/// <param name="name">The name, fe. DT100</param>
/// <param name="reg">An <see cref="IRegister"/> or null if </param>
/// <returns>True if successfully parsed</returns>
public static bool TryBuildFromName(string name, out IRegister reg) {
reg = null;
return false;
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
namespace MewtocolNet.RegisterBuilding {
public class RegisterBuilderStep {
internal bool wasCasted = false;
internal string OriginalInput;
internal string Name;
internal RegisterType RegType;
internal int MemAddress;
internal byte? SpecialAddress;
internal PlcVarType? plcVarType;
internal Type dotnetVarType;
public RegisterBuilderStep () => throw new NotSupportedException("Cant make a new instance of RegisterBuilderStep, use the builder pattern");
internal RegisterBuilderStep (RegisterType regType, int memAddr) {
RegType = regType;
MemAddress = memAddr;
}
internal RegisterBuilderStep(RegisterType regType, int memAddr, byte specialAddr) {
RegType = regType;
MemAddress = memAddr;
SpecialAddress = specialAddr;
}
public RegisterBuilderStep AsPlcType (PlcVarType varType) {
dotnetVarType = null;
plcVarType = varType;
wasCasted = true;
return this;
}
public RegisterBuilderStep AsType<T> () {
if(!typeof(T).IsAllowedPlcCastingType()) {
throw new NotSupportedException($"The dotnet type {typeof(T)}, is not supported for PLC type casting");
}
dotnetVarType = typeof(T);
plcVarType = null;
wasCasted = true;
return this;
}
internal RegisterBuilderStep AutoType () {
switch (RegType) {
case RegisterType.X:
case RegisterType.Y:
case RegisterType.R:
dotnetVarType = typeof(bool);
break;
case RegisterType.DT:
dotnetVarType = typeof(short);
break;
case RegisterType.DDT:
dotnetVarType = typeof(int);
break;
case RegisterType.DT_START:
dotnetVarType = typeof(string);
break;
}
plcVarType = null;
wasCasted = true;
return this;
}
}
}

View File

@@ -18,28 +18,23 @@
/// </summary>
R = 2,
/// <summary>
/// Data area as a short (Register)
/// Single word area (Register)
/// </summary>
DT_short = 3,
DT = 3,
/// <summary>
/// Data area as an unsigned short (Register)
/// Double word area (Register)
/// </summary>
DT_ushort = 4,
DDT = 4,
/// <summary>
/// Double data area as an integer (Register)
/// Start area of a byte sequence longer than 2 words
/// </summary>
DDT_int = 5,
/// <summary>
/// Double data area as an unsigned integer (Register)
/// </summary>
DDT_uint = 6,
/// <summary>
/// Double data area as an floating point number (Register)
/// </summary>
DDT_float = 7,
DT_START = 5,
}
// this is just used as syntactic sugar,
// when creating registers that are R/X/Y typed you dont need the DT types
/// <summary>
/// The type of an input/output register
/// </summary>

View File

@@ -19,7 +19,7 @@ namespace MewtocolNet.Registers {
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
internal RegisterType RegType { get; private set; }
public RegisterType RegisterType { get; private set; }
internal Type collectionType;
@@ -80,7 +80,7 @@ namespace MewtocolNet.Registers {
specialAddress = _spAddress;
name = _name;
RegType = (RegisterType)(int)_io;
RegisterType = (RegisterType)(int)_io;
}
@@ -91,13 +91,15 @@ namespace MewtocolNet.Registers {
}
public byte? GetSpecialAddress() => SpecialAddress;
/// <summary>
/// Builds the register area name
/// </summary>
public string BuildMewtocolQuery() {
//build area code from register type
StringBuilder asciistring = new StringBuilder(RegType.ToString());
StringBuilder asciistring = new StringBuilder(RegisterType.ToString());
string memPadded = MemoryAddress.ToString().PadLeft(4, '0');
string sp = SpecialAddress.ToString("X1");
@@ -127,11 +129,13 @@ namespace MewtocolNet.Registers {
public Type GetCollectionType() => CollectionType;
public RegisterType GetRegisterType() => RegisterType;
public string GetValueString() => Value.ToString();
public void ClearValue() => SetValueFromPLC(false);
public string GetRegisterString() => RegType.ToString();
public string GetRegisterString() => RegisterType.ToString();
public string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}";
@@ -163,6 +167,22 @@ namespace MewtocolNet.Registers {
public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
public string ToString(bool additional) {
if (!additional) return this.ToString();
StringBuilder sb = new StringBuilder();
sb.AppendLine($"PLC Naming: {GetRegisterPLCName()}");
sb.AppendLine($"Name: {Name ?? "Not named"}");
sb.AppendLine($"Value: {GetValueString()}");
sb.AppendLine($"Register Type: {RegisterType}");
sb.AppendLine($"Memory Address: {MemoryAddress}");
sb.AppendLine($"Special Address: {SpecialAddress:X1}");
return sb.ToString();
}
}
}

View File

@@ -22,6 +22,8 @@ namespace MewtocolNet.Registers {
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public RegisterType RegisterType { get; private set; }
internal Type collectionType;
/// <summary>
@@ -87,9 +89,16 @@ namespace MewtocolNet.Registers {
throw new NotSupportedException($"The type {numType} is not allowed for Number Registers");
}
//set register type
if(memoryLength == 1) {
RegisterType = RegisterType.DDT;
} else {
RegisterType = RegisterType.DT;
}
internal NRegister(int _adress, string _name = null, bool isBitwise = false, Type _enumType = null) {
}
public NRegister (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;
@@ -111,6 +120,13 @@ namespace MewtocolNet.Registers {
throw new NotSupportedException($"The type {numType} is not allowed for Number Registers");
}
//set register type
if (memoryLength == 1) {
RegisterType = RegisterType.DDT;
} else {
RegisterType = RegisterType.DT;
}
isUsedBitwise = isBitwise;
enumType = _enumType;
@@ -131,6 +147,8 @@ namespace MewtocolNet.Registers {
}
public byte? GetSpecialAddress() => null;
public string GetStartingMemoryArea() => MemoryAddress.ToString();
public Type GetCollectionType() => CollectionType;
@@ -267,8 +285,12 @@ namespace MewtocolNet.Registers {
public void TriggerNotifyChange() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
public RegisterType GetRegisterType() => RegisterType;
public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
public string ToString(bool additional) => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
}
}

View File

@@ -19,6 +19,8 @@ namespace MewtocolNet.Registers {
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public RegisterType RegisterType { get; private set; }
internal Type collectionType;
/// <summary>
@@ -69,6 +71,8 @@ namespace MewtocolNet.Registers {
wordsize++;
}
RegisterType = RegisterType.DT_START;
memoryLength = (int)Math.Round(wordsize + 1);
}
@@ -105,6 +109,8 @@ namespace MewtocolNet.Registers {
return asciistring.ToString();
}
public byte? GetSpecialAddress() => null;
public Type GetCollectionType() => CollectionType;
public bool IsUsedBitwise() => false;
@@ -138,6 +144,8 @@ namespace MewtocolNet.Registers {
public override string ToString() => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
public string ToString(bool additional) => $"{GetRegisterPLCName()} - Value: {GetValueString()}";
}
}

View File

@@ -1,6 +1,5 @@
using MewtocolNet;
using MewtocolNet.Logging;
using MewtocolNet.PLCEnums;
using Xunit;
using Xunit.Abstractions;

View File

@@ -0,0 +1,204 @@
using MewtocolNet;
using MewtocolNet.RegisterBuilding;
using MewtocolNet.Registers;
using System.Collections;
using Xunit;
using Xunit.Abstractions;
using static System.Net.Mime.MediaTypeNames;
namespace MewtocolTests;
public class TestRegisterBuilder {
private readonly ITestOutputHelper output;
public TestRegisterBuilder(ITestOutputHelper output) {
this.output = output;
}
[Fact(DisplayName = "Parsing as BRegister 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)},
{"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)},
{"Y1A", new BRegister(IOType.Y, 0xA, 1)},
{"Y10B", new BRegister(IOType.Y, 0xB, 10)},
{"Y109C", new BRegister(IOType.Y, 0xC, 109)},
};
TestBoolDict(tests);
}
[Fact(DisplayName = "Parsing as BRegister 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)},
{"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)},
{"X1A", new BRegister(IOType.X, 0xA, 1)},
{"X10B", new BRegister(IOType.X, 0xB, 10)},
{"X109C", new BRegister(IOType.X, 0xC, 109)},
};
TestBoolDict(tests);
}
[Fact(DisplayName = "Parsing as BRegister 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)},
{"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)},
{"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)},
};
TestBoolDict(tests);
}
private void TestBoolDict (Dictionary<string, IRegister> dict) {
foreach (var item in dict) {
try {
output.WriteLine($"Expected: {item.Key}");
var built = RegBuilder.FromPlcRegName(item.Key).AsPlcType(PlcVarType.BOOL).Build();
output.WriteLine($"{(built?.ToString(true) ?? "null")}\n");
Assert.Equivalent(item.Value, built);
} catch (Exception ex) {
output.WriteLine(ex.Message.ToString());
}
}
}
[Fact(DisplayName = "Parsing as BRegister (Casted)")]
public void TestRegisterBuildingBoolCasted () {
var expect = new BRegister(IOType.R, 0x1, 0);
var expect2 = new BRegister(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(expect2, RegBuilder.FromPlcRegName("Y103A").AsPlcType(PlcVarType.BOOL).Build());
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").AsType<bool>().Build());
}
[Fact(DisplayName = "Parsing as BRegister (Auto)")]
public void TestRegisterBuildingBoolAuto () {
var expect = new BRegister(IOType.R, 0x1, 0);
var expect2 = new BRegister(IOType.Y, 0xA, 103);
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").Build());
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("R1").Build());
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").Build());
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("Y103A").Build());
}
[Fact(DisplayName = "Parsing as NRegister (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 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(expect2, RegBuilder.FromPlcRegName("DDT10002").AsPlcType(PlcVarType.DINT).Build());
Assert.Equivalent(expect2, RegBuilder.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(expect4, RegBuilder.FromPlcRegName("DT103").AsType<BitArray>().Build());
}
[Fact(DisplayName = "Parsing as NRegister (Auto)")]
public void TestRegisterBuildingNumericAuto() {
var expect = new NRegister<short>(303, null);
var expect2 = new NRegister<int>(10002, null);
Assert.Equivalent(expect, RegBuilder.FromPlcRegName("DT303").Build());
Assert.Equivalent(expect2, RegBuilder.FromPlcRegName("DDT10002").Build());
}
}

View File

@@ -1,78 +0,0 @@
using MewtocolNet;
using MewtocolNet.Registers;
using Xunit;
using Xunit.Abstractions;
namespace MewtocolTests;
public class TestRegisterParsing {
private readonly ITestOutputHelper output;
public TestRegisterParsing(ITestOutputHelper output) {
this.output = output;
}
[Fact(DisplayName = "Parsing as BRegister (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)},
{"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)},
{"Y1A", new BRegister(IOType.Y, 0xA, 1)},
{"Y10B", new BRegister(IOType.Y, 0xB, 10)},
{"Y109C", new BRegister(IOType.Y, 0xC, 109)},
};
}
[Fact(DisplayName = "Parsing as BRegister (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)},
{"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)},
{"X1A", new BRegister(IOType.X, 0xA, 1)},
{"X10B", new BRegister(IOType.X, 0xB, 10)},
{"X109C", new BRegister(IOType.X, 0xC, 109)},
};
}
}