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">
|
StartupUri="MainWindow.xaml">
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<conv:NegationConverter x:Key="bInv"/>
|
<conv:NegationConverter x:Key="bInv"/>
|
||||||
|
<conv:ColorHashConverter x:Key="hashColor"/>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</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;
|
||||||
|
using MewtocolNet.Events;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -9,6 +10,24 @@ namespace Examples.WPF.ViewModels;
|
|||||||
|
|
||||||
public class PlcDataViewViewModel : ViewModelBase {
|
public class PlcDataViewViewModel : ViewModelBase {
|
||||||
|
|
||||||
|
private ReconnectArgs plcCurrentReconnectArgs = null!;
|
||||||
|
|
||||||
public IPlc Plc => App.ViewModel.Plc!;
|
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)
|
App.ViewModel.Plc = Mewtocol.Ethernet(viewModel.SelectedIP, parsedInt)
|
||||||
.WithPoller()
|
.WithPoller()
|
||||||
.WithInterfaceSettings(setting => {
|
.WithInterfaceSettings(setting => {
|
||||||
setting.TryReconnectAttempts = 30;
|
setting.TryReconnectAttempts = 0;
|
||||||
setting.TryReconnectDelayMs = 2000;
|
setting.TryReconnectDelayMs = 2000;
|
||||||
|
setting.SendReceiveTimeoutMs = 1000;
|
||||||
setting.HeartbeatIntervalMs = 3000;
|
setting.HeartbeatIntervalMs = 3000;
|
||||||
|
setting.MaxDataBlocksPerWrite = 12;
|
||||||
|
setting.MaxOptimizationDistance = 10;
|
||||||
})
|
})
|
||||||
.WithCustomPollLevels(lvl => {
|
.WithCustomPollLevels(lvl => {
|
||||||
lvl.SetLevel(2, 3);
|
lvl.SetLevel(2, 3);
|
||||||
@@ -73,6 +76,8 @@ public partial class ConnectView : UserControl {
|
|||||||
//b.Struct<short>("DT0").Build();
|
//b.Struct<short>("DT0").Build();
|
||||||
//b.Struct<short>("DT0").AsArray(30).Build();
|
//b.Struct<short>("DT0").AsArray(30).Build();
|
||||||
|
|
||||||
|
b.Bool("R10A").Build();
|
||||||
|
|
||||||
b.Struct<short>("DT1000").Build(out heartbeatSetter);
|
b.Struct<short>("DT1000").Build(out heartbeatSetter);
|
||||||
b.Struct<Word>("DT1000").Build();
|
b.Struct<Word>("DT1000").Build();
|
||||||
|
|
||||||
|
|||||||
@@ -33,16 +33,56 @@
|
|||||||
<StackPanel Margin="10"
|
<StackPanel Margin="10"
|
||||||
Grid.Row="1">
|
Grid.Row="1">
|
||||||
|
|
||||||
<TextBlock>
|
<TextBlock IsEnabled="{Binding Plc.IsConnected}">
|
||||||
|
|
||||||
<Run Text="{Binding Plc.PlcInfo.TypeName, Mode=OneWay}"
|
<Run Text="{Binding Plc.PlcInfo.TypeName, Mode=OneWay}"
|
||||||
FontSize="24"
|
FontSize="24"
|
||||||
|
BaselineAlignment="Center"
|
||||||
FontWeight="SemiBold"/>
|
FontWeight="SemiBold"/>
|
||||||
|
|
||||||
<Run Text="{Binding Plc.PlcInfo.CpuVersion, StringFormat='v{0}', Mode=OneWay}"
|
<Run Text="{Binding Plc.PlcInfo.CpuVersion, StringFormat='v{0}', Mode=OneWay}"
|
||||||
FontSize="24"
|
FontSize="24"
|
||||||
FontWeight="Light"/>
|
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>
|
||||||
|
|
||||||
<TextBlock Text="{Binding Plc.PlcInfo.TypeCode, StringFormat='#{0:X}', Mode=OneWay}"
|
<TextBlock Text="{Binding Plc.PlcInfo.TypeCode, StringFormat='#{0:X}', Mode=OneWay}"
|
||||||
@@ -84,6 +124,25 @@
|
|||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</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>
|
</StackPanel>
|
||||||
|
|
||||||
<DataGrid Grid.Row="2"
|
<DataGrid Grid.Row="2"
|
||||||
@@ -96,6 +155,13 @@
|
|||||||
<DataGridTextColumn Header="Value" Binding="{Binding ValueStr}"/>
|
<DataGridTextColumn Header="Value" Binding="{Binding ValueStr}"/>
|
||||||
<DataGridTextColumn Header="Poll Level" Binding="{Binding PollLevel, Mode=OneWay}"/>
|
<DataGridTextColumn Header="Poll Level" Binding="{Binding PollLevel, Mode=OneWay}"/>
|
||||||
<DataGridTextColumn Header="Update Frequency" Binding="{Binding UpdateFreqHz, StringFormat='{}{0} Hz',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.Columns>
|
||||||
</DataGrid>
|
</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
|
#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) {
|
public static int DetermineTypeByteIntialSize(this Type type) {
|
||||||
|
|
||||||
//enums can only be of numeric types
|
//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.RegisterBuilding;
|
||||||
using MewtocolNet.Registers;
|
using MewtocolNet.Registers;
|
||||||
using System;
|
using System;
|
||||||
@@ -12,6 +13,31 @@ namespace MewtocolNet {
|
|||||||
/// Provides a interface for Panasonic PLCs
|
/// Provides a interface for Panasonic PLCs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IPlc : IDisposable, INotifyPropertyChanged {
|
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>
|
/// <summary>
|
||||||
/// The current connection state of the interface
|
/// The current connection state of the interface
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ namespace MewtocolNet {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event PlcConnectionEventHandler Reconnected;
|
public event PlcConnectionEventHandler Reconnected;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public event PlcReconnectEventHandler ReconnectTryStarted;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event PlcConnectionEventHandler Disconnected;
|
public event PlcConnectionEventHandler Disconnected;
|
||||||
|
|
||||||
@@ -139,13 +142,7 @@ namespace MewtocolNet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public int BytesPerSecondUpstream {
|
public int BytesPerSecondUpstream => bytesPerSecondUpstream;
|
||||||
get { return bytesPerSecondUpstream; }
|
|
||||||
private protected set {
|
|
||||||
bytesPerSecondUpstream = value;
|
|
||||||
OnPropChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool IsReceiving {
|
public bool IsReceiving {
|
||||||
@@ -157,13 +154,7 @@ namespace MewtocolNet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public int BytesPerSecondDownstream {
|
public int BytesPerSecondDownstream => bytesPerSecondDownstream;
|
||||||
get { return bytesPerSecondDownstream; }
|
|
||||||
private protected set {
|
|
||||||
bytesPerSecondDownstream = value;
|
|
||||||
OnPropChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public MewtocolVersion MewtocolVersion {
|
public MewtocolVersion MewtocolVersion {
|
||||||
@@ -302,7 +293,15 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
if (pollCycleTask != null && !pollCycleTask.IsCompleted) pollCycleTask.Wait();
|
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
|
//stop the heartbeat timer for the time of retries
|
||||||
StopHeartBeat();
|
StopHeartBeat();
|
||||||
|
|
||||||
|
var eArgs = new ReconnectArgs(retryCount, tryReconnectAttempts, TimeSpan.FromMilliseconds(tryReconnectDelayMs));
|
||||||
|
ReconnectTryStarted?.Invoke(this, eArgs);
|
||||||
|
|
||||||
|
Reconnected += (s, e) => eArgs.ConnectionSuccess();
|
||||||
|
|
||||||
await ReconnectAsync(tryReconnectDelayMs);
|
await ReconnectAsync(tryReconnectDelayMs);
|
||||||
await Task.Delay(2000);
|
|
||||||
|
if (IsConnected) return;
|
||||||
|
|
||||||
|
await Task.Delay(tryReconnectDelayMs);
|
||||||
|
|
||||||
retryCount++;
|
retryCount++;
|
||||||
|
|
||||||
@@ -378,17 +385,7 @@ namespace MewtocolNet {
|
|||||||
if (regularSendTask != null && !regularSendTask.IsCompleted) {
|
if (regularSendTask != null && !regularSendTask.IsCompleted) {
|
||||||
|
|
||||||
//queue self
|
//queue self
|
||||||
|
return await EnqueueMessage(_msg, onReceiveProgress);
|
||||||
var t = new Task<MewtocolFrameResponse>(() => SendCommandInternalAsync(_msg, onReceiveProgress).Result);
|
|
||||||
userInputSendTasks.Enqueue(t);
|
|
||||||
|
|
||||||
OnPropChange(nameof(QueuedMessages));
|
|
||||||
|
|
||||||
await t;
|
|
||||||
|
|
||||||
OnPropChange(nameof(QueuedMessages));
|
|
||||||
|
|
||||||
return t.Result;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,17 +396,12 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
isMessageLocked = true;
|
isMessageLocked = true;
|
||||||
|
|
||||||
//wait for the last send task to complete
|
|
||||||
//if (regularSendTask != null && !regularSendTask.IsCompleted) await regularSendTask;
|
|
||||||
|
|
||||||
//send request
|
//send request
|
||||||
regularSendTask = SendTwoDirectionalFrameAsync(_msg, onReceiveProgress);
|
regularSendTask = SendTwoDirectionalFrameAsync(_msg, onReceiveProgress);
|
||||||
|
|
||||||
//if (regularSendTask == null) return MewtocolFrameResponse.Canceled;
|
|
||||||
|
|
||||||
try {
|
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) {
|
if (timeoutAwaiter != regularSendTask) {
|
||||||
|
|
||||||
@@ -444,10 +436,52 @@ namespace MewtocolNet {
|
|||||||
isMessageLocked = false;
|
isMessageLocked = false;
|
||||||
regularSendTask = null;
|
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;
|
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) {
|
private protected async Task<MewtocolFrameResponse> SendOneDirectionalFrameAsync (string frame) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -763,17 +797,17 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private protected void OnMajorSocketExceptionWhileConnected() {
|
private protected void OnSocketExceptionWhileConnected() {
|
||||||
|
|
||||||
if (IsConnected) {
|
tSource.Cancel();
|
||||||
|
|
||||||
tSource.Cancel();
|
bytesPerSecondDownstream = 0;
|
||||||
isMessageLocked = false;
|
bytesPerSecondUpstream = 0;
|
||||||
|
|
||||||
Logger.Log("The PLC connection was closed", LogLevel.Error, this);
|
isMessageLocked = false;
|
||||||
OnDisconnect();
|
IsConnected = false;
|
||||||
|
|
||||||
}
|
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) {
|
private protected virtual void OnConnected(PLCInfo plcinf) {
|
||||||
|
|
||||||
Logger.Log("Connected to PLC", LogLevel.Info, this);
|
Logger.Log("Connected to PLC", LogLevel.Info, this);
|
||||||
@@ -807,18 +831,23 @@ namespace MewtocolNet {
|
|||||||
//notify the registers
|
//notify the registers
|
||||||
GetAllRegisters().Cast<Register>().ToList().ForEach(x => x.OnPlcConnected());
|
GetAllRegisters().Cast<Register>().ToList().ForEach(x => x.OnPlcConnected());
|
||||||
|
|
||||||
|
reconnectTask = null;
|
||||||
IsConnected = true;
|
IsConnected = true;
|
||||||
|
isReconnectingStage = false;
|
||||||
Connected?.Invoke(this, new PlcConnectionArgs());
|
isConnectingStage = false;
|
||||||
|
|
||||||
if (!usePoller) {
|
if (!usePoller) {
|
||||||
firstPollTask.RunSynchronously();
|
firstPollTask.RunSynchronously();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connected?.Invoke(this, new PlcConnectionArgs());
|
||||||
|
|
||||||
PolledCycle += OnPollCycleDone;
|
PolledCycle += OnPollCycleDone;
|
||||||
void OnPollCycleDone() {
|
void OnPollCycleDone() {
|
||||||
|
|
||||||
firstPollTask.RunSynchronously();
|
if(firstPollTask != null && !firstPollTask.IsCompleted)
|
||||||
|
firstPollTask.RunSynchronously();
|
||||||
|
|
||||||
PolledCycle -= OnPollCycleDone;
|
PolledCycle -= OnPollCycleDone;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -829,8 +858,8 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
IsReceiving = false;
|
IsReceiving = false;
|
||||||
IsSending = false;
|
IsSending = false;
|
||||||
BytesPerSecondDownstream = 0;
|
bytesPerSecondDownstream = 0;
|
||||||
BytesPerSecondUpstream = 0;
|
bytesPerSecondUpstream = 0;
|
||||||
PollerCycleDurationMs = 0;
|
PollerCycleDurationMs = 0;
|
||||||
|
|
||||||
isMessageLocked = false;
|
isMessageLocked = false;
|
||||||
@@ -856,8 +885,8 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
IsReceiving = false;
|
IsReceiving = false;
|
||||||
IsSending = false;
|
IsSending = false;
|
||||||
BytesPerSecondDownstream = 0;
|
bytesPerSecondDownstream = 0;
|
||||||
BytesPerSecondUpstream = 0;
|
bytesPerSecondUpstream = 0;
|
||||||
PollerCycleDurationMs = 0;
|
PollerCycleDurationMs = 0;
|
||||||
PlcInfo = null;
|
PlcInfo = null;
|
||||||
|
|
||||||
@@ -887,6 +916,9 @@ namespace MewtocolNet {
|
|||||||
GetAllRegisters().Cast<Register>()
|
GetAllRegisters().Cast<Register>()
|
||||||
.ToList().ForEach(x => x.OnInterfaceCyclicTimerUpdate((int)cyclicGenericUpdateCounter.Interval));
|
.ToList().ForEach(x => x.OnInterfaceCyclicTimerUpdate((int)cyclicGenericUpdateCounter.Interval));
|
||||||
|
|
||||||
|
OnPropChange(nameof(BytesPerSecondUpstream));
|
||||||
|
OnPropChange(nameof(BytesPerSecondDownstream));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetUpstreamStopWatchStart() {
|
private void SetUpstreamStopWatchStart() {
|
||||||
@@ -921,7 +953,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
var perSecUpstream = (double)((bytesTotalCountedUpstream / speedStopwatchUpstr.Elapsed.TotalMilliseconds) * 1000);
|
var perSecUpstream = (double)((bytesTotalCountedUpstream / speedStopwatchUpstr.Elapsed.TotalMilliseconds) * 1000);
|
||||||
if (perSecUpstream <= 10000)
|
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);
|
var perSecDownstream = (double)((bytesTotalCountedDownstream / speedStopwatchDownstr.Elapsed.TotalMilliseconds) * 1000);
|
||||||
|
|
||||||
if (perSecDownstream <= 10000)
|
if (perSecDownstream <= 10000)
|
||||||
BytesPerSecondDownstream = (int)Math.Round(perSecDownstream, MidpointRounding.AwayFromZero);
|
bytesPerSecondDownstream = (int)Math.Round(perSecDownstream, MidpointRounding.AwayFromZero);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ namespace MewtocolNet {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract partial class MewtocolInterface {
|
public abstract partial class MewtocolInterface {
|
||||||
|
|
||||||
|
private bool heartbeatNeedsRun = false;
|
||||||
|
|
||||||
internal Task heartbeatTask = Task.CompletedTask;
|
internal Task heartbeatTask = Task.CompletedTask;
|
||||||
|
|
||||||
internal Func<Task> heartbeatCallbackTask;
|
internal Func<Task> heartbeatCallbackTask;
|
||||||
@@ -127,9 +129,13 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
heartbeatNeedsRun = true;
|
heartbeatNeedsRun = true;
|
||||||
|
|
||||||
}
|
if(!PollerActive) {
|
||||||
|
|
||||||
private bool heartbeatNeedsRun = false;
|
Task.Run(HeartbeatTickTask);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private async Task HeartbeatTickTask () {
|
private async Task HeartbeatTickTask () {
|
||||||
|
|
||||||
@@ -141,8 +147,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
Logger.LogError("Heartbeat timed out", this);
|
Logger.LogError("Heartbeat timed out", this);
|
||||||
|
|
||||||
//OnSocketExceptionWhileConnected();
|
OnSocketExceptionWhileConnected();
|
||||||
//StartReconnectTask();
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -224,15 +229,7 @@ namespace MewtocolNet {
|
|||||||
|
|
||||||
await memoryManager.PollAllAreasAsync(async () => {
|
await memoryManager.PollAllAreasAsync(async () => {
|
||||||
|
|
||||||
if (userInputSendTasks != null && userInputSendTasks.Count > 0) {
|
await RunOneOpenQueuedTask();
|
||||||
|
|
||||||
var t = userInputSendTasks.Dequeue();
|
|
||||||
|
|
||||||
t.Start();
|
|
||||||
|
|
||||||
await t;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -138,7 +138,12 @@ namespace MewtocolNet {
|
|||||||
var result = await SendCommandInternalAsync(requeststring);
|
var result = await SendCommandInternalAsync(requeststring);
|
||||||
|
|
||||||
if (result.Success) {
|
if (result.Success) {
|
||||||
|
|
||||||
Logger.Log($"Operation mode was changed to {(setRun ? "Run" : "Prog")}", LogLevel.Info, this);
|
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 {
|
} else {
|
||||||
Logger.Log("Operation mode change failed", LogLevel.Error, this);
|
Logger.Log("Operation mode change failed", LogLevel.Error, this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,30 +211,12 @@ namespace MewtocolNet {
|
|||||||
regularSendTask = null;
|
regularSendTask = null;
|
||||||
reconnectTask = Task.CompletedTask;
|
reconnectTask = Task.CompletedTask;
|
||||||
|
|
||||||
//try to abort any non read message
|
if (await SendCommandInternalAsync($"%{GetStationNumber()}#RT") != null) {
|
||||||
//await SendNoResponseCommandAsync($"%{GetStationNumber()}#AB");
|
|
||||||
|
|
||||||
//get plc info 2 times to clear old stuff from the buffer
|
Logger.Log("Reconnect successfull");
|
||||||
|
OnReconnected();
|
||||||
|
|
||||||
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;
|
await Task.CompletedTask;
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
|
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,21 @@ namespace MewtocolNet.RegisterBuilding.BuilderPatterns {
|
|||||||
|
|
||||||
internal Register Assemble(StepBase stp) => assembler.Assemble(stp.Data);
|
internal Register Assemble(StepBase stp) => assembler.Assemble(stp.Data);
|
||||||
|
|
||||||
//struct constructor
|
//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 {
|
public StructStp<T> Struct<T>(string fpAddr, string name = null) where T : struct {
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ namespace MewtocolNet.Registers {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
int PollLevel { get; }
|
int PollLevel { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Info string about the memory area
|
||||||
|
/// </summary>
|
||||||
|
string MemoryAreaInfo { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The update frequency of the register in Hz
|
/// The update frequency of the register in Hz
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -59,6 +64,8 @@ namespace MewtocolNet.Registers {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
uint MemoryAddress { get; }
|
uint MemoryAddress { get; }
|
||||||
|
|
||||||
|
string MemoryAreaHash { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the value of the register as the plc representation string
|
/// Gets the value of the register as the plc representation string
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace MewtocolNet.Registers {
|
|||||||
internal List<RegisterPropTarget> boundProperties = new List<RegisterPropTarget>();
|
internal List<RegisterPropTarget> boundProperties = new List<RegisterPropTarget>();
|
||||||
|
|
||||||
internal Type underlyingSystemType;
|
internal Type underlyingSystemType;
|
||||||
internal IMemoryArea underlyingMemory;
|
internal AreaBase underlyingMemory;
|
||||||
internal bool autoGenerated;
|
internal bool autoGenerated;
|
||||||
internal bool isAnonymous;
|
internal bool isAnonymous;
|
||||||
|
|
||||||
@@ -94,6 +94,9 @@ namespace MewtocolNet.Registers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string MemoryAreaInfo => underlyingMemory.GetName();
|
||||||
|
|
||||||
|
public string MemoryAreaHash => underlyingMemory.GetHashCode().ToString();
|
||||||
|
|
||||||
internal Register() { }
|
internal Register() { }
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,17 @@ namespace MewtocolNet.Registers {
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override uint GetRegisterAddressLen() => 1;
|
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
|
//if string correct the sizing of the byte hint was wrong
|
||||||
var reservedSize = BitConverter.ToInt16(bytes, 0);
|
var reservedSize = BitConverter.ToInt16(bytes, 0);
|
||||||
|
|
||||||
if (reservedStringLength != reservedSize &&
|
if (reservedStringLength != reservedSize && attachedInterface.PlcInfo.IsRunMode)
|
||||||
attachedInterface.PlcInfo.IsRunMode &&
|
|
||||||
(attachedInterface.pollerTaskStopped || attachedInterface.pollerFirstCycleCompleted))
|
|
||||||
throw new NotSupportedException(
|
throw new NotSupportedException(
|
||||||
$"The STRING register at {GetMewName()} is not correctly sized, " +
|
$"The STRING register at {GetMewName()} is not correctly sized, " +
|
||||||
$"the size should be STRING[{reservedSize}] instead of STRING[{reservedStringLength}]"
|
$"the size should be STRING[{reservedSize}] instead of STRING[{reservedStringLength}]"
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MewtocolNet.UnderlyingRegisters {
|
namespace MewtocolNet.UnderlyingRegisters {
|
||||||
public class DTArea : IMemoryArea {
|
|
||||||
|
public abstract class AreaBase {
|
||||||
|
|
||||||
private MewtocolInterface mewInterface;
|
private MewtocolInterface mewInterface;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
public ulong AddressStart => addressStart;
|
public ulong AddressStart => addressStart;
|
||||||
public ulong AddressEnd => addressEnd;
|
public ulong AddressEnd => addressEnd;
|
||||||
|
|
||||||
internal DTArea(MewtocolInterface mewIf) {
|
internal AreaBase(MewtocolInterface mewIf) {
|
||||||
|
|
||||||
mewInterface = 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 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 {
|
internal interface IMemoryArea {
|
||||||
|
|
||||||
|
string GetName();
|
||||||
|
|
||||||
byte[] GetUnderlyingBytes(Register reg);
|
byte[] GetUnderlyingBytes(Register reg);
|
||||||
|
|
||||||
void SetUnderlyingBytes(Register reg, byte[] bytes);
|
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);
|
TestPollLevelExistence(reg);
|
||||||
|
|
||||||
switch (reg.RegisterType) {
|
AddToArea(reg, reg.RegisterType);
|
||||||
case RegisterPrefix.X:
|
|
||||||
case RegisterPrefix.Y:
|
|
||||||
case RegisterPrefix.R:
|
|
||||||
AddToWRArea(reg);
|
|
||||||
break;
|
|
||||||
case RegisterPrefix.DT:
|
|
||||||
case RegisterPrefix.DDT:
|
|
||||||
AddToDTArea(reg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,73 +145,33 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AddToWRArea(Register insertReg) {
|
private void AddToArea(Register insertReg, RegisterPrefix prefix) {
|
||||||
|
|
||||||
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) {
|
|
||||||
|
|
||||||
uint regInsAddStart = insertReg.MemoryAddress;
|
uint regInsAddStart = insertReg.MemoryAddress;
|
||||||
uint regInsAddEnd = insertReg.MemoryAddress + insertReg.GetRegisterAddressLen() - 1;
|
uint regInsAddEnd = insertReg.MemoryAddress + insertReg.GetRegisterAddressLen() - 1;
|
||||||
|
|
||||||
DTArea targetArea = null;
|
AreaBase targetArea = null;
|
||||||
|
|
||||||
var pollLevelFound = pollLevels.FirstOrDefault(x => x.level == insertReg.pollLevel);
|
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 &&
|
bool addressInsideArea = regInsAddStart >= dtArea.AddressStart &&
|
||||||
regInsAddEnd <= dtArea.AddressEnd;
|
regInsAddEnd <= dtArea.AddressEnd;
|
||||||
@@ -278,7 +228,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
};
|
};
|
||||||
|
|
||||||
targetArea.BoundaryUdpdate();
|
targetArea.BoundaryUdpdate();
|
||||||
dataAreas.Add(targetArea);
|
pollLevelAreas.Add(targetArea);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +308,7 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//update registers in poll level
|
//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
|
//set the whole memory area at once
|
||||||
await dtArea.RequestByteReadAsync(dtArea.AddressStart, dtArea.AddressEnd);
|
await dtArea.RequestByteReadAsync(dtArea.AddressStart, dtArea.AddressEnd);
|
||||||
@@ -493,10 +443,9 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
foreach (var lvl in pollLevels) {
|
foreach (var lvl in pollLevels) {
|
||||||
|
|
||||||
registers.AddRange(lvl.dataAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
|
registers.AddRange(lvl.dataAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
|
||||||
|
registers.AddRange(lvl.internalRelayAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
|
||||||
registers.AddRange(lvl.internalRelayAreas.SelectMany(x => x.linkedRegisters));
|
registers.AddRange(lvl.externalRelayInAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
|
||||||
registers.AddRange(lvl.externalRelayInAreas.SelectMany(x => x.linkedRegisters));
|
registers.AddRange(lvl.externalRelayOutAreas.SelectMany(x => x.managedRegisters).SelectMany(x => x.Linked));
|
||||||
registers.AddRange(lvl.externalRelayOutAreas.SelectMany(x => x.linkedRegisters));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,30 +6,43 @@ namespace MewtocolNet.UnderlyingRegisters {
|
|||||||
|
|
||||||
internal int lastReadTimeMs = 0;
|
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;
|
internal int level;
|
||||||
|
|
||||||
// WR areas are n of words, each word has 2 bytes representing the "special address component"
|
// WR areas are n of words, each word has 2 bytes representing the "special address component"
|
||||||
|
|
||||||
//X WR
|
//X WR
|
||||||
internal List<WRArea> externalRelayInAreas;
|
internal List<AreaBase> externalRelayInAreas;
|
||||||
|
|
||||||
//Y WR
|
//Y WR
|
||||||
internal List<WRArea> externalRelayOutAreas;
|
internal List<AreaBase> externalRelayOutAreas;
|
||||||
|
|
||||||
//R WR
|
//R WR
|
||||||
internal List<WRArea> internalRelayAreas;
|
internal List<AreaBase> internalRelayAreas;
|
||||||
|
|
||||||
//DT
|
//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