Skip to content

Commit

Permalink
[chip-tool] Add write-by-id, command-by-id and a custom payload build…
Browse files Browse the repository at this point in the history
…er (#14398)

* [chip-tool] Add write-by-id, command-by-id and a custom payload builder

* Update generated content
  • Loading branch information
vivien-apple authored and pull[bot] committed Feb 16, 2022
1 parent e925a5b commit d715f82
Show file tree
Hide file tree
Showing 7 changed files with 437 additions and 4 deletions.
25 changes: 25 additions & 0 deletions examples/chip-tool/commands/clusters/ClusterCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,35 @@
class ClusterCommand : public ModelCommand, public chip::app::CommandSender::Callback
{
public:
ClusterCommand() : ModelCommand("command-by-id")
{
AddArgument("cluster-id", 0, UINT32_MAX, &mClusterId);
AddArgument("command-id", 0, UINT32_MAX, &mCommandId);
AddArgument("payload", &mPayload);
AddArgument("timedInteractionTimeoutMs", 0, UINT16_MAX, &mTimedInteractionTimeoutMs);
ModelCommand::AddArguments();
}

ClusterCommand(chip::ClusterId clusterId) : ModelCommand("command-by-id"), mClusterId(clusterId)
{
AddArgument("command-id", 0, UINT32_MAX, &mCommandId);
AddArgument("payload", &mPayload);
AddArgument("timedInteractionTimeoutMs", 0, UINT16_MAX, &mTimedInteractionTimeoutMs);
ModelCommand::AddArguments();
}

ClusterCommand(const char * commandName) : ModelCommand(commandName)
{
AddArgument("timedInteractionTimeoutMs", 0, UINT16_MAX, &mTimedInteractionTimeoutMs);
}

~ClusterCommand() {}

CHIP_ERROR SendCommand(ChipDevice * device, chip::EndpointId endpointId) override
{
return ClusterCommand::SendCommand(device, endpointId, mClusterId, mCommandId, mPayload);
}

/////////// CommandSender Callback Interface /////////
virtual void OnResponse(chip::app::CommandSender * client, const chip::app::ConcreteCommandPath & path,
const chip::app::StatusIB & status, chip::TLV::TLVReader * data) override
Expand Down Expand Up @@ -84,7 +106,10 @@ class ClusterCommand : public ModelCommand, public chip::app::CommandSender::Cal
}

private:
chip::ClusterId mClusterId;
chip::CommandId mCommandId;
chip::Optional<uint16_t> mTimedInteractionTimeoutMs;

CustomArgument mPayload;
std::unique_ptr<chip::app::CommandSender> mCommandSender;
};
232 changes: 232 additions & 0 deletions examples/chip-tool/commands/clusters/CustomArgument.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
/*
* 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

#include <app-common/zap-generated/cluster-objects.h>
#include <lib/support/SafeInt.h>

namespace {
static constexpr char kPayloadHexPrefix[] = "hex:";
static constexpr char kPayloadSignedPrefix[] = "s:";
static constexpr char kPayloadUnkPayloadSignedPrefix[] = "u:";
static constexpr size_t kPayloadHexPrefixLen = ArraySize(kPayloadHexPrefix) - 1; // ignore null character
static constexpr size_t kPayloadSignedPrefixLen = ArraySize(kPayloadSignedPrefix) - 1; // ignore null character
static constexpr size_t kPayloadUnkPayloadSignedPrefixLen = ArraySize(kPayloadUnkPayloadSignedPrefix) - 1; // ignore null character
} // namespace

class CustomArgumentParser
{
public:
static CHIP_ERROR Put(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
{
if (value.isObject())
{
return CustomArgumentParser::PutObject(writer, tag, value);
}

if (value.isArray())
{
return CustomArgumentParser::PutArray(writer, tag, value);
}

if (value.isString())
{
if (IsOctetString(value))
{
return CustomArgumentParser::PutOctetString(writer, tag, value);
}
else if (IsUnsignedNumberPrefix(value))
{
return CustomArgumentParser::PutUnsignedFromString(writer, tag, value);
}
else if (IsSignedNumberPrefix(value))
{
return CustomArgumentParser::PutSignedFromString(writer, tag, value);
}

return CustomArgumentParser::PutCharString(writer, tag, value);
}

if (value.isNull())
{
return chip::app::DataModel::Encode(*writer, tag, chip::app::DataModel::Nullable<uint8_t>());
}

if (value.isBool())
{
return chip::app::DataModel::Encode(*writer, tag, value.asBool());
}

if (value.isUInt())
{
return chip::app::DataModel::Encode(*writer, tag, value.asLargestUInt());
}

if (value.isInt())
{
return chip::app::DataModel::Encode(*writer, tag, value.asLargestInt());
}

if (value.isNumeric())
{
return chip::app::DataModel::Encode(*writer, tag, value.asDouble());
}

return CHIP_ERROR_NOT_IMPLEMENTED;
}

private:
static CHIP_ERROR PutArray(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
{
chip::TLV::TLVType outer;
ReturnErrorOnFailure(writer->StartContainer(tag, chip::TLV::kTLVType_Array, outer));

Json::ArrayIndex size = value.size();

for (Json::ArrayIndex i = 0; i < size; i++)
{
ReturnErrorOnFailure(CustomArgumentParser::Put(writer, chip::TLV::AnonymousTag(), value[i]));
}

return writer->EndContainer(outer);
}

static CHIP_ERROR PutObject(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
{
chip::TLV::TLVType outer;
ReturnErrorOnFailure(writer->StartContainer(tag, chip::TLV::kTLVType_Structure, outer));

for (auto const & id : value.getMemberNames())
{
auto index = std::stoul(id, nullptr, 0);
VerifyOrReturnError(chip::CanCastTo<uint8_t>(index), CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(CustomArgumentParser::Put(writer, chip::TLV::ContextTag(static_cast<uint8_t>(index)), value[id]));
}

return writer->EndContainer(outer);
}

static CHIP_ERROR PutOctetString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
{
size_t size = strlen(value.asCString());
VerifyOrReturnError(size % 2 == 0, CHIP_ERROR_INVALID_STRING_LENGTH);

chip::Platform::ScopedMemoryBuffer<uint8_t> buffer;
VerifyOrReturnError(buffer.Calloc(size / 2), CHIP_ERROR_NO_MEMORY);
size_t octetCount = chip::Encoding::HexToBytes(value.asCString() + kPayloadHexPrefixLen, size - kPayloadHexPrefixLen,
buffer.Get(), (size - kPayloadHexPrefixLen) / 2);
VerifyOrReturnError(octetCount != 0, CHIP_ERROR_NO_MEMORY);

return chip::app::DataModel::Encode(*writer, tag, chip::ByteSpan(buffer.Get(), octetCount));
}

static CHIP_ERROR PutCharString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
{
size_t size = strlen(value.asCString());

chip::Platform::ScopedMemoryBuffer<char> buffer;
VerifyOrReturnError(buffer.Calloc(size), CHIP_ERROR_NO_MEMORY);
strncpy(buffer.Get(), value.asCString(), size);

return chip::app::DataModel::Encode(*writer, tag, chip::CharSpan(buffer.Get(), size));
}

static CHIP_ERROR PutUnsignedFromString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
{
size_t size = strlen(value.asCString());
char numberAsString[21];

chip::Platform::ScopedMemoryBuffer<char> buffer;
strncpy(numberAsString, value.asCString() + kPayloadUnkPayloadSignedPrefixLen, size - kPayloadUnkPayloadSignedPrefixLen);

auto numberAsUint = std::stoull(numberAsString, nullptr, 0);
return chip::app::DataModel::Encode(*writer, tag, static_cast<uint64_t>(numberAsUint));
}

static CHIP_ERROR PutSignedFromString(chip::TLV::TLVWriter * writer, chip::TLV::Tag tag, Json::Value & value)
{
size_t size = strlen(value.asCString());
char numberAsString[21];

chip::Platform::ScopedMemoryBuffer<char> buffer;
strncpy(numberAsString, value.asCString() + kPayloadSignedPrefixLen, size - kPayloadSignedPrefixLen);

auto numberAsInt = std::stoll(numberAsString, nullptr, 0);
return chip::app::DataModel::Encode(*writer, tag, static_cast<int64_t>(numberAsInt));
}

static bool IsOctetString(Json::Value & value)
{
return (strncmp(value.asCString(), kPayloadHexPrefix, kPayloadHexPrefixLen) == 0);
}

static bool IsUnsignedNumberPrefix(Json::Value & value)
{
return (strncmp(value.asCString(), kPayloadUnkPayloadSignedPrefix, kPayloadUnkPayloadSignedPrefixLen) == 0);
}

static bool IsSignedNumberPrefix(Json::Value & value)
{
return (strncmp(value.asCString(), kPayloadSignedPrefix, kPayloadSignedPrefixLen) == 0);
}
};

class CustomArgument
{
public:
~CustomArgument()
{
if (mData != nullptr)
{
chip::Platform::MemoryFree(mData);
}
}

CHIP_ERROR Parse(const char * label, const char * json)
{
Json::Reader reader;
Json::Value value;
reader.parse(json, value);

mData = static_cast<uint8_t *>(chip::Platform::MemoryCalloc(sizeof(uint8_t), mDataMaxLen));
VerifyOrReturnError(mData != nullptr, CHIP_ERROR_NO_MEMORY);

chip::TLV::TLVWriter writer;
writer.Init(mData, mDataMaxLen);

ReturnErrorOnFailure(CustomArgumentParser::Put(&writer, chip::TLV::AnonymousTag(), value));

mDataLen = writer.GetLengthWritten();
return writer.Finalize();
}

CHIP_ERROR Encode(chip::TLV::TLVWriter & writer, chip::TLV::Tag tag) const
{
chip::TLV::TLVReader reader;
reader.Init(mData, mDataLen);
reader.Next();

return writer.CopyElement(tag, reader);
}

private:
uint8_t * mData = nullptr;
uint32_t mDataLen = 0;
static constexpr uint32_t mDataMaxLen = 4096;
};
25 changes: 25 additions & 0 deletions examples/chip-tool/commands/clusters/WriteAttributeCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,35 @@
class WriteAttribute : public ModelCommand, public chip::app::WriteClient::Callback
{
public:
WriteAttribute() : ModelCommand("write-by-id")
{
AddArgument("cluster-id", 0, UINT32_MAX, &mClusterId);
AddArgument("attribute-id", 0, UINT32_MAX, &mAttributeId);
AddArgument("attribute-value", &mAttributeValue);
AddArgument("timedInteractionTimeoutMs", 0, UINT16_MAX, &mTimedInteractionTimeoutMs);
ModelCommand::AddArguments();
}

WriteAttribute(chip::ClusterId clusterId) : ModelCommand("write-by-id"), mClusterId(clusterId)
{
AddArgument("attribute-id", 0, UINT32_MAX, &mAttributeId);
AddArgument("attribute-value", &mAttributeValue);
AddArgument("timedInteractionTimeoutMs", 0, UINT16_MAX, &mTimedInteractionTimeoutMs);
ModelCommand::AddArguments();
}

WriteAttribute(const char * attributeName) : ModelCommand("write")
{
AddArgument("timedInteractionTimeoutMs", 0, UINT16_MAX, &mTimedInteractionTimeoutMs);
}

~WriteAttribute() {}

CHIP_ERROR SendCommand(ChipDevice * device, chip::EndpointId endpointId) override
{
return WriteAttribute::SendCommand(device, endpointId, mClusterId, mAttributeId, mAttributeValue);
}

/////////// WriteClient Callback Interface /////////
void OnResponse(const chip::app::WriteClient * client, const chip::app::ConcreteAttributePath & path,
chip::app::StatusIB status) override
Expand Down Expand Up @@ -79,7 +101,10 @@ class WriteAttribute : public ModelCommand, public chip::app::WriteClient::Callb
}

private:
chip::ClusterId mClusterId;
chip::AttributeId mAttributeId;
chip::Optional<uint16_t> mTimedInteractionTimeoutMs;

CustomArgument mAttributeValue;
std::unique_ptr<chip::app::WriteClient> mWriteClient;
};
16 changes: 16 additions & 0 deletions examples/chip-tool/commands/common/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ bool Command::InitArgument(size_t argIndex, char * argValue)
return CHIP_NO_ERROR == complexArgument->Parse(arg.name, argValue);
}

case ArgumentType::Custom: {
auto customArgument = static_cast<CustomArgument *>(arg.value);
return CHIP_NO_ERROR == customArgument->Parse(arg.name, argValue);
}

case ArgumentType::Attribute: {
if (arg.isOptional() || arg.isNullable())
{
Expand Down Expand Up @@ -495,6 +500,17 @@ size_t Command::AddArgument(const char * name, ComplexArgument * value)
return AddArgumentToList(std::move(arg));
}

size_t Command::AddArgument(const char * name, CustomArgument * value)
{
Argument arg;
arg.type = ArgumentType::Custom;
arg.name = name;
arg.value = const_cast<void *>(reinterpret_cast<const void *>(value));
arg.flags = 0;

return AddArgumentToList(std::move(arg));
}

size_t Command::AddArgument(const char * name, float min, float max, float * out, uint8_t flags)
{
Argument arg;
Expand Down
5 changes: 4 additions & 1 deletion examples/chip-tool/commands/common/Command.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <app/data-model/Nullable.h>
#include <commands/clusters/ComplexArgument.h>
#include <commands/clusters/CustomArgument.h>
#include <controller/CHIPDeviceController.h>
#include <inet/InetInterface.h>
#include <lib/core/Optional.h>
Expand Down Expand Up @@ -67,7 +68,8 @@ enum ArgumentType
OctetString,
Attribute,
Address,
Complex
Complex,
Custom
};

struct Argument
Expand Down Expand Up @@ -127,6 +129,7 @@ class Command
size_t AddArgument(const char * name, chip::Span<const char> * value, uint8_t flags = 0);
size_t AddArgument(const char * name, AddressWithInterface * out, uint8_t flags = 0);
size_t AddArgument(const char * name, ComplexArgument * value);
size_t AddArgument(const char * name, CustomArgument * value);
size_t AddArgument(const char * name, int64_t min, uint64_t max, bool * out, uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Boolean, flags);
Expand Down
Loading

0 comments on commit d715f82

Please sign in to comment.