Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linux sample implementation for Messages cluster #32043

Merged
merged 25 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e33eff5
Linux sample implementation for Messages cluster
chrisdecenzo Feb 9, 2024
326644f
cleanup the chip:: and chip::app prefixes
chrisdecenzo Feb 9, 2024
0dabc8c
Merge branch 'master' into tv2-6
lazarkov Feb 9, 2024
caea32b
CI fixes
chrisdecenzo Feb 9, 2024
0e94261
Merge branch 'tv2-6' of github.com:project-chip/connectedhomeip into …
chrisdecenzo Feb 9, 2024
222019f
Merge branch 'master' into tv2-6
lazarkov Feb 12, 2024
18fe638
Address comments
chrisdecenzo Feb 13, 2024
e716867
Merge branch 'tv2-6' of github.com:project-chip/connectedhomeip into …
chrisdecenzo Feb 13, 2024
784b9a2
Make memory management less error prone
chrisdecenzo Feb 14, 2024
3681d6d
Fix CI
chrisdecenzo Feb 14, 2024
66a31cb
more feedback
chrisdecenzo Feb 14, 2024
47809aa
fix CI
chrisdecenzo Feb 14, 2024
6183338
Restyled by clang-format (#32123)
restyled-io[bot] Feb 14, 2024
d976b01
address comments
chrisdecenzo Feb 15, 2024
4b08069
address comments
chrisdecenzo Feb 15, 2024
4e3b2d8
Restyled by clang-format (#32136)
restyled-io[bot] Feb 15, 2024
d759a6f
fix CI
chrisdecenzo Feb 15, 2024
9fb2f0b
address comments
chrisdecenzo Feb 15, 2024
760d0c5
address comments
chrisdecenzo Feb 15, 2024
8e8bcff
Restyled by clang-format (#32144)
restyled-io[bot] Feb 15, 2024
0c2fa27
address comments
chrisdecenzo Feb 15, 2024
01ebed8
address comments
chrisdecenzo Feb 15, 2024
ccac074
address comments
chrisdecenzo Feb 15, 2024
98b39ee
Restyled by clang-format (#32148)
restyled-io[bot] Feb 15, 2024
9c71610
Merge branch 'master' into tv2-6
lazarkov Feb 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
}

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
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
}
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
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;
andy31415 marked this conversation as resolved.
Show resolved Hide resolved
};
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,
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
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(),
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
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
Loading