Add underlying byte data for registers

- change backend logic for register r/w
- remade interface builder pattern for better syntactic sugar
- refined tests
This commit is contained in:
Felix Weiß
2023-07-12 00:32:56 +02:00
parent fbd53c850f
commit 6c7e91f648
32 changed files with 1172 additions and 463 deletions

View File

@@ -25,7 +25,7 @@ internal class OnlineCommand : CommandLineExcecuteable {
string ip = split[0]; string ip = split[0];
int port = int.Parse(split[1]); int port = int.Parse(split[1]);
using (var plc = Mewtocol.Ethernet(ip, port)) { using (var plc = Mewtocol.Ethernet(ip, port).Build()) {
await AfterSetup(plc); await AfterSetup(plc);

View File

@@ -45,7 +45,7 @@ internal class ScanCommand : CommandLineExcecuteable {
ctx.Status($"Getting cassette PLC {item.Cassette.IPAddress}:{item.Cassette.Port}") ctx.Status($"Getting cassette PLC {item.Cassette.IPAddress}:{item.Cassette.Port}")
.Spinner(Spinner.Known.Dots); .Spinner(Spinner.Known.Dots);
var dev = Mewtocol.Ethernet(item.Cassette.IPAddress, item.Cassette.Port); var dev = Mewtocol.Ethernet(item.Cassette.IPAddress, item.Cassette.Port).Build();
dev.ConnectTimeout = 1000; dev.ConnectTimeout = 1000;
await dev.ConnectAsync(); await dev.ConnectAsync();
item.PLCInf = dev.PlcInfo; item.PLCInf = dev.PlcInfo;

View File

@@ -1,6 +1,7 @@
using CommandLine; using CommandLine;
using CommandLine.Text; using CommandLine.Text;
using MewTerminal.Commands; using MewTerminal.Commands;
using MewTerminal.Commands.OnlineCommands;
using MewtocolNet.Logging; using MewtocolNet.Logging;
using Spectre.Console; using Spectre.Console;
using System.Globalization; using System.Globalization;

View File

@@ -1,4 +1,5 @@
using MewtocolNet.DocAttributes; using MewtocolNet.DocAttributes;
using MewtocolNet.Registers;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@@ -118,7 +119,9 @@ namespace MewtocolNet {
/// <returns>A <see cref="T:byte[]"/> or null of failed</returns> /// <returns>A <see cref="T:byte[]"/> or null of failed</returns>
internal static byte[] ParseDTRawStringAsBytes (this string _onString) { internal static byte[] ParseDTRawStringAsBytes (this string _onString) {
var res = new Regex(@"\%([0-9]{2})\$RD(?<data>.*)(?<csum>..)..").Match(_onString); _onString = _onString.Replace("\r", "");
var res = new Regex(@"\%([0-9]{2})\$RD(?<data>.*)(?<csum>..)").Match(_onString);
if (res.Success) { if (res.Success) {
string val = res.Groups["data"].Value; string val = res.Groups["data"].Value;
@@ -248,6 +251,16 @@ namespace MewtocolNet {
} }
internal static bool CompareIsDuplicateNonCast (this BaseRegister reg1, BaseRegister compare) {
bool valCompare = reg1.GetType() != compare.GetType() &&
reg1.MemoryAddress == compare.MemoryAddress &&
reg1.GetSpecialAddress() == compare.GetSpecialAddress();
return valCompare;
}
internal static bool CompareIsNameDuplicate(this IRegisterInternal reg1, IRegisterInternal compare) { internal static bool CompareIsNameDuplicate(this IRegisterInternal reg1, IRegisterInternal compare) {
return ( reg1.Name != null || compare.Name != null) && reg1.Name == compare.Name; return ( reg1.Name != null || compare.Name != null) && reg1.Name == compare.Name;

View File

@@ -124,6 +124,11 @@ namespace MewtocolNet {
/// </summary> /// </summary>
IEnumerable<IRegister> GetAllRegisters(); IEnumerable<IRegister> GetAllRegisters();
/// <summary>
/// Explains the register internal layout at this moment in time
/// </summary>
string Explain();
} }
} }

View File

@@ -38,18 +38,6 @@ namespace MewtocolNet {
/// <param name="_station">Station Number of the PLC</param> /// <param name="_station">Station Number of the PLC</param>
void ConfigureConnection(string _ip, int _port = 9094, int _station = 1); void ConfigureConnection(string _ip, int _port = 9094, int _station = 1);
/// <summary>
/// Attaches a poller to the interface
/// </summary>
IPlcEthernet WithPoller();
/// <summary>
/// Attaches a register collection object to
/// the interface that can be updated automatically.
/// </summary>
/// <param name="collection">The type of the collection base class</param>
IPlcEthernet AddRegisterCollection(RegisterCollection collection);
} }
} }

View File

@@ -58,18 +58,6 @@ namespace MewtocolNet {
/// </summary> /// </summary>
Task ConnectAsync(Action onTryingConfig); Task ConnectAsync(Action onTryingConfig);
/// <summary>
/// Attaches a poller to the interface
/// </summary>
IPlcSerial WithPoller();
/// <summary>
/// Attaches a register collection object to
/// the interface that can be updated automatically.
/// </summary>
/// <param name="collection">The type of the collection base class</param>
IPlcSerial AddRegisterCollection(RegisterCollection collection);
} }
} }

View File

@@ -1,6 +1,8 @@
using MewtocolNet.Exceptions; using MewtocolNet.Exceptions;
using MewtocolNet.RegisterAttributes;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO.Ports; using System.IO.Ports;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -20,11 +22,13 @@ namespace MewtocolNet {
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="station">Plc station number</param> /// <param name="station">Plc station number</param>
/// <returns></returns> /// <returns></returns>
public static IPlcEthernet Ethernet (string ip, int port = 9094, int station = 1) { public static PostInit<IPlcEthernet> Ethernet (string ip, int port = 9094, int station = 1) {
var instance = new MewtocolInterfaceTcp(); var instance = new MewtocolInterfaceTcp();
instance.ConfigureConnection(ip, port, station); instance.ConfigureConnection(ip, port, station);
return instance; return new PostInit<IPlcEthernet> {
intf = instance
};
} }
@@ -35,11 +39,13 @@ namespace MewtocolNet {
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="station">Plc station number</param> /// <param name="station">Plc station number</param>
/// <returns></returns> /// <returns></returns>
public static IPlcEthernet Ethernet(IPAddress ip, int port = 9094, int station = 1) { public static PostInit<IPlcEthernet> Ethernet(IPAddress ip, int port = 9094, int station = 1) {
var instance = new MewtocolInterfaceTcp(); var instance = new MewtocolInterfaceTcp();
instance.ConfigureConnection(ip, port, station); instance.ConfigureConnection(ip, port, station);
return instance; return new PostInit<IPlcEthernet> {
intf = instance
};
} }
@@ -53,13 +59,15 @@ namespace MewtocolNet {
/// <param name="stopBits"></param> /// <param name="stopBits"></param>
/// <param name="station"></param> /// <param name="station"></param>
/// <returns></returns> /// <returns></returns>
public static IPlcSerial Serial (string portName, BaudRate baudRate = BaudRate._19200, DataBits dataBits = DataBits.Eight, Parity parity = Parity.Odd, StopBits stopBits = StopBits.One, int station = 1) { public static PostInit<IPlcSerial> Serial (string portName, BaudRate baudRate = BaudRate._19200, DataBits dataBits = DataBits.Eight, Parity parity = Parity.Odd, StopBits stopBits = StopBits.One, int station = 1) {
TestPortName(portName); TestPortName(portName);
var instance = new MewtocolInterfaceSerial(); var instance = new MewtocolInterfaceSerial();
instance.ConfigureConnection(portName, (int)baudRate, (int)dataBits, parity, stopBits, station); instance.ConfigureConnection(portName, (int)baudRate, (int)dataBits, parity, stopBits, station);
return instance; return new PostInit<IPlcSerial> {
intf = instance
};
} }
@@ -69,14 +77,16 @@ namespace MewtocolNet {
/// <param name="portName"></param> /// <param name="portName"></param>
/// <param name="station"></param> /// <param name="station"></param>
/// <returns></returns> /// <returns></returns>
public static IPlcSerial SerialAuto (string portName, int station = 1) { public static PostInit<IPlcSerial> SerialAuto (string portName, int station = 1) {
TestPortName(portName); TestPortName(portName);
var instance = new MewtocolInterfaceSerial(); var instance = new MewtocolInterfaceSerial();
instance.ConfigureConnection(portName, station); instance.ConfigureConnection(portName, station);
instance.ConfigureConnectionAuto(); instance.ConfigureConnectionAuto();
return instance; return new PostInit<IPlcSerial> {
intf = instance
};
} }
@@ -89,6 +99,112 @@ namespace MewtocolNet {
} }
public class MemoryManagerSettings {
/// <summary>
/// <code>
/// This feature can improve read write times by a big margin but also
/// block outgoing messages inbetween polling cycles more frequently
/// </code>
/// The max distance of the gap between registers (if there is a gap between
/// adjacent registers) to merge them into one request <br/>
/// Example: <br/>
/// <example>
/// We have a register at DT100 (1 word long) and a
/// register at DT101 (1 word long) <br/>
/// - If the max distance is 0 it will not merge them into one request<br/>
/// - If the max distance is 1 it will merge them into one request<br/>
/// - If the max distance is 2 and the next register is at DT102 it will also merge them and ignore the spacer byte in the response<br/>
/// </example>
/// </summary>
public int MaxOptimizationDistance { get; set; } = 8;
/// <summary>
/// The max number of registers per request group
/// </summary>
public int MaxRegistersPerGroup { get; set; } = -1;
}
public class PostInit<T> {
internal T intf;
/// <summary>
/// Attaches a auto poller to the interface that reads all registers
/// cyclic
/// </summary>
/// <returns></returns>
public PostInit<T> WithPoller() {
if (intf is MewtocolInterface imew) {
imew.usePoller = true;
}
return this;
}
/// <summary>
/// General setting for the memory manager
/// </summary>
public PostInit<T> WithMemoryManagerSettings (Action<MemoryManagerSettings> settings) {
var res = new MemoryManagerSettings();
settings.Invoke(res);
if (res.MaxOptimizationDistance < 0)
throw new NotSupportedException($"A value lower than 0 is not allowed for " +
$"{nameof(MemoryManagerSettings.MaxOptimizationDistance)}");
if (intf is MewtocolInterface imew) {
imew.memoryManager.maxOptimizationDistance = res.MaxOptimizationDistance;
imew.memoryManager.maxRegistersPerGroup = res.MaxRegistersPerGroup;
}
return this;
}
/// <summary>
/// A builder for attaching register collections
/// </summary>
public EndInit<T> WithRegisterCollections(Action<RegisterCollectionCollector> collector) {
var res = new RegisterCollectionCollector();
collector.Invoke(res);
if (intf is MewtocolInterface imew) {
imew.WithRegisterCollections(res.collections);
}
return new EndInit<T> {
postInit = this
};
}
/// <summary>
/// Builds and returns the final plc interface
/// </summary>
public T Build() => intf;
}
public class EndInit<T> {
internal PostInit<T> postInit;
/// <summary>
/// Builds and returns the final plc interface
/// </summary>
public T Build() => postInit.intf;
}
} }
} }

