/* * * Copyright (c) 2020-2021 Project CHIP Authors * * 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. */ /** * @file * This file implements a chip-im-responder, for the * CHIP Interaction Data Model Protocol. * * Currently it provides simple command and read handler with sample cluster * */ #include "MockEvents.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace chip { namespace app { namespace { class TestTLVDataEncoder : public DataModel::EncodableToTLV { public: CHIP_ERROR EncodeTo(TLV::TLVWriter & writer, TLV::Tag tag) const override { TLV::TLVType outerType; ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outerType)); ReturnErrorOnFailure(writer.Put(chip::TLV::ContextTag(kTestFieldId1), kTestFieldValue1)); ReturnErrorOnFailure(writer.Put(chip::TLV::ContextTag(kTestFieldId2), kTestFieldValue2)); return writer.EndContainer(outerType); } }; } // namespace // TODO: // The overrides here do NOT provide a consistent data model view: // This should be overriden with a Mock data model if using direct ember and // CodegenDataModel OR a custom DataModel::Provider should be written // // We cannot just say "every attribut exist, every device on every endpoint exists, // every data version compare is the same etc.". // // The following override implementation need changing: // - ServerClusterCommandExists - should have a proper data mmodel // - ConcreteAttributePathExists - cannot say "Yes" on all paths when query for EP/Cluster would fail // - CheckEventSupportStatus - cannot say yes for invalid endpoints/clusters // - IsClusterDataVersionEqual returning true on everything is odd // - IsDeviceTypeOnEndpoint returning true on every value seems odd Protocols::InteractionModel::Status ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath) { // The Mock cluster catalog -- only have one command on one cluster on one endpoint. using Protocols::InteractionModel::Status; if (aCommandPath.mEndpointId != kTestEndpointId) { return Status::UnsupportedEndpoint; } if (aCommandPath.mClusterId != kTestClusterId) { return Status::UnsupportedCluster; } if (aCommandPath.mCommandId != kTestCommandId) { return Status::UnsupportedCommand; } return Status::Success; } void DispatchSingleClusterCommand(const ConcreteCommandPath & aRequestCommandPath, chip::TLV::TLVReader & aReader, CommandHandler * apCommandObj) { static bool statusCodeFlipper = false; if (ServerClusterCommandExists(aRequestCommandPath) != Protocols::InteractionModel::Status::Success) { return; } if (aReader.GetLength() != 0) { chip::TLV::Debug::Dump(aReader, TLVPrettyPrinter); } chip::app::ConcreteCommandPath path = { kTestEndpointId, // Endpoint kTestClusterId, // ClusterId kTestCommandId, // CommandId }; // Add command data here if (statusCodeFlipper) { printf("responder constructing status code in command"); apCommandObj->AddStatus(path, Protocols::InteractionModel::Status::Success); } else { printf("responder constructing command data in command"); TestTLVDataEncoder testData; apCommandObj->AddResponse(path, kTestCommandId, testData); } statusCodeFlipper = !statusCodeFlipper; } CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeEncodeState * apEncoderState) { return AttributeValueEncoder(aAttributeReports, aSubjectDescriptor, aPath, 0).Encode(kTestFieldValue1); } bool ConcreteAttributePathExists(const ConcreteAttributePath & aPath) { return true; } Protocols::InteractionModel::Status CheckEventSupportStatus(const ConcreteEventPath & aPath) { return Protocols::InteractionModel::Status::Success; } const EmberAfAttributeMetadata * GetAttributeMetadata(const ConcreteAttributePath & aConcreteClusterPath) { // Note: This test does not make use of the real attribute metadata. static EmberAfAttributeMetadata stub = { .defaultValue = EmberAfDefaultOrMinMaxAttributeValue(uint32_t(0)) }; return &stub; } CHIP_ERROR WriteSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteDataAttributePath & aPath, TLV::TLVReader & aReader, WriteHandler * apWriteHandler) { CHIP_ERROR err = CHIP_NO_ERROR; ConcreteDataAttributePath attributePath(2, 3, 4); err = apWriteHandler->AddStatus(attributePath, Protocols::InteractionModel::Status::Success); return err; } bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath, DataVersion aRequiredVersion) { return true; } bool IsDeviceTypeOnEndpoint(DeviceTypeId deviceType, EndpointId endpoint) { return false; } } // namespace app } // namespace chip namespace { chip::TransportMgr gTransportManager; LivenessEventGenerator gLivenessGenerator; uint8_t gDebugEventBuffer[2048]; uint8_t gInfoEventBuffer[2048]; uint8_t gCritEventBuffer[2048]; chip::app::CircularEventBuffer gCircularEventBuffer[3]; chip::MonotonicallyIncreasingCounter gEventCounter; CHIP_ERROR InitializeEventLogging(chip::Messaging::ExchangeManager * apMgr) { ReturnErrorOnFailure(gEventCounter.Init(0)); chip::app::LogStorageResources logStorageResources[] = { { &gDebugEventBuffer[0], sizeof(gDebugEventBuffer), chip::app::PriorityLevel::Debug }, { &gInfoEventBuffer[0], sizeof(gInfoEventBuffer), chip::app::PriorityLevel::Info }, { &gCritEventBuffer[0], sizeof(gCritEventBuffer), chip::app::PriorityLevel::Critical }, }; chip::app::EventManagement::CreateEventManagement(apMgr, ArraySize(logStorageResources), gCircularEventBuffer, logStorageResources, &gEventCounter); return CHIP_NO_ERROR; } } // namespace int main(int argc, char * argv[]) { CHIP_ERROR err = CHIP_NO_ERROR; chip::Transport::PeerAddress peer(chip::Transport::Type::kUndefined); const chip::FabricIndex gFabricIndex = 0; InitializeChip(); err = gTransportManager.Init(chip::Transport::UdpListenParameters(chip::DeviceLayer::UDPEndPointManager()) .SetAddressType(chip::Inet::IPAddressType::kIPv6)); SuccessOrExit(err); err = gSessionManager.Init(&chip::DeviceLayer::SystemLayer(), &gTransportManager, &gMessageCounterManager, &gStorage, &gFabricTable, gSessionKeystore); SuccessOrExit(err); err = gExchangeManager.Init(&gSessionManager); SuccessOrExit(err); err = gMessageCounterManager.Init(&gExchangeManager); SuccessOrExit(err); err = chip::app::InteractionModelEngine::GetInstance()->Init(&gExchangeManager, &gFabricTable, chip::app::reporting::GetDefaultReportScheduler()); SuccessOrExit(err); err = InitializeEventLogging(&gExchangeManager); SuccessOrExit(err); err = gSessionManager.InjectPaseSessionWithTestKey(gSession, 1, chip::kTestControllerNodeId, 1, gFabricIndex, peer, chip::CryptoContext::SessionRole::kResponder); SuccessOrExit(err); printf("Listening for IM requests...\n"); MockEventGenerator::GetInstance()->Init(&gExchangeManager, &gLivenessGenerator, 1000, true); chip::DeviceLayer::PlatformMgr().RunEventLoop(); exit: MockEventGenerator::GetInstance()->SetEventGeneratorStop(); if (err != CHIP_NO_ERROR) { printf("IM responder failed, err:%s\n", chip::ErrorStr(err)); exit(EXIT_FAILURE); } chip::app::InteractionModelEngine::GetInstance()->Shutdown(); gTransportManager.Close(); ShutdownChip(); return EXIT_SUCCESS; }