Skip to content

Commit 621bbde

Browse files
authored
Update examples/placeholder to supports an interactive websocket mode (project-chip#24652)
1 parent 09ea936 commit 621bbde

File tree

7 files changed

+239
-1
lines changed

7 files changed

+239
-1
lines changed

examples/placeholder/linux/AppOptions.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ using chip::ArgParser::OptionSet;
2323
using chip::ArgParser::PrintArgError;
2424

2525
constexpr uint16_t kOptionDacProviderFilePath = 0xFF01;
26+
constexpr uint16_t kOptionInteractiveMode = 0xFF02;
27+
constexpr uint16_t kOptionInteractiveModePort = 0xFF03;
2628

2729
static chip::Credentials::Examples::TestHarnessDACProvider mDacProvider;
30+
static bool gInteractiveMode = false;
31+
static chip::Optional<uint16_t> gInteractiveModePort;
2832

2933
bool AppOptions::HandleOptions(const char * program, OptionSet * options, int identifier, const char * name, const char * value)
3034
{
@@ -34,6 +38,12 @@ bool AppOptions::HandleOptions(const char * program, OptionSet * options, int id
3438
case kOptionDacProviderFilePath:
3539
mDacProvider.Init(value);
3640
break;
41+
case kOptionInteractiveMode:
42+
gInteractiveMode = true;
43+
break;
44+
case kOptionInteractiveModePort:
45+
gInteractiveModePort = chip::MakeOptional(static_cast<uint16_t>(atoi(value)));
46+
break;
3747
default:
3848
PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", program, name);
3949
retval = false;
@@ -47,13 +57,19 @@ OptionSet * AppOptions::GetOptions()
4757
{
4858
static OptionDef optionsDef[] = {
4959
{ "dac_provider", chip::ArgParser::kArgumentRequired, kOptionDacProviderFilePath },
60+
{ "interactive", chip::ArgParser::kNoArgument, kOptionInteractiveMode },
61+
{ "port", chip::ArgParser::kArgumentRequired, kOptionInteractiveModePort },
5062
{},
5163
};
5264

5365
static OptionSet options = {
5466
AppOptions::HandleOptions, optionsDef, "PROGRAM OPTIONS",
5567
" --dac_provider <filepath>\n"
5668
" A json file with data used by the example dac provider to validate device attestation procedure.\n"
69+
" --interactive\n"
70+
" Enable server interactive mode.\n"
71+
" --port <port>\n"
72+
" Specify the listening port for the server interactive mode.\n"
5773
};
5874

5975
return &options;
@@ -63,3 +79,13 @@ chip::Credentials::DeviceAttestationCredentialsProvider * AppOptions::GetDACProv
6379
{
6480
return &mDacProvider;
6581
}
82+
83+
bool AppOptions::GetInteractiveMode()
84+
{
85+
return gInteractiveMode;
86+
}
87+
88+
chip::Optional<uint16_t> AppOptions::GetInteractiveModePort()
89+
{
90+
return gInteractiveModePort;
91+
}

examples/placeholder/linux/AppOptions.h

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class AppOptions
2727
public:
2828
static chip::ArgParser::OptionSet * GetOptions();
2929
static chip::Credentials::DeviceAttestationCredentialsProvider * GetDACProvider();
30+
static bool GetInteractiveMode();
31+
static chip::Optional<uint16_t> GetInteractiveModePort();
3032

3133
private:
3234
static bool HandleOptions(const char * program, chip::ArgParser::OptionSet * options, int identifier, const char * name,

examples/placeholder/linux/BUILD.gn

+5-1
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,23 @@ chip_data_model("configuration") {
3131
config("includes") {
3232
include_dirs = [
3333
".",
34+
"${chip_root}/examples/common",
3435
"include",
3536
]
3637
}
3738

3839
executable("chip-${chip_tests_zap_config}") {
3940
sources = [
4041
"AppOptions.cpp",
42+
"InteractiveServer.cpp",
4143
"main.cpp",
4244
"src/bridged-actions-stub.cpp",
4345
"static-supported-modes-manager.cpp",
4446
]
4547

4648
deps = [
4749
":configuration",
50+
"${chip_root}/examples/common/websocket-server",
4851
"${chip_root}/examples/platform/linux:app-main",
4952
"${chip_root}/src/app/tests/suites/commands/delay",
5053
"${chip_root}/src/app/tests/suites/commands/discovery",
@@ -54,9 +57,10 @@ executable("chip-${chip_tests_zap_config}") {
5457
"${chip_root}/src/lib",
5558
"${chip_root}/src/lib/support:testing", # For sleepMillis. TODO: this is
5659
# odd and should be fixed
60+
"${chip_root}/third_party/jsoncpp",
5761
]
5862

59-
include_dirs = [ "include" ]
63+
public_configs = [ ":includes" ]
6064

6165
cflags = [ "-Wconversion" ]
6266

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
*
3+
* Copyright (c) 2023 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#include "InteractiveServer.h"
20+
21+
#include <json/json.h>
22+
#include <platform/CHIPDeviceLayer.h>
23+
24+
using namespace chip::DeviceLayer;
25+
26+
namespace {
27+
constexpr const char * kClusterIdKey = "clusterId";
28+
constexpr const char * kEndpointIdKey = "endpointId";
29+
constexpr const char * kAttributeIdKey = "attributeId";
30+
constexpr const char * kWaitTypeKey = "waitType";
31+
constexpr const char * kAttributeWriteKey = "writeAttribute";
32+
constexpr const char * kAttributeReadKey = "readAttribute";
33+
constexpr const char * kCommandIdKey = "commandId";
34+
constexpr const char * kWaitForCommissioningCommand = "WaitForCommissioning";
35+
36+
std::string JsonToString(Json::Value & json)
37+
{
38+
Json::FastWriter writer;
39+
writer.omitEndingLineFeed();
40+
return writer.write(json);
41+
}
42+
43+
void OnPlatformEvent(const ChipDeviceEvent * event, intptr_t arg);
44+
45+
void OnCommissioningComplete(intptr_t context)
46+
{
47+
PlatformMgr().RemoveEventHandler(OnPlatformEvent);
48+
InteractiveServer::GetInstance().CommissioningComplete();
49+
}
50+
51+
void OnPlatformEvent(const ChipDeviceEvent * event, intptr_t arg)
52+
{
53+
switch (event->Type)
54+
{
55+
case DeviceEventType::kCommissioningComplete:
56+
PlatformMgr().ScheduleWork(OnCommissioningComplete, arg);
57+
break;
58+
}
59+
}
60+
} // namespace
61+
62+
InteractiveServer * InteractiveServer::instance = nullptr;
63+
InteractiveServer & InteractiveServer::GetInstance()
64+
{
65+
if (instance == nullptr)
66+
{
67+
instance = new InteractiveServer();
68+
}
69+
return *instance;
70+
}
71+
72+
void InteractiveServer::Run(const chip::Optional<uint16_t> port)
73+
{
74+
mIsReady = false;
75+
wsThread = std::thread(&WebSocketServer::Run, &mWebSocketServer, port, this);
76+
}
77+
78+
bool InteractiveServer::OnWebSocketMessageReceived(char * msg)
79+
{
80+
ChipLogError(chipTool, "Receive message: %s", msg);
81+
if (strcmp(msg, kWaitForCommissioningCommand) == 0)
82+
{
83+
mIsReady = false;
84+
PlatformMgr().AddEventHandler(OnPlatformEvent);
85+
}
86+
else
87+
{
88+
mIsReady = true;
89+
}
90+
return true;
91+
}
92+
93+
bool InteractiveServer::Command(const chip::app::ConcreteCommandPath & path)
94+
{
95+
VerifyOrReturnValue(mIsReady, false);
96+
97+
Json::Value value;
98+
value[kClusterIdKey] = path.mClusterId;
99+
value[kEndpointIdKey] = path.mEndpointId;
100+
value[kCommandIdKey] = path.mCommandId;
101+
102+
auto valueStr = JsonToString(value);
103+
LogErrorOnFailure(mWebSocketServer.Send(valueStr.c_str()));
104+
return mIsReady;
105+
}
106+
107+
bool InteractiveServer::ReadAttribute(const chip::app::ConcreteAttributePath & path)
108+
{
109+
VerifyOrReturnValue(mIsReady, false);
110+
111+
Json::Value value;
112+
value[kClusterIdKey] = path.mClusterId;
113+
value[kEndpointIdKey] = path.mEndpointId;
114+
value[kAttributeIdKey] = path.mAttributeId;
115+
value[kWaitTypeKey] = kAttributeReadKey;
116+
117+
auto valueStr = JsonToString(value);
118+
LogErrorOnFailure(mWebSocketServer.Send(valueStr.c_str()));
119+
return mIsReady;
120+
}
121+
122+
bool InteractiveServer::WriteAttribute(const chip::app::ConcreteAttributePath & path)
123+
{
124+
VerifyOrReturnValue(mIsReady, false);
125+
126+
Json::Value value;
127+
value[kClusterIdKey] = path.mClusterId;
128+
value[kEndpointIdKey] = path.mEndpointId;
129+
value[kAttributeIdKey] = path.mAttributeId;
130+
value[kWaitTypeKey] = kAttributeWriteKey;
131+
132+
auto valueStr = JsonToString(value);
133+
LogErrorOnFailure(mWebSocketServer.Send(valueStr.c_str()));
134+
return mIsReady;
135+
}
136+
137+
void InteractiveServer::CommissioningComplete()
138+
{
139+
VerifyOrReturn(!mIsReady);
140+
mIsReady = true;
141+
142+
Json::Value value = Json::objectValue;
143+
auto valueStr = JsonToString(value);
144+
LogErrorOnFailure(mWebSocketServer.Send(valueStr.c_str()));
145+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
*
3+
* Copyright (c) 2023 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#pragma once
20+
21+
#include <app/ConcreteAttributePath.h>
22+
#include <app/ConcreteCommandPath.h>
23+
#include <thread>
24+
#include <websocket-server/WebSocketServer.h>
25+
26+
class InteractiveServer : public WebSocketServerDelegate
27+
{
28+
public:
29+
static InteractiveServer & GetInstance();
30+
void Run(const chip::Optional<uint16_t> port);
31+
32+
bool Command(const chip::app::ConcreteCommandPath & path);
33+
bool ReadAttribute(const chip::app::ConcreteAttributePath & path);
34+
bool WriteAttribute(const chip::app::ConcreteAttributePath & path);
35+
void CommissioningComplete();
36+
37+
/////////// WebSocketServerDelegate Interface /////////
38+
bool OnWebSocketMessageReceived(char * msg) override;
39+
40+
private:
41+
InteractiveServer(){};
42+
static InteractiveServer * instance;
43+
44+
WebSocketServer mWebSocketServer;
45+
std::thread wsThread;
46+
bool mIsReady;
47+
};

examples/placeholder/linux/include/MatterCallbacks.h

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#pragma once
2020

21+
#include "InteractiveServer.h"
2122
#include "Options.h"
2223

2324
#include <app/ConcreteAttributePath.h>
@@ -55,6 +56,8 @@ TestCommand * GetTargetTest()
5556
void MatterPostCommandReceivedCallback(const chip::app::ConcreteCommandPath & commandPath,
5657
const chip::Access::SubjectDescriptor & subjectDescriptor)
5758
{
59+
VerifyOrReturn(!InteractiveServer::GetInstance().Command(commandPath));
60+
5861
auto test = GetTargetTest();
5962
VerifyOrReturn(test != nullptr && test->isRunning);
6063

@@ -66,6 +69,8 @@ void MatterPostCommandReceivedCallback(const chip::app::ConcreteCommandPath & co
6669

6770
void MatterPostAttributeReadCallback(const chip::app::ConcreteAttributePath & attributePath)
6871
{
72+
VerifyOrReturn(!InteractiveServer::GetInstance().ReadAttribute(attributePath));
73+
6974
auto test = GetTargetTest();
7075
VerifyOrReturn(test != nullptr && test->isRunning);
7176

@@ -77,6 +82,8 @@ void MatterPostAttributeReadCallback(const chip::app::ConcreteAttributePath & at
7782

7883
void MatterPostAttributeWriteCallback(const chip::app::ConcreteAttributePath & attributePath)
7984
{
85+
VerifyOrReturn(!InteractiveServer::GetInstance().WriteAttribute(attributePath));
86+
8087
auto test = GetTargetTest();
8188
VerifyOrReturn(test != nullptr && test->isRunning);
8289

examples/placeholder/linux/main.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ int main(int argc, char * argv[])
3333
}
3434

3535
LinuxDeviceOptions::GetInstance().dacProvider = AppOptions::GetDACProvider();
36+
37+
auto & server = InteractiveServer::GetInstance();
38+
if (AppOptions::GetInteractiveMode())
39+
{
40+
server.Run(AppOptions::GetInteractiveModePort());
41+
}
42+
3643
ChipLinuxAppMainLoop();
3744
return 0;
3845
}

0 commit comments

Comments
 (0)