Adjusted for new mewtocol ver

This commit is contained in:
Felix Weiß
2023-08-13 23:31:34 +02:00
parent 111eacb785
commit 4fb9910d54
26 changed files with 696 additions and 168 deletions

View File

@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MewtocolNet\MewtocolNet.csproj" />
<ProjectReference Include="..\MewtocolNet\MewtocolNet.csproj"/>
</ItemGroup>
</Project>

View File

@@ -19,6 +19,9 @@ public class TestRegisterCollection : RegisterCollection {
[Register("R16B")]
public bool TestR16B { get; set; }
[Register("R902")]
public bool Test { get; set; }
[BitRegister("DT1000", 0), PollLevel(3)]
public bool? TestDT100_Word_Duplicate_SingleBit { get; set; }

View File

@@ -2,6 +2,7 @@
using Examples.WPF.ViewModels;
using MewtocolNet;
using MewtocolNet.ComCassette;
using MewtocolNet.Logging;
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
@@ -106,25 +107,44 @@ public partial class ConnectView : UserControl {
b.Struct<float>("DDT1016").PollLevel(2).Build();
b.Struct<TimeSpan>("DDT1018").PollLevel(2).Build();
b.String("DT1024", 32).PollLevel(3).Build();
b.String("DT1042", 5).PollLevel(4).Build();
b.Struct<DateAndTime>("DDT1020").PollLevel(2).Build();
b.Struct<DateAndTime>("DDT1022").PollLevel(2).Build();
b.String("DT1028", 32).PollLevel(3).Build();
b.String("DT1046", 5).PollLevel(4).Build();
b.Struct<Word>("DT1000").AsArray(5).PollLevel(1).Build();
})
.WithHeartbeatTask(async () => {
.WithHeartbeatTask(async (plc) => {
await heartbeatSetter.WriteAsync((short)new Random().Next(short.MinValue, short.MaxValue));
var randShort = (short)new Random().Next(short.MinValue, short.MaxValue);
if (outputContactReference.Value != null)
await outputContactReference.WriteAsync(!outputContactReference.Value.Value);
//write direct
//await heartbeatSetter.WriteAsync(randShort);
//or by anonymous
await plc.Register.Struct<short>("DT1000").WriteAsync(randShort);
if(testBoolReference.Value != null)
//write a register without a reference
bool randBool = new Random().Next(0, 2) == 1;
await plc.Register.Bool("Y4").WriteAsync(randBool);
if (testBoolReference.Value != null)
await testBoolReference.WriteAsync(!testBoolReference.Value.Value);
await plc.Register.Struct<DateAndTime>("DDT1022").WriteAsync(DateAndTime.FromDateTime(DateTime.UtcNow));
})
.Build();
//connect to it
await App.ViewModel.Plc.ConnectAsync();
await App.ViewModel.Plc.ConnectAsync(async () => {
await App.ViewModel.Plc.RestartProgramAsync();
});
await App.ViewModel.Plc.AwaitFirstDataCycleAsync();
if (App.ViewModel.Plc.IsConnected) {

View File

@@ -49,6 +49,21 @@
Fill="Lime"
IsEnabled="{Binding Plc.IsConnected}"/>
<Run>
<Run.Style>
<Style TargetType="Run">
<Style.Triggers>
<DataTrigger Binding="{Binding Plc.IsRunMode, Mode=OneWay}" Value="True">
<Setter Property="Text" Value="RUN MODE"/>
</DataTrigger>
<DataTrigger Binding="{Binding Plc.IsRunMode, Mode=OneWay}" Value="False">
<Setter Property="Text" Value="NO RUN MODE"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Run.Style>
</Run>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
@@ -156,10 +171,13 @@
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<GridSplitter Grid.Column="1"
Grid.Row="1"
Grid.RowSpan="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="Gray"
@@ -179,6 +197,7 @@
Margin="10"/>
<DataGrid Grid.Row="1"
Grid.RowSpan="3"
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding Plc.Registers, Mode=OneWay}">
@@ -219,14 +238,14 @@
<DataGridTemplateColumn Width="15">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border Background="{Binding MemoryAreaHash, Mode=OneWay, Converter={StaticResource hashColor}}"/>
<Border Background="{Binding MemoryArea, Mode=OneWay, Converter={StaticResource hashColor}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<TextBlock Text="Property Bindings"
<TextBlock Text="Memory Areas"
Grid.Column="2"
FontSize="18"
Margin="10"/>
@@ -238,6 +257,43 @@
BorderBrush="LightBlue"
BorderThickness="1.5">
<DataGrid IsReadOnly="True"
AutoGenerateColumns="False"
ItemsSource="{Binding Plc.MemoryAreas, Mode=OneWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="Address Range" Binding="{Binding AddressRange, Mode=OneWay}"/>
<DataGridTemplateColumn Width="15">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border Background="{Binding Converter={StaticResource hashColor}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Words" Binding="{Binding UnderlyingWordsString, Mode=OneWay}"/>
</DataGrid.Columns>
</DataGrid>
</Border>
<TextBlock Text="Property Bindings"
Grid.Column="2"
Grid.Row="2"
FontSize="18"
Margin="10"/>
<Border Grid.Column="2"
Grid.Row="3"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderBrush="LightBlue"
BorderThickness="1.5">
<ScrollViewer>
<StackPanel>

View File

@@ -0,0 +1,92 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace MewtocolNet {
/// <summary>
/// A DateAndTime struct of 4 bytes represented as seconds from 2001-01-01 in the PLC<br/>
/// This also works for the PLC type TIME_OF_DAY and DATE
/// </summary>
public struct DateAndTime : MewtocolExtTypeInit2Word {
internal DateTime value;
public DateAndTime(int year = 2001, int month = 1, int day = 1, int hour = 0, int minute = 0, int second = 0) {
var minDate = MinDate;
var maxDate = MaxDate;
if (year < 2001 || year > 2099)
throw new NotSupportedException("Year must be between 2001 and 2099");
if (month < 1 || month > 12)
throw new NotSupportedException("Month must be between 1 and 12");
if (day < 1 || day > 32)
throw new NotSupportedException("Day must be between 1 and 32");
if (day < 1 || day > 32)
throw new NotSupportedException("Month must be between 1 and 32");
var dt = new DateTime(year, month, day, hour, minute, second);
if (dt < minDate)
throw new Exception($"The minimal DATE_AND_TIME repesentation is {minDate}");
if (dt > maxDate)
throw new Exception($"The maximal DATE_AND_TIME repesentation is {maxDate}");
value = dt;
}
public static DateAndTime FromBytes(byte[] bytes) {
var secondsFrom = BitConverter.ToUInt32(bytes, 0);
return FromDateTime(MinDate + TimeSpan.FromSeconds(secondsFrom));
}
public static DateAndTime FromDateTime(DateTime time) => new DateAndTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second);
//operations
public static TimeSpan operator -(DateAndTime a, DateAndTime b) => a.value - b.value;
public static DateAndTime operator +(DateAndTime a, TimeSpan b) => FromDateTime(a.value + b);
public static bool operator ==(DateAndTime a, DateAndTime b) => a.value == b.value;
public static bool operator !=(DateAndTime a, DateAndTime b) => a.value != b.value;
public override bool Equals(object obj) {
if ((obj == null) || !this.GetType().Equals(obj.GetType())) {
return false;
} else {
return (DateAndTime)obj == this;
}
}
public override int GetHashCode() => value.GetHashCode();
public byte[] ToByteArray() => BitConverter.GetBytes(SecondsSinceStart());
public DateTime ToDateTime() => value;
private uint SecondsSinceStart() => (uint)(value - MinDate).TotalSeconds;
private static DateTime MinDate => new DateTime(2001, 01, 01, 0, 0, 0);
private static DateTime MaxDate => new DateTime(2099, 12, 31, 23, 59, 59);
//string ops
public override string ToString() => $"DT#{value:yyyy-MM-dd-HH:mm:ss}";
}
}

View File

@@ -82,8 +82,11 @@ namespace MewtocolNet {
typeCode = (int)t;
discontinued = t.IsDiscontinued();
#if Debug
exrt = t.IsEXRTPLC();
tested = t.WasTestedLive();
#endif
}

View File

@@ -1,7 +1,9 @@
using MewtocolNet.Events;
using MewtocolNet.ProgramParsing;
using MewtocolNet.RegisterBuilding;
using MewtocolNet.RegisterBuilding.BuilderPatterns;
using MewtocolNet.Registers;
using MewtocolNet.UnderlyingRegisters;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -69,6 +71,11 @@ namespace MewtocolNet {
/// </summary>
int PollerCycleDurationMs { get; }
/// <summary>
/// Shorthand indicator if the plc is in RUN mode
/// </summary>
bool IsRunMode { get; }
/// <summary>
/// Currently queued message count
/// </summary>
@@ -94,12 +101,14 @@ namespace MewtocolNet {
/// </summary>
int ConnectTimeout { get; set; }
IEnumerable<IRegister> Registers { get; }
IEnumerable<IRegister> Registers { get; }
RBuildAnon Register { get; }
/// <summary>
/// Tries to establish a connection with the device asynchronously
/// </summary>
/// <param name="onConnected">A callback for excecuting something right after the plc connected</param>
/// <param name="onConnected">A callback for excecuting something inside the plc connetion process</param>
/// <returns></returns>
Task ConnectAsync(Func<Task> onConnected = null);
@@ -187,6 +196,11 @@ namespace MewtocolNet {
/// </summary>
string Explain();
/// <summary>
/// A readonly list of the underlying memory areas
/// </summary>
IReadOnlyList<IMemoryArea> MemoryAreas { get; }
}
}

View File

@@ -16,7 +16,7 @@ namespace MewtocolNet.Logging {
/// <summary>
/// Defines the default output logger targets
/// </summary>
public static LoggerTargets DefaultTargets { get; set; } = LoggerTargets.Console;
public static LoggerTargets DefaultTargets { get; set; } = LoggerTargets.None;
internal static Action<DateTime, LogLevel, string> LogInvoked;
@@ -26,7 +26,7 @@ namespace MewtocolNet.Logging {
OnNewLogMessage((d, l, m) => {
if(isConsoleApplication && DefaultTargets.HasFlag(LoggerTargets.Console)) {
if(isConsoleApplication || DefaultTargets.HasFlag(LoggerTargets.Console)) {
switch (l) {
case LogLevel.Error:

View File

@@ -379,12 +379,12 @@ namespace MewtocolNet
/// <summary>
/// A builder for attaching register collections
/// </summary>
public PostInit<T> WithRegisters(Action<RBuild> builder) {
public PostInit<T> WithRegisters(Action<RBuildMulti> builder) {
try {
var plc = (MewtocolInterface)(object)intf;
var regBuilder = new RBuild(plc);
var regBuilder = new RBuildMulti(plc);
builder.Invoke(regBuilder);
@@ -406,7 +406,7 @@ namespace MewtocolNet
/// </summary>
/// <param name="heartBeatAsync"></param>
/// <returns></returns>
public EndInitSetup<T> WithHeartbeatTask(Func<Task> heartBeatAsync, bool executeInProg = false) {
public EndInitSetup<T> WithHeartbeatTask(Func<IPlc,Task> heartBeatAsync, bool executeInProg = false) {
try {
var plc = (MewtocolInterface)(object)this.intf;
@@ -426,6 +426,14 @@ namespace MewtocolNet
}
/// <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) => WithHeartbeatTask(heartBeatAsync, executeInProg);
/// <summary>
/// Builds and returns the final plc interface
/// </summary>

View File

@@ -1,10 +1,12 @@
using MewtocolNet.Events;
using MewtocolNet.Helpers;
using MewtocolNet.Logging;
using MewtocolNet.RegisterBuilding.BuilderPatterns;
using MewtocolNet.Registers;
using MewtocolNet.UnderlyingRegisters;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
@@ -156,6 +158,9 @@ namespace MewtocolNet {
/// <inheritdoc/>
public int BytesPerSecondDownstream => bytesPerSecondDownstream;
/// <inheritdoc/>
public bool IsRunMode => PlcInfo.IsRunMode;
/// <inheritdoc/>
public MewtocolVersion MewtocolVersion {
get => mewtocolVersion;
@@ -168,6 +173,9 @@ namespace MewtocolNet {
/// <inheritdoc/>
public string ConnectionInfo => GetConnectionInfo();
/// <inheritdoc/>
public RBuildAnon Register => new RBuildAnon(this);
#endregion
#region Public read/write Properties / Fields
@@ -191,6 +199,21 @@ namespace MewtocolNet {
Disconnected += MewtocolInterface_Disconnected;
RegisterChanged += OnRegisterChanged;
PropertyChanged += (s, e) => {
if (e.PropertyName == nameof(PlcInfo)) {
PlcInfo.PropertyChanged += (s1, e1) => {
if (e1.PropertyName == nameof(PlcInfo.IsRunMode))
OnPropChange(nameof(IsRunMode));
};
}
};
memoryManager.MemoryLayoutChanged += () => {
OnPropChange(nameof(MemoryAreas));
};
}
internal MewtocolInterface Build () {
@@ -269,11 +292,20 @@ namespace MewtocolNet {
if (callBack != null) {
await Task.Run(callBack);
await callBack();
Logger.Log($">> OnConnected run complete <<", LogLevel.Verbose, this);
}
//run all register collection on online tasks
foreach (var col in registerCollections) {
await col.OnInterfaceLinkedAndOnline(this);
}
Logger.Log($">> OnConnected register collections run complete <<", LogLevel.Verbose, this);
}
}
@@ -281,7 +313,14 @@ namespace MewtocolNet {
protected virtual Task ReconnectAsync(int conTimeout) => throw new NotImplementedException();
/// <inheritdoc/>
public async Task AwaitFirstDataCycleAsync() => await firstPollTask;
public async Task AwaitFirstDataCycleAsync() {
if(firstPollTask != null && !firstPollTask.IsCompleted)
await firstPollTask;
await Task.CompletedTask;
}
/// <inheritdoc/>
public async Task DisconnectAsync() {
@@ -842,22 +881,8 @@ namespace MewtocolNet {
isReconnectingStage = false;
isConnectingStage = false;
if (!usePoller) {
firstPollTask.RunSynchronously();
}
Connected?.Invoke(this, new PlcConnectionArgs());
PolledCycle += OnPollCycleDone;
void OnPollCycleDone() {
if(firstPollTask != null && !firstPollTask.IsCompleted)
firstPollTask.RunSynchronously();
PolledCycle -= OnPollCycleDone;
}
}
private protected void OnReconnected () {
@@ -982,8 +1007,12 @@ namespace MewtocolNet {
#endregion
/// <inheritdoc/>
public string Explain() => memoryManager.ExplainLayout();
/// <inheritdoc/>
public IReadOnlyList<IMemoryArea> MemoryAreas => memoryManager.GetAllMemoryAreas();
}
}

View File

@@ -18,10 +18,11 @@ namespace MewtocolNet {
public abstract partial class MewtocolInterface {
private bool heartbeatNeedsRun = false;
private bool heartbeatTimerRunning = false;
internal Task heartbeatTask = Task.CompletedTask;
internal Func<Task> heartbeatCallbackTask;
internal Func<IPlc, Task> heartbeatCallbackTask;
internal bool execHeartBeatCallbackTaskInProg = false;
internal Task pollCycleTask;
@@ -73,6 +74,7 @@ namespace MewtocolNet {
heartBeatTimer.Elapsed -= PollTimerTick;
heartBeatTimer.Dispose();
heartbeatTimerRunning = false;
}
@@ -82,12 +84,15 @@ namespace MewtocolNet {
if (!IsConnected) return;
heartBeatTimer = new System.Timers.Timer();
heartBeatTimer.Interval = 3000;
heartBeatTimer.Elapsed += PollTimerTick;
heartBeatTimer.Start();
if (!usePoller) return;
if(!heartbeatTimerRunning) {
heartBeatTimer = new System.Timers.Timer();
heartBeatTimer.Interval = 3000;
heartBeatTimer.Elapsed += PollTimerTick;
heartBeatTimer.Start();
heartbeatTimerRunning = true;
}
if (!usePoller || PollerActive) return;
bool hasCyclic = memoryManager.HasCyclicPollableRegisters();
bool hasFirstCycle = memoryManager.HasSingleCyclePollableRegisters();
@@ -154,7 +159,7 @@ namespace MewtocolNet {
}
if(heartbeatCallbackTask != null && (plcInfo.IsRunMode || execHeartBeatCallbackTaskInProg))
await heartbeatCallbackTask();
await heartbeatCallbackTask(this);
Logger.LogVerbose("End heartbeat", this);
@@ -235,6 +240,12 @@ namespace MewtocolNet {
sw.Stop();
if (firstPollTask != null && !firstPollTask.IsCompleted) {
firstPollTask.RunSynchronously();
firstPollTask = null;
Logger.Log("poll cycle first done");
}
pollerFirstCycleCompleted = true;
PollerCycleDurationMs = (int)sw.ElapsedMilliseconds;
@@ -296,11 +307,6 @@ namespace MewtocolNet {
collection.OnInterfaceLinked(this);
}
Connected += (s,e) => {
if (collection != null)
collection.OnInterfaceLinkedAndOnline(this);
};
}
AddRegisters(regBuild.assembler.assembled.ToArray());

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<IsPublishable>false</IsPublishable>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Mewtocol.NET</PackageId>
<Version>0.0.0</Version>
@@ -17,15 +16,19 @@
<PackageTags>plc;panasonic;mewtocol;automation;</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<UserSecretsId>2ccdcc9b-94a3-4e76-8827-453ab889ea33</UserSecretsId>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<DocumentationFile>..\Builds\MewtocolNet\MewtocolNet.xml</DocumentationFile>
<OutputPath>..\Builds\MewtocolNet</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DocumentationFile>..\Builds\MewtocolNet\Debug\MewtocolNet.xml</DocumentationFile>
<OutputPath>..\Builds\MewtocolNet\Debug</OutputPath>
</PropertyGroup>
<ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>MewtocolTests</_Parameter1>
</AssemblyAttribute>

View File

@@ -15,35 +15,35 @@ namespace MewtocolNet {
/// <summary>
/// Is in RUN mode, otherwise its PROG Mode
/// </summary>
RunMode = 1,
RunMode = 1 << 0,
/// <summary>
/// Is in test mode, otherwise ok
/// </summary>
TestMode = 2,
TestMode = 1 << 1,
/// <summary>
/// Is BRK/1 step executed
/// </summary>
BreakPointPerOneStep = 4,
BreakPointPerOneStep = 1 << 2,
/// <summary>
/// Is BRK command enabled
/// </summary>
BreakEnabled = 16,
BreakEnabled = 1 << 3,
/// <summary>
/// Is outputting to external device
/// </summary>
ExternalOutput = 32,
ExternalOutput = 1 << 4,
/// <summary>
/// Is 1 step exec enabled
/// </summary>
OneStepExecEnabled = 64,
OneStepExecEnabled = 1 << 5,
/// <summary>
/// Is a message displayed?
/// </summary>
MessageInstructionDisplayed = 128,
MessageInstructionDisplayed = 1 << 6,
/// <summary>
/// Is in remote mode
/// </summary>
RemoteMode = 255,
RemoteMode = 1 << 7,
}

View File

@@ -1,6 +1,7 @@
using MewtocolNet.Registers;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace MewtocolNet.RegisterAttributes {
@@ -57,7 +58,7 @@ namespace MewtocolNet.RegisterAttributes {
/// and the plc connection is established
/// </summary>
/// <param name="plc">The parent interface</param>
public virtual void OnInterfaceLinkedAndOnline(MewtocolInterface plc) { }
public virtual Task OnInterfaceLinkedAndOnline(MewtocolInterface plc) => Task.CompletedTask;
}

View File

@@ -25,12 +25,12 @@ namespace MewtocolNet.RegisterBuilding {
(x) => TryBuildNumericBased(x),
};
//bool registers
private static ParseResult TryBuildBoolean(string plcAddrName) {
//regex to find special register values
var patternBool = new Regex(@"(?<prefix>X|Y|R)(?<area>[0-9]{0,3})(?<special>(?:[0-9]|[A-F]){1})?");
var patternBool = new Regex(@"(?<prefix>X|Y|R)(?<area>[0-9]{0,3})(?<special>(?:[0-9]|[A-F]){1})");
var match = patternBool.Match(plcAddrName);

View File

@@ -34,10 +34,13 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
//bool constructor
public StructStp<bool> Bool(string fpAddr, string name = null) {
internal StructStp<bool> Bool(string fpAddr, string name = null) {
var data = AddressTools.ParseAddress(fpAddr, name);
if (!data.regType.IsBoolean())
throw new NotSupportedException($"The address '{fpAddr}' was no boolean FP address");
data.dotnetVarType = typeof(bool);
return new StructStp<bool>(data) {
@@ -48,10 +51,13 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
//struct constructor
public StructStp<T> Struct<T>(string fpAddr, string name = null) where T : struct {
internal StructStp<T> Struct<T>(string fpAddr, string name = null) where T : struct {
var data = AddressTools.ParseAddress(fpAddr, name);
if (data.regType.IsBoolean())
throw new NotSupportedException($"The address '{fpAddr}' was no DT address");
data.dotnetVarType = typeof(T);
return new StructStp<T>(data) {
@@ -62,10 +68,13 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
//string constructor
public StringStp<string> String(string fpAddr, int sizeHint, string name = null) {
internal StringStp<string> String(string fpAddr, int sizeHint, string name = null) {
var data = AddressTools.ParseAddress(fpAddr, name);
if (data.regType.IsBoolean())
throw new NotSupportedException($"The address '{fpAddr}' was no string address");
data.dotnetVarType = typeof(string);
data.byteSizeHint = (uint)sizeHint;
@@ -82,6 +91,8 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
//structs can lead to arrays
public class StructStp<T> : ArrayStp<T> where T : struct {
internal StructStp() {}
internal StructStp(StepData data) {
this.Data = data;
@@ -89,30 +100,13 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
}
public void Build() => builder.Assemble(this);
public void Build(out IRegister<T> reference) => reference = (IRegister<T>)builder.Assemble(this);
public StructStpOut<T> PollLevel(int level) {
Data.pollLevel = level;
return new StructStpOut<T>().Map(this);
}
}
public class StructStpOut<T> : SBaseRB where T : struct {
public void Build() => builder.Assemble(this);
public void Build(out IRegister<T> reference) => reference = (IRegister<T>)builder.Assemble(this);
}
//strings can lead to arrays
public class StringStp<T> : ArrayStp<T> where T : class {
internal StringStp() { }
internal StringStp(StepData data) {
this.Data = data;
@@ -120,25 +114,6 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
}
public void Build() => builder.Assemble(this);
public void Build(out IStringRegister reference) => reference = (IStringRegister)builder.Assemble(this);
public StringOutStp PollLevel(int level) {
Data.pollLevel = level;
return new StringOutStp().Map(this);
}
}
public class StringOutStp : SBaseRB {
public void Build() => builder.Assemble(this);
public void Build(out IStringRegister reference) => reference = (IStringRegister)builder.Assemble(this);
}
//arrays
@@ -199,66 +174,21 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
//1D array
public class TypedArr1D<T> : TypedArr1DOut<T> {
public class TypedArr1D<T> : TypedArr1DOut<T> { }
public TypedArr1DOut<T> PollLevel(int level) {
Data.pollLevel = level;
return new TypedArr1DOut<T>().Map(this);
}
}
public class TypedArr1DOut<T> : SBaseRB {
public IArrayRegister<T> Build() => (IArrayRegister<T>)builder.Assemble(this);
public void Build(out IArrayRegister<T> reference) => reference = (IArrayRegister<T>)builder.Assemble(this);
}
public class TypedArr1DOut<T> : SBaseRB { }
//2D array
public class TypedArr2D<T> : TypedArr2DOut<T> {
public class TypedArr2D<T> : TypedArr2DOut<T> { }
public TypedArr2DOut<T> PollLevel(int level) {
Data.pollLevel = level;
return new TypedArr2DOut<T>().Map(this);
}
}
public class TypedArr2DOut<T> : SBaseRB {
public IArrayRegister2D<T> Build() => (IArrayRegister2D<T>)builder.Assemble(this);
public void Build(out IArrayRegister2D<T> reference) => reference = (IArrayRegister2D<T>)builder.Assemble(this);
}
public class TypedArr2DOut<T> : SBaseRB { }
//3D array
public class TypedArr3D<T> : SBaseRB {
public class TypedArr3D<T> : SBaseRB { }
public TypedArr3DOut<T> PollLevel(int level) {
Data.pollLevel = level;
return new TypedArr3DOut<T>().Map(this);
}
}
public class TypedArr3DOut<T> : SBaseRB {
public IArrayRegister3D<T> Build() => (IArrayRegister3D<T>)builder.Assemble(this);
public void Build(out IArrayRegister3D<T> reference) => reference = (IArrayRegister3D<T>)builder.Assemble(this);
}
public class TypedArr3DOut<T> : SBaseRB { }
#endregion

View File

@@ -0,0 +1,130 @@
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
/// <summary>
/// An anonymous register build interface
/// </summary>
public class RBuildAnon : RBuild {
internal RBuildAnon(MewtocolInterface plc) : base(plc) { }
public new MultStructStp<bool> Bool(string fpAddr) => new MultStructStp<bool>().Map(base.Bool(fpAddr));
public new MultStructStp<T> Struct<T>(string fpAddr) where T : struct => new MultStructStp<T>().Map(base.Struct<T>(fpAddr));
public new MultStringStp<string> String(string fpAddr, int sizeHint) => new MultStringStp<string>().Map(base.String(fpAddr, sizeHint));
public class MultStructStp<T> : MultArrayStp<T> where T : struct {
public async Task WriteAsync(T value) {
var reg = (IRegister<T>)builder.Assemble(this);
await reg.WriteAsync(value);
}
public async Task<T> ReadAsync() {
var reg = (IRegister<T>)builder.Assemble(this);
return await reg.ReadAsync();
}
}
public class MultStringStp<T> : MultArrayStp<T> where T : class {
public async Task WriteAsync(string value) {
var reg = (IStringRegister)builder.Assemble(this);
await reg.WriteAsync(value);
}
public async Task<string> ReadAsync() {
var reg = (IStringRegister)builder.Assemble(this);
return await reg.ReadAsync();
}
}
public class MultArrayStp<T> : ArrayStp<T> {
public new MultTypedArr1D<T> AsArray(int i) => new MultTypedArr1D<T>().Map(base.AsArray(i));
public new MultTypedArr2D<T> AsArray(int i1, int i2) => new MultTypedArr2D<T>().Map(base.AsArray(i1, i2));
public new MultTypedArr3D<T> AsArray(int i1, int i2, int i3) => new MultTypedArr3D<T>().Map(base.AsArray(i1, i2, i3));
}
//1D array
public class MultTypedArr1D<T> : TypedArr1D<T> {
public async Task WriteAsync(T[] value) {
var reg = (IArrayRegister<T>)builder.Assemble(this);
await reg.WriteAsync(value);
}
public async Task<T[]> ReadAsync() {
var reg = (IArrayRegister<T>)builder.Assemble(this);
return await reg.ReadAsync();
}
}
//2D array
public class MultTypedArr2D<T> : TypedArr2D<T> {
public async Task WriteAsync(T[,] value) {
var reg = (IArrayRegister2D<T>)builder.Assemble(this);
await reg.WriteAsync(value);
}
public async Task<T[,]> ReadAsync() {
var reg = (IArrayRegister2D<T>)builder.Assemble(this);
return await reg.ReadAsync();
}
}
//3D array
public class MultTypedArr3D<T> : TypedArr3D<T> {
public async Task WriteAsync(T[,,] value) {
var reg = (IArrayRegister3D<T>)builder.Assemble(this);
await reg.WriteAsync(value);
}
public async Task<T[,,]> ReadAsync() {
var reg = (IArrayRegister3D<T>)builder.Assemble(this);
return await reg.ReadAsync();
}
}
}
}

View File

@@ -0,0 +1,142 @@
using MewtocolNet.Registers;
using System;
using System.Collections.Generic;
using System.Text;
namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
public class RBuildMulti : RBuild {
internal RBuildMulti(MewtocolInterface plc) : base(plc) {}
//bool constructor
public new MultStructStp<bool> Bool(string fpAddr, string name = null) => new MultStructStp<bool>().Map(base.Bool(fpAddr, name));
//struct constructor
public new MultStructStp<T> Struct<T>(string fpAddr, string name = null) where T : struct => new MultStructStp<T>().Map(base.Struct<T>(fpAddr, name));
//string constructor
public new MultStringStp<string> String(string fpAddr, int sizeHint, string name = null) => new MultStringStp<string>().Map(base.String(fpAddr, sizeHint, name));
public class MultStructStp<T> : MultArrayStp<T> where T : struct {
public void Build() => builder.Assemble(this);
public void Build(out IRegister<T> reference) => reference = (IRegister<T>)builder.Assemble(this);
public StructStpOut<T> PollLevel(int level) {
Data.pollLevel = level;
return new StructStpOut<T>().Map(this);
}
}
public class StructStpOut<T> : SBaseRB where T : struct {
public void Build() => builder.Assemble(this);
public void Build(out IRegister<T> reference) => reference = (IRegister<T>)builder.Assemble(this);
}
public class MultStringStp<T> : MultArrayStp<T> where T : class {
public void Build() => builder.Assemble(this);
public void Build(out IStringRegister reference) => reference = (IStringRegister)builder.Assemble(this);
public StringOutStp PollLevel(int level) {
Data.pollLevel = level;
return new StringOutStp().Map(this);
}
}
public class StringOutStp : SBaseRB {
public void Build() => builder.Assemble(this);
public void Build(out IStringRegister reference) => reference = (IStringRegister)builder.Assemble(this);
}
public class MultArrayStp<T> : ArrayStp<T> {
public new MultTypedArr1D<T> AsArray(int i) => new MultTypedArr1D<T>().Map(base.AsArray(i));
public new MultTypedArr2D<T> AsArray(int i1, int i2) => new MultTypedArr2D<T>().Map(base.AsArray(i1, i2));
public new MultTypedArr3D<T> AsArray(int i1, int i2, int i3) => new MultTypedArr3D<T>().Map(base.AsArray(i1, i2, i3));
}
//1D array
public class MultTypedArr1D<T> : TypedArr1D<T> {
public MultTypedArr1DOut<T> PollLevel(int level) {
Data.pollLevel = level;
return new MultTypedArr1DOut<T>().Map(this);
}
}
public class MultTypedArr1DOut<T> : TypedArr1DOut<T> {
public IArrayRegister<T> Build() => (IArrayRegister<T>)builder.Assemble(this);
public void Build(out IArrayRegister<T> reference) => reference = (IArrayRegister<T>)builder.Assemble(this);
}
//2D array
public class MultTypedArr2D<T> : TypedArr2D<T> {
public MultTypedArr2DOut<T> PollLevel(int level) {
Data.pollLevel = level;
return new MultTypedArr2DOut<T>().Map(this);
}
}
public class MultTypedArr2DOut<T> : TypedArr2DOut<T> {
public IArrayRegister2D<T> Build() => (IArrayRegister2D<T>)builder.Assemble(this);
public void Build(out IArrayRegister2D<T> reference) => reference = (IArrayRegister2D<T>)builder.Assemble(this);
}
//3D array
public class MultTypedArr3D<T> : TypedArr3D<T> {
public MultTypedArr3DOut<T> PollLevel(int level) {
Data.pollLevel = level;
return new MultTypedArr3DOut<T>().Map(this);
}
}
public class MultTypedArr3DOut<T> : TypedArr3DOut<T> {
public IArrayRegister3D<T> Build() => (IArrayRegister3D<T>)builder.Assemble(this);
public void Build(out IArrayRegister3D<T> reference) => reference = (IArrayRegister3D<T>)builder.Assemble(this);
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using MewtocolNet.Events;
using MewtocolNet.UnderlyingRegisters;
namespace MewtocolNet.Registers {
@@ -20,6 +21,11 @@ namespace MewtocolNet.Registers {
/// </summary>
bool IsAutoGenerated { get; }
/// <summary>
/// The readonly memory area of the register
/// </summary>
IMemoryArea MemoryArea { get; }
/// <summary>
/// Type of the underlying register
/// </summary>

View File

@@ -66,6 +66,9 @@ namespace MewtocolNet.Registers {
/// <inheritdoc/>
public RegisterPrefix RegisterType { get; internal set; }
/// <inheritdoc/>
public IMemoryArea MemoryArea => underlyingMemory;
/// <inheritdoc/>
public string Name => name;

View File

@@ -123,7 +123,7 @@ namespace MewtocolNet.Registers {
//if string correct the sizing of the byte hint was wrong
var reservedSize = BitConverter.ToInt16(bytes, 0);
if (reservedStringLength != reservedSize && attachedInterface.PlcInfo.IsRunMode)
if (reservedStringLength != reservedSize && attachedInterface.PlcInfo.IsRunMode && !attachedInterface.isConnectingStage)
throw new NotSupportedException(
$"The STRING register at {GetMewName()} is not correctly sized, " +
$"the size should be STRING[{reservedSize}] instead of STRING[{reservedStringLength}]"

View File

@@ -127,6 +127,15 @@ namespace MewtocolNet.TypeConversion {
},
},
//default Datetime DDT conversion
new PlcTypeConversion<DateAndTime>(RegisterPrefix.DDT) {
HoldingRegisterType = typeof(StructRegister<DateAndTime>),
PlcVarType = PlcVarType.DATE_AND_TIME,
FromRaw = (reg, bytes) => DateAndTime.FromBytes(bytes),
ToRaw = (reg, value) => value.ToByteArray(),
},
//default string DT Range conversion Example bytes: (04 00 03 00 XX XX XX)
//first 4 bytes are reserved size (2 bytes) and used size (2 bytes)
//the remaining bytes are the ascii bytes for the string

View File

@@ -2,17 +2,21 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace MewtocolNet.UnderlyingRegisters {
public class AreaBase {
internal class AreaBase : IMemoryArea {
private MewtocolInterface mewInterface;
private int pollLevel;
internal RegisterPrefix registerType;
internal ulong addressStart;
internal ulong addressEnd;
@@ -23,12 +27,25 @@ namespace MewtocolNet.UnderlyingRegisters {
/// </summary>
internal List<LinkedRegisterGroup> managedRegisters = new List<LinkedRegisterGroup>();
public event PropertyChangedEventHandler PropertyChanged;
public ulong AddressStart => addressStart;
public ulong AddressEnd => addressEnd;
internal AreaBase(MewtocolInterface mewIf) {
//interface
public string AddressRange => GetAddressRangeString();
public IReadOnlyList<Word> UnderlyingWords => GetUnderlyingWords();
public string UnderlyingWordsString => string.Join(" ", GetUnderlyingWords());
public int PollLevel => pollLevel;
internal AreaBase(MewtocolInterface mewIf, int pollLvl) {
mewInterface = mewIf;
pollLevel = pollLvl;
}
@@ -49,6 +66,9 @@ namespace MewtocolNet.UnderlyingRegisters {
addressStart = addFrom;
addressEnd = addTo;
OnPropChange(nameof(AddressRange));
OnPropChange(nameof(UnderlyingWords));
}
public void UpdateAreaRegisterValues() {
@@ -61,6 +81,9 @@ namespace MewtocolNet.UnderlyingRegisters {
var bytes = this.GetUnderlyingBytes(regStart, addLen);
register.SetValueFromBytes(bytes);
OnPropChange(nameof(UnderlyingWords));
OnPropChange(nameof(UnderlyingWordsString));
}
}
@@ -112,6 +135,8 @@ namespace MewtocolNet.UnderlyingRegisters {
var bitArr = new BitArray(underlyingBefore);
bitArr[bitIndex] = value;
bitArr.CopyTo(underlyingBefore, 0);
SetUnderlyingBytes(underlyingBefore, reg.MemoryAddress);
@@ -131,22 +156,45 @@ namespace MewtocolNet.UnderlyingRegisters {
}
public override string ToString() {
private List<Word> GetUnderlyingWords () {
var bytes = GetUnderlyingBytes((uint)AddressStart, (int)(addressEnd - AddressStart) + 1);
var words = new List<Word>();
for (int i = 0; i < bytes.Length / 2; i += 2) {
words.Add(new Word(new byte[] { bytes[i], bytes[i + 1] }));
}
return words;
}
private string GetAddressRangeString() {
switch (registerType) {
case RegisterPrefix.X:
case RegisterPrefix.Y:
case RegisterPrefix.R:
return $"W{registerType}{AddressStart}-{AddressEnd} ({managedRegisters.Count} Registers)";
return $"W{registerType}{AddressStart}-{AddressEnd}";
case RegisterPrefix.DT:
case RegisterPrefix.DDT:
return $"DT{AddressStart}-{AddressEnd} ({managedRegisters.Count} Registers)";
return $"DT{AddressStart}-{AddressEnd}";
}
return "";
}
public override string ToString() => $"{GetAddressRangeString()} ({managedRegisters.Count} Registers)";
private protected void OnPropChange([CallerMemberName] string propertyName = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -1,16 +1,18 @@
using MewtocolNet.Registers;
using System.Collections.Generic;
using System.ComponentModel;
namespace MewtocolNet.UnderlyingRegisters {
internal interface IMemoryArea {
public interface IMemoryArea : INotifyPropertyChanged {
string GetName();
string AddressRange { get; }
byte[] GetUnderlyingBytes(Register reg);
IReadOnlyList<Word> UnderlyingWords { get; }
void SetUnderlyingBytes(Register reg, byte[] bytes);
string UnderlyingWordsString { get; }
void UpdateAreaRegisterValues();
int PollLevel { get; }
}

View File

@@ -221,7 +221,7 @@ namespace MewtocolNet.UnderlyingRegisters {
//create a new area
if (targetArea == null) {
targetArea = new AreaBase(mewInterface) {
targetArea = new AreaBase(mewInterface, pollLevelFound.level) {
addressStart = regInsAddStart,
addressEnd = regInsAddEnd,
registerType = insertReg.RegisterType,
@@ -455,6 +455,20 @@ namespace MewtocolNet.UnderlyingRegisters {
}
internal IReadOnlyList<IMemoryArea> GetAllMemoryAreas() {
List<IMemoryArea> areas = new List<IMemoryArea>();
foreach (var lvl in pollLevels) {
areas.AddRange(lvl.GetAllAreas());
}
return areas;
}
internal bool HasSingleCyclePollableRegisters() {
bool hasCyclicPollableLevels = pollLevels.Any(x => x.level != MewtocolNet.PollLevel.FirstIteration);

9
nuget.config Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="LocalMewtocolNetNuget" value="Builds/MewtocolNet/Debug" />
</packageSources>
<activePackageSource>
<add key="All" value="(Aggregate source)" />
</activePackageSource>
</configuration>