Skip to content

Commit c88d5cf

Browse files
andy31415andreilitvinbzbarsky-apple
authored
Implement DataModel based read support in the interaction model engine. (project-chip#34419)
* Implement DataModel based read support in the interacton model engine. This implements reads via ember and via codegen as well as a checked-mode where reads are validated as returning the same status codes and data size (because data content is not available) * Comment update and logic update: we MAY get different sizes if data size changes for numbers * Fix typo for ember implementation while renaming things * Fix override markers * run_tv_casting_test should be executable * Do not report errors on chunking * Typo fix * move chipDie for tests only * move all chipdie to unit test only * fix comment and formatting * Update encoderstate logic a bit - code is cleaner, will have to see if CI tests pass * Restyle * Enable tracing of messages for tv tests, to see what is going on in CI * Restyle * Start adding some log line processing for the tv tests, to have propper timeouts and not block on IO buffers * Significant reformat and start refactoring the tv casting test * TV tests pass * Restyle * Fix ruff * Review comment update: set state in all cases * Added a TODO regarding the awkward "callback on success only" * Merge fix * Update src/app/reporting/Read-Checked.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Review updates * Fix placement of dm state * Restyle * Code review comments: double-check that IM not active when setting model, explain why we have the ifdef * Code review: comment why we did not re-use code * Code review feedback: warn if running in checked mode * Restyle * Avoid loop of err/out empty output * Support a log directory argument for the casting tests, so I can debug their content * Better debuggability and error reporting support for shell - this is to debug cast failures --------- Co-authored-by: Andrei Litvin <andreilitvin@google.com> Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent 8ad0bdf commit c88d5cf

28 files changed

+953
-64
lines changed

scripts/tests/linux/log_line_processing.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import select
1818
import subprocess
1919
import threading
20+
import time
2021
from typing import List
2122

2223

@@ -57,23 +58,31 @@ def _io_thread(self):
5758
reading
5859
"""
5960
out_wait = select.poll()
60-
out_wait.register(self.process.stdout, select.POLLIN)
61+
out_wait.register(self.process.stdout, select.POLLIN | select.POLLHUP)
6162

6263
err_wait = select.poll()
63-
err_wait.register(self.process.stderr, select.POLLIN)
64+
err_wait.register(self.process.stderr, select.POLLIN | select.POLLHUP)
6465

6566
with open(self.output_path, "wt") as f:
67+
f.write("PROCESS START: %s\n" % time.ctime())
6668
while not self.done:
6769
changes = out_wait.poll(0.1)
6870
if changes:
6971
out_line = self.process.stdout.readline()
72+
if not out_line:
73+
# stdout closed (otherwise readline should have at least \n)
74+
continue
7075
f.write(out_line)
7176
self.output_lines.put(out_line)
7277

7378
changes = err_wait.poll(0)
7479
if changes:
7580
err_line = self.process.stderr.readline()
81+
if not err_line:
82+
# stderr closed (otherwise readline should have at least \n)
83+
continue
7684
f.write(f"!!STDERR!! : {err_line}")
85+
f.write("PROCESS END: %s\n" % time.ctime())
7786

7887
def __enter__(self):
7988
self.done = False

scripts/tests/run_tv_casting_test.py

+17-5
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,14 @@ def cmd_execute_list(app_path):
295295
default=False,
296296
help="Enable the commissioner generated passcode test flow.",
297297
)
298+
@click.option(
299+
"--log-directory",
300+
type=str,
301+
default=None,
302+
help="Where to place output logs",
303+
)
298304
def test_casting_fn(
299-
tv_app_rel_path, tv_casting_app_rel_path, commissioner_generated_passcode
305+
tv_app_rel_path, tv_casting_app_rel_path, commissioner_generated_passcode, log_directory
300306
):
301307
"""Test if the casting experience between the Linux tv-casting-app and the Linux tv-app continues to work.
302308
@@ -320,10 +326,16 @@ def test_casting_fn(
320326

321327
# Store the log files to a temporary directory.
322328
with tempfile.TemporaryDirectory() as temp_dir:
323-
linux_tv_app_log_path = os.path.join(temp_dir, LINUX_TV_APP_LOGS)
324-
linux_tv_casting_app_log_path = os.path.join(
325-
temp_dir, LINUX_TV_CASTING_APP_LOGS
326-
)
329+
if log_directory:
330+
linux_tv_app_log_path = os.path.join(log_directory, LINUX_TV_APP_LOGS)
331+
linux_tv_casting_app_log_path = os.path.join(
332+
log_directory, LINUX_TV_CASTING_APP_LOGS
333+
)
334+
else:
335+
linux_tv_app_log_path = os.path.join(temp_dir, LINUX_TV_APP_LOGS)
336+
linux_tv_casting_app_log_path = os.path.join(
337+
temp_dir, LINUX_TV_CASTING_APP_LOGS
338+
)
327339

328340
# Get all the test sequences.
329341
test_sequences = Sequence.get_test_sequences()

src/app/AttributeValueEncoder.h

+1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ class AttributeValueEncoder
162162
private:
163163
// We made EncodeListItem() private, and ListEncoderHelper will expose it by Encode()
164164
friend class ListEncodeHelper;
165+
friend class TestOnlyAttributeValueEncoderAccessor;
165166

166167
template <typename... Ts>
167168
CHIP_ERROR EncodeListItem(Ts &&... aArgs)

src/app/BUILD.gn

+24-10
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,6 @@ declare_args() {
5858
chip_im_static_global_interaction_model_engine =
5959
current_os != "linux" && current_os != "mac" && current_os != "ios" &&
6060
current_os != "android"
61-
62-
# Data model interface usage:
63-
# - disabled: does not use data model interface at all
64-
# - check: runs BOTH datamodel and non-data-model (if possible) functionality and compares results
65-
# - enabled: runs only the data model interface (does not use the legacy code)
66-
if (current_os == "linux") {
67-
chip_use_data_model_interface = "check"
68-
} else {
69-
chip_use_data_model_interface = "disabled"
70-
}
7161
}
7262

7363
buildconfig_header("app_buildconfig") {
@@ -219,6 +209,7 @@ static_library("interaction-model") {
219209
"WriteClient.h",
220210
"reporting/Engine.cpp",
221211
"reporting/Engine.h",
212+
"reporting/Read.h",
222213
"reporting/ReportScheduler.h",
223214
"reporting/ReportSchedulerImpl.cpp",
224215
"reporting/ReportSchedulerImpl.h",
@@ -260,6 +251,29 @@ static_library("interaction-model") {
260251

261252
public_configs = [ "${chip_root}/src:includes" ]
262253

254+
if (chip_use_data_model_interface == "disabled") {
255+
sources += [
256+
"reporting/Read-Ember.cpp",
257+
"reporting/Read-Ember.h",
258+
]
259+
} else if (chip_use_data_model_interface == "check") {
260+
sources += [
261+
"reporting/Read-Checked.cpp",
262+
"reporting/Read-Checked.h",
263+
"reporting/Read-DataModel.cpp",
264+
"reporting/Read-DataModel.h",
265+
"reporting/Read-Ember.cpp",
266+
"reporting/Read-Ember.h",
267+
]
268+
public_deps += [ "${chip_root}/src/app/data-model-interface" ]
269+
} else { # enabled
270+
sources += [
271+
"reporting/Read-DataModel.cpp",
272+
"reporting/Read-DataModel.h",
273+
]
274+
public_deps += [ "${chip_root}/src/app/data-model-interface" ]
275+
}
276+
263277
if (chip_enable_read_client) {
264278
sources += [ "ReadClient.cpp" ]
265279
}

src/app/InteractionModelEngine.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ CHIP_ERROR InteractionModelEngine::Init(Messaging::ExchangeManager * apExchangeM
9393

9494
StatusIB::RegisterErrorFormatter();
9595

96+
#if CHIP_CONFIG_USE_EMBER_DATA_MODEL && CHIP_CONFIG_USE_DATA_MODEL_INTERFACE
97+
ChipLogError(InteractionModel, "WARNING ┌────────────────────────────────────────────────────");
98+
ChipLogError(InteractionModel, "WARNING │ Interaction Model Engine running in 'Checked' mode.");
99+
ChipLogError(InteractionModel, "WARNING │ This executes BOTH ember and data-model code paths.");
100+
ChipLogError(InteractionModel, "WARNING │ which is inefficient and consumes more flash space.");
101+
ChipLogError(InteractionModel, "WARNING │ This should be done for testing only.");
102+
ChipLogError(InteractionModel, "WARNING └────────────────────────────────────────────────────");
103+
#endif
104+
96105
return CHIP_NO_ERROR;
97106
}
98107

@@ -1697,6 +1706,16 @@ Protocols::InteractionModel::Status InteractionModelEngine::CommandExists(const
16971706
return ServerClusterCommandExists(aCommandPath);
16981707
}
16991708

1709+
InteractionModel::DataModel * InteractionModelEngine::SetDataModel(InteractionModel::DataModel * model)
1710+
{
1711+
// Alternting data model should not be done while IM is actively handling requests.
1712+
VerifyOrDie(mReadHandlers.begin() == mReadHandlers.end());
1713+
1714+
InteractionModel::DataModel * oldModel = GetDataModel();
1715+
mDataModel = model;
1716+
return oldModel;
1717+
}
1718+
17001719
InteractionModel::DataModel * InteractionModelEngine::GetDataModel() const
17011720
{
17021721
#if CHIP_CONFIG_USE_DATA_MODEL_INTERFACE

src/app/InteractionModelEngine.h

+7-4
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,6 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
125125
* @param[in] apFabricTable A pointer to the FabricTable object.
126126
* @param[in] apCASESessionMgr An optional pointer to a CASESessionManager (used for re-subscriptions).
127127
*
128-
* @retval #CHIP_ERROR_INCORRECT_STATE If the state is not equal to
129-
* kState_NotInitialized.
130-
* @retval #CHIP_NO_ERROR On success.
131-
*
132128
*/
133129
CHIP_ERROR Init(Messaging::ExchangeManager * apExchangeMgr, FabricTable * apFabricTable,
134130
reporting::ReportScheduler * reportScheduler, CASESessionManager * apCASESessionMgr = nullptr,
@@ -408,6 +404,13 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
408404

409405
InteractionModel::DataModel * GetDataModel() const;
410406

407+
// MUST NOT be used while the interaction model engine is running as interaction
408+
// model functionality (e.g. active reads/writes/subscriptions) rely on data model
409+
// state
410+
//
411+
// Returns the old data model value.
412+
InteractionModel::DataModel * SetDataModel(InteractionModel::DataModel * model);
413+
411414
private:
412415
friend class reporting::Engine;
413416
friend class TestCommandInteraction;

src/app/common_flags.gni

+10
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,14 @@ declare_args() {
2121
# Flag that controls whether the time-to-wait from BUSY responses is
2222
# communicated to OperationalSessionSetup API consumers.
2323
chip_enable_busy_handling_for_operational_session_setup = true
24+
25+
# Data model interface usage:
26+
# - disabled: does not use data model interface at all
27+
# - check: runs BOTH datamodel and non-data-model (if possible) functionality and compares results
28+
# - enabled: runs only the data model interface (does not use the legacy code)
29+
if (current_os == "linux") {
30+
chip_use_data_model_interface = "check"
31+
} else {
32+
chip_use_data_model_interface = "disabled"
33+
}
2434
}

src/app/data-model-interface/OperationTypes.h

-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ enum class ReadFlags : uint32_t
6565
struct ReadAttributeRequest : OperationRequest
6666
{
6767
ConcreteAttributePath path;
68-
std::optional<DataVersion> dataVersion;
6968
BitFlags<ReadFlags> readFlags;
7069
};
7170

src/app/reporting/Engine.cpp

+3-21
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <app/InteractionModelEngine.h>
3232
#include <app/RequiredPrivilege.h>
3333
#include <app/reporting/Engine.h>
34+
#include <app/reporting/Read.h>
3435
#include <app/util/MatterCallbacks.h>
3536
#include <app/util/ember-compatibility-functions.h>
3637

@@ -79,25 +80,6 @@ bool Engine::IsClusterDataVersionMatch(const SingleLinkedListNode<DataVersionFil
7980
return existPathMatch && !existVersionMismatch;
8081
}
8182

82-
CHIP_ERROR
83-
Engine::RetrieveClusterData(const SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
84-
AttributeReportIBs::Builder & aAttributeReportIBs, const ConcreteReadAttributePath & aPath,
85-
AttributeEncodeState * aEncoderState)
86-
{
87-
ChipLogDetail(DataManagement, "<RE:Run> Cluster %" PRIx32 ", Attribute %" PRIx32 " is dirty", aPath.mClusterId,
88-
aPath.mAttributeId);
89-
90-
DataModelCallbacks::GetInstance()->AttributeOperation(DataModelCallbacks::OperationType::Read,
91-
DataModelCallbacks::OperationOrder::Pre, aPath);
92-
93-
ReturnErrorOnFailure(ReadSingleClusterData(aSubjectDescriptor, aIsFabricFiltered, aPath, aAttributeReportIBs, aEncoderState));
94-
95-
DataModelCallbacks::GetInstance()->AttributeOperation(DataModelCallbacks::OperationType::Read,
96-
DataModelCallbacks::OperationOrder::Post, aPath);
97-
98-
return CHIP_NO_ERROR;
99-
}
100-
10183
static bool IsOutOfWriterSpaceError(CHIP_ERROR err)
10284
{
10385
return err == CHIP_ERROR_NO_MEMORY || err == CHIP_ERROR_BUFFER_TOO_SMALL;
@@ -200,8 +182,8 @@ CHIP_ERROR Engine::BuildSingleReportDataAttributeReportIBs(ReportDataMessage::Bu
200182
ConcreteReadAttributePath pathForRetrieval(readPath);
201183
// Load the saved state from previous encoding session for chunking of one single attribute (list chunking).
202184
AttributeEncodeState encodeState = apReadHandler->GetAttributeEncodeState();
203-
err = RetrieveClusterData(apReadHandler->GetSubjectDescriptor(), apReadHandler->IsFabricFiltered(), attributeReportIBs,
204-
pathForRetrieval, &encodeState);
185+
err = Impl::RetrieveClusterData(mpImEngine->GetDataModel(), apReadHandler->GetSubjectDescriptor(),
186+
apReadHandler->IsFabricFiltered(), attributeReportIBs, pathForRetrieval, &encodeState);
205187
if (err != CHIP_NO_ERROR)
206188
{
207189
// If error is not an "out of writer space" error, rollback and encode status.

src/app/reporting/Engine.h

-3
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,6 @@ class Engine
168168
bool * apHasMoreChunks, bool * apHasEncodedData);
169169
CHIP_ERROR BuildSingleReportDataEventReports(ReportDataMessage::Builder & reportDataBuilder, ReadHandler * apReadHandler,
170170
bool aBufferIsUsed, bool * apHasMoreChunks, bool * apHasEncodedData);
171-
CHIP_ERROR RetrieveClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
172-
AttributeReportIBs::Builder & aAttributeReportIBs,
173-
const ConcreteReadAttributePath & aClusterInfo, AttributeEncodeState * apEncoderState);
174171
CHIP_ERROR CheckAccessDeniedEventPaths(TLV::TLVWriter & aWriter, bool & aHasEncodedData, ReadHandler * apReadHandler);
175172

176173
// If version match, it means don't send, if version mismatch, it means send.

0 commit comments

Comments
 (0)