mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Fixed bool support and duplicate register refs
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
MinWidth="500"
|
MinWidth="500"
|
||||||
MinHeight="400"
|
MinHeight="400"
|
||||||
Height="850"
|
Height="850"
|
||||||
Width="800"
|
Width="1200"
|
||||||
Title="MewtocolNet WPF Demo">
|
Title="MewtocolNet WPF Demo">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
|||||||
31
Examples.WPF/RegisterCollections/TestRegisterCollection.cs
Normal file
31
Examples.WPF/RegisterCollections/TestRegisterCollection.cs
Normal file
@@ -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; }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using MewtocolNet;
|
using Examples.WPF.RegisterCollections;
|
||||||
|
using MewtocolNet;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -10,6 +11,7 @@ namespace Examples.WPF.ViewModels {
|
|||||||
public class AppViewModel : ViewModelBase {
|
public class AppViewModel : ViewModelBase {
|
||||||
|
|
||||||
private IPlc? plc;
|
private IPlc? plc;
|
||||||
|
private TestRegisterCollection testRegCollection = null!;
|
||||||
|
|
||||||
public bool PlcIsNull => plc == null;
|
public bool PlcIsNull => plc == null;
|
||||||
|
|
||||||
@@ -25,6 +27,14 @@ namespace Examples.WPF.ViewModels {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TestRegisterCollection TestRegCollection {
|
||||||
|
get => testRegCollection;
|
||||||
|
set {
|
||||||
|
testRegCollection = value;
|
||||||
|
OnPropChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MewtocolNet;
|
using Examples.WPF.RegisterCollections;
|
||||||
|
using MewtocolNet;
|
||||||
using MewtocolNet.Events;
|
using MewtocolNet.Events;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -14,6 +15,8 @@ public class PlcDataViewViewModel : ViewModelBase {
|
|||||||
|
|
||||||
public IPlc Plc => App.ViewModel.Plc!;
|
public IPlc Plc => App.ViewModel.Plc!;
|
||||||
|
|
||||||
|
public TestRegisterCollection RegCollection => App.ViewModel.TestRegCollection;
|
||||||
|
|
||||||
public ReconnectArgs PlcCurrentReconnectArgs {
|
public ReconnectArgs PlcCurrentReconnectArgs {
|
||||||
get => plcCurrentReconnectArgs;
|
get => plcCurrentReconnectArgs;
|
||||||
set {
|
set {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Examples.WPF.ViewModels;
|
using Examples.WPF.RegisterCollections;
|
||||||
|
using Examples.WPF.ViewModels;
|
||||||
using MewtocolNet;
|
using MewtocolNet;
|
||||||
using MewtocolNet.ComCassette;
|
using MewtocolNet.ComCassette;
|
||||||
using MewtocolNet.Registers;
|
using MewtocolNet.Registers;
|
||||||
@@ -55,31 +56,46 @@ public partial class ConnectView : UserControl {
|
|||||||
var parsedInt = int.Parse(viewModel.SelectedPort);
|
var parsedInt = int.Parse(viewModel.SelectedPort);
|
||||||
|
|
||||||
IRegister<short> heartbeatSetter = null!;
|
IRegister<short> heartbeatSetter = null!;
|
||||||
|
IRegister<bool> outputContactReference = null!;
|
||||||
|
IRegister<bool> testBoolReference = null!;
|
||||||
|
IRegister<Word> wordRefTest = null!;
|
||||||
|
|
||||||
|
//build a new interface
|
||||||
App.ViewModel.Plc = Mewtocol.Ethernet(viewModel.SelectedIP, parsedInt)
|
App.ViewModel.Plc = Mewtocol.Ethernet(viewModel.SelectedIP, parsedInt)
|
||||||
.WithPoller()
|
.WithPoller()
|
||||||
.WithInterfaceSettings(setting => {
|
.WithInterfaceSettings(setting => {
|
||||||
setting.TryReconnectAttempts = 0;
|
|
||||||
|
setting.TryReconnectAttempts = 10;
|
||||||
setting.TryReconnectDelayMs = 2000;
|
setting.TryReconnectDelayMs = 2000;
|
||||||
setting.SendReceiveTimeoutMs = 1000;
|
setting.SendReceiveTimeoutMs = 1000;
|
||||||
setting.HeartbeatIntervalMs = 3000;
|
setting.HeartbeatIntervalMs = 3000;
|
||||||
setting.MaxDataBlocksPerWrite = 12;
|
setting.MaxDataBlocksPerWrite = 20;
|
||||||
setting.MaxOptimizationDistance = 10;
|
setting.MaxOptimizationDistance = 10;
|
||||||
|
|
||||||
})
|
})
|
||||||
.WithCustomPollLevels(lvl => {
|
.WithCustomPollLevels(lvl => {
|
||||||
|
|
||||||
lvl.SetLevel(2, 3);
|
lvl.SetLevel(2, 3);
|
||||||
lvl.SetLevel(3, TimeSpan.FromSeconds(5));
|
lvl.SetLevel(3, TimeSpan.FromSeconds(5));
|
||||||
lvl.SetLevel(4, TimeSpan.FromSeconds(10));
|
lvl.SetLevel(4, TimeSpan.FromSeconds(10));
|
||||||
|
|
||||||
|
})
|
||||||
|
.WithRegisterCollections(collector => {
|
||||||
|
|
||||||
|
App.ViewModel.TestRegCollection = collector.AddCollection<TestRegisterCollection>();
|
||||||
|
|
||||||
})
|
})
|
||||||
.WithRegisters(b => {
|
.WithRegisters(b => {
|
||||||
|
|
||||||
//b.Struct<short>("DT0").Build();
|
b.Bool("X4").Build();
|
||||||
//b.Struct<short>("DT0").AsArray(30).Build();
|
b.Bool("Y4").Build(out outputContactReference);
|
||||||
|
b.Bool("R10A").PollLevel(PollLevel.FirstIteration).Build(out testBoolReference);
|
||||||
b.Bool("R10A").Build();
|
|
||||||
|
|
||||||
b.Struct<short>("DT1000").Build(out heartbeatSetter);
|
b.Struct<short>("DT1000").Build(out heartbeatSetter);
|
||||||
b.Struct<Word>("DT1000").Build();
|
|
||||||
|
//these will be merged into one
|
||||||
|
b.Struct<Word>("DT1000").Build(out wordRefTest);
|
||||||
|
b.Struct<Word>("DT1000").Build(out wordRefTest);
|
||||||
|
|
||||||
b.Struct<ushort>("DT1001").PollLevel(2).Build();
|
b.Struct<ushort>("DT1001").PollLevel(2).Build();
|
||||||
b.Struct<Word>("DT1002").PollLevel(2).Build();
|
b.Struct<Word>("DT1002").PollLevel(2).Build();
|
||||||
@@ -98,9 +114,16 @@ public partial class ConnectView : UserControl {
|
|||||||
|
|
||||||
await heartbeatSetter.WriteAsync((short)new Random().Next(short.MinValue, short.MaxValue));
|
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();
|
.Build();
|
||||||
|
|
||||||
|
//connect to it
|
||||||
await App.ViewModel.Plc.ConnectAsync();
|
await App.ViewModel.Plc.ConnectAsync();
|
||||||
|
|
||||||
if (App.ViewModel.Plc.IsConnected) {
|
if (App.ViewModel.Plc.IsConnected) {
|
||||||
|
|||||||
@@ -145,25 +145,148 @@
|
|||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<DataGrid Grid.Row="2"
|
<Grid Grid.Row="2">
|
||||||
AutoGenerateColumns="False"
|
|
||||||
IsReadOnly="True"
|
<Grid.ColumnDefinitions>
|
||||||
ItemsSource="{Binding Plc.Registers, Mode=OneWay}">
|
<ColumnDefinition Width="1.5*"/>
|
||||||
<DataGrid.Columns>
|
<ColumnDefinition Width="auto"/>
|
||||||
<DataGridTextColumn Header="FP Address" Binding="{Binding PLCAddressName}"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<DataGridTextColumn Header="Type" Binding="{Binding UnderlyingSystemType.Name}"/>
|
</Grid.ColumnDefinitions>
|
||||||
<DataGridTextColumn Header="Value" Binding="{Binding ValueStr}"/>
|
|
||||||
<DataGridTextColumn Header="Poll Level" Binding="{Binding PollLevel, Mode=OneWay}"/>
|
<Grid.RowDefinitions>
|
||||||
<DataGridTextColumn Header="Update Frequency" Binding="{Binding UpdateFreqHz, StringFormat='{}{0} Hz',Mode=OneWay}"/>
|
<RowDefinition Height="auto"/>
|
||||||
<DataGridTemplateColumn>
|
<RowDefinition/>
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
</Grid.RowDefinitions>
|
||||||
<DataTemplate>
|
|
||||||
<Border Background="{Binding MemoryAreaHash, Mode=OneWay, Converter={StaticResource hashColor}}"/>
|
<GridSplitter Grid.Column="1"
|
||||||
</DataTemplate>
|
Grid.Row="1"
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
VerticalAlignment="Stretch"
|
||||||
</DataGridTemplateColumn>
|
HorizontalAlignment="Stretch"
|
||||||
</DataGrid.Columns>
|
Background="Gray"
|
||||||
</DataGrid>
|
ShowsPreview="true"
|
||||||
|
Width="10">
|
||||||
|
<GridSplitter.Template>
|
||||||
|
<ControlTemplate>
|
||||||
|
<Rectangle Fill="Gray"
|
||||||
|
Width="2"
|
||||||
|
Margin="2"/>
|
||||||
|
</ControlTemplate>
|
||||||
|
</GridSplitter.Template>
|
||||||
|
</GridSplitter>
|
||||||
|
|
||||||
|
<TextBlock Text="Underlying Registers"
|
||||||
|
FontSize="18"
|
||||||
|
Margin="10"/>
|
||||||
|
|
||||||
|
<DataGrid Grid.Row="1"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
ItemsSource="{Binding Plc.Registers, Mode=OneWay}">
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsAutoGenerated}" Value="True">
|
||||||
|
<Setter Property="IsEnabled" Value="False"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="FP Address" Binding="{Binding PLCAddressName}"/>
|
||||||
|
<DataGridTextColumn Header="Type" Binding="{Binding UnderlyingSystemType.Name}"/>
|
||||||
|
<DataGridTemplateColumn Header="Value" Width="*">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border HorizontalAlignment="Left"
|
||||||
|
Padding="2,0">
|
||||||
|
<TextBlock Text="{Binding ValueStr}"/>
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding ValueStr}" Value="True">
|
||||||
|
<Setter Property="Background" Value="Blue"/>
|
||||||
|
<Setter Property="TextElement.Foreground" Value="White"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTextColumn Header="Poll Level" Binding="{Binding PollLevel, Mode=OneWay}"/>
|
||||||
|
<DataGridTextColumn Header="Update Frequency" Binding="{Binding UpdateFreqHz, StringFormat='{}{0} Hz',Mode=OneWay}"/>
|
||||||
|
<DataGridTemplateColumn Width="15">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border Background="{Binding MemoryAreaHash, Mode=OneWay, Converter={StaticResource hashColor}}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<TextBlock Text="Property Bindings"
|
||||||
|
Grid.Column="2"
|
||||||
|
FontSize="18"
|
||||||
|
Margin="10"/>
|
||||||
|
|
||||||
|
<Border Grid.Column="2"
|
||||||
|
Grid.Row="1"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
BorderBrush="LightBlue"
|
||||||
|
BorderThickness="1.5">
|
||||||
|
|
||||||
|
<ScrollViewer>
|
||||||
|
|
||||||
|
<StackPanel>
|
||||||
|
|
||||||
|
<TextBlock Text="Boolean internal relays"/>
|
||||||
|
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="R11A Nullable property: "/>
|
||||||
|
<Run Text="{Binding RegCollection.TestR11A}"
|
||||||
|
FontWeight="Bold"/>
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="R11A Duplicate non nullable property: "/>
|
||||||
|
<Run Text="{Binding RegCollection.TestR11A_Duplicate_NonNullable}"
|
||||||
|
FontWeight="Bold"/>
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="R16B"/>
|
||||||
|
<Run Text="{Binding RegCollection.TestR16B}"
|
||||||
|
FontWeight="Bold"/>
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="DT1000 Word duplicate: "/>
|
||||||
|
<Run Text="{Binding RegCollection.TestDT100_Word_Duplicate, Mode=OneWay}"
|
||||||
|
FontWeight="Bold"/>
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="DT1000 Word direct bit read (Idx 0): "/>
|
||||||
|
<Run Text="{Binding RegCollection.TestDT100_Word_Duplicate_SingleBit, Mode=OneWay}"
|
||||||
|
FontWeight="Bold"/>
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="DDT1010 DWord direct bit read (Idx 1): "/>
|
||||||
|
<Run Text="{Binding RegCollection.TestDDT1010_DWord_Duplicate_SingleBit, Mode=OneWay}"
|
||||||
|
FontWeight="Bold"/>
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -53,10 +53,14 @@ namespace MewtocolNet {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool this[int bitIndex] {
|
public bool this[int bitIndex] {
|
||||||
get {
|
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;
|
return (value & (1 << bitIndex)) != 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,10 +53,14 @@ namespace MewtocolNet {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool this[int bitIndex] {
|
public bool this[int bitIndex] {
|
||||||
get {
|
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})");
|
throw new IndexOutOfRangeException($"The word bit index was out of range ({bitIndex}/{bitLength - 1})");
|
||||||
|
|
||||||
|
if (bitLength == 0) return false;
|
||||||
|
|
||||||
return (value & (1 << bitIndex)) != 0;
|
return (value & (1 << bitIndex)) != 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,11 +139,11 @@ namespace MewtocolNet {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="_onString"></param>
|
/// <param name="_onString"></param>
|
||||||
/// <returns>A <see cref="T:byte[]"/> or null of failed</returns>
|
/// <returns>A <see cref="T:byte[]"/> or null of failed</returns>
|
||||||
internal static byte[] ParseDTRawStringAsBytes(this string _onString) {
|
internal static byte[] ParseResponseStringAsBytes(this string _onString) {
|
||||||
|
|
||||||
_onString = _onString.Replace("\r", "");
|
_onString = _onString.Replace("\r", "");
|
||||||
|
|
||||||
var res = new Regex(@"\%([0-9a-fA-F]{2})\$(?:RD|RP)(?<data>.*)(?<csum>..)").Match(_onString);
|
var res = new Regex(@"\%([0-9a-fA-F]{2})\$(?:RD|RP|RC)(?<data>.*)(?<csum>..)").Match(_onString);
|
||||||
if (res.Success) {
|
if (res.Success) {
|
||||||
|
|
||||||
string val = res.Groups["data"].Value;
|
string val = res.Groups["data"].Value;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using System.Net;
|
|||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using static MewtocolNet.Mewtocol;
|
||||||
|
|
||||||
namespace MewtocolNet
|
namespace MewtocolNet
|
||||||
{
|
{
|
||||||
@@ -210,21 +211,21 @@ namespace MewtocolNet
|
|||||||
|
|
||||||
internal List<RegisterCollection> collections = new List<RegisterCollection>();
|
internal List<RegisterCollection> collections = new List<RegisterCollection>();
|
||||||
|
|
||||||
public RegCollector AddCollection(RegisterCollection collection) {
|
public T AddCollection<T>(T collection) where T : RegisterCollection {
|
||||||
|
|
||||||
collections.Add(collection);
|
collections.Add(collection);
|
||||||
|
|
||||||
return this;
|
return collection;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegCollector AddCollection<T>() where T : RegisterCollection {
|
public T AddCollection<T>() where T : RegisterCollection {
|
||||||
|
|
||||||
var instance = (RegisterCollection)Activator.CreateInstance(typeof(T));
|
var instance = (RegisterCollection)Activator.CreateInstance(typeof(T));
|
||||||
|
|
||||||
collections.Add(instance);
|
collections.Add(instance);
|
||||||
|
|
||||||
return this;
|
return (T)instance;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,7 +355,7 @@ namespace MewtocolNet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A builder for attaching register collections
|
/// A builder for attaching register collections
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PostRegisterSetup<T> WithRegisterCollections(Action<RegCollector> collector) {
|
public PostInit<T> WithRegisterCollections(Action<RegCollector> collector) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -365,9 +366,7 @@ namespace MewtocolNet
|
|||||||
imew.WithRegisterCollections(res.collections);
|
imew.WithRegisterCollections(res.collections);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PostRegisterSetup<T> {
|
return this;
|
||||||
postInit = this
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
|
|
||||||
@@ -380,7 +379,7 @@ namespace MewtocolNet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A builder for attaching register collections
|
/// A builder for attaching register collections
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PostRegisterSetup<T> WithRegisters(Action<RBuild> builder) {
|
public PostInit<T> WithRegisters(Action<RBuild> builder) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -391,7 +390,31 @@ namespace MewtocolNet
|
|||||||
|
|
||||||
plc.AddRegisters(regBuilder.assembler.assembled.ToArray());
|
plc.AddRegisters(regBuilder.assembler.assembled.ToArray());
|
||||||
|
|
||||||
return new PostRegisterSetup<T> {
|
return this;
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
throw;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Repeats the passed method each time the hearbeat is triggered,
|
||||||
|
/// use
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="heartBeatAsync"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public EndInitSetup<T> WithHeartbeatTask(Func<Task> heartBeatAsync, bool executeInProg = false) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
var plc = (MewtocolInterface)(object)this.intf;
|
||||||
|
|
||||||
|
plc.heartbeatCallbackTask = heartBeatAsync;
|
||||||
|
plc.execHeartBeatCallbackTaskInProg = executeInProg;
|
||||||
|
|
||||||
|
return new EndInitSetup<T> {
|
||||||
postInit = this,
|
postInit = this,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -406,63 +429,22 @@ namespace MewtocolNet
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds and returns the final plc interface
|
/// Builds and returns the final plc interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T Build() => intf;
|
public T Build() => (T)(object)((MewtocolInterface)(object)intf).Build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public class PostRegisterSetup<T> {
|
|
||||||
|
|
||||||
internal PostInit<T> postInit;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Repeats the passed method each time the hearbeat is triggered,
|
|
||||||
/// use
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="heartBeatAsync"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public EndInitSetup<T> WithHeartbeatTask(Func<Task> heartBeatAsync, bool executeInProg = false) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
var plc = (MewtocolInterface)(object)postInit.intf;
|
|
||||||
|
|
||||||
plc.heartbeatCallbackTask = heartBeatAsync;
|
|
||||||
plc.execHeartBeatCallbackTaskInProg = executeInProg;
|
|
||||||
|
|
||||||
return new EndInitSetup<T> {
|
|
||||||
postInit = this.postInit,
|
|
||||||
postRegSetupInit = this
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
|
|
||||||
throw;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds and returns the final plc interface
|
|
||||||
/// </summary>
|
|
||||||
public T Build() => postInit.intf;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Interface building step 4
|
#region Interface building step 4
|
||||||
|
|
||||||
public class EndInitSetup<T> {
|
public class EndInitSetup<T> {
|
||||||
|
|
||||||
internal PostInit<T> postInit;
|
internal PostInit<T> postInit;
|
||||||
|
|
||||||
internal PostRegisterSetup<T> postRegSetupInit;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds and returns the final plc interface
|
/// Builds and returns the final plc interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T Build() => postInit.intf;
|
public T Build() => (T)(object)((MewtocolInterface)(object)postInit.intf).Build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -193,6 +193,14 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal MewtocolInterface Build () {
|
||||||
|
|
||||||
|
memoryManager.LinkAndMergeRegisters();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void MewtocolInterface_Connected(object sender, PlcConnectionArgs args) {
|
private void MewtocolInterface_Connected(object sender, PlcConnectionArgs args) {
|
||||||
|
|
||||||
IsConnected = true;
|
IsConnected = true;
|
||||||
@@ -226,8 +234,6 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OnRegisterChangedUpdateProps(asInternal);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -245,7 +251,7 @@ namespace MewtocolNet {
|
|||||||
Logger.Log($"DIAG ERR: {PlcInfo.SelfDiagnosticError}", LogLevel.Verbose, this);
|
Logger.Log($"DIAG ERR: {PlcInfo.SelfDiagnosticError}", LogLevel.Verbose, this);
|
||||||
Logger.Log($"CPU VER: {PlcInfo.CpuVersion}", 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($"METADATA: {PlcInfo.Metadata.MetaDataVersion}", this);
|
||||||
Logger.LogVerbose($"FP-WIN VERSION: {PlcInfo.Metadata.FPWinVersion}", this);
|
Logger.LogVerbose($"FP-WIN VERSION: {PlcInfo.Metadata.FPWinVersion}", this);
|
||||||
|
|||||||
@@ -242,57 +242,6 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
#endregion
|
#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
|
#region Register Collection adding
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -331,9 +280,10 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
//add builder item
|
//add builder item
|
||||||
regBuild
|
regBuild
|
||||||
.AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef, collection, prop, byteHint)
|
.AddressFromAttribute(cAttribute.MewAddress, cAttribute.TypeDef, collection, prop, cAttribute, byteHint)
|
||||||
.AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType)
|
.AsType(dotnetType.IsEnum ? dotnetType.UnderlyingSystemType : dotnetType)
|
||||||
.PollLevel(pollLevel);
|
.PollLevel(pollLevel)
|
||||||
|
.Finalize();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,27 +303,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var assembler = new RegisterAssembler(this);
|
AddRegisters(regBuild.assembler.assembled.ToArray());
|
||||||
|
|
||||||
AddRegisters(assembler.assembled.ToArray());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes back the values changes of the underlying registers to the corrosponding property
|
|
||||||
/// </summary>
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,16 +313,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
internal void AddRegisters(params Register[] registers) {
|
internal void AddRegisters(params Register[] registers) {
|
||||||
|
|
||||||
InsertRegistersToMemoryStack(registers.ToList());
|
memoryManager.LinkAndMergeRegisters(registers.ToList());
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void InsertRegistersToMemoryStack(List<Register> registers) {
|
|
||||||
|
|
||||||
memoryManager.LinkAndMergeRegisters(registers);
|
|
||||||
|
|
||||||
//run a second iteration
|
|
||||||
//memoryManager.LinkAndMergeRegisters();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MewtocolNet.Helpers;
|
using MewtocolNet.Helpers;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices.ComTypes;
|
||||||
|
|
||||||
namespace MewtocolNet {
|
namespace MewtocolNet {
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
var metaMarker = new byte[] { 0x4D, 0x65, 0x74, 0x41 };
|
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) {
|
if (data != null && data.SearchBytePattern(metaMarker) == readBytes - 4) {
|
||||||
|
|
||||||
@@ -204,7 +205,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
if (res.Success) {
|
if (res.Success) {
|
||||||
|
|
||||||
var bytes = res.Response.ParseDTRawStringAsBytes();
|
var bytes = res.Response.ParseResponseStringAsBytes();
|
||||||
var foundEndPattern = bytes.SearchBytePattern(new byte[] { 0xF8, 0xFF, 0xFF });
|
var foundEndPattern = bytes.SearchBytePattern(new byte[] { 0xF8, 0xFF, 0xFF });
|
||||||
|
|
||||||
for (int j = 0; j < bytes.Length; j += 2) {
|
for (int j = 0; j < bytes.Length; j += 2) {
|
||||||
@@ -243,7 +244,7 @@ namespace MewtocolNet {
|
|||||||
/// /// <param name="start">start address of the array</param>
|
/// /// <param name="start">start address of the array</param>
|
||||||
/// <param name="byteArr"></param>
|
/// <param name="byteArr"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> WriteByteRange(int start, byte[] byteArr) {
|
public async Task<bool> WriteAreaByteRange(int start, byte[] byteArr) {
|
||||||
|
|
||||||
if (byteArr == null)
|
if (byteArr == null)
|
||||||
throw new ArgumentNullException(nameof(byteArr));
|
throw new ArgumentNullException(nameof(byteArr));
|
||||||
@@ -271,7 +272,7 @@ namespace MewtocolNet {
|
|||||||
/// <param name="byteCount">Number of bytes to get</param>
|
/// <param name="byteCount">Number of bytes to get</param>
|
||||||
/// <param name="onProgress">Gets invoked when the progress changes, contains the progress as a double from 0 - 1.0</param>
|
/// <param name="onProgress">Gets invoked when the progress changes, contains the progress as a double from 0 - 1.0</param>
|
||||||
/// <returns>A byte array of the requested DT area</returns>
|
/// <returns>A byte array of the requested DT area</returns>
|
||||||
public async Task<byte[]> ReadByteRangeNonBlocking(int start, int byteCount, Action<double> onProgress = null) {
|
public async Task<byte[]> ReadAreaByteRangeAsync(int start, int byteCount, RegisterPrefix areaPrefix = RegisterPrefix.DT, Action<double> onProgress = null) {
|
||||||
|
|
||||||
//on odd bytes add one word
|
//on odd bytes add one word
|
||||||
var wordLength = byteCount / 2;
|
var wordLength = byteCount / 2;
|
||||||
@@ -287,19 +288,41 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
List<byte> readBytes = new List<byte>();
|
List<byte> readBytes = new List<byte>();
|
||||||
|
|
||||||
|
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<double> readProg) {
|
async Task ReadBlock(int wordStart, int wordEnd, Action<double> readProg) {
|
||||||
|
|
||||||
int blockSize = wordEnd - wordStart + 1;
|
int blockSize = wordEnd - wordStart + 1;
|
||||||
string startStr = wordStart.ToString().PadLeft(5, '0');
|
string startStr = wordStart.ToString().PadLeft(padLeftLen, '0');
|
||||||
string endStr = wordEnd.ToString().PadLeft(5, '0');
|
string endStr = wordEnd.ToString().PadLeft(padLeftLen, '0');
|
||||||
|
string requeststring = $"%{GetStationNumber()}#{areaCodeStr}{startStr}{endStr}";
|
||||||
string requeststring = $"%{GetStationNumber()}#RDD{startStr}{endStr}";
|
|
||||||
|
|
||||||
var result = await SendCommandInternalAsync(requeststring, onReceiveProgress: readProg);
|
var result = await SendCommandInternalAsync(requeststring, onReceiveProgress: readProg);
|
||||||
|
|
||||||
if (result.Success && !string.IsNullOrEmpty(result.Response)) {
|
if (result.Success && !string.IsNullOrEmpty(result.Response)) {
|
||||||
|
|
||||||
var bytes = result.Response.ParseDTRawStringAsBytes();
|
var bytes = result.Response.ParseResponseStringAsBytes();
|
||||||
|
|
||||||
if (bytes != null)
|
if (bytes != null)
|
||||||
readBytes.AddRange(bytes);
|
readBytes.AddRange(bytes);
|
||||||
|
|||||||
23
MewtocolNet/RegisterAttributes/BitRegisterAttribute.cs
Normal file
23
MewtocolNet/RegisterAttributes/BitRegisterAttribute.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MewtocolNet.RegisterAttributes {
|
||||||
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||||
|
public class BitRegisterAttribute : RegisterAttribute {
|
||||||
|
|
||||||
|
internal int bitIndex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds automatic data transfer between the property below this and
|
||||||
|
/// the plc register
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mewAddress">The FP-Address (DT, DDT, R, X, Y..)</param>
|
||||||
|
/// <param name="plcTypeDef">The type definition from the PLC (STRING[n], ARRAY [0..2] OF ...)</param>
|
||||||
|
public BitRegisterAttribute(string mewAddress, byte bitIndex) : base(mewAddress, null) {
|
||||||
|
|
||||||
|
this.bitIndex = bitIndex;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,15 +8,14 @@ namespace MewtocolNet.RegisterAttributes {
|
|||||||
//propinfo of the bound property
|
//propinfo of the bound property
|
||||||
internal PropertyInfo BoundProperty;
|
internal PropertyInfo BoundProperty;
|
||||||
|
|
||||||
//general number of bits or bytes to read back to the prop
|
internal RegisterAttribute PropertyAttribute;
|
||||||
internal int? LinkLength;
|
|
||||||
|
internal RegisterCollection ContainedCollection;
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.Append($"{BoundProperty}");
|
sb.Append($"{BoundProperty}");
|
||||||
if (LinkLength != null) sb.Append($" -Len: {LinkLength}");
|
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//internal use only, adds a type definition (for use when building from attibute)
|
//internal use only, adds a type definition (for use when building from attibute)
|
||||||
internal 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);
|
var stpData = AddressTools.ParseAddress(dtAddr);
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
stpData.buildSource = RegisterBuildSource.Attribute;
|
stpData.buildSource = RegisterBuildSource.Attribute;
|
||||||
stpData.regCollection = regCol;
|
stpData.regCollection = regCol;
|
||||||
stpData.boundProperty = prop;
|
stpData.boundProperty = prop;
|
||||||
|
stpData.boundPropertyAttribute = propAttr;
|
||||||
stpData.byteSizeHint = bytesizeHint;
|
stpData.byteSizeHint = bytesizeHint;
|
||||||
|
|
||||||
return new DynamicStp {
|
return new DynamicStp {
|
||||||
@@ -58,9 +59,16 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
|
|
||||||
internal class DynamicRegister : SBaseRBDyn {
|
internal class DynamicRegister : SBaseRBDyn {
|
||||||
|
|
||||||
public void PollLevel (int level) {
|
public DynamicRegister PollLevel (int level) {
|
||||||
|
|
||||||
Data.pollLevel = level;
|
Data.pollLevel = level;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Finalize () {
|
||||||
|
|
||||||
|
builder.assembler.Assemble(Data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using MewtocolNet.RegisterBuilding.BuilderPatterns;
|
|||||||
using MewtocolNet.Registers;
|
using MewtocolNet.Registers;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -142,23 +143,51 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
if (generatedInstance == null)
|
if (generatedInstance == null)
|
||||||
throw new ArgumentException("Failed to build register");
|
throw new ArgumentException("Failed to build register");
|
||||||
|
|
||||||
if (collectionTarget != null)
|
|
||||||
generatedInstance.WithRegisterCollection(collectionTarget);
|
|
||||||
|
|
||||||
if (data.boundProperty != null)
|
if (data.boundProperty != null)
|
||||||
generatedInstance.WithBoundProperty(new RegisterPropTarget {
|
generatedInstance.WithBoundProperty(new RegisterPropTarget {
|
||||||
BoundProperty = data.boundProperty,
|
BoundProperty = data.boundProperty,
|
||||||
|
PropertyAttribute = data.boundPropertyAttribute,
|
||||||
|
ContainedCollection = data.regCollection
|
||||||
});
|
});
|
||||||
|
|
||||||
generatedInstance.attachedInterface = onInterface;
|
generatedInstance.attachedInterface = onInterface;
|
||||||
generatedInstance.underlyingSystemType = data.dotnetVarType;
|
generatedInstance.underlyingSystemType = data.dotnetVarType;
|
||||||
generatedInstance.pollLevel = data.pollLevel;
|
generatedInstance.pollLevel = data.pollLevel;
|
||||||
|
|
||||||
if (data.regCollection != null)
|
//set auto generated
|
||||||
generatedInstance.autoGenerated = true;
|
generatedInstance.autoGenerated = data.buildSource == PublicEnums.RegisterBuildSource.Attribute;
|
||||||
|
|
||||||
assembled.Add(generatedInstance);
|
//Check for a dupe, first in all registers, then in the local generation group
|
||||||
return generatedInstance;
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MewtocolNet.PublicEnums;
|
using MewtocolNet.PublicEnums;
|
||||||
|
using MewtocolNet.RegisterAttributes;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -38,6 +39,11 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
/// </param>
|
/// </param>
|
||||||
internal static StepBase AsType(this StepBase b, Type type) {
|
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
|
//for internal only, relay to AsType from string
|
||||||
if (b.Data.buildSource == RegisterBuildSource.Attribute) {
|
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;
|
return b;
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ namespace MewtocolNet.RegisterBuilding {
|
|||||||
//only for building from attributes
|
//only for building from attributes
|
||||||
internal RegisterCollection regCollection;
|
internal RegisterCollection regCollection;
|
||||||
internal PropertyInfo boundProperty;
|
internal PropertyInfo boundProperty;
|
||||||
|
internal RegisterAttribute boundPropertyAttribute;
|
||||||
|
|
||||||
internal string typeDef;
|
internal string typeDef;
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ namespace MewtocolNet.Registers {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
event RegisterChangedEventHandler ValueChanged;
|
event RegisterChangedEventHandler ValueChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines if the register was auto generated from a property.<br/>
|
||||||
|
/// If so it is not allowed to remove the register from the interface stack
|
||||||
|
/// </summary>
|
||||||
|
bool IsAutoGenerated { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Type of the underlying register
|
/// Type of the underlying register
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ namespace MewtocolNet.Registers {
|
|||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
//links to
|
//links to
|
||||||
internal RegisterCollection containedCollection;
|
|
||||||
internal MewtocolInterface attachedInterface;
|
internal MewtocolInterface attachedInterface;
|
||||||
|
|
||||||
internal List<RegisterPropTarget> boundProperties = new List<RegisterPropTarget>();
|
internal List<RegisterPropTarget> boundProperties = new List<RegisterPropTarget>();
|
||||||
@@ -50,7 +49,7 @@ namespace MewtocolNet.Registers {
|
|||||||
private float[] updateFreqAvgList;
|
private float[] updateFreqAvgList;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
internal RegisterCollection ContainedCollection => containedCollection;
|
public bool IsAutoGenerated => autoGenerated;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
internal MewtocolInterface AttachedInterface => attachedInterface;
|
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();
|
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(ValueObj)));
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ValueStr)));
|
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;
|
private int updateFreqAvgListIteration = 0;
|
||||||
@@ -206,13 +260,11 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
internal virtual object SetValueFromBytes(byte[] bytes) => throw new NotImplementedException();
|
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 WithBoundProperty(RegisterPropTarget propInfo) => boundProperties.Add(propInfo);
|
||||||
|
|
||||||
internal void WithBoundProperties(IEnumerable<RegisterPropTarget> propInfos) {
|
internal void WithBoundProperties(IEnumerable<RegisterPropTarget> propInfos) {
|
||||||
|
|
||||||
foreach (var item in propInfos)
|
foreach (var item in propInfos.ToArray())
|
||||||
boundProperties.Add(item);
|
boundProperties.Add(item);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -227,10 +279,6 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
public virtual string GetRegisterString() => RegisterType.ToString();
|
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 string GetMewName() => $"{GetRegisterString()}{MemoryAddress}";
|
||||||
|
|
||||||
public virtual uint GetRegisterAddressLen() => throw new NotImplementedException();
|
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<Register> testAgainst, PollLevelOverwriteMode mode) {
|
internal int AveragePollLevel(List<Register> testAgainst, PollLevelOverwriteMode mode) {
|
||||||
|
|
||||||
var whereAddressFitsInto = this.CanFitAddressRange(testAgainst)
|
var whereAddressFitsInto = this.CanFitAddressRange(testAgainst)
|
||||||
@@ -353,9 +407,6 @@ namespace MewtocolNet.Registers {
|
|||||||
if (GetSpecialAddress() != null)
|
if (GetSpecialAddress() != null)
|
||||||
sb.Append($"SPAddress: {GetSpecialAddress():X1}\n");
|
sb.Append($"SPAddress: {GetSpecialAddress():X1}\n");
|
||||||
|
|
||||||
if (containedCollection != null)
|
|
||||||
sb.Append($"In collection: {containedCollection.GetType()}\n");
|
|
||||||
|
|
||||||
if (boundProperties != null && boundProperties.Count > 0)
|
if (boundProperties != null && boundProperties.Count > 0)
|
||||||
sb.Append($"Bound props: \n\t{string.Join(",\n\t", boundProperties)}");
|
sb.Append($"Bound props: \n\t{string.Join(",\n\t", boundProperties)}");
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
var encoded = PlcValueParser.EncodeArray(this, value);
|
var encoded = PlcValueParser.EncodeArray(this, value);
|
||||||
|
|
||||||
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
var res = await attachedInterface.WriteAreaByteRange((int)MemoryAddress, encoded);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ namespace MewtocolNet.Registers {
|
|||||||
|
|
||||||
var encoded = PlcValueParser.EncodeArray(this, value);
|
var encoded = PlcValueParser.EncodeArray(this, value);
|
||||||
|
|
||||||
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
var res = await attachedInterface.WriteAreaByteRange((int)MemoryAddress, encoded);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
private async Task<object> ReadAsync() {
|
private async Task<object> ReadAsync() {
|
||||||
|
|
||||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
var res = await attachedInterface.ReadAreaByteRangeAsync((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
||||||
if (res == null) throw new Exception();
|
if (res == null) throw new Exception();
|
||||||
|
|
||||||
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
|
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MewtocolNet.Registers {
|
namespace MewtocolNet.Registers {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a register containing a boolean
|
/// Defines a register containing a boolean
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BoolRegister : Register {
|
public class BoolRegister : Register, IRegister<bool> {
|
||||||
|
|
||||||
internal byte specialAddress;
|
internal byte specialAddress;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The registers memory adress if not a special register
|
/// The registers memory adress if not a special register
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte SpecialAddress => specialAddress;
|
public byte SpecialAddress => specialAddress;
|
||||||
|
|
||||||
|
public bool? Value => (bool?)ValueObj;
|
||||||
|
|
||||||
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
|
[Obsolete("Creating registers directly is not supported use IPlc.Register instead")]
|
||||||
public BoolRegister() =>
|
public BoolRegister() =>
|
||||||
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
throw new NotSupportedException("Direct register instancing is not supported, use the builder pattern");
|
||||||
@@ -74,14 +80,67 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override uint GetRegisterAddressLen() => 1;
|
public override uint GetRegisterAddressLen() => 1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task WriteAsync(bool value) {
|
||||||
|
|
||||||
|
var res = await WriteSingleBitAsync(value);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
|
||||||
|
//find the underlying memory
|
||||||
|
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
|
||||||
|
.FirstOrDefault(x => x.IsSameAddressAndType(this));
|
||||||
|
|
||||||
|
if (matchingReg != null)
|
||||||
|
matchingReg.underlyingMemory.SetUnderlyingBits(matchingReg, specialAddress, value);
|
||||||
|
|
||||||
|
AddSuccessWrite();
|
||||||
|
UpdateHoldingValue(value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> WriteSingleBitAsync(bool val) {
|
||||||
|
|
||||||
|
var rawAddr = $"{MemoryAddress}{SpecialAddress.ToString("X1")}".PadLeft(4, '0');
|
||||||
|
|
||||||
|
string addStr = $"{GetRegisterString()}{rawAddr}";
|
||||||
|
string cmd = $"%{attachedInterface.GetStationNumber()}#WCS{addStr}{(val ? "1" : "0")}";
|
||||||
|
var res = await attachedInterface.SendCommandInternalAsync(cmd);
|
||||||
|
|
||||||
|
return res.Success;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<bool> ReadAsync() {
|
||||||
|
|
||||||
|
var res = await attachedInterface.ReadAreaByteRangeAsync((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
||||||
|
if (res == null) throw new Exception($"Failed to read the register {this}");
|
||||||
|
|
||||||
|
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
|
||||||
|
.FirstOrDefault(x => x.IsSameAddressAndType(this));
|
||||||
|
|
||||||
|
if (matchingReg != null) {
|
||||||
|
|
||||||
|
matchingReg.underlyingMemory.SetUnderlyingBytes(matchingReg, res);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool)SetValueFromBytes(res);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
internal override object SetValueFromBytes(byte[] bytes) {
|
internal override object SetValueFromBytes(byte[] bytes) {
|
||||||
|
|
||||||
AddSuccessRead();
|
AddSuccessRead();
|
||||||
|
|
||||||
var parsed = PlcValueParser.Parse<bool>(this, bytes);
|
var bitArrVal = new BitArray(bytes)[SpecialAddress];
|
||||||
|
|
||||||
UpdateHoldingValue(parsed);
|
UpdateHoldingValue(bitArrVal);
|
||||||
return parsed;
|
|
||||||
|
return bitArrVal;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ namespace MewtocolNet.Registers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var encoded = PlcValueParser.Encode(this, value);
|
var encoded = PlcValueParser.Encode(this, value);
|
||||||
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
var res = await attachedInterface.WriteAreaByteRange((int)MemoryAddress, encoded);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<string> ReadAsync() {
|
public async Task<string> ReadAsync() {
|
||||||
|
|
||||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
var res = await attachedInterface.ReadAreaByteRangeAsync((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
||||||
if (res == null) return null;
|
if (res == null) return null;
|
||||||
|
|
||||||
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
|
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ namespace MewtocolNet.Registers {
|
|||||||
public async Task WriteAsync(T value) {
|
public async Task WriteAsync(T value) {
|
||||||
|
|
||||||
var encoded = PlcValueParser.Encode(this, (T)value);
|
var encoded = PlcValueParser.Encode(this, (T)value);
|
||||||
var res = await attachedInterface.WriteByteRange((int)MemoryAddress, encoded);
|
var res = await attachedInterface.WriteAreaByteRange((int)MemoryAddress, encoded);
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<T> ReadAsync() {
|
public async Task<T> ReadAsync() {
|
||||||
|
|
||||||
var res = await attachedInterface.ReadByteRangeNonBlocking((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
var res = await attachedInterface.ReadAreaByteRangeAsync((int)MemoryAddress, (int)GetRegisterAddressLen() * 2);
|
||||||
if (res == null) throw new Exception($"Failed to read the register {this}");
|
if (res == null) throw new Exception($"Failed to read the register {this}");
|
||||||
|
|
||||||
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
|
var matchingReg = attachedInterface.memoryManager.GetAllRegisters()
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace MewtocolNet.SetupClasses {
|
|||||||
/// Defines how many WORD blocks the interface will send on a DT area write request before splitting up messages <br/>
|
/// Defines how many WORD blocks the interface will send on a DT area write request before splitting up messages <br/>
|
||||||
/// Higher numbers will result in a longer send and receive thread blocking time
|
/// Higher numbers will result in a longer send and receive thread blocking time
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxDataBlocksPerWrite { get; set; } = 8;
|
public int MaxDataBlocksPerWrite { get; set; } = 20;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The send and receive timout for messages in milliseconds
|
/// The send and receive timout for messages in milliseconds
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using MewtocolNet.Registers;
|
using MewtocolNet.Registers;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -6,7 +8,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace MewtocolNet.UnderlyingRegisters {
|
namespace MewtocolNet.UnderlyingRegisters {
|
||||||
|
|
||||||
public abstract class AreaBase {
|
public class AreaBase {
|
||||||
|
|
||||||
private MewtocolInterface mewInterface;
|
private MewtocolInterface mewInterface;
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
internal async Task<bool> RequestByteReadAsync(ulong addStart, ulong addEnd) {
|
internal async Task<bool> RequestByteReadAsync(ulong addStart, ulong addEnd) {
|
||||||
|
|
||||||
var byteCount = (addEnd - addStart + 1) * 2;
|
var byteCount = (addEnd - addStart + 1) * 2;
|
||||||
var result = await mewInterface.ReadByteRangeNonBlocking((int)addStart, (int)byteCount);
|
var result = await mewInterface.ReadAreaByteRangeAsync((int)addStart, (int)byteCount, registerType);
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
|
||||||
@@ -104,6 +106,18 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetUnderlyingBits(Register reg, int bitIndex, bool value) {
|
||||||
|
|
||||||
|
var underlyingBefore = GetUnderlyingBytes(reg);
|
||||||
|
|
||||||
|
var bitArr = new BitArray(underlyingBefore);
|
||||||
|
|
||||||
|
bitArr.CopyTo(underlyingBefore, 0);
|
||||||
|
|
||||||
|
SetUnderlyingBytes(underlyingBefore, reg.MemoryAddress);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void SetUnderlyingBytes(byte[] bytes, ulong addStart) {
|
private void SetUnderlyingBytes(byte[] bytes, ulong addStart) {
|
||||||
|
|
||||||
int copyOffset = (int)((addStart - addressStart) * 2);
|
int copyOffset = (int)((addStart - addressStart) * 2);
|
||||||
@@ -117,11 +131,21 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"DT{AddressStart}-{AddressEnd}";
|
public override string ToString() {
|
||||||
|
|
||||||
public virtual string GetName() => $"{ToString()} ({managedRegisters.Count} Registers)";
|
switch (registerType) {
|
||||||
|
case RegisterPrefix.X:
|
||||||
|
case RegisterPrefix.Y:
|
||||||
|
case RegisterPrefix.R:
|
||||||
|
return $"W{registerType}{AddressStart}-{AddressEnd} ({managedRegisters.Count} Registers)";
|
||||||
|
case RegisterPrefix.DT:
|
||||||
|
case RegisterPrefix.DDT:
|
||||||
|
return $"DT{AddressStart}-{AddressEnd} ({managedRegisters.Count} Registers)";
|
||||||
|
}
|
||||||
|
|
||||||
public string GetHash() => GetHashCode().ToString();
|
return "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
using MewtocolNet.Registers;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MewtocolNet.UnderlyingRegisters {
|
|
||||||
|
|
||||||
public class DTArea : AreaBase, IMemoryArea {
|
|
||||||
|
|
||||||
internal DTArea(MewtocolInterface mewIf) : base(mewIf) { }
|
|
||||||
|
|
||||||
public override string ToString() => $"DT{AddressStart}-{AddressEnd}";
|
|
||||||
|
|
||||||
public override string GetName() => $"{ToString()} ({managedRegisters.Count} Registers)";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using MewtocolNet.Registers;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MewtocolNet.UnderlyingRegisters {
|
|
||||||
|
|
||||||
public class WRArea : AreaBase, IMemoryArea {
|
|
||||||
|
|
||||||
internal WRArea(MewtocolInterface mewIf) : base(mewIf) { }
|
|
||||||
|
|
||||||
public override string ToString() => $"DT{AddressStart}-{AddressEnd}";
|
|
||||||
|
|
||||||
public override string GetName() => $"{ToString()} ({managedRegisters.Count} Registers)";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -94,6 +94,9 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
AddToArea(reg, reg.RegisterType);
|
AddToArea(reg, reg.RegisterType);
|
||||||
|
|
||||||
|
//reset overlap fitted for all
|
||||||
|
reg.wasOverlapFitted = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//order
|
//order
|
||||||
@@ -102,23 +105,20 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
PollLevel lvl = pollLevels[i];
|
PollLevel lvl = pollLevels[i];
|
||||||
|
|
||||||
//poll level has no areas
|
//poll level has no areas
|
||||||
if(lvl.dataAreas.Count == 0 &&
|
if(lvl.GetAllAreas().Count() == 0) {
|
||||||
lvl.externalRelayInAreas.Count == 0 &&
|
|
||||||
lvl.externalRelayOutAreas.Count == 0 &&
|
|
||||||
lvl.internalRelayAreas.Count == 0) {
|
|
||||||
|
|
||||||
pollLevels.Remove(lvl);
|
pollLevels.Remove(lvl);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var area in lvl.dataAreas) {
|
foreach (var area in lvl.GetAllAreas()) {
|
||||||
|
|
||||||
area.managedRegisters = area.managedRegisters.OrderBy(x => x.AddressStart).ToList();
|
area.managedRegisters = area.managedRegisters.OrderBy(x => x.AddressStart).ToList();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lvl.dataAreas = lvl.dataAreas.OrderBy(x => x.AddressStart).ToList();
|
//lvl.dataAreas = lvl.dataAreas.OrderBy(x => x.AddressStart).ToList();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
//create a new area
|
//create a new area
|
||||||
if (targetArea == null) {
|
if (targetArea == null) {
|
||||||
|
|
||||||
targetArea = new DTArea(mewInterface) {
|
targetArea = new AreaBase(mewInterface) {
|
||||||
addressStart = regInsAddStart,
|
addressStart = regInsAddStart,
|
||||||
addressEnd = regInsAddEnd,
|
addressEnd = regInsAddEnd,
|
||||||
registerType = insertReg.RegisterType,
|
registerType = insertReg.RegisterType,
|
||||||
@@ -253,10 +253,12 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
//check if the linked group has duplicate type registers
|
//check if the linked group has duplicate type registers
|
||||||
|
|
||||||
var dupedTypeReg = existinglinkedGroup.Linked.FirstOrDefault(x => x.IsSameAddressAndType(insertReg));
|
var dupedTypeReg = existinglinkedGroup.Linked
|
||||||
|
.FirstOrDefault(x => x.IsSameAddressAndType(insertReg) && x.PollLevel == insertReg.PollLevel);
|
||||||
|
|
||||||
if (dupedTypeReg != null && insertReg.autoGenerated) {
|
if (dupedTypeReg != null) {
|
||||||
dupedTypeReg.WithBoundProperties(insertReg.boundProperties);
|
dupedTypeReg.WithBoundProperties(insertReg.boundProperties);
|
||||||
|
dupedTypeReg.autoGenerated = insertReg.autoGenerated;
|
||||||
} else {
|
} else {
|
||||||
existinglinkedGroup.Linked.Add(insertReg);
|
existinglinkedGroup.Linked.Add(insertReg);
|
||||||
existinglinkedGroup.Linked = existinglinkedGroup.Linked.OrderBy(x => x.MemoryAddress).ToList();
|
existinglinkedGroup.Linked = existinglinkedGroup.Linked.OrderBy(x => x.MemoryAddress).ToList();
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace MewtocolTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(DisplayName = nameof(MewtocolHelpers.ParseDTRawStringAsBytes))]
|
[Fact(DisplayName = nameof(MewtocolHelpers.ParseResponseStringAsBytes))]
|
||||||
public void ParseDTByteStringGeneration() {
|
public void ParseDTByteStringGeneration() {
|
||||||
|
|
||||||
var testList = new List<byte[]>() {
|
var testList = new List<byte[]>() {
|
||||||
@@ -77,7 +77,7 @@ namespace MewtocolTests {
|
|||||||
|
|
||||||
foreach (var item in testList) {
|
foreach (var item in testList) {
|
||||||
|
|
||||||
Assert.Equal(item, $"%01$RD{item.ToHexString()}".BCC_Mew().ParseDTRawStringAsBytes());
|
Assert.Equal(item, $"%01$RD{item.ToHexString()}".BCC_Mew().ParseResponseStringAsBytes());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user