首先-我不是程序员,几个月前才开始学习。
我没有创建一个WPF程序使用EasyModbusTCP库的工作!甚至设法读取Modbus异步,所以它不冻结视图。
现在需要扩展这个程序,从modbus中提取超过600个线圈和寄存器。沿着,我在CommunityToolkit的帮助下转移到MVVM模式。
我不知道,老实说,我被如何在ViewModel中创建服务所淹没。
应用程序现在是复杂的显示完整的代码,所以我会尽可能简化。我的视图是具有ItemsControl的UserControl,另一个UserControl的集合作为ItemsSource。
ConductivityView XAML:
<ItemsControl Grid.Row="2" ItemsSource="{Binding ConductivityTanks}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ConductivityGridElementViewModel}">
<controls:ConductivityGridElement DataContext="{Binding}"></controls:ConductivityGridElement>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
CodeBehind:
public partial class BConductivityView : UserControl
{
ConductivityViewModel conductivityViewModel;
public BConductivityView()
{
InitializeComponent();
conductivityViewModel = new ConductivityViewModel();
this.DataContext = conductivityViewModel;
}
}
在我的ConductivityViewModel中,我需要插入未来的Modbus拉取服务并使其成为可扩展的。
public partial class ConductivityViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<ConductivityGridElementViewModel> _conductivityTanks;
public ConductivityViewModel()
{
_conductivityTanks = new ObservableCollection<ConductivityGridElementViewModel>();
ConductivityTanks.Clear();
//Add some initial dummy data to display
for (int i = 0; i <= 32; i++)
{
ConductivityTanks.Add(new ConductivityGridElementViewModel
{
Id = i.ToString(),
Use = true,
TankNo = i.ToString(),
Sp = (1000 + i).ToString(),
Pv = (1500 + i).ToString(),
Valve = false,
TimeLeft = (4 + i).ToString(),
Timer = (5).ToString(),
SensorMinimum = "0",
SensorMaximum = "10000",
Raw = "1670"
});
}
public void ModbusReadAsync()
{
// Make async call to start modbus pull and update all ConductivityGridElement in ConductivityTanks
}
}
}
我的Modbus服务类读取PLC并在静态类中分配静态字段,所有这些看起来像这样:
public static class ModbusService
{
public static CancellationTokenSource cts = new CancellationTokenSource();
public static ModbusClient MB = new ModbusClient();
private static bool[] coils1 = new bool[120];
private static bool[] coils2 = new bool[40];
private static int[] registers1 = new int[121];
private static int[] registers2 = new int[121];
private static int[] registers3 = new int[121];
public static void ConnectToPLC(string IP, int Port, bool ReConnect)
{
cts.Cancel();
ConnectionExist = false;
MB.Disconnect();
MB = new ModbusClient(IP, Port);
try
{
MB.Connect();
ConnectionExist = true;
ReadPLCAsync();
}
catch (Exception)
{
MessageBox.Show("Connection error.\r\nStart without reading PLC.");
}
}
public static async void ReadPLCAsync()
{
await Task.Run(async () =>
{
await Read(cts.Token);
});
}
private static async Task<bool> Read(CancellationToken cancellationToken)
{
while (ConnectionExist)
{
try
{
coils1 = MB.ReadCoils(0, 120);
coils2 = MB.ReadCoils(121, 40);
registers1 = MB.ReadHoldingRegisters(0, 122);
registers2 = MB.ReadHoldingRegisters(122, 122);
registers3 = MB.ReadHoldingRegisters(244, 122);
General.CPUinRUN = coils1[0];
General.ValveON = coils1[1];
General.SDCardEject = coils1[2];
General.SDCardEject = coils1[3];
Conductivity.MasterON = coils1[4];
Fresh.MasterON = coils1[5];
Level.MasterON = coils1[6];
General.SendEmail = coils1[7];
Array.Copy(coils1, 8, Conductivity.Valve, 0, 32);
Array.Copy(coils1, 40, Conductivity.CValveMan, 0, 32);
Array.Copy(coils1, 72, Fresh.FValve, 0, 32);
Fresh.RunOnSaturday = coils1[104];
Fresh.RunOnSunday = coils1[105];
Fresh.RunOnWeekend = coils1[106];
Array.Copy(coils1, 107, Level.L_Alarm, 0, 10);
Array.Copy(coils1, 117, Level.L_AlarmOFF, 0, 5);
Array.Copy(coils2, 0, Level.L_AlarmOFF, 5, 5);
Array.Copy(coils2, 5, Level.L_Filling, 0, 10);
Array.Copy(coils2, 15, Level.L_InRange, 0, 10);
Array.Copy(coils2, 25, Level.L_Valve, 0, 10);
Conductivity.ON = coils2[35];
Fresh.ON = coils2[36];
Level.ON = coils2[37];
}
catch (ConnectionException)
{
ConnectionExist = false;
MBC.MB.Disconnect();
MessageBox.Show("Problem reading PLC from MBC class.");
}
await Task.Delay(1000);
}
return true;
}
}
一般来说,应用程序看起来像这样
1条答案
按热度按时间46scxncf1#
好吧,我会非常详细地介绍我的实现,所以它可以是有用的评论和建议更好的方法来做,因为我不是c#程序员,没有MVVM社区工具包的想法。主要问题是
1.组织将通过EasyModbus接收的数据结构。
1.使用ModBus实现异步和恒定周期性从PLC读取数据并刷新View。
1.为多个视图和相应的视图模型实现导航。
第一次导航:我所有的视图都是在单独的用户控件(HomeView,ConductivityView等)中实现的。每个视图都有自己的ViewModel类。对于导航,我创建了两个类:BaseViewModel和MainViewModel。BaseViewModel类为空,并从ObservableObject继承。MainViewModel类继承自BaseViewModel。在这里,我用[ObservableProperty]属性示例化了所有ViewModel。主视图模型:
{
在MainWindow.xaml中,我使用ListBox创建导航面板。我是using behaviors to access the "Command" property。只有一个“Home”按钮。每个页面都有多个ListBoxItem副本,并有相应的名称和路径。
为了使应用程序能够识别每个页面上将要使用的数据类型,我使用DataTemplates创建了ResourceDictionary,并将其引入App. xaml。
这最后一步就像魔术一样(魔术,因为当时感觉就像)。这是导航。
我最大的挑战是创建MVVM的模型部分。对于像用Modbus异步读取PLC这样的任务,没有太多的信息。所以我必须要有创造力。我读到的每一篇关于模型的文章都对我没有帮助。我决定用我的方式创建两组模型。第一个集合以ViewModel可以轻松使用的形式表示数据。这些数据显示在View中,所以我可以创建它的Collection并在特定的ViewModel中操作它。我把它们命名为模特。第二种模式-在形式,Easymodbus从PLC读取,它很容易浏览该数据。我把它们命名为数据模型。模型的例子。正如你所看到的,我实现了[ObservableProperty]属性,这样模型中的更改就可以传播到视图中:
DataModels是具有静态成员的静态类。EasyModbus给我数组类型的数据。下面是其中一个数据模型:
这是我的模特们。为了阅读Modbus,我创建了Modbus类。在这个类中,我有几个静态成员用于存储数据和几个方法。我不得不深入研究DNC编程,使其更有效。读取每个Modbus调用的dec没有太大意义,因为它是客户端服务器协议(我已经通过艰苦的方式学习了它),但我认为将数据分配给变量可以是dec和几个组。请参阅寄存器的最大数量,我可以在一个去读是125.所以我不得不把我的阅读分成几个小的。所以我决定利用这个“停机时间”通过将数据复制到DataModel来做一些工作。
要连接到PLC和启动定期读取数据,我有几个方法在主窗口CodeBehind。这将触发Modbus类创建Modbus客户端,连接到PLC并启动阅读PLC的后台循环。我意识到这种方法是不正确的,但我仍然在设置工作,能够改变IP和保存它。
作为触发视图更新状态的一部分,我在MainViewModel中设置了Timer(参见开头的代码)。对我来说就像蒙着眼睛走在黑暗的森林里。我不知道怎么回事。假设我使用回调函数而不是Timer.Tick事件。无论如何,它每1000毫秒调用一次UpdateModel方法。这里我只有ConductivityViewModel更新它的值。但这也适用于所有其他ViewModel。
希望这对任何处理modbus库或MVVM导航的人都有帮助。