From 17142102073e13ee0d968507aeea7bad7032aa5e Mon Sep 17 00:00:00 2001 From: Kamil Kasperczyk <66371704+kkasperczyk-no@users.noreply.github.com> Date: Wed, 13 Apr 2022 14:55:20 +0200 Subject: [PATCH] [nrfconnect] Added recovering CASE session for the light switch (#17315) There isn't CASE recovery mechanism, so after resetting lighting device, the light switch is not able to communicate with it, as it holds out of date CASE session. Added releasing CASE session in case invoke command failure due to timeout reason. After that new session is established and command retransmitted. CASE session is recovered only once per failure to prevent multiple tries in case comunnication is seriously broken. --- .../nrfconnect/main/AppTask.cpp | 1 - .../nrfconnect/main/BindingHandler.cpp | 56 ++++++++++++++++--- .../nrfconnect/main/LightSwitch.cpp | 6 +- .../nrfconnect/main/ShellCommands.cpp | 2 +- .../nrfconnect/main/include/BindingHandler.h | 20 +++++-- 5 files changed, 68 insertions(+), 17 deletions(-) diff --git a/examples/light-switch-app/nrfconnect/main/AppTask.cpp b/examples/light-switch-app/nrfconnect/main/AppTask.cpp index 9643dce1a8ef4b..f1d7d8b0064f38 100644 --- a/examples/light-switch-app/nrfconnect/main/AppTask.cpp +++ b/examples/light-switch-app/nrfconnect/main/AppTask.cpp @@ -18,7 +18,6 @@ #include "AppTask.h" #include "AppConfig.h" -#include "BindingHandler.h" #include "LEDWidget.h" #include "LightSwitch.h" #include "ThreadUtil.h" diff --git a/examples/light-switch-app/nrfconnect/main/BindingHandler.cpp b/examples/light-switch-app/nrfconnect/main/BindingHandler.cpp index c94801ed1730f7..54cce9924887fd 100644 --- a/examples/light-switch-app/nrfconnect/main/BindingHandler.cpp +++ b/examples/light-switch-app/nrfconnect/main/BindingHandler.cpp @@ -35,17 +35,54 @@ void BindingHandler::Init() DeviceLayer::PlatformMgr().ScheduleWork(InitInternal); } +void BindingHandler::OnInvokeCommandFailure(DeviceProxy * aDevice, BindingData & aBindingData, CHIP_ERROR aError) +{ + CHIP_ERROR error; + + if (aError == CHIP_ERROR_TIMEOUT && !BindingHandler::GetInstance().mCaseSessionRecovered) + { + LOG_INF("Response timeout for invoked command, trying to recover CASE session."); + if (!aDevice) + return; + + // Release current CASE session. + error = aDevice->Disconnect(); + + if (CHIP_NO_ERROR != error) + { + LOG_ERR("Disconnecting from CASE session failed due to: %" CHIP_ERROR_FORMAT, error.Format()); + return; + } + + // Set flag to not try recover session multiple times. + BindingHandler::GetInstance().mCaseSessionRecovered = true; + + // Establish new CASE session and retrasmit command that was not applied. + error = BindingManager::GetInstance().NotifyBoundClusterChanged(aBindingData.EndpointId, aBindingData.ClusterId, + static_cast(&aBindingData)); + } + else + { + LOG_ERR("Binding command was not applied! Reason: %" CHIP_ERROR_FORMAT, aError.Format()); + } +} + void BindingHandler::OnOffProcessCommand(CommandId aCommandId, const EmberBindingTableEntry & aBinding, DeviceProxy * aDevice, void * aContext) { - CHIP_ERROR ret = CHIP_NO_ERROR; + CHIP_ERROR ret = CHIP_NO_ERROR; + BindingData * data = reinterpret_cast(aContext); auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { LOG_DBG("Binding command applied successfully!"); + + // If session was recovered and communication works, reset flag to the initial state. + if (BindingHandler::GetInstance().mCaseSessionRecovered) + BindingHandler::GetInstance().mCaseSessionRecovered = false; }; - auto onFailure = [](CHIP_ERROR error) { - LOG_INF("Binding command was not applied! Reason: %" CHIP_ERROR_FORMAT, error.Format()); + auto onFailure = [aDevice, dataRef = *data](CHIP_ERROR aError) mutable { + BindingHandler::OnInvokeCommandFailure(aDevice, dataRef, aError); }; switch (aCommandId) @@ -105,12 +142,18 @@ void BindingHandler::OnOffProcessCommand(CommandId aCommandId, const EmberBindin void BindingHandler::LevelControlProcessCommand(CommandId aCommandId, const EmberBindingTableEntry & aBinding, DeviceProxy * aDevice, void * aContext) { + BindingData * data = reinterpret_cast(aContext); + auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { LOG_DBG("Binding command applied successfully!"); + + // If session was recovered and communication works, reset flag to the initial state. + if (BindingHandler::GetInstance().mCaseSessionRecovered) + BindingHandler::GetInstance().mCaseSessionRecovered = false; }; - auto onFailure = [](CHIP_ERROR error) { - LOG_INF("Binding command was not applied! Reason: %" CHIP_ERROR_FORMAT, error.Format()); + auto onFailure = [aDevice, dataRef = *data](CHIP_ERROR aError) mutable { + BindingHandler::OnInvokeCommandFailure(aDevice, dataRef, aError); }; CHIP_ERROR ret = CHIP_NO_ERROR; @@ -119,7 +162,6 @@ void BindingHandler::LevelControlProcessCommand(CommandId aCommandId, const Embe { case Clusters::LevelControl::Commands::MoveToLevel::Id: { Clusters::LevelControl::Commands::MoveToLevel::Type moveToLevelCommand; - BindingData * data = reinterpret_cast(aContext); moveToLevelCommand.level = data->Value; if (aDevice) { @@ -192,7 +234,7 @@ void BindingHandler::InitInternal(intptr_t aArg) } BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(LightSwitchChangedHandler); - PrintBindingTable(); + BindingHandler::GetInstance().PrintBindingTable(); } bool BindingHandler::IsGroupBound() diff --git a/examples/light-switch-app/nrfconnect/main/LightSwitch.cpp b/examples/light-switch-app/nrfconnect/main/LightSwitch.cpp index 275dcdceccf1f4..7420d8f5254feb 100644 --- a/examples/light-switch-app/nrfconnect/main/LightSwitch.cpp +++ b/examples/light-switch-app/nrfconnect/main/LightSwitch.cpp @@ -29,7 +29,7 @@ using namespace chip::app; void LightSwitch::Init(chip::EndpointId aLightSwitchEndpoint) { - BindingHandler::Init(); + BindingHandler::GetInstance().Init(); mLightSwitchEndpoint = aLightSwitchEndpoint; } @@ -55,7 +55,7 @@ void LightSwitch::InitiateActionSwitch(Action mAction) Platform::Delete(data); return; } - data->IsGroup = BindingHandler::IsGroupBound(); + data->IsGroup = BindingHandler::GetInstance().IsGroupBound(); DeviceLayer::PlatformMgr().ScheduleWork(BindingHandler::SwitchWorkerHandler, reinterpret_cast(data)); Platform::Delete(data); } @@ -77,7 +77,7 @@ void LightSwitch::DimmerChangeBrightness() sBrightness = 0; } data->Value = (uint8_t) sBrightness; - data->IsGroup = BindingHandler::IsGroupBound(); + data->IsGroup = BindingHandler::GetInstance().IsGroupBound(); DeviceLayer::PlatformMgr().ScheduleWork(BindingHandler::SwitchWorkerHandler, reinterpret_cast(data)); Platform::Delete(data); } diff --git a/examples/light-switch-app/nrfconnect/main/ShellCommands.cpp b/examples/light-switch-app/nrfconnect/main/ShellCommands.cpp index 4dc0a3db58258c..235b9f9aedb336 100644 --- a/examples/light-switch-app/nrfconnect/main/ShellCommands.cpp +++ b/examples/light-switch-app/nrfconnect/main/ShellCommands.cpp @@ -55,7 +55,7 @@ static CHIP_ERROR SwitchCommandHandler(int argc, char ** argv) static CHIP_ERROR TableCommandHelper(int argc, char ** argv) { - BindingHandler::PrintBindingTable(); + BindingHandler::GetInstance().PrintBindingTable(); return CHIP_NO_ERROR; } diff --git a/examples/light-switch-app/nrfconnect/main/include/BindingHandler.h b/examples/light-switch-app/nrfconnect/main/include/BindingHandler.h index 02aed9d7aedc49..fcd2d4fa8288b3 100644 --- a/examples/light-switch-app/nrfconnect/main/include/BindingHandler.h +++ b/examples/light-switch-app/nrfconnect/main/include/BindingHandler.h @@ -28,11 +28,6 @@ class BindingHandler { public: - static void Init(); - static void SwitchWorkerHandler(intptr_t); - static void PrintBindingTable(); - static bool IsGroupBound(); - struct BindingData { chip::EndpointId EndpointId; @@ -42,9 +37,24 @@ class BindingHandler bool IsGroup{ false }; }; + void Init(); + void PrintBindingTable(); + bool IsGroupBound(); + + static void SwitchWorkerHandler(intptr_t); + static void OnInvokeCommandFailure(chip::DeviceProxy * aDevice, BindingData & aBindingData, CHIP_ERROR aError); + + static BindingHandler & GetInstance() + { + static BindingHandler sBindingHandler; + return sBindingHandler; + } + private: static void OnOffProcessCommand(chip::CommandId, const EmberBindingTableEntry &, chip::DeviceProxy *, void *); static void LevelControlProcessCommand(chip::CommandId, const EmberBindingTableEntry &, chip::DeviceProxy *, void *); static void LightSwitchChangedHandler(const EmberBindingTableEntry &, chip::DeviceProxy *, void *); static void InitInternal(intptr_t); + + bool mCaseSessionRecovered = false; };