mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Added register builder for booleans and numerics
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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());
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MewtocolNet.PLCEnums;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -239,14 +239,23 @@ 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) {
|
||||
internal static bool IsBoolean (this RegisterType type) {
|
||||
|
||||
return type == RegisterType.X || type == RegisterType.Y || type == RegisterType.R;
|
||||
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MewtocolNet.Logging;
|
||||
using MewtocolNet.PLCEnums;
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MewtocolNet.PLCEnums {
|
||||
namespace MewtocolNet {
|
||||
|
||||
/// <summary>
|
||||
/// CPU type of the PLC
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MewtocolNet.PLCEnums {
|
||||
namespace MewtocolNet {
|
||||
|
||||
/// <summary>
|
||||
/// CPU type of the PLC
|
||||
|
||||
18
MewtocolNet/PLCEnums/PlcVarType.cs
Normal file
18
MewtocolNet/PLCEnums/PlcVarType.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Text;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
public enum PlcVarType {
|
||||
|
||||
BOOL,
|
||||
INT,
|
||||
UINT,
|
||||
DINT,
|
||||
UDINT,
|
||||
REAL,
|
||||
TIME,
|
||||
STRING
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
91
MewtocolNet/PLCEnums/PlcVarTypeConversions.cs
Normal file
91
MewtocolNet/PLCEnums/PlcVarTypeConversions.cs
Normal 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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
97
MewtocolNet/RegisterBuilding/FinalizerExtensions.cs
Normal file
97
MewtocolNet/RegisterBuilding/FinalizerExtensions.cs
Normal 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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
12
MewtocolNet/RegisterBuilding/ParseResult.cs
Normal file
12
MewtocolNet/RegisterBuilding/ParseResult.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace MewtocolNet.RegisterBuilding {
|
||||
internal struct ParseResult {
|
||||
|
||||
public ParseResultState state;
|
||||
|
||||
public string hardFailReason;
|
||||
|
||||
public RegisterBuilderStep stepData;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
19
MewtocolNet/RegisterBuilding/ParseResultState.cs
Normal file
19
MewtocolNet/RegisterBuilding/ParseResultState.cs
Normal 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,
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
184
MewtocolNet/RegisterBuilding/RegBuilder.cs
Normal file
184
MewtocolNet/RegisterBuilding/RegBuilder.cs
Normal 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),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
92
MewtocolNet/RegisterBuilding/RegisterBuilderStep.cs
Normal file
92
MewtocolNet/RegisterBuilding/RegisterBuilderStep.cs
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace MewtocolNet.Registers {
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public RegisterType RegisterType { get; private set; }
|
||||
|
||||
internal Type collectionType;
|
||||
|
||||
/// <summary>
|
||||
@@ -63,7 +65,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 NRegister (int _adress, string _name = null) {
|
||||
|
||||
if (_adress > 99999)
|
||||
throw new NotSupportedException("Memory adresses cant be greater than 99999");
|
||||
@@ -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()}";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace MewtocolNet.Registers {
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public RegisterType RegisterType { get; private set; }
|
||||
|
||||
internal Type collectionType;
|
||||
|
||||
/// <summary>
|
||||
@@ -69,10 +71,12 @@ namespace MewtocolNet.Registers {
|
||||
wordsize++;
|
||||
}
|
||||
|
||||
RegisterType = RegisterType.DT_START;
|
||||
|
||||
memoryLength = (int)Math.Round(wordsize + 1);
|
||||
}
|
||||
|
||||
internal SRegister WithCollectionType(Type colType) {
|
||||
internal SRegister WithCollectionType (Type colType) {
|
||||
|
||||
collectionType = colType;
|
||||
return this;
|
||||
@@ -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()}";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.Logging;
|
||||
using MewtocolNet.PLCEnums;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
|
||||
204
MewtocolTests/TestRegisterBuilder.cs
Normal file
204
MewtocolTests/TestRegisterBuilder.cs
Normal 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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)},
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user