Я пытаюсь протестировать часть modbus проекта python, который я сейчас реализую. У меня есть два устройства Modbus, которые я читаю, используя pymodbus и ascyio .
Цель этого теста - увидеть отклонение во времени чтения, когда добавляется больше потоков, которые читают данное устройство Modbus. Каждый поток будет читать только одно устройство Modbus, но, учитывая, что у меня есть только два устройства, некоторые потоки будут совместно использовать устройства. Скрипты потока выглядят примерно так, как показано ниже:
# IP address of the smart meter
meter_IP = "192.168.0.80"
async def main(meter):
file_name = "results/thread_two_result.csv"
with open(file_name, mode='a') as csv_file:
csv_writer = csv.writer(csv_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
print("Executing thread two event loop")
while True:
await meter.read_all(csv_writer)
loop = asyncio.get_event_loop()
loop, client = ModbusClient(schedulers.ASYNC_IO,host =meter_IP, loop=loop)
meter = smart_meter(meter_IP,client)
loop.create_task(main(meter))
loop.run_forever()
Класс для реализации части чтения Modbus содержится в классе smart_meter
, и единственная функция, которую я использую, - это функция read_all
, которая принимает входной файл CSV, в который записывается отметка времени при получении результата от Modbus. Каждый поток имеет свой собственный CSV-файл. Я читаю 12 регистров с этой функцией в одном запросе Modbus. Это показано во фрагменте кода ниже:
class smart_meter():
## __init__ ##
# Initialiase method to intatiated object
# Inputs:
# IP - The IP address of the smart meter
# client - A modbus client of type 'pymodbus.client.asynchronous.asyncio.ReconnectingAsyncioModbusTcpClient'
def __init__(self,IP,client):
#Create the client object passing the IP address of the slave to it
logger.info(f"New instance of smart_meter instantaited with IP of {IP}")
self.IP = IP
#self.client - 'pymodbus.client.asynchronous.asyncio.ModbusClientProtocol'
self.client = client.protocol
## read_all ##
# Reads the registers:
# V_2 - 0x1112
# I_2 - 0x1114
# kW_2 - 0x1116
# kvar_2 - 0x1118
# kVA_2 - 0x111A
# PF_2 - 0x111C
async def read_all(self,file):
logger.info(f"Attempting to read all registers for channel two")
try:
response = await self.client.read_input_registers(0x1112,12)
reg_value = dict.fromkeys(["Volts","Current","kW","kvar","kVA","PF"])
x,y = 0,2
for key in reg_value:
reg_value[key] = (BinaryPayloadDecoder.fromRegisters(response.registers[x:y],Endian.Big, wordorder=Endian.Little )).decode_32bit_float()
x+=2
y+=2
file.writerow([datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]])
except Exception as e:
logger.exception("Error in reading all regeisters for channel two")
Скрипт запуска для запуска этих потоков представляет собой скрипт оболочки, который выглядит следующим образом:
#!/bin/sh
python thread_one.py &
python thread_two.py &
python thread_three.py &
python thread_four.py &
python thread_five.py &
python thread_six.py &
python thread_seven.py &
python thread_eight.py &
У меня до восьми потоки, работающие в настоящее время с чтениями потоков 1,2,3 и 7 из meter_1 и чтениями потоков 2,4,6 и 8 из meter_2. Тест, в котором реализовано только 7 потоков, прошел успешно, однако тест с 8 потоками начинает проваливаться. Ответ от чтения Modbus начинает возвращать значение None
, трассировка от одного из потоков, захваченных в файле журнала, показана ниже:
2020-04-18 16:08:32,599:ERROR:4446:MainProcess:smart_meters.py:Error in reading all regeisters for channel two
Traceback (most recent call last):
File "/home/openhabian/Environments/env_1/openHAB_Proj/openHAB_Proj/smart_meters.py", line 77, in read_all
response = await self.client.read_input_registers(0x1112,12)
File "/home/openhabian/Environments/env_1/lib/python3.7/site-packages/pymodbus/client/common.py", line 125, in read_input_registers
return self.execute(request)
File "/home/openhabian/Environments/env_1/lib/python3.7/site-packages/pymodbus/client/asynchronous/asyncio/__init__.py", line 129, in execute
self.write_transport(packet)
File "/home/openhabian/Environments/env_1/lib/python3.7/site-packages/pymodbus/client/asynchronous/asyncio/__init__.py", line 119, in write_transport
return self.transport.write(packet)
AttributeError: 'NoneType' object has no attribute 'write'
Это не приводит к получению результатов для некоторые потоки, поскольку он уже ставит в очередь следующее чтение для этого потока, что снова приводит к возвращению типа None
. Я запускаю эти тесты на RasberryPi 3 .
Это то, что я просто достигаю пределов аппаратного / программного обеспечения? Если у кого-то есть предложения о том, как внедрить лучший тест, он будет рад.
РЕДАКТИРОВАТЬ
Файлы потоков в значительной степени одинаковы, единственное отличие состоит в том, что они считывают устройство Modbus и файл CSV, в котором сохраняются значения, в которых я тоже сохраняю переменная file_name
. Четные потоки (2,4,6,8) считываются с одного и того же устройства Modbus, ниже показан файл четных потоков для thread_eight.py
# IP address of the smart meter
meter_IP = "192.168.0.80"
async def main(meter):
file_name = "results/thread_eight_result.csv"
with open(file_name, mode='a') as csv_file:
csv_writer = csv.writer(csv_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
print("Executing thread eight event loop")
while True:
await meter.read_all(csv_writer)
loop = asyncio.get_event_loop()
loop, client = ModbusClient(schedulers.ASYNC_IO,host =meter_IP, loop=loop)
meter = smart_meter(meter_IP,client)
loop.create_task(main(meter))
loop.run_forever()
Файл нечетных потоков (1,3,5 7) все читается с одного устройства. Пример нечетного файла показан в приведенном ниже коде из файла thread_seven.py
# IP address of the smart meter
meter_IP = "192.168.0.116"
async def main(meter):
file_name = "results/thread_seven_result.csv"
with open(file_name, mode='a') as csv_file:
csv_writer = csv.writer(csv_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
print("Executing thread seven event loop")
while True:
await meter.read_all(csv_writer)
loop = asyncio.get_event_loop()
loop, client = ModbusClient(schedulers.ASYNC_IO,host =meter_IP, loop=loop)
meter = smart_meter(meter_IP,client)
loop.create_task(main(meter))
loop.run_forever()