Создайте службу для извлечения данных из ПЛК с помощью библиотеки EasyModbusTCP.
Для начала: я не программист и начал изучать это всего несколько месяцев назад.
Я создал программу WPF, используя библиотеку EasyModbusTCP, которая работает! и даже удалось прочитать Modbus асинхронно, чтобы он не зависал.
Теперь есть необходимость расширить эту программу, чтобы вытащить из Modbus более 600 катушек и регистров. Попутно перехожу на паттерн MVVM с помощью CommunityToolkit.
Я не знаю и, честно говоря, озадачен тем, как создать сервис внутри ViewModel.
Приложение теперь слишком сложное, чтобы отображать полный код, поэтому я упрощу его, насколько это возможно. Мое представление — это UserControl, у которого есть ItemsControl с коллекцией другого UserControl в качестве ItemsSource.
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>
Код позади:
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 читает ПЛК и назначает статические поля в статических классах, все это выглядит следующим образом:
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;
}
}
В целом приложение выглядит так