commit 2fe1ddee70fe1eab08062fea8aa03134cc259670 Author: moxitech Date: Wed Oct 23 10:33:21 2024 +0700 emulator v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b694934 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.venv \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..3f6d760 --- /dev/null +++ b/Readme.md @@ -0,0 +1,35 @@ + + +#### Эмулятор rs-485 по modbus для linux + +0. Создаем и активируем виртуальное окружение +``` + python -m venv .venv +``` + +1. Перед запуском устанавливаем зависимости: +``` + pip install -r requirements.txt +``` + +2. Монтируем виртуальные порты: (смонтированы будут как /dev/pts/*, где * - номер) +``` + socat -d -d pty,raw,echo=0 pty,raw,echo=0 +``` + +3. Настраиваем эмулятор +``` + # Настройка последовательного соединения + ser = serial.Serial( + port='/dev/pts/5', # Используйте правильный порт из socat + baudrate=9600, # Скорость обмена + parity=serial.PARITY_NONE, # + stopbits=serial.STOPBITS_ONE, # Бит остановки + bytesize=serial.EIGHTBITS, # Размер сообщения + timeout=1 # Таймаут + ) +``` +4. Запуск: +``` + python main.py +``` \ No newline at end of file diff --git a/emulate.py b/emulate.py new file mode 100644 index 0000000..8edd261 --- /dev/null +++ b/emulate.py @@ -0,0 +1,84 @@ +import serial +import struct +import random +import time + +# Адрес Modbus-устройства (slave) +SLAVE_ADDRESS = 1 + +# Адрес регистра для расстояния +DISTANCE_REGISTER = 0x0001 + +# Настройка последовательного соединения +ser = serial.Serial( + port='/dev/pts/5', # Используйте правильный порт из socat + baudrate=9600, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS, + timeout=1 +) + +# Функция расчёта CRC16 +def _calculate_crc16(data): + crc = 0xFFFF + for byte in data: + crc ^= byte + for _ in range(8): + if crc & 0x0001: + crc = (crc >> 1) ^ 0xA001 + else: + crc >>= 1 + return crc + +# Конвертация float в формат IEEE 754 (binary32) +def float_to_ieee754(value): + return struct.pack('>f', value) + +# Эмуляция ультразвукового датчика - генерация случайного расстояния +def get_emulated_distance(): + return random.uniform(0.10, 4.00) # Возвращаем случайное расстояние от 0.10 м до 4.00 м + +# Функция обработки запроса Modbus +def handle_read_request(request): + function_code = request[1] + register_address = (request[2] << 8) | request[3] + + if function_code == 0x04 and register_address == DISTANCE_REGISTER: + distance_value = get_emulated_distance() + + # Конвертируем значение в формат IEEE 754 (4 байта) + data_bytes = float_to_ieee754(distance_value) + + # Формируем ответ: [адрес слейва, код функции, длина данных, данные...] + response_data = bytes([SLAVE_ADDRESS, function_code, len(data_bytes)]) + data_bytes + + # Рассчитываем CRC16 для ответа + crc16 = _calculate_crc16(response_data) + crc_bytes = struct.pack('= 9: + print(f'Response (hex): {response.hex()}') + + # Распарсим ответ + slave_address = response[0] # Адрес устройства + function_code = response[1] # Код функции + byte_count = response[2] # Количество байт данных + + # Получаем 4 байта данных (float в IEEE 754) + data_bytes = response[3:3+4] + crc_received = struct.unpack('f', data_bytes)[0] + + # Вывод распарсенной информации + print(f"Slave Address: {slave_address}") + print(f"Function Code: {function_code}") + print(f"Byte Count: {byte_count}") + print(f"Distance Value (meters): {distance_value:.2f}") + print(f"CRC Received: {hex(crc_received)}") + +else: + print("Incomplete response received") + +ser.close()