Skip to content

Commit

Permalink
[Fabric-Admin] Add sync-device command to sync a device from another …
Browse files Browse the repository at this point in the history
…fabric (project-chip#33912)

* Add sync-device command in fabricsync command sets

* Update API documents

* Address review comments

* Adjust the legth of kMaxManaulCodeLength and move some long functions from .h to .cpp

* Address new review comments

* Remove un-used defines
  • Loading branch information
yufengwangca authored Jun 18, 2024
1 parent 8ba371a commit 4cdce52
Show file tree
Hide file tree
Showing 16 changed files with 328 additions and 71 deletions.
2 changes: 2 additions & 0 deletions examples/fabric-admin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ static_library("fabric-admin-utils") {
"${chip_root}/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp",
"commands/clusters/ModelCommand.cpp",
"commands/clusters/ModelCommand.h",
"commands/clusters/ReportCommand.cpp",
"commands/clusters/ReportCommand.h",
"commands/common/CHIPCommand.cpp",
"commands/common/CHIPCommand.h",
"commands/common/Command.cpp",
Expand Down
80 changes: 80 additions & 0 deletions examples/fabric-admin/commands/clusters/ReportCommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2024 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.
*
*/

#include "ReportCommand.h"

#include <app/InteractionModelEngine.h>
#include <inttypes.h>

using namespace ::chip;

void ReportCommand::OnAttributeData(const app::ConcreteDataAttributePath & path, TLV::TLVReader * data,
const app::StatusIB & status)
{
CHIP_ERROR error = status.ToChipError();
if (CHIP_NO_ERROR != error)
{
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(path, status));

ChipLogError(NotSpecified, "Response Failure: %s", ErrorStr(error));
mError = error;
return;
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
mError = CHIP_ERROR_INTERNAL;
return;
}

LogErrorOnFailure(RemoteDataModelLogger::LogAttributeAsJSON(path, data));
}

void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status)
{
if (status != nullptr)
{
CHIP_ERROR error = status->ToChipError();
if (CHIP_NO_ERROR != error)
{
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(eventHeader, *status));

ChipLogError(NotSpecified, "Response Failure: %s", ErrorStr(error));
mError = error;
return;
}
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
mError = CHIP_ERROR_INTERNAL;
return;
}

LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data));

CHIP_ERROR error = DataModelLogger::LogEvent(eventHeader, data);
if (CHIP_NO_ERROR != error)
{
ChipLogError(NotSpecified, "Response Failure: Can not decode Data");
mError = error;
return;
}
}
63 changes: 2 additions & 61 deletions examples/fabric-admin/commands/clusters/ReportCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,69 +32,10 @@ class ReportCommand : public InteractionModelReports, public ModelCommand, publi

/////////// ReadClient Callback Interface /////////
void OnAttributeData(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data,
const chip::app::StatusIB & status) override
{
CHIP_ERROR error = status.ToChipError();
if (CHIP_NO_ERROR != error)
{
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(path, status));

ChipLogError(NotSpecified, "Response Failure: %s", chip::ErrorStr(error));
mError = error;
return;
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
mError = CHIP_ERROR_INTERNAL;
return;
}

LogErrorOnFailure(RemoteDataModelLogger::LogAttributeAsJSON(path, data));

error = DataModelLogger::LogAttribute(path, data);
if (CHIP_NO_ERROR != error)
{
ChipLogError(NotSpecified, "Response Failure: Can not decode Data");
mError = error;
return;
}
}
const chip::app::StatusIB & status) override;

void OnEventData(const chip::app::EventHeader & eventHeader, chip::TLV::TLVReader * data,
const chip::app::StatusIB * status) override
{
if (status != nullptr)
{
CHIP_ERROR error = status->ToChipError();
if (CHIP_NO_ERROR != error)
{
LogErrorOnFailure(RemoteDataModelLogger::LogErrorAsJSON(eventHeader, *status));

ChipLogError(NotSpecified, "Response Failure: %s", chip::ErrorStr(error));
mError = error;
return;
}
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
mError = CHIP_ERROR_INTERNAL;
return;
}

LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data));

CHIP_ERROR error = DataModelLogger::LogEvent(eventHeader, data);
if (CHIP_NO_ERROR != error)
{
ChipLogError(NotSpecified, "Response Failure: Can not decode Data");
mError = error;
return;
}
}
const chip::app::StatusIB * status) override;

