Skip to content

Commit

Permalink
Linux sample implementation for Messages cluster (#32043)
Browse files Browse the repository at this point in the history
* Linux sample implementation for Messages cluster

* cleanup the chip:: and chip::app prefixes

* CI fixes

* Address comments

* Make memory management less error prone

* Fix CI

* more feedback

* fix CI

* Restyled by clang-format (#32123)

Co-authored-by: Restyled.io <[email protected]>

* address comments

* Restyled by clang-format (#32136)

Co-authored-by: Restyled.io <[email protected]>

* fix CI

* address comments

* address comments

* Restyled by clang-format (#32144)

Co-authored-by: Restyled.io <[email protected]>

* address comments

* address comments

* address comments

---------

Co-authored-by: Lazar Kovacic <[email protected]>
Co-authored-by: restyled-io[bot] <32688539+restyled-io[bot]@users.noreply.github.com>
Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
4 people authored and pull[bot] committed Mar 3, 2024
1 parent 6e87dc4 commit 23d1b4a
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 40 deletions.
82 changes: 56 additions & 26 deletions examples/tv-app/tv-common/clusters/messages/MessagesManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,64 +18,94 @@
#include "MessagesManager.h"

#include <app-common/zap-generated/attributes/Accessors.h>
#include <vector>

using namespace std;
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters::Messages;
using Message = chip::app::Clusters::Messages::Structs::MessageStruct::Type;
using Message = chip::app::Clusters::Messages::Structs::MessageStruct::Type;
using MessageResponseOption = chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type;

// Commands
void MessagesManager::HandlePresentMessagesRequest(
const chip::ByteSpan & messageId, const MessagePriorityEnum & priority,
const chip::BitMask<MessageControlBitmap> & messageControl, const chip::app::DataModel::Nullable<uint32_t> & startTime,
const chip::app::DataModel::Nullable<uint16_t> & duration, const chip::CharSpan & messageText,
const chip::Optional<chip::app::DataModel::DecodableList<MessageResponseOption>> & responses)
CHIP_ERROR MessagesManager::HandlePresentMessagesRequest(
const ByteSpan & messageId, const MessagePriorityEnum & priority, const BitMask<MessageControlBitmap> & messageControl,
const DataModel::Nullable<uint32_t> & startTime, const DataModel::Nullable<uint16_t> & duration, const CharSpan & messageText,
const Optional<DataModel::DecodableList<MessageResponseOption>> & responses)
{
Message message{
// TODO: Enable id
chip::ByteSpan(), priority, messageControl, startTime, duration,
// TODO: Enable text
chip::CharSpan()
// TODO: Convert responses to Optional<chip::app::DataModel::List<const
// chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type>> message.responses = responses;
};
ChipLogProgress(Zcl, "HandlePresentMessagesRequest message:%s", std::string(messageText.data(), messageText.size()).c_str());

auto cachedMessage = CachedMessage(messageId, priority, messageControl, startTime, duration,
std::string(messageText.data(), messageText.size()));
if (responses.HasValue())
{
auto iter = responses.Value().begin();
while (iter.Next())
{
auto & response = iter.GetValue();

CachedMessageOption option(response.messageResponseID.Value(),
std::string(response.label.Value().data(), response.label.Value().size()));

cachedMessage.AddOption(option);
}
}

mCachedMessages.push_back(cachedMessage);

mMessages.push_back(message);
// Add your code to present Message
ChipLogProgress(Zcl, "HandlePresentMessagesRequest complete");
return CHIP_NO_ERROR;
}

void MessagesManager::HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList<chip::ByteSpan> & messageIds)
CHIP_ERROR MessagesManager::HandleCancelMessagesRequest(const DataModel::DecodableList<ByteSpan> & messageIds)
{
// TODO: Cancel Message
auto iter = messageIds.begin();
while (iter.Next())
{
auto & id = iter.GetValue();

mCachedMessages.remove_if([id](CachedMessage & entry) { return entry.MessageIdMatches(id); });
// per spec, the command succeeds even when the message id does not match an existing message
}
return CHIP_NO_ERROR;
}

// Attributes
CHIP_ERROR MessagesManager::HandleGetMessages(chip::app::AttributeValueEncoder & aEncoder)
CHIP_ERROR MessagesManager::HandleGetMessages(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR {
for (Message & entry : mMessages)
for (CachedMessage & entry : mCachedMessages)
{
ReturnErrorOnFailure(encoder.Encode(entry));
ReturnErrorOnFailure(encoder.Encode(entry.GetMessage()));
}
return CHIP_NO_ERROR;
});
}

CHIP_ERROR MessagesManager::HandleGetActiveMessageIds(chip::app::AttributeValueEncoder & aEncoder)
CHIP_ERROR MessagesManager::HandleGetActiveMessageIds(AttributeValueEncoder & aEncoder)
{
return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR {
for (Message & entry : mMessages)
for (CachedMessage & entry : mCachedMessages)
{
ReturnErrorOnFailure(encoder.Encode(entry.messageID));
ReturnErrorOnFailure(encoder.Encode(entry.GetMessage().messageID));
}
return CHIP_NO_ERROR;
});
}

// Global Attributes
uint32_t MessagesManager::GetFeatureMap(chip::EndpointId endpoint)
uint32_t MessagesManager::GetFeatureMap(EndpointId endpoint)
{
uint32_t featureMap = 0;
Attributes::FeatureMap::Get(endpoint, &featureMap);
BitMask<Feature> FeatureMap;
FeatureMap.Set(Feature::kReceivedConfirmation);
FeatureMap.Set(Feature::kConfirmationResponse);
FeatureMap.Set(Feature::kConfirmationReply);
FeatureMap.Set(Feature::kProtectedMessages);

uint32_t featureMap = FeatureMap.Raw();
ChipLogProgress(Zcl, "GetFeatureMap featureMap=%d", featureMap);
// forcing to all features since this implementation supports all
// Attributes::FeatureMap::Get(endpoint, &featureMap);
return featureMap;
}
103 changes: 100 additions & 3 deletions examples/tv-app/tv-common/clusters/messages/MessagesManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,117 @@

#include <iostream>
#include <list>
#include <vector>

struct CachedMessageOption
{
CachedMessageOption(uint32_t id, std::string label) :
mLabel(label), mOption{ chip::MakeOptional(id), chip::MakeOptional(chip::CharSpan::fromCharString(mLabel.c_str())) }
{}

CachedMessageOption(const CachedMessageOption & option) :
mLabel(option.mLabel),
mOption{ option.mOption.messageResponseID, chip::MakeOptional(chip::CharSpan::fromCharString(mLabel.c_str())) }
{}

CachedMessageOption & operator=(const CachedMessageOption & option) = delete;

chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type GetMessageOption() { return mOption; }

~CachedMessageOption() {}

protected:
std::string mLabel;
chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type mOption;
};

struct CachedMessage
{
CachedMessage(const CachedMessage & message) :
mPriority(message.mPriority), mMessageControl(message.mMessageControl), mStartTime(message.mStartTime),
mDuration(message.mDuration), mMessageText(message.mMessageText), mOptions(message.mOptions)
{
memcpy(mMessageIdBuffer, message.mMessageIdBuffer, sizeof(mMessageIdBuffer));

for (CachedMessageOption & entry : mOptions)
{
mResponseOptions.push_back(entry.GetMessageOption());
}
}

CachedMessage & operator=(const CachedMessage & message) = delete;

CachedMessage(const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority,
const chip::BitMask<chip::app::Clusters::Messages::MessageControlBitmap> & messageControl,
const chip::app::DataModel::Nullable<uint32_t> & startTime,
const chip::app::DataModel::Nullable<uint16_t> & duration, std::string messageText) :
mPriority(priority),
mMessageControl(messageControl), mStartTime(startTime), mDuration(duration), mMessageText(messageText)
{
memcpy(mMessageIdBuffer, messageId.data(), sizeof(mMessageIdBuffer));
}

bool MessageIdMatches(const chip::ByteSpan & id) { return chip::ByteSpan(mMessageIdBuffer).data_equal(id); }

void AddOption(CachedMessageOption option)
{
mOptions.push_back(option);
mResponseOptions.push_back(option.GetMessageOption());
}

chip::app::Clusters::Messages::Structs::MessageStruct::Type GetMessage()
{
if (mResponseOptions.size() > 0)
{
chip::app::DataModel::List<chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type> options(
mResponseOptions.data(), mResponseOptions.size());
chip::app::Clusters::Messages::Structs::MessageStruct::Type message{ chip::ByteSpan(mMessageIdBuffer),
mPriority,
mMessageControl,
mStartTime,
mDuration,
chip::CharSpan::fromCharString(
mMessageText.c_str()),
chip::MakeOptional(options) };
return message;
}
chip::app::Clusters::Messages::Structs::MessageStruct::Type message{ chip::ByteSpan(mMessageIdBuffer),
mPriority,
mMessageControl,
mStartTime,
mDuration,
chip::CharSpan::fromCharString(mMessageText.c_str()) };
return message;
}

~CachedMessage() {}

protected:
const chip::app::Clusters::Messages::MessagePriorityEnum mPriority;
const chip::BitMask<chip::app::Clusters::Messages::MessageControlBitmap> mMessageControl;
const chip::app::DataModel::Nullable<uint32_t> mStartTime;
const chip::app::DataModel::Nullable<uint16_t> mDuration;

std::string mMessageText;
uint8_t mMessageIdBuffer[chip::app::Clusters::Messages::kMessageIdLength];

std::vector<chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type> mResponseOptions;
std::list<CachedMessageOption> mOptions;
};

class MessagesManager : public chip::app::Clusters::Messages::Delegate
{
public:
// Commands
void HandlePresentMessagesRequest(
CHIP_ERROR HandlePresentMessagesRequest(
const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority,
const chip::BitMask<chip::app::Clusters::Messages::MessageControlBitmap> & messageControl,
const chip::app::DataModel::Nullable<uint32_t> & startTime, const chip::app::DataModel::Nullable<uint16_t> & duration,
const chip::CharSpan & messageText,
const chip::Optional<
chip::app::DataModel::DecodableList<chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type>> &
responses) override;
void HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList<chip::ByteSpan> & messageIds) override;
CHIP_ERROR HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList<chip::ByteSpan> & messageIds) override;

// Attributes
CHIP_ERROR HandleGetMessages(chip::app::AttributeValueEncoder & aEncoder) override;
Expand All @@ -44,5 +141,5 @@ class MessagesManager : public chip::app::Clusters::Messages::Delegate
uint32_t GetFeatureMap(chip::EndpointId endpoint) override;

protected:
std::list<chip::app::Clusters::Messages::Structs::MessageStruct::Type> mMessages;
std::list<CachedMessage> mCachedMessages;
};
20 changes: 12 additions & 8 deletions src/app/clusters/messages-server/messages-delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,23 @@ namespace app {
namespace Clusters {
namespace Messages {

using MessageResponseOption = chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type;
constexpr static size_t kMessageIdLength = 16;
constexpr static size_t kMessageTextLengthMax = 256;
constexpr static size_t kMessageMaxOptionCount = 4;
constexpr static size_t kMessageResponseIdMin = 1;
constexpr static size_t kMessageResponseLabelMaxLength = 32;

class Delegate
{
public:
// Commands
virtual void
HandlePresentMessagesRequest(const ByteSpan & messageId, const MessagePriorityEnum & priority,
const chip::BitMask<MessageControlBitmap> & messageControl,
const DataModel::Nullable<uint32_t> & startTime, const DataModel::Nullable<uint16_t> & duration,
const CharSpan & messageText,
const chip::Optional<DataModel::DecodableList<MessageResponseOption>> & responses) = 0;
virtual void HandleCancelMessagesRequest(const DataModel::DecodableList<chip::ByteSpan> & messageIds) = 0;
virtual CHIP_ERROR HandlePresentMessagesRequest(
const ByteSpan & messageId, const MessagePriorityEnum & priority,
const chip::BitMask<MessageControlBitmap> & messageControl, const DataModel::Nullable<uint32_t> & startTime,
const DataModel::Nullable<uint16_t> & duration, const CharSpan & messageText,
const chip::Optional<DataModel::DecodableList<chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type>> &
responses) = 0;
virtual CHIP_ERROR HandleCancelMessagesRequest(const DataModel::DecodableList<chip::ByteSpan> & messageIds) = 0;

// Attributes
virtual CHIP_ERROR HandleGetMessages(app::AttributeValueEncoder & aEncoder) = 0;
Expand Down
69 changes: 66 additions & 3 deletions src/app/clusters/messages-server/messages-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,58 @@ bool emberAfMessagesClusterPresentMessagesRequestCallback(
auto & responses = commandData.responses;

Delegate * delegate = GetDelegate(endpoint);
VerifyOrExit(isDelegateNull(delegate, endpoint) != true, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(isDelegateNull(delegate, endpoint) != true, status = Status::NotFound);

VerifyOrExit(messageId.size() == kMessageIdLength,
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback invalid message id length");
status = Status::ConstraintError);

VerifyOrExit(messageText.size() <= kMessageTextLengthMax,
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback invalid message text length");
status = Status::ConstraintError);

if (responses.HasValue())
{
size_t size = 0;
err = responses.Value().ComputeSize(&size);
VerifyOrExit(err == CHIP_NO_ERROR,
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback size check failed");
status = Status::ConstraintError);

VerifyOrExit(
delegate->HasFeature(endpoint, Feature::kConfirmationResponse),
ChipLogProgress(
Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback responses sent but response feature not supported");
status = Status::InvalidCommand);

VerifyOrExit(size <= kMessageMaxOptionCount,
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback too many options");
status = Status::ConstraintError);

auto iter = responses.Value().begin();
while (iter.Next())
{
auto & response = iter.GetValue();

// response feature is checked above
VerifyOrExit(response.messageResponseID.HasValue() && response.label.HasValue(),
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback missing response id or label");
status = Status::InvalidCommand);

VerifyOrExit(response.messageResponseID.Value() >= kMessageResponseIdMin,
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback responseID value check failed");
status = Status::ConstraintError);

delegate->HandlePresentMessagesRequest(messageId, priority, messageControl, startTime, duration, messageText, responses);
VerifyOrExit(response.label.Value().size() <= kMessageResponseLabelMaxLength,
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback label length check failed");
status = Status::ConstraintError);
}
VerifyOrExit(iter.GetStatus() == CHIP_NO_ERROR,
ChipLogProgress(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback TLV parsing error");
status = Status::InvalidAction);
}

err = delegate->HandlePresentMessagesRequest(messageId, priority, messageControl, startTime, duration, messageText, responses);

exit:
if (err != CHIP_NO_ERROR)
Expand Down Expand Up @@ -214,7 +263,21 @@ bool emberAfMessagesClusterCancelMessagesRequestCallback(
Delegate * delegate = GetDelegate(endpoint);
VerifyOrExit(isDelegateNull(delegate, endpoint) != true, err = CHIP_ERROR_INCORRECT_STATE);

delegate->HandleCancelMessagesRequest(messageIds);
{
auto iter = messageIds.begin();
while (iter.Next())
{
auto & id = iter.GetValue();
VerifyOrExit(id.size() >= kMessageIdLength,
ChipLogProgress(Zcl, "emberAfMessagesClusterCancelMessagesRequestCallback message id size check failed");
status = Status::ConstraintError);
}
VerifyOrExit(iter.GetStatus() == CHIP_NO_ERROR,
ChipLogProgress(Zcl, "emberAfMessagesClusterCancelMessagesRequestCallback TLV parsing error");
status = Status::InvalidAction);
}

err = delegate->HandleCancelMessagesRequest(messageIds);

exit:
if (err != CHIP_NO_ERROR)
Expand Down

0 comments on commit 23d1b4a

Please sign in to comment.