mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 11:11:23 +00:00
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:
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|
||||||
|
|||||||
@@ -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/>
|
||||||
|
|||||||
20
MewtocolNet/RegisterAttributes/PollFrequencyAttribute.cs
Normal file
20
MewtocolNet/RegisterAttributes/PollFrequencyAttribute.cs
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
26
MewtocolNet/RegisterAttributes/RegisterPropTarget.cs
Normal file
26
MewtocolNet/RegisterAttributes/RegisterPropTarget.cs
Normal 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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
152
MewtocolNet/UnderlyingRegisters/DTArea.cs
Normal file
152
MewtocolNet/UnderlyingRegisters/DTArea.cs
Normal 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}";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
MewtocolNet/UnderlyingRegisters/IMemoryArea.cs
Normal file
16
MewtocolNet/UnderlyingRegisters/IMemoryArea.cs
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
318
MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
Normal file
318
MewtocolNet/UnderlyingRegisters/MemoryAreaManager.cs
Normal 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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
83
MewtocolNet/UnderlyingRegisters/WRArea.cs
Normal file
83
MewtocolNet/UnderlyingRegisters/WRArea.cs
Normal 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";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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; }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user