mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Multiple fixes
This commit is contained in:
13
MewtocolNet/Events/PlcConnectionArgs.cs
Normal file
13
MewtocolNet/Events/PlcConnectionArgs.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MewtocolNet.Events {
|
||||||
|
|
||||||
|
public delegate void PlcConnectionEventHandler(object sender, PlcConnectionArgs e);
|
||||||
|
|
||||||
|
public class PlcConnectionArgs : EventArgs {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
22
MewtocolNet/Events/RegisterChanged.cs
Normal file
22
MewtocolNet/Events/RegisterChanged.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using MewtocolNet.Registers;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MewtocolNet.Events {
|
||||||
|
|
||||||
|
public delegate void RegisterChangedEventHandler(object sender, RegisterChangedArgs e);
|
||||||
|
|
||||||
|
public class RegisterChangedArgs : EventArgs {
|
||||||
|
|
||||||
|
public IRegister Register { get; internal set; }
|
||||||
|
|
||||||
|
public object Value { get; internal set; }
|
||||||
|
|
||||||
|
public object PreviousValue { get; internal set; }
|
||||||
|
|
||||||
|
public string PreviousValueString { get; internal set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using MewtocolNet.Registers;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MewtocolNet.Exceptions {
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class MewtocolException : Exception {
|
|
||||||
|
|
||||||
public MewtocolException() { }
|
|
||||||
|
|
||||||
public MewtocolException(string message) : base(message) { }
|
|
||||||
|
|
||||||
public MewtocolException(string message, Exception inner) : base(message, inner) { }
|
|
||||||
|
|
||||||
protected MewtocolException(
|
|
||||||
System.Runtime.Serialization.SerializationInfo info,
|
|
||||||
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
|
|
||||||
|
|
||||||
internal static MewtocolException NotConnectedSend() {
|
|
||||||
|
|
||||||
return new MewtocolException($"Can not send a message to the PLC if it isn't connected");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static MewtocolException DupeRegister(Register register) {
|
|
||||||
|
|
||||||
return new MewtocolException($"The mewtocol interface already contains this register: {register.GetMewName()}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static MewtocolException DupeNameRegister(Register register) {
|
|
||||||
|
|
||||||
return new MewtocolException($"The mewtocol interface registers already contains a register with the name: {register.GetMewName()}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static MewtocolException OverlappingRegister(Register registerA, Register registerB) {
|
|
||||||
|
|
||||||
return new MewtocolException($"The register: {registerA.GetRegisterWordRangeString()} " +
|
|
||||||
$"has overlapping addresses with: {registerB.GetRegisterWordRangeString()}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -19,13 +19,15 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
#region Byte and string operation helpers
|
#region Byte and string operation helpers
|
||||||
|
|
||||||
public static int DetermineTypeByteSize(this Type type) {
|
public static int DetermineTypeByteIntialSize(this Type type) {
|
||||||
|
|
||||||
//enums can only be of numeric types
|
//enums can only be of numeric types
|
||||||
if (type.IsEnum) return Marshal.SizeOf(Enum.GetUnderlyingType(type));
|
if (type.IsEnum) return Marshal.SizeOf(Enum.GetUnderlyingType(type));
|
||||||
|
|
||||||
//strings get always set with 4 bytes because the first 4 bytes contain the length
|
//strings get always set with 4 bytes because the first 4 bytes contain the length
|
||||||
if (type == typeof(string)) return 4;
|
if (type == typeof(string)) return 4;
|
||||||
|
if (type == typeof(TimeSpan)) return 4;
|
||||||
|
if (type == typeof(DateTime)) return 4;
|
||||||
|
|
||||||
if (type.Namespace.StartsWith("System")) return Marshal.SizeOf(type);
|
if (type.Namespace.StartsWith("System")) return Marshal.SizeOf(type);
|
||||||
|
|
||||||
@@ -157,6 +159,15 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string Ellipsis(this string str, int maxLength) {
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(str) || str.Length <= maxLength)
|
||||||
|
return str;
|
||||||
|
|
||||||
|
return $"{str.Substring(0, maxLength - 3)}...";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a hex string (AB01C1) to a byte array
|
/// Converts a hex string (AB01C1) to a byte array
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MewtocolNet {
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
internal enum DynamicSizeState {
|
|
||||||
|
|
||||||
None = 0,
|
|
||||||
DynamicallySized = 1,
|
|
||||||
NeedsSizeUpdate = 2,
|
|
||||||
WasSizeUpdated = 4,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
using MewtocolNet.Exceptions;
|
using MewtocolNet.RegisterAttributes;
|
||||||
using MewtocolNet.RegisterAttributes;
|
using MewtocolNet.RegisterBuilding;
|
||||||
using MewtocolNet.SetupClasses;
|
using MewtocolNet.SetupClasses;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO.Ports;
|
using System.IO.Ports;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MewtocolNet {
|
namespace MewtocolNet {
|
||||||
|
|
||||||
@@ -96,7 +97,7 @@ namespace MewtocolNet {
|
|||||||
var portnames = SerialPort.GetPortNames();
|
var portnames = SerialPort.GetPortNames();
|
||||||
|
|
||||||
if (!portnames.Any(x => x == portName))
|
if (!portnames.Any(x => x == portName))
|
||||||
throw new MewtocolException($"The port {portName} is no valid port");
|
throw new NotSupportedException($"The port {portName} is no valid port");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +266,32 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A builder for attaching register collections
|
||||||
|
/// </summary>
|
||||||
|
public PostInit<T> WithRegisters(Action<RBuildMult> builder) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
var plc = (MewtocolInterface)(object)intf;
|
||||||
|
var assembler = new RegisterAssembler(plc);
|
||||||
|
var regBuilder = new RBuildMult(plc);
|
||||||
|
|
||||||
|
builder.Invoke(regBuilder);
|
||||||
|
|
||||||
|
var registers = assembler.AssembleAll(regBuilder);
|
||||||
|
plc.AddRegisters(registers.ToArray());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
throw;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds and returns the final plc interface
|
/// Builds and returns the final plc interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MewtocolNet.Helpers;
|
using MewtocolNet.Events;
|
||||||
|
using MewtocolNet.Helpers;
|
||||||
using MewtocolNet.Logging;
|
using MewtocolNet.Logging;
|
||||||
using MewtocolNet.Registers;
|
using MewtocolNet.Registers;
|
||||||
using MewtocolNet.UnderlyingRegisters;
|
using MewtocolNet.UnderlyingRegisters;
|
||||||
@@ -17,6 +18,22 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
public abstract partial class MewtocolInterface : IPlc {
|
public abstract partial class MewtocolInterface : IPlc {
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public event PlcConnectionEventHandler Connected;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public event PlcConnectionEventHandler Disconnected;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public event RegisterChangedEventHandler RegisterChanged;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Private fields
|
#region Private fields
|
||||||
|
|
||||||
private protected Stream stream;
|
private protected Stream stream;
|
||||||
@@ -40,6 +57,7 @@ namespace MewtocolNet {
|
|||||||
private protected Stopwatch speedStopwatchDownstr;
|
private protected Stopwatch speedStopwatchDownstr;
|
||||||
private protected Task firstPollTask = new Task(() => { });
|
private protected Task firstPollTask = new Task(() => { });
|
||||||
|
|
||||||
|
private protected bool wasInitialStatusReceived;
|
||||||
private protected MewtocolVersion mewtocolVersion;
|
private protected MewtocolVersion mewtocolVersion;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -56,18 +74,6 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
#region Public Read Only Properties / Fields
|
#region Public Read Only Properties / Fields
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event Action<PLCInfo> Connected;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event Action Disconnected;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event Action<IRegister> RegisterChanged;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Disposed { get; private set; }
|
public bool Disposed { get; private set; }
|
||||||
|
|
||||||
@@ -143,7 +149,7 @@ namespace MewtocolNet {
|
|||||||
Connected += MewtocolInterface_Connected;
|
Connected += MewtocolInterface_Connected;
|
||||||
RegisterChanged += OnRegisterChanged;
|
RegisterChanged += OnRegisterChanged;
|
||||||
|
|
||||||
void MewtocolInterface_Connected(PLCInfo obj) {
|
void MewtocolInterface_Connected(object sender, PlcConnectionArgs args) {
|
||||||
|
|
||||||
if (usePoller)
|
if (usePoller)
|
||||||
AttachPoller();
|
AttachPoller();
|
||||||
@@ -154,29 +160,48 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRegisterChanged(IRegister o) {
|
private void OnRegisterChanged(object sender, RegisterChangedArgs args) {
|
||||||
|
|
||||||
var asInternal = (Register)o;
|
var asInternal = (Register)args.Register;
|
||||||
|
|
||||||
|
//log
|
||||||
|
if(IsConnected) {
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.Append(asInternal.GetMewName());
|
||||||
|
if (asInternal.Name != null) {
|
||||||
|
sb.Append(asInternal.autoGenerated ? $" (Auto)" : $" ({asInternal.Name})");
|
||||||
|
}
|
||||||
|
sb.Append($" {asInternal.underlyingSystemType.Name}");
|
||||||
|
sb.Append($" changed \"{args.PreviousValueString.Ellipsis(25)}\"" +
|
||||||
|
$" => \"{asInternal.GetValueString().Ellipsis(75)}\"");
|
||||||
|
|
||||||
|
Logger.Log(sb.ToString(), LogLevel.Change, this);
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
sb.Append(asInternal.GetMewName());
|
|
||||||
if (asInternal.Name != null) {
|
|
||||||
sb.Append(asInternal.autoGenerated ? $" (Auto)" : $" ({o.Name})");
|
|
||||||
}
|
}
|
||||||
sb.Append($" {asInternal.underlyingSystemType.Name}");
|
|
||||||
sb.Append($" changed to \"{asInternal.GetValueString()}\"");
|
|
||||||
|
|
||||||
Logger.Log(sb.ToString(), LogLevel.Change, this);
|
OnRegisterChangedUpdateProps(asInternal);
|
||||||
|
|
||||||
OnRegisterChangedUpdateProps((Register)o);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task ConnectAsync() {
|
public virtual async Task ConnectAsync() {
|
||||||
|
|
||||||
|
isConnectingStage = false;
|
||||||
|
|
||||||
await memoryManager.OnPlcConnected();
|
await memoryManager.OnPlcConnected();
|
||||||
|
|
||||||
|
Logger.Log($"PLC: {PlcInfo.TypeName}", LogLevel.Verbose, this);
|
||||||
|
Logger.Log($"TYPE CODE: {PlcInfo.TypeCode.ToString("X")}", LogLevel.Verbose, this);
|
||||||
|
Logger.Log($"OP MODE: {PlcInfo.OperationMode}", LogLevel.Verbose, this);
|
||||||
|
Logger.Log($"PROG CAP: {PlcInfo.ProgramCapacity}k", LogLevel.Verbose, this);
|
||||||
|
Logger.Log($"HW INFO: {PlcInfo.HardwareInformation}", LogLevel.Verbose, this);
|
||||||
|
Logger.Log($"DIAG ERR: {PlcInfo.SelfDiagnosticError}", LogLevel.Verbose, this);
|
||||||
|
Logger.Log($"CPU VER: {PlcInfo.CpuVersion}", LogLevel.Verbose, this);
|
||||||
|
|
||||||
|
Logger.Log($">> Intial connection end <<", LogLevel.Verbose, this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -220,6 +245,9 @@ namespace MewtocolNet {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<MewtocolFrameResponse> SendCommandAsync(string _msg, bool withTerminator = true, int timeoutMs = -1, Action<double> onReceiveProgress = null) {
|
public async Task<MewtocolFrameResponse> SendCommandAsync(string _msg, bool withTerminator = true, int timeoutMs = -1, Action<double> onReceiveProgress = null) {
|
||||||
|
|
||||||
|
if (!IsConnected && !isConnectingStage)
|
||||||
|
throw new NotSupportedException("The device must be connected to send a message");
|
||||||
|
|
||||||
//send request
|
//send request
|
||||||
queuedMessages++;
|
queuedMessages++;
|
||||||
|
|
||||||
@@ -468,11 +496,10 @@ namespace MewtocolNet {
|
|||||||
private protected virtual void OnConnected(PLCInfo plcinf) {
|
private protected virtual void OnConnected(PLCInfo plcinf) {
|
||||||
|
|
||||||
Logger.Log("Connected to PLC", LogLevel.Info, this);
|
Logger.Log("Connected to PLC", LogLevel.Info, this);
|
||||||
Logger.Log($"{plcinf.ToString()}", LogLevel.Verbose, this);
|
|
||||||
|
|
||||||
IsConnected = true;
|
IsConnected = true;
|
||||||
|
|
||||||
Connected?.Invoke(plcinf);
|
Connected?.Invoke(this, new PlcConnectionArgs());
|
||||||
|
|
||||||
if (!usePoller) {
|
if (!usePoller) {
|
||||||
firstPollTask.RunSynchronously();
|
firstPollTask.RunSynchronously();
|
||||||
@@ -493,11 +520,12 @@ namespace MewtocolNet {
|
|||||||
BytesPerSecondDownstream = 0;
|
BytesPerSecondDownstream = 0;
|
||||||
BytesPerSecondUpstream = 0;
|
BytesPerSecondUpstream = 0;
|
||||||
PollerCycleDurationMs = 0;
|
PollerCycleDurationMs = 0;
|
||||||
|
PlcInfo = null;
|
||||||
|
|
||||||
IsConnected = false;
|
IsConnected = false;
|
||||||
ClearRegisterVals();
|
ClearRegisterVals();
|
||||||
|
|
||||||
Disconnected?.Invoke();
|
Disconnected?.Invoke(this, new PlcConnectionArgs());
|
||||||
KillPoller();
|
KillPoller();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MewtocolNet.Logging;
|
using MewtocolNet.Events;
|
||||||
|
using MewtocolNet.Logging;
|
||||||
using MewtocolNet.RegisterAttributes;
|
using MewtocolNet.RegisterAttributes;
|
||||||
using MewtocolNet.RegisterBuilding;
|
using MewtocolNet.RegisterBuilding;
|
||||||
using MewtocolNet.Registers;
|
using MewtocolNet.Registers;
|
||||||
@@ -210,19 +211,19 @@ namespace MewtocolNet {
|
|||||||
if (attr is RegisterAttribute cAttribute) {
|
if (attr is RegisterAttribute cAttribute) {
|
||||||
|
|
||||||
var pollFreqAttr = (PollLevelAttribute)attributes.FirstOrDefault(x => x.GetType() == typeof(PollLevelAttribute));
|
var pollFreqAttr = (PollLevelAttribute)attributes.FirstOrDefault(x => x.GetType() == typeof(PollLevelAttribute));
|
||||||
|
var stringHintAttr = (StringHintAttribute)attributes.FirstOrDefault(x => x.GetType() == typeof(StringHintAttribute));
|
||||||
|
|
||||||
var dotnetType = prop.PropertyType;
|
var dotnetType = prop.PropertyType;
|
||||||
int pollLevel = 1;
|
int pollLevel = 1;
|
||||||
|
uint? byteHint = (uint?)stringHintAttr?.size;
|
||||||
|
|
||||||
if (pollFreqAttr != null) pollLevel = pollFreqAttr.pollLevel;
|
if (pollFreqAttr != null) pollLevel = pollFreqAttr.pollLevel;
|
||||||
|
|
||||||
//add builder item
|
//add builder item
|
||||||
var stp1 = regBuild
|
var stp1 = regBuild
|
||||||
.AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef)
|
.AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef, collection, prop, byteHint)
|
||||||
.AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType)
|
.AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType)
|
||||||
.PollLevel(pollLevel)
|
.PollLevel(pollLevel);
|
||||||
.RegCollection(collection)
|
|
||||||
.BoundProp(prop);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +236,7 @@ namespace MewtocolNet {
|
|||||||
collection.OnInterfaceLinked(this);
|
collection.OnInterfaceLinked(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Connected += (i) => {
|
Connected += (s,e) => {
|
||||||
if (collection != null)
|
if (collection != null)
|
||||||
collection.OnInterfaceLinkedAndOnline(this);
|
collection.OnInterfaceLinkedAndOnline(this);
|
||||||
};
|
};
|
||||||
@@ -280,6 +281,9 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
memoryManager.LinkAndMergeRegisters(registers);
|
memoryManager.LinkAndMergeRegisters(registers);
|
||||||
|
|
||||||
|
//run a second iteration
|
||||||
|
//memoryManager.LinkAndMergeRegisters();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckDuplicateRegister(Register instance, out Register foundDupe) {
|
private bool CheckDuplicateRegister(Register instance, out Register foundDupe) {
|
||||||
@@ -383,9 +387,14 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InvokeRegisterChanged(Register reg) {
|
internal void InvokeRegisterChanged(Register reg, object preValue, string preValueString) {
|
||||||
|
|
||||||
RegisterChanged?.Invoke(reg);
|
RegisterChanged?.Invoke(this, new RegisterChangedArgs {
|
||||||
|
Register = reg,
|
||||||
|
PreviousValue = preValue,
|
||||||
|
PreviousValueString = preValueString,
|
||||||
|
Value = reg.Value,
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
using MewtocolNet.Exceptions;
|
|
||||||
using MewtocolNet.Logging;
|
using MewtocolNet.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MewtocolNet {
|
namespace MewtocolNet {
|
||||||
|
|
||||||
public abstract partial class MewtocolInterface {
|
public abstract partial class MewtocolInterface {
|
||||||
|
|
||||||
|
internal bool isConnectingStage = false;
|
||||||
|
|
||||||
internal int maxDataBlocksPerWrite = 8;
|
internal int maxDataBlocksPerWrite = 8;
|
||||||
|
|
||||||
#region PLC info getters
|
#region PLC info getters
|
||||||
@@ -17,20 +19,26 @@ namespace MewtocolNet {
|
|||||||
/// Gets generic information about the PLC
|
/// Gets generic information about the PLC
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A PLCInfo class</returns>
|
/// <returns>A PLCInfo class</returns>
|
||||||
public async Task<PLCInfo?> GetPLCInfoAsync(int timeout = -1) {
|
public async Task<PLCInfo> GetPLCInfoAsync(int timeout = -1) {
|
||||||
|
|
||||||
var resRT = await SendCommandAsync("%EE#RT", timeoutMs: timeout);
|
MewtocolFrameResponse resRT = await SendCommandAsync("%EE#RT", timeoutMs: timeout);
|
||||||
|
|
||||||
if (!resRT.Success) {
|
if (!resRT.Success) {
|
||||||
|
|
||||||
//timeouts are ok and dont throw
|
//timeouts are ok and don't throw
|
||||||
if (resRT == MewtocolFrameResponse.Timeout) return null;
|
if (resRT == MewtocolFrameResponse.Timeout) return null;
|
||||||
|
|
||||||
throw new MewtocolException(resRT.Error);
|
throw new Exception(resRT.Error);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var resEXRT = await SendCommandAsync("%EE#EX00RT00", timeoutMs: timeout);
|
MewtocolFrameResponse? resEXRT = null;
|
||||||
|
|
||||||
|
if(isConnectingStage) {
|
||||||
|
|
||||||
|
resEXRT = await SendCommandAsync("%EE#EX00RT00", timeoutMs: timeout);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//timeouts are ok and dont throw
|
//timeouts are ok and dont throw
|
||||||
if (!resRT.Success && resRT == MewtocolFrameResponse.Timeout) return null;
|
if (!resRT.Success && resRT == MewtocolFrameResponse.Timeout) return null;
|
||||||
@@ -40,20 +48,27 @@ namespace MewtocolNet {
|
|||||||
//dont overwrite, use first
|
//dont overwrite, use first
|
||||||
if (!PLCInfo.TryFromRT(resRT.Response, out plcInf)) {
|
if (!PLCInfo.TryFromRT(resRT.Response, out plcInf)) {
|
||||||
|
|
||||||
throw new MewtocolException("The RT message could not be parsed");
|
throw new Exception("The RT message could not be parsed");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//overwrite first with EXRT
|
//overwrite first with EXRT only on connecting stage
|
||||||
if (resEXRT.Success && !plcInf.TryExtendFromEXRT(resEXRT.Response)) {
|
if (isConnectingStage && resEXRT != null && resEXRT.Value.Success && !plcInf.TryExtendFromEXRT(resEXRT.Value.Response)) {
|
||||||
|
|
||||||
throw new MewtocolException("The EXRT message could not be parsed");
|
throw new Exception("The EXRT message could not be parsed");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlcInfo = plcInf;
|
if(isConnectingStage) {
|
||||||
|
//set the intial obj
|
||||||
|
PlcInfo = plcInf;
|
||||||
|
} else {
|
||||||
|
//update the obj with RT dynamic values only
|
||||||
|
PlcInfo.SelfDiagnosticError = plcInf.SelfDiagnosticError;
|
||||||
|
PlcInfo.OperationMode = plcInf.OperationMode;
|
||||||
|
}
|
||||||
|
|
||||||
return plcInf;
|
return PlcInfo;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,9 +108,6 @@ namespace MewtocolNet {
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> WriteByteRange(int start, byte[] byteArr) {
|
public async Task<bool> WriteByteRange(int start, byte[] byteArr) {
|
||||||
|
|
||||||
if (!IsConnected)
|
|
||||||
throw MewtocolException.NotConnectedSend();
|
|
||||||
|
|
||||||
string byteString = byteArr.ToHexString();
|
string byteString = byteArr.ToHexString();
|
||||||
|
|
||||||
var wordLength = byteArr.Length / 2;
|
var wordLength = byteArr.Length / 2;
|
||||||
@@ -121,11 +133,6 @@ namespace MewtocolNet {
|
|||||||
/// <returns>A byte array of the requested DT area</returns>
|
/// <returns>A byte array of the requested DT area</returns>
|
||||||
public async Task<byte[]> ReadByteRangeNonBlocking(int start, int byteCount, Action<double> onProgress = null) {
|
public async Task<byte[]> ReadByteRangeNonBlocking(int start, int byteCount, Action<double> onProgress = null) {
|
||||||
|
|
||||||
if (!IsConnected)
|
|
||||||
throw MewtocolException.NotConnectedSend();
|
|
||||||
|
|
||||||
onProgress += (p) => Console.WriteLine($"{p * 100:N2}%");
|
|
||||||
|
|
||||||
//on odd bytes add one word
|
//on odd bytes add one word
|
||||||
var wordLength = byteCount / 2;
|
var wordLength = byteCount / 2;
|
||||||
if (byteCount % 2 != 0) wordLength++;
|
if (byteCount % 2 != 0) wordLength++;
|
||||||
|
|||||||
@@ -113,7 +113,10 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
PLCInfo? gotInfo = null;
|
Logger.Log($">> Intial connection start <<", LogLevel.Verbose, this);
|
||||||
|
isConnectingStage = true;
|
||||||
|
|
||||||
|
PLCInfo gotInfo = null;
|
||||||
|
|
||||||
if (autoSerial) {
|
if (autoSerial) {
|
||||||
|
|
||||||
@@ -129,8 +132,9 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
if (gotInfo != null) {
|
if (gotInfo != null) {
|
||||||
|
|
||||||
|
IsConnected = true;
|
||||||
await base.ConnectAsync();
|
await base.ConnectAsync();
|
||||||
OnConnected(gotInfo.Value);
|
OnConnected(gotInfo);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -145,13 +149,15 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
OnMajorSocketExceptionWhileConnecting();
|
OnMajorSocketExceptionWhileConnecting();
|
||||||
|
|
||||||
|
isConnectingStage = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tryingSerialConfig -= OnTryConfig;
|
tryingSerialConfig -= OnTryConfig;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<PLCInfo?> TryConnectAsyncMulti() {
|
private async Task<PLCInfo> TryConnectAsyncMulti() {
|
||||||
|
|
||||||
var baudRates = Enum.GetValues(typeof(BaudRate)).Cast<BaudRate>();
|
var baudRates = Enum.GetValues(typeof(BaudRate)).Cast<BaudRate>();
|
||||||
|
|
||||||
@@ -197,7 +203,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<PLCInfo?> TryConnectAsyncSingle(string port, int baud, int dbits, Parity par, StopBits sbits) {
|
private async Task<PLCInfo> TryConnectAsyncSingle(string port, int baud, int dbits, Parity par, StopBits sbits) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using MewtocolNet.Exceptions;
|
|
||||||
using MewtocolNet.Logging;
|
using MewtocolNet.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -34,7 +33,7 @@ namespace MewtocolNet {
|
|||||||
public void ConfigureConnection(string ip, int port = 9094, int station = 0xEE) {
|
public void ConfigureConnection(string ip, int port = 9094, int station = 0xEE) {
|
||||||
|
|
||||||
if (!IPAddress.TryParse(ip, out ipAddr))
|
if (!IPAddress.TryParse(ip, out ipAddr))
|
||||||
throw new MewtocolException($"The ip: {ip} is no valid ip address");
|
throw new NotSupportedException($"The ip: {ip} is no valid ip address");
|
||||||
|
|
||||||
if (stationNumber != 0xEE && stationNumber > 99)
|
if (stationNumber != 0xEE && stationNumber > 99)
|
||||||
throw new NotSupportedException("Station number can't be greater than 99");
|
throw new NotSupportedException("Station number can't be greater than 99");
|
||||||
@@ -66,6 +65,9 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
Logger.Log($">> Intial connection start <<", LogLevel.Verbose, this);
|
||||||
|
isConnectingStage = true;
|
||||||
|
|
||||||
if (HostEndpoint != null) {
|
if (HostEndpoint != null) {
|
||||||
|
|
||||||
client = new TcpClient(HostEndpoint) {
|
client = new TcpClient(HostEndpoint) {
|
||||||
@@ -109,9 +111,9 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
if (plcinf != null) {
|
if (plcinf != null) {
|
||||||
|
|
||||||
|
IsConnected = true;
|
||||||
await base.ConnectAsync();
|
await base.ConnectAsync();
|
||||||
|
OnConnected(plcinf);
|
||||||
OnConnected(plcinf.Value);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -125,6 +127,7 @@ namespace MewtocolNet {
|
|||||||
} catch (SocketException) {
|
} catch (SocketException) {
|
||||||
|
|
||||||
OnMajorSocketExceptionWhileConnecting();
|
OnMajorSocketExceptionWhileConnecting();
|
||||||
|
isConnectingStage = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Globalization;
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace MewtocolNet {
|
namespace MewtocolNet {
|
||||||
@@ -6,43 +8,85 @@ namespace MewtocolNet {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds various informations about the PLC
|
/// Holds various informations about the PLC
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct PLCInfo {
|
public class PLCInfo : INotifyPropertyChanged {
|
||||||
|
|
||||||
|
private PlcType typeCode;
|
||||||
|
private string typeName;
|
||||||
|
private OPMode operationMode;
|
||||||
|
private HWInformation hardwareInformation;
|
||||||
|
private string selfDiagnosticError;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of the PLC named by Panasonic
|
/// The type of the PLC named by Panasonic
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PlcType TypeCode { get; private set; }
|
public PlcType TypeCode {
|
||||||
|
get => typeCode;
|
||||||
|
internal set {
|
||||||
|
typeCode = value;
|
||||||
|
OnPropChange();
|
||||||
|
//update name
|
||||||
|
typeName = typeCode.ToName();
|
||||||
|
OnPropChange(nameof(TypeName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains information about the PLCs operation modes as flags
|
/// The full qualified name of the PLC
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public OPMode OperationMode { get; private set; }
|
public string TypeName => typeName;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Hardware information flags about the PLC
|
|
||||||
/// </summary>
|
|
||||||
public HWInformation HardwareInformation { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Program capacity in 1K steps
|
/// Program capacity in 1K steps
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int ProgramCapacity { get; private set; }
|
public int ProgramCapacity { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the cpu
|
/// Version of the cpu
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string CpuVersion { get; private set; }
|
public string CpuVersion { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains information about the PLCs operation modes as flags
|
||||||
|
/// </summary>
|
||||||
|
public OPMode OperationMode {
|
||||||
|
get => operationMode;
|
||||||
|
internal set {
|
||||||
|
operationMode = value;
|
||||||
|
OnPropChange();
|
||||||
|
OnPropChange(nameof(IsRunMode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hardware information flags about the PLC
|
||||||
|
/// </summary>
|
||||||
|
public HWInformation HardwareInformation {
|
||||||
|
get => hardwareInformation;
|
||||||
|
internal set {
|
||||||
|
hardwareInformation = value;
|
||||||
|
OnPropChange();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current error code of the PLC
|
/// Current error code of the PLC
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SelfDiagnosticError { get; internal set; }
|
public string SelfDiagnosticError {
|
||||||
|
get => selfDiagnosticError;
|
||||||
|
internal set {
|
||||||
|
selfDiagnosticError = value;
|
||||||
|
OnPropChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Quickcheck for the runmode flag
|
/// Quickcheck for the runmode flag
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsRunMode => OperationMode.HasFlag(OPMode.RunMode);
|
public bool IsRunMode => OperationMode.HasFlag(OPMode.RunMode);
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
internal bool TryExtendFromEXRT(string msg) {
|
internal bool TryExtendFromEXRT(string msg) {
|
||||||
|
|
||||||
var regexEXRT = new Regex(@"\%EE\$EX00RT00(?<icnt>..)(?<mc>..)..(?<cap>..)(?<op>..)..(?<flg>..)(?<sdiag>....)(?<ver>..)(?<hwif>..)(?<nprog>.)(?<progsz>....)(?<hdsz>....)(?<sysregsz>....).*", RegexOptions.IgnoreCase);
|
var regexEXRT = new Regex(@"\%EE\$EX00RT00(?<icnt>..)(?<mc>..)..(?<cap>..)(?<op>..)..(?<flg>..)(?<sdiag>....)(?<ver>..)(?<hwif>..)(?<nprog>.)(?<progsz>....)(?<hdsz>....)(?<sysregsz>....).*", RegexOptions.IgnoreCase);
|
||||||
@@ -114,7 +158,25 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
|
|
||||||
return $"{TypeCode.ToName()}, OP: {OperationMode}";
|
return $"{TypeName}, OP: {OperationMode}";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj) {
|
||||||
|
|
||||||
|
if ((obj == null) || !this.GetType().Equals(obj.GetType())) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return (PLCInfo)obj == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() => GetHashCode();
|
||||||
|
|
||||||
|
private protected void OnPropChange([CallerMemberName] string propertyName = null) {
|
||||||
|
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
MewtocolNet/RegisterAttributes/StringHintAttribute.cs
Normal file
20
MewtocolNet/RegisterAttributes/StringHintAttribute.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MewtocolNet.RegisterAttributes {
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a string size hint
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||||
|
public class StringHintAttribute : Attribute {
|
||||||
|
|
||||||
|
internal int size;
|
||||||
|
|
||||||
|
public StringHintAttribute(int size) {
|
||||||
|
|
||||||
|
this.size = size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -305,7 +305,7 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
/// <typeparam name="T">
|
/// <typeparam name="T">
|
||||||
/// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
|
/// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
|
||||||
/// </typeparam>
|
/// </typeparam>
|
||||||
public TempRegister<T> AsType<T>(int? sizeHint = null) {
|
public TypedRegister AsType<T>() {
|
||||||
|
|
||||||
if (!typeof(T).IsAllowedPlcCastingType()) {
|
if (!typeof(T).IsAllowedPlcCastingType()) {
|
||||||
|
|
||||||
@@ -313,10 +313,9 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Data.byteSizeHint = (uint?)sizeHint;
|
|
||||||
Data.dotnetVarType = typeof(T);
|
Data.dotnetVarType = typeof(T);
|
||||||
|
|
||||||
return new TempRegister<T>(Data, builder);
|
return new TypedRegister().Map(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +326,7 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
/// <param name="type">
|
/// <param name="type">
|
||||||
/// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
|
/// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
|
||||||
/// </param>
|
/// </param>
|
||||||
public TempRegister AsType(Type type) {
|
public TypedRegister AsType(Type type) {
|
||||||
|
|
||||||
//was ranged syntax array build
|
//was ranged syntax array build
|
||||||
if (Data.wasAddressStringRangeBased && type.IsArray && type.GetArrayRank() == 1) {
|
if (Data.wasAddressStringRangeBased && type.IsArray && type.GetArrayRank() == 1) {
|
||||||
@@ -344,14 +343,14 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int byteSizePerItem = elementType.DetermineTypeByteSize();
|
int byteSizePerItem = elementType.DetermineTypeByteIntialSize();
|
||||||
|
|
||||||
//check if it fits without remainder
|
//check if it fits without remainder
|
||||||
if (Data.byteSizeHint % byteSizePerItem != 0) {
|
if (Data.byteSizeHint % byteSizePerItem != 0) {
|
||||||
throw new NotSupportedException($"The array element type {elementType} doesn't fit into the adress range");
|
throw new NotSupportedException($"The array element type {elementType} doesn't fit into the adress range");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (TempRegister)generic.Invoke(this, new object[] {
|
return (TypedRegister)generic.Invoke(this, new object[] {
|
||||||
//element count
|
//element count
|
||||||
new int[] { (int)((Data.byteSizeHint / byteSizePerItem)) }
|
new int[] { (int)((Data.byteSizeHint / byteSizePerItem)) }
|
||||||
});
|
});
|
||||||
@@ -369,9 +368,9 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
return AsType(Data.typeDef);
|
return AsType(Data.typeDef);
|
||||||
|
|
||||||
} else if ((type.IsArray || type == typeof(string)) && Data.typeDef == null) {
|
} else if (type.IsArray && Data.typeDef == null) {
|
||||||
|
|
||||||
throw new NotSupportedException("Typedef parameter is needed for array or string types");
|
throw new NotSupportedException("Typedef parameter is needed for array types");
|
||||||
|
|
||||||
} else if (Data.typeDef != null) {
|
} else if (Data.typeDef != null) {
|
||||||
|
|
||||||
@@ -389,18 +388,18 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
Data.dotnetVarType = type;
|
Data.dotnetVarType = type;
|
||||||
|
|
||||||
return new TempRegister(Data, builder);
|
return new TypedRegister().Map(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the register type as a predefined <see cref="PlcVarType"/>
|
/// Sets the register type as a predefined <see cref="PlcVarType"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TempRegister AsType(PlcVarType type) {
|
public TypedRegister AsType(PlcVarType type) {
|
||||||
|
|
||||||
Data.dotnetVarType = type.GetDefaultDotnetType();
|
Data.dotnetVarType = type.GetDefaultDotnetType();
|
||||||
|
|
||||||
return new TempRegister(Data, builder);
|
return new TypedRegister().Map(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,10 +420,13 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
/// <item><term>DWORD</term><description>32 bit double word interpreted as <see cref="uint"/></description></item>
|
/// <item><term>DWORD</term><description>32 bit double word interpreted as <see cref="uint"/></description></item>
|
||||||
/// </list>
|
/// </list>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TempRegister AsType(string type) {
|
public TypedRegister AsType(string type) {
|
||||||
|
|
||||||
var stringMatch = Regex.Match(type, @"STRING *\[(?<len>[0-9]*)\]", RegexOptions.IgnoreCase);
|
var regexString = new Regex(@"^STRING *\[(?<len>[0-9]*)\]$", RegexOptions.IgnoreCase);
|
||||||
var arrayMatch = Regex.Match(type, @"ARRAY *\[(?<S1>[0-9]*)..(?<E1>[0-9]*)(?:\,(?<S2>[0-9]*)..(?<E2>[0-9]*))?(?:\,(?<S3>[0-9]*)..(?<E3>[0-9]*))?\] *OF {1,}(?<t>.*)", RegexOptions.IgnoreCase);
|
var regexArray = new Regex(@"^ARRAY *\[(?<S1>[0-9]*)..(?<E1>[0-9]*)(?:\,(?<S2>[0-9]*)..(?<E2>[0-9]*))?(?:\,(?<S3>[0-9]*)..(?<E3>[0-9]*))?\] *OF {1,}(?<t>.*)$", RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
var stringMatch = regexString.Match(type);
|
||||||
|
var arrayMatch = regexArray.Match(type);
|
||||||
|
|
||||||
if (Enum.TryParse<PlcVarType>(type, out var parsed)) {
|
if (Enum.TryParse<PlcVarType>(type, out var parsed)) {
|
||||||
|
|
||||||
@@ -440,52 +442,63 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
//invoke generic AsTypeArray
|
//invoke generic AsTypeArray
|
||||||
|
|
||||||
string arrTypeString = arrayMatch.Groups["t"].Value;
|
string arrTypeString = arrayMatch.Groups["t"].Value;
|
||||||
|
Type dotnetArrType = null;
|
||||||
|
|
||||||
if (Enum.TryParse<PlcVarType>(arrTypeString, out var parsedArrType)) {
|
var stringMatchInArray = regexString.Match(arrTypeString);
|
||||||
|
|
||||||
var dotnetArrType = parsedArrType.GetDefaultDotnetType();
|
if (Enum.TryParse<PlcVarType>(arrTypeString, out var parsedArrType) && parsedArrType != PlcVarType.STRING) {
|
||||||
var indices = new List<int>();
|
|
||||||
|
|
||||||
for (int i = 1; i < 4; i++) {
|
dotnetArrType = parsedArrType.GetDefaultDotnetType();
|
||||||
|
|
||||||
var arrStart = arrayMatch.Groups[$"S{i}"]?.Value;
|
|
||||||
var arrEnd = arrayMatch.Groups[$"E{i}"]?.Value;
|
|
||||||
if (string.IsNullOrEmpty(arrStart) || string.IsNullOrEmpty(arrEnd)) break;
|
|
||||||
|
|
||||||
var arrStartInt = int.Parse(arrStart);
|
} else if (stringMatchInArray.Success) {
|
||||||
var arrEndInt = int.Parse(arrEnd);
|
|
||||||
|
|
||||||
indices.Add(arrEndInt - arrStartInt + 1);
|
dotnetArrType = typeof(string);
|
||||||
|
//Data.byteSizeHint = uint.Parse(stringMatch.Groups["len"].Value);
|
||||||
}
|
|
||||||
|
|
||||||
var arr = Array.CreateInstance(dotnetArrType, indices.ToArray());
|
|
||||||
var arrType = arr.GetType();
|
|
||||||
|
|
||||||
MethodInfo method = typeof(SAddress).GetMethod(nameof(AsTypeArray));
|
|
||||||
MethodInfo generic = method.MakeGenericMethod(arrType);
|
|
||||||
|
|
||||||
var tmp = (TempRegister)generic.Invoke(this, new object[] {
|
|
||||||
indices.ToArray()
|
|
||||||
});
|
|
||||||
|
|
||||||
tmp.builder = builder;
|
|
||||||
tmp.Data = Data;
|
|
||||||
|
|
||||||
return tmp;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
throw new NotSupportedException($"The FP type '{arrTypeString}' was not recognized");
|
throw new NotSupportedException($"The FP type '{arrTypeString}' was not recognized");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var indices = new List<int>();
|
||||||
|
|
||||||
|
for (int i = 1; i < 4; i++) {
|
||||||
|
|
||||||
|
var arrStart = arrayMatch.Groups[$"S{i}"]?.Value;
|
||||||
|
var arrEnd = arrayMatch.Groups[$"E{i}"]?.Value;
|
||||||
|
if (string.IsNullOrEmpty(arrStart) || string.IsNullOrEmpty(arrEnd)) break;
|
||||||
|
|
||||||
|
var arrStartInt = int.Parse(arrStart);
|
||||||
|
var arrEndInt = int.Parse(arrEnd);
|
||||||
|
|
||||||
|
indices.Add(arrEndInt - arrStartInt + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var arr = Array.CreateInstance(dotnetArrType, indices.ToArray());
|
||||||
|
var arrType = arr.GetType();
|
||||||
|
|
||||||
|
MethodInfo method = typeof(SAddress).GetMethod(nameof(AsTypeArray));
|
||||||
|
MethodInfo generic = method.MakeGenericMethod(arrType);
|
||||||
|
|
||||||
|
var tmp = (TypedRegister)generic.Invoke(this, new object[] {
|
||||||
|
indices.ToArray()
|
||||||
|
});
|
||||||
|
|
||||||
|
tmp.builder = builder;
|
||||||
|
tmp.Data = Data;
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
throw new NotSupportedException($"The FP type '{type}' was not recognized");
|
throw new NotSupportedException($"The FP type '{type}' was not recognized");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TempRegister(Data, builder);
|
return new TypedRegister().Map(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +520,7 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
/// ARRAY [0..2, 0..3, 0..4] OF INT = <c>AsTypeArray<short[,,]>(3,4,5)</c><br/>
|
/// ARRAY [0..2, 0..3, 0..4] OF INT = <c>AsTypeArray<short[,,]>(3,4,5)</c><br/>
|
||||||
/// ARRAY [5..6, 0..2] OF DWORD = <c>AsTypeArray<DWord[,]>(2, 3)</c><br/>
|
/// ARRAY [5..6, 0..2] OF DWORD = <c>AsTypeArray<DWord[,]>(2, 3)</c><br/>
|
||||||
/// </example>
|
/// </example>
|
||||||
public TempRegister AsTypeArray<T>(params int[] indicies) {
|
public TypedRegister AsTypeArray<T>(params int[] indicies) {
|
||||||
|
|
||||||
if (!typeof(T).IsArray)
|
if (!typeof(T).IsArray)
|
||||||
throw new NotSupportedException($"The type {typeof(T)} was no array");
|
throw new NotSupportedException($"The type {typeof(T)} was no array");
|
||||||
@@ -526,7 +539,7 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
Data.dotnetVarType = typeof(T);
|
Data.dotnetVarType = typeof(T);
|
||||||
|
|
||||||
int byteSizePerItem = elBaseType.DetermineTypeByteSize();
|
int byteSizePerItem = elBaseType.DetermineTypeByteIntialSize();
|
||||||
int calcedTotalByteSize = indicies.Aggregate((a, x) => a * x) * byteSizePerItem;
|
int calcedTotalByteSize = indicies.Aggregate((a, x) => a * x) * byteSizePerItem;
|
||||||
|
|
||||||
Data.byteSizeHint = (uint)calcedTotalByteSize;
|
Data.byteSizeHint = (uint)calcedTotalByteSize;
|
||||||
@@ -536,7 +549,31 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
throw new NotSupportedException($"The array element type {elBaseType} doesn't fit into the adress range");
|
throw new NotSupportedException($"The array element type {elBaseType} doesn't fit into the adress range");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TempRegister(Data, builder);
|
return new TypedRegister().Map(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Typing size hint
|
||||||
|
|
||||||
|
public class TypedRegister : SBase {
|
||||||
|
|
||||||
|
public OptionsRegister SizeHint(int hint) {
|
||||||
|
|
||||||
|
Data.byteSizeHint = (uint)hint;
|
||||||
|
|
||||||
|
return new OptionsRegister().Map(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsRegister PollLevel(int level) {
|
||||||
|
|
||||||
|
Data.pollLevel = level;
|
||||||
|
|
||||||
|
return new OptionsRegister().Map(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,36 +583,19 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
#region Options stage
|
#region Options stage
|
||||||
|
|
||||||
public class TempRegister<T> : SBase {
|
public class OptionsRegister : SBase {
|
||||||
|
|
||||||
internal TempRegister() { }
|
internal OptionsRegister() { }
|
||||||
|
|
||||||
internal TempRegister(StepData data, RBuildBase bldr) : base(data, bldr) { }
|
internal OptionsRegister(StepData data, RBuildBase bldr) : base(data, bldr) { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the poll level of the register
|
/// Sets the poll level of the register
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TempRegister<T> PollLevel(int level) {
|
public OptionsRegister PollLevel(int level) {
|
||||||
|
|
||||||
Data.pollLevel = level;
|
Data.pollLevel = level;
|
||||||
return this;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TempRegister : SBase {
|
|
||||||
|
|
||||||
internal TempRegister() { }
|
|
||||||
|
|
||||||
internal TempRegister(StepData data, RBuildBase bldr) : base(data, bldr) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the poll level of the register
|
|
||||||
/// </summary>
|
|
||||||
public TempRegister PollLevel(int level) {
|
|
||||||
|
|
||||||
Data.pollLevel = level;
|
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,11 +34,16 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//internal use only, adds a type definition (for use when building from attibute)
|
//internal use only, adds a type definition (for use when building from attibute)
|
||||||
internal SAddress AddressFromAttribute(string plcAddrName, string typeDef) {
|
internal SAddress AddressFromAttribute(string plcAddrName, string typeDef, RegisterCollection regCol, PropertyInfo prop, uint? bytesizeHint = null) {
|
||||||
|
|
||||||
var built = Address(plcAddrName);
|
var built = Address(plcAddrName);
|
||||||
|
|
||||||
built.Data.typeDef = typeDef;
|
built.Data.typeDef = typeDef;
|
||||||
built.Data.buildSource = RegisterBuildSource.Attribute;
|
built.Data.buildSource = RegisterBuildSource.Attribute;
|
||||||
|
built.Data.regCollection = regCol;
|
||||||
|
built.Data.boundProperty = prop;
|
||||||
|
built.Data.byteSizeHint = bytesizeHint;
|
||||||
|
|
||||||
return built;
|
return built;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -49,15 +54,38 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
public new class SAddress : RBuildBase.SAddress {
|
public new class SAddress : RBuildBase.SAddress {
|
||||||
|
|
||||||
public new TempRegister<T> AsType<T>(int? sizeHint = null) => new TempRegister<T>().Map(base.AsType<T>(sizeHint));
|
public new TypedRegister AsType<T>() => new TypedRegister().Map(base.AsType<T>());
|
||||||
|
|
||||||
public new TempRegister AsType(Type type) => new TempRegister().Map(base.AsType(type));
|
public new TypedRegister AsType(Type type) => new TypedRegister().Map(base.AsType(type));
|
||||||
|
|
||||||
public new TempRegister AsType(PlcVarType type) => new TempRegister().Map(base.AsType(type));
|
public new TypedRegister AsType(PlcVarType type) => new TypedRegister().Map(base.AsType(type));
|
||||||
|
|
||||||
public new TempRegister AsType(string type) => new TempRegister().Map(base.AsType(type));
|
public new TypedRegister AsType(string type) => new TypedRegister().Map(base.AsType(type));
|
||||||
|
|
||||||
public new TempRegister AsTypeArray<T>(params int[] indicies) => new TempRegister().Map(base.AsTypeArray<T>(indicies));
|
public new TypedRegister AsTypeArray<T>(params int[] indicies) => new TypedRegister().Map(base.AsTypeArray<T>(indicies));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Typing size hint
|
||||||
|
|
||||||
|
public new class TypedRegister : RBuildBase.TypedRegister {
|
||||||
|
|
||||||
|
public new OptionsRegister SizeHint(int hint) => new OptionsRegister().Map(base.SizeHint(hint));
|
||||||
|
|
||||||
|
///<inheritdoc cref="RBuildBase.OptionsRegister.PollLevel(int)"/>
|
||||||
|
public new OutRegister PollLevel(int level) => new OutRegister().Map(base.PollLevel(level));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Outputs the generated <see cref="IRegister"/>
|
||||||
|
/// </summary>
|
||||||
|
public void Out(Action<IRegister> registerOut) {
|
||||||
|
|
||||||
|
Data.registerOut = registerOut;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,58 +93,17 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
#region Options stage
|
#region Options stage
|
||||||
|
|
||||||
public new class TempRegister<T> : RBuildBase.TempRegister<T> {
|
public new class OptionsRegister : RBuildBase.OptionsRegister {
|
||||||
|
|
||||||
internal TempRegister() { }
|
///<inheritdoc cref="RBuildBase.OptionsRegister.PollLevel(int)"/>
|
||||||
|
public new OutRegister PollLevel(int level) => new OutRegister().Map(base.PollLevel(level));
|
||||||
internal TempRegister(StepData data, RBuildBase bldr) : base(data, bldr) { }
|
|
||||||
|
|
||||||
///<inheritdoc cref="RBuildBase.TempRegister.PollLevel(int)"/>
|
|
||||||
public new TempRegister<T> PollLevel(int level) => new TempRegister<T>().Map(base.PollLevel(level));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Outputs the generated <see cref="IRegister"/>
|
/// Outputs the generated <see cref="IRegister"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TempRegister<T> Out(Action<IRegister> registerOut) {
|
public void Out(Action<IRegister> registerOut) {
|
||||||
|
|
||||||
Data.registerOut = registerOut;
|
Data.registerOut = registerOut;
|
||||||
return this;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public new class TempRegister : RBuildBase.TempRegister {
|
|
||||||
|
|
||||||
internal TempRegister() { }
|
|
||||||
|
|
||||||
internal TempRegister(StepData data, RBuildBase bldr) : base(data, bldr) { }
|
|
||||||
|
|
||||||
///<inheritdoc cref="RBuildBase.TempRegister.PollLevel(int)"/>
|
|
||||||
public new TempRegister PollLevel(int level) => new TempRegister().Map(base.PollLevel(level));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Outputs the generated <see cref="IRegister"/>
|
|
||||||
/// </summary>
|
|
||||||
public TempRegister Out(Action<IRegister> registerOut) {
|
|
||||||
|
|
||||||
Data.registerOut = registerOut;
|
|
||||||
return this;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//internal use only
|
|
||||||
internal TempRegister RegCollection(RegisterCollection col) {
|
|
||||||
|
|
||||||
Data.regCollection = col;
|
|
||||||
return this;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal TempRegister BoundProp(PropertyInfo prop) {
|
|
||||||
|
|
||||||
Data.boundProperty = prop;
|
|
||||||
return this;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,6 +111,19 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public class OutRegister : SBase {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Outputs the generated <see cref="IRegister"/>
|
||||||
|
/// </summary>
|
||||||
|
public void Out(Action<IRegister> registerOut) {
|
||||||
|
|
||||||
|
Data.registerOut = registerOut;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,32 +25,6 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
interf.AddRegisters(registers.ToArray());
|
interf.AddRegisters(registers.ToArray());
|
||||||
|
|
||||||
Task.Run(interf.memoryManager.CheckAllDynamicallySizedAreas);
|
|
||||||
|
|
||||||
return registers.First();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a single register to the plc stack and returns the generated <see cref="IRegister"/>
|
|
||||||
/// Waits
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The generated <see cref="IRegister"/></returns>
|
|
||||||
public static async Task<IRegister> AddRegisterAsync (this IPlc plc, Action<RBuildSingle> builder) {
|
|
||||||
|
|
||||||
var assembler = new RegisterAssembler((MewtocolInterface)plc);
|
|
||||||
var regBuilder = new RBuildSingle((MewtocolInterface)plc);
|
|
||||||
|
|
||||||
builder.Invoke(regBuilder);
|
|
||||||
|
|
||||||
var registers = assembler.AssembleAll(regBuilder);
|
|
||||||
|
|
||||||
var interf = (MewtocolInterface)plc;
|
|
||||||
|
|
||||||
interf.AddRegisters(registers.ToArray());
|
|
||||||
|
|
||||||
await interf.memoryManager.CheckAllDynamicallySizedAreas();
|
|
||||||
|
|
||||||
return registers.First();
|
return registers.First();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -77,33 +51,6 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
interf.AddRegisters(registers.ToArray());
|
interf.AddRegisters(registers.ToArray());
|
||||||
|
|
||||||
Task.Run(interf.memoryManager.CheckAllDynamicallySizedAreas);
|
|
||||||
|
|
||||||
return plc;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds multiple registers to the plc stack at once <br/>
|
|
||||||
/// Using this over adding each register individually will result in better generation time performance
|
|
||||||
/// of the <see cref="UnderlyingRegisters.MemoryAreaManager"/><br/><br/>
|
|
||||||
/// This waits for the memory manager to size all dynamic registers correctly
|
|
||||||
/// </summary>
|
|
||||||
public static async Task<IPlc> AddRegistersAsync (this IPlc plc, Action<RBuildMult> builder) {
|
|
||||||
|
|
||||||
var assembler = new RegisterAssembler((MewtocolInterface)plc);
|
|
||||||
var regBuilder = new RBuildMult((MewtocolInterface)plc);
|
|
||||||
|
|
||||||
builder.Invoke(regBuilder);
|
|
||||||
|
|
||||||
var registers = assembler.AssembleAll(regBuilder);
|
|
||||||
|
|
||||||
var interf = (MewtocolInterface)plc;
|
|
||||||
|
|
||||||
interf.AddRegisters(registers.ToArray());
|
|
||||||
|
|
||||||
await interf.memoryManager.CheckAllDynamicallySizedAreas();
|
|
||||||
|
|
||||||
return plc;
|
return plc;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using MewtocolNet.Exceptions;
|
using MewtocolNet.RegisterAttributes;
|
||||||
using MewtocolNet.RegisterAttributes;
|
|
||||||
using MewtocolNet.Registers;
|
using MewtocolNet.Registers;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -50,7 +49,7 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
Type elementType = data.dotnetVarType.GetElementType();
|
Type elementType = data.dotnetVarType.GetElementType();
|
||||||
|
|
||||||
uint numericSizePerElement = (uint)elementType.DetermineTypeByteSize();
|
uint numericSizePerElement = (uint)elementType.DetermineTypeByteIntialSize();
|
||||||
|
|
||||||
if (elementType.IsEnum && numericSizePerElement > 4) {
|
if (elementType.IsEnum && numericSizePerElement > 4) {
|
||||||
if (data.boundProperty != null) {
|
if (data.boundProperty != null) {
|
||||||
@@ -60,25 +59,10 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sizeStateFlags = DynamicSizeState.None;
|
|
||||||
|
|
||||||
//string with size hint
|
|
||||||
if (elementType == typeof(string) && data.perElementByteSizeHint != null) {
|
|
||||||
|
|
||||||
numericSizePerElement = (uint)data.byteSizeHint + 4;
|
|
||||||
sizeStateFlags = DynamicSizeState.DynamicallySized | DynamicSizeState.WasSizeUpdated;
|
|
||||||
|
|
||||||
} else if (elementType == typeof(string)) {
|
|
||||||
|
|
||||||
sizeStateFlags = DynamicSizeState.DynamicallySized | DynamicSizeState.NeedsSizeUpdate;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameters = new object[] {
|
var parameters = new object[] {
|
||||||
data.memAddress,
|
data.memAddress,
|
||||||
data.byteSizeHint,
|
data.byteSizeHint,
|
||||||
data.arrayIndicies,
|
data.arrayIndicies,
|
||||||
sizeStateFlags,
|
|
||||||
data.name
|
data.name
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,7 +73,6 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
typeof(uint),
|
typeof(uint),
|
||||||
typeof(uint),
|
typeof(uint),
|
||||||
typeof(int[]),
|
typeof(int[]),
|
||||||
typeof(DynamicSizeState),
|
|
||||||
typeof(string)
|
typeof(string)
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
@@ -107,7 +90,7 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
//-------------------------------------------
|
//-------------------------------------------
|
||||||
//as single register
|
//as single register
|
||||||
|
|
||||||
uint numericSize = (uint)data.dotnetVarType.DetermineTypeByteSize();
|
uint numericSize = (uint)data.dotnetVarType.DetermineTypeByteIntialSize();
|
||||||
|
|
||||||
if (data.dotnetVarType.IsEnum && numericSize > 4) {
|
if (data.dotnetVarType.IsEnum && numericSize > 4) {
|
||||||
if (data.boundProperty != null) {
|
if (data.boundProperty != null) {
|
||||||
@@ -117,17 +100,15 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sizeStateFlags = DynamicSizeState.None;
|
if(data.dotnetVarType == typeof(string)) {
|
||||||
|
|
||||||
//string with size hint
|
if(data.byteSizeHint == null)
|
||||||
if(data.dotnetVarType == typeof(string) && data.byteSizeHint != null) {
|
throw new NotSupportedException($"Can't create a STRING register without a string size hint");
|
||||||
|
|
||||||
numericSize = (uint)data.byteSizeHint + 4;
|
if(data.byteSizeHint < 0)
|
||||||
sizeStateFlags = DynamicSizeState.DynamicallySized | DynamicSizeState.WasSizeUpdated;
|
throw new NotSupportedException($"Can't create a STRING register with a string size hint < 0");
|
||||||
|
|
||||||
} else if (data.dotnetVarType == typeof(string)) {
|
numericSize = 4 + data.byteSizeHint.Value;
|
||||||
|
|
||||||
sizeStateFlags = DynamicSizeState.DynamicallySized | DynamicSizeState.NeedsSizeUpdate;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,13 +116,12 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
|
|
||||||
Type paramedClass = typeof(SingleRegister<>).MakeGenericType(data.dotnetVarType);
|
Type paramedClass = typeof(SingleRegister<>).MakeGenericType(data.dotnetVarType);
|
||||||
ConstructorInfo constr = paramedClass.GetConstructor(flags, null, new Type[] {
|
ConstructorInfo constr = paramedClass.GetConstructor(flags, null, new Type[] {
|
||||||
typeof(uint), typeof(uint), typeof(DynamicSizeState) ,typeof(string)
|
typeof(uint), typeof(uint) ,typeof(string)
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
var parameters = new object[] {
|
var parameters = new object[] {
|
||||||
data.memAddress,
|
data.memAddress,
|
||||||
numericSize,
|
numericSize,
|
||||||
sizeStateFlags,
|
|
||||||
data.name
|
data.name
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -167,7 +147,7 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
//finalize set for every
|
//finalize set for every
|
||||||
|
|
||||||
if (generatedInstance == null)
|
if (generatedInstance == null)
|
||||||
throw new MewtocolException("Failed to build register");
|
throw new ArgumentException("Failed to build register");
|
||||||
|
|
||||||
if (collectionTarget != null)
|
if (collectionTarget != null)
|
||||||
generatedInstance.WithRegisterCollection(collectionTarget);
|
generatedInstance.WithRegisterCollection(collectionTarget);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using MewtocolNet.Exceptions;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -12,7 +12,7 @@ namespace MewtocolNet.Registers {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ArrayRegister<T> : Register {
|
public class ArrayRegister<T> : Register {
|
||||||
|
|
||||||
internal int[] indicies;
|
internal int[] indices;
|
||||||
|
|
||||||
internal uint addressLength;
|
internal uint addressLength;
|
||||||
|
|
||||||
@@ -25,12 +25,11 @@ namespace MewtocolNet.Registers {
|
|||||||
public ArrayRegister() =>
|
public ArrayRegister() =>
|
||||||
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
||||||
|
|
||||||
internal ArrayRegister(uint _address, uint _reservedByteSize, int[] _indicies , DynamicSizeState dynamicSizeSt, string _name = null) {
|
internal ArrayRegister(uint _address, uint _reservedByteSize, int[] _indicies, string _name = null) {
|
||||||
|
|
||||||
name = _name;
|
name = _name;
|
||||||
memoryAddress = _address;
|
memoryAddress = _address;
|
||||||
dynamicSizeState = dynamicSizeSt;
|
indices = _indicies;
|
||||||
indicies = _indicies;
|
|
||||||
|
|
||||||
//calc mem length
|
//calc mem length
|
||||||
//because one register is always 1 word (2 bytes) long, if the bytecount is uneven we get the trailing word too
|
//because one register is always 1 word (2 bytes) long, if the bytecount is uneven we get the trailing word too
|
||||||
@@ -50,7 +49,22 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
if (Value == null) return "null";
|
if (Value == null) return "null";
|
||||||
|
|
||||||
return ((byte[])Value).ToHexString("-");
|
if(typeof(T) == typeof(byte[])) {
|
||||||
|
|
||||||
|
return ((byte[])Value).ToHexString("-");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
var valueIenum = (IEnumerable)Value;
|
||||||
|
|
||||||
|
foreach (var el in valueIenum) {
|
||||||
|
|
||||||
|
sb.Append($"{el}, ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ArrayToString((Array)Value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,9 +77,6 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task<bool> WriteAsync(object value) {
|
public override async Task<bool> WriteAsync(object value) {
|
||||||
|
|
||||||
if (!attachedInterface.IsConnected)
|
|
||||||
throw MewtocolException.NotConnectedSend();
|
|
||||||
|
|
||||||
var encoded = PlcValueParser.Encode(this, (T)value);
|
var encoded = PlcValueParser.Encode(this, (T)value);
|
||||||
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
||||||
|
|
||||||
@@ -90,9 +101,6 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task<object> ReadAsync() {
|
public override async Task<object> ReadAsync() {
|
||||||
|
|
||||||
if (!attachedInterface.IsConnected)
|
|
||||||
throw MewtocolException.NotConnectedSend();
|
|
||||||
|
|
||||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
||||||
if (res == null) return null;
|
if (res == null) return null;
|
||||||
|
|
||||||
@@ -110,8 +118,9 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
AddSuccessRead();
|
AddSuccessRead();
|
||||||
|
|
||||||
var parsed = PlcValueParser.ParseArray<T>(this, indicies, bytes);
|
var parsed = PlcValueParser.ParseArray<T>(this, indices, bytes);
|
||||||
UpdateHoldingValue(parsed);
|
UpdateHoldingValue(parsed);
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -127,15 +136,63 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
if (changeTriggerBitArr || changeTriggerGeneral) {
|
if (changeTriggerBitArr || changeTriggerGeneral) {
|
||||||
|
|
||||||
|
var beforeVal = lastValue;
|
||||||
|
var beforeValStr = GetValueString();
|
||||||
|
|
||||||
lastValue = val;
|
lastValue = val;
|
||||||
|
|
||||||
TriggerNotifyChange();
|
TriggerNotifyChange();
|
||||||
attachedInterface.InvokeRegisterChanged(this);
|
attachedInterface.InvokeRegisterChanged(this, beforeVal, beforeValStr);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private string ArrayToString(Array array) {
|
||||||
|
|
||||||
|
int rank = array.Rank;
|
||||||
|
int[] lengths = new int[rank];
|
||||||
|
int[] indices = new int[rank];
|
||||||
|
for (int i = 0; i < rank; i++) {
|
||||||
|
lengths[i] = array.GetLength(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
string result = "[";
|
||||||
|
result += ArrayToStringRecursive(array, lengths, indices, 0);
|
||||||
|
result += "]";
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ArrayToStringRecursive(Array array, int[] lengths, int[] indices, int dimension) {
|
||||||
|
|
||||||
|
if (dimension == array.Rank - 1) {
|
||||||
|
|
||||||
|
string result = "[";
|
||||||
|
for (indices[dimension] = 0; indices[dimension] < lengths[dimension]; indices[dimension]++) {
|
||||||
|
result += array.GetValue(indices).ToString();
|
||||||
|
if (indices[dimension] < lengths[dimension] - 1) {
|
||||||
|
result += ",";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += "]";
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
string result = "[";
|
||||||
|
for (indices[dimension] = 0; indices[dimension] < lengths[dimension]; indices[dimension]++) {
|
||||||
|
result += ArrayToStringRecursive(array, lengths, indices, dimension + 1);
|
||||||
|
if (indices[dimension] < lengths[dimension] - 1) {
|
||||||
|
result += ",";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += "]";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MewtocolNet.Events;
|
||||||
|
|
||||||
namespace MewtocolNet.Registers {
|
namespace MewtocolNet.Registers {
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called whenever the value was changed
|
/// Gets called whenever the value was changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event Action<object> ValueChanged;
|
event RegisterChangedEventHandler ValueChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Type of the underlying register
|
/// Type of the underlying register
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MewtocolNet.RegisterAttributes;
|
using MewtocolNet.Events;
|
||||||
|
using MewtocolNet.RegisterAttributes;
|
||||||
using MewtocolNet.UnderlyingRegisters;
|
using MewtocolNet.UnderlyingRegisters;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -14,7 +15,7 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called whenever the value was changed
|
/// Gets called whenever the value was changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<object> ValueChanged;
|
public event RegisterChangedEventHandler ValueChanged;
|
||||||
|
|
||||||
//links to
|
//links to
|
||||||
internal RegisterCollection containedCollection;
|
internal RegisterCollection containedCollection;
|
||||||
@@ -26,8 +27,6 @@ namespace MewtocolNet.Registers {
|
|||||||
internal IMemoryArea underlyingMemory;
|
internal IMemoryArea underlyingMemory;
|
||||||
internal bool autoGenerated;
|
internal bool autoGenerated;
|
||||||
|
|
||||||
internal DynamicSizeState dynamicSizeState;
|
|
||||||
|
|
||||||
internal object lastValue = null;
|
internal object lastValue = null;
|
||||||
internal string name;
|
internal string name;
|
||||||
internal uint memoryAddress;
|
internal uint memoryAddress;
|
||||||
@@ -73,10 +72,13 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
if (lastValue?.ToString() != val?.ToString()) {
|
if (lastValue?.ToString() != val?.ToString()) {
|
||||||
|
|
||||||
|
var beforeVal = lastValue;
|
||||||
|
var beforeValStr = GetValueString();
|
||||||
|
|
||||||
lastValue = val;
|
lastValue = val;
|
||||||
|
|
||||||
TriggerNotifyChange();
|
TriggerNotifyChange();
|
||||||
attachedInterface.InvokeRegisterChanged(this);
|
attachedInterface.InvokeRegisterChanged(this, beforeVal, beforeValStr);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using MewtocolNet.Exceptions;
|
using MewtocolNet.Logging;
|
||||||
using MewtocolNet.Logging;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -14,6 +16,8 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <typeparam name="T">The type of the numeric value</typeparam>
|
/// <typeparam name="T">The type of the numeric value</typeparam>
|
||||||
public class SingleRegister<T> : Register {
|
public class SingleRegister<T> : Register {
|
||||||
|
|
||||||
|
internal uint byteLength;
|
||||||
|
|
||||||
internal uint addressLength;
|
internal uint addressLength;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -21,17 +25,15 @@ namespace MewtocolNet.Registers {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public uint AddressLength => addressLength;
|
public uint AddressLength => addressLength;
|
||||||
|
|
||||||
|
|
||||||
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
|
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
|
||||||
public SingleRegister() =>
|
public SingleRegister() =>
|
||||||
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
||||||
|
|
||||||
internal SingleRegister(uint _address, uint _reservedByteSize, DynamicSizeState dynamicSizeSt, string _name = null) {
|
internal SingleRegister(uint _address, uint _reservedByteSize, string _name = null) {
|
||||||
|
|
||||||
memoryAddress = _address;
|
memoryAddress = _address;
|
||||||
name = _name;
|
name = _name;
|
||||||
dynamicSizeState = dynamicSizeSt;
|
Resize(_reservedByteSize);
|
||||||
addressLength = _reservedByteSize / 2;
|
|
||||||
|
|
||||||
if (_reservedByteSize == 2) RegisterType = RegisterType.DT;
|
if (_reservedByteSize == 2) RegisterType = RegisterType.DT;
|
||||||
if(_reservedByteSize == 4) RegisterType = RegisterType.DDT;
|
if(_reservedByteSize == 4) RegisterType = RegisterType.DDT;
|
||||||
@@ -43,6 +45,14 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Resize (uint reservedByteSize) {
|
||||||
|
|
||||||
|
addressLength = reservedByteSize / 2;
|
||||||
|
if (reservedByteSize % 2 != 0) addressLength++;
|
||||||
|
byteLength = reservedByteSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override string GetAsPLC() {
|
public override string GetAsPLC() {
|
||||||
|
|
||||||
@@ -55,27 +65,23 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override string GetValueString() {
|
public override string GetValueString() {
|
||||||
|
|
||||||
if (Value != null && typeof(T) == typeof(TimeSpan)) {
|
if (Value != null && typeof(T) == typeof(TimeSpan)) return $"{Value} [{((TimeSpan)Value).ToPlcTime()}]";
|
||||||
|
|
||||||
return $"{Value} [{((TimeSpan)Value).ToPlcTime()}]";
|
if (Value != null && typeof(T) == typeof(Word)) return $"{Value} [{((Word)Value).ToStringBitsPlc()}]";
|
||||||
|
|
||||||
}
|
if (Value != null && typeof(T) == typeof(DWord)) return $"{Value} [{((DWord)Value).ToStringBitsPlc()}]";
|
||||||
|
|
||||||
if (Value != null && typeof(T) == typeof(Word)) {
|
var hasFlags = typeof(T).GetCustomAttribute<FlagsAttribute>() != null;
|
||||||
|
|
||||||
return $"{Value} [{((Word)Value).ToStringBitsPlc()}]";
|
if (Value != null && typeof(T).IsEnum && !hasFlags) {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Value != null && typeof(T).IsEnum) {
|
|
||||||
|
|
||||||
var underlying = Enum.GetUnderlyingType(typeof(T));
|
var underlying = Enum.GetUnderlyingType(typeof(T));
|
||||||
object val = Convert.ChangeType(Value, underlying);
|
object val = Convert.ChangeType(Value, underlying);
|
||||||
|
|
||||||
return $"{Value} [{val}]";
|
return $"{Value} [{val}]";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Value != null && typeof(T).IsEnum && hasFlags) return $"{Value}";
|
||||||
|
|
||||||
return Value?.ToString() ?? "null";
|
return Value?.ToString() ?? "null";
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -86,12 +92,6 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task<bool> WriteAsync(object value) {
|
public override async Task<bool> WriteAsync(object value) {
|
||||||
|
|
||||||
if (!attachedInterface.IsConnected)
|
|
||||||
throw MewtocolException.NotConnectedSend();
|
|
||||||
|
|
||||||
if (dynamicSizeState.HasFlag(DynamicSizeState.DynamicallySized | DynamicSizeState.NeedsSizeUpdate))
|
|
||||||
await UpdateDynamicSize();
|
|
||||||
|
|
||||||
var encoded = PlcValueParser.Encode(this, (T)value);
|
var encoded = PlcValueParser.Encode(this, (T)value);
|
||||||
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
||||||
|
|
||||||
@@ -116,12 +116,6 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task<object> ReadAsync() {
|
public override async Task<object> ReadAsync() {
|
||||||
|
|
||||||
if (!attachedInterface.IsConnected)
|
|
||||||
throw MewtocolException.NotConnectedSend();
|
|
||||||
|
|
||||||
if(dynamicSizeState.HasFlag(DynamicSizeState.DynamicallySized | DynamicSizeState.NeedsSizeUpdate))
|
|
||||||
await UpdateDynamicSize();
|
|
||||||
|
|
||||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
||||||
if (res == null) return null;
|
if (res == null) return null;
|
||||||
|
|
||||||
@@ -132,54 +126,32 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
if (matchingReg is SingleRegister<string> sreg && this is SingleRegister<string> selfSreg) {
|
if (matchingReg is SingleRegister<string> sreg && this is SingleRegister<string> selfSreg) {
|
||||||
sreg.addressLength = selfSreg.addressLength;
|
sreg.addressLength = selfSreg.addressLength;
|
||||||
sreg.dynamicSizeState = DynamicSizeState.DynamicallySized | DynamicSizeState.WasSizeUpdated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res);
|
matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return SetValueFromBytes(res);
|
return SetValueFromBytes(res);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override async Task UpdateDynamicSize() {
|
internal override object SetValueFromBytes (byte[] bytes) {
|
||||||
|
|
||||||
if (typeof(T) == typeof(string)) await UpdateDynamicSizeString();
|
|
||||||
|
|
||||||
dynamicSizeState = DynamicSizeState.DynamicallySized | DynamicSizeState.WasSizeUpdated;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpdateDynamicSizeString () {
|
|
||||||
|
|
||||||
Logger.Log($"Calibrating dynamic register ({GetRegisterWordRangeString()}) from PLC source", LogLevel.Verbose, attachedInterface);
|
|
||||||
|
|
||||||
//get the string describer bytes
|
|
||||||
var bytes = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, 4);
|
|
||||||
|
|
||||||
if (bytes == null || bytes.Length == 0 || bytes.All(x => x == 0x0)) {
|
|
||||||
|
|
||||||
throw new MewtocolException($"The string register ({GetMewName()}{MemoryAddress}) doesn't exist in the PLC program");
|
|
||||||
|
|
||||||
|
//if string correct the sizing of the byte hint was wrong
|
||||||
|
if (typeof(T) == typeof(string)) {
|
||||||
|
var reservedSize = BitConverter.ToInt16(bytes, 0);
|
||||||
|
if (reservedSize != byteLength - 4)
|
||||||
|
throw new NotSupportedException(
|
||||||
|
$"The STRING register at {GetMewName()} is not correctly sized, " +
|
||||||
|
$"the size should be STRING[{reservedSize}] instead of STRING[{byteLength - 4}]"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var reservedSize = BitConverter.ToInt16(bytes, 0);
|
|
||||||
var usedSize = BitConverter.ToInt16(bytes, 2);
|
|
||||||
var wordsSize = Math.Max(0, (uint)(2 + (reservedSize + 1) / 2));
|
|
||||||
|
|
||||||
addressLength = wordsSize;
|
|
||||||
|
|
||||||
CheckAddressOverflow(memoryAddress, wordsSize);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override object SetValueFromBytes(byte[] bytes) {
|
|
||||||
|
|
||||||
AddSuccessRead();
|
AddSuccessRead();
|
||||||
|
|
||||||
var parsed = PlcValueParser.Parse<T>(this, bytes);
|
var parsed = PlcValueParser.Parse<T>(this, bytes);
|
||||||
|
|
||||||
UpdateHoldingValue(parsed);
|
UpdateHoldingValue(parsed);
|
||||||
return parsed;
|
return parsed;
|
||||||
|
|
||||||
@@ -189,11 +161,13 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
if (lastValue?.ToString() != val?.ToString()) {
|
if (lastValue?.ToString() != val?.ToString()) {
|
||||||
|
|
||||||
if (val != null) lastValue = (T)val;
|
var beforeVal = lastValue;
|
||||||
else lastValue = null;
|
var beforeValStr = GetValueString();
|
||||||
|
|
||||||
|
lastValue = val;
|
||||||
|
|
||||||
TriggerNotifyChange();
|
TriggerNotifyChange();
|
||||||
attachedInterface.InvokeRegisterChanged(this);
|
attachedInterface.InvokeRegisterChanged(this, beforeVal, beforeValStr);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using MewtocolNet.Exceptions;
|
using MewtocolNet.Registers;
|
||||||
using MewtocolNet.Registers;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -136,9 +135,11 @@ namespace MewtocolNet.TypeConversion {
|
|||||||
PlcVarType = PlcVarType.STRING,
|
PlcVarType = PlcVarType.STRING,
|
||||||
FromRaw = (reg, bytes) => {
|
FromRaw = (reg, bytes) => {
|
||||||
|
|
||||||
if(bytes == null || bytes.Length <= 4) {
|
if(bytes.Length == 4) return string.Empty;
|
||||||
|
|
||||||
throw new MewtocolException("Failed to convert string bytes, response not long enough");
|
if(bytes == null || bytes.Length < 4) {
|
||||||
|
|
||||||
|
throw new Exception("Failed to convert string bytes, response not long enough");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using MewtocolNet.Exceptions;
|
using MewtocolNet.Registers;
|
||||||
using MewtocolNet.Registers;
|
|
||||||
using MewtocolNet.TypeConversion;
|
using MewtocolNet.TypeConversion;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -30,7 +29,7 @@ namespace MewtocolNet {
|
|||||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingType);
|
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingType);
|
||||||
|
|
||||||
if (converter == null)
|
if (converter == null)
|
||||||
throw new MewtocolException($"A converter for the dotnet type {underlyingType} doesn't exist");
|
throw new Exception($"A converter for the dotnet type {underlyingType} doesn't exist");
|
||||||
|
|
||||||
return (T)converter.FromRawData(register, bytes);
|
return (T)converter.FromRawData(register, bytes);
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ namespace MewtocolNet {
|
|||||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingElementType);
|
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingElementType);
|
||||||
|
|
||||||
if (converter == null)
|
if (converter == null)
|
||||||
throw new MewtocolException($"A converter for the dotnet type {underlyingElementType} doesn't exist");
|
throw new Exception($"A converter for the dotnet type {underlyingElementType} doesn't exist");
|
||||||
|
|
||||||
//parse the array from one to n dimensions
|
//parse the array from one to n dimensions
|
||||||
var outArray = Array.CreateInstance(underlyingElementType, indices);
|
var outArray = Array.CreateInstance(underlyingElementType, indices);
|
||||||
@@ -66,10 +65,10 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sizePerItem = underlyingElementType.DetermineTypeByteSize();
|
int sizePerItem = underlyingElementType.DetermineTypeByteIntialSize();
|
||||||
|
|
||||||
var iterateItems = indices.Aggregate((a, x) => a * x);
|
var iterateItems = indices.Aggregate((a, x) => a * x);
|
||||||
var indexer = new int[indices.Length];
|
var indexer = new int[indices.Length];
|
||||||
|
|
||||||
for (int i = 0; i < iterateItems; i++) {
|
for (int i = 0; i < iterateItems; i++) {
|
||||||
|
|
||||||
int j = i * sizePerItem;
|
int j = i * sizePerItem;
|
||||||
@@ -150,7 +149,7 @@ namespace MewtocolNet {
|
|||||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingType);
|
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingType);
|
||||||
|
|
||||||
if (converter == null)
|
if (converter == null)
|
||||||
throw new MewtocolException($"A converter for the type {underlyingType} doesn't exist");
|
throw new Exception($"A converter for the type {underlyingType} doesn't exist");
|
||||||
|
|
||||||
return converter.ToRawData(register, value);
|
return converter.ToRawData(register, value);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using MewtocolNet.Exceptions;
|
using MewtocolNet.Registers;
|
||||||
using MewtocolNet.Registers;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@@ -45,7 +44,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MewtocolException("No default register type found");
|
throw new Exception("No default register type found");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +58,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MewtocolException("No default plcvar type found");
|
throw new Exception("No default plcvar type found");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,8 +64,6 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
internal async Task<bool> RequestByteReadAsync(ulong addStart, ulong addEnd) {
|
internal async Task<bool> RequestByteReadAsync(ulong addStart, ulong addEnd) {
|
||||||
|
|
||||||
await CheckDynamicallySizedRegistersAsync();
|
|
||||||
|
|
||||||
var station = mewInterface.GetStationNumber();
|
var station = mewInterface.GetStationNumber();
|
||||||
|
|
||||||
string requeststring = $"%{station}#RD{GetMewtocolIdent(addStart, addEnd)}";
|
string requeststring = $"%{station}#RD{GetMewtocolIdent(addStart, addEnd)}";
|
||||||
@@ -116,22 +114,6 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task CheckDynamicallySizedRegistersAsync() {
|
|
||||||
|
|
||||||
//calibrating at runtime sized registers
|
|
||||||
var uncalibratedStringRegisters = managedRegisters
|
|
||||||
.SelectMany(x => x.Linked)
|
|
||||||
.Where(x => x.dynamicSizeState.HasFlag(DynamicSizeState.DynamicallySized | DynamicSizeState.NeedsSizeUpdate))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var register in uncalibratedStringRegisters)
|
|
||||||
await register.UpdateDynamicSize();
|
|
||||||
|
|
||||||
if (uncalibratedStringRegisters.Count > 0)
|
|
||||||
mewInterface.memoryManager.LinkAndMergeRegisters();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetMewtocolIdent() {
|
private string GetMewtocolIdent() {
|
||||||
|
|
||||||
StringBuilder asciistring = new StringBuilder("D");
|
StringBuilder asciistring = new StringBuilder("D");
|
||||||
|
|||||||
@@ -46,8 +46,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
internal async Task OnPlcConnected () {
|
internal async Task OnPlcConnected () {
|
||||||
|
|
||||||
//check all area for dynamic sized registers
|
await Task.CompletedTask;
|
||||||
await CheckAllDynamicallySizedAreas();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,13 +63,14 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//maxes the highest poll level for all registers that contain each other
|
//maxes the highest poll level for all registers that contain each other
|
||||||
registers
|
var ordered = registers
|
||||||
.OrderByDescending(x => x.GetRegisterAddressLen())
|
.OrderByDescending(x => x.GetRegisterAddressLen())
|
||||||
.ToList()
|
.ToList();
|
||||||
.ForEach(x => x.AveragePollLevel(registers, pollLevelOrMode));
|
|
||||||
|
ordered.ForEach(x => x.AveragePollLevel(registers, pollLevelOrMode));
|
||||||
|
|
||||||
//insert into area
|
//insert into area
|
||||||
foreach (var reg in registers) {
|
foreach (var reg in ordered) {
|
||||||
|
|
||||||
TestPollLevelExistence(reg);
|
TestPollLevelExistence(reg);
|
||||||
|
|
||||||
@@ -278,7 +278,8 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
//check if the linked group has duplicate type registers
|
//check if the linked group has duplicate type registers
|
||||||
|
|
||||||
var dupedTypeReg = existinglinkedGroup.Linked.FirstOrDefault(x => x.IsSameAddressAndType(insertReg));
|
var dupedTypeReg = existinglinkedGroup.Linked.FirstOrDefault(x => x.IsSameAddressAndType(insertReg));
|
||||||
if (dupedTypeReg != null) {
|
|
||||||
|
if (dupedTypeReg != null && insertReg.autoGenerated) {
|
||||||
dupedTypeReg.WithBoundProperties(insertReg.boundProperties);
|
dupedTypeReg.WithBoundProperties(insertReg.boundProperties);
|
||||||
} else {
|
} else {
|
||||||
existinglinkedGroup.Linked.Add(insertReg);
|
existinglinkedGroup.Linked.Add(insertReg);
|
||||||
@@ -287,20 +288,6 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task CheckAllDynamicallySizedAreas () {
|
|
||||||
|
|
||||||
foreach (var pollLevel in pollLevels.ToArray()) {
|
|
||||||
|
|
||||||
foreach (var area in pollLevel.dataAreas.ToArray()) {
|
|
||||||
|
|
||||||
await area.CheckDynamicallySizedRegistersAsync();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task PollAllAreasAsync() {
|
internal async Task PollAllAreasAsync() {
|
||||||
|
|
||||||
foreach (var pollLevel in pollLevels) {
|
foreach (var pollLevel in pollLevels) {
|
||||||
|
|||||||
Reference in New Issue
Block a user