View File

@@ -11,10 +11,11 @@ using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using MewtocolNet.UnderlyingRegisters;
namespace MewtocolNet { namespace MewtocolNet {
public partial class MewtocolInterface : IPlc, INotifyPropertyChanged, IDisposable { public abstract partial class MewtocolInterface : IPlc, INotifyPropertyChanged, IDisposable {
#region Private fields #region Private fields
@@ -49,6 +50,7 @@ namespace MewtocolNet {
internal volatile bool pollerTaskStopped = true; internal volatile bool pollerTaskStopped = true;
internal volatile bool pollerFirstCycle; internal volatile bool pollerFirstCycle;
internal bool usePoller = false; internal bool usePoller = false;
internal MemoryAreaManager memoryManager;
internal List<BaseRegister> RegistersUnderlying { get; private set; } = new List<BaseRegister>(); internal List<BaseRegister> RegistersUnderlying { get; private set; } = new List<BaseRegister>();
internal IEnumerable<IRegisterInternal> RegistersInternal => RegistersUnderlying.Cast<IRegisterInternal>(); internal IEnumerable<IRegisterInternal> RegistersInternal => RegistersUnderlying.Cast<IRegisterInternal>();
@@ -142,6 +144,8 @@ namespace MewtocolNet {
private protected MewtocolInterface () { private protected MewtocolInterface () {
memoryManager = new MemoryAreaManager(this);
Connected += MewtocolInterface_Connected; Connected += MewtocolInterface_Connected;
RegisterChanged += OnRegisterChanged; RegisterChanged += OnRegisterChanged;
@@ -164,6 +168,8 @@ namespace MewtocolNet {
$"{(o.Name != null ? $"({o.Name}) " : "")}" + $"{(o.Name != null ? $"({o.Name}) " : "")}" +
$"changed to \"{asInternal.GetValueString()}\"", LogLevel.Change, this); $"changed to \"{asInternal.GetValueString()}\"", LogLevel.Change, this);
OnRegisterChangedUpdateProps((IRegisterInternal)o);
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -352,8 +358,9 @@ namespace MewtocolNet {
} }
//request next frame //request next frame
var writeBuffer = Encoding.UTF8.GetBytes("%01**&\r"); var writeBuffer = Encoding.UTF8.GetBytes($"%{GetStationNumber()}**&\r");
await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length); await stream.WriteAsync(writeBuffer, 0, writeBuffer.Length);
Logger.Log($">> Requested next frame", LogLevel.Critical, this);
wasMultiFramedResponse = true; wasMultiFramedResponse = true;
} }
@@ -521,6 +528,8 @@ namespace MewtocolNet {
#endregion #endregion
public string Explain() => memoryManager.ExplainLayout();
} }
} }

View File