void OnError(CHIP_ERROR error) override
{
Expand Down
21 changes: 21 additions & 0 deletions examples/fabric-admin/commands/common/Commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ static void DetectAndLogMismatchedDoubleQuotes(int argc, char ** argv)

} // namespace

// Define the static member
Commands Commands::sInstance;

void Commands::Register(const char * commandSetName, commands_list commandsList, const char * helpText, bool isCluster)
{
VerifyOrDieWithMsg(isCluster || helpText != nullptr, NotSpecified, "Non-cluster command sets must have help text");
Expand Down Expand Up @@ -337,6 +340,7 @@ Commands::CommandSetMap::iterator Commands::GetCommandSet(std::string commandSet
{
std::string key(commandSet.first);
std::transform(key.begin(), key.end(), key.begin(), ::tolower);

if (key.compare(commandSetName) == 0)
{
return mCommandSets.find(commandSet.first);
Expand All @@ -346,6 +350,23 @@ Commands::CommandSetMap::iterator Commands::GetCommandSet(std::string commandSet
return mCommandSets.end();
}

Command * Commands::GetCommandByName(std::string commandSetName, std::string commandName)
{
auto commandSetIter = GetCommandSet(commandSetName);
if (commandSetIter != mCommandSets.end())
{
auto & commandList = commandSetIter->second.commands;
for (auto & command : commandList)
{
if (command->GetName() == commandName)
{
return command.get();
}
}
}
return nullptr;
}

Command * Commands::GetCommand(CommandsVector & commands, std::string commandName)
{
for (auto & command : commands)
Expand Down
16 changes: 16 additions & 0 deletions examples/fabric-admin/commands/common/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class Commands
int Run(int argc, char ** argv);
int RunInteractive(const char * command, const chip::Optional<char *> & storageDirectory, bool advertiseOperational);

Command * GetCommandByName(std::string commandSetName, std::string commandName);

private:
struct CommandSet
{
Expand Down Expand Up @@ -87,4 +89,18 @@ class Commands
#ifdef CONFIG_USE_LOCAL_STORAGE
PersistentStorage mStorage;
#endif // CONFIG_USE_LOCAL_STORAGE

friend Commands & CommandMgr();
static Commands sInstance;
};

/**
* Returns the public interface of the CommandManager singleton object.
*
* Applications should use this to access features of the CommandManager
* object.
*/
inline Commands & CommandMgr()
{
return Commands::sInstance;
}
1 change: 1 addition & 0 deletions examples/fabric-admin/commands/fabric-sync/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ void registerCommandsFabricSync(Commands & commands, CredentialIssuerCommands *

commands_list clusterCommands = {
make_unique<FabricSyncAddDeviceCommand>(credsIssuerConfig),
make_unique<FabricSyncDeviceCommand>(credsIssuerConfig),
};

commands.RegisterCommandSet(clusterName, clusterCommands, "Commands for fabric synchronization.");
Expand Down
95 changes: 95 additions & 0 deletions examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

#include "FabricSyncCommand.h"
#include <commands/common/RemoteDataModelLogger.h>
#include <commands/interactive/InteractiveCommands.h>
#include <setup_payload/ManualSetupPayloadGenerator.h>
#include <thread>
#include <unistd.h>

Expand All @@ -27,6 +29,16 @@

using namespace ::chip;

namespace {

// Constants
constexpr uint32_t kCommissionPrepareTimeMs = 500;
constexpr uint16_t kMaxManaulCodeLength = 21;
constexpr uint16_t kSubscribeMinInterval = 0;
constexpr uint16_t kSubscribeMaxInterval = 60;

} // namespace

CHIP_ERROR FabricSyncAddDeviceCommand::RunCommand(NodeId remoteId)
{
#if defined(PW_RPC_ENABLED)
Expand All @@ -36,3 +48,86 @@ CHIP_ERROR FabricSyncAddDeviceCommand::RunCommand(NodeId remoteId)
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif
}

void FabricSyncDeviceCommand::OnCommissioningWindowOpened(NodeId deviceId, CHIP_ERROR err, chip::SetupPayload payload)
{
if (err == CHIP_NO_ERROR)
{
char payloadBuffer[kMaxManaulCodeLength + 1];
MutableCharSpan manualCode(payloadBuffer);
CHIP_ERROR error = ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualCode);
if (error == CHIP_NO_ERROR)
{
char command[kMaxCommandSize];
NodeId nodeId = 2; // TODO: (Issue #33947) need to switch to dynamically assigned ID
snprintf(command, sizeof(command), "pairing code %ld %s", nodeId, payloadBuffer);

PairingCommand * pairingCommand = static_cast<PairingCommand *>(CommandMgr().GetCommandByName("pairing", "code"));

if (pairingCommand == nullptr)
{
ChipLogError(NotSpecified, "Pairing code command is not available");
return;
}

pairingCommand->RegisterCommissioningDelegate(this);
mAssignedNodeId = nodeId;

usleep(kCommissionPrepareTimeMs * 1000);

PushCommand(command);
}
else
{
ChipLogError(NotSpecified, "Unable to generate manual code for setup payload: %" CHIP_ERROR_FORMAT, error.Format());
}
}
else
{
ChipLogError(NotSpecified,
"Failed to open synced device (0x:" ChipLogFormatX64 ") commissioning window: %" CHIP_ERROR_FORMAT,
ChipLogValueX64(deviceId), err.Format());
}
}

void FabricSyncDeviceCommand::OnCommissioningComplete(chip::NodeId deviceId, CHIP_ERROR err)
{
if (mAssignedNodeId != deviceId)
{
// Ignore if the deviceId does not match the mAssignedNodeId.
// This scenario should not occur because no other device should be commissioned during the fabric sync process.
return;
}

if (err == CHIP_NO_ERROR)
{
// TODO: (Issue #33947) Add Synced Device to device manager
}
else
{
ChipLogError(NotSpecified, "Failed to pair synced device (0x:" ChipLogFormatX64 ") with error: %" CHIP_ERROR_FORMAT,
ChipLogValueX64(deviceId), err.Format());
}
}

CHIP_ERROR FabricSyncDeviceCommand::RunCommand(EndpointId remoteId)
{
char command[kMaxCommandSize];
NodeId bridgeNodeId = 1; // TODO: (Issue #33947) need to switch to configured ID
snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d", bridgeNodeId, remoteId,
kEnhancedCommissioningMethod, kWindowTimeout, kIteration, kDiscriminator);

OpenCommissioningWindowCommand * openCommand =
static_cast<OpenCommissioningWindowCommand *>(CommandMgr().GetCommandByName("pairing", "open-commissioning-window"));

if (openCommand == nullptr)
{
return CHIP_ERROR_UNINITIALIZED;
}

openCommand->RegisterDelegate(this);

PushCommand(command);

return CHIP_NO_ERROR;
}
31 changes: 31 additions & 0 deletions examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
#pragma once

#include <commands/common/CHIPCommand.h>
#include <commands/pairing/OpenCommissioningWindowCommand.h>
#include <commands/pairing/PairingCommand.h>

constexpr uint16_t kMaxCommandSize = 64;
constexpr uint16_t kDiscriminator = 3840;
constexpr uint16_t kWindowTimeout = 300;
constexpr uint16_t kIteration = 1000;
constexpr uint8_t kEnhancedCommissioningMethod = 1;

class FabricSyncAddDeviceCommand : public CHIPCommand
{
Expand All @@ -38,3 +46,26 @@ class FabricSyncAddDeviceCommand : public CHIPCommand

CHIP_ERROR RunCommand(NodeId remoteId);
};

class FabricSyncDeviceCommand : public CHIPCommand, public CommissioningWindowDelegate, public CommissioningDelegate
{
public:
FabricSyncDeviceCommand(CredentialIssuerCommands * credIssuerCommands) : CHIPCommand("sync-device", credIssuerCommands)
{
AddArgument("endpointid", 0, UINT16_MAX, &mRemoteEndpointId);
}

void OnCommissioningWindowOpened(NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload) override;
void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR err) override;

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override { return RunCommand(mRemoteEndpointId); }

chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(1); }

private:
chip::EndpointId mRemoteEndpointId = chip::kInvalidEndpointId;
chip::NodeId mAssignedNodeId = chip::kUndefinedNodeId;

CHIP_ERROR RunCommand(chip::EndpointId remoteId);
};
Loading

0 comments on commit 4cdce52

Please sign in to comment.