1. Обязательно представиться на русском языке кириллицей (заполнить поле "Имя").
  2. Фиктивные имена мы не приветствуем. Ивановых и Пупкиных здесь уже достаточно.
  3. Не надо писать свой вопрос в первую попавшуюся тему - всегда лучше создать новую тему.
  4. За поиск, предложение и обсуждение пиратского ПО и средств взлома - бан без предупреждения. Непонятно? - Читать здесь.
  5. Рекламу и частные объявления "куплю/продам/есть халтура" мы не размещаем ни на каких условиях.
  6. Перед тем как что-то написать - читать здесь, а затем здесь и здесь.
  7. Не надо писать в ЛС администраторам свои технические вопросы. Администраторы форума отлично знают как работает форум, а не все-все контроллеры, о которых тут пишут.

Modbus для новичка, c#, mvvm, wpf

PLC, прочие контроллеры, промышленные компьютеры, операторские панели

Модераторы: Глоб.модераторы, специалисты по PLC

Ответить

Автор темы
smolkit
новенький
новенький
Сообщения: 1
Зарегистрирован: 18 авг 2024, 19:33
Имя: Саша
Страна: Россия
город/регион: Пермь, Пермский край

Modbus для новичка, c#, mvvm, wpf

Сообщение smolkit »

Добрый вечер! По ощущениям, прогуглил уже всё что только мог, но не смог решить проблему. Впервые вообще взаимодействую с железом, до этого занимался только высокоуровневыми штуками. Задача такая: есть плата, есть стенд для тестирования этой платы, стенд подключается через COM порт. Программа должна реализовывать некоторый алгоритм тестирования этой платы. Сейчас попробовал добавить во view кнопку, которая переключает состояние части стенда 0/1 и наоборот (вкл-выкл, проще говоря, при этом стенд щелкает и я получаю хотя бы какой-то фидбек по тому, что я делаю). Столкнулся со следующей ошибкой - failed to connect: Access to Port COM3 is denied. При этом компорт не занят вообще никакими другими ресурсами, все запускается от имени администратора и все остальные методы решения, которые я нашёл мне не помогают. Предварительно написал консольную прогу, которая не могла читать состояние, но могла его изменять. При этом выдавала либо таймаут эксшепшен, либо Access to the path 63 is denied, пофиксить так же не смог, но стенд хотя бы щелкал при вводе 0/1. Помогите мне пожалуйста. Я буду очень рад если вы объясните что я делаю не так и поможете понять как это все вообще должно работать.

using System;
using System.IO.Ports;
using System.Windows.Input;
using Modbus.Device;
using System.ComponentModel;

namespace RPStesting
{
public class MainViewModel : INotifyPropertyChanged
{
private double _temperature;
public double Temperature
{
get { return _temperature; }
set
{
_temperature = value;
OnPropertyChanged(nameof(Temperature));
}
}

private IModbusSerialMaster _modbusMaster;
private SerialPort _serialPort;
private bool _isConnected; // текущее состояние подключен или нет
private ushort _currentRegisterValue;

// Команда для переключения значения регистра
public ICommand ToggleRegisterCommand { get; }

// Сообщение о состоянии
public string StatusMessage { get; private set; }

// Конструктор ViewModel, где инициализируется команда и соединение с устройством
public MainViewModel()
{
ToggleRegisterCommand = new RelayCommand(ToggleRegister, CanToggleRegister);
_isConnected = false; // По умолчанию соединение закрыто
InitializeModbusConnection(); // Инициализация соединения Modbus
}

//соединения с устройством
public void InitializeModbusConnection()
{
_serialPort = new SerialPort("COM3")
{
BaudRate = 4800,
Parity = Parity.Even,
DataBits = 8,
StopBits = StopBits.One,
ReadTimeout = 1000
};

try
{
// Открываем COM-порт
_serialPort.Open();
StatusMessage = "Device connected";
// Создаем ModbusRTU мастер
_modbusMaster = ModbusSerialMaster.CreateRtu(_serialPort);
_modbusMaster.Transport.Retries = 3; // Устанавливаем количество попыток на 3

_isConnected = true; // Обновляем состояние подключения
StatusMessage = "Device connected"; // Устанавливаем сообщение о состоянии
}
catch (Exception ex)
{
// Если возникает ошибка, выводим сообщение об ошибке
StatusMessage = $"Failed to connect: {ex.Message}";
}

// Уведомляем UI об изменении StatusMessage
OnPropertyChanged(nameof(StatusMessage));
}

// Метод для проверки возможности переключения значения регистра
private bool CanToggleRegister(object parameter)
{
return _isConnected; // Можно переключать, только если соединение установлено
}

// Метод для переключения значения регистра
private void ToggleRegister(object parameter)
{
if (_modbusMaster == null || !_isConnected) return; // Если соединение не установлено, выходим

try
{
// Переключаем значение регистра (если 0, то ставим 1 и наоборот)
_currentRegisterValue = _currentRegisterValue == 0 ? (ushort)1 : (ushort)0;

// Записываем значение в Modbus-устройство
WriteToSlave(2, 1300, _currentRegisterValue);

// Обновляем статусное сообщение
StatusMessage = $"Register value toggled to {_currentRegisterValue}";
}
catch (Exception ex)
{
// Если возникает ошибка, выводим сообщение об ошибке
StatusMessage = $"Error toggling register: {ex.Message}";
}

// Уведомляем UI об изменении StatusMessage
OnPropertyChanged(nameof(StatusMessage));
}

// Метод для записи значения в Modbus-устройство
private void WriteToSlave(byte slaveId, ushort startAddress, ushort valueToWrite)
{
try
{
// Создаем массив значений и записываем в устройство
ushort[] values = { valueToWrite };
_modbusMaster.WriteMultipleRegisters(slaveId, startAddress, values);
}
catch (Exception ex)
{
// Если возникает ошибка, выбрасываем исключение с описанием ошибки
throw new Exception($"Write error: {ex.Message}");
}
}

// Реализация интерфейса INotifyPropertyChanged для уведомления UI об изменениях
public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

// Деструктор для закрытия и освобождения ресурсов SerialPort
~MainViewModel()
{
_serialPort?.Close();
_serialPort?.Dispose();
}
}

// Реализация RelayCommand для команд
public class RelayCommand : ICommand
{
private readonly Action<object> _execute; // для выполнения команды
private readonly Predicate<object> _canExecute; // для проверки возможности выполнения команды

public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}

public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}

public void Execute(object parameter)
{
_execute(parameter);
}

// Событие для уведомления об изменении состояния возможности выполнения команды
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
Ответить

Вернуться в «Средний уровень автоматизации (управляющий)»