fix(v2c): set host address before connectDevice() in setupThing()

Bug: ModbusTcpMaster: device ':502' ConnectionError when adding a trydan
by manual IP (createMethod "user") or after nymead restart before the
NetworkDeviceMonitor completes its ARP scan.

Root cause: TrydanModbusTcpMaster was constructed with
monitor->networkDeviceInfo().address(), which is null at first
registerMonitor() — the monitor only populates its cache after an
asynchronous ARP/mDNS resolution. connectDevice() then ran against
QHostAddress() → ":502".

Fix:
- Read the initial IP from the stored address param (always set at
  discovery or user-entry) and use it as the fallback when the monitor
  has not yet resolved the address.
- Guard the networkDeviceInfoChanged callback: only call setHostAddress()
  when the new address is non-null (the monitor emits transient null info
  during its internal refresh cycles).

Order is now: create master with valid address → wire callbacks →
connectDevice(). The monitor update path remains for DHCP renewals.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Schurig 2026-06-12 17:13:37 +02:00
parent 2ac00e1330
commit d838bce331

View File

@ -68,13 +68,23 @@ void IntegrationPluginV2c::setupThing(ThingSetupInfo *info)
const quint16 port = static_cast<quint16>(thing->paramValue(trydanThingPortParamTypeId).toUInt());
const quint16 slaveId = 1; // V2C Trydan always uses unit ID 1
TrydanModbusTcpMaster *master = new TrydanModbusTcpMaster(
monitor->networkDeviceInfo().address(), port, slaveId, thing);
// Prefer the monitor's cached address (ARP/mDNS already resolved) but fall back
// to the stored address param. The monitor has a null address at first
// registerMonitor() when the device was added by IP (createMethod "user") or when
// nymead restarts before the monitor's ARP scan completes — connecting to a null
// QHostAddress would produce "device ':502'" and fail immediately.
QHostAddress address = monitor->networkDeviceInfo().address();
if (address.isNull())
address = QHostAddress(thing->paramValue(trydanThingAddressParamTypeId).toString());
// Update target IP if the device gets a new DHCP lease.
TrydanModbusTcpMaster *master = new TrydanModbusTcpMaster(address, port, slaveId, thing);
// Update target IP when DHCP renews. Guard against transient null info that
// the monitor can emit during its own internal refresh cycles.
connect(monitor, &NetworkDeviceMonitor::networkDeviceInfoChanged,
master, [master](const NetworkDeviceInfo &info) {
master->setHostAddress(info.address());
if (!info.address().isNull())
master->setHostAddress(info.address());
});
// Abort the setup if the info object is destroyed before we finish.