mirror of
https://github.com/OpenLogics/MewtocolNet.git
synced 2025-12-06 03:01:24 +00:00
Move examples from root to /Examples
This commit is contained in:
11
Examples/Examples.WPF/App.xaml
Normal file
11
Examples/Examples.WPF/App.xaml
Normal file
@@ -0,0 +1,11 @@
|
||||
<Application x:Class="Examples.WPF.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Examples.WPF"
|
||||
xmlns:conv="clr-namespace:Examples.WPF.Converters"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
<conv:NegationConverter x:Key="bInv"/>
|
||||
<conv:ColorHashConverter x:Key="hashColor"/>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
85
Examples/Examples.WPF/App.xaml.cs
Normal file
85
Examples/Examples.WPF/App.xaml.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Examples.WPF.ViewModels;
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace Examples.WPF;
|
||||
|
||||
public partial class App : Application {
|
||||
|
||||
public static AppViewModel ViewModel { get; private set; } = null!;
|
||||
|
||||
public static new App Current = null!;
|
||||
|
||||
public static new MainWindow MainWindow = null!;
|
||||
|
||||
public static ObservableCollection<TextBlock> LoggerItems = null!;
|
||||
|
||||
internal static event Action? LogEventProcessed;
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e) {
|
||||
|
||||
ViewModel = new AppViewModel();
|
||||
|
||||
Current = this;
|
||||
LoggerItems = new();
|
||||
|
||||
Logger.LogLevel = LogLevel.Verbose;
|
||||
Logger.DefaultTargets = LoggerTargets.Trace;
|
||||
|
||||
Logger.OnNewLogMessage((d, l, m) => {
|
||||
|
||||
Application.Current.Dispatcher.BeginInvoke(() => {
|
||||
|
||||
Brush msgColor = null!;
|
||||
|
||||
switch (l) {
|
||||
case LogLevel.Error:
|
||||
msgColor = Brushes.Red;
|
||||
break;
|
||||
case LogLevel.Change:
|
||||
msgColor = Brushes.Blue;
|
||||
break;
|
||||
case LogLevel.Verbose:
|
||||
msgColor = Brushes.Gold;
|
||||
break;
|
||||
case LogLevel.Critical:
|
||||
msgColor = Brushes.Gray;
|
||||
break;
|
||||
}
|
||||
|
||||
if (LoggerItems.Count > 1000) LoggerItems.RemoveAt(0);
|
||||
|
||||
var contRun = msgColor == null ? new Run(m) : new Run(m) {
|
||||
Foreground = msgColor,
|
||||
};
|
||||
|
||||
LoggerItems.Add(new TextBlock {
|
||||
Inlines = {
|
||||
new Run($"[{d:hh:mm:ss:ff}] ") {
|
||||
Foreground = Brushes.LimeGreen,
|
||||
},
|
||||
contRun
|
||||
}
|
||||
});
|
||||
|
||||
LogEventProcessed?.Invoke();
|
||||
|
||||
}, System.Windows.Threading.DispatcherPriority.Background);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
10
Examples/Examples.WPF/AssemblyInfo.cs
Normal file
10
Examples/Examples.WPF/AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
62
Examples/Examples.WPF/Converters/ColorHashConverter.cs
Normal file
62
Examples/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();
|
||||
|
||||
}
|
||||
14
Examples/Examples.WPF/Converters/NegationConverter.cs
Normal file
14
Examples/Examples.WPF/Converters/NegationConverter.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Examples.WPF.Converters;
|
||||
|
||||
[ValueConversion(typeof(bool), typeof(bool))]
|
||||
public class NegationConverter : IValueConverter {
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => value != null ? !(bool)value : false;
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => value != null ? !(bool)value : false;
|
||||
|
||||
}
|
||||
16
Examples/Examples.WPF/Examples.WPF.csproj
Normal file
16
Examples/Examples.WPF/Examples.WPF.csproj
Normal file
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPublishable>false</IsPublishable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MewtocolNet\MewtocolNet.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
169
Examples/Examples.WPF/MainWindow.xaml
Normal file
169
Examples/Examples.WPF/MainWindow.xaml
Normal file
@@ -0,0 +1,169 @@
|
||||
<Window x:Class="Examples.WPF.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Examples.WPF"
|
||||
d:DataContext="{d:DesignInstance local:MainWindow, IsDesignTimeCreatable=True}"
|
||||
mc:Ignorable="d"
|
||||
MinWidth="500"
|
||||
MinHeight="400"
|
||||
Height="850"
|
||||
Width="1200"
|
||||
Title="MewtocolNet WPF Demo">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition MinHeight="30" Height="150"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ContentControl x:Name="mainContent"/>
|
||||
<GridSplitter Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="Gray"
|
||||
ShowsPreview="true"
|
||||
Height="5">
|
||||
<GridSplitter.Template>
|
||||
<ControlTemplate>
|
||||
<Separator/>
|
||||
</ControlTemplate>
|
||||
</GridSplitter.Template>
|
||||
</GridSplitter>
|
||||
<Grid Grid.Row="2">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="20"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Background="LightGray"/>
|
||||
|
||||
<StackPanel HorizontalAlignment="Left"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBlock Text="Logger"
|
||||
Margin="5"/>
|
||||
|
||||
<Border Width="1"
|
||||
Margin="5"
|
||||
Background="Gray"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
<TextBlock Text="Rx"
|
||||
Margin="5"/>
|
||||
|
||||
<Ellipse IsEnabled="{Binding AppViewModel.Plc.IsReceiving, Mode=OneWay}"
|
||||
Fill="Lime"
|
||||
Width="10"
|
||||
Height="10">
|
||||
<Ellipse.Style>
|
||||
<Style TargetType="Ellipse">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value=".1"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Ellipse.Style>
|
||||
</Ellipse>
|
||||
|
||||
<TextBlock Text="Tx"
|
||||
Margin="5"/>
|
||||
|
||||
<Ellipse Fill="Orange"
|
||||
IsEnabled="{Binding AppViewModel.Plc.IsSending, Mode=OneWay}"
|
||||
Width="10"
|
||||
Height="10">
|
||||
<Ellipse.Style>
|
||||
<Style TargetType="Ellipse">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value=".1"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Ellipse.Style>
|
||||
</Ellipse>
|
||||
|
||||
<Border Width="1"
|
||||
Margin="5"
|
||||
Background="Gray"/>
|
||||
|
||||
<TextBlock Text="{Binding AppViewModel.Plc.BytesPerSecondDownstream, Mode=OneWay}"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<Border Width="1"
|
||||
Margin="5"
|
||||
Background="Gray"/>
|
||||
|
||||
<TextBlock Text="{Binding AppViewModel.Plc.BytesPerSecondUpstream, Mode=OneWay}"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<Border Width="1"
|
||||
Margin="5"
|
||||
Background="Gray"/>
|
||||
|
||||
<TextBlock Text="{Binding AppViewModel.Plc.QueuedMessages, StringFormat='{}Q: {0}', Mode=OneWay}"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<StackPanel.Style>
|
||||
<Style TargetType="StackPanel">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding AppViewModel.PlcIsNull}" Value="True">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StackPanel.Style>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Right">
|
||||
|
||||
<Button Content="Expand"
|
||||
Margin="2"/>
|
||||
|
||||
<ToggleButton Content="Autoscroll"
|
||||
IsChecked="True"
|
||||
Margin="2"
|
||||
x:Name="autoScrollBtn"/>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<ListBox Grid.Row="1"
|
||||
Background="Black"
|
||||
Foreground="White"
|
||||
BorderThickness="0"
|
||||
VirtualizingPanel.IsVirtualizing="true"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
ItemsSource="{Binding LoggerItems, Mode=OneWay}"
|
||||
x:Name="loggerList">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentPresenter Content="{Binding}"/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<Border Background="Black"
|
||||
Grid.Row="2">
|
||||
<TextBlock Text=">"
|
||||
Foreground="White"
|
||||
Margin="5,0,0,0"/>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
57
Examples/Examples.WPF/MainWindow.xaml.cs
Normal file
57
Examples/Examples.WPF/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using Examples.WPF.ViewModels;
|
||||
using Examples.WPF.Views;
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Examples.WPF;
|
||||
|
||||
public partial class MainWindow : Window {
|
||||
|
||||
public ObservableCollection<TextBlock> LoggerItems => App.LoggerItems;
|
||||
|
||||
public AppViewModel AppViewModel => App.ViewModel;
|
||||
|
||||
public MainWindow() {
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
this.DataContext = this;
|
||||
App.MainWindow = this;
|
||||
|
||||
mainContent.Content = new ConnectView();
|
||||
|
||||
loggerList.PreviewMouseWheel += (s, e) => {
|
||||
|
||||
autoScrollBtn.IsChecked = false;
|
||||
|
||||
};
|
||||
|
||||
App.LogEventProcessed += () => {
|
||||
|
||||
Application.Current.Dispatcher.BeginInvoke(() => {
|
||||
|
||||
if (autoScrollBtn?.IsChecked != null && autoScrollBtn.IsChecked.Value)
|
||||
loggerList.ScrollIntoView(App.LoggerItems.Last());
|
||||
|
||||
}, System.Windows.Threading.DispatcherPriority.Send);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.RegisterAttributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Examples.WPF.RegisterCollections;
|
||||
|
||||
public class TestRegisterCollection : RegisterCollection {
|
||||
|
||||
[Register("R11A")]
|
||||
public bool? TestR11A { get; set; }
|
||||
|
||||
[Register("R11A")]
|
||||
public bool TestR11A_Duplicate_NonNullable { get; set; }
|
||||
|
||||
[Register("R16B")]
|
||||
public bool TestR16B { get; set; }
|
||||
|
||||
[Register("R902")]
|
||||
public bool Test { get; set; }
|
||||
|
||||
[BitRegister("DT1000", 0), PollLevel(3)]
|
||||
public bool? TestDT100_Word_Duplicate_SingleBit { get; set; }
|
||||
|
||||
[Register("DT1000")]
|
||||
public Word TestDT100_Word_Duplicate { get; set; }
|
||||
|
||||
[BitRegister("DDT1010", 1)]
|
||||
public bool? TestDDT1010_DWord_Duplicate_SingleBit { get; set; }
|
||||
|
||||
}
|
||||
40
Examples/Examples.WPF/ViewModels/AppViewModel.cs
Normal file
40
Examples/Examples.WPF/ViewModels/AppViewModel.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Examples.WPF.RegisterCollections;
|
||||
using MewtocolNet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Examples.WPF.ViewModels {
|
||||
|
||||
public class AppViewModel : ViewModelBase {
|
||||
|
||||
private IPlc? plc;
|
||||
private TestRegisterCollection testRegCollection = null!;
|
||||
|
||||
public bool PlcIsNull => plc == null;
|
||||
|
||||
public bool PlcIsNotNull => plc != null;
|
||||
|
||||
public IPlc? Plc {
|
||||
get => plc;
|
||||
set {
|
||||
plc = value;
|
||||
OnPropChange();
|
||||
OnPropChange(nameof(PlcIsNull));
|
||||
OnPropChange(nameof(PlcIsNotNull));
|
||||
}
|
||||
}
|
||||
|
||||
public TestRegisterCollection TestRegCollection {
|
||||
get => testRegCollection;
|
||||
set {
|
||||
testRegCollection = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
117
Examples/Examples.WPF/ViewModels/ConnectViewViewModel.cs
Normal file
117
Examples/Examples.WPF/ViewModels/ConnectViewViewModel.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration.Internal;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.ComCassette;
|
||||
|
||||
namespace Examples.WPF.ViewModels;
|
||||
|
||||
internal class ConnectViewViewModel : ViewModelBase {
|
||||
|
||||
private bool hasComports = false;
|
||||
private IEnumerable<int> baudRates = null!;
|
||||
private IEnumerable<string> comPorts = null!;
|
||||
private IEnumerable<CassetteInformation> foundCassettes = null!;
|
||||
|
||||
private string selectedIP = "192.168.115.210";
|
||||
private string selectedPort = "9094";
|
||||
private bool isConnecting;
|
||||
|
||||
public IEnumerable<int> BaudRates {
|
||||
get => baudRates;
|
||||
set {
|
||||
baudRates = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> ComPorts {
|
||||
get => comPorts;
|
||||
set {
|
||||
comPorts = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<CassetteInformation> FoundCassettes {
|
||||
get => foundCassettes;
|
||||
set {
|
||||
foundCassettes = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasComports {
|
||||
get => hasComports;
|
||||
set {
|
||||
hasComports = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedIP {
|
||||
get { return selectedIP; }
|
||||
set {
|
||||
selectedIP = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string SelectedPort {
|
||||
get { return selectedPort; }
|
||||
set {
|
||||
selectedPort = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsConnecting {
|
||||
get { return isConnecting; }
|
||||
set {
|
||||
isConnecting = value;
|
||||
OnPropChange();
|
||||
}
|
||||
}
|
||||
|
||||
private DispatcherTimer tm;
|
||||
|
||||
public ConnectViewViewModel() {
|
||||
|
||||
BaudRates = Mewtocol.GetUseableBaudRates();
|
||||
ScanTimerTick(null, null!);
|
||||
|
||||
tm = new DispatcherTimer {
|
||||
Interval = TimeSpan.FromSeconds(3),
|
||||
};
|
||||
tm.Tick += ScanTimerTick;
|
||||
|
||||
tm.Start();
|
||||
|
||||
}
|
||||
|
||||
private async void ScanTimerTick(object? sender, EventArgs e) {
|
||||
|
||||
ComPorts = Mewtocol.GetSerialPortNames();
|
||||
HasComports = ComPorts != null && ComPorts.Count() > 0;
|
||||
|
||||
var found = await CassetteFinder.FindClientsAsync(timeoutMs: 1000);
|
||||
if (FoundCassettes == null || !Enumerable.SequenceEqual(found, FoundCassettes))
|
||||
FoundCassettes = found;
|
||||
|
||||
}
|
||||
|
||||
internal void SelectedCassette (CassetteInformation cassette) {
|
||||
|
||||
SelectedIP = cassette.IPAddress.ToString();
|
||||
SelectedPort = cassette.Port.ToString();
|
||||
|
||||
}
|
||||
|
||||
internal void EndTimer() => tm.Stop();
|
||||
|
||||
}
|
||||
36
Examples/Examples.WPF/ViewModels/PlcDataViewViewModel.cs
Normal file
36
Examples/Examples.WPF/ViewModels/PlcDataViewViewModel.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Examples.WPF.RegisterCollections;
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Examples.WPF.ViewModels;
|
||||
|
||||
public class PlcDataViewViewModel : ViewModelBase {
|
||||
|
||||
private ReconnectArgs plcCurrentReconnectArgs = null!;
|
||||
|
||||
public IPlc Plc => App.ViewModel.Plc!;
|
||||
|
||||
public TestRegisterCollection RegCollection => App.ViewModel.TestRegCollection;
|
||||
|
||||
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!;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
18
Examples/Examples.WPF/ViewModels/ViewModelBase.cs
Normal file
18
Examples/Examples.WPF/ViewModels/ViewModelBase.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Examples.WPF.ViewModels;
|
||||
|
||||
public abstract class ViewModelBase : INotifyPropertyChanged {
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public void PropChange(string _name) {
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_name));
|
||||
}
|
||||
|
||||
protected void OnPropChange([CallerMemberName] string propertyName = null) {
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
}
|
||||
129
Examples/Examples.WPF/Views/ConnectView.xaml
Normal file
129
Examples/Examples.WPF/Views/ConnectView.xaml
Normal file
@@ -0,0 +1,129 @@
|
||||
<UserControl x:Class="Examples.WPF.Views.ConnectView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Examples.WPF.Views"
|
||||
xmlns:vm="clr-namespace:Examples.WPF.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance vm:ConnectViewViewModel, IsDesignTimeCreatable=True}"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid Margin="10">
|
||||
<StackPanel>
|
||||
|
||||
<StackPanel.Resources>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Padding" Value="5"/>
|
||||
</Style>
|
||||
</StackPanel.Resources>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Text="Connect to a PLC"
|
||||
FontSize="24"/>
|
||||
<Label Content="Set your connection type"/>
|
||||
<ComboBox SelectedIndex="0" x:Name="conTypeCombo">
|
||||
<ComboBoxItem>Ethernet</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding HasComports}">Serial</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<Separator/>
|
||||
|
||||
<StackPanel MinWidth="200">
|
||||
|
||||
<TextBlock Text="Cassettes"/>
|
||||
|
||||
<DataGrid ItemsSource="{Binding FoundCassettes}"
|
||||
SelectionChanged="SelectedCassette"
|
||||
AutoGenerateColumns="False"
|
||||
MinHeight="150"
|
||||
MaxHeight="200"
|
||||
IsReadOnly="True">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width=".5*"/>
|
||||
<DataGridTextColumn Header="IP" Binding="{Binding IPAddress}" Width=".3*"/>
|
||||
<DataGridTextColumn Header="Port" Binding="{Binding Port}" Width=".3*"/>
|
||||
<DataGridCheckBoxColumn Header="DHCP" Binding="{Binding UsesDHCP}" Width="auto"/>
|
||||
<DataGridTextColumn Header="MAC" Binding="{Binding MacAddressStr, Mode=OneWay}" Width="auto"/>
|
||||
<DataGridTextColumn Header="Status" Binding="{Binding Status}" Width="auto"/>
|
||||
</DataGrid.Columns>
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="{x:Type DataGridRow}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Port}" Value="0">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
</DataGrid>
|
||||
|
||||
<TextBlock Text="Connection"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="IP Address"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBox Text="{Binding SelectedIP}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Label Content="Port"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBox Text="{Binding SelectedPort}"
|
||||
VerticalAlignment="Center"/>
|
||||
<Button Content="Connect"
|
||||
Click="ClickedConnectEth"
|
||||
VerticalAlignment="Center"
|
||||
Padding="5"
|
||||
Margin="10,0,0,0">
|
||||
<Button.Style>
|
||||
<Style TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsConnecting}">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel.Style>
|
||||
<Style TargetType="StackPanel">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding SelectedIndex, ElementName=conTypeCombo}" Value="0">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StackPanel.Style>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel MinWidth="200"
|
||||
HorizontalAlignment="Left">
|
||||
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Label Content="COM Port"/>
|
||||
<ComboBox ItemsSource="{Binding ComPorts}"
|
||||
SelectedIndex="0"/>
|
||||
<Label Content="BaudRate"/>
|
||||
<ComboBox ItemsSource="{Binding BaudRates}"
|
||||
SelectedIndex="0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel.Style>
|
||||
<Style TargetType="StackPanel">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding SelectedIndex, ElementName=conTypeCombo}" Value="1">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</StackPanel.Style>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
161
Examples/Examples.WPF/Views/ConnectView.xaml.cs
Normal file
161
Examples/Examples.WPF/Views/ConnectView.xaml.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using Examples.WPF.RegisterCollections;
|
||||
using Examples.WPF.ViewModels;
|
||||
using MewtocolNet;
|
||||
using MewtocolNet.ComCassette;
|
||||
using MewtocolNet.Logging;
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Examples.WPF.Views;
|
||||
|
||||
/// <summary>
|
||||
/// Interaktionslogik für ConnectView.xaml
|
||||
/// </summary>
|
||||
public partial class ConnectView : UserControl {
|
||||
|
||||
private ConnectViewViewModel viewModel;
|
||||
|
||||
public ConnectView() {
|
||||
|
||||
InitializeComponent();
|
||||
viewModel = new ConnectViewViewModel();
|
||||
this.DataContext = viewModel;
|
||||
|
||||
Unloaded += (s, e) => viewModel.EndTimer();
|
||||
|
||||
}
|
||||
|
||||
private void SelectedCassette(object sender, SelectionChangedEventArgs e) {
|
||||
|
||||
var cassette = (CassetteInformation)((DataGrid)sender).SelectedItem;
|
||||
if (cassette == null) return;
|
||||
|
||||
viewModel.SelectedCassette(cassette);
|
||||
|
||||
}
|
||||
|
||||
private void ClickedConnectEth(object sender, RoutedEventArgs e) {
|
||||
|
||||
Application.Current.Dispatcher.BeginInvoke(async () => {
|
||||
|
||||
viewModel.IsConnecting = true;
|
||||
|
||||
var parsedInt = int.Parse(viewModel.SelectedPort);
|
||||
|
||||
IRegister<short> heartbeatSetter = null!;
|
||||
IRegister<bool> outputContactReference = null!;
|
||||
IRegister<bool> testBoolReference = null!;
|
||||
IRegister<Word> wordRefTest = null!;
|
||||
|
||||
//build a new interface
|
||||
App.ViewModel.Plc = Mewtocol.Ethernet(viewModel.SelectedIP, parsedInt)
|
||||
.WithPoller()
|
||||
.WithInterfaceSettings(setting => {
|
||||
|
||||
setting.TryReconnectAttempts = 10;
|
||||
setting.TryReconnectDelayMs = 2000;
|
||||
setting.SendReceiveTimeoutMs = 1000;
|
||||
setting.HeartbeatIntervalMs = 3000;
|
||||
setting.MaxDataBlocksPerWrite = 128;
|
||||
setting.MaxOptimizationDistance = 20;
|
||||
|
||||
})
|
||||
.WithCustomPollLevels(lvl => {
|
||||
|
||||
lvl.SetLevel(2, 3);
|
||||
lvl.SetLevel(3, TimeSpan.FromSeconds(5));
|
||||
lvl.SetLevel(4, TimeSpan.FromSeconds(10));
|
||||
|
||||
})
|
||||
.WithRegisterCollections(collector => {
|
||||
|
||||
App.ViewModel.TestRegCollection = collector.AddCollection<TestRegisterCollection>();
|
||||
|
||||
})
|
||||
.WithRegisters(b => {
|
||||
|
||||
b.Bool("X4").Build();
|
||||
b.Bool("Y4").Build(out outputContactReference);
|
||||
b.Bool("R10A").PollLevel(PollLevel.FirstIteration).Build(out testBoolReference);
|
||||
|
||||
b.Struct<short>("DT1000").Build(out heartbeatSetter);
|
||||
|
||||
//these will be merged into one
|
||||
b.Struct<Word>("DT1000").Build(out wordRefTest);
|
||||
b.Struct<Word>("DT1000").Build(out wordRefTest);
|
||||
|
||||
b.Struct<ushort>("DT1001").PollLevel(2).Build();
|
||||
b.Struct<Word>("DT1002").PollLevel(2).Build();
|
||||
|
||||
b.Struct<int>("DDT1010").PollLevel(2).Build();
|
||||
b.Struct<uint>("DDT1012").PollLevel(2).Build();
|
||||
b.Struct<DWord>("DDT1014").PollLevel(2).Build();
|
||||
b.Struct<float>("DDT1016").PollLevel(2).Build();
|
||||
b.Struct<TimeSpan>("DDT1018").PollLevel(2).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 (plc) => {
|
||||
|
||||
var randShort = (short)new Random().Next(short.MinValue, short.MaxValue);
|
||||
|
||||
//write direct
|
||||
//await heartbeatSetter.WriteAsync(randShort);
|
||||
//or by anonymous
|
||||
await plc.Register.Struct<short>("DT1000").WriteAsync(randShort);
|
||||
|
||||
//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(async () => {
|
||||
|
||||
await App.ViewModel.Plc.RestartProgramAsync();
|
||||
|
||||
});
|
||||
|
||||
await App.ViewModel.Plc.AwaitFirstDataCycleAsync();
|
||||
|
||||
if (App.ViewModel.Plc.IsConnected) {
|
||||
|
||||
App.MainWindow.mainContent.Content = new PlcDataView();
|
||||
|
||||
}
|
||||
|
||||
viewModel.IsConnecting = false;
|
||||
|
||||
}, System.Windows.Threading.DispatcherPriority.Send);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
350
Examples/Examples.WPF/Views/PlcDataView.xaml
Normal file
350
Examples/Examples.WPF/Views/PlcDataView.xaml
Normal file
@@ -0,0 +1,350 @@
|
||||
<UserControl x:Class="Examples.WPF.Views.PlcDataView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:vm="clr-namespace:Examples.WPF.ViewModels"
|
||||
xmlns:local="clr-namespace:Examples.WPF.Views"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance vm:PlcDataViewViewModel, IsDesignTimeCreatable=True}"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800">
|
||||
<Grid>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Menu>
|
||||
<MenuItem Header="PLC">
|
||||
<MenuItem Header="Disconnect" IsEnabled="{Binding Plc.IsConnected}"
|
||||
Click="ClickedDisconnect"/>
|
||||
<MenuItem Header="Connect" IsEnabled="{Binding Plc.IsConnected, Converter={StaticResource bInv}}"
|
||||
Click="ClickedConnect"/>
|
||||
<MenuItem Header="Set Random DT1000" IsEnabled="{Binding Plc.IsConnected}"
|
||||
Click="ClickedSetRandom"/>
|
||||
<MenuItem Header="Queue test" IsEnabled="{Binding Plc.IsConnected}"
|
||||
Click="ClickedAddQueueTest"/>
|
||||
<MenuItem Header="Toggle OP mode" IsEnabled="{Binding Plc.IsConnected}"
|
||||
Click="ClickedToggleRunMode"/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<StackPanel Margin="10"
|
||||
Grid.Row="1">
|
||||
|
||||
<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}"/>
|
||||
|
||||
<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>
|
||||
<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}"
|
||||
FontSize="18"/>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding Plc.PlcInfo.OperationModeTags, Mode=OneWay}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="LightGray"
|
||||
CornerRadius="5"
|
||||
Margin="2"
|
||||
Padding="5">
|
||||
<TextBlock Text="{Binding}"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<ItemsControl ItemsSource="{Binding Plc.PlcInfo.HardwareInformationTags, Mode=OneWay}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="LightGray"
|
||||
CornerRadius="5"
|
||||
Margin="2"
|
||||
Padding="5">
|
||||
<TextBlock Text="{Binding}"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</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>
|
||||
|
||||
<Grid Grid.Row="2">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1.5*"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<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"
|
||||
ShowsPreview="true"
|
||||
Width="10">
|
||||
<GridSplitter.Template>
|
||||
<ControlTemplate>
|
||||
<Rectangle Fill="Gray"
|
||||
Width="2"
|
||||
Margin="2"/>
|
||||
</ControlTemplate>
|
||||
</GridSplitter.Template>
|
||||
</GridSplitter>
|
||||
|
||||
<TextBlock Text="Underlying Registers"
|
||||
FontSize="18"
|
||||
Margin="10"/>
|
||||
|
||||
<DataGrid Grid.Row="1"
|
||||
Grid.RowSpan="3"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding Plc.Registers, Mode=OneWay}">
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsAutoGenerated}" Value="True">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="FP Address" Binding="{Binding PLCAddressName}"/>
|
||||
<DataGridTextColumn Header="Type" Binding="{Binding UnderlyingSystemType.Name}"/>
|
||||
<DataGridTemplateColumn Header="Value" Width="*">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border HorizontalAlignment="Left"
|
||||
Padding="2,0">
|
||||
<TextBlock Text="{Binding ValueStr}"/>
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ValueStr}" Value="True">
|
||||
<Setter Property="Background" Value="Blue"/>
|
||||
<Setter Property="TextElement.Foreground" Value="White"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Header="Poll Level" Binding="{Binding PollLevel, Mode=OneWay}"/>
|
||||
<DataGridTextColumn Header="Update Frequency" Binding="{Binding UpdateFreqHz, StringFormat='{}{0} Hz',Mode=OneWay}"/>
|
||||
<DataGridTemplateColumn Width="15">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{Binding MemoryArea, Mode=OneWay, Converter={StaticResource hashColor}}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<TextBlock Text="Memory Areas"
|
||||
Grid.Column="2"
|
||||
FontSize="18"
|
||||
Margin="10"/>
|
||||
|
||||
<Border Grid.Column="2"
|
||||
Grid.Row="1"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
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>
|
||||
|
||||
<TextBlock Text="Boolean internal relays"/>
|
||||
|
||||
<TextBlock>
|
||||
<Run Text="R11A Nullable property: "/>
|
||||
<Run Text="{Binding RegCollection.TestR11A}"
|
||||
FontWeight="Bold"/>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock>
|
||||
<Run Text="R11A Duplicate non nullable property: "/>
|
||||
<Run Text="{Binding RegCollection.TestR11A_Duplicate_NonNullable}"
|
||||
FontWeight="Bold"/>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock>
|
||||
<Run Text="R16B"/>
|
||||
<Run Text="{Binding RegCollection.TestR16B}"
|
||||
FontWeight="Bold"/>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock>
|
||||
<Run Text="DT1000 Word duplicate: "/>
|
||||
<Run Text="{Binding RegCollection.TestDT100_Word_Duplicate, Mode=OneWay}"
|
||||
FontWeight="Bold"/>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock>
|
||||
<Run Text="DT1000 Word direct bit read (Idx 0): "/>
|
||||
<Run Text="{Binding RegCollection.TestDT100_Word_Duplicate_SingleBit, Mode=OneWay}"
|
||||
FontWeight="Bold"/>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock>
|
||||
<Run Text="DDT1010 DWord direct bit read (Idx 1): "/>
|
||||
<Run Text="{Binding RegCollection.TestDDT1010_DWord_Duplicate_SingleBit, Mode=OneWay}"
|
||||
FontWeight="Bold"/>
|
||||
</TextBlock>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</ScrollViewer>
|
||||
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
81
Examples/Examples.WPF/Views/PlcDataView.xaml.cs
Normal file
81
Examples/Examples.WPF/Views/PlcDataView.xaml.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using Examples.WPF.ViewModels;
|
||||
using MewtocolNet.Registers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Examples.WPF.Views;
|
||||
|
||||
public partial class PlcDataView : UserControl {
|
||||
|
||||
private PlcDataViewViewModel viewModel;
|
||||
|
||||
public PlcDataView() {
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
viewModel = new PlcDataViewViewModel();
|
||||
this.DataContext = viewModel;
|
||||
|
||||
}
|
||||
|
||||
private void ClickedDisconnect(object sender, RoutedEventArgs e) {
|
||||
|
||||
viewModel.Plc.Disconnect();
|
||||
|
||||
}
|
||||
|
||||
private async void ClickedConnect(object sender, RoutedEventArgs e) {
|
||||
|
||||
await viewModel.Plc.ConnectAsync();
|
||||
|
||||
}
|
||||
|
||||
private async void ClickedSetRandom(object sender, RoutedEventArgs e) {
|
||||
|
||||
var reg = (IRegister<ushort>?)viewModel.Plc.GetAllRegisters()?.FirstOrDefault(x => x.PLCAddressName == "DT1001");
|
||||
|
||||
if(reg != null) {
|
||||
|
||||
await reg.WriteAsync((ushort)new Random().Next(ushort.MinValue, ushort.MaxValue));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async void ClickedAddQueueTest(object sender, RoutedEventArgs e) {
|
||||
|
||||
var tasks = new List<Task<short>>();
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
|
||||
var t = viewModel.Plc.Register.Struct<short>("DT1000").ReadAsync();
|
||||
|
||||
tasks.Add(t);
|
||||
|
||||
}
|
||||
|
||||
var list = await Task.WhenAll(tasks);
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
}
|
||||
|
||||
private async void ClickedToggleRunMode(object sender, RoutedEventArgs e) {
|
||||
|
||||
await viewModel.Plc.ToggleOperationModeAsync();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user