From 1b6b28b4da75224ece416e61b894e28d49de7ea1 Mon Sep 17 00:00:00 2001 From: Manuel Pietschmann Date: Wed, 25 Dec 2024 18:35:36 +0100 Subject: [PATCH] Refactor poll scheduler legacy code vs. Device code Basically we have two sub systems fighting each other. Which often works but puts stress on the queues. This PR puts the DeviceTick scheduler in charge to also drive the legacy PollManager so that they don't block each other randomly. Further the legacy delayedFastEnddeviceProbe() function for joining sensors is simplified to not do ZDP querying of Active Endpoints, Node and Simple Descriptors, manufacturer name and modelid. This is already done by Device code. One drawback of this PR is that polling lights after group commands is more delayed for now, this will be speed up later on again. For lights which support reporting this isn't a problem. --- de_web_plugin.cpp | 410 +++++++++------------------------------- de_web_plugin_private.h | 1 - device.cpp | 17 +- device_tick.cpp | 61 +++++- ias_zone.cpp | 4 +- permitJoin.cpp | 10 - poll_manager.cpp | 7 + resource.cpp | 1 + resource.h | 1 + 9 files changed, 168 insertions(+), 344 deletions(-) diff --git a/de_web_plugin.cpp b/de_web_plugin.cpp index 1ae078f593..e838fec48f 100644 --- a/de_web_plugin.cpp +++ b/de_web_plugin.cpp @@ -45,6 +45,7 @@ #include "ui/device_widget.h" #include "gateway_scanner.h" #include "ias_ace.h" +#include "ias_zone.h" #include "json.h" #include "poll_control.h" #include "poll_manager.h" @@ -1533,84 +1534,6 @@ void DeRestPluginPrivate::apsdeDataConfirm(const deCONZ::ApsDataConfirm &conf) continue; } - if (conf.dstAddressMode() == deCONZ::ApsNwkAddress && - task.req.dstAddressMode() == deCONZ::ApsNwkAddress && - !isSameAddress(conf.dstAddress(), task.req.dstAddress())) - { - DBG_Printf(DBG_INFO, "warn APSDE-DATA.confirm: 0x%02X nwk mismatch\n", conf.id()); - //continue; - } - - QDateTime now = QDateTime::currentDateTime(); - - if (conf.status() != deCONZ::ApsSuccessStatus) - { - DBG_Printf(DBG_INFO, "0x%016llX error APSDE-DATA.confirm: 0x%02X on task\n", task.req.dstAddress().ext(), conf.status()); - } - else if (task.req.dstAddressMode() == deCONZ::ApsGroupAddress && - (task.req.clusterId() == ONOFF_CLUSTER_ID || - task.req.clusterId() == LEVEL_CLUSTER_ID || - task.req.clusterId() == COLOR_CLUSTER_ID)) - { - quint16 groupId = task.req.dstAddress().group(); - quint16 attrId = 0x0000; - if (task.req.clusterId() == COLOR_CLUSTER_ID) - { - attrId = 0x0003; // currentX - } - - for (LightNode &l : nodes) - { - if (gwPermitJoinDuration > 0) - { - break; - } - - if (!l.isAvailable() || - !l.lastRx().isValid() /*|| - l.manufacturerCode() == VENDOR_IKEA || - l.manufacturerCode() == VENDOR_OSRAM || - l.manufacturerCode() == VENDOR_OSRAM_STACK || - l.manufacturer().startsWith(QLatin1String("IKEA")) || - l.manufacturer().startsWith(QLatin1String("OSRAM"))*/) - { - continue; - } - - - // fast poll lights which don't support or have active ZCL reporting - const NodeValue &val = l.getZclValue(ONOFF_CLUSTER_ID, attrId); - if ((!val.timestampLastReport.isValid() || val.timestampLastReport.secsTo(now) > (60 * 5)) && - isLightNodeInGroup(&l, groupId)) - { - DBG_Printf(DBG_INFO_L2, "\t0x%016llX force poll\n", l.address().ext()); - queuePollNode(&l); - } - } - } - else if (task.lightNode && gwPermitJoinDuration == 0) - { - switch (task.taskType) - { - case TaskSendOnOffToggle: - case TaskSetLevel: - case TaskSetXyColor: - case TaskSetEnhancedHue: - case TaskSetSat: - case TaskSetColorTemperature: - case TaskSetHue: - case TaskSetHueAndSaturation: - case TaskIncColorTemperature: - { - DBG_Printf(DBG_INFO, "\t0x%016llX force poll (2)\n", task.lightNode->address().ext()); - queuePollNode(task.lightNode); - } - break; - default: - break; - } - } - if (DBG_IsEnabled(DBG_INFO_L2)) { DBG_Printf(DBG_INFO_L2, "Erase task req-id: %u, type: %d zcl seqno: %u send time %d, profileId: 0x%04X, clusterId: 0x%04X\n", @@ -3129,7 +3052,6 @@ void DeRestPluginPrivate::addLightNode(const deCONZ::Node *node) lightNode.setHandle(R_CreateResourceHandle(&lightNode, nodes.size())); nodes.push_back(lightNode); lightNode2 = &nodes.back(); - queuePollNode(lightNode2); device->addSubDevice(lightNode2); if (searchLightsState == SearchLightsActive || permitJoinFlag) @@ -3270,45 +3192,6 @@ void DeRestPluginPrivate::setLightNodeStaticCapabilities(LightNode *lightNode) } } -/*! Force polling if the node has updated simple descriptors in setup phase. - \param node - the base for the LightNode - - TODO(mpi): This function can likely be removed entirely, after testing. - */ -void DeRestPluginPrivate::updatedLightNodeEndpoint(const deCONZ::NodeEvent &event) -{ - if (DEV_TestManaged()) - { - return; - } - - if (!event.node()) - { - return; - } - - if (event.clusterId() != ZDP_SIMPLE_DESCRIPTOR_RSP_CLID) - { - return; - } - - for (LightNode &lightNode : nodes) - { - if (lightNode.address().ext() != event.node()->address().ext()) - { - continue; - } - - if (event.endpoint() != lightNode.haEndpoint().endpoint()) - { - continue; - } - - lightNode.rx(); - queuePollNode(&lightNode); - } -} - /*! Checks if a known node changed its reachable state changed. \param node - the base for the LightNode \return the related LightNode or 0 @@ -11937,7 +11820,6 @@ void DeRestPluginPrivate::nodeEvent(const deCONZ::NodeEvent &event) case deCONZ::NodeEvent::UpdatedSimpleDescriptor: { addLightNode(event.node()); - updatedLightNodeEndpoint(event); addSensorNode(event.node()); if (!event.node()) @@ -13170,11 +13052,6 @@ void DeRestPluginPrivate::handleSceneClusterIndication(const deCONZ::ApsDataIndi LightNode *lightNode = getLightNodeForId(ls->lid()); if (lightNode && lightNode->isAvailable() && lightNode->state() == LightNode::StateNormal) { - if (gwPermitJoinDuration == 0) - { - queuePollNode(lightNode); - } - bool changed = false; if (lightNode->hasColor()) { @@ -14038,8 +13915,8 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve SensorCandidate *sc = nullptr; { - std::vector::iterator i = searchSensorsCandidates.begin(); - std::vector::iterator end = searchSensorsCandidates.end(); + auto i = searchSensorsCandidates.begin(); + const auto end = searchSensorsCandidates.end(); for (; i != end; ++i) { @@ -14056,7 +13933,6 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve return; } -#if DECONZ_LIB_VERSION >= 0x010900 // when macPoll = true core will handle ZDP descriptor queries bool macPoll = event && event->event() == deCONZ::NodeEvent::NodeMacDataRequest; if (macPoll) @@ -14065,38 +13941,43 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve { return; } + } - DBG_Printf(DBG_INFO, "MAC poll fastEnddeviceProbe() 0x%016llX\n", sc->address.ext()); + Device *device = DEV_GetDevice(m_devices, fastProbeAddr.ext()); + if (!device) + { + return; + } + + // Device code already fetches ZDP descriptors and Basic cluster attributes (regardless of existing DDFs) + // proceed here only once this is done. + const QString manufacturer = device->item(RAttrManufacturerName)->toString(); + const QString modelId = device->item(RAttrModelId)->toString(); + + if (manufacturer.isEmpty() || modelId.isEmpty()) + { + // wait until Device fetches these + return; + } + + if (!isDeviceSupported(device->node(), modelId)) + { + return; + } + + if (device->managed()) + { + DBG_Printf(DBG_DDF, "DDF device %s / %s still listed in isDeviceSupported(), can potentially be removed!\n", qPrintable(manufacturer), qPrintable(modelId)); } -#else -// bool macPoll = false; -#endif { Sensor *sensor = getSensorNodeForAddress(sc->address); - const deCONZ::Node *node = sensor ? sensor->node() : nullptr; + const deCONZ::Node *node = device->node(); if (sensor && sensor->deletedState() != Sensor::StateNormal) { - DBG_Printf(DBG_INFO, "don't use deleted sensor and node 0x%016llX as candidate\n", sc->address.ext()); + DBG_Printf(DBG_INFO, "don't use deleted sensor " FMT_MAC " as candidate\n", FMT_MAC_CAST(sc->address.ext())); sensor = nullptr; - node = nullptr; - } - - if (!node) - { - int i = 0; - const deCONZ::Node *n; - - while (apsCtrl->getNode(i, &n) == 0) - { - if (fastProbeAddr.ext() == n->address().ext()) - { - node = n; - break; - } - i++; - } } if (!node) @@ -14106,97 +13987,10 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve if (sc->timeout.isValid() && sc->timeout.elapsed() < 9000) { - DBG_Printf(DBG_INFO, "wait response fastEnddeviceProbe() 0x%016llX, elapsed %d ms\n", sc->address.ext(), sc->timeout.elapsed()); - return; - } - - bool hasNodeDescriptor = false; - bool hasActiveEndpoints = false; - - for (auto const &ind : fastProbeIndications) - { - if (ind.clusterId() == ZDP_NODE_DESCRIPTOR_RSP_CLID) { hasNodeDescriptor = true; } - else if (ind.clusterId() == ZDP_ACTIVE_ENDPOINTS_RSP_CLID) { hasActiveEndpoints = true; } - } - - if (!hasNodeDescriptor) - { - if (DEV_TestManaged()) - { - return; // Device code handles this - } - - DBG_Printf(DBG_INFO, "[1] get node descriptor for 0x%016llx\n", sc->address.ext()); - - if (ZDP_NodeDescriptorReq(sc->address, apsCtrl)) - { - queryTime = queryTime.addSecs(5); - sc->timeout.restart(); - sc->waitIndicationClusterId = ZDP_NODE_DESCRIPTOR_RSP_CLID; - } - return; - } - - sc->endpoints = node->endpoints(); - - if (!hasActiveEndpoints) - { - if (DEV_TestManaged()) - { - return; // Device code handles this - } - - DBG_Printf(DBG_INFO, "[2] get active endpoints for 0x%016llx\n", sc->address.ext()); - - if (ZDP_ActiveEndpointsReq(sc->address, apsCtrl)) - { - queryTime = queryTime.addSecs(5); - sc->timeout.restart(); - sc->waitIndicationClusterId = ZDP_ACTIVE_ENDPOINTS_RSP_CLID; - } + DBG_Printf(DBG_INFO, "wait response fastEnddeviceProbe() " FMT_MAC ", elapsed %d ms\n", FMT_MAC_CAST(sc->address.ext()), (int)sc->timeout.elapsed()); return; } - // simple descriptor for endpoint 0x01 - { - quint8 ep = 0; - - for (size_t i = 0; i < node->endpoints().size(); i++) - { - ep = node->endpoints()[i]; // search - - for (const auto &sd : node->simpleDescriptors()) - { - if (sd.endpoint() == ep && sd.deviceId() != 0xffff) - { - ep = 0; - break; - } - } - - if (DEV_TestManaged()) - { - return; // Device code handles this - } - - if (ep) // fetch - { - DBG_Printf(DBG_INFO, "[3] get simple descriptor 0x%02X for 0x%016llx\n", ep, sc->address.ext()); - - if (ZDP_SimpleDescriptorReq(sc->address, ep, apsCtrl)) - { - queryTime = queryTime.addSecs(1); - sc->timeout.restart(); - sc->waitIndicationClusterId = ZDP_SIMPLE_DESCRIPTOR_RSP_CLID; - } - - return; - } - } - } - - QString manufacturer; - QString modelId; QString swBuildId; QString dateCode; quint16 iasZoneType = 0; @@ -14206,13 +14000,10 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve if (sensor) { - manufacturer = sensor->manufacturer(); - modelId = sensor->modelId(); swBuildId = sensor->swVersion(); } quint8 basicClusterEndpoint = 0; - std::vector unavailBasicAttr; for (const deCONZ::SimpleDescriptor &sd : node->simpleDescriptors()) { @@ -14227,39 +14018,41 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve basicClusterEndpoint = sd.endpoint(); } - if (!attr.isAvailable()) - { - unavailBasicAttr.push_back(attr.id()); - continue; - } - else if (attr.lastRead() <= 0 && attr.dataType() == deCONZ::ZclCharacterString && attr.toString().isEmpty()) + if (attr.isAvailable() && attr.lastRead() >= 0 && attr.dataType() == deCONZ::ZclCharacterString && attr.toString().isEmpty()) { // e.g. some devices return empty strings. // Check read timestamp to make sure the attribute is read at least once. - unavailBasicAttr.push_back(attr.id()); + if (attr.id() == 0x0006) { dateCodeAvailable = false; } + else if (attr.id() == 0x4000) { swBuildIdAvailable = false; } + continue; } - if (attr.id() == 0x0004 && manufacturer.isEmpty()) - { - manufacturer = attr.toString(); - } - else if (attr.id() == 0x0005 && modelId.isEmpty()) - { - modelId = attr.toString(); - } - else if (attr.id() == 0x0006 && dateCode.isEmpty()) + if (attr.id() == 0x0006) { - dateCode = attr.toString(); + dateCodeAvailable = attr.isAvailable(); + + if (dateCode.isEmpty()) + { + dateCode = attr.toString(); + } } - else if (attr.id() == 0x4000 && swBuildId.isEmpty()) + else if (attr.id() == 0x4000) { - swBuildId = attr.toString(); + swBuildIdAvailable = attr.isAvailable(); + if (swBuildId.isEmpty()) + { + swBuildId = attr.toString(); + } } } else if (cl.id() == IAS_ZONE_CLUSTER_ID) { - if (attr.id() == 0x0001 && attr.numericValue().u64 != 0) // Zone type + if (attr.id() == IAS_ZONE_TYPE && !attr.isAvailable()) + { + iasZoneType = 0xffff; // don't query twice + } + else if (attr.id() == IAS_ZONE_TYPE && attr.numericValue().u64 != 0) // Zone type { DBG_Assert(attr.numericValue().u64 <= UINT16_MAX); iasZoneType = static_cast(attr.numericValue().u64); @@ -14272,14 +14065,11 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve } } - swBuildIdAvailable = std::find(unavailBasicAttr.cbegin(), unavailBasicAttr.cend(), 0x4000) == unavailBasicAttr.cend(); - dateCodeAvailable = std::find(unavailBasicAttr.cbegin(), unavailBasicAttr.cend(), 0x0006) == unavailBasicAttr.cend(); - if ((sd.deviceId() == DEV_ID_IAS_ZONE || sd.deviceId() == DEV_ID_IAS_WARNING_DEVICE) && iasZoneType == 0) { deCONZ::ApsDataRequest apsReq; - DBG_Printf(DBG_INFO, "[3.1] get IAS Zone type for 0x%016llx\n", sc->address.ext()); + DBG_Printf(DBG_INFO, "[3.1] get IAS Zone type for " FMT_MAC "\n", FMT_MAC_CAST(sc->address.ext())); // ZDP Header apsReq.dstAddress() = sc->address; @@ -14302,7 +14092,7 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve QDataStream stream(&zclFrame.payload(), QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); - stream << (quint16)0x0001; // IAS Zone type + stream << (uint16_t)IAS_ZONE_TYPE; // IAS Zone type } { // ZCL frame @@ -14327,43 +14117,23 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve } // manufacturer, model id, sw build id - if (!sensor || modelId.isEmpty() || manufacturer.isEmpty() || (swBuildId.isEmpty() && dateCode.isEmpty() && (dateCodeAvailable || swBuildIdAvailable))) + // TODO(mpi): this branch should be removed since it only queries swBuildId and DateCode which should be done via DDFs + if (!sensor || (swBuildId.isEmpty() && dateCode.isEmpty() && (dateCodeAvailable || swBuildIdAvailable))) { - if (!modelId.isEmpty() && !isDeviceSupported(node, modelId)) - { - return; - } - if (basicClusterEndpoint == 0) { return; } - deCONZ::ApsDataRequest apsReq; - std::vector attributes; - - // ZDP Header - apsReq.dstAddress() = sc->address; - apsReq.setDstAddressMode(deCONZ::ApsNwkAddress); - apsReq.setDstEndpoint(basicClusterEndpoint); - apsReq.setSrcEndpoint(endpoint()); - apsReq.setProfileId(HA_PROFILE_ID); - apsReq.setRadius(0); - apsReq.setClusterId(BASIC_CLUSTER_ID); - //apsReq.setTxOptions(deCONZ::ApsTxAcknowledgedTransmission); - - deCONZ::ZclFrame zclFrame; - zclFrame.setSequenceNumber(zclSeq++); - zclFrame.setCommandId(deCONZ::ZclReadAttributesId); - zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand | - deCONZ::ZclFCDirectionClientToServer | - deCONZ::ZclFCDisableDefaultResponse); + std::vector attributes; bool skip = false; - if (thermostatClusterEndpoint > 0) // e.g. Eurotronic SPZB0001 thermostat - { } - else if (iasZoneType > 0) // IAS motion and contact sensors + if (device->managed()) + { + skip = true; // DDF handles extra Basic cluster attributes + } + else if (thermostatClusterEndpoint > 0) // e.g. Eurotronic SPZB0001 thermostat { } else if (modelId.startsWith(QLatin1String("lumi.")) && node->nodeDescriptor().manufacturerCode() != VENDOR_XIAOMI) // older Xiaomi devices { @@ -14383,16 +14153,6 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve // don't read these (Xiaomi, Trust, ...) // response is empty or no response at all } - else if (manufacturer.isEmpty()) - { - DBG_Printf(DBG_INFO, "[4.1] Get manufacturer code\n"); - attributes.push_back(0x0004); // manufacturer - } - else if (modelId.isEmpty()) - { - DBG_Printf(DBG_INFO, "[4.1] Get model ID\n"); - attributes.push_back(0x0005); // model id - } else if (swBuildId.isEmpty() && dateCode.isEmpty()) { if (!swBuildIdAvailable && dateCodeAvailable) @@ -14407,20 +14167,26 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve } } - { // filter for available basic cluster attributes - std::vector tmp = attributes; - attributes.clear(); - for (auto id: tmp) - { - if (std::find(unavailBasicAttr.begin(), unavailBasicAttr.end(), id) == unavailBasicAttr.end()) - { - attributes.push_back(id); - } - } - } - if (!attributes.empty()) { + deCONZ::ApsDataRequest apsReq; + // ZDP Header + apsReq.dstAddress() = sc->address; + apsReq.setDstAddressMode(deCONZ::ApsNwkAddress); + apsReq.setDstEndpoint(basicClusterEndpoint); + apsReq.setSrcEndpoint(endpoint()); + apsReq.setProfileId(HA_PROFILE_ID); + apsReq.setRadius(0); + apsReq.setClusterId(BASIC_CLUSTER_ID); + //apsReq.setTxOptions(deCONZ::ApsTxAcknowledgedTransmission); + + deCONZ::ZclFrame zclFrame; + zclFrame.setSequenceNumber(zclSeq++); + zclFrame.setCommandId(deCONZ::ZclReadAttributesId); + zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand | + deCONZ::ZclFCDirectionClientToServer | + deCONZ::ZclFCDisableDefaultResponse); + // payload QDataStream stream(&zclFrame.payload(), QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); @@ -14428,7 +14194,7 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve for (quint16 attrId : attributes) { stream << attrId; - DBG_Printf(DBG_INFO, "[4.2] get basic cluster attr 0x%04X for 0x%016llx\n", attrId, sc->address.ext()); + DBG_Printf(DBG_INFO, "[4.2] get basic cluster attr 0x%04X for " FMT_MAC "\n", attrId, FMT_MAC_CAST(sc->address.ext())); } { // ZCL frame @@ -14445,14 +14211,14 @@ void DeRestPluginPrivate::delayedFastEnddeviceProbe(const deCONZ::NodeEvent *eve sc->waitIndicationClusterId = apsReq.clusterId(); } } - else if (!sensor) + else if (!device->managed() && !sensor) { addSensorNode(node); } return; } - if (!sensor || searchSensorsState != SearchSensorsActive) + if (!sensor) { // do nothing } @@ -16906,6 +16672,7 @@ void DeRestPluginPrivate::pushSensorInfoToCore(Sensor *sensor) void DEV_PollLegacy(Device *device) { + auto count = plugin->pollNodes.size(); for (Resource *r : device->subDevices()) { RestNodeBase *restNode = dynamic_cast(r); @@ -16914,6 +16681,11 @@ void DEV_PollLegacy(Device *device) plugin->queuePollNode(restNode); } } + + if (count == plugin->pollNodes.size()) // nothing was queued + { + emit device->eventNotify(Event(device->prefix(), REventPollDone, 0, device->key())); + } } /*! Selects the next device to poll. diff --git a/de_web_plugin_private.h b/de_web_plugin_private.h index f4b6cd7831..7019981e63 100644 --- a/de_web_plugin_private.h +++ b/de_web_plugin_private.h @@ -1321,7 +1321,6 @@ public Q_SLOTS: void handleMacDataRequest(const deCONZ::NodeEvent &event); void addLightNode(const deCONZ::Node *node); void setLightNodeStaticCapabilities(LightNode *lightNode); - void updatedLightNodeEndpoint(const deCONZ::NodeEvent &event); void nodeZombieStateChanged(const deCONZ::Node *node); LightNode *updateLightNode(const deCONZ::NodeEvent &event); LightNode *getLightNodeForAddress(const deCONZ::Address &addr, quint8 endpoint = 0); diff --git a/device.cpp b/device.cpp index f56b61cd8f..0acf37662c 100644 --- a/device.cpp +++ b/device.cpp @@ -229,14 +229,10 @@ void DEV_InitStateHandler(Device *device, const Event &event) if (event.what() == REventStateEnter) { d->zdpResult = { }; + d->node = DEV_GetCoreNode(device->key()); // always get fresh pointer if ((event.deviceKey() & 0x00212E0000000000LLU) == 0x00212E0000000000LLU) { - if (!d->node) - { - d->node = DEV_GetCoreNode(device->key()); - } - if (d->node && d->node->isCoordinator()) { d->setState(DEV_DeadStateHandler); @@ -1951,6 +1947,15 @@ void DEV_PollIdleStateHandler(Device *device, const Event &event) d->setState(DEV_PollNextStateHandler, STATE_LEVEL_POLL); return; } + else + { + if (event.what() == REventPoll) + { + DBG_Printf(DBG_DEV, "DEV Poll Idle nothing to poll %s/" FMT_MAC "\n", event.resource(), FMT_MAC_CAST(event.deviceKey())); + // notify DeviceTick to proceed + DEV_EnqueueEvent(device, REventPollDone); + } + } } } @@ -1972,6 +1977,8 @@ void DEV_PollNextStateHandler(Device *device, const Event &event) if (d->pollItems.empty()) { d->setState(DEV_PollIdleStateHandler, STATE_LEVEL_POLL); + // notify DeviceTick to proceed + DEV_EnqueueEvent(device, REventPollDone); return; } diff --git a/device_tick.cpp b/device_tick.cpp index 5ba6be7f9d..6d4a7f5ce9 100644 --- a/device_tick.cpp +++ b/device_tick.cpp @@ -20,6 +20,7 @@ #define TICK_INTERVAL_JOIN 500 #define TICK_INTERVAL_IDLE 1000 #define TICK_INTERVAL_IDLE_OTAU 6000 +#define TICK_INTERVAL_POLL_TIMOUT 10000 extern int DEV_ApsQueueSize(); extern bool DEV_OtauBusy(); @@ -37,6 +38,7 @@ typedef void (*DT_StateHandler)(DeviceTickPrivate *d, const Event &event); static void DT_StateInit(DeviceTickPrivate *d, const Event &event); static void DT_StateJoin(DeviceTickPrivate *d, const Event &event); static void DT_StateIdle(DeviceTickPrivate *d, const Event &event); +static void DT_StatePoll(DeviceTickPrivate *d, const Event &event); class DeviceTickPrivate { @@ -48,6 +50,9 @@ class DeviceTickPrivate QTimer *timer = nullptr; size_t devIter = 0; const DeviceContainer *devices = nullptr; + // for logging + DeviceKey curDeviceKey = 0; + bool curDeviceManaged = false; }; /*! Constructor. @@ -120,31 +125,37 @@ static void DT_StateInit(DeviceTickPrivate *d, const Event &event) { if (event.resource() == RLocal && event.what() == REventStateTimeout) { - DBG_Printf(DBG_INFO, "DEV Tick.Init: booted after %lld seconds\n", DEV_TICK_BOOT_TIME); + DBG_Printf(DBG_DEV, "DEV Tick.Init: booted after %ld seconds\n", (long)DEV_TICK_BOOT_TIME); DT_SetState(d, DT_StateIdle); } } /*! Emits REventPoll to the next device in DT_StateIdle. */ -static void DT_PollNextIdleDevice(DeviceTickPrivate *d) +static bool DT_PollNextIdleDevice(DeviceTickPrivate *d) { const auto devCount = d->devices->size(); if (devCount == 0) { - return; + return false; } d->devIter %= devCount; const auto &device = d->devices->at(d->devIter); + d->devIter++; Q_ASSERT(device); + if (device->reachable()) { + d->curDeviceKey = device->key(); + d->curDeviceManaged = device->managed(); emit d->q->eventNotify(Event(device->prefix(), REventPoll, 0, device->key())); + return true; } - d->devIter++; + + return false; } /*! This state is active while Permit Join is disabled for normal idle operation. @@ -165,7 +176,11 @@ static void DT_StateIdle(DeviceTickPrivate *d, const Event &event) int timeout = DEV_OtauBusy() ? TICK_INTERVAL_IDLE_OTAU : TICK_INTERVAL_IDLE; if (DA_ApsUnconfirmedRequests() < 4) { - DT_PollNextIdleDevice(d); + if (DT_PollNextIdleDevice(d)) + { + DT_SetState(d, DT_StatePoll); + return; + } } DT_StartTimer(d, timeout); } @@ -180,6 +195,38 @@ static void DT_StateIdle(DeviceTickPrivate *d, const Event &event) } } +/*! Wait for poll state to finish either by timeout or device signaling that nothing needs to be polled. + */ +static void DT_StatePoll(DeviceTickPrivate *d, const Event &event) +{ + if (event.what() == REventPermitjoinEnabled) + { + DT_SetState(d, DT_StateJoin); + } + else if (event.resource() == RLocal) + { + if (event.what() == REventStateTimeout) + { + DT_SetState(d, DT_StateIdle); + } + else if (event.what() == REventStateEnter) + { + DBG_Printf(DBG_DEV, "DEV Tick: poll enter " FMT_MAC ", managed = %d\n", FMT_MAC_CAST(d->curDeviceKey), d->curDeviceManaged); + DT_StartTimer(d, TICK_INTERVAL_POLL_TIMOUT); + } + else if (event.what() == REventStateLeave) + { + DBG_Printf(DBG_DEV, "DEV Tick: poll leave " FMT_MAC "\n", FMT_MAC_CAST(d->curDeviceKey)); + DT_StopTimer(d); + } + } + else if (event.resource() == RDevices && event.what() == REventPollDone) + { + DBG_Printf(DBG_DEV, "DEV Tick: poll done " FMT_MAC "\n", FMT_MAC_CAST(d->curDeviceKey)); + DT_SetState(d, DT_StateIdle); + } +} + /*! Adds a joining device entry to the queue if not already present. */ static void DT_RegisterJoiningDevice(DeviceTickPrivate *d, DeviceKey deviceKey, quint8 macCapabilities) @@ -197,7 +244,7 @@ static void DT_RegisterJoiningDevice(DeviceTickPrivate *d, DeviceKey deviceKey, dev.deviceKey = deviceKey; dev.macCapabilities = macCapabilities; d->joinDevices.push_back(dev); - DBG_Printf(DBG_INFO, "DEV Tick: fast poll 0x%016llX, mac capabilities: 0x%02X\n", deviceKey, macCapabilities); + DBG_Printf(DBG_DEV, "DEV Tick: fast poll " FMT_MAC ", mac capabilities: 0x%02X\n", FMT_MAC_CAST(deviceKey), macCapabilities); } } @@ -233,7 +280,7 @@ static void DT_StateJoin(DeviceTickPrivate *d, const Event &event) } else if (event.what() == REventDeviceAnnounce) { - DBG_Printf(DBG_INFO, "DEV Tick.Join: %s\n", event.what()); + DBG_Printf(DBG_DEV, "DEV Tick.Join: %s\n", event.what()); DT_RegisterJoiningDevice(d, event.deviceKey(), static_cast(event.num())); } else if (event.resource() == RLocal) diff --git a/ias_zone.cpp b/ias_zone.cpp index c54e7cef94..4c72189c60 100644 --- a/ias_zone.cpp +++ b/ias_zone.cpp @@ -714,9 +714,9 @@ void DeRestPluginPrivate::checkIasEnrollmentStatus(Sensor *sensor) if (iasState == IAS_STATE_READ) { - DBG_Printf(DBG_IAS, "[IAS ZONE] - 0x%016llX Read IAS zone state and CIE address...\n", sensor->address().ext()); + DBG_Printf(DBG_IAS, "[IAS ZONE] - 0x%016llX Read IAS zone state, type and CIE address...\n", sensor->address().ext()); - if (readAttributes(sensor, sensor->fingerPrint().endpoint, IAS_ZONE_CLUSTER_ID, {IAS_ZONE_STATE, IAS_CIE_ADDRESS})) + if (readAttributes(sensor, sensor->fingerPrint().endpoint, IAS_ZONE_CLUSTER_ID, {IAS_ZONE_STATE, IAS_ZONE_TYPE, IAS_CIE_ADDRESS})) { queryTime = queryTime.addSecs(1); IAS_SetState(sensor, itemIasState, IAS_STATE_WAIT_READ); diff --git a/permitJoin.cpp b/permitJoin.cpp index bb4e4f4de1..c4180f8c5c 100644 --- a/permitJoin.cpp +++ b/permitJoin.cpp @@ -90,16 +90,6 @@ void DeRestPluginPrivate::permitJoinTimerFired() i++; } } - else if ((gwPermitJoinDuration % 15) == 0) // TODO bad this needs to go - { - for (LightNode &l : nodes) - { - if (l.isAvailable() && l.modelId().isEmpty()) - { - queuePollNode(&l); - } - } - } updateEtag(gwConfigEtag); // update Etag so that webApp can count down permitJoin duration } diff --git a/poll_manager.cpp b/poll_manager.cpp index 841b09dfff..aceab65db6 100644 --- a/poll_manager.cpp +++ b/poll_manager.cpp @@ -198,6 +198,13 @@ void PollManager::pollTimerFired() { pollState = StateIdle; timer->start(50); + + // Notify the DeviceTick manager that legacy code is done polling. + Device *device = DEV_GetDevice(plugin->m_devices, dstAddr.ext()); + if (device) + { + emit device->eventNotify(Event(device->prefix(), REventPollDone, 0, device->key())); + } emit done(); return; } diff --git a/resource.cpp b/resource.cpp index fc5ecdc97a..ab884646a2 100644 --- a/resource.cpp +++ b/resource.cpp @@ -40,6 +40,7 @@ const char *REventPermitjoinDisabled = "event/permit.join.disabled"; const char *REventPermitjoinEnabled = "event/permit.join.enabled"; const char *REventPermitjoinRunning = "event/permit.join.running"; const char *REventPoll = "event/poll"; +const char *REventPollDone = "event/poll.done"; const char *REventSimpleDescriptor = "event/simple.descriptor"; const char *REventStartTimer = "event/start.timer"; const char *REventStateEnter = "event/state.enter"; diff --git a/resource.h b/resource.h index 925c0489d7..b1642a0717 100644 --- a/resource.h +++ b/resource.h @@ -61,6 +61,7 @@ extern const char *REventPermitjoinEnabled; extern const char *REventPermitjoinDisabled; extern const char *REventPermitjoinRunning; extern const char *REventPoll; +extern const char *REventPollDone; extern const char *REventDDFReload; extern const char *REventDDFInitRequest; extern const char *REventDDFInitResponse;