From 1e3f12041013dd71a53d035dbf2f6d142fc06c4e Mon Sep 17 00:00:00 2001 From: jwinder-ca <105392648+jwinder-ca@users.noreply.github.com> Date: Wed, 15 Jun 2022 18:44:15 -0600 Subject: [PATCH] Actions cluster test support (#19487) * Added support for action clusters tests to the Linux bridge-app example. Commands were added to support the actions cluster test plan for renaming rooms, adding and removing rooms, and changing rooms so that they are not visible in the endpoint lists. * Whitespace updates. * Restyled by clang-format * Build fixes and removed unused capture. Co-authored-by: Restyled.io --- examples/bridge-app/linux/BUILD.gn | 1 + examples/bridge-app/linux/Device.cpp | 47 +++++-- .../bridge-app/linux/bridged-actions-stub.cpp | 21 +++- examples/bridge-app/linux/include/Device.h | 66 ++++++++-- examples/bridge-app/linux/include/main.h | 21 ++++ examples/bridge-app/linux/main.cpp | 117 +++++++++++++++++- 6 files changed, 252 insertions(+), 21 deletions(-) create mode 100644 examples/bridge-app/linux/include/main.h diff --git a/examples/bridge-app/linux/BUILD.gn b/examples/bridge-app/linux/BUILD.gn index 6860566c5bdba5..3912e887a846be 100644 --- a/examples/bridge-app/linux/BUILD.gn +++ b/examples/bridge-app/linux/BUILD.gn @@ -24,6 +24,7 @@ executable("chip-bridge-app") { "${chip_root}/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h", "Device.cpp", "include/Device.h", + "include/main.h", "main.cpp", ] diff --git a/examples/bridge-app/linux/Device.cpp b/examples/bridge-app/linux/Device.cpp index f726baf06c21c2..e2d2122a49ce25 100644 --- a/examples/bridge-app/linux/Device.cpp +++ b/examples/bridge-app/linux/Device.cpp @@ -22,12 +22,14 @@ #include #include +using namespace chip::app::Clusters::BridgedActions; + // LightingManager LightingManager::sLight; -Device::Device(const char * szDeviceName, const char * szLocation) +Device::Device(const char * szDeviceName, std::string szLocation) { strncpy(mName, szDeviceName, sizeof(mName)); - strncpy(mLocation, szLocation, sizeof(mLocation)); + mLocation = szLocation; mReachable = false; mEndpointId = 0; } @@ -72,13 +74,13 @@ void Device::SetName(const char * szName) } } -void Device::SetLocation(const char * szLocation) +void Device::SetLocation(std::string szLocation) { - bool changed = (strncmp(mLocation, szLocation, sizeof(mLocation)) != 0); + bool changed = (mLocation.compare(szLocation) != 0); - strncpy(mLocation, szLocation, sizeof(mLocation)); + mLocation = szLocation; - ChipLogProgress(DeviceLayer, "Device[%s]: Location=\"%s\"", mName, mLocation); + ChipLogProgress(DeviceLayer, "Device[%s]: Location=\"%s\"", mName, mLocation.c_str()); if (changed) { @@ -86,7 +88,7 @@ void Device::SetLocation(const char * szLocation) } } -DeviceOnOff::DeviceOnOff(const char * szDeviceName, const char * szLocation) : Device(szDeviceName, szLocation) +DeviceOnOff::DeviceOnOff(const char * szDeviceName, std::string szLocation) : Device(szDeviceName, szLocation) { mOn = false; } @@ -129,7 +131,7 @@ void DeviceOnOff::HandleDeviceChange(Device * device, Device::Changed_t changeMa } } -DeviceSwitch::DeviceSwitch(const char * szDeviceName, const char * szLocation, uint32_t aFeatureMap) : +DeviceSwitch::DeviceSwitch(const char * szDeviceName, std::string szLocation, uint32_t aFeatureMap) : Device(szDeviceName, szLocation) { mNumberOfPositions = 2; @@ -231,3 +233,32 @@ void DevicePowerSource::SetDescription(std::string aDescription) mChanged_CB(this, kChanged_Description); } } + +EndpointListInfo::EndpointListInfo(uint16_t endpointListId, std::string name, EndpointListTypeEnum type) +{ + mEndpointListId = endpointListId; + mName = name; + mType = type; +} + +EndpointListInfo::EndpointListInfo(uint16_t endpointListId, std::string name, EndpointListTypeEnum type, + chip::EndpointId endpointId) +{ + mEndpointListId = endpointListId; + mName = name; + mType = type; + mEndpoints.push_back(endpointId); +} + +void EndpointListInfo::AddEndpointId(chip::EndpointId endpointId) +{ + mEndpoints.push_back(endpointId); +} + +Room::Room(std::string name, uint16_t endpointListId, EndpointListTypeEnum type, bool isVisible) +{ + mName = name; + mEndpointListId = endpointListId; + mType = type; + mIsVisible = isVisible; +} diff --git a/examples/bridge-app/linux/bridged-actions-stub.cpp b/examples/bridge-app/linux/bridged-actions-stub.cpp index bade30c1c102cb..21712982dea9f7 100644 --- a/examples/bridge-app/linux/bridged-actions-stub.cpp +++ b/examples/bridge-app/linux/bridged-actions-stub.cpp @@ -24,6 +24,11 @@ #include #include +#include + +#include "Device.h" +#include "main.h" + using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; @@ -58,8 +63,20 @@ CHIP_ERROR BridgedActionsAttrAccess::ReadActionListAttribute(EndpointId endpoint CHIP_ERROR BridgedActionsAttrAccess::ReadEndpointListAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder) { - // Just return an empty list - return aEncoder.EncodeEmptyList(); + std::vector infoList = GetEndpointListInfo(endpoint); + + CHIP_ERROR err = aEncoder.EncodeList([&infoList](const auto & encoder) -> CHIP_ERROR { + for (auto info : infoList) + { + BridgedActions::Structs::EndpointListStruct::Type endpointListStruct = { + info.GetEndpointListId(), CharSpan::fromCharString(info.GetName().c_str()), info.GetType(), + DataModel::List(info.GetEndpointListData(), info.GetEndpointListSize()) + }; + ReturnErrorOnFailure(encoder.Encode(endpointListStruct)); + } + return CHIP_NO_ERROR; + }); + return err; } CHIP_ERROR BridgedActionsAttrAccess::ReadSetupUrlAttribute(EndpointId endpoint, AttributeValueEncoder & aEncoder) diff --git a/examples/bridge-app/linux/include/Device.h b/examples/bridge-app/linux/include/Device.h index 3bc7a88a47b6ae..b959454e7a8498 100644 --- a/examples/bridge-app/linux/include/Device.h +++ b/examples/bridge-app/linux/include/Device.h @@ -24,12 +24,15 @@ #include #include +#include + +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::BridgedActions; class Device { public: - static const int kDeviceNameSize = 32; - static const int kDeviceLocationSize = 32; + static const int kDeviceNameSize = 32; enum Changed_t { @@ -39,17 +42,21 @@ class Device kChanged_Last = kChanged_Name, } Changed; - Device(const char * szDeviceName, const char * szLocation); + Device(const char * szDeviceName, std::string szLocation); virtual ~Device() {} bool IsReachable(); void SetReachable(bool aReachable); void SetName(const char * szDeviceName); - void SetLocation(const char * szLocation); + void SetLocation(std::string szLocation); inline void SetEndpointId(chip::EndpointId id) { mEndpointId = id; }; inline chip::EndpointId GetEndpointId() { return mEndpointId; }; + inline void SetParentEndpointId(chip::EndpointId id) { mParentEndpointId = id; }; + inline chip::EndpointId GetParentEndpointId() { return mParentEndpointId; }; inline char * GetName() { return mName; }; - inline char * GetLocation() { return mLocation; }; + inline std::string GetLocation() { return mLocation; }; + inline std::string GetZone() { return mZone; }; + inline void SetZone(std::string zone) { mZone = zone; }; private: virtual void HandleDeviceChange(Device * device, Device::Changed_t changeMask) = 0; @@ -57,8 +64,10 @@ class Device protected: bool mReachable; char mName[kDeviceNameSize]; - char mLocation[kDeviceLocationSize]; + std::string mLocation; chip::EndpointId mEndpointId; + chip::EndpointId mParentEndpointId; + std::string mZone; }; class DeviceOnOff : public Device @@ -69,7 +78,7 @@ class DeviceOnOff : public Device kChanged_OnOff = kChanged_Last << 1, } Changed; - DeviceOnOff(const char * szDeviceName, const char * szLocation); + DeviceOnOff(const char * szDeviceName, std::string szLocation); bool IsOn(); void SetOnOff(bool aOn); @@ -96,7 +105,7 @@ class DeviceSwitch : public Device kChanged_MultiPressMax = kChanged_Last << 3, } Changed; - DeviceSwitch(const char * szDeviceName, const char * szLocation, uint32_t aFeatureMap); + DeviceSwitch(const char * szDeviceName, std::string szLocation, uint32_t aFeatureMap); void SetNumberOfPositions(uint8_t aNumberOfPositions); void SetCurrentPosition(uint8_t aCurrentPosition); @@ -124,7 +133,7 @@ class DeviceSwitch : public Device class ComposedDevice : public Device { public: - ComposedDevice(const char * szDeviceName, const char * szLocation) : Device(szDeviceName, szLocation){}; + ComposedDevice(const char * szDeviceName, std::string szLocation) : Device(szDeviceName, szLocation){}; using DeviceCallback_fn = std::function; @@ -146,7 +155,7 @@ class DevicePowerSource : public Device kChanged_Description = kChanged_Last << 2, } Changed; - DevicePowerSource(const char * szDeviceName, const char * szLocation, uint32_t aFeatureMap) : + DevicePowerSource(const char * szDeviceName, std::string szLocation, uint32_t aFeatureMap) : Device(szDeviceName, szLocation), mFeatureMap(aFeatureMap){}; using DeviceCallback_fn = std::function; @@ -172,3 +181,40 @@ class DevicePowerSource : public Device uint32_t mFeatureMap; DeviceCallback_fn mChanged_CB; }; + +class EndpointListInfo +{ +public: + EndpointListInfo(uint16_t endpointListId, std::string name, EndpointListTypeEnum type); + EndpointListInfo(uint16_t endpointListId, std::string name, EndpointListTypeEnum type, chip::EndpointId endpointId); + void AddEndpointId(chip::EndpointId endpointId); + inline uint16_t GetEndpointListId() { return mEndpointListId; }; + std::string GetName() { return mName; }; + inline EndpointListTypeEnum GetType() { return mType; }; + inline chip::EndpointId * GetEndpointListData() { return mEndpoints.data(); }; + inline size_t GetEndpointListSize() { return mEndpoints.size(); }; + +private: + uint16_t mEndpointListId = static_cast(0); + std::string mName; + EndpointListTypeEnum mType = static_cast(0); + std::vector mEndpoints; +}; + +class Room +{ +public: + Room(std::string name, uint16_t endpointListId, EndpointListTypeEnum type, bool isVisible); + inline void setIsVisible(bool isVisible) { mIsVisible = isVisible; }; + inline bool getIsVisible() { return mIsVisible; }; + inline void setName(std::string name) { mName = name; }; + inline std::string getName() { return mName; }; + inline EndpointListTypeEnum getType() { return mType; }; + inline uint16_t getEndpointListId() { return mEndpointListId; }; + +private: + bool mIsVisible; + std::string mName; + uint16_t mEndpointListId; + EndpointListTypeEnum mType; +}; diff --git a/examples/bridge-app/linux/include/main.h b/examples/bridge-app/linux/include/main.h new file mode 100644 index 00000000000000..fe81d30685f7ec --- /dev/null +++ b/examples/bridge-app/linux/include/main.h @@ -0,0 +1,21 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +std::vector GetEndpointListInfo(chip::EndpointId parentId); diff --git a/examples/bridge-app/linux/main.cpp b/examples/bridge-app/linux/main.cpp index 5709352d28b21e..d069d8ce7a0caf 100644 --- a/examples/bridge-app/linux/main.cpp +++ b/examples/bridge-app/linux/main.cpp @@ -44,16 +44,19 @@ #include "CommissionableInit.h" #include "Device.h" +#include "main.h" #include #include #include +#include using namespace chip; using namespace chip::Credentials; using namespace chip::Inet; using namespace chip::Transport; using namespace chip::DeviceLayer; +using namespace chip::app::Clusters; namespace { @@ -64,6 +67,7 @@ const int kDescriptorAttributeArraySize = 254; EndpointId gCurrentEndpointId; EndpointId gFirstDynamicEndpointId; Device * gDevices[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT]; +std::vector gRooms; // ENDPOINT DEFINITIONS: // ================================================================================= @@ -149,6 +153,21 @@ DeviceSwitch Switch2("Switch 2", "Office", EMBER_AF_SWITCH_FEATURE_MOMENTARY_SWITCH_LONG_PRESS | EMBER_AF_SWITCH_FEATURE_MOMENTARY_SWITCH_MULTI_PRESS); +// Declare Bridged endpoints used for Action clusters +DataVersion gActionLight1DataVersions[ArraySize(bridgedLightClusters)]; +DataVersion gActionLight2DataVersions[ArraySize(bridgedLightClusters)]; +DataVersion gActionLight3DataVersions[ArraySize(bridgedLightClusters)]; +DataVersion gActionLight4DataVersions[ArraySize(bridgedLightClusters)]; + +DeviceOnOff ActionLight1("Action Light 1", "Room 1"); +DeviceOnOff ActionLight2("Action Light 2", "Room 1"); +DeviceOnOff ActionLight3("Action Light 3", "Room 2"); +DeviceOnOff ActionLight4("Action Light 4", "Room 2"); + +Room room1("Room 1", 0xE001, BridgedActions::EndpointListTypeEnum::kRoom, true); +Room room2("Room 2", 0xE002, BridgedActions::EndpointListTypeEnum::kRoom, true); +Room room3("Zone 3", 0xE003, BridgedActions::EndpointListTypeEnum::kZone, false); + // --------------------------------------------------------------------------- // // SWITCH ENDPOINT: contains the following clusters: @@ -255,6 +274,7 @@ int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, const SpanSetEndpointId(gCurrentEndpointId); + dev->SetParentEndpointId(parentEndpointId); ret = emberAfSetDynamicEndpoint(index, gCurrentEndpointId, ep, dataVersionStorage, deviceTypeList, parentEndpointId); if (ret == EMBER_ZCL_STATUS_SUCCESS) @@ -300,6 +320,46 @@ int RemoveDeviceEndpoint(Device * dev) return -1; } +std::vector GetEndpointListInfo(chip::EndpointId parentId) +{ + std::vector infoList; + + for (auto room : gRooms) + { + if (room->getIsVisible()) + { + EndpointListInfo info(room->getEndpointListId(), room->getName(), room->getType()); + int index = 0; + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + if ((gDevices[index] != nullptr) && (gDevices[index]->GetParentEndpointId() == parentId)) + { + std::string location; + if (room->getType() == BridgedActions::EndpointListTypeEnum::kZone) + { + location = gDevices[index]->GetZone(); + } + else + { + location = gDevices[index]->GetLocation(); + } + if (room->getName().compare(location) == 0) + { + info.AddEndpointId(gDevices[index]->GetEndpointId()); + } + } + index++; + } + if (info.GetEndpointListSize() > 0) + { + infoList.push_back(info); + } + } + } + + return infoList; +} + void HandleDeviceStatusChanged(Device * dev, Device::Changed_t itemChangedMask) { if (itemChangedMask & Device::kChanged_Reachable) @@ -614,25 +674,31 @@ void * bridge_polling_thread(void * context) if (kbhit()) { int ch = getchar(); + + // Commands used for the actions bridge test plan. if (ch == '2' && light2_added == false) { + // TC-BR-2 step 2, Add Light2 AddDeviceEndpoint(&Light2, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), Span(gLight2DataVersions), 1); light2_added = true; } else if (ch == '4' && light1_added == true) { + // TC-BR-2 step 4, Remove Light 1 RemoveDeviceEndpoint(&Light1); light1_added = false; } if (ch == '5' && light1_added == false) { + // TC-BR-2 step 5, Add Light 1 back AddDeviceEndpoint(&Light1, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), Span(gLight1DataVersions), 1); light1_added = true; } if (ch == 'b') { + // TC-BR-3 step 1b, rename lights if (light1_added) { Light1.SetName("Light 1b"); @@ -644,6 +710,7 @@ void * bridge_polling_thread(void * context) } if (ch == 'c') { + // TC-BR-3 step 2c, change the state of the lights if (light1_added) { Light1.Toggle(); @@ -653,6 +720,31 @@ void * bridge_polling_thread(void * context) Light2.Toggle(); } } + + // Commands used for the actions cluster test plan. + if (ch == 'r') + { + // TC-ACT-2.2 step 2c, rename "Room 1" + room1.setName("Room 1 renamed"); + ActionLight1.SetLocation(room1.getName()); + ActionLight2.SetLocation(room1.getName()); + } + if (ch == 'f') + { + // TC-ACT-2.2 step 2f, move "Action Light 3" from "Room 2" to "Room 1" + ActionLight3.SetLocation(room1.getName()); + } + if (ch == 'i') + { + // TC-ACT-2.2 step 2i, remove "Room 2" (make it not visible in the endpoint list), do not remove the lights + room2.setIsVisible(false); + } + if (ch == 'l') + { + // TC-ACT-2.2 step 2l, add a new "Zone 3" and add "Action Light 2" to the new zone + room3.setIsVisible(true); + ActionLight2.SetZone("Zone 3"); + } continue; } @@ -669,7 +761,6 @@ int main(int argc, char * argv[]) memset(gDevices, 0, sizeof(gDevices)); // Setup Mock Devices - Light1.SetChangeCallback(&HandleDeviceOnOffStatusChanged); Light2.SetChangeCallback(&HandleDeviceOnOffStatusChanged); @@ -682,6 +773,17 @@ int main(int argc, char * argv[]) Switch1.SetReachable(true); Switch2.SetReachable(true); + // Setup devices for action cluster tests + ActionLight1.SetChangeCallback(&HandleDeviceOnOffStatusChanged); + ActionLight2.SetChangeCallback(&HandleDeviceOnOffStatusChanged); + ActionLight3.SetChangeCallback(&HandleDeviceOnOffStatusChanged); + ActionLight4.SetChangeCallback(&HandleDeviceOnOffStatusChanged); + + ActionLight1.SetReachable(true); + ActionLight2.SetReachable(true); + ActionLight3.SetReachable(true); + ActionLight4.SetReachable(true); + // Define composed device with two switches ComposedDevice ComposedDevice("Composed Switcher", "Bedroom"); DeviceSwitch ComposedSwitch1("Composed Switch 1", "Bedroom", EMBER_AF_SWITCH_FEATURE_LATCHING_SWITCH); @@ -752,6 +854,19 @@ int main(int argc, char * argv[]) Span(gComposedPowerSourceDeviceTypes), Span(gComposedPowerSourceDataVersions), ComposedDevice.GetEndpointId()); + // Add 4 lights for the Action Clusters tests + AddDeviceEndpoint(&ActionLight1, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), + Span(gActionLight1DataVersions), 1); + AddDeviceEndpoint(&ActionLight2, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), + Span(gActionLight2DataVersions), 1); + AddDeviceEndpoint(&ActionLight3, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), + Span(gActionLight3DataVersions), 1); + AddDeviceEndpoint(&ActionLight4, &bridgedLightEndpoint, Span(gBridgedOnOffDeviceTypes), + Span(gActionLight4DataVersions), 1); + gRooms.push_back(&room1); + gRooms.push_back(&room2); + gRooms.push_back(&room3); + { pthread_t poll_thread; int res = pthread_create(&poll_thread, nullptr, bridge_polling_thread, nullptr);