-
Notifications
You must be signed in to change notification settings - Fork 943
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AttributeError: 'AsyncModbusTcpClient' object has no attribute 'loop' #1578
Comments
This happens if there are no active asyncio loop, when you make an instance of AsyncModbus*. In short the function / method where you create an object of AsyncModbus* must be async. It is not detected as and error until you call connect (the first async call). |
I am thinking of a fix for v3.3.1, but I am not sure it is 100%. |
The code is unchanged and worked with the last version. This has been reported by a user. I will upgrade my dev machine and test it if you want but the code hasn't been chaged since days and it worked for several users. This function is the first to be called: asyncio is used: def run(self, queue):
self.queue = queue
logging.getLogger('asyncio').setLevel(logging.WARNING)
self.loop = asyncio.get_event_loop()
if self.new_config is not None:
self.apply_new_config()
if self.eqConfig['eqRefreshMode'] == 'polling':
self.loop.run_until_complete(self.run_polling())
elif self.eqConfig['eqRefreshMode'] == 'cyclic':
self.loop.run_until_complete(self.run_cyclic())
elif self.eqConfig['eqRefreshMode'] == 'on_event':
self.loop.run_until_complete(self.run_one())
self.loop.close()
self.shutdown() Here is the shortest called function: async def run_one(self):
self.connected = False
while not self.should_stop.is_set():
# for time measuring
t_begin = time.time()
self.check_queue()
await self.execute_write_requests(True, self.connected)
if self.new_config is not None:
self.apply_new_config()
if self.read_cmd.is_set():
self.read_cmd.clear()
# Connect
if not self.connected or not self.client.connected:
self.connected = await self.connect()
await self.read_all()
# The loop has exited
self.connected = await self.disconnect()
self.cycle_times[self.cycle % 5] = time.time() - t_begin |
With the fix you can instantiate without an async loop. Clearly there are differences between v3.2.2 and v3.3.0 just look at the changelog, it includes a brand new transport layer for async clients. So it is natural that you need to do some adaptations in your program, if you do not want to do that then do not upgrade :-) |
Your code "def run" is NOT async, you create a new loop with get_event_loop(), to test if you have a running loop use get_running_loop(). In both version there are no "client = AsyncModbusTcp(...)", this is the call that must happen in an async function (unless you apply my fix). |
The run function is called as a multiprocess.Process (one Process per device), so the loop has been created into the Process, I get the running loop in the called run() function: import multiprocessing as mp
...
pymodbus_client = PyModbusClient(eqConfig, self.jcom, jeedom_utils.convert_log_level(self._log_level))
queue = mp.Queue()
process = mp.Process(target=pymodbus_client.run, args=(queue, ), name=eqConfig['name'], daemon=True)
process.start() |
multiprocessing is not the same as asyncio. With version 3.3 you need to have an active asyncio loop when creating the client. get_running_loop() creates a loop but does not activate it. Anyhow you have a fix for your situation. |
Versions
Pymodbus Specific
Description
When calling client.open(), an exception is raised.
Code and Logs
Traceback, when I don't catch the exception:
The text was updated successfully, but these errors were encountered: