From d6ae36c91ee6484e44b602a75bc5b7bf0a466943 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Mon, 23 Jan 2023 20:36:25 +0100 Subject: [PATCH] [chip-tool] Add DataModelLogger::LogJson command and makes it goes over websockets in interactive server mode (#24387) --- examples/chip-tool/BUILD.gn | 1 + .../commands/clusters/ClusterCommand.h | 6 + .../commands/clusters/DataModelLogger.h | 130 ++++++++++++++ .../commands/clusters/ReportCommand.h | 6 + .../commands/clusters/WriteAttributeCommand.h | 4 + .../interactive/InteractiveCommands.cpp | 170 +++++++++++++++++- .../interactive/InteractiveCommands.h | 8 +- .../commands/interactive/WebSocketServer.cpp | 24 +++ .../commands/interactive/WebSocketServer.h | 1 + .../logging/DataModelLogger-src.zapt | 2 + .../tv-casting-app/tv-casting-common/BUILD.gn | 1 + .../cluster/logging/DataModelLogger.cpp | 2 + 12 files changed, 349 insertions(+), 6 deletions(-) diff --git a/examples/chip-tool/BUILD.gn b/examples/chip-tool/BUILD.gn index 1ca148ceeb1246..aea4fe99ff02e2 100644 --- a/examples/chip-tool/BUILD.gn +++ b/examples/chip-tool/BUILD.gn @@ -110,6 +110,7 @@ static_library("chip-tool-utils") { "${chip_root}/src/controller/data_model", "${chip_root}/src/credentials:file_attestation_trust_store", "${chip_root}/src/lib", + "${chip_root}/src/lib/support/jsontlv", "${chip_root}/src/platform", "${chip_root}/third_party/inipp", "${chip_root}/third_party/jsoncpp", diff --git a/examples/chip-tool/commands/clusters/ClusterCommand.h b/examples/chip-tool/commands/clusters/ClusterCommand.h index c2562843e024a8..4ea55a2786df6d 100644 --- a/examples/chip-tool/commands/clusters/ClusterCommand.h +++ b/examples/chip-tool/commands/clusters/ClusterCommand.h @@ -74,6 +74,8 @@ class ClusterCommand : public InteractionModelCommands, public ModelCommand, pub CHIP_ERROR error = status.ToChipError(); if (CHIP_NO_ERROR != error) { + ReturnOnFailure(DataModelLogger::LogErrorAsJSON(path, status)); + ChipLogError(chipTool, "Response Failure: %s", chip::ErrorStr(error)); mError = error; return; @@ -81,6 +83,8 @@ class ClusterCommand : public InteractionModelCommands, public ModelCommand, pub if (data != nullptr) { + ReturnOnFailure(DataModelLogger::LogCommandAsJSON(path, data)); + error = DataModelLogger::LogCommand(path, data); if (CHIP_NO_ERROR != error) { @@ -93,6 +97,8 @@ class ClusterCommand : public InteractionModelCommands, public ModelCommand, pub virtual void OnError(const chip::app::CommandSender * client, CHIP_ERROR error) override { + ReturnOnFailure(DataModelLogger::LogErrorAsJSON(error)); + ChipLogProgress(chipTool, "Error: %s", chip::ErrorStr(error)); mError = error; } diff --git a/examples/chip-tool/commands/clusters/DataModelLogger.h b/examples/chip-tool/commands/clusters/DataModelLogger.h index 38a2fb1812f3f4..5c8e01cdecba00 100644 --- a/examples/chip-tool/commands/clusters/DataModelLogger.h +++ b/examples/chip-tool/commands/clusters/DataModelLogger.h @@ -24,8 +24,25 @@ #include #include #include +#include #include #include +#include + +constexpr const char * kClusterIdKey = "clusterId"; +constexpr const char * kEndpointIdKey = "endpointId"; +constexpr const char * kAttributeIdKey = "attributeId"; +constexpr const char * kEventIdKey = "eventId"; +constexpr const char * kCommandIdKey = "commandId"; +constexpr const char * kErrorIdKey = "error"; +constexpr const char * kClusterErrorIdKey = "clusterError"; + +class DataModelLoggerJSONDelegate +{ +public: + CHIP_ERROR virtual LogJSON(const char *) = 0; + virtual ~DataModelLoggerJSONDelegate(){}; +}; class DataModelLogger { @@ -34,7 +51,119 @@ class DataModelLogger static CHIP_ERROR LogCommand(const chip::app::ConcreteCommandPath & path, chip::TLV::TLVReader * data); static CHIP_ERROR LogEvent(const chip::app::EventHeader & header, chip::TLV::TLVReader * data); + static CHIP_ERROR LogAttributeAsJSON(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data) + { + VerifyOrReturnError(mJSONDelegate != nullptr, CHIP_NO_ERROR); + + Json::Value value; + value[kClusterIdKey] = path.mClusterId; + value[kEndpointIdKey] = path.mEndpointId; + value[kAttributeIdKey] = path.mAttributeId; + + chip::TLV::TLVReader reader; + reader.Init(*data); + ReturnErrorOnFailure(chip::TlvToJson(reader, value)); + + auto valueStr = chip::JsonToString(value); + return mJSONDelegate->LogJSON(valueStr.c_str()); + } + + static CHIP_ERROR LogErrorAsJSON(const chip::app::ConcreteDataAttributePath & path, const chip::app::StatusIB & status) + { + VerifyOrReturnError(mJSONDelegate != nullptr, CHIP_NO_ERROR); + + Json::Value value; + value[kClusterIdKey] = path.mClusterId; + value[kEndpointIdKey] = path.mEndpointId; + value[kAttributeIdKey] = path.mAttributeId; + + return LogError(value, status); + } + + static CHIP_ERROR LogCommandAsJSON(const chip::app::ConcreteCommandPath & path, chip::TLV::TLVReader * data) + { + VerifyOrReturnError(mJSONDelegate != nullptr, CHIP_NO_ERROR); + + Json::Value value; + value[kClusterIdKey] = path.mClusterId; + value[kEndpointIdKey] = path.mEndpointId; + value[kCommandIdKey] = path.mCommandId; + + chip::TLV::TLVReader reader; + reader.Init(*data); + ReturnErrorOnFailure(chip::TlvToJson(reader, value)); + + auto valueStr = chip::JsonToString(value); + return mJSONDelegate->LogJSON(valueStr.c_str()); + } + + static CHIP_ERROR LogErrorAsJSON(const chip::app::ConcreteCommandPath & path, const chip::app::StatusIB & status) + { + VerifyOrReturnError(mJSONDelegate != nullptr, CHIP_NO_ERROR); + + Json::Value value; + value[kClusterIdKey] = path.mClusterId; + value[kEndpointIdKey] = path.mEndpointId; + value[kCommandIdKey] = path.mCommandId; + + return LogError(value, status); + } + + static CHIP_ERROR LogEventAsJSON(const chip::app::EventHeader & header, chip::TLV::TLVReader * data) + { + VerifyOrReturnError(mJSONDelegate != nullptr, CHIP_NO_ERROR); + + Json::Value value; + value[kClusterIdKey] = header.mPath.mClusterId; + value[kEndpointIdKey] = header.mPath.mEndpointId; + value[kEventIdKey] = header.mPath.mEventId; + + chip::TLV::TLVReader reader; + reader.Init(*data); + ReturnErrorOnFailure(chip::TlvToJson(reader, value)); + + auto valueStr = chip::JsonToString(value); + return mJSONDelegate->LogJSON(valueStr.c_str()); + } + + static CHIP_ERROR LogErrorAsJSON(const chip::app::EventHeader & header, const chip::app::StatusIB & status) + { + VerifyOrReturnError(mJSONDelegate != nullptr, CHIP_NO_ERROR); + + Json::Value value; + value[kClusterIdKey] = header.mPath.mClusterId; + value[kEndpointIdKey] = header.mPath.mEndpointId; + value[kEventIdKey] = header.mPath.mEventId; + + return LogError(value, status); + } + + static CHIP_ERROR LogErrorAsJSON(const CHIP_ERROR & error) + { + Json::Value value; + chip::app::StatusIB status; + status.InitFromChipError(error); + return LogError(value, status); + } + + static void SetJSONDelegate(DataModelLoggerJSONDelegate * delegate) { mJSONDelegate = delegate; } + private: + static CHIP_ERROR LogError(Json::Value & value, const chip::app::StatusIB & status) + { + if (status.mClusterStatus.HasValue()) + { + auto statusValue = status.mClusterStatus.Value(); + value[kClusterErrorIdKey] = statusValue; + } + + auto statusName = chip::Protocols::InteractionModel::StatusName(status.mStatus); + value[kErrorIdKey] = statusName; + + auto valueStr = chip::JsonToString(value); + return mJSONDelegate->LogJSON(valueStr.c_str()); + } + static CHIP_ERROR LogValue(const char * label, size_t indent, bool value) { DataModelLogger::LogString(label, indent, value ? "TRUE" : "FALSE"); @@ -194,4 +323,5 @@ class DataModelLogger } static size_t ComputePrefixSize(const std::string label, size_t indent) { return ComputePrefix(label, indent).size(); } + static DataModelLoggerJSONDelegate * mJSONDelegate; }; diff --git a/examples/chip-tool/commands/clusters/ReportCommand.h b/examples/chip-tool/commands/clusters/ReportCommand.h index eae5fa8ee51054..e4177f87a02bf8 100644 --- a/examples/chip-tool/commands/clusters/ReportCommand.h +++ b/examples/chip-tool/commands/clusters/ReportCommand.h @@ -37,6 +37,8 @@ class ReportCommand : public InteractionModelReports, public ModelCommand, publi CHIP_ERROR error = status.ToChipError(); if (CHIP_NO_ERROR != error) { + ReturnOnFailure(DataModelLogger::LogErrorAsJSON(path, status)); + ChipLogError(chipTool, "Response Failure: %s", chip::ErrorStr(error)); mError = error; return; @@ -49,6 +51,8 @@ class ReportCommand : public InteractionModelReports, public ModelCommand, publi return; } + ReturnOnFailure(DataModelLogger::LogAttributeAsJSON(path, data)); + error = DataModelLogger::LogAttribute(path, data); if (CHIP_NO_ERROR != error) { @@ -66,6 +70,8 @@ class ReportCommand : public InteractionModelReports, public ModelCommand, publi CHIP_ERROR error = status->ToChipError(); if (CHIP_NO_ERROR != error) { + ReturnOnFailure(DataModelLogger::LogErrorAsJSON(eventHeader, *status)); + ChipLogError(chipTool, "Response Failure: %s", chip::ErrorStr(error)); mError = error; return; diff --git a/examples/chip-tool/commands/clusters/WriteAttributeCommand.h b/examples/chip-tool/commands/clusters/WriteAttributeCommand.h index 9b5e8f3ef70728..f3f9233a854815 100644 --- a/examples/chip-tool/commands/clusters/WriteAttributeCommand.h +++ b/examples/chip-tool/commands/clusters/WriteAttributeCommand.h @@ -109,6 +109,8 @@ class WriteAttribute : public InteractionModelWriter, public ModelCommand, publi CHIP_ERROR error = status.ToChipError(); if (CHIP_NO_ERROR != error) { + ReturnOnFailure(DataModelLogger::LogErrorAsJSON(path, status)); + ChipLogError(chipTool, "Response Failure: %s", chip::ErrorStr(error)); mError = error; } @@ -116,6 +118,8 @@ class WriteAttribute : public InteractionModelWriter, public ModelCommand, publi void OnError(const chip::app::WriteClient * client, CHIP_ERROR error) override { + ReturnOnFailure(DataModelLogger::LogErrorAsJSON(error)); + ChipLogProgress(chipTool, "Error: %s", chip::ErrorStr(error)); mError = error; } diff --git a/examples/chip-tool/commands/interactive/InteractiveCommands.cpp b/examples/chip-tool/commands/interactive/InteractiveCommands.cpp index 73b7017352a104..4bcbc5c28e4565 100644 --- a/examples/chip-tool/commands/interactive/InteractiveCommands.cpp +++ b/examples/chip-tool/commands/interactive/InteractiveCommands.cpp @@ -25,6 +25,9 @@ constexpr const char * kInteractiveModePrompt = ">>> "; constexpr const char * kInteractiveModeHistoryFilePath = "/tmp/chip_tool_history"; constexpr const char * kInteractiveModeStopCommand = "quit()"; +constexpr const char * kCategoryError = "Error"; +constexpr const char * kCategoryProgress = "Info"; +constexpr const char * kCategoryDetail = "Debug"; namespace { @@ -40,6 +43,133 @@ void ENFORCE_FORMAT(3, 0) LoggingCallback(const char * module, uint8_t category, ClearLine(); } +struct InteractiveServerResultLog +{ + std::string module; + std::string message; + std::string messageType; +}; + +struct InteractiveServerResult +{ + bool mEnabled = false; + bool mIsAsyncReport = false; + int mStatus = EXIT_SUCCESS; + std::vector mResults; + std::vector mLogs; + + void Setup(bool isAsyncReport) + { + mEnabled = true; + mIsAsyncReport = isAsyncReport; + } + + void Reset() + { + mEnabled = false; + mIsAsyncReport = false; + mStatus = EXIT_SUCCESS; + mResults.clear(); + mLogs.clear(); + } + + bool IsAsyncReport() const { return mIsAsyncReport; } + + void MaybeAddLog(const char * module, uint8_t category, const char * base64Message) + { + VerifyOrReturn(mEnabled); + + const char * messageType = nullptr; + switch (category) + { + case chip::Logging::kLogCategory_Error: + messageType = kCategoryError; + break; + case chip::Logging::kLogCategory_Progress: + messageType = kCategoryProgress; + break; + case chip::Logging::kLogCategory_Detail: + messageType = kCategoryDetail; + break; + } + + mLogs.push_back(InteractiveServerResultLog({ module, base64Message, messageType })); + } + + void MaybeAddResult(const char * result) + { + VerifyOrReturn(mEnabled); + mResults.push_back(result); + } + + std::string AsJsonString() const + { + std::string resultsStr; + if (mResults.size()) + { + for (const auto & result : mResults) + { + resultsStr = resultsStr + result + ","; + } + + // Remove last comma. + resultsStr.pop_back(); + } + + if (mStatus != EXIT_SUCCESS) + { + if (resultsStr.size()) + { + resultsStr = resultsStr + ","; + } + resultsStr = resultsStr + "{ \"error\": \"FAILURE\" }"; + } + + std::string logsStr; + if (mLogs.size()) + { + for (const auto & log : mLogs) + { + logsStr = logsStr + "{"; + logsStr = logsStr + " \"module\": \"" + log.module + "\","; + logsStr = logsStr + " \"category\": \"" + log.messageType + "\","; + logsStr = logsStr + " \"message\": \"" + log.message + "\""; + logsStr = logsStr + "},"; + } + + // Remove last comma. + logsStr.pop_back(); + } + + std::string jsonLog; + jsonLog = jsonLog + "{"; + jsonLog = jsonLog + " \"results\": [" + resultsStr + "],"; + jsonLog = jsonLog + " \"logs\": [" + logsStr + "]"; + jsonLog = jsonLog + "}"; + + return jsonLog; + } +}; + +InteractiveServerResult gInteractiveServerResult; + +void ENFORCE_FORMAT(3, 0) InteractiveServerLoggingCallback(const char * module, uint8_t category, const char * msg, va_list args) +{ + va_list args_copy; + va_copy(args_copy, args); + + chip::Logging::Platform::LogV(module, category, msg, args); + + char message[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE]; + vsnprintf(message, sizeof(message), msg, args_copy); + va_end(args_copy); + + char base64Message[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE * 2] = {}; + chip::Base64Encode(chip::Uint8::from_char(message), static_cast(strlen(message)), base64Message); + + gInteractiveServerResult.MaybeAddLog(module, category, base64Message); +} + char * GetCommand(char * command) { if (command != nullptr) @@ -63,15 +193,44 @@ char * GetCommand(char * command) CHIP_ERROR InteractiveServerCommand::RunCommand() { + // Logs needs to be redirected in order to refresh the screen appropriately when something + // is dumped to stdout while the user is typing a command. + chip::Logging::SetLogRedirectCallback(InteractiveServerLoggingCallback); + + DataModelLogger::SetJSONDelegate(this); ReturnErrorOnFailure(mWebSocketServer.Run(mPort, this)); + gInteractiveServerResult.Reset(); SetCommandExitStatus(CHIP_NO_ERROR); return CHIP_NO_ERROR; } +void SendOverWebSocket(intptr_t context) +{ + auto server = reinterpret_cast(context); + server->Send(gInteractiveServerResult.AsJsonString().c_str()); + gInteractiveServerResult.Reset(); +} + bool InteractiveServerCommand::OnWebSocketMessageReceived(char * msg) { - return ParseCommand(msg); + bool isAsyncReport = strlen(msg) == 0; + gInteractiveServerResult.Setup(isAsyncReport); + VerifyOrReturnValue(!isAsyncReport, true); + + auto shouldStop = ParseCommand(msg, &gInteractiveServerResult.mStatus); + chip::DeviceLayer::PlatformMgr().ScheduleWork(SendOverWebSocket, reinterpret_cast(&mWebSocketServer)); + return shouldStop; +} + +CHIP_ERROR InteractiveServerCommand::LogJSON(const char * json) +{ + gInteractiveServerResult.MaybeAddResult(json); + if (gInteractiveServerResult.IsAsyncReport()) + { + chip::DeviceLayer::PlatformMgr().ScheduleWork(SendOverWebSocket, reinterpret_cast(&mWebSocketServer)); + } + return CHIP_NO_ERROR; } CHIP_ERROR InteractiveStartCommand::RunCommand() @@ -83,10 +242,11 @@ CHIP_ERROR InteractiveStartCommand::RunCommand() chip::Logging::SetLogRedirectCallback(LoggingCallback); char * command = nullptr; + int status; while (true) { command = GetCommand(command); - if (command != nullptr && !ParseCommand(command)) + if (command != nullptr && !ParseCommand(command, &status)) { break; } @@ -102,7 +262,7 @@ CHIP_ERROR InteractiveStartCommand::RunCommand() return CHIP_NO_ERROR; } -bool InteractiveCommand::ParseCommand(char * command) +bool InteractiveCommand::ParseCommand(char * command, int * status) { if (strcmp(command, kInteractiveModeStopCommand) == 0) { @@ -111,6 +271,8 @@ bool InteractiveCommand::ParseCommand(char * command) } ClearLine(); - mHandler->RunInteractive(command); + + *status = mHandler->RunInteractive(command); + return true; } diff --git a/examples/chip-tool/commands/interactive/InteractiveCommands.h b/examples/chip-tool/commands/interactive/InteractiveCommands.h index 830c9d5fd408f2..6de3a7a8bf8242 100644 --- a/examples/chip-tool/commands/interactive/InteractiveCommands.h +++ b/examples/chip-tool/commands/interactive/InteractiveCommands.h @@ -18,6 +18,7 @@ #pragma once +#include "../clusters/DataModelLogger.h" #include "../common/CHIPCommand.h" #include "../common/Commands.h" @@ -35,7 +36,7 @@ class InteractiveCommand : public CHIPCommand /////////// CHIPCommand Interface ///////// chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(0); } - bool ParseCommand(char * command); + bool ParseCommand(char * command, int * status); private: Commands * mHandler = nullptr; @@ -52,7 +53,7 @@ class InteractiveStartCommand : public InteractiveCommand CHIP_ERROR RunCommand() override; }; -class InteractiveServerCommand : public InteractiveCommand, public WebSocketServerDelegate +class InteractiveServerCommand : public InteractiveCommand, public WebSocketServerDelegate, public DataModelLoggerJSONDelegate { public: InteractiveServerCommand(Commands * commandsHandler, CredentialIssuerCommands * credsIssuerConfig) : @@ -67,6 +68,9 @@ class InteractiveServerCommand : public InteractiveCommand, public WebSocketServ /////////// WebSocketServerDelegate Interface ///////// bool OnWebSocketMessageReceived(char * msg) override; + /////////// DataModelLoggerJSONDelegate interface ///////// + CHIP_ERROR LogJSON(const char * json) override; + private: WebSocketServer mWebSocketServer; chip::Optional mPort; diff --git a/examples/chip-tool/commands/interactive/WebSocketServer.cpp b/examples/chip-tool/commands/interactive/WebSocketServer.cpp index c7269b38af538f..a41754716ae961 100644 --- a/examples/chip-tool/commands/interactive/WebSocketServer.cpp +++ b/examples/chip-tool/commands/interactive/WebSocketServer.cpp @@ -18,10 +18,14 @@ #include "WebSocketServer.h" +#include + constexpr uint16_t kDefaultWebSocketServerPort = 9002; constexpr uint16_t kMaxMessageBufferLen = 8192; namespace { +lws * gWebSocketInstance = nullptr; + void LogWebSocketCallbackReason(lws_callback_reasons reason) { #if CHIP_DETAIL_LOGGING @@ -121,6 +125,14 @@ static int OnWebSocketCallback(lws * wsi, lws_callback_reasons reason, void * us { lws_callback_on_writable(wsi); } + else if (LWS_CALLBACK_ESTABLISHED == reason) + { + gWebSocketInstance = wsi; + } + else if (LWS_CALLBACK_WSI_DESTROY == reason) + { + gWebSocketInstance = nullptr; + } return 0; } @@ -145,3 +157,15 @@ CHIP_ERROR WebSocketServer::Run(chip::Optional port, WebSocketServerDe lws_context_default_loop_run_destroy(context); return CHIP_NO_ERROR; } + +CHIP_ERROR WebSocketServer::Send(const char * msg) +{ + VerifyOrReturnError(nullptr != gWebSocketInstance, CHIP_ERROR_INCORRECT_STATE); + + chip::Platform::ScopedMemoryBuffer buffer; + VerifyOrReturnError(buffer.Calloc(LWS_PRE + strlen(msg)), CHIP_ERROR_NO_MEMORY); + memcpy(&buffer[LWS_PRE], (void *) msg, strlen(msg)); + lws_write(gWebSocketInstance, &buffer[LWS_PRE], strlen(msg), LWS_WRITE_TEXT); + + return CHIP_NO_ERROR; +} diff --git a/examples/chip-tool/commands/interactive/WebSocketServer.h b/examples/chip-tool/commands/interactive/WebSocketServer.h index d24ab07f88f217..d18b68bee2b611 100644 --- a/examples/chip-tool/commands/interactive/WebSocketServer.h +++ b/examples/chip-tool/commands/interactive/WebSocketServer.h @@ -28,4 +28,5 @@ class WebSocketServer { public: CHIP_ERROR Run(chip::Optional port, WebSocketServerDelegate * delegate); + CHIP_ERROR Send(const char * msg); }; diff --git a/examples/chip-tool/templates/logging/DataModelLogger-src.zapt b/examples/chip-tool/templates/logging/DataModelLogger-src.zapt index 5bff45c9951f7d..e984cb63680e3a 100644 --- a/examples/chip-tool/templates/logging/DataModelLogger-src.zapt +++ b/examples/chip-tool/templates/logging/DataModelLogger-src.zapt @@ -4,6 +4,8 @@ using namespace chip::app::Clusters; +DataModelLoggerJSONDelegate * DataModelLogger::mJSONDelegate = nullptr; + {{#structs_with_clusters groupByStructName=1}} CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, const chip::app::Clusters::{{#unless (is_number_greater_than structClusterCount 1)}}{{as_camel_cased clusterName false}}{{else}}detail{{/unless}}::Structs::{{name}}::DecodableType & value) { diff --git a/examples/tv-casting-app/tv-casting-common/BUILD.gn b/examples/tv-casting-app/tv-casting-common/BUILD.gn index c0b1c060d59e1c..920203bc86c72e 100644 --- a/examples/tv-casting-app/tv-casting-common/BUILD.gn +++ b/examples/tv-casting-app/tv-casting-common/BUILD.gn @@ -85,6 +85,7 @@ chip_data_model("tv-casting-common") { deps = [ "${chip_root}/src/app/tests/suites/commands/interaction_model", + "${chip_root}/src/lib/support/jsontlv", "${chip_root}/third_party/inipp", "${chip_root}/third_party/jsoncpp", ] diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp index 2adc203afe60bd..8f1c1b5cc84c2d 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -21,6 +21,8 @@ using namespace chip::app::Clusters; +DataModelLoggerJSONDelegate * DataModelLogger::mJSONDelegate = nullptr; + CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, const chip::app::Clusters::AccessControl::Structs::AccessControlEntryStruct::DecodableType & value)