mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Generized memory areas
This commit is contained in:
@@ -6,5 +6,6 @@
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
<conv:NegationConverter x:Key="bInv"/>
|
||||
<conv:ColorHashConverter x:Key="hashColor"/>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
62
Examples.WPF/Converters/ColorHashConverter.cs
Normal file
62
Examples.WPF/Converters/ColorHashConverter.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
|
||||
namespace Examples.WPF.Converters;
|
||||
|
||||
[ValueConversion(typeof(bool), typeof(bool))]
|
||||
public class ColorHashConverter : IValueConverter {
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
|
||||
var hashCode = value.GetHashCode();
|
||||
var randColor = GenerateRandomVibrantColor(new Random(hashCode));
|
||||
|
||||
System.Windows.Media.Brush outBrush = new System.Windows.Media.SolidColorBrush(new System.Windows.Media.Color {
|
||||
R = randColor.R,
|
||||
G = randColor.G,
|
||||
B = randColor.B,
|
||||
A = 255,
|
||||
});
|
||||
|
||||
return outBrush;
|
||||
|
||||
}
|
||||
|
||||
private Color GenerateRandomVibrantColor(Random random) {
|
||||
|
||||
byte red = (byte)random.Next(256);
|
||||
byte green = (byte)random.Next(256);
|
||||
byte blue = (byte)random.Next(256);
|
||||
|
||||
Color color = Color.FromArgb(255, red, green, blue);
|
||||
|
||||
// Ensure the color is vibrant and colorful
|
||||
while (!IsVibrantColor(color)) {
|
||||
red = (byte)random.Next(256);
|
||||
green = (byte)random.Next(256);
|
||||
blue = (byte)random.Next(256);
|
||||
color = Color.FromArgb(255,red, green, blue);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
private bool IsVibrantColor(Color color) {
|
||||
|
||||
int minBrightness = 100;
|
||||
int maxBrightness = 200;
|
||||
int minSaturation = 150;
|
||||
|
||||
int brightness = (int)(color.GetBrightness() * 255);
|
||||
int saturation = (int)(color.GetSaturation() * 255);
|
||||
|
||||
return brightness >= minBrightness && brightness <= maxBrightness && saturation >= minSaturation;
|
||||
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -9,6 +10,24 @@ namespace Examples.WPF.ViewModels;
|
||||
|
||||
public class PlcDataViewViewModel : ViewModelBase {
|
||||
|
||||
private ReconnectArgs plcCurrentReconnectArgs = null!;
|
||||
|
||||
public IPlc Plc => App.ViewModel.Plc!;
|
||||
|
||||
public ReconnectArgs PlcCurrentReconnectArgs {
|
||||
get => plcCurrentReconnectArgs;
|
||||
set {
|
||||
plcCurrentReconnectArgs = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
public PlcDataViewViewModel () {
|
||||
|
||||
Plc.ReconnectTryStarted += (s, e) => PlcCurrentReconnectArgs = e;
|
||||
Plc.Reconnected += (s, e) => PlcCurrentReconnectArgs = null!;
|
||||
Plc.Disconnected += (s, e) => PlcCurrentReconnectArgs = null!;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,9 +59,12 @@ public partial class ConnectView : UserControl {
|
||||
App.ViewModel.Plc = Mewtocol.Ethernet(viewModel.SelectedIP, parsedInt)
|
||||
.WithPoller()
|
||||
.WithInterfaceSettings(setting => {
|
||||
setting.TryReconnectAttempts = 30;
|
||||
setting.TryReconnectAttempts = 0;
|
||||
setting.TryReconnectDelayMs = 2000;
|
||||
setting.SendReceiveTimeoutMs = 1000;
|
||||
setting.HeartbeatIntervalMs = 3000;
|
||||
setting.MaxDataBlocksPerWrite = 12;
|
||||
setting.MaxOptimizationDistance = 10;
|
||||
})
|
||||
.WithCustomPollLevels(lvl => {
|
||||
lvl.SetLevel(2, 3);
|
||||
@@ -73,6 +76,8 @@ public partial class ConnectView : UserControl {
|
||||
//b.Struct<short>("DT0").Build();
|
||||
//b.Struct<short>("DT0").AsArray(30).Build();
|
||||
|
||||
b.Bool("R10A").Build();
|
||||
|
||||
b.Struct<short>("DT1000").Build(out heartbeatSetter);
|
||||
b.Struct<Word>("DT1000").Build();
|
||||
|
||||
|
||||
@@ -33,16 +33,56 @@
|
||||
<StackPanel Margin="10"
|
||||
Grid.Row="1">
|
||||
|
||||
<TextBlock>
|
||||
<TextBlock IsEnabled="{Binding Plc.IsConnected}">
|
||||
|
||||
<Run Text="{Binding Plc.PlcInfo.TypeName, Mode=OneWay}"
|
||||
FontSize="24"
|
||||
BaselineAlignment="Center"
|
||||
FontWeight="SemiBold"/>
|
||||
|
||||
<Run Text="{Binding Plc.PlcInfo.CpuVersion, StringFormat='v{0}', Mode=OneWay}"
|
||||
FontSize="24"
|
||||
FontWeight="Light"/>
|
||||
|
||||
<Ellipse Width="10"
|
||||
Height="10"
|
||||
Fill="Lime"
|
||||
IsEnabled="{Binding Plc.IsConnected}"/>
|
||||
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock IsEnabled="{Binding Plc.IsConnected, Converter={StaticResource bInv}}">
|
||||
|
||||
<Run Text="Disconnected"
|
||||
FontSize="24"
|
||||
BaselineAlignment="Center"
|
||||
FontWeight="SemiBold"/>
|
||||
|
||||
<Ellipse Width="10"
|
||||
Height="10"
|
||||
Fill="Red"
|
||||
IsEnabled="{Binding Plc.IsConnected}"/>
|
||||
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Text="{Binding Plc.PlcInfo.TypeCode, StringFormat='#{0:X}', Mode=OneWay}"
|
||||
@@ -84,6 +124,25 @@
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<TextBlock>
|
||||
|
||||
<Run Text="Reconnecting..."/>
|
||||
<Run Text="{Binding PlcCurrentReconnectArgs.ReconnectTry, Mode=OneWay, StringFormat='{}{0}/'}"/>
|
||||
<Run Text="{Binding PlcCurrentReconnectArgs.MaxAttempts, Mode=OneWay}"/> in
|
||||
<Run Text="{Binding PlcCurrentReconnectArgs.RetryCountDownRemaining, Mode=OneWay}"/>
|
||||
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding PlcCurrentReconnectArgs, Mode=OneWay}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
|
||||
</TextBlock>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<DataGrid Grid.Row="2"
|
||||
@@ -96,6 +155,13 @@
|
||||
<DataGridTextColumn Header="Value" Binding="{Binding ValueStr}"/>
|
||||
<DataGridTextColumn Header="Poll Level" Binding="{Binding PollLevel, Mode=OneWay}"/>
|
||||
<DataGridTextColumn Header="Update Frequency" Binding="{Binding UpdateFreqHz, StringFormat='{}{0} Hz',Mode=OneWay}"/>
|
||||
<DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{Binding MemoryAreaHash, Mode=OneWay, Converter={StaticResource hashColor}}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
|
||||
84
MewtocolNet/Events/ReconnectArgs.cs
Normal file
84
MewtocolNet/Events/ReconnectArgs.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace MewtocolNet.Events {
|
||||
|
||||
public delegate void PlcReconnectEventHandler(object sender, ReconnectArgs e);
|
||||
|
||||
public class ReconnectArgs : EventArgs, INotifyPropertyChanged {
|
||||
|
||||
private TimeSpan retryCountDownRemaining;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public int ReconnectTry { get; internal set; }
|
||||
|
||||
public int MaxAttempts { get; internal set; }
|
||||
|
||||
public TimeSpan RetryCountDownTime { get; internal set; }
|
||||
|
||||
public TimeSpan RetryCountDownRemaining {
|
||||
get => retryCountDownRemaining;
|
||||
private set {
|
||||
retryCountDownRemaining = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
private System.Timers.Timer countDownTimer;
|
||||
|
||||
internal ReconnectArgs(int currentAttempt, int totalAttempts, TimeSpan delayBetween) {
|
||||
|
||||
ReconnectTry = currentAttempt;
|
||||
MaxAttempts = totalAttempts;
|
||||
RetryCountDownTime = delayBetween;
|
||||
|
||||
//start countdown timer
|
||||
RetryCountDownRemaining = RetryCountDownTime;
|
||||
|
||||
var interval = 100;
|
||||
var intervalTS = TimeSpan.FromMilliseconds(interval);
|
||||
|
||||
countDownTimer = new System.Timers.Timer(100);
|
||||
|
||||
countDownTimer.Elapsed += (s, e) => {
|
||||
|
||||
if (RetryCountDownRemaining <= TimeSpan.Zero) {
|
||||
StopTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
RetryCountDownRemaining -= intervalTS;
|
||||
|
||||
};
|
||||
|
||||
countDownTimer.Start();
|
||||
|
||||
}
|
||||
|
||||
internal void ConnectionSuccess () {
|
||||
|
||||
StopTimer();
|
||||
|
||||
}
|
||||
|
||||
private void StopTimer () {
|
||||
|
||||
countDownTimer?.Stop();
|
||||
RetryCountDownRemaining = TimeSpan.Zero;
|
||||
|
||||
}
|
||||
|
||||
private void OnPropChange([CallerMemberName] string propertyName = null) {
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,23 @@ namespace MewtocolNet {
|
||||
|
||||
#region Byte and string operation helpers
|
||||
|
||||
public static T SetFlag<T>(this Enum value, T flag, bool set) {
|
||||
|
||||
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
|
||||
|
||||
dynamic valueAsInt = Convert.ChangeType(value, underlyingType);
|
||||
dynamic flagAsInt = Convert.ChangeType(flag, underlyingType);
|
||||
|
||||
if (set) {
|
||||
valueAsInt |= flagAsInt;
|
||||
} else {
|
||||
valueAsInt &= ~flagAsInt;
|
||||
}
|
||||
|
||||
return (T)valueAsInt;
|
||||
|
||||
}
|
||||
|
||||
public static int DetermineTypeByteIntialSize(this Type type) {
|
||||
|
||||
//enums can only be of numeric types
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using MewtocolNet.ProgramParsing;
|
||||
using MewtocolNet.Events;
|
||||
using MewtocolNet.ProgramParsing;
|
||||
using MewtocolNet.RegisterBuilding;
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
@@ -13,6 +14,31 @@ namespace MewtocolNet {
|
||||
/// </summary>
|
||||
public interface IPlc : IDisposable, INotifyPropertyChanged {
|
||||
|
||||
/// <summary>
|
||||
/// Fires when the interface is fully connected to a PLC
|
||||
/// </summary>
|
||||
event PlcConnectionEventHandler Connected;
|
||||
|
||||
/// <summary>
|
||||
/// Fires when a reconnect attempt was successfull
|
||||
/// </summary>
|
||||
event PlcConnectionEventHandler Reconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Fires when the interfaces makes a reconnect try to the PLC
|
||||
/// </summary>
|
||||
event PlcReconnectEventHandler ReconnectTryStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Fires when the plc/interface connection was fully closed
|
||||
/// </summary>
|
||||
event PlcConnectionEventHandler Disconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Fires when the value of a register changes
|
||||
/// </summary>
|
||||
event RegisterChangedEventHandler RegisterChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The current connection state of the interface
|
||||
/// </summary>
|
||||
|
||||
@@ -29,6 +29,9 @@ namespace MewtocolNet {
|
||||
/// <inheritdoc/>
|
||||
public event PlcConnectionEventHandler Reconnected;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event PlcReconnectEventHandler ReconnectTryStarted;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event PlcConnectionEventHandler Disconnected;
|
||||
|
||||
@@ -139,13 +142,7 @@ namespace MewtocolNet {
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int BytesPerSecondUpstream {
|
||||
get { return bytesPerSecondUpstream; }
|
||||
private protected set {
|
||||
bytesPerSecondUpstream = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
public int BytesPerSecondUpstream => bytesPerSecondUpstream;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsReceiving {
|
||||
@@ -157,13 +154,7 @@ namespace MewtocolNet {
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int BytesPerSecondDownstream {
|
||||
get { return bytesPerSecondDownstream; }
|
||||
private protected set {
|
||||
bytesPerSecondDownstream = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
public int BytesPerSecondDownstream => bytesPerSecondDownstream;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public MewtocolVersion MewtocolVersion {
|
||||
@@ -302,7 +293,15 @@ namespace MewtocolNet {
|
||||
|
||||
if (pollCycleTask != null && !pollCycleTask.IsCompleted) pollCycleTask.Wait();
|
||||
|
||||
OnMajorSocketExceptionWhileConnected();
|
||||
if (IsConnected) {
|
||||
|
||||
tSource.Cancel();
|
||||
isMessageLocked = false;
|
||||
|
||||
Logger.Log("The PLC connection was closed manually", LogLevel.Error, this);
|
||||
OnDisconnect();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -348,8 +347,16 @@ namespace MewtocolNet {
|
||||
//stop the heartbeat timer for the time of retries
|
||||
StopHeartBeat();
|
||||
|
||||
var eArgs = new ReconnectArgs(retryCount, tryReconnectAttempts, TimeSpan.FromMilliseconds(tryReconnectDelayMs));
|
||||
ReconnectTryStarted?.Invoke(this, eArgs);
|
||||
|
||||
Reconnected += (s, e) => eArgs.ConnectionSuccess();
|
||||
|
||||
await ReconnectAsync(tryReconnectDelayMs);
|
||||
await Task.Delay(2000);
|
||||
|
||||
if (IsConnected) return;
|
||||
|
||||
await Task.Delay(tryReconnectDelayMs);
|
||||
|
||||
retryCount++;
|
||||
|
||||
@@ -378,17 +385,7 @@ namespace MewtocolNet {
|
||||
if (regularSendTask != null && !regularSendTask.IsCompleted) {
|
||||
|
||||
//queue self
|
||||
|
||||
var t = new Task<MewtocolFrameResponse>(() => SendCommandInternalAsync(_msg, onReceiveProgress).Result);
|
||||
userInputSendTasks.Enqueue(t);
|
||||
|
||||
OnPropChange(nameof(QueuedMessages));
|
||||
|
||||
await t;
|
||||
|
||||
OnPropChange(nameof(QueuedMessages));
|
||||
|
||||
return t.Result;
|
||||
return await EnqueueMessage(_msg, onReceiveProgress);
|
||||
|
||||
}
|
||||
|
||||
@@ -399,17 +396,12 @@ namespace MewtocolNet {
|
||||
|
||||
isMessageLocked = true;
|
||||
|
||||
//wait for the last send task to complete
|
||||
//if (regularSendTask != null && !regularSendTask.IsCompleted) await regularSendTask;
|
||||
|
||||
//send request
|
||||
regularSendTask = SendTwoDirectionalFrameAsync(_msg, onReceiveProgress);
|
||||
|
||||
//if (regularSendTask == null) return MewtocolFrameResponse.Canceled;
|
||||
|
||||
try {
|
||||
|
||||
var timeoutAwaiter = await Task.WhenAny(regularSendTask, Task.Delay(2000, tSource.Token));
|
||||
var timeoutAwaiter = await Task.WhenAny(regularSendTask, Task.Delay(sendReceiveTimeoutMs, tSource.Token));
|
||||
|
||||
if (timeoutAwaiter != regularSendTask) {
|
||||
|
||||
@@ -444,10 +436,52 @@ namespace MewtocolNet {
|
||||
isMessageLocked = false;
|
||||
regularSendTask = null;
|
||||
|
||||
//run the remaining tasks if no poller is used
|
||||
if(!PollerActive && !tSource.Token.IsCancellationRequested) {
|
||||
|
||||
while(userInputSendTasks.Count > 1) {
|
||||
|
||||
if (PollerActive || tSource.Token.IsCancellationRequested) break;
|
||||
|
||||
await RunOneOpenQueuedTask();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return responseData;
|
||||
|
||||
}
|
||||
|
||||
protected async Task RunOneOpenQueuedTask() {
|
||||
|
||||
if (userInputSendTasks != null && userInputSendTasks.Count > 0) {
|
||||
|
||||
var t = userInputSendTasks.Dequeue();
|
||||
|
||||
t.Start();
|
||||
|
||||
await t;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected async Task<MewtocolFrameResponse> EnqueueMessage(string _msg, Action<double> onReceiveProgress = null) {
|
||||
|
||||
var t = new Task<MewtocolFrameResponse>(() => SendCommandInternalAsync(_msg, onReceiveProgress).Result);
|
||||
userInputSendTasks.Enqueue(t);
|
||||
|
||||
OnPropChange(nameof(QueuedMessages));
|
||||
|
||||
await t;
|
||||
|
||||
OnPropChange(nameof(QueuedMessages));
|
||||
|
||||
return t.Result;
|
||||
|
||||
}
|
||||
|
||||
private protected async Task<MewtocolFrameResponse> SendOneDirectionalFrameAsync (string frame) {
|
||||
|
||||
try {
|
||||
@@ -763,17 +797,17 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
private protected void OnMajorSocketExceptionWhileConnected() {
|
||||
|
||||
if (IsConnected) {
|
||||
private protected void OnSocketExceptionWhileConnected() {
|
||||
|
||||
tSource.Cancel();
|
||||
|
||||
bytesPerSecondDownstream = 0;
|
||||
bytesPerSecondUpstream = 0;
|
||||
|
||||
isMessageLocked = false;
|
||||
IsConnected = false;
|
||||
|
||||
Logger.Log("The PLC connection was closed", LogLevel.Error, this);
|
||||
OnDisconnect();
|
||||
|
||||
}
|
||||
if (reconnectTask == null) StartReconnectTask();
|
||||
|
||||
}
|
||||
|
||||
@@ -785,16 +819,6 @@ namespace MewtocolNet {
|
||||
|
||||
}
|
||||
|
||||
private protected void OnSocketExceptionWhileConnected () {
|
||||
|
||||
tSource.Cancel();
|
||||
isMessageLocked = false;
|
||||
IsConnected = false;
|
||||
|
||||
if (reconnectTask == null) StartReconnectTask();
|
||||
|
||||
}
|
||||
|
||||
private protected virtual void OnConnected(PLCInfo plcinf) {
|
||||
|
||||
Logger.Log("Connected to PLC", LogLevel.Info, this);
|
||||
@@ -807,18 +831,23 @@ namespace MewtocolNet {
|
||||
//notify the registers
|
||||
GetAllRegisters().Cast<Register>().ToList().ForEach(x => x.OnPlcConnected());
|
||||
|
||||
reconnectTask = null;
|
||||
IsConnected = true;
|
||||
|
||||
Connected?.Invoke(this, new PlcConnectionArgs());
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -829,8 +858,8 @@ namespace MewtocolNet {
|
||||
|
||||
IsReceiving = false;
|
||||
IsSending = false;
|
||||
BytesPerSecondDownstream = 0;
|
||||
BytesPerSecondUpstream = 0;
|
||||
bytesPerSecondDownstream = 0;
|
||||
bytesPerSecondUpstream = 0;
|
||||
PollerCycleDurationMs = 0;
|
||||
|
||||
isMessageLocked = false;
|
||||
@@ -856,8 +885,8 @@ namespace MewtocolNet {
|
||||
|
||||
IsReceiving = false;
|
||||
IsSending = false;
|
||||
BytesPerSecondDownstream = 0;
|
||||
BytesPerSecondUpstream = 0;
|
||||
bytesPerSecondDownstream = 0;
|
||||
bytesPerSecondUpstream = 0;
|
||||
PollerCycleDurationMs = 0;
|
||||
PlcInfo = null;
|
||||
|
||||
@@ -887,6 +916,9 @@ namespace MewtocolNet {
|
||||
GetAllRegisters().Cast<Register>()
|
||||
.ToList().ForEach(x => x.OnInterfaceCyclicTimerUpdate((int)cyclicGenericUpdateCounter.Interval));
|
||||
|
||||
OnPropChange(nameof(BytesPerSecondUpstream));
|
||||
OnPropChange(nameof(BytesPerSecondDownstream));
|
||||
|
||||
}
|
||||
|
||||
private void SetUpstreamStopWatchStart() {
|
||||
@@ -921,7 +953,7 @@ namespace MewtocolNet {
|
||||
|
||||
var perSecUpstream = (double)((bytesTotalCountedUpstream / speedStopwatchUpstr.Elapsed.TotalMilliseconds) * 1000);
|
||||
if (perSecUpstream <= 10000)
|
||||
BytesPerSecondUpstream = (int)Math.Round(perSecUpstream, MidpointRounding.AwayFromZero);
|
||||
bytesPerSecondUpstream = (int)Math.Round(perSecUpstream, MidpointRounding.AwayFromZero);
|
||||
|
||||
}
|
||||
|
||||
@@ -932,7 +964,7 @@ namespace MewtocolNet {
|
||||
var perSecDownstream = (double)((bytesTotalCountedDownstream / speedStopwatchDownstr.Elapsed.TotalMilliseconds) * 1000);
|
||||
|
||||
if (perSecDownstream <= 10000)
|
||||
BytesPerSecondDownstream = (int)Math.Round(perSecDownstream, MidpointRounding.AwayFromZero);
|
||||
bytesPerSecondDownstream = (int)Math.Round(perSecDownstream, MidpointRounding.AwayFromZero);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace MewtocolNet {
|
||||
/// </summary>
|
||||
public abstract partial class MewtocolInterface {
|
||||
|
||||
private bool heartbeatNeedsRun = false;
|
||||
|
||||
internal Task heartbeatTask = Task.CompletedTask;
|
||||
|
||||
internal Func<Task> heartbeatCallbackTask;
|
||||
@@ -127,9 +129,13 @@ namespace MewtocolNet {
|
||||
|
||||
heartbeatNeedsRun = true;
|
||||
|
||||
if(!PollerActive) {
|
||||
|
||||
Task.Run(HeartbeatTickTask);
|
||||
|
||||
}
|
||||
|
||||
private bool heartbeatNeedsRun = false;
|
||||
}
|
||||
|
||||
private async Task HeartbeatTickTask () {
|
||||
|
||||
@@ -141,8 +147,7 @@ namespace MewtocolNet {
|
||||
|
||||
Logger.LogError("Heartbeat timed out", this);
|
||||
|
||||
//OnSocketExceptionWhileConnected();
|
||||
//StartReconnectTask();
|
||||
OnSocketExceptionWhileConnected();
|
||||
|
||||
return;
|
||||
|
||||
@@ -224,15 +229,7 @@ namespace MewtocolNet {
|
||||
|
||||
await memoryManager.PollAllAreasAsync(async () => {
|
||||
|
||||
if (userInputSendTasks != null && userInputSendTasks.Count > 0) {
|
||||
|
||||
var t = userInputSendTasks.Dequeue();
|
||||
|
||||
t.Start();
|
||||
|
||||
await t;
|
||||
|
||||
}
|
||||
await RunOneOpenQueuedTask();
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -138,7 +138,12 @@ namespace MewtocolNet {
|
||||
var result = await SendCommandInternalAsync(requeststring);
|
||||
|
||||
if (result.Success) {
|
||||
|
||||
Logger.Log($"Operation mode was changed to {(setRun ? "Run" : "Prog")}", LogLevel.Info, this);
|
||||
|
||||
//directily update the op mode
|
||||
PlcInfo.OperationMode = PlcInfo.OperationMode.SetFlag(OPMode.RunMode, setRun);
|
||||
|
||||
} else {
|
||||
Logger.Log("Operation mode change failed", LogLevel.Error, this);
|
||||
}
|
||||
|
||||
@@ -211,30 +211,12 @@ namespace MewtocolNet {
|
||||
regularSendTask = null;
|
||||
reconnectTask = Task.CompletedTask;
|
||||
|
||||
//try to abort any non read message
|
||||
//await SendNoResponseCommandAsync($"%{GetStationNumber()}#AB");
|
||||
|
||||
//get plc info 2 times to clear old stuff from the buffer
|
||||
if (await SendCommandInternalAsync($"%{GetStationNumber()}#RT") != null) {
|
||||
|
||||
Logger.Log("Reconnect successfull");
|
||||
OnReconnected();
|
||||
|
||||
//var plcinf = await SendCommandAsync($"%{GetStationNumber()}#RT");
|
||||
|
||||
//if (plcinf != null) {
|
||||
|
||||
// Logger.Log("Reconnect successfull");
|
||||
|
||||
// OnReconnected();
|
||||
|
||||
// //await base.ConnectAsync();
|
||||
// //OnConnected(plcinf);
|
||||
|
||||
//} else {
|
||||
|
||||
// Logger.Log("Initial connection failed", LogLevel.Error, this);
|
||||
// OnDisconnect();
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -32,6 +32,20 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
||||
|
||||
internal Register Assemble(StepBase stp) => assembler.Assemble(stp.Data);
|
||||
|
||||
//bool constructor
|
||||
|
||||
public StructStp<bool> Bool(string fpAddr, string name = null) {
|
||||
|
||||
var data = AddressTools.ParseAddress(fpAddr, name);
|
||||
|
||||
data.dotnetVarType = typeof(bool);
|
||||
|
||||
return new StructStp<bool>(data) {
|
||||
builder = this,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//struct constructor
|
||||
|
||||
public StructStp<T> Struct<T>(string fpAddr, string name = null) where T : struct {
|
||||
|
||||
@@ -29,6 +29,11 @@ namespace MewtocolNet.Registers {
|
||||
/// </summary>
|
||||
int PollLevel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Info string about the memory area
|
||||
/// </summary>
|
||||
string MemoryAreaInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The update frequency of the register in Hz
|
||||
/// </summary>
|
||||
@@ -59,6 +64,8 @@ namespace MewtocolNet.Registers {
|
||||
/// </summary>
|
||||
uint MemoryAddress { get; }
|
||||
|
||||
string MemoryAreaHash { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the register as the plc representation string
|
||||
/// </summary>
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace MewtocolNet.Registers {
|
||||
internal List<RegisterPropTarget> boundProperties = new List<RegisterPropTarget>();
|
||||
|
||||
internal Type underlyingSystemType;
|
||||
internal IMemoryArea underlyingMemory;
|
||||
internal AreaBase underlyingMemory;
|
||||
internal bool autoGenerated;
|
||||
internal bool isAnonymous;
|
||||
|
||||
@@ -94,6 +94,9 @@ namespace MewtocolNet.Registers {
|
||||
}
|
||||
}
|
||||
|
||||
public string MemoryAreaInfo => underlyingMemory.GetName();
|
||||
|
||||
public string MemoryAreaHash => underlyingMemory.GetHashCode().ToString();
|
||||
|
||||
internal Register() { }
|
||||
|
||||
|
||||
@@ -74,6 +74,17 @@ namespace MewtocolNet.Registers {
|
||||
/// <inheritdoc/>
|
||||
public override uint GetRegisterAddressLen() => 1;
|
||||
|
||||
internal override object SetValueFromBytes(byte[] bytes) {
|
||||
|
||||
AddSuccessRead();
|
||||
|
||||
var parsed = PlcValueParser.Parse<bool>(this, bytes);
|
||||
|
||||
UpdateHoldingValue(parsed);
|
||||
return parsed;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -123,9 +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 &&
|
||||
(attachedInterface.pollerTaskStopped || attachedInterface.pollerFirstCycleCompleted))
|
||||
if (reservedStringLength != reservedSize && attachedInterface.PlcInfo.IsRunMode)
|
||||
throw new NotSupportedException(
|
||||
$"The STRING register at {GetMewName()} is not correctly sized, " +
|
||||
$"the size should be STRING[{reservedSize}] instead of STRING[{reservedStringLength}]"
|
||||
|
||||
@@ -5,7 +5,8 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet.UnderlyingRegisters {
|
||||
public class DTArea : IMemoryArea {
|
||||
|
||||
public abstract class AreaBase {
|
||||
|
||||
private MewtocolInterface mewInterface;
|
||||
|
||||
@@ -23,7 +24,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
public ulong AddressStart => addressStart;
|
||||
public ulong AddressEnd => addressEnd;
|
||||
|
||||
internal DTArea(MewtocolInterface mewIf) {
|
||||
internal AreaBase(MewtocolInterface mewIf) {
|
||||
|
||||
mewInterface = mewIf;
|
||||
|
||||
@@ -116,26 +117,12 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
}
|
||||
|
||||
private string GetMewtocolIdent() {
|
||||
|
||||
StringBuilder asciistring = new StringBuilder("D");
|
||||
asciistring.Append(AddressStart.ToString().PadLeft(5, '0'));
|
||||
asciistring.Append(AddressEnd.ToString().PadLeft(5, '0'));
|
||||
return asciistring.ToString();
|
||||
|
||||
}
|
||||
|
||||
private string GetMewtocolIdent(ulong addStart, ulong addEnd) {
|
||||
|
||||
StringBuilder asciistring = new StringBuilder("D");
|
||||
asciistring.Append(addStart.ToString().PadLeft(5, '0'));
|
||||
asciistring.Append(addEnd.ToString().PadLeft(5, '0'));
|
||||
return asciistring.ToString();
|
||||
|
||||
}
|
||||
|
||||
public override string ToString() => $"DT{AddressStart}-{AddressEnd}";
|
||||
|
||||
public virtual string GetName() => $"{ToString()} ({managedRegisters.Count} Registers)";
|
||||
|
||||
public string GetHash() => GetHashCode().ToString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
19
MewtocolNet/UnderlyingRegisters/Areas/DTArea.cs
Normal file
19
MewtocolNet/UnderlyingRegisters/Areas/DTArea.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
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)";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,8 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
internal interface IMemoryArea {
|
||||
|
||||
string GetName();
|
||||
|
||||
byte[] GetUnderlyingBytes(Register reg);
|
||||
|
||||
void SetUnderlyingBytes(Register reg, byte[] bytes);
|
||||
19
MewtocolNet/UnderlyingRegisters/Areas/WRArea.cs
Normal file
19
MewtocolNet/UnderlyingRegisters/Areas/WRArea.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
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)";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -92,17 +92,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
TestPollLevelExistence(reg);
|
||||
|
||||
switch (reg.RegisterType) {
|
||||
case RegisterPrefix.X:
|
||||
case RegisterPrefix.Y:
|
||||
case RegisterPrefix.R:
|
||||
AddToWRArea(reg);
|
||||
break;
|
||||
case RegisterPrefix.DT:
|
||||
case RegisterPrefix.DDT:
|
||||
AddToDTArea(reg);
|
||||
break;
|
||||
}
|
||||
AddToArea(reg, reg.RegisterType);
|
||||
|
||||
}
|
||||
|
||||
@@ -155,73 +145,33 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
}
|
||||
|
||||
private bool AddToWRArea(Register insertReg) {
|
||||
|
||||
var pollLevelFound = pollLevels.FirstOrDefault(x => x.level == insertReg.pollLevel);
|
||||
|
||||
List<WRArea> collection = null;
|
||||
|
||||
switch (insertReg.RegisterType) {
|
||||
case RegisterPrefix.X:
|
||||
collection = pollLevelFound.externalRelayInAreas;
|
||||
break;
|
||||
case RegisterPrefix.Y:
|
||||
collection = pollLevelFound.externalRelayOutAreas;
|
||||
break;
|
||||
case RegisterPrefix.R:
|
||||
collection = pollLevelFound.internalRelayAreas;
|
||||
break;
|
||||
}
|
||||
|
||||
WRArea area = collection.FirstOrDefault(x => x.AddressStart == insertReg.MemoryAddress);
|
||||
|
||||
if (area != null) {
|
||||
|
||||
var existingLinkedRegister = area.linkedRegisters
|
||||
.FirstOrDefault(x => x.CompareIsDuplicate(insertReg));
|
||||
|
||||
if (existingLinkedRegister != null) {
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
insertReg.underlyingMemory = area;
|
||||
area.linkedRegisters.Add(insertReg);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
area = new WRArea(mewInterface) {
|
||||
registerType = insertReg.RegisterType,
|
||||
addressStart = insertReg.MemoryAddress,
|
||||
};
|
||||
|
||||
insertReg.underlyingMemory = area;
|
||||
area.linkedRegisters.Add(insertReg);
|
||||
|
||||
collection.Add(area);
|
||||
collection = collection.OrderBy(x => x.AddressStart).ToList();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void AddToDTArea(Register insertReg) {
|
||||
private void AddToArea(Register insertReg, RegisterPrefix prefix) {
|
||||
|
||||
uint regInsAddStart = insertReg.MemoryAddress;
|
||||
uint regInsAddEnd = insertReg.MemoryAddress + insertReg.GetRegisterAddressLen() - 1;
|
||||
|
||||
DTArea targetArea = null;
|
||||
AreaBase targetArea = null;
|
||||
|
||||
var pollLevelFound = pollLevels.FirstOrDefault(x => x.level == insertReg.pollLevel);
|
||||
var dataAreas = pollLevelFound.dataAreas;
|
||||
List<AreaBase> pollLevelAreas = null;
|
||||
|
||||
foreach (var dtArea in dataAreas) {
|
||||
switch (prefix) {
|
||||
case RegisterPrefix.X:
|
||||
pollLevelAreas = pollLevelFound.externalRelayInAreas;
|
||||
break;
|
||||
case RegisterPrefix.Y:
|
||||
pollLevelAreas = pollLevelFound.externalRelayOutAreas;
|
||||
break;
|
||||
case RegisterPrefix.R:
|
||||
pollLevelAreas = pollLevelFound.internalRelayAreas;
|
||||
break;
|
||||
case RegisterPrefix.DT:
|
||||
case RegisterPrefix.DDT:
|
||||
pollLevelAreas = pollLevelFound.dataAreas;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var dtArea in pollLevelAreas) {
|
||||
|
||||
bool addressInsideArea = regInsAddStart >= dtArea.AddressStart &&
|
||||
regInsAddEnd <= dtArea.AddressEnd;
|
||||
@@ -278,7 +228,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
};
|
||||
|
||||
targetArea.BoundaryUdpdate();
|
||||
dataAreas.Add(targetArea);
|
||||
pollLevelAreas.Add(targetArea);
|
||||
|
||||
}
|
||||
|
||||
@@ -358,7 +308,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
}
|
||||
|
||||
//update registers in poll level
|
||||
foreach (var dtArea in pollLevel.dataAreas.ToArray()) {
|
||||
foreach (var dtArea in pollLevel.GetAllAreas().ToArray()) {
|
||||
|
||||
//set the whole memory area at once
|
||||
await dtArea.RequestByteReadAsync(dtArea.AddressStart, dtArea.AddressEnd);
|
||||
@@ -493,10 +443,9 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
foreach (var lvl in pollLevels) {
|
||||
|
||||
registers.AddRange(lvl.dataAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
|
||||
|
||||
registers.AddRange(lvl.internalRelayAreas.SelectMany(x => x.linkedRegisters));
|
||||
registers.AddRange(lvl.externalRelayInAreas.SelectMany(x => x.linkedRegisters));
|
||||
registers.AddRange(lvl.externalRelayOutAreas.SelectMany(x => x.linkedRegisters));
|
||||
registers.AddRange(lvl.internalRelayAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
|
||||
registers.AddRange(lvl.externalRelayInAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
|
||||
registers.AddRange(lvl.externalRelayOutAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,30 +6,43 @@ namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
internal int lastReadTimeMs = 0;
|
||||
|
||||
internal PollLevel(int wrSize, int dtSize) {
|
||||
|
||||
externalRelayInAreas = new List<WRArea>(wrSize * 16);
|
||||
externalRelayOutAreas = new List<WRArea>(wrSize * 16);
|
||||
internalRelayAreas = new List<WRArea>(wrSize * 16);
|
||||
dataAreas = new List<DTArea>(dtSize);
|
||||
|
||||
}
|
||||
|
||||
internal int level;
|
||||
|
||||
// WR areas are n of words, each word has 2 bytes representing the "special address component"
|
||||
|
||||
//X WR
|
||||
internal List<WRArea> externalRelayInAreas;
|
||||
internal List<AreaBase> externalRelayInAreas;
|
||||
|
||||
//Y WR
|
||||
internal List<WRArea> externalRelayOutAreas;
|
||||
internal List<AreaBase> externalRelayOutAreas;
|
||||
|
||||
//R WR
|
||||
internal List<WRArea> internalRelayAreas;
|
||||
internal List<AreaBase> internalRelayAreas;
|
||||
|
||||
//DT
|
||||
internal List<DTArea> dataAreas;
|
||||
internal List<AreaBase> dataAreas;
|
||||
|
||||
internal PollLevel(int wrSize, int dtSize) {
|
||||
|
||||
externalRelayInAreas = new List<AreaBase>(wrSize * 16);
|
||||
externalRelayOutAreas = new List<AreaBase>(wrSize * 16);
|
||||
internalRelayAreas = new List<AreaBase>(wrSize * 16);
|
||||
dataAreas = new List<AreaBase>(dtSize);
|
||||
|
||||
}
|
||||
|
||||
internal IEnumerable<AreaBase> GetAllAreas () {
|
||||
|
||||
List<AreaBase> combined = new List<AreaBase>();
|
||||
|
||||
combined.AddRange(internalRelayAreas);
|
||||
combined.AddRange(externalRelayInAreas);
|
||||
combined.AddRange(externalRelayOutAreas);
|
||||
combined.AddRange(dataAreas);
|
||||
|
||||
return combined;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
using MewtocolNet.Registers;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MewtocolNet.UnderlyingRegisters {
|
||||
|
||||
public class WRArea : IMemoryArea {
|
||||
|
||||
private MewtocolInterface mewInterface;
|
||||
|
||||
internal RegisterPrefix registerType;
|
||||
internal ulong addressStart;
|
||||
|
||||
internal byte[] wordData = new byte[2];
|
||||
|
||||
internal List<Register> linkedRegisters = new List<Register>();
|
||||
|
||||
public ulong AddressStart => addressStart;
|
||||
|
||||
internal WRArea(MewtocolInterface mewIf) {
|
||||
|
||||
mewInterface = mewIf;
|
||||
|
||||
}
|
||||
|
||||
public void UpdateAreaRegisterValues() {
|
||||
|
||||
|
||||
|
||||
}
|
||||
public void SetUnderlyingBytes(Register reg, byte[] bytes) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public byte[] GetUnderlyingBytes(Register reg) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> ReadRegisterAsync(Register reg) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> WriteRegisterAsync(Register reg, byte[] bytes) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public string GetMewtocolIdent() => GetMewtocolIdentsAllBits();
|
||||
|
||||
public string GetMewtocolIdentsAllBits() {
|
||||
|
||||
StringBuilder asciistring = new StringBuilder();
|
||||
|
||||
for (byte i = 0; i < 16; i++) {
|
||||
|
||||
asciistring.Append(GetMewtocolIdentSingleBit(i));
|
||||
|
||||
}
|
||||
|
||||
return asciistring.ToString();
|
||||
|
||||
}
|
||||
|
||||
public string GetMewtocolIdentSingleBit(byte specialAddress) {
|
||||
|
||||
//(R|X|Y)(area add [3] + special add [1])
|
||||
StringBuilder asciistring = new StringBuilder();
|
||||
|
||||
string prefix = registerType.ToString();
|
||||
string mem = AddressStart.ToString();
|
||||
string sp = specialAddress.ToString("X1");
|
||||
|
||||
asciistring.Append(prefix);
|
||||
asciistring.Append(mem.PadLeft(3, '0'));
|
||||
asciistring.Append(sp);
|
||||
|
||||
return asciistring.ToString();
|
||||
|
||||
}
|
||||
|
||||
public override string ToString() => $"{registerType}{AddressStart} 0-F";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user