@@ -3,9 +3,11 @@ using MewtocolNet.Logging;
using MewtocolNet.RegisterAttributes; using MewtocolNet.RegisterAttributes;
using MewtocolNet.RegisterBuilding; using MewtocolNet.RegisterBuilding;
using MewtocolNet.Registers; using MewtocolNet.Registers;
using MewtocolNet.UnderlyingRegisters;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -18,7 +20,7 @@ namespace MewtocolNet {
/// <summary> /// <summary>
/// The PLC com interface class /// The PLC com interface class
/// </summary> /// </summary>
public partial class MewtocolInterface { public abstract partial class MewtocolInterface {
internal Task pollCycleTask; internal Task pollCycleTask;
@@ -38,6 +40,8 @@ namespace MewtocolNet {
} }
} }
private List<RegisterCollection> registerCollections = new List<RegisterCollection>();
#region Register Polling #region Register Polling
/// <summary> /// <summary>
@@ -70,7 +74,7 @@ namespace MewtocolNet {
/// useful if you want to use a custom update frequency /// useful if you want to use a custom update frequency
/// </summary> /// </summary>
/// <returns>The number of inidvidual mewtocol commands sent</returns> /// <returns>The number of inidvidual mewtocol commands sent</returns>
public async Task<int> RunPollerCylceManual () { public async Task<int> RunPollerCylceManual() {
if (!pollerTaskStopped) if (!pollerTaskStopped)
throw new NotSupportedException($"The poller is already running, " + throw new NotSupportedException($"The poller is already running, " +
@@ -86,7 +90,7 @@ namespace MewtocolNet {
} }
//polls all registers one by one (slow) //polls all registers one by one (slow)
internal async Task Poll () { internal async Task Poll() {
Logger.Log("Poller is attaching", LogLevel.Info, this); Logger.Log("Poller is attaching", LogLevel.Info, this);
@@ -111,13 +115,15 @@ namespace MewtocolNet {
} }
private async Task OnMultiFrameCycle () { private async Task OnMultiFrameCycle() {
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
await UpdateRCPRegisters(); //await UpdateRCPRegisters();
await UpdateDTRegisters(); //await UpdateDTRegisters();
await memoryManager.PollAllAreasAsync();
await GetPLCInfoAsync(); await GetPLCInfoAsync();
@@ -130,7 +136,7 @@ namespace MewtocolNet {
#region Smart register polling methods #region Smart register polling methods
private async Task UpdateRCPRegisters () { private async Task UpdateRCPRegisters() {
//build booleans //build booleans
var rcpList = RegistersUnderlying.Where(x => x.GetType() == typeof(BoolRegister)) var rcpList = RegistersUnderlying.Where(x => x.GetType() == typeof(BoolRegister))
@@ -145,7 +151,7 @@ namespace MewtocolNet {
int toReadRegistersCount = 8; int toReadRegistersCount = 8;
if(i == rcpFrameCount - 1) toReadRegistersCount = rcpLastFrameRemainder; if (i == rcpFrameCount - 1) toReadRegistersCount = rcpLastFrameRemainder;
var rcpString = new StringBuilder($"%{GetStationNumber()}#RCP{toReadRegistersCount}"); var rcpString = new StringBuilder($"%{GetStationNumber()}#RCP{toReadRegistersCount}");
@@ -166,7 +172,7 @@ namespace MewtocolNet {
var register = rcpList[i + k]; var register = rcpList[i + k];
if((bool)register.Value != resultBitArray[k]) { if ((bool)register.Value != resultBitArray[k]) {
register.SetValueFromPLC(resultBitArray[k]); register.SetValueFromPLC(resultBitArray[k]);
} }
@@ -176,13 +182,13 @@ namespace MewtocolNet {
} }
private async Task UpdateDTRegisters () { private async Task UpdateDTRegisters() {
foreach (var reg in RegistersUnderlying) { foreach (var reg in RegistersUnderlying) {
var type = reg.GetType(); var type = reg.GetType();
if(reg.RegisterType.IsNumericDTDDT() || reg.RegisterType == RegisterType.DT_BYTE_RANGE) { if (reg.RegisterType.IsNumericDTDDT() || reg.RegisterType == RegisterType.DT_BYTE_RANGE) {
var lastVal = reg.Value; var lastVal = reg.Value;
var rwReg = (IRegisterInternal)reg; var rwReg = (IRegisterInternal)reg;
@@ -203,7 +209,17 @@ namespace MewtocolNet {
#region Register Colleciton adding #region Register Colleciton adding
internal MewtocolInterface WithRegisterCollection (RegisterCollection collection) { /// <summary>
/// Adds the given register collection and all its registers with attributes to the register list
/// </summary>
internal void WithRegisterCollections(List<RegisterCollection> collections) {
if (registerCollections.Count != 0)
throw new NotSupportedException("Register collections can only be build once");
List<RegisterBuildInfo> buildInfos = new List<RegisterBuildInfo>();
foreach (var collection in collections) {
collection.PLCInterface = this; collection.PLCInterface = this;
@@ -218,21 +234,17 @@ namespace MewtocolNet {
if (attr is RegisterAttribute cAttribute) { if (attr is RegisterAttribute cAttribute) {
if(!prop.PropertyType.IsAllowedPlcCastingType()) { if (!prop.PropertyType.IsAllowedPlcCastingType()) {
throw new MewtocolException($"The register attribute property type is not allowed ({prop.PropertyType})"); throw new MewtocolException($"The register attribute property type is not allowed ({prop.PropertyType})");
} }
var dotnetType = prop.PropertyType; var dotnetType = prop.PropertyType;
AddRegister(new RegisterBuildInfo { buildInfos.Add(new RegisterBuildInfo {
mewAddress = cAttribute.MewAddress, mewAddress = cAttribute.MewAddress,
memoryAddress = cAttribute.MemoryArea,
specialAddress = cAttribute.SpecialAddress,
memorySizeBytes = cAttribute.ByteLength,
registerType = cAttribute.RegisterType,
dotnetCastType = dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType, dotnetCastType = dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType,
collectionType = collection.GetType(), collectionTarget = collection,
name = prop.Name, boundPropTarget = prop,
}); });
} }
@@ -241,106 +253,37 @@ namespace MewtocolNet {
} }
RegisterChanged += (reg) => { if (collection != null) {
registerCollections.Add(collection);
//register is used bitwise
if (reg.GetType() == typeof(BytesRegister)) {
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); collection.OnInterfaceLinked(this);
}
Connected += (i) => { Connected += (i) => {
if (collection != null) if (collection != null)
collection.OnInterfaceLinkedAndOnline(this); collection.OnInterfaceLinkedAndOnline(this);
}; };
return this; }
AddRegisters(buildInfos);
}
/// <summary>
/// Writes back the values changes of the underlying registers to the corrosponding property
/// </summary>
private void OnRegisterChangedUpdateProps(IRegisterInternal reg) {
var collection = reg.ContainedCollection;
if (collection == null) return;
var props = collection.GetType().GetProperties();
//set the specific bit array if needed
//prop.SetValue(collection, bitAr);
//collection.TriggerPropertyChanged(prop.Name);
} }
@@ -349,10 +292,10 @@ namespace MewtocolNet {
#region Register Adding #region Register Adding
/// <inheritdoc/> /// <inheritdoc/>
public void AddRegister(IRegister register) => AddRegister(register as BaseRegister); public void AddRegister (IRegister register) => AddRegister(register as BaseRegister);
/// <inheritdoc/> /// <inheritdoc/>
public void AddRegister(BaseRegister register) { public void AddRegister (BaseRegister register) {
if (CheckDuplicateRegister(register)) if (CheckDuplicateRegister(register))
throw MewtocolException.DupeRegister(register); throw MewtocolException.DupeRegister(register);
@@ -368,28 +311,56 @@ namespace MewtocolNet {
} }
internal void AddRegister (RegisterBuildInfo buildInfo) { // Used for internal property based register building
internal void AddRegisters (List<RegisterBuildInfo> buildInfos) {
var builtRegister = buildInfo.Build(); //build all from attribute
List<BaseRegister> registers = new List<BaseRegister>();
//is bitwise and the register list already contains that area register foreach (var buildInfo in buildInfos) {
if(builtRegister.GetType() == typeof(BytesRegister) && CheckDuplicateRegister(builtRegister, out var existing)) {
return; var builtRegister = buildInfo.BuildForCollectionAttribute();
int? linkLen = null;
if(builtRegister is BytesRegister bReg) {
linkLen = (int?)bReg.ReservedBytesSize ?? bReg.ReservedBitSize;
} }
if (CheckDuplicateRegister(builtRegister)) //attach the property and collection
throw MewtocolException.DupeRegister(builtRegister); builtRegister.WithBoundProperty(new RegisterPropTarget {
BoundProperty = buildInfo.boundPropTarget,
LinkLength = linkLen,
});
if(CheckDuplicateNameRegister(builtRegister)) builtRegister.WithRegisterCollection(buildInfo.collectionTarget);
throw MewtocolException.DupeNameRegister(builtRegister);
if (CheckOverlappingRegister(builtRegister, out var regB))
throw MewtocolException.OverlappingRegister(builtRegister, regB);
builtRegister.attachedInterface = this; builtRegister.attachedInterface = this;
RegistersUnderlying.Add(builtRegister); registers.Add(builtRegister);
}
//order by address
registers = registers.OrderBy(x => x.GetSpecialAddress()).ToList();
registers = registers.OrderBy(x => x.MemoryAddress).ToList();
//link to memory manager
for (int i = 0, j = 0; i < registers.Count; i++) {
BaseRegister reg = registers[i];
reg.name = $"auto_prop_register_{j + 1}";
//link the memory area to the register
if (memoryManager.LinkRegister(reg)) {
RegistersUnderlying.Add(reg);
j++;
}
}
} }
@@ -480,7 +451,6 @@ namespace MewtocolNet {
} }
internal void PropertyRegisterWasSet(string propName, object value) { internal void PropertyRegisterWasSet(string propName, object value) {
_ = SetRegisterAsync(GetRegister(propName), value); _ = SetRegisterAsync(GetRegister(propName), value);

View File

@@ -12,7 +12,7 @@ using System.Threading.Tasks;
namespace MewtocolNet { namespace MewtocolNet {
public partial class MewtocolInterface { public abstract partial class MewtocolInterface {
#region PLC info getters #region PLC info getters

View File

@@ -14,7 +14,7 @@ using MewtocolNet.RegisterAttributes;
namespace MewtocolNet { namespace MewtocolNet {
public class MewtocolInterfaceSerial : MewtocolInterface, IPlcSerial { public sealed class MewtocolInterfaceSerial : MewtocolInterface, IPlcSerial {
private bool autoSerial; private bool autoSerial;
@@ -50,13 +50,6 @@ namespace MewtocolNet {
} }
public IPlcSerial AddRegisterCollection (RegisterCollection collection) {
WithRegisterCollection(collection);
return this;
}
/// <inheritdoc/> /// <inheritdoc/>
public override string GetConnectionInfo() { public override string GetConnectionInfo() {

View File

@@ -11,7 +11,7 @@ namespace MewtocolNet {
/// <summary> /// <summary>
/// The PLC com interface class /// The PLC com interface class
/// </summary> /// </summary>
public class MewtocolInterfaceTcp : MewtocolInterface, IPlcEthernet { public sealed class MewtocolInterfaceTcp : MewtocolInterface, IPlcEthernet {
//TCP //TCP
internal TcpClient client; internal TcpClient client;
@@ -29,22 +29,6 @@ namespace MewtocolNet {
internal MewtocolInterfaceTcp () : base() { } internal MewtocolInterfaceTcp () : base() { }
/// <inheritdoc/>
public IPlcEthernet WithPoller () {
usePoller = true;
return this;
}
/// <inheritdoc/>
public IPlcEthernet AddRegisterCollection (RegisterCollection collection) {
WithRegisterCollection(collection);
return this;
}
#region TCP connection state handling #region TCP connection state handling
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -0,0 +1,20 @@
using System;
namespace MewtocolNet.RegisterAttributes {
/// <summary>
/// Defines the behavior of a register property
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class PollFrequencyAttribute : Attribute {
internal int skipEachCycle;
public PollFrequencyAttribute(int eachCycleN) {
skipEachCycle = eachCycleN;
}
}
}

View File

@@ -9,13 +9,6 @@ namespace MewtocolNet.RegisterAttributes {
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RegisterAttribute : Attribute { public class RegisterAttribute : Attribute {
internal RegisterType? RegisterType;
internal uint MemoryArea = 0;
internal uint ByteLength = 2;
internal byte SpecialAddress = 0x0;
internal BitCount BitCount;
internal int AssignedBitIndex = -1; internal int AssignedBitIndex = -1;
internal string MewAddress = null; internal string MewAddress = null;
@@ -26,65 +19,6 @@ namespace MewtocolNet.RegisterAttributes {
} }
/// <summary>
/// Attribute for string type or numeric registers
/// </summary>
/// <param name="memoryArea">The area in the plcs memory</param>
public RegisterAttribute(uint memoryArea) {
MemoryArea = memoryArea;
}
public RegisterAttribute(uint memoryArea, uint byteLength) {
MemoryArea = memoryArea;
ByteLength = byteLength;
}
public RegisterAttribute(uint memoryArea, BitCount bitCount) {
MemoryArea = memoryArea;
BitCount = bitCount;
AssignedBitIndex = 0;
RegisterType = BitCount == BitCount.B16 ? MewtocolNet.RegisterType.DT : MewtocolNet.RegisterType.DDT;
}
public RegisterAttribute(uint memoryArea, BitCount bitCount, int bitIndex) {
MemoryArea = memoryArea;
BitCount = bitCount;
AssignedBitIndex = bitIndex;
RegisterType = BitCount == BitCount.B16 ? MewtocolNet.RegisterType.DT : MewtocolNet.RegisterType.DDT;
}
/// <summary>
/// Attribute for boolean registers
/// </summary>
public RegisterAttribute(IOType type, byte spAdress = 0x0) {
MemoryArea = 0;
RegisterType = (RegisterType)(int)type;
SpecialAddress = spAdress;
}
/// <summary>
/// Attribute for boolean registers
/// </summary>
public RegisterAttribute(IOType type, uint memoryArea, byte spAdress = 0x0) {
MemoryArea = memoryArea;
RegisterType = (RegisterType)(int)type;
SpecialAddress = spAdress;
}
} }
} }

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace MewtocolNet.RegisterAttributes {
public class RegisterCollectionCollector {
internal List<RegisterCollection> collections = new List<RegisterCollection>();
public RegisterCollectionCollector AddCollection (RegisterCollection collection) {
collections.Add(collection);
return this;
}
public RegisterCollectionCollector AddCollection<T> () where T : RegisterCollection {
var instance = (RegisterCollection)Activator.CreateInstance(typeof(T));
collections.Add(instance);
return this;
}
}
}

View File

@@ -0,0 +1,26 @@
using System.Reflection;
using System.Text;
namespace MewtocolNet.RegisterAttributes {
internal class RegisterPropTarget {
//propinfo of the bound property
internal PropertyInfo BoundProperty;
//general number of bits or bytes to read back to the prop
internal int? LinkLength;
public override string ToString() {
var sb = new StringBuilder();
sb.Append($"{BoundProperty}");
if(LinkLength != null) sb.Append($" -Len: {LinkLength}");
return sb.ToString();
}
}
}

View File

@@ -1,4 +1,5 @@
using MewtocolNet.Exceptions; using MewtocolNet.Exceptions;
using MewtocolNet.RegisterAttributes;
using MewtocolNet.Registers; using MewtocolNet.Registers;
using System; using System;
using System.Collections; using System.Collections;
@@ -20,14 +21,20 @@ namespace MewtocolNet.RegisterBuilding {
internal byte? specialAddress; internal byte? specialAddress;
internal RegisterType? registerType; internal RegisterType? registerType;
internal Type dotnetCastType; internal Type dotnetCastType;
internal Type collectionType;
internal RegisterCollection collectionTarget;
internal PropertyInfo boundPropTarget;
internal BaseRegister BuildForCollectionAttribute () {
return (BaseRegister)RegBuilder.Factory.FromPlcRegName(mewAddress, name).AsType(dotnetCastType).Build();
}
internal BaseRegister Build () { internal BaseRegister Build () {
//Has mew address use this before the default checks
if (mewAddress != null) return BuildFromMewAddress();
//parse enums //parse enums
if (dotnetCastType.IsEnum) { if (dotnetCastType.IsEnum) {
@@ -46,8 +53,8 @@ namespace MewtocolNet.RegisterBuilding {
var parameters = new object[] { memoryAddress, name }; var parameters = new object[] { memoryAddress, name };
var instance = (BaseRegister)constr.Invoke(parameters); var instance = (BaseRegister)constr.Invoke(parameters);
if (collectionType != null) if (collectionTarget != null)
instance.WithCollectionType(collectionType); instance.WithRegisterCollection(collectionTarget);
return instance; return instance;
@@ -56,22 +63,18 @@ namespace MewtocolNet.RegisterBuilding {
//parse all others where the type is known //parse all others where the type is known
RegisterType regType = registerType ?? dotnetCastType.ToRegisterTypeDefault(); RegisterType regType = registerType ?? dotnetCastType.ToRegisterTypeDefault();
Type registerClassType = dotnetCastType.GetDefaultRegisterHoldingType(); Type registerClassType = dotnetCastType.GetDefaultRegisterHoldingType();
bool isBytesRegister = !registerClassType.IsGenericType && registerClassType == typeof(BytesRegister);
bool isBoolRegister = regType.IsBoolean();
bool isBytesArrRegister = !registerClassType.IsGenericType && registerClassType == typeof(BytesRegister) && dotnetCastType == typeof(byte[]);
bool isBytesBitsRegister = !registerClassType.IsGenericType && registerClassType == typeof(BytesRegister) && dotnetCastType == typeof(BitArray);
bool isStringRegister = !registerClassType.IsGenericType && registerClassType == typeof(StringRegister); bool isStringRegister = !registerClassType.IsGenericType && registerClassType == typeof(StringRegister);
if (regType.IsNumericDTDDT() && (dotnetCastType == typeof(bool))) { bool isNormalNumericResiter = regType.IsNumericDTDDT() && !isBytesArrRegister && !isBytesBitsRegister && !isStringRegister;
//------------------------------------------- if (isNormalNumericResiter) {
//as numeric register with boolean bit target
//create a new bregister instance
var instance = new BytesRegister(memoryAddress, memorySizeBytes.Value, name);
if (collectionType != null)
instance.WithCollectionType(collectionType);
return instance;
} else if (regType.IsNumericDTDDT() && !isBytesRegister && !isStringRegister) {
//------------------------------------------- //-------------------------------------------
//as numeric register //as numeric register
@@ -83,28 +86,43 @@ namespace MewtocolNet.RegisterBuilding {
var parameters = new object[] { memoryAddress, name }; var parameters = new object[] { memoryAddress, name };
var instance = (BaseRegister)Activator.CreateInstance(registerClassType, flags, null, parameters, null); var instance = (BaseRegister)Activator.CreateInstance(registerClassType, flags, null, parameters, null);
if(collectionType != null) if(collectionTarget != null)
instance.WithCollectionType(collectionType); instance.WithRegisterCollection(collectionTarget);
return instance; return instance;
} }
if(isBytesRegister) { if(isBytesArrRegister) {
//------------------------------------------- //-------------------------------------------
//as byte range register //as byte range register
BytesRegister instance; BytesRegister instance = new BytesRegister(memoryAddress, memorySizeBytes.Value, name);
instance.ReservedBytesSize = (ushort)memorySizeBytes.Value;
if (collectionTarget != null)
instance.WithRegisterCollection(collectionTarget);
return instance;
if(memorySizeBits != null) {
instance = new BytesRegister(memoryAddress, memorySizeBits.Value, name);
} else {
instance = new BytesRegister(memoryAddress, memorySizeBytes.Value, name);
} }
if (collectionType != null) if(isBytesBitsRegister) {
instance.WithCollectionType(collectionType);
//-------------------------------------------
//as bit range register
BytesRegister instance;
if (memorySizeBits != null) {
instance = new BytesRegister(memoryAddress, memorySizeBits.Value, name);
} else {
instance = new BytesRegister(memoryAddress, 16, name);
}
if (collectionTarget != null)
instance.WithRegisterCollection(collectionTarget);
return instance; return instance;
@@ -116,14 +134,14 @@ namespace MewtocolNet.RegisterBuilding {
//as byte range register //as byte range register
var instance = (BaseRegister)new StringRegister(memoryAddress, name); var instance = (BaseRegister)new StringRegister(memoryAddress, name);
if (collectionType != null) if (collectionTarget != null)
instance.WithCollectionType(collectionType); instance.WithRegisterCollection(collectionTarget);
return instance; return instance;
} }
if (regType.IsBoolean()) { if (isBoolRegister) {
//------------------------------------------- //-------------------------------------------
//as boolean register //as boolean register
@@ -134,8 +152,8 @@ namespace MewtocolNet.RegisterBuilding {
var instance = new BoolRegister(io, spAddr.Value, areaAddr, name); var instance = new BoolRegister(io, spAddr.Value, areaAddr, name);
if (collectionType != null) if (collectionTarget != null)
((IRegisterInternal)instance).WithCollectionType(collectionType); instance.WithRegisterCollection(collectionTarget);
return instance; return instance;
@@ -145,12 +163,6 @@ namespace MewtocolNet.RegisterBuilding {
} }
private BaseRegister BuildFromMewAddress () {
return (BaseRegister)RegBuilder.Factory.FromPlcRegName(mewAddress, name).AsType(dotnetCastType).Build();
}
} }
} }

View File

@@ -1,5 +1,9 @@
using System; using MewtocolNet.RegisterAttributes;
using MewtocolNet.UnderlyingRegisters;
using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -12,12 +16,19 @@ namespace MewtocolNet.Registers {
/// </summary> /// </summary>
public event Action<object> ValueChanged; public event Action<object> ValueChanged;
//links to
internal RegisterCollection containedCollection;
internal MewtocolInterface attachedInterface; internal MewtocolInterface attachedInterface;
internal List<RegisterPropTarget> boundToProps = new List<RegisterPropTarget>();
internal IMemoryArea underlyingMemory;
internal object lastValue = null; internal object lastValue = null;
internal Type collectionType;
internal string name; internal string name;
internal uint memoryAddress; internal uint memoryAddress;
/// <inheritdoc/>
public RegisterCollection ContainedCollection => containedCollection;
/// <inheritdoc/> /// <inheritdoc/>
public MewtocolInterface AttachedInterface => attachedInterface; public MewtocolInterface AttachedInterface => attachedInterface;
@@ -27,9 +38,6 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/> /// <inheritdoc/>
public RegisterType RegisterType { get; protected set; } public RegisterType RegisterType { get; protected set; }
/// <inheritdoc/>
public Type CollectionType => collectionType;
/// <inheritdoc/> /// <inheritdoc/>
public string Name => name; public string Name => name;
@@ -62,12 +70,22 @@ namespace MewtocolNet.Registers {
} }
public void WithCollectionType(Type colType) => collectionType = colType; internal virtual object SetValueFromBytes(byte[] bytes) => throw new NotImplementedException();
internal void WithRegisterCollection (RegisterCollection collection) => containedCollection = collection;
internal void WithBoundProperty(RegisterPropTarget propInfo) => boundToProps.Add(propInfo);
#region Read / Write
public virtual Task<object> ReadAsync() => throw new NotImplementedException();
public virtual Task<bool> WriteAsync(object data) => throw new NotImplementedException();
#endregion
#region Default accessors #region Default accessors
public Type GetCollectionType() => CollectionType;
public RegisterType GetRegisterType() => RegisterType; public RegisterType GetRegisterType() => RegisterType;
public virtual string BuildMewtocolQuery() { public virtual string BuildMewtocolQuery() {
@@ -89,9 +107,9 @@ namespace MewtocolNet.Registers {
public virtual string GetRegisterString() => RegisterType.ToString(); public virtual string GetRegisterString() => RegisterType.ToString();
public virtual string GetCombinedName() => $"{(CollectionType != null ? $"{CollectionType.Name}." : "")}{Name ?? "Unnamed"}"; public virtual string GetCombinedName() => $"{GetContainerName()}{(GetContainerName() != null ? "." : "")}{Name ?? "Unnamed"}";
public virtual string GetContainerName() => $"{(CollectionType != null ? $"{CollectionType.Name}" : "")}"; public virtual string GetContainerName() => $"{(containedCollection != null ? $"{containedCollection.GetType().Name}" : null)}";
public virtual string GetMewName() => $"{GetRegisterString()}{MemoryAddress}"; public virtual string GetMewName() => $"{GetRegisterString()}{MemoryAddress}";
@@ -101,14 +119,6 @@ namespace MewtocolNet.Registers {
#endregion #endregion
#region Read / Write
public virtual async Task<object> ReadAsync() => throw new NotImplementedException();
public virtual async Task<bool> WriteAsync(object data) => throw new NotImplementedException();
#endregion
protected virtual void CheckAddressOverflow (uint addressStart, uint addressLen) { protected virtual void CheckAddressOverflow (uint addressStart, uint addressLen) {
if (addressStart < 0) if (addressStart < 0)
@@ -119,18 +129,33 @@ namespace MewtocolNet.Registers {
} }
public override string ToString() => $"{GetMewName()}{(Name != null ? $" ({Name})" : "")} - Value: {GetValueString()}"; public override string ToString() {
var sb = new StringBuilder();
sb.Append(GetMewName());
if(Name != null) sb.Append($" ({Name})");
if (Value != null) sb.Append($" Val: {GetValueString()}");
return sb.ToString();
}
public virtual string ToString(bool additional) { public virtual string ToString(bool additional) {
if (!additional) return this.ToString(); if (!additional) return this.ToString();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.AppendLine($"PLC Naming: {GetMewName()}"); sb.AppendLine($"MewName: {GetMewName()}");
sb.AppendLine($"Name: {Name ?? "Not named"}"); sb.AppendLine($"Name: {Name ?? "Not named"}");
sb.AppendLine($"Value: {GetValueString()}"); sb.AppendLine($"Value: {GetValueString()}");
sb.AppendLine($"Register Type: {RegisterType}"); sb.AppendLine($"Register Type: {RegisterType}");
sb.AppendLine($"Memory Address: {MemoryAddress}"); sb.AppendLine($"Address: {GetRegisterWordRangeString()}");
if(GetSpecialAddress() != null) sb.AppendLine($"SPAddress: {GetSpecialAddress()}");
if (GetType().IsGenericType) sb.AppendLine($"Type: NumberRegister<{GetType().GenericTypeArguments[0]}>");
else sb.AppendLine($"Type: {GetType()}");
if(containedCollection != null) sb.AppendLine($"In collection: {containedCollection.GetType()}");
if(boundToProps != null && boundToProps.Count != 0)
sb.AppendLine($"Bound props: {string.Join(",", boundToProps)}");
return sb.ToString(); return sb.ToString();

View File

@@ -1,4 +1,5 @@
using System; using MewtocolNet.UnderlyingRegisters;
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Net; using System.Net;
using System.Text; using System.Text;
@@ -64,6 +65,7 @@ namespace MewtocolNet.Registers {
var read = await attachedInterface.ReadRawRegisterAsync(this); var read = await attachedInterface.ReadRawRegisterAsync(this);
if(read == null) return null; if(read == null) return null;
var parsed = PlcValueParser.Parse<bool>(this, read); var parsed = PlcValueParser.Parse<bool>(this, read);
SetValueFromPLC(parsed); SetValueFromPLC(parsed);
@@ -76,8 +78,13 @@ namespace MewtocolNet.Registers {
if (!attachedInterface.IsConnected) return false; if (!attachedInterface.IsConnected) return false;
var res = await attachedInterface.WriteRawRegisterAsync(this, PlcValueParser.Encode(this, (bool)data)); var encoded = PlcValueParser.Encode(this, (bool)data);
if (res) SetValueFromPLC(data);
var res = await attachedInterface.WriteRawRegisterAsync(this, encoded);
if (res) {
SetValueFromPLC(data);
}
return res; return res;
} }
@@ -132,23 +139,6 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/> /// <inheritdoc/>
public override uint GetRegisterAddressLen () => 1; public override uint GetRegisterAddressLen () => 1;
/// <inheritdoc/>
public override string ToString(bool additional) {
if (!additional) return this.ToString();
StringBuilder sb = new StringBuilder();
sb.AppendLine($"PLC Naming: {GetMewName()}");
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

@@ -19,9 +19,9 @@ namespace MewtocolNet.Registers {
/// </summary> /// </summary>
public uint AddressLength => addressLength; public uint AddressLength => addressLength;
internal uint ReservedBytesSize { get; private set; } internal uint ReservedBytesSize { get; set; }
internal ushort? ReservedBitSize { get; private set; } internal ushort? ReservedBitSize { get; set; }
/// <summary> /// <summary>
/// Defines a register containing bytes /// Defines a register containing bytes
@@ -128,15 +128,22 @@ namespace MewtocolNet.Registers {
if (!attachedInterface.IsConnected) return null; if (!attachedInterface.IsConnected) return null;
var read = await attachedInterface.ReadRawRegisterAsync(this); var res = await underlyingMemory.ReadRegisterAsync(this);
if (read == null) return null; if (!res) return null;
var bytes = underlyingMemory.GetUnderlyingBytes(this);
return SetValueFromBytes(bytes);
}
internal override object SetValueFromBytes(byte[] bytes) {
object parsed; object parsed;
if (ReservedBitSize != null) {
if(ReservedBitSize != null) { parsed = PlcValueParser.Parse<BitArray>(this, bytes);
parsed = PlcValueParser.Parse<BitArray>(this, read);
} else { } else {
parsed = PlcValueParser.Parse<byte[]>(this, read); parsed = PlcValueParser.Parse<byte[]>(this, bytes);
} }
SetValueFromPLC(parsed); SetValueFromPLC(parsed);
@@ -157,8 +164,10 @@ namespace MewtocolNet.Registers {
encoded = PlcValueParser.Encode(this, (byte[])data); encoded = PlcValueParser.Encode(this, (byte[])data);
} }
var res = await attachedInterface.WriteRawRegisterAsync(this, encoded); var res = await underlyingMemory.WriteRegisterAsync(this, encoded);
if (res) SetValueFromPLC(data); if (res) {
SetValueFromPLC(data);
}
return res; return res;

View File

@@ -1,4 +1,5 @@
using MewtocolNet.Registers; using MewtocolNet.RegisterAttributes;
using MewtocolNet.Registers;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -19,9 +20,9 @@ namespace MewtocolNet {
uint MemoryAddress { get; } uint MemoryAddress { get; }
// setters RegisterCollection ContainedCollection { get; }
void WithCollectionType(Type colType); // setters
void SetValueFromPLC(object value); void SetValueFromPLC(object value);
@@ -29,8 +30,6 @@ namespace MewtocolNet {
// Accessors // Accessors
Type GetCollectionType();
string GetRegisterString(); string GetRegisterString();
string GetCombinedName(); string GetCombinedName();

View File

@@ -1,4 +1,6 @@
using System; using MewtocolNet.Exceptions;
using MewtocolNet.UnderlyingRegisters;
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@@ -131,11 +133,18 @@ namespace MewtocolNet.Registers {
if (!attachedInterface.IsConnected) return null; if (!attachedInterface.IsConnected) return null;
var read = await attachedInterface.ReadRawRegisterAsync(this); var res = await underlyingMemory.ReadRegisterAsync(this);
if (read == null) return null; if (!res) return null;
var parsed = PlcValueParser.Parse<T>(this, read); var bytes = underlyingMemory.GetUnderlyingBytes(this);
return SetValueFromBytes(bytes);
}
internal override object SetValueFromBytes(byte[] bytes) {
var parsed = PlcValueParser.Parse<T>(this, bytes);
SetValueFromPLC(parsed); SetValueFromPLC(parsed);
return parsed; return parsed;
@@ -146,8 +155,13 @@ namespace MewtocolNet.Registers {
if (!attachedInterface.IsConnected) return false; if (!attachedInterface.IsConnected) return false;
var res = await attachedInterface.WriteRawRegisterAsync(this, PlcValueParser.Encode(this, (T)data)); var encoded = PlcValueParser.Encode(this, (T)data);
if (res) SetValueFromPLC(data); var res = await underlyingMemory.WriteRegisterAsync(this, encoded);
if (res) {
SetValueFromPLC(data);
}
return res; return res;
} }

View File

@@ -130,7 +130,8 @@ namespace MewtocolNet.Registers {
} }
var res = await attachedInterface.WriteRawRegisterAsync(this, PlcValueParser.Encode(this, (string)data)); var encoded = PlcValueParser.Encode(this, (string)data);
var res = await attachedInterface.WriteRawRegisterAsync(this, encoded);
if (res) { if (res) {
SetValueFromPLC(data); SetValueFromPLC(data);

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MewtocolNet.Helpers;
namespace MewtocolNet.TypeConversion { namespace MewtocolNet.TypeConversion {
@@ -134,9 +135,9 @@ namespace MewtocolNet.TypeConversion {
PlcVarType = PlcVarType.REAL, PlcVarType = PlcVarType.REAL,
FromRaw = (reg, bytes) => { FromRaw = (reg, bytes) => {
var val = BitConverter.ToUInt32(bytes, 0); //bytes = new byte[] { 0xCD, 0xCC, 0x8C, 0x40 };
byte[] floatVals = BitConverter.GetBytes(val);
float finalFloat = BitConverter.ToSingle(floatVals, 0); float finalFloat = BitConverter.ToSingle(bytes, 0);
return finalFloat; return finalFloat;

View File

@@ -0,0 +1,152 @@
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MewtocolNet.UnderlyingRegisters {
public class DTArea : IMemoryArea {
private MewtocolInterface mewInterface;
internal RegisterType registerType;
internal ulong addressStart;
internal ulong addressEnd;
internal byte[] underlyingBytes = new byte[2];
internal List<BaseRegister> linkedRegisters = new List<BaseRegister>();
public ulong AddressStart => addressStart;
public ulong AddressEnd => addressEnd;
internal DTArea (MewtocolInterface mewIf) {
mewInterface = mewIf;
}
internal void BoundaryUdpdate (uint? addrFrom = null, uint? addrTo = null) {
var addFrom = addrFrom ?? addressStart;
var addTo = addrTo ?? addressEnd;
var oldFrom = addressStart;
var oldUnderlying = underlyingBytes.ToArray();
underlyingBytes = new byte[(addTo + 1 - addFrom) * 2];
//copy old bytes to new array
var offset = (int)(oldFrom - addFrom) * 2;
oldUnderlying.CopyTo(oldUnderlying, offset);
addressStart = addFrom;
addressEnd = addTo;
}
public async Task<bool> ReadRegisterAsync (BaseRegister reg) {
return await RequestByteReadAsync(reg.MemoryAddress, reg.MemoryAddress + reg.GetRegisterAddressLen() - 1);
}
public async Task<bool> WriteRegisterAsync (BaseRegister reg, byte[] bytes) {
return await RequestByteWriteAsync(reg.MemoryAddress, bytes);
}
internal async Task<bool> RequestByteReadAsync (ulong addStart, ulong addEnd) {
var station = mewInterface.GetStationNumber();
string requeststring = $"%{station}#RD{GetMewtocolIdent(addStart, addEnd)}";
var result = await mewInterface.SendCommandAsync(requeststring);
if(result.Success) {
var resBytes = result.Response.ParseDTRawStringAsBytes();
SetUnderlyingBytes(resBytes, addStart);
}
return result.Success;
}
internal async Task<bool> RequestByteWriteAsync(ulong addStart, byte[] bytes) {
var station = mewInterface.GetStationNumber();
var addEnd = addStart + ((ulong)bytes.Length / 2) - 1;
string requeststring = $"%{station}#WD{GetMewtocolIdent(addStart, addEnd)}{bytes.ToHexString()}";
var result = await mewInterface.SendCommandAsync(requeststring);
if (result.Success) {
SetUnderlyingBytes(bytes, addStart);
}
return result.Success;
}
public byte[] GetUnderlyingBytes(BaseRegister reg) {
int byteLen = (int)(reg.GetRegisterAddressLen() * 2);
return GetUnderlyingBytes(reg.MemoryAddress, byteLen);
}
internal byte[] GetUnderlyingBytes (uint addStart, int addLen) {
int byteLen = (int)(addLen * 2);
int copyOffset = (int)((addStart - addressStart) * 2);
var gotBytes = underlyingBytes.Skip(copyOffset).Take(byteLen).ToArray();
return gotBytes;
}
public void SetUnderlyingBytes(BaseRegister reg, byte[] bytes) {
SetUnderlyingBytes(bytes, reg.MemoryAddress);
}
private void SetUnderlyingBytes(byte[] bytes, ulong addStart) {
int copyOffset = (int)((addStart - addressStart) * 2);
bytes.CopyTo(underlyingBytes, copyOffset);
}
private string GetMewtocolIdent () {
StringBuilder asciistring = new StringBuilder("D");
asciistring.Append(AddressStart.ToString().PadLeft(5, '0'));
asciistring.Append(AddressEnd.ToString().PadLeft(5, '0'));
return asciistring.ToString();
}
private string GetMewtocolIdent(ulong addStart, ulong addEnd) {
StringBuilder asciistring = new StringBuilder("D");
asciistring.Append(addStart.ToString().PadLeft(5, '0'));
asciistring.Append(addEnd.ToString().PadLeft(5, '0'));
return asciistring.ToString();
}
public override string ToString() => $"DT{AddressStart}-{AddressEnd}";
}
}

View File

@@ -0,0 +1,16 @@
using MewtocolNet.Registers;
using System.Threading.Tasks;
namespace MewtocolNet.UnderlyingRegisters {
internal interface IMemoryArea {
byte[] GetUnderlyingBytes(BaseRegister reg);
Task<bool> ReadRegisterAsync(BaseRegister reg);
Task<bool> WriteRegisterAsync(BaseRegister reg, byte[] bytes);
}
}

View File

@@ -0,0 +1,318 @@
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MewtocolNet.UnderlyingRegisters {
internal class MemoryAreaManager {
internal int maxOptimizationDistance = 8;
internal int maxRegistersPerGroup = -1;
internal MewtocolInterface mewInterface;
// WR areas are n of words, each word has 2 bytes representing the "special address component"
//X WR
internal List<WRArea> externalRelayInAreas;
//Y WR
internal List<WRArea> externalRelayOutAreas;
//R WR
internal List<WRArea> internalRelayAreas;
//DT
internal List<DTArea> dataAreas;
internal MemoryAreaManager (MewtocolInterface mewIf, int wrSize = 512, int dtSize = 32_765) {
mewInterface = mewIf;
Setup(wrSize, dtSize);
}
// Later on pass memory area sizes here
internal void Setup (int wrSize, int dtSize) {
externalRelayInAreas = new List<WRArea>(wrSize * 16);
externalRelayOutAreas = new List<WRArea>(wrSize * 16);
internalRelayAreas = new List<WRArea>(wrSize * 16);
dataAreas = new List<DTArea>(dtSize);
}
internal bool LinkRegister (BaseRegister reg) {
switch (reg.RegisterType) {
case RegisterType.X:
return AddWRArea(reg, externalRelayInAreas);
case RegisterType.Y:
return AddWRArea(reg, externalRelayOutAreas);
case RegisterType.R:
return AddWRArea(reg, internalRelayAreas);
case RegisterType.DT:
case RegisterType.DDT:
case RegisterType.DT_BYTE_RANGE:
return AddDTArea(reg);
}
return false;
}
private bool AddWRArea (BaseRegister insertReg, List<WRArea> collection) {
WRArea area = collection.FirstOrDefault(x => x.AddressStart == insertReg.MemoryAddress);
if(area != null) {
var existingLinkedRegister = area.linkedRegisters
.FirstOrDefault(x => x.CompareIsDuplicate(insertReg));
if(existingLinkedRegister != null) {
foreach (var prop in insertReg.boundToProps)
existingLinkedRegister.WithBoundProperty(prop);
return false;
} else {
insertReg.underlyingMemory = area;
area.linkedRegisters.Add(insertReg);
return true;
}
} else {
area = new WRArea(mewInterface) {
registerType = insertReg.RegisterType,
addressStart = insertReg.MemoryAddress,
};
insertReg.underlyingMemory = area;
area.linkedRegisters.Add(insertReg);
collection.Add(area);
collection = collection.OrderBy(x => x.AddressStart).ToList();
return true;
}
}
private bool AddDTArea (BaseRegister insertReg) {
uint regInsAddStart = insertReg.MemoryAddress;
uint regInsAddEnd = insertReg.MemoryAddress + insertReg.GetRegisterAddressLen() - 1;
DTArea targetArea = null;
foreach (var dtArea in dataAreas) {
bool matchingAddress = regInsAddStart >= dtArea.AddressStart &&
regInsAddEnd <= dtArea.addressEnd;
//found matching
if (matchingAddress) {
//check if the area has registers linked that are overlapping (not matching)
var foundDupe = dtArea.linkedRegisters.FirstOrDefault(x => x.CompareIsDuplicateNonCast(insertReg));
if(foundDupe != null) {
throw new NotSupportedException(
message: $"Can't have registers of different types at the same referenced plc address: " +
$"{insertReg.PLCAddressName} ({insertReg.GetType()}) <=> " +
$"{foundDupe.PLCAddressName} ({foundDupe.GetType()})"
);
}
targetArea = dtArea;
break;
}
//found adjacent before
if(dtArea.AddressEnd <= regInsAddStart) {
ulong distance = regInsAddStart - dtArea.AddressEnd;
if (distance <= (uint)maxOptimizationDistance) {
//expand the boundaries for the area to include the new adjacent area
dtArea.BoundaryUdpdate(addrTo: regInsAddEnd);
targetArea = dtArea;
break;
}
}
//found adjacent after
if (dtArea.AddressStart >= regInsAddEnd) {
ulong distance = dtArea.AddressStart - regInsAddEnd;
if (distance <= (uint)maxOptimizationDistance) {
//expand the boundaries for the area to include the new adjacent area
dtArea.BoundaryUdpdate(addrFrom: regInsAddStart);
targetArea = dtArea;
break;
}
}
}
if (targetArea == null) {
targetArea = new DTArea(mewInterface) {
addressStart = regInsAddStart,
addressEnd = regInsAddEnd,
registerType = insertReg.RegisterType,
};
targetArea.BoundaryUdpdate();
dataAreas.Add(targetArea);
}
insertReg.underlyingMemory = targetArea;
var existingLinkedRegister = targetArea.linkedRegisters
.FirstOrDefault(x => x.CompareIsDuplicate(insertReg));
if (existingLinkedRegister != null) {
foreach (var prop in insertReg.boundToProps)
existingLinkedRegister.WithBoundProperty(prop);
return false;
} else {
targetArea.linkedRegisters.Add(insertReg);
return true;
}
}
internal async Task PollAllAreasAsync () {
foreach (var dtArea in dataAreas) {
//set the whole memory area at once
var res = await dtArea.RequestByteReadAsync(dtArea.AddressStart, dtArea.AddressEnd);
foreach (var register in dtArea.linkedRegisters) {
var regStart = register.MemoryAddress;
var addLen = (int)register.GetRegisterAddressLen();
var bytes = dtArea.GetUnderlyingBytes(regStart, addLen);
register.SetValueFromBytes(bytes);
}
}
}
internal void Merge () {
//merge gaps that the algorithm didn't catch be rerunning the register attachment
var allDataAreaRegisters = dataAreas.SelectMany(x => x.linkedRegisters).ToList();
dataAreas = new List<DTArea>(allDataAreaRegisters.Capacity);
foreach (var reg in allDataAreaRegisters)
AddDTArea(reg);
}
internal string ExplainLayout () {
var sb = new StringBuilder();
sb.AppendLine("---- DT Area ----");
sb.AppendLine($"Optimization distance: {maxOptimizationDistance}");
foreach (var area in dataAreas) {
sb.AppendLine();
sb.AppendLine($"=> {area} = {area.underlyingBytes.Length} bytes");
sb.AppendLine();
sb.AppendLine(string.Join("\n", area.underlyingBytes.ToHexString(" ").SplitInParts(3 * 8)));
sb.AppendLine();
foreach (var reg in area.linkedRegisters) {
sb.AppendLine($"{reg.ToString(true)}");
}
}
sb.AppendLine("---- WR X Area ----");
foreach (var area in externalRelayInAreas) {
sb.AppendLine(area.ToString());
foreach (var reg in area.linkedRegisters) {
sb.AppendLine($"{reg.ToString(true)}");
}
}
sb.AppendLine("---- WR Y Area ----");
foreach (var area in externalRelayOutAreas) {
sb.AppendLine(area.ToString());
foreach (var reg in area.linkedRegisters) {
sb.AppendLine($"{reg.ToString(true)}");
}
}
sb.AppendLine("---- WR R Area ----");
foreach (var area in internalRelayAreas) {
sb.AppendLine(area.ToString());
foreach (var reg in area.linkedRegisters) {
sb.AppendLine($"{reg.ToString(true)}");
}
}
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,83 @@
using MewtocolNet.Registers;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace MewtocolNet.UnderlyingRegisters {
public class WRArea : IMemoryArea {
private MewtocolInterface mewInterface;
internal RegisterType registerType;
internal ulong addressStart;
internal byte[] wordData = new byte[2];
internal List<BaseRegister> linkedRegisters = new List<BaseRegister>();
public ulong AddressStart => addressStart;
internal WRArea(MewtocolInterface mewIf) {
mewInterface = mewIf;
}
public byte[] GetUnderlyingBytes(BaseRegister reg) {
return null;
}
public async Task<bool> ReadRegisterAsync(BaseRegister reg) {
return true;
}
public async Task<bool> WriteRegisterAsync(BaseRegister reg, byte[] bytes) {
return true;
}
public string GetMewtocolIdent() => GetMewtocolIdentsAllBits();
public string GetMewtocolIdentsAllBits () {
StringBuilder asciistring = new StringBuilder();
for (byte i = 0; i < 16; i++) {
asciistring.Append(GetMewtocolIdentSingleBit(i));
}
return asciistring.ToString();
}
public string GetMewtocolIdentSingleBit (byte specialAddress) {
//(R|X|Y)(area add [3] + special add [1])
StringBuilder asciistring = new StringBuilder();
string prefix = registerType.ToString();
string mem = AddressStart.ToString();
string sp = specialAddress.ToString("X1");
asciistring.Append(prefix);
asciistring.Append(mem.PadLeft(3, '0'));
asciistring.Append(sp);
return asciistring.ToString();
}
public override string ToString() => $"{registerType}{AddressStart} 0-F";
}
}

View File

@@ -14,10 +14,10 @@ namespace MewtocolTests {
this.output = output; this.output = output;
} }
private void Test(IRegisterInternal reg, string propName, uint expectAddr, string expectPlcName) { private void Test(IRegisterInternal reg, uint expectAddr, string expectPlcName) {
Assert.NotNull(reg); Assert.NotNull(reg);
Assert.Equal(propName, reg.Name); Assert.StartsWith("auto_prop_register_", reg.Name);
Assert.Null(reg.Value); Assert.Null(reg.Value);
Assert.Equal(expectAddr, reg.MemoryAddress); Assert.Equal(expectAddr, reg.MemoryAddress);
@@ -32,83 +32,103 @@ namespace MewtocolTests {
[Fact(DisplayName = "Boolean generation")] [Fact(DisplayName = "Boolean generation")]
public void BooleanGen() { public void BooleanGen() {
var interf = Mewtocol.Ethernet("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1")
interf.AddRegisterCollection(new TestBoolRegisters()); .WithRegisterCollections(x =>
x.AddCollection(new TestBoolRegisters())
).Build();
var register1 = interf.GetRegister(nameof(TestBoolRegisters.RType)); output.WriteLine(((MewtocolInterface)interf).memoryManager.ExplainLayout());
var register2 = interf.GetRegister(nameof(TestBoolRegisters.XType));
var register3 = interf.GetRegister(nameof(TestBoolRegisters.RType_MewString)); var register1 = interf.GetRegister("auto_prop_register_1");
var register2 = interf.GetRegister("auto_prop_register_2");
var register3 = interf.GetRegister("auto_prop_register_3");
Test((IRegisterInternal)register1, nameof(TestBoolRegisters.RType), 85, "R85A"); Test((IRegisterInternal)register1, 0, "XD");
Test((IRegisterInternal)register2, nameof(TestBoolRegisters.XType), 0, "XD"); Test((IRegisterInternal)register2, 85, "R85A");
Test((IRegisterInternal)register3, 85, "R85B");
Test((IRegisterInternal)register3, nameof(TestBoolRegisters.RType_MewString), 85, "R85B");
} }
[Fact(DisplayName = "Number 16 bit generation")] [Fact(DisplayName = "Number 16 bit generation")]
public void N16BitGen () { public void N16BitGen () {
var interf = Mewtocol.Ethernet("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1")
interf.AddRegisterCollection(new Nums16Bit()); .WithRegisterCollections(x =>
x.AddCollection(new Nums16Bit())
).Build();
var register1 = interf.GetRegister(nameof(Nums16Bit.Int16Type)); var register1 = interf.GetRegister("auto_prop_register_1");
var register2 = interf.GetRegister(nameof(Nums16Bit.UInt16Type)); var register2 = interf.GetRegister("auto_prop_register_2");
var register3 = interf.GetRegister(nameof(Nums16Bit.Enum16Type)); var register3 = interf.GetRegister("auto_prop_register_3");
var register4 = interf.GetRegister(nameof(Nums16Bit.Int16Type_MewString));
var register5 = interf.GetRegister(nameof(Nums16Bit.Enum16Type_MewString));
//test generic properties //test generic properties
Test((IRegisterInternal)register1, nameof(Nums16Bit.Int16Type), 899, "DT899"); Test((IRegisterInternal)register1, 50, "DT50");
Test((IRegisterInternal)register2, nameof(Nums16Bit.UInt16Type), 342, "DT342"); Test((IRegisterInternal)register2, 342, "DT342");
Test((IRegisterInternal)register3, nameof(Nums16Bit.Enum16Type), 50, "DT50"); Test((IRegisterInternal)register3, 899, "DT899");
Test((IRegisterInternal)register4, nameof(Nums16Bit.Int16Type_MewString), 900, "DT900");
Test((IRegisterInternal)register5, nameof(Nums16Bit.Enum16Type_MewString), 51, "DT51");
} }
[Fact(DisplayName = "Number 32 bit generation")] [Fact(DisplayName = "Number 32 bit generation")]
public void N32BitGen () { public void N32BitGen () {
var interf = Mewtocol.Ethernet("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1")
interf.AddRegisterCollection(new Nums32Bit()); .WithRegisterCollections(x => x
.AddCollection(new Nums32Bit())
).Build();
var register1 = interf.GetRegister(nameof(Nums32Bit.Int32Type)); output.WriteLine(((MewtocolInterface)interf).memoryManager.ExplainLayout());
var register2 = interf.GetRegister(nameof(Nums32Bit.UInt32Type));
var register3 = interf.GetRegister(nameof(Nums32Bit.Enum32Type));
var register4 = interf.GetRegister(nameof(Nums32Bit.FloatType));
var register5 = interf.GetRegister(nameof(Nums32Bit.TimeSpanType));
var register6 = interf.GetRegister(nameof(Nums32Bit.Enum32Type_MewString)); var register1 = interf.GetRegister("auto_prop_register_1");
var register7 = interf.GetRegister(nameof(Nums32Bit.TimeSpanType_MewString)); var register2 = interf.GetRegister("auto_prop_register_2");
var register3 = interf.GetRegister("auto_prop_register_3");
//only one generated because same type
var register4 = interf.GetRegister("auto_prop_register_4");
var register6 = interf.GetRegister("auto_prop_register_5");
var register7 = interf.GetRegister("auto_prop_register_6");
//test generic properties //test generic properties
Test((IRegisterInternal)register1, nameof(Nums32Bit.Int32Type), 7001, "DDT7001"); Test((IRegisterInternal)register1, 7000, "DDT7000");
Test((IRegisterInternal)register2, nameof(Nums32Bit.UInt32Type), 765, "DDT765"); Test((IRegisterInternal)register2, 7002, "DDT7002");
Test((IRegisterInternal)register3, nameof(Nums32Bit.Enum32Type), 51, "DDT51"); Test((IRegisterInternal)register3, 7004, "DDT7004");
Test((IRegisterInternal)register4, nameof(Nums32Bit.FloatType), 7003, "DDT7003");
Test((IRegisterInternal)register5, nameof(Nums32Bit.TimeSpanType), 7012, "DDT7012");
Test((IRegisterInternal)register6, nameof(Nums32Bit.Enum32Type_MewString), 53, "DDT53"); Test((IRegisterInternal)register4, 7006, "DDT7006");
Test((IRegisterInternal)register7, nameof(Nums32Bit.TimeSpanType_MewString), 7014, "DDT7014");
Test((IRegisterInternal)register6, 7008, "DDT7008");
Test((IRegisterInternal)register7, 7010, "DDT7010");
} }
[Fact(DisplayName = "String generation")] [Fact(DisplayName = "String generation")]
public void StringGen() { public void StringGen() {
var interf = Mewtocol.Ethernet("192.168.0.1"); var interf = Mewtocol.Ethernet("192.168.0.1")
interf.AddRegisterCollection(new TestStringRegisters()); .WithRegisterCollections(x =>
x.AddCollection(new TestStringRegisters())
).Build();
var register1 = interf.GetRegister(nameof(TestStringRegisters.StringType)); var register1 = interf.GetRegister("auto_prop_register_1");
var register2 = interf.GetRegister(nameof(TestStringRegisters.StringType_MewString));
//test generic properties //test generic properties
Test((IRegisterInternal)register1, nameof(TestStringRegisters.StringType), 7005, "DT7005"); Test((IRegisterInternal)register1, 7005, "DT7005");
Test((IRegisterInternal)register2, nameof(TestStringRegisters.StringType_MewString), 7050, "DT7050");
}
[Fact(DisplayName = "Byte Array generation")]
public void ByteArrGen() {
var interf = Mewtocol.Ethernet("192.168.0.1")
.WithRegisterCollections(x =>
x.AddCollection(new TestBitwiseRegisters())
).Build();
var register1 = interf.GetRegister("auto_prop_register_1");
//var register2 = interf.GetRegister("auto_prop_register_2");
//test generic properties
Test((IRegisterInternal)register1, 7000, "DT7000");
//Test((IRegisterInternal)register2, 7001, "DT7001");
} }

View File

@@ -30,10 +30,10 @@ namespace MewtocolTests.EncapsulatedTests {
public class TestBoolRegisters : RegisterCollection { public class TestBoolRegisters : RegisterCollection {
[Register(IOType.R, memoryArea: 85, spAdress: 0xA)] [Register("R85A")]
public bool RType { get; set; } public bool RType { get; set; }
[Register(IOType.X, (byte)0xD)] [Register("XD")]
public bool XType { get; set; } public bool XType { get; set; }
[Register("R85B")] [Register("R85B")]
@@ -44,71 +44,62 @@ namespace MewtocolTests.EncapsulatedTests {
public class Nums16Bit : RegisterCollection { public class Nums16Bit : RegisterCollection {
[Register(899)] [Register("DT899")]
public short Int16Type { get; set; } public short Int16Type { get; set; }
[Register(342)] [Register("DT342")]
public ushort UInt16Type { get; set; } public ushort UInt16Type { get; set; }
[Register(50)] [Register("DT50")]
public CurrentState Enum16Type { get; set; } public CurrentState Enum16Type { get; set; }
[Register("DT900")]
public short Int16Type_MewString { get; set; }
[Register("DT51")]
public CurrentState Enum16Type_MewString { get; set; }
} }
public class Nums32Bit : RegisterCollection { public class Nums32Bit : RegisterCollection {
[Register(7001)] [Register("DDT7000")]
public int Int32Type { get; set; } public int Int32Type { get; set; }
[Register(765)] [Register("DDT7002")]
public uint UInt32Type { get; set; } public uint UInt32Type { get; set; }
[Register(51)] [Register("DDT7004")]
public CurrentState32 Enum32Type { get; set; } public CurrentState32 Enum32Type { get; set; }
[Register(7003)] [Register("DDT7006")]
public float FloatType { get; set; } public float FloatType { get; set; }
[Register(7012)] [Register("DDT7006")]
public float FloatType2 { get; set; } // this is legal, because the cast type is the same
//[Register("DDT7006")]
//public int FloatType3 { get; set; } // this is not legal
[Register("DDT7010")]
public TimeSpan TimeSpanType { get; set; } public TimeSpan TimeSpanType { get; set; }
[Register("DDT53")] [Register("DDT7008")]
public CurrentState32 Enum32Type_MewString { get; set; } public TimeSpan TimeSpanType2 { get; set; }
[Register("DDT7014")] [Register("DDT7013")]
public TimeSpan TimeSpanType_MewString { get; set; } public TimeSpan TimeSpanType3 { get; set; }
} }
public class TestStringRegisters : RegisterCollection { public class TestStringRegisters : RegisterCollection {
[Register(7005, 5)] [Register("DT7005")]
public string? StringType { get; set; } public string? StringType { get; set; }
[Register("DT7050")]
public string? StringType_MewString { get; set; }
} }
public class TestBitwiseRegisters : RegisterCollection { public class TestBitwiseRegisters : RegisterCollection {
[Register(7010)] [Register("DT7000")]
public BitArray TestBitRegister { get; set; } public BitArray BitArr16 { get; set; }
[Register(8010, BitCount.B32)] //[Register("DT7001")]
public BitArray TestBitRegister32 { get; set; } //public BitArray BitArr32 { get; set; }
[Register(1204, BitCount.B16, 9)]
public bool BitValue { get; set; }
[Register(1204, BitCount.B32, 5)]
public bool FillTest { get; set; }
} }

View File

@@ -103,7 +103,7 @@ namespace MewtocolTests
output.WriteLine($"Testing: {plc.PLCName}"); output.WriteLine($"Testing: {plc.PLCName}");
var cycleClient = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort); var cycleClient = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort).Build();
await cycleClient.ConnectAsync(); await cycleClient.ConnectAsync();
@@ -124,7 +124,7 @@ namespace MewtocolTests
output.WriteLine($"Testing: {plc.PLCName}\n"); output.WriteLine($"Testing: {plc.PLCName}\n");
var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort); var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort).Build();
await client.ConnectAsync(); await client.ConnectAsync();
@@ -155,7 +155,7 @@ namespace MewtocolTests
output.WriteLine($"\n\n --- Testing: {plc.PLCName} ---\n"); output.WriteLine($"\n\n --- Testing: {plc.PLCName} ---\n");
var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort); var client = Mewtocol.Ethernet(plc.PLCIP, plc.PLCPort).Build();
foreach (var testRW in testRegisterRW) { foreach (var testRW in testRegisterRW) {