diff --git a/Examples.WPF/MainWindow.xaml b/Examples.WPF/MainWindow.xaml
index 57867ca..a8b6a41 100644
--- a/Examples.WPF/MainWindow.xaml
+++ b/Examples.WPF/MainWindow.xaml
@@ -9,7 +9,7 @@
MinWidth="500"
MinHeight="400"
Height="850"
- Width="800"
+ Width="1200"
Title="MewtocolNet WPF Demo">
diff --git a/Examples.WPF/RegisterCollections/TestRegisterCollection.cs b/Examples.WPF/RegisterCollections/TestRegisterCollection.cs
new file mode 100644
index 0000000..4ea16b7
--- /dev/null
+++ b/Examples.WPF/RegisterCollections/TestRegisterCollection.cs
@@ -0,0 +1,31 @@
+using MewtocolNet;
+using MewtocolNet.RegisterAttributes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Examples.WPF.RegisterCollections;
+
+public class TestRegisterCollection : RegisterCollection {
+
+ [Register("R11A")]
+ public bool? TestR11A { get; set; }
+
+ [Register("R11A")]
+ public bool TestR11A_Duplicate_NonNullable { get; set; }
+
+ [Register("R16B")]
+ public bool TestR16B { get; set; }
+
+ [BitRegister("DT1000", 0), PollLevel(3)]
+ public bool? TestDT100_Word_Duplicate_SingleBit { get; set; }
+
+ [Register("DT1000")]
+ public Word TestDT100_Word_Duplicate { get; set; }
+
+ [BitRegister("DDT1010", 1)]
+ public bool? TestDDT1010_DWord_Duplicate_SingleBit { get; set; }
+
+}
diff --git a/Examples.WPF/ViewModels/AppViewModel.cs b/Examples.WPF/ViewModels/AppViewModel.cs
index 014db4a..4689fd0 100644
--- a/Examples.WPF/ViewModels/AppViewModel.cs
+++ b/Examples.WPF/ViewModels/AppViewModel.cs
@@ -1,4 +1,5 @@
-using MewtocolNet;
+using Examples.WPF.RegisterCollections;
+using MewtocolNet;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -10,6 +11,7 @@ namespace Examples.WPF.ViewModels {
public class AppViewModel : ViewModelBase {
private IPlc? plc;
+ private TestRegisterCollection testRegCollection = null!;
public bool PlcIsNull => plc == null;
@@ -25,6 +27,14 @@ namespace Examples.WPF.ViewModels {
}
}
+ public TestRegisterCollection TestRegCollection {
+ get => testRegCollection;
+ set {
+ testRegCollection = value;
+ OnPropChange();
+ }
+ }
+
}
}
diff --git a/Examples.WPF/ViewModels/PlcDataViewViewModel.cs b/Examples.WPF/ViewModels/PlcDataViewViewModel.cs
index 7cacd07..1cef18a 100644
--- a/Examples.WPF/ViewModels/PlcDataViewViewModel.cs
+++ b/Examples.WPF/ViewModels/PlcDataViewViewModel.cs
@@ -1,4 +1,5 @@
-using MewtocolNet;
+using Examples.WPF.RegisterCollections;
+using MewtocolNet;
using MewtocolNet.Events;
using System;
using System.Collections.Generic;
@@ -14,6 +15,8 @@ public class PlcDataViewViewModel : ViewModelBase {
public IPlc Plc => App.ViewModel.Plc!;
+ public TestRegisterCollection RegCollection => App.ViewModel.TestRegCollection;
+
public ReconnectArgs PlcCurrentReconnectArgs {
get => plcCurrentReconnectArgs;
set {
diff --git a/Examples.WPF/Views/ConnectView.xaml.cs b/Examples.WPF/Views/ConnectView.xaml.cs
index adc4f35..b438b2c 100644
--- a/Examples.WPF/Views/ConnectView.xaml.cs
+++ b/Examples.WPF/Views/ConnectView.xaml.cs
@@ -1,4 +1,5 @@
-using Examples.WPF.ViewModels;
+using Examples.WPF.RegisterCollections;
+using Examples.WPF.ViewModels;
using MewtocolNet;
using MewtocolNet.ComCassette;
using MewtocolNet.Registers;
@@ -55,31 +56,46 @@ public partial class ConnectView : UserControl {
var parsedInt = int.Parse(viewModel.SelectedPort);
IRegister heartbeatSetter = null!;
+ IRegister outputContactReference = null!;
+ IRegister testBoolReference = null!;
+ IRegister wordRefTest = null!;
+ //build a new interface
App.ViewModel.Plc = Mewtocol.Ethernet(viewModel.SelectedIP, parsedInt)
.WithPoller()
.WithInterfaceSettings(setting => {
- setting.TryReconnectAttempts = 0;
+
+ setting.TryReconnectAttempts = 10;
setting.TryReconnectDelayMs = 2000;
setting.SendReceiveTimeoutMs = 1000;
setting.HeartbeatIntervalMs = 3000;
- setting.MaxDataBlocksPerWrite = 12;
+ setting.MaxDataBlocksPerWrite = 20;
setting.MaxOptimizationDistance = 10;
+
})
.WithCustomPollLevels(lvl => {
+
lvl.SetLevel(2, 3);
lvl.SetLevel(3, TimeSpan.FromSeconds(5));
lvl.SetLevel(4, TimeSpan.FromSeconds(10));
+
+ })
+ .WithRegisterCollections(collector => {
+
+ App.ViewModel.TestRegCollection = collector.AddCollection();
+
})
.WithRegisters(b => {
- //b.Struct("DT0").Build();
- //b.Struct("DT0").AsArray(30).Build();
-
- b.Bool("R10A").Build();
+ b.Bool("X4").Build();
+ b.Bool("Y4").Build(out outputContactReference);
+ b.Bool("R10A").PollLevel(PollLevel.FirstIteration).Build(out testBoolReference);
b.Struct("DT1000").Build(out heartbeatSetter);
- b.Struct("DT1000").Build();
+
+ //these will be merged into one
+ b.Struct("DT1000").Build(out wordRefTest);
+ b.Struct("DT1000").Build(out wordRefTest);
b.Struct("DT1001").PollLevel(2).Build();
b.Struct("DT1002").PollLevel(2).Build();
@@ -98,9 +114,16 @@ public partial class ConnectView : UserControl {
await heartbeatSetter.WriteAsync((short)new Random().Next(short.MinValue, short.MaxValue));
+ if (outputContactReference.Value != null)
+ await outputContactReference.WriteAsync(!outputContactReference.Value.Value);
+
+ if(testBoolReference.Value != null)
+ await testBoolReference.WriteAsync(!testBoolReference.Value.Value);
+
})
.Build();
+ //connect to it
await App.ViewModel.Plc.ConnectAsync();
if (App.ViewModel.Plc.IsConnected) {
diff --git a/Examples.WPF/Views/PlcDataView.xaml b/Examples.WPF/Views/PlcDataView.xaml
index d0ca91c..d3951c0 100644
--- a/Examples.WPF/Views/PlcDataView.xaml
+++ b/Examples.WPF/Views/PlcDataView.xaml
@@ -145,25 +145,148 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MewtocolNet/CustomTypes/DWord.cs b/MewtocolNet/CustomTypes/DWord.cs
index d7c10ea..2dcd7cd 100644
--- a/MewtocolNet/CustomTypes/DWord.cs
+++ b/MewtocolNet/CustomTypes/DWord.cs
@@ -53,10 +53,14 @@ namespace MewtocolNet {
///
public bool this[int bitIndex] {
get {
- if (bitIndex > bitLength - 1)
- throw new IndexOutOfRangeException($"The DWord bit index was out of range ({bitIndex}/{bitLength - 1})");
+
+ if (bitIndex > bitLength - 1 && bitLength != 0)
+ throw new IndexOutOfRangeException($"The word bit index was out of range ({bitIndex}/{bitLength - 1})");
+
+ if (bitLength == 0) return false;
return (value & (1 << bitIndex)) != 0;
+
}
}
diff --git a/MewtocolNet/CustomTypes/Word.cs b/MewtocolNet/CustomTypes/Word.cs
index d53e6b6..82c7ae3 100644
--- a/MewtocolNet/CustomTypes/Word.cs
+++ b/MewtocolNet/CustomTypes/Word.cs
@@ -53,10 +53,14 @@ namespace MewtocolNet {
///
public bool this[int bitIndex] {
get {
- if (bitIndex > bitLength - 1)
+
+ if (bitIndex > bitLength - 1 && bitLength != 0)
throw new IndexOutOfRangeException($"The word bit index was out of range ({bitIndex}/{bitLength - 1})");
+ if (bitLength == 0) return false;
+
return (value & (1 << bitIndex)) != 0;
+
}
}
diff --git a/MewtocolNet/Helpers/MewtocolHelpers.cs b/MewtocolNet/Helpers/MewtocolHelpers.cs
index c0878fd..b5c840e 100644
--- a/MewtocolNet/Helpers/MewtocolHelpers.cs
+++ b/MewtocolNet/Helpers/MewtocolHelpers.cs
@@ -139,11 +139,11 @@ namespace MewtocolNet {
///
///
/// A or null of failed
- internal static byte[] ParseDTRawStringAsBytes(this string _onString) {
+ internal static byte[] ParseResponseStringAsBytes(this string _onString) {
_onString = _onString.Replace("\r", "");
- var res = new Regex(@"\%([0-9a-fA-F]{2})\$(?:RD|RP)(?.*)(?..)").Match(_onString);
+ var res = new Regex(@"\%([0-9a-fA-F]{2})\$(?:RD|RP|RC)(?.*)(?..)").Match(_onString);
if (res.Success) {
string val = res.Groups["data"].Value;
diff --git a/MewtocolNet/Mewtocol.cs b/MewtocolNet/Mewtocol.cs
index e7ffe2a..8dc1d6c 100644
--- a/MewtocolNet/Mewtocol.cs
+++ b/MewtocolNet/Mewtocol.cs
@@ -10,6 +10,7 @@ using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
+using static MewtocolNet.Mewtocol;
namespace MewtocolNet
{
@@ -210,21 +211,21 @@ namespace MewtocolNet
internal List collections = new List();
- public RegCollector AddCollection(RegisterCollection collection) {
+ public T AddCollection(T collection) where T : RegisterCollection {
collections.Add(collection);
- return this;
+ return collection;
}
- public RegCollector AddCollection() where T : RegisterCollection {
+ public T AddCollection() where T : RegisterCollection {
var instance = (RegisterCollection)Activator.CreateInstance(typeof(T));
collections.Add(instance);
- return this;
+ return (T)instance;
}
@@ -354,7 +355,7 @@ namespace MewtocolNet
///
/// A builder for attaching register collections
///
- public PostRegisterSetup WithRegisterCollections(Action collector) {
+ public PostInit WithRegisterCollections(Action collector) {
try {
@@ -365,9 +366,7 @@ namespace MewtocolNet
imew.WithRegisterCollections(res.collections);
}
- return new PostRegisterSetup {
- postInit = this
- };
+ return this;
} catch {
@@ -380,7 +379,7 @@ namespace MewtocolNet
///
/// A builder for attaching register collections
///
- public PostRegisterSetup WithRegisters(Action builder) {
+ public PostInit WithRegisters(Action builder) {
try {
@@ -391,7 +390,31 @@ namespace MewtocolNet
plc.AddRegisters(regBuilder.assembler.assembled.ToArray());
- return new PostRegisterSetup {
+ return this;
+
+ } catch {
+
+ throw;
+
+ }
+
+ }
+
+ ///
+ /// Repeats the passed method each time the hearbeat is triggered,
+ /// use
+ ///
+ ///
+ ///
+ public EndInitSetup WithHeartbeatTask(Func heartBeatAsync, bool executeInProg = false) {
+ try {
+
+ var plc = (MewtocolInterface)(object)this.intf;
+
+ plc.heartbeatCallbackTask = heartBeatAsync;
+ plc.execHeartBeatCallbackTaskInProg = executeInProg;
+
+ return new EndInitSetup {
postInit = this,
};
@@ -406,63 +429,22 @@ namespace MewtocolNet
///
/// Builds and returns the final plc interface
///
- public T Build() => intf;
+ public T Build() => (T)(object)((MewtocolInterface)(object)intf).Build();
}
#endregion
- public class PostRegisterSetup {
-
- internal PostInit postInit;
-
- ///
- /// Repeats the passed method each time the hearbeat is triggered,
- /// use
- ///
- ///
- ///
- public EndInitSetup WithHeartbeatTask(Func heartBeatAsync, bool executeInProg = false) {
-
- try {
-
- var plc = (MewtocolInterface)(object)postInit.intf;
-
- plc.heartbeatCallbackTask = heartBeatAsync;
- plc.execHeartBeatCallbackTaskInProg = executeInProg;
-
- return new EndInitSetup {
- postInit = this.postInit,
- postRegSetupInit = this
- };
-
- } catch {
-
- throw;
-
- }
-
- }
-
- ///
- /// Builds and returns the final plc interface
- ///
- public T Build() => postInit.intf;
-
- }
-
#region Interface building step 4
public class EndInitSetup {
internal PostInit postInit;
- internal PostRegisterSetup postRegSetupInit;
-
///
/// Builds and returns the final plc interface
///
- public T Build() => postInit.intf;
+ public T Build() => (T)(object)((MewtocolInterface)(object)postInit.intf).Build();
}
diff --git a/MewtocolNet/MewtocolInterface.cs b/MewtocolNet/MewtocolInterface.cs
index 45de15e..50f7e69 100644
--- a/MewtocolNet/MewtocolInterface.cs
+++ b/MewtocolNet/MewtocolInterface.cs
@@ -193,6 +193,14 @@ namespace MewtocolNet {
}
+ internal MewtocolInterface Build () {
+
+ memoryManager.LinkAndMergeRegisters();
+
+ return this;
+
+ }
+
private void MewtocolInterface_Connected(object sender, PlcConnectionArgs args) {
IsConnected = true;
@@ -226,8 +234,6 @@ namespace MewtocolNet {
}
- OnRegisterChangedUpdateProps(asInternal);
-
}
///
@@ -245,7 +251,7 @@ namespace MewtocolNet {
Logger.Log($"DIAG ERR: {PlcInfo.SelfDiagnosticError}", LogLevel.Verbose, this);
Logger.Log($"CPU VER: {PlcInfo.CpuVersion}", LogLevel.Verbose, this);
- if(alwaysGetMetadata) {
+ if(alwaysGetMetadata && PlcInfo.Metadata != null) {
Logger.LogVerbose($"METADATA: {PlcInfo.Metadata.MetaDataVersion}", this);
Logger.LogVerbose($"FP-WIN VERSION: {PlcInfo.Metadata.FPWinVersion}", this);
diff --git a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
index 0fb4d56..dcc9163 100644
--- a/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
+++ b/MewtocolNet/MewtocolInterfaceRegisterHandling.cs
@@ -242,57 +242,6 @@ namespace MewtocolNet {
#endregion
- #region Smart register polling methods
-
- [Obsolete]
- private async Task UpdateRCPRegisters() {
-
- //build booleans
- //var rcpList = RegistersUnderlying.Where(x => x.GetType() == typeof(BoolRegister))
- // .Select(x => (BoolRegister)x)
- // .ToArray();
-
- ////one frame can only read 8 registers at a time
- //int rcpFrameCount = (int)Math.Ceiling((double)rcpList.Length / 8);
- //int rcpLastFrameRemainder = rcpList.Length <= 8 ? rcpList.Length : rcpList.Length % 8;
-
- //for (int i = 0; i < rcpFrameCount; i++) {
-
- // int toReadRegistersCount = 8;
-
- // if (i == rcpFrameCount - 1) toReadRegistersCount = rcpLastFrameRemainder;
-
- // var rcpString = new StringBuilder($"%{GetStationNumber()}#RCP{toReadRegistersCount}");
-
- // for (int j = 0; j < toReadRegistersCount; j++) {
-
- // BoolRegister register = rcpList[i + j];
- // rcpString.Append(register.BuildMewtocolQuery());
-
- // }
-
- // string rcpRequest = rcpString.ToString();
- // var result = await SendCommandAsync(rcpRequest);
- // if (!result.Success) return;
-
- // var resultBitArray = result.Response.ParseRCMultiBit();
-
- // for (int k = 0; k < resultBitArray.Length; k++) {
-
- // var register = rcpList[i + k];
-
- // if ((bool)register.Value != resultBitArray[k]) {
- // register.SetValueFromPLC(resultBitArray[k]);
- // }
-
- // }
-
- //}
-
- }
-
- #endregion
-
#region Register Collection adding
///
@@ -331,9 +280,10 @@ namespace MewtocolNet {
//add builder item
regBuild
- .AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef, collection, prop, byteHint)
+ .AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef, collection, prop, cAttribute, byteHint)
.AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType)
- .PollLevel(pollLevel);
+ .PollLevel(pollLevel)
+ .Finalize();
}
@@ -353,27 +303,7 @@ namespace MewtocolNet {
}
- var assembler = new RegisterAssembler(this);
-
- AddRegisters(assembler.assembled.ToArray());
-
- }
-
- ///
- /// Writes back the values changes of the underlying registers to the corrosponding property
- ///
- private void OnRegisterChangedUpdateProps(Register reg) {
-
- var collection = reg.ContainedCollection;
- if (collection == null) return;
-
- var props = collection.GetType().GetProperties();
-
- //set the specific bit array if needed
- //prop.SetValue(collection, bitAr);
- //collection.TriggerPropertyChanged(prop.Name);
-
-
+ AddRegisters(regBuild.assembler.assembled.ToArray());
}
@@ -383,16 +313,7 @@ namespace MewtocolNet {
internal void AddRegisters(params Register[] registers) {
- InsertRegistersToMemoryStack(registers.ToList());
-
- }
-
- internal void InsertRegistersToMemoryStack(List registers) {
-
- memoryManager.LinkAndMergeRegisters(registers);
-
- //run a second iteration
- //memoryManager.LinkAndMergeRegisters();
+ memoryManager.LinkAndMergeRegisters(registers.ToList());
}
diff --git a/MewtocolNet/MewtocolInterfaceRequests.cs b/MewtocolNet/MewtocolInterfaceRequests.cs
index 3c9bde9..1db3047 100644
--- a/MewtocolNet/MewtocolInterfaceRequests.cs
+++ b/MewtocolNet/MewtocolInterfaceRequests.cs
@@ -9,6 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using MewtocolNet.Helpers;
using System.Reflection;
+using System.Runtime.InteropServices.ComTypes;
namespace MewtocolNet {
@@ -98,7 +99,7 @@ namespace MewtocolNet {
var metaMarker = new byte[] { 0x4D, 0x65, 0x74, 0x41 };
- var data = await ReadByteRangeNonBlocking(endAddress - 2 - (readBytes / 2), readBytes);
+ var data = await ReadAreaByteRangeAsync(endAddress - 2 - (readBytes / 2), readBytes);
if (data != null && data.SearchBytePattern(metaMarker) == readBytes - 4) {
@@ -204,7 +205,7 @@ namespace MewtocolNet {
if (res.Success) {
- var bytes = res.Response.ParseDTRawStringAsBytes();
+ var bytes = res.Response.ParseResponseStringAsBytes();
var foundEndPattern = bytes.SearchBytePattern(new byte[] { 0xF8, 0xFF, 0xFF });
for (int j = 0; j < bytes.Length; j += 2) {
@@ -243,7 +244,7 @@ namespace MewtocolNet {
/// /// start address of the array
///
///
- public async Task WriteByteRange(int start, byte[] byteArr) {
+ public async Task WriteAreaByteRange(int start, byte[] byteArr) {
if (byteArr == null)
throw new ArgumentNullException(nameof(byteArr));
@@ -271,7 +272,7 @@ namespace MewtocolNet {
/// Number of bytes to get
/// Gets invoked when the progress changes, contains the progress as a double from 0 - 1.0
/// A byte array of the requested DT area
- public async Task ReadByteRangeNonBlocking(int start, int byteCount, Action onProgress = null) {
+ public async Task ReadAreaByteRangeAsync(int start, int byteCount, RegisterPrefix areaPrefix = RegisterPrefix.DT, Action onProgress = null) {
//on odd bytes add one word
var wordLength = byteCount / 2;
@@ -287,19 +288,41 @@ namespace MewtocolNet {
List readBytes = new List();
+ int padLeftLen = 0;
+ string areaCodeStr = null;
+
+ switch (areaPrefix) {
+ case RegisterPrefix.X:
+ areaCodeStr = $"RCCX";
+ padLeftLen = 4;
+ break;
+ case RegisterPrefix.Y:
+ areaCodeStr = $"RCCY";
+ padLeftLen = 4;
+ break;
+ case RegisterPrefix.R:
+ areaCodeStr = $"RCCR";
+ padLeftLen = 4;
+ break;
+ case RegisterPrefix.DT:
+ case RegisterPrefix.DDT:
+ areaCodeStr = $"RDD";
+ padLeftLen = 5;
+ break;
+ }
+
async Task ReadBlock(int wordStart, int wordEnd, Action readProg) {
int blockSize = wordEnd - wordStart + 1;
- string startStr = wordStart.ToString().PadLeft(5, '0');
- string endStr = wordEnd.ToString().PadLeft(5, '0');
-
- string requeststring = $"%{GetStationNumber()}#RDD{startStr}{endStr}";
+ string startStr = wordStart.ToString().PadLeft(padLeftLen, '0');
+ string endStr = wordEnd.ToString().PadLeft(padLeftLen, '0');
+ string requeststring = $"%{GetStationNumber()}#{areaCodeStr}{startStr}{endStr}";
var result = await SendCommandInternalAsync(requeststring, onReceiveProgress: readProg);
if (result.Success && !string.IsNullOrEmpty(result.Response)) {
- var bytes = result.Response.ParseDTRawStringAsBytes();
+ var bytes = result.Response.ParseResponseStringAsBytes();
if (bytes != null)
readBytes.AddRange(bytes);
diff --git a/MewtocolNet/RegisterAttributes/BitRegisterAttribute.cs b/MewtocolNet/RegisterAttributes/BitRegisterAttribute.cs
new file mode 100644
index 0000000..f8322dd
--- /dev/null
+++ b/MewtocolNet/RegisterAttributes/BitRegisterAttribute.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace MewtocolNet.RegisterAttributes {
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
+ public class BitRegisterAttribute : RegisterAttribute {
+
+ internal int bitIndex;
+
+ ///
+ /// Builds automatic data transfer between the property below this and
+ /// the plc register
+ ///
+ /// The FP-Address (DT, DDT, R, X, Y..)
+ /// The type definition from the PLC (STRING[n], ARRAY [0..2] OF ...)
+ public BitRegisterAttribute(string mewAddress, byte bitIndex) : base(mewAddress, null) {
+
+ this.bitIndex = bitIndex;
+
+ }
+
+ }
+
+}
diff --git a/MewtocolNet/RegisterAttributes/RegisterPropTarget.cs b/MewtocolNet/RegisterAttributes/RegisterPropTarget.cs
index ddafcbb..adebb5a 100644
--- a/MewtocolNet/RegisterAttributes/RegisterPropTarget.cs
+++ b/MewtocolNet/RegisterAttributes/RegisterPropTarget.cs
@@ -8,15 +8,14 @@ namespace MewtocolNet.RegisterAttributes {
//propinfo of the bound property
internal PropertyInfo BoundProperty;
- //general number of bits or bytes to read back to the prop
- internal int? LinkLength;
+ internal RegisterAttribute PropertyAttribute;
+
+ internal RegisterCollection ContainedCollection;
public override string ToString() {
var sb = new StringBuilder();
sb.Append($"{BoundProperty}");
- if (LinkLength != null) sb.Append($" -Len: {LinkLength}");
-
return sb.ToString();
}
diff --git a/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildFromAttributes.cs b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildFromAttributes.cs
index d2b8279..b580f00 100644
--- a/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildFromAttributes.cs
+++ b/MewtocolNet/RegisterBuilding/BuilderPatterns/RBuildFromAttributes.cs
@@ -24,7 +24,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
}
//internal use only, adds a type definition (for use when building from attibute)
- internal DynamicStp AddressFromAttribute(string dtAddr, string typeDef, RegisterCollection regCol, PropertyInfo prop, uint? bytesizeHint = null) {
+ internal DynamicStp AddressFromAttribute(string dtAddr, string typeDef, RegisterCollection regCol, PropertyInfo prop, RegisterAttribute propAttr, uint? bytesizeHint = null) {
var stpData = AddressTools.ParseAddress(dtAddr);
@@ -32,6 +32,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
stpData.buildSource = RegisterBuildSource.Attribute;
stpData.regCollection = regCol;
stpData.boundProperty = prop;
+ stpData.boundPropertyAttribute = propAttr;
stpData.byteSizeHint = bytesizeHint;
return new DynamicStp {
@@ -58,9 +59,16 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
internal class DynamicRegister : SBaseRBDyn {
- public void PollLevel (int level) {
+ public DynamicRegister PollLevel (int level) {
Data.pollLevel = level;
+ return this;
+
+ }
+
+ public void Finalize () {
+
+ builder.assembler.Assemble(Data);
}
diff --git a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
index b7e9ade..2f0ae79 100644
--- a/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
+++ b/MewtocolNet/RegisterBuilding/RegisterAssembler.cs
@@ -3,6 +3,7 @@ using MewtocolNet.RegisterBuilding.BuilderPatterns;
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -142,23 +143,51 @@ namespace MewtocolNet.RegisterBuilding {
if (generatedInstance == null)
throw new ArgumentException("Failed to build register");
- if (collectionTarget != null)
- generatedInstance.WithRegisterCollection(collectionTarget);
-
if (data.boundProperty != null)
generatedInstance.WithBoundProperty(new RegisterPropTarget {
BoundProperty = data.boundProperty,
+ PropertyAttribute = data.boundPropertyAttribute,
+ ContainedCollection = data.regCollection
});
generatedInstance.attachedInterface = onInterface;
generatedInstance.underlyingSystemType = data.dotnetVarType;
generatedInstance.pollLevel = data.pollLevel;
- if (data.regCollection != null)
- generatedInstance.autoGenerated = true;
+ //set auto generated
+ generatedInstance.autoGenerated = data.buildSource == PublicEnums.RegisterBuildSource.Attribute;
- assembled.Add(generatedInstance);
- return generatedInstance;
+ //Check for a dupe, first in all registers, then in the local generation group
+ var foundDupe = onInterface.GetAllRegistersInternal().FirstOrDefault(x => x.IsSameAddressAndType(generatedInstance)) ??
+ assembled.FirstOrDefault(x => x.IsSameAddressAndType(generatedInstance));
+
+ if (foundDupe != null) {
+
+ if(data.boundProperty != null)
+ foundDupe.WithBoundProperty(new RegisterPropTarget {
+ BoundProperty = data.boundProperty,
+ ContainedCollection = data.regCollection,
+ PropertyAttribute = data.boundPropertyAttribute,
+ });
+
+ if(onInterface.memoryManager.pollLevelOrMode == PollLevelOverwriteMode.Highest) {
+
+ foundDupe.pollLevel = Math.Max(foundDupe.pollLevel, generatedInstance.pollLevel);
+
+ } else {
+
+ foundDupe.pollLevel = Math.Min(foundDupe.pollLevel, generatedInstance.pollLevel);
+
+ }
+
+ return foundDupe;
+
+ } else {
+
+ assembled.Add(generatedInstance);
+ return generatedInstance;
+
+ }
}
diff --git a/MewtocolNet/RegisterBuilding/StepBaseTyper.cs b/MewtocolNet/RegisterBuilding/StepBaseTyper.cs
index 6585c58..789e8e5 100644
--- a/MewtocolNet/RegisterBuilding/StepBaseTyper.cs
+++ b/MewtocolNet/RegisterBuilding/StepBaseTyper.cs
@@ -1,4 +1,5 @@
using MewtocolNet.PublicEnums;
+using MewtocolNet.RegisterAttributes;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -38,6 +39,11 @@ namespace MewtocolNet.RegisterBuilding {
///
internal static StepBase AsType(this StepBase b, Type type) {
+ //check for nullable props
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
+ type = Nullable.GetUnderlyingType(type);
+ }
+
//for internal only, relay to AsType from string
if (b.Data.buildSource == RegisterBuildSource.Attribute) {
@@ -63,7 +69,29 @@ namespace MewtocolNet.RegisterBuilding {
}
- b.Data.dotnetVarType = type;
+ bool isDDTDT = b.Data.regType == RegisterPrefix.DT || b.Data.regType == RegisterPrefix.DDT;
+
+ bool isDeclaredNormalRegisterAttribite = b.Data.boundPropertyAttribute != null &&
+ b.Data.boundPropertyAttribute.GetType().DeclaringType == typeof(RegisterAttribute);
+
+ if (b.Data.boundPropertyAttribute is BitRegisterAttribute && type != typeof(bool)) {
+
+ throw new NotSupportedException($"Only booleans are allowed as the target type for BitRegister attributes");
+
+ } else if (isDeclaredNormalRegisterAttribite && isDDTDT && type == typeof(bool)) {
+
+ throw new NotSupportedException($"Single bit DT registers are only supported with the BitRegister attribute");
+
+ }
+
+ //special case type defintions for register that use an other underlying type
+ if (b.Data.regType == RegisterPrefix.DT && type == typeof(bool)) {
+ b.Data.dotnetVarType = typeof(Word);
+ } else if (b.Data.regType == RegisterPrefix.DDT && type == typeof(bool)) {
+ b.Data.dotnetVarType = typeof(DWord);
+ } else {
+ b.Data.dotnetVarType = type;
+ }
return b;
diff --git a/MewtocolNet/RegisterBuilding/StepData.cs b/MewtocolNet/RegisterBuilding/StepData.cs
index 287078d..10ed430 100644
--- a/MewtocolNet/RegisterBuilding/StepData.cs
+++ b/MewtocolNet/RegisterBuilding/StepData.cs
@@ -36,6 +36,7 @@ namespace MewtocolNet.RegisterBuilding {
//only for building from attributes
internal RegisterCollection regCollection;
internal PropertyInfo boundProperty;
+ internal RegisterAttribute boundPropertyAttribute;
internal string typeDef;
diff --git a/MewtocolNet/Registers/Base/IRegister.cs b/MewtocolNet/Registers/Base/IRegister.cs
index fe589e6..e4cbe4f 100644
--- a/MewtocolNet/Registers/Base/IRegister.cs
+++ b/MewtocolNet/Registers/Base/IRegister.cs
@@ -14,6 +14,12 @@ namespace MewtocolNet.Registers {
///
event RegisterChangedEventHandler ValueChanged;
+ ///
+ /// Defines if the register was auto generated from a property.
+ /// If so it is not allowed to remove the register from the interface stack
+ ///
+ bool IsAutoGenerated { get; }
+
///
/// Type of the underlying register
///
diff --git a/MewtocolNet/Registers/Base/Register.cs b/MewtocolNet/Registers/Base/Register.cs
index f1598a3..d5dd69d 100644
--- a/MewtocolNet/Registers/Base/Register.cs
+++ b/MewtocolNet/Registers/Base/Register.cs
@@ -21,7 +21,6 @@ namespace MewtocolNet.Registers {
public event PropertyChangedEventHandler PropertyChanged;
//links to
- internal RegisterCollection containedCollection;
internal MewtocolInterface attachedInterface;
internal List boundProperties = new List();
@@ -50,7 +49,7 @@ namespace MewtocolNet.Registers {
private float[] updateFreqAvgList;
///
- internal RegisterCollection ContainedCollection => containedCollection;
+ public bool IsAutoGenerated => autoGenerated;
///
internal MewtocolInterface AttachedInterface => attachedInterface;
@@ -94,7 +93,7 @@ namespace MewtocolNet.Registers {
}
}
- public string MemoryAreaInfo => underlyingMemory.GetName();
+ public string MemoryAreaInfo => underlyingMemory.ToString();
public string MemoryAreaHash => underlyingMemory.GetHashCode().ToString();
@@ -131,6 +130,61 @@ namespace MewtocolNet.Registers {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ValueObj)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ValueStr)));
+ UpdateBoundProperties();
+
+ }
+
+ private void UpdateBoundProperties () {
+
+ //set the bound property values of there is one
+
+ foreach (var prop in boundProperties) {
+
+ //nullable
+ var boundPropType = prop.BoundProperty.PropertyType;
+
+ if (boundPropType.IsGenericType && boundPropType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
+ boundPropType = Nullable.GetUnderlyingType(boundPropType);
+ }
+
+ bool isBitRegisterAttribute = prop.PropertyAttribute is BitRegisterAttribute;
+
+ if (boundPropType == underlyingSystemType) {
+
+ //the bound prop is the same type as the one of the underlying register
+ prop.BoundProperty.SetValue(prop.ContainedCollection, ValueObj);
+ prop.ContainedCollection.TriggerPropertyChanged(prop.BoundProperty.Name);
+
+ } else if(boundPropType == typeof(bool) && isBitRegisterAttribute && underlyingSystemType == typeof(Word)) {
+
+ var bitRegAttr = ((BitRegisterAttribute)prop.PropertyAttribute).bitIndex;
+
+ if(ValueObj != null) {
+ prop.BoundProperty.SetValue(prop.ContainedCollection, ((Word)ValueObj)[bitRegAttr]);
+ } else {
+ prop.BoundProperty.SetValue(prop.ContainedCollection, null);
+ }
+
+ prop.ContainedCollection.TriggerPropertyChanged(prop.BoundProperty.Name);
+
+
+ } else if (boundPropType == typeof(bool) && isBitRegisterAttribute && underlyingSystemType == typeof(DWord)) {
+
+ var bitRegAttr = ((BitRegisterAttribute)prop.PropertyAttribute).bitIndex;
+
+ if (ValueObj != null) {
+ prop.BoundProperty.SetValue(prop.ContainedCollection, ((DWord)ValueObj)[bitRegAttr]);
+ } else {
+ prop.BoundProperty.SetValue(prop.ContainedCollection, null);
+ }
+
+ prop.ContainedCollection.TriggerPropertyChanged(prop.BoundProperty.Name);
+
+
+ }
+
+ }
+
}
private int updateFreqAvgListIteration = 0;
@@ -206,13 +260,11 @@ namespace MewtocolNet.Registers {
internal virtual object SetValueFromBytes(byte[] bytes) => throw new NotImplementedException();
- internal void WithRegisterCollection(RegisterCollection collection) => containedCollection = collection;
-
internal void WithBoundProperty(RegisterPropTarget propInfo) => boundProperties.Add(propInfo);
internal void WithBoundProperties(IEnumerable propInfos) {
- foreach (var item in propInfos)
+ foreach (var item in propInfos.ToArray())
boundProperties.Add(item);
}
@@ -227,10 +279,6 @@ namespace MewtocolNet.Registers {
public virtual string GetRegisterString() => RegisterType.ToString();
- public virtual string GetCombinedName() => $"{GetContainerName()}{(GetContainerName() != null ? "." : "")}{Name ?? "Unnamed"}";
-
- public virtual string GetContainerName() => $"{(containedCollection != null ? $"{containedCollection.GetType().Name}" : null)}";
-
public virtual string GetMewName() => $"{GetRegisterString()}{MemoryAddress}";
public virtual uint GetRegisterAddressLen() => throw new NotImplementedException();
@@ -271,6 +319,12 @@ namespace MewtocolNet.Registers {
}
+ internal virtual bool IsSameAddressTypeAndPollLevel(Register toCompare) {
+
+ return IsSameAddressAndType(toCompare) && PollLevel == toCompare.PollLevel;
+
+ }
+
internal int AveragePollLevel(List testAgainst, PollLevelOverwriteMode mode) {
var whereAddressFitsInto = this.CanFitAddressRange(testAgainst)
@@ -353,9 +407,6 @@ namespace MewtocolNet.Registers {
if (GetSpecialAddress() != null)
sb.Append($"SPAddress: {GetSpecialAddress():X1}\n");
- if (containedCollection != null)
- sb.Append($"In collection: {containedCollection.GetType()}\n");
-
if (boundProperties != null && boundProperties.Count > 0)
sb.Append($"Bound props: \n\t{string.Join(",\n\t", boundProperties)}");
else
diff --git a/MewtocolNet/Registers/Classes/ArrayRegister.cs b/MewtocolNet/Registers/Classes/ArrayRegister.cs
index 3fac936..8977b5a 100644
--- a/MewtocolNet/Registers/Classes/ArrayRegister.cs
+++ b/MewtocolNet/Registers/Classes/ArrayRegister.cs
@@ -87,7 +87,7 @@ namespace MewtocolNet.Registers {
var encoded = PlcValueParser.EncodeArray(this, value);
- var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
+ var res = await attachedInterface.WriteAreaByteRange((int)MemoryAddress, encoded);
if (res) {
@@ -116,7 +116,7 @@ namespace MewtocolNet.Registers {
var encoded = PlcValueParser.EncodeArray(this, value);
- var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
+ var res = await attachedInterface.WriteAreaByteRange((int)MemoryAddress, encoded);
if (res) {
@@ -144,7 +144,7 @@ namespace MewtocolNet.Registers {
///
private async Task