Доработка. Добавлен драйвер (json | udp) <-> (serial | modbus)
This commit is contained in:
105
modbus/client-serial-tcp.py
Normal file
105
modbus/client-serial-tcp.py
Normal file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Драйвер клиента для интерфейса Modbus работающего через последовательный порт.
|
||||
(приложение | UDP | JSON) <- (этот драйвер клиента) -> (сервер | Serial | Modbus RTU)
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import argparse
|
||||
import time
|
||||
from socket import *
|
||||
|
||||
import logger
|
||||
from pymodbus.client.sync import ModbusSerialClient
|
||||
|
||||
DEF_SERIAL = 'COM1' if os.name == 'nt' else '/dev/tty0'
|
||||
log_file_dir = os.path.join(os.path.expanduser('~'))
|
||||
log = logger.get_logger(__file__, log_file_dir)
|
||||
EXCEPT_MSGS = {
|
||||
'write': 'ошибка: запись в регистры не выполнена',
|
||||
'read_holding': 'ошибка: чтение регистров хранения не выполнено',
|
||||
'read_input': 'ошибка: чтение регистров входов не выполнено',
|
||||
'<unknown>': 'ошибка: получена неверная команда'
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument('-modbus-unit', default=1, type=int, help='Номер устройства Modbus')
|
||||
parser.add_argument('-serial-baud', default=115200, type=int, help='Скорость последовательного порта, бод')
|
||||
parser.add_argument('-serial-port', default=DEF_SERIAL, type=str, help='Последовательный порт для подключение к Modbus серверу')
|
||||
parser.add_argument('-udp-addr', default='localhost', type=str, help='Адрес для отправки сообщений приложению')
|
||||
parser.add_argument('-udp-port-rx', default=44100, type=int, help='Порт для приёма сообщений от приложения')
|
||||
parser.add_argument('-udp-port-tx', default=48000, type=int, help='Порт для отправки сообщений приложению')
|
||||
opts, opts_unk = parser.parse_known_args()
|
||||
print_opts_error(opts_unk)
|
||||
run_sync_client(opts.udp_addr, opts.udp_port_rx, opts.udp_port_tx, opts.serial_port, opts.serial_baud, opts.modbus_unit)
|
||||
|
||||
|
||||
def run_sync_client(udp_addr, udp_port_rx, udp_port_tx, serial_port, serial_baud, unit_num):
|
||||
sock = socket(AF_INET, SOCK_DGRAM)
|
||||
sock.bind((udp_addr, udp_port_rx))
|
||||
log.debug(f'привязан к адресу {udp_addr}:{udp_port_rx}')
|
||||
|
||||
json_decoder = json.JSONDecoder()
|
||||
json_encoder = json.JSONEncoder()
|
||||
|
||||
client = ModbusSerialClient(method='rtu', port=serial_port, baudrate=int(serial_baud))
|
||||
while not client.connect(): # Ждать подключения к серверу
|
||||
time.sleep(3)
|
||||
|
||||
log.debug(f'подключен к серверу через {serial_port} на скорости {serial_baud} бод')
|
||||
|
||||
while True:
|
||||
udp_data = sock.recv(2048) # Ждать команду от приложения
|
||||
udp_data = udp_data.decode('utf-8')
|
||||
message = json_decoder.decode(udp_data) # type: dict
|
||||
|
||||
if 'code' not in message:
|
||||
message['code'] = '<unknown>'
|
||||
|
||||
if 'data' not in message:
|
||||
message['data'] = []
|
||||
|
||||
rc = False
|
||||
if message['code'] == 'write':
|
||||
data = message['data']
|
||||
rq = client.write_registers(data[0], data[1:], unit=unit_num)
|
||||
rc = rq.isError()
|
||||
elif message['code'] == 'read_holding':
|
||||
data = message['data']
|
||||
rq = client.read_holding_registers(data[0], data[1:], unit=unit_num)
|
||||
rc = rq.isError()
|
||||
if not rc:
|
||||
tx_data = json_encoder.encode({'code': 'response', 'data': rq.registers})
|
||||
sock.sendto(bytes(tx_data), (udp_addr, udp_port_tx))
|
||||
elif message['code'] == 'read_input':
|
||||
data = message['data']
|
||||
rq = client.read_input_registers(data[0], data[1:], unit=unit_num)
|
||||
rc = rq.isError()
|
||||
if not rc:
|
||||
tx_data = json_encoder.encode({'code': 'response', 'data': rq.registers})
|
||||
sock.sendto(bytes(tx_data), (udp_addr, udp_port_tx))
|
||||
elif message['code'] == '<unknown>':
|
||||
rc = False
|
||||
|
||||
if rc:
|
||||
code = message['code']
|
||||
data = message['data']
|
||||
rc_msg = EXCEPT_MSGS[code]
|
||||
tx_data = json_encoder.encode({'code': 'exception', 'data': rc_msg})
|
||||
sock.sendto(bytes(tx_data), (udp_addr, udp_port_tx))
|
||||
log.debug(
|
||||
f'ошибка при выполнении команды: \"%s\". данные: %s, порт: \"%s\", скорость: %s'
|
||||
% (code, data, serial_port, serial_baud))
|
||||
|
||||
|
||||
def print_opts_error(opts: list) -> None:
|
||||
for opt in opts:
|
||||
log.debug('внимание: неизвестный аргумент: \"%s\"\n' % opt)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Драйвер клиента для интерфейса modbus
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
import logger
|
||||
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
|
||||
|
||||
|
||||
log = logger.get_logger(__file__)
|
||||
UNIT = 1
|
||||
|
||||
|
||||
def run_sync_client(ip_addr, ip_port):
|
||||
client = ModbusClient(ip_addr, port=int(ip_port))
|
||||
rc = client.connect()
|
||||
assert rc
|
||||
log.debug(f'Клиент подключился к серверу {ip_addr}:{ip_port}')
|
||||
|
||||
while client.is_socket_open():
|
||||
log.debug('Запись в несколько регистров хранения')
|
||||
rq = client.write_registers(1, [(1 << 4) | (1 << 2) | (1 << 0)], unit=UNIT)
|
||||
log.debug('Чтение из нескольких регистров хранения')
|
||||
rr = client.read_holding_registers(1, 1, unit=UNIT)
|
||||
assert not rq.isError()
|
||||
assert rr.registers == [(1 << 4) | (1 << 2) | (1 << 0)]
|
||||
log.debug('Успешно')
|
||||
time.sleep(1.0)
|
||||
|
||||
log.debug('Чтение из нескольких регистров хранения')
|
||||
rq = client.write_registers(1, [(1 << 4)], unit=UNIT)
|
||||
rr = client.read_holding_registers(1, 1, unit=UNIT)
|
||||
assert not rq.isError()
|
||||
log.debug('Запись в несколько регистров хранения')
|
||||
assert rr.registers == [(0 << 4) | (0 << 0)]
|
||||
log.debug('Успешно')
|
||||
time.sleep(1.0)
|
||||
|
||||
log.debug('Запись в несколько регистров хранения')
|
||||
rq = client.write_registers(2, [(1 << 4)], unit=UNIT)
|
||||
log.debug('Чтение из нескольких регистров хранения')
|
||||
rr = client.read_holding_registers(2, 1, unit=UNIT)
|
||||
assert not rq.isError()
|
||||
assert rr.registers == [(1 << 4)]
|
||||
log.debug('Успешно')
|
||||
time.sleep(1.0)
|
||||
|
||||
log.debug('Запись в несколько регистров хранения')
|
||||
rq = client.write_registers(5, [(1 << 4) | (1 << 2)], unit=UNIT)
|
||||
log.debug('Чтение из нескольких регистров хранения')
|
||||
rr = client.read_holding_registers(5, 1, unit=UNIT)
|
||||
assert not rq.isError()
|
||||
assert rr.registers == [(1 << 4) | (1 << 2)]
|
||||
log.debug('Успешно')
|
||||
time.sleep(1.0)
|
||||
|
||||
log.debug('Запись в несколько регистров хранения')
|
||||
rq = client.write_registers(3, [(1 << 2)], unit=UNIT)
|
||||
log.debug('Чтение из нескольких регистров хранения')
|
||||
rr = client.read_holding_registers(3, 1, unit=UNIT)
|
||||
assert not rq.isError()
|
||||
assert rr.registers == [(1 << 2)]
|
||||
log.debug('Успешно')
|
||||
time.sleep(1.0)
|
||||
|
||||
log.debug('Запись в несколько регистров хранения')
|
||||
rq = client.write_registers(6, [(1 << 4) | (1 << 5)], unit=UNIT)
|
||||
log.debug('Чтение из нескольких регистров хранения')
|
||||
rr = client.read_holding_registers(6, 1, unit=UNIT)
|
||||
assert not rq.isError()
|
||||
assert rr.registers == [(1 << 4) | (1 << 5)]
|
||||
log.debug('Успешно')
|
||||
time.sleep(1.0)
|
||||
|
||||
client.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
assert(len(sys.argv) == 3)
|
||||
run_sync_client(sys.argv[1], sys.argv[2])
|
||||
|
||||
60
modbus/start-driver.sh
Normal file
60
modbus/start-driver.sh
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SERIAL_PORT2="/home/$(whoami)/tty2" # Порт для подключения драйвера клиента
|
||||
SERIAL_PORT3="/home/$(whoami)/tty3" # Порт для подключения сервера Modbus
|
||||
UDP_ADDR="localhost"
|
||||
UDP_PORT_RX="44100"
|
||||
UDP_PORT_TX="48000"
|
||||
TEST_STR="${SERIAL_PORT2} ${SERIAL_PORT3} ${UDP_ADDR} ${UDP_PORT_RX} ${UDP_PORT_TX}"
|
||||
|
||||
PROMPT_COMMAND=
|
||||
echo -en "\033]0;Драйвер modbus для проверки\a"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
kill "${pid_serial}" >> /dev/null
|
||||
}
|
||||
|
||||
trap cleanup EXIT TERM
|
||||
|
||||
# /---------------------------------------------------------------------------------\
|
||||
# Создать порты, и дождаться их появления.
|
||||
# Нужно использовать xterm, т.к. с xfce4-terminal не работает.
|
||||
xterm -geometry 120x5 -fa Monospace -fs 12 -hold -T "Последовательные порты" -e "socat -d -d pty,link=${SERIAL_PORT2},rawer,echo=0 pty,link=${SERIAL_PORT3},rawer,echo=0" &
|
||||
pid_serial="$!"
|
||||
while [ "$(inotifywait -q --format "%f" -e create "${SERIAL_PORT2%/*}")" != "${SERIAL_PORT2##*/}" ]; do ""; done
|
||||
# \---------------------------------------------------------------------------------/
|
||||
|
||||
|
||||
# /---------------------------------------------------------------------------------\
|
||||
# Проверить, что связь между портами исправна
|
||||
exec 4<"${SERIAL_PORT2}"
|
||||
exec 5>"${SERIAL_PORT3}"
|
||||
echo "${TEST_STR}" >&5
|
||||
read -r reply <&4
|
||||
exec 4<&-
|
||||
exec 5<&-
|
||||
if [[ "${reply}" != "${TEST_STR}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec 4<"${SERIAL_PORT2}"
|
||||
exec 5>"${SERIAL_PORT3}"
|
||||
echo "${TEST_STR}" >&5
|
||||
read -r reply <&4
|
||||
exec 4<&-
|
||||
exec 5<&-
|
||||
if [[ "${reply}" != "${TEST_STR}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
# \---------------------------------------------------------------------------------/
|
||||
|
||||
|
||||
# /---------------------------------------------------------------------------------\
|
||||
# Запустить драйвер клиента Modbus
|
||||
xterm -geometry 120x5 -fa Monospace -fs 12 -hold -T "Драйвер клиента Modbus Serial \"${SERIAL_PORT2} ${UDP_ADDR}:${UDP_PORT_RX}\"" -e "python3 client-serial-tcp.py -serial-port ${SERIAL_PORT2} -udp-addr ${UDP_ADDR} -udp-port-rx ${UDP_PORT_RX} -udp-port-tx ${UDP_PORT_TX}" &
|
||||
# \---------------------------------------------------------------------------------/
|
||||
|
||||
wait ${pid_serial}
|
||||
|
||||
exit 0
|
||||
@@ -31,7 +31,7 @@ interfer/default_color0=Vector3(0.38, 0.47, 0.51)
|
||||
interfer/default_color1=Vector3(0.39, 0.44, 0.5)
|
||||
interfer/select_color=Vector3(1, 1, 1)
|
||||
interfer/interfer_color=Vector3(0.58, 0.47, 0.51)
|
||||
config/mod_bus_driver="client-tcp.py"
|
||||
config/mod_bus_driver="client-serial-tcp.py"
|
||||
config/mod_bus_driver_port="48000"
|
||||
config/mod_bus_driver_address="localhost"
|
||||
config/mod_bus_interpreter="python"
|
||||
|
||||
Reference in New Issue
Block a user