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
|
||||
|
||||
public static int DetermineTypeByteSize(this Type type) {
|
||||
public static int DetermineTypeByteIntialSize(this Type type) {
|
||||
|
||||
//enums can only be of numeric types
|
||||
if (type.IsEnum) return Marshal.SizeOf(Enum.GetUnderlyingType(type));
|
||||
|
||||
//strings get always set with 4 bytes because the first 4 bytes contain the length
|
||||
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);
|
||||
|
||||
@@ -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>
|
||||
/// Converts a hex string (AB01C1) to a byte array
|
||||
/// </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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
@@ -96,7 +97,7 @@ namespace MewtocolNet {
|
||||
var portnames = SerialPort.GetPortNames();
|
||||
|
||||
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>
|
||||
/// Builds and returns the final plc interface
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using MewtocolNet.Helpers;
|
||||
using MewtocolNet.Events;
|
||||
using MewtocolNet.Helpers;
|
||||
using MewtocolNet.Logging;
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolNet.UnderlyingRegisters;
|
||||
@@ -17,6 +18,22 @@ namespace MewtocolNet {
|
||||
|
||||
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
|
||||
|
||||
private protected Stream stream;
|
||||
@@ -40,6 +57,7 @@ namespace MewtocolNet {
|
||||
private protected Stopwatch speedStopwatchDownstr;
|
||||
private protected Task firstPollTask = new Task(() => { });
|
||||
|
||||
private protected bool wasInitialStatusReceived;
|
||||
private protected MewtocolVersion mewtocolVersion;
|
||||
|
||||
#endregion
|
||||
@@ -56,18 +74,6 @@ namespace MewtocolNet {
|
||||
|
||||
#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/>
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
@@ -143,7 +149,7 @@ namespace MewtocolNet {
|
||||
Connected += MewtocolInterface_Connected;
|
||||
RegisterChanged += OnRegisterChanged;
|
||||
|
||||
void MewtocolInterface_Connected(PLCInfo obj) {
|
||||
void MewtocolInterface_Connected(object sender, PlcConnectionArgs args) {
|
||||
|
||||
if (usePoller)
|
||||
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)" : $" ({o.Name})");
|
||||
sb.Append(asInternal.autoGenerated ? $" (Auto)" : $" ({asInternal.Name})");
|
||||
}
|
||||
sb.Append($" {asInternal.underlyingSystemType.Name}");
|
||||
sb.Append($" changed to \"{asInternal.GetValueString()}\"");
|
||||
sb.Append($" changed \"{args.PreviousValueString.Ellipsis(25)}\"" +
|
||||
$" => \"{asInternal.GetValueString().Ellipsis(75)}\"");
|
||||
|
||||
Logger.Log(sb.ToString(), LogLevel.Change, this);
|
||||
|
||||
OnRegisterChangedUpdateProps((Register)o);
|
||||
}
|
||||
|
||||
OnRegisterChangedUpdateProps(asInternal);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task ConnectAsync() {
|
||||
|
||||
isConnectingStage = false;
|
||||
|
||||
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/>
|
||||
@@ -220,6 +245,9 @@ namespace MewtocolNet {
|
||||
/// <inheritdoc/>
|
||||
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
|
||||
queuedMessages++;
|
||||
|
||||
@@ -468,11 +496,10 @@ namespace MewtocolNet {
|
||||
private protected virtual void OnConnected(PLCInfo plcinf) {
|
||||
|
||||
Logger.Log("Connected to PLC", LogLevel.Info, this);
|
||||
Logger.Log($"{plcinf.ToString()}", LogLevel.Verbose, this);
|
||||
|
||||
IsConnected = true;
|
||||
|
||||
Connected?.Invoke(plcinf);
|
||||
Connected?.Invoke(this, new PlcConnectionArgs());
|
||||
|
||||
if (!usePoller) {
|
||||
firstPollTask.RunSynchronously();
|
||||
@@ -493,11 +520,12 @@ namespace MewtocolNet {
|
||||
BytesPerSecondDownstream = 0;
|
||||
BytesPerSecondUpstream = 0;
|
||||
PollerCycleDurationMs = 0;
|
||||
PlcInfo = null;
|
||||
|
||||
IsConnected = false;
|
||||
ClearRegisterVals();
|
||||
|
||||
Disconnected?.Invoke();
|
||||
Disconnected?.Invoke(this, new PlcConnectionArgs());
|
||||
KillPoller();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using MewtocolNet.Logging;
|
||||
using MewtocolNet.Events;
|
||||
using MewtocolNet.Logging;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using MewtocolNet.RegisterBuilding;
|
||||
using MewtocolNet.Registers;
|
||||
@@ -210,19 +211,19 @@ namespace MewtocolNet {
|
||||
if (attr is RegisterAttribute cAttribute) {
|
||||
|
||||
var pollFreqAttr = (PollLevelAttribute)attributes.FirstOrDefault(x => x.GetType() == typeof(PollLevelAttribute));
|
||||
var stringHintAttr = (StringHintAttribute)attributes.FirstOrDefault(x => x.GetType() == typeof(StringHintAttribute));
|
||||
|
||||
var dotnetType = prop.PropertyType;
|
||||
int pollLevel = 1;
|
||||
uint? byteHint = (uint?)stringHintAttr?.size;
|
||||
|
||||
if (pollFreqAttr != null) pollLevel = pollFreqAttr.pollLevel;
|
||||
|
||||
//add builder item
|
||||
var stp1 = regBuild
|
||||
.AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef)
|
||||
.AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef, collection, prop, byteHint)
|
||||
.AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType)
|
||||
.PollLevel(pollLevel)
|
||||
.RegCollection(collection)
|
||||
.BoundProp(prop);
|
||||
.PollLevel(pollLevel);
|
||||
|
||||
}
|
||||
|
||||
@@ -235,7 +236,7 @@ namespace MewtocolNet {
|
||||
collection.OnInterfaceLinked(this);
|
||||
}
|
||||
|
||||
Connected += (i) => {
|
||||
Connected += (s,e) => {
|
||||
if (collection != null)
|
||||
collection.OnInterfaceLinkedAndOnline(this);
|
||||
};
|
||||
@@ -280,6 +281,9 @@ namespace MewtocolNet {
|
||||
|
||||
memoryManager.LinkAndMergeRegisters(registers);
|
||||
|
||||
//run a second iteration
|
||||
//memoryManager.LinkAndMergeRegisters();
|
||||
|
||||
}
|
||||
|
||||
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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet {
|
||||
|
||||
public abstract partial class MewtocolInterface {
|
||||
|
||||
internal bool isConnectingStage = false;
|
||||
|
||||
internal int maxDataBlocksPerWrite = 8;
|
||||
|
||||
#region PLC info getters
|
||||
@@ -17,20 +19,26 @@ namespace MewtocolNet {
|
||||
/// Gets generic information about the PLC
|
||||
/// </summary>
|
||||
/// <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) {
|
||||
|
||||
//timeouts are ok and dont throw
|
||||
//timeouts are ok and don't throw
|
||||
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
|
||||
if (!resRT.Success && resRT == MewtocolFrameResponse.Timeout) return null;
|
||||
@@ -40,20 +48,27 @@ namespace MewtocolNet {
|
||||
//dont overwrite, use first
|
||||
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
|
||||
if (resEXRT.Success && !plcInf.TryExtendFromEXRT(resEXRT.Response)) {
|
||||
//overwrite first with EXRT only on connecting stage
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
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>
|
||||
public async Task<bool> WriteByteRange(int start, byte[] byteArr) {
|
||||
|
||||
if (!IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
string byteString = byteArr.ToHexString();
|
||||
|
||||
var wordLength = byteArr.Length / 2;
|
||||
@@ -121,11 +133,6 @@ namespace MewtocolNet {
|
||||
/// <returns>A byte array of the requested DT area</returns>
|
||||
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
|
||||
var wordLength = byteCount / 2;
|
||||
if (byteCount % 2 != 0) wordLength++;
|
||||
|
||||
@@ -113,7 +113,10 @@ namespace MewtocolNet {
|
||||
|
||||
try {
|
||||
|
||||
PLCInfo? gotInfo = null;
|
||||
Logger.Log($">> Intial connection start <<", LogLevel.Verbose, this);
|
||||
isConnectingStage = true;
|
||||
|
||||
PLCInfo gotInfo = null;
|
||||
|
||||
if (autoSerial) {
|
||||
|
||||
@@ -129,8 +132,9 @@ namespace MewtocolNet {
|
||||
|
||||
if (gotInfo != null) {
|
||||
|
||||
IsConnected = true;
|
||||
await base.ConnectAsync();
|
||||
OnConnected(gotInfo.Value);
|
||||
OnConnected(gotInfo);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -145,13 +149,15 @@ namespace MewtocolNet {
|
||||
|
||||
OnMajorSocketExceptionWhileConnecting();
|
||||
|
||||
isConnectingStage = false;
|
||||
|
||||
}
|
||||
|
||||
tryingSerialConfig -= OnTryConfig;
|
||||
|
||||
}
|
||||
|
||||
private async Task<PLCInfo?> TryConnectAsyncMulti() {
|
||||
private async Task<PLCInfo> TryConnectAsyncMulti() {
|
||||
|
||||
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 {
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.Logging;
|
||||
using System;
|
||||
using System.Net;
|
||||
@@ -34,7 +33,7 @@ namespace MewtocolNet {
|
||||
public void ConfigureConnection(string ip, int port = 9094, int station = 0xEE) {
|
||||
|
||||
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)
|
||||
throw new NotSupportedException("Station number can't be greater than 99");
|
||||
@@ -66,6 +65,9 @@ namespace MewtocolNet {
|
||||
|
||||
try {
|
||||
|
||||
Logger.Log($">> Intial connection start <<", LogLevel.Verbose, this);
|
||||
isConnectingStage = true;
|
||||
|
||||
if (HostEndpoint != null) {
|
||||
|
||||
client = new TcpClient(HostEndpoint) {
|
||||
@@ -109,9 +111,9 @@ namespace MewtocolNet {
|
||||
|
||||
if (plcinf != null) {
|
||||
|
||||
IsConnected = true;
|
||||
await base.ConnectAsync();
|
||||
|
||||
OnConnected(plcinf.Value);
|
||||
OnConnected(plcinf);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -125,6 +127,7 @@ namespace MewtocolNet {
|
||||
} catch (SocketException) {
|
||||
|
||||
OnMajorSocketExceptionWhileConnecting();
|
||||
isConnectingStage = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Globalization;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MewtocolNet {
|
||||
@@ -6,43 +8,85 @@ namespace MewtocolNet {
|
||||
/// <summary>
|
||||
/// Holds various informations about the PLC
|
||||
/// </summary>
|
||||
public struct PLCInfo {
|
||||
public class PLCInfo : INotifyPropertyChanged {
|
||||
|
||||
private PlcType typeCode;
|
||||
private string typeName;
|
||||
private OPMode operationMode;
|
||||
private HWInformation hardwareInformation;
|
||||
private string selfDiagnosticError;
|
||||
|
||||
/// <summary>
|
||||
/// The type of the PLC named by Panasonic
|
||||
/// </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>
|
||||
/// Contains information about the PLCs operation modes as flags
|
||||
/// The full qualified name of the PLC
|
||||
/// </summary>
|
||||
public OPMode OperationMode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hardware information flags about the PLC
|
||||
/// </summary>
|
||||
public HWInformation HardwareInformation { get; private set; }
|
||||
public string TypeName => typeName;
|
||||
|
||||
/// <summary>
|
||||
/// Program capacity in 1K steps
|
||||
/// </summary>
|
||||
public int ProgramCapacity { get; private set; }
|
||||
public int ProgramCapacity { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version of the cpu
|
||||
/// </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>
|
||||
/// Current error code of the PLC
|
||||
/// </summary>
|
||||
public string SelfDiagnosticError { get; internal set; }
|
||||
public string SelfDiagnosticError {
|
||||
get => selfDiagnosticError;
|
||||
internal set {
|
||||
selfDiagnosticError = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quickcheck for the runmode flag
|
||||
/// </summary>
|
||||
public bool IsRunMode => OperationMode.HasFlag(OPMode.RunMode);
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
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);
|
||||
@@ -114,7 +158,25 @@ namespace MewtocolNet {
|
||||
|
||||
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">
|
||||
/// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
|
||||
/// </typeparam>
|
||||
public TempRegister<T> AsType<T>(int? sizeHint = null) {
|
||||
public TypedRegister AsType<T>() {
|
||||
|
||||
if (!typeof(T).IsAllowedPlcCastingType()) {
|
||||
|
||||
@@ -313,10 +313,9 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
}
|
||||
|
||||
Data.byteSizeHint = (uint?)sizeHint;
|
||||
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">
|
||||
/// <include file="../Documentation/docs.xml" path='extradoc/class[@name="support-conv-types"]/*' />
|
||||
/// </param>
|
||||
public TempRegister AsType(Type type) {
|
||||
public TypedRegister AsType(Type type) {
|
||||
|
||||
//was ranged syntax array build
|
||||
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
|
||||
if (Data.byteSizeHint % byteSizePerItem != 0) {
|
||||
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
|
||||
new int[] { (int)((Data.byteSizeHint / byteSizePerItem)) }
|
||||
});
|
||||
@@ -369,9 +368,9 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
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) {
|
||||
|
||||
@@ -389,18 +388,18 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
Data.dotnetVarType = type;
|
||||
|
||||
return new TempRegister(Data, builder);
|
||||
return new TypedRegister().Map(this);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the register type as a predefined <see cref="PlcVarType"/>
|
||||
/// </summary>
|
||||
public TempRegister AsType(PlcVarType type) {
|
||||
public TypedRegister AsType(PlcVarType type) {
|
||||
|
||||
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>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public TempRegister AsType(string type) {
|
||||
public TypedRegister AsType(string type) {
|
||||
|
||||
var stringMatch = Regex.Match(type, @"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 regexString = new Regex(@"^STRING *\[(?<len>[0-9]*)\]$", 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)) {
|
||||
|
||||
@@ -440,10 +442,26 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
//invoke generic AsTypeArray
|
||||
|
||||
string arrTypeString = arrayMatch.Groups["t"].Value;
|
||||
Type dotnetArrType = null;
|
||||
|
||||
if (Enum.TryParse<PlcVarType>(arrTypeString, out var parsedArrType)) {
|
||||
var stringMatchInArray = regexString.Match(arrTypeString);
|
||||
|
||||
if (Enum.TryParse<PlcVarType>(arrTypeString, out var parsedArrType) && parsedArrType != PlcVarType.STRING) {
|
||||
|
||||
dotnetArrType = parsedArrType.GetDefaultDotnetType();
|
||||
|
||||
|
||||
} else if (stringMatchInArray.Success) {
|
||||
|
||||
dotnetArrType = typeof(string);
|
||||
//Data.byteSizeHint = uint.Parse(stringMatch.Groups["len"].Value);
|
||||
|
||||
} else {
|
||||
|
||||
throw new NotSupportedException($"The FP type '{arrTypeString}' was not recognized");
|
||||
|
||||
}
|
||||
|
||||
var dotnetArrType = parsedArrType.GetDefaultDotnetType();
|
||||
var indices = new List<int>();
|
||||
|
||||
for (int i = 1; i < 4; i++) {
|
||||
@@ -465,7 +483,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
MethodInfo method = typeof(SAddress).GetMethod(nameof(AsTypeArray));
|
||||
MethodInfo generic = method.MakeGenericMethod(arrType);
|
||||
|
||||
var tmp = (TempRegister)generic.Invoke(this, new object[] {
|
||||
var tmp = (TypedRegister)generic.Invoke(this, new object[] {
|
||||
indices.ToArray()
|
||||
});
|
||||
|
||||
@@ -474,18 +492,13 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
return tmp;
|
||||
|
||||
} else {
|
||||
|
||||
throw new NotSupportedException($"The FP type '{arrTypeString}' was not recognized");
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
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 [5..6, 0..2] OF DWORD = <c>AsTypeArray<DWord[,]>(2, 3)</c><br/>
|
||||
/// </example>
|
||||
public TempRegister AsTypeArray<T>(params int[] indicies) {
|
||||
public TypedRegister AsTypeArray<T>(params int[] indicies) {
|
||||
|
||||
if (!typeof(T).IsArray)
|
||||
throw new NotSupportedException($"The type {typeof(T)} was no array");
|
||||
@@ -526,7 +539,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
Data.dotnetVarType = typeof(T);
|
||||
|
||||
int byteSizePerItem = elBaseType.DetermineTypeByteSize();
|
||||
int byteSizePerItem = elBaseType.DetermineTypeByteIntialSize();
|
||||
int calcedTotalByteSize = indicies.Aggregate((a, x) => a * x) * byteSizePerItem;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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>
|
||||
/// Sets the poll level of the register
|
||||
/// </summary>
|
||||
public TempRegister<T> PollLevel(int level) {
|
||||
public OptionsRegister PollLevel(int 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;
|
||||
|
||||
}
|
||||
|
||||
@@ -34,11 +34,16 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
built.Data.typeDef = typeDef;
|
||||
built.Data.buildSource = RegisterBuildSource.Attribute;
|
||||
built.Data.regCollection = regCol;
|
||||
built.Data.boundProperty = prop;
|
||||
built.Data.byteSizeHint = bytesizeHint;
|
||||
|
||||
return built;
|
||||
|
||||
}
|
||||
@@ -49,15 +54,38 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
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
|
||||
|
||||
public new class TempRegister<T> : RBuildBase.TempRegister<T> {
|
||||
public new class OptionsRegister : RBuildBase.OptionsRegister {
|
||||
|
||||
internal TempRegister() { }
|
||||
|
||||
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));
|
||||
///<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 TempRegister<T> Out(Action<IRegister> registerOut) {
|
||||
public void Out(Action<IRegister> 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
|
||||
|
||||
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());
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
@@ -77,33 +51,6 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -50,7 +49,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
Type elementType = data.dotnetVarType.GetElementType();
|
||||
|
||||
uint numericSizePerElement = (uint)elementType.DetermineTypeByteSize();
|
||||
uint numericSizePerElement = (uint)elementType.DetermineTypeByteIntialSize();
|
||||
|
||||
if (elementType.IsEnum && numericSizePerElement > 4) {
|
||||
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[] {
|
||||
data.memAddress,
|
||||
data.byteSizeHint,
|
||||
data.arrayIndicies,
|
||||
sizeStateFlags,
|
||||
data.name
|
||||
};
|
||||
|
||||
@@ -89,7 +73,6 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
typeof(uint),
|
||||
typeof(uint),
|
||||
typeof(int[]),
|
||||
typeof(DynamicSizeState),
|
||||
typeof(string)
|
||||
}, null);
|
||||
|
||||
@@ -107,7 +90,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
//-------------------------------------------
|
||||
//as single register
|
||||
|
||||
uint numericSize = (uint)data.dotnetVarType.DetermineTypeByteSize();
|
||||
uint numericSize = (uint)data.dotnetVarType.DetermineTypeByteIntialSize();
|
||||
|
||||
if (data.dotnetVarType.IsEnum && numericSize > 4) {
|
||||
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.dotnetVarType == typeof(string) && data.byteSizeHint != null) {
|
||||
if(data.byteSizeHint == null)
|
||||
throw new NotSupportedException($"Can't create a STRING register without a string size hint");
|
||||
|
||||
numericSize = (uint)data.byteSizeHint + 4;
|
||||
sizeStateFlags = DynamicSizeState.DynamicallySized | DynamicSizeState.WasSizeUpdated;
|
||||
if(data.byteSizeHint < 0)
|
||||
throw new NotSupportedException($"Can't create a STRING register with a string size hint < 0");
|
||||
|
||||
} else if (data.dotnetVarType == typeof(string)) {
|
||||
|
||||
sizeStateFlags = DynamicSizeState.DynamicallySized | DynamicSizeState.NeedsSizeUpdate;
|
||||
numericSize = 4 + data.byteSizeHint.Value;
|
||||
|
||||
}
|
||||
|
||||
@@ -135,13 +116,12 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
|
||||
Type paramedClass = typeof(SingleRegister<>).MakeGenericType(data.dotnetVarType);
|
||||
ConstructorInfo constr = paramedClass.GetConstructor(flags, null, new Type[] {
|
||||
typeof(uint), typeof(uint), typeof(DynamicSizeState) ,typeof(string)
|
||||
typeof(uint), typeof(uint) ,typeof(string)
|
||||
}, null);
|
||||
|
||||
var parameters = new object[] {
|
||||
data.memAddress,
|
||||
numericSize,
|
||||
sizeStateFlags,
|
||||
data.name
|
||||
};
|
||||
|
||||
@@ -167,7 +147,7 @@ namespace MewtocolNet.RegisterBuilding {
|
||||
//finalize set for every
|
||||
|
||||
if (generatedInstance == null)
|
||||
throw new MewtocolException("Failed to build register");
|
||||
throw new ArgumentException("Failed to build register");
|
||||
|
||||
if (collectionTarget != null)
|
||||
generatedInstance.WithRegisterCollection(collectionTarget);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using MewtocolNet.Exceptions;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -12,7 +12,7 @@ namespace MewtocolNet.Registers {
|
||||
/// </summary>
|
||||
public class ArrayRegister<T> : Register {
|
||||
|
||||
internal int[] indicies;
|
||||
internal int[] indices;
|
||||
|
||||
internal uint addressLength;
|
||||
|
||||
@@ -25,12 +25,11 @@ namespace MewtocolNet.Registers {
|
||||
public ArrayRegister() =>
|
||||
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;
|
||||
memoryAddress = _address;
|
||||
dynamicSizeState = dynamicSizeSt;
|
||||
indicies = _indicies;
|
||||
indices = _indicies;
|
||||
|
||||
//calc mem length
|
||||
//because one register is always 1 word (2 bytes) long, if the bytecount is uneven we get the trailing word too
|
||||
@@ -50,10 +49,25 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
if (Value == null) return "null";
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetRegisterString() => "DT";
|
||||
|
||||
@@ -63,9 +77,6 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override async Task<bool> WriteAsync(object value) {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var encoded = PlcValueParser.Encode(this, (T)value);
|
||||
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
||||
|
||||
@@ -90,9 +101,6 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override async Task<object> ReadAsync() {
|
||||
|
||||
if (!attachedInterface.IsConnected)
|
||||
throw MewtocolException.NotConnectedSend();
|
||||
|
||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
||||
if (res == null) return null;
|
||||
|
||||
@@ -110,8 +118,9 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
AddSuccessRead();
|
||||
|
||||
var parsed = PlcValueParser.ParseArray<T>(this, indicies, bytes);
|
||||
var parsed = PlcValueParser.ParseArray<T>(this, indices, bytes);
|
||||
UpdateHoldingValue(parsed);
|
||||
|
||||
return parsed;
|
||||
|
||||
}
|
||||
@@ -127,15 +136,63 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
if (changeTriggerBitArr || changeTriggerGeneral) {
|
||||
|
||||
var beforeVal = lastValue;
|
||||
var beforeValStr = GetValueString();
|
||||
|
||||
lastValue = val;
|
||||
|
||||
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.Threading.Tasks;
|
||||
using MewtocolNet.Events;
|
||||
|
||||
namespace MewtocolNet.Registers {
|
||||
|
||||
@@ -11,7 +12,7 @@ namespace MewtocolNet.Registers {
|
||||
/// <summary>
|
||||
/// Gets called whenever the value was changed
|
||||
/// </summary>
|
||||
event Action<object> ValueChanged;
|
||||
event RegisterChangedEventHandler ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Type of the underlying register
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using MewtocolNet.Events;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using MewtocolNet.UnderlyingRegisters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -14,7 +15,7 @@ namespace MewtocolNet.Registers {
|
||||
/// <summary>
|
||||
/// Gets called whenever the value was changed
|
||||
/// </summary>
|
||||
public event Action<object> ValueChanged;
|
||||
public event RegisterChangedEventHandler ValueChanged;
|
||||
|
||||
//links to
|
||||
internal RegisterCollection containedCollection;
|
||||
@@ -26,8 +27,6 @@ namespace MewtocolNet.Registers {
|
||||
internal IMemoryArea underlyingMemory;
|
||||
internal bool autoGenerated;
|
||||
|
||||
internal DynamicSizeState dynamicSizeState;
|
||||
|
||||
internal object lastValue = null;
|
||||
internal string name;
|
||||
internal uint memoryAddress;
|
||||
@@ -73,10 +72,13 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
if (lastValue?.ToString() != val?.ToString()) {
|
||||
|
||||
var beforeVal = lastValue;
|
||||
var beforeValStr = GetValueString();
|
||||
|
||||
lastValue = val;
|
||||
|
||||
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.Collections;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -14,6 +16,8 @@ namespace MewtocolNet.Registers {
|
||||
/// <typeparam name="T">The type of the numeric value</typeparam>
|
||||
public class SingleRegister<T> : Register {
|
||||
|
||||
internal uint byteLength;
|
||||
|
||||
internal uint addressLength;
|
||||
|
||||
/// <summary>
|
||||
@@ -21,17 +25,15 @@ namespace MewtocolNet.Registers {
|
||||
/// </summary>
|
||||
public uint AddressLength => addressLength;
|
||||
|
||||
|
||||
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
|
||||
public SingleRegister() =>
|
||||
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;
|
||||
name = _name;
|
||||
dynamicSizeState = dynamicSizeSt;
|
||||
addressLength = _reservedByteSize / 2;
|
||||
Resize(_reservedByteSize);
|
||||
|
||||
if (_reservedByteSize == 2) RegisterType = RegisterType.DT;
|
||||
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/>
|
||||
public override string GetAsPLC() {
|
||||
|
||||
@@ -55,27 +65,23 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
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) {
|
||||
if (Value != null && typeof(T).IsEnum && !hasFlags) {
|
||||
|
||||
var underlying = Enum.GetUnderlyingType(typeof(T));
|
||||
object val = Convert.ChangeType(Value, underlying);
|
||||
|
||||
return $"{Value} [{val}]";
|
||||
|
||||
}
|
||||
|
||||
if (Value != null && typeof(T).IsEnum && hasFlags) return $"{Value}";
|
||||
|
||||
return Value?.ToString() ?? "null";
|
||||
|
||||
}
|
||||
@@ -86,12 +92,6 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
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 res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
||||
|
||||
@@ -116,12 +116,6 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
if (res == null) return null;
|
||||
|
||||
@@ -132,54 +126,32 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
if (matchingReg is SingleRegister<string> sreg && this is SingleRegister<string> selfSreg) {
|
||||
sreg.addressLength = selfSreg.addressLength;
|
||||
sreg.dynamicSizeState = DynamicSizeState.DynamicallySized | DynamicSizeState.WasSizeUpdated;
|
||||
}
|
||||
|
||||
matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return SetValueFromBytes(res);
|
||||
|
||||
}
|
||||
|
||||
internal override async Task UpdateDynamicSize() {
|
||||
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
//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}]"
|
||||
);
|
||||
}
|
||||
|
||||
AddSuccessRead();
|
||||
|
||||
var parsed = PlcValueParser.Parse<T>(this, bytes);
|
||||
|
||||
UpdateHoldingValue(parsed);
|
||||
return parsed;
|
||||
|
||||
@@ -189,11 +161,13 @@ namespace MewtocolNet.Registers {
|
||||
|
||||
if (lastValue?.ToString() != val?.ToString()) {
|
||||
|
||||
if (val != null) lastValue = (T)val;
|
||||
else lastValue = null;
|
||||
var beforeVal = lastValue;
|
||||
var beforeValStr = GetValueString();
|
||||
|
||||
lastValue = val;
|
||||
|
||||
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.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -136,9 +135,11 @@ namespace MewtocolNet.TypeConversion {
|
||||
PlcVarType = PlcVarType.STRING,
|
||||
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 System;
|
||||
using System.Collections.Generic;
|
||||
@@ -30,7 +29,7 @@ namespace MewtocolNet {
|
||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingType);
|
||||
|
||||
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);
|
||||
|
||||
@@ -55,7 +54,7 @@ namespace MewtocolNet {
|
||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingElementType);
|
||||
|
||||
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
|
||||
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 indexer = new int[indices.Length];
|
||||
|
||||
for (int i = 0; i < iterateItems; i++) {
|
||||
|
||||
int j = i * sizePerItem;
|
||||
@@ -150,7 +149,7 @@ namespace MewtocolNet {
|
||||
converter = conversions.FirstOrDefault(x => x.GetDotnetType() == underlyingType);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using MewtocolNet.Exceptions;
|
||||
using MewtocolNet.Registers;
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
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) {
|
||||
|
||||
await CheckDynamicallySizedRegistersAsync();
|
||||
|
||||
var station = mewInterface.GetStationNumber();
|
||||
|
||||
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() {
|
||||
|
||||
StringBuilder asciistring = new StringBuilder("D");
|
||||
|
||||
@@ -46,8 +46,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
internal async Task OnPlcConnected () {
|
||||
|
||||
//check all area for dynamic sized registers
|
||||
await CheckAllDynamicallySizedAreas();
|
||||
await Task.CompletedTask;
|
||||
|
||||
}
|
||||
|
||||
@@ -64,13 +63,14 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
}
|
||||
|
||||
//maxes the highest poll level for all registers that contain each other
|
||||
registers
|
||||
var ordered = registers
|
||||
.OrderByDescending(x => x.GetRegisterAddressLen())
|
||||
.ToList()
|
||||
.ForEach(x => x.AveragePollLevel(registers, pollLevelOrMode));
|
||||
.ToList();
|
||||
|
||||
ordered.ForEach(x => x.AveragePollLevel(registers, pollLevelOrMode));
|
||||
|
||||
//insert into area
|
||||
foreach (var reg in registers) {
|
||||
foreach (var reg in ordered) {
|
||||
|
||||
TestPollLevelExistence(reg);
|
||||
|
||||
@@ -278,7 +278,8 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
//check if the linked group has duplicate type registers
|
||||
|
||||
var dupedTypeReg = existinglinkedGroup.Linked.FirstOrDefault(x => x.IsSameAddressAndType(insertReg));
|
||||
if (dupedTypeReg != null) {
|
||||
|
||||
if (dupedTypeReg != null && insertReg.autoGenerated) {
|
||||
dupedTypeReg.WithBoundProperties(insertReg.boundProperties);
|
||||
} else {
|
||||
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() {
|
||||
|
||||
foreach (var pollLevel in pollLevels) {
|
||||
|
||||
Reference in New Issue
Block a user