InteractiveServer.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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. #include "InteractiveServer.h"
  19. #include <json/json.h>
  20. #include <lib/support/Base64.h>
  21. #include <platform/CHIPDeviceLayer.h>
  22. #include <platform/logging/LogV.h>
  23. using namespace chip::DeviceLayer;
  24. namespace {
  25. constexpr const char * kClusterIdKey = "clusterId";
  26. constexpr const char * kEndpointIdKey = "endpointId";
  27. constexpr const char * kAttributeIdKey = "attributeId";
  28. constexpr const char * kWaitTypeKey = "waitType";
  29. constexpr const char * kAttributeWriteKey = "writeAttribute";
  30. constexpr const char * kAttributeReadKey = "readAttribute";
  31. constexpr const char * kCommandIdKey = "commandId";
  32. constexpr const char * kWaitForCommissioningCommand = "WaitForCommissioning";
  33. constexpr const char * kCategoryError = "Error";
  34. constexpr const char * kCategoryProgress = "Info";
  35. constexpr const char * kCategoryDetail = "Debug";
  36. constexpr const char * kCategoryAutomation = "Automation";
  37. struct InteractiveServerResultLog
  38. {
  39. std::string module;
  40. std::string message;
  41. std::string messageType;
  42. };
  43. struct InteractiveServerResult
  44. {
  45. bool mEnabled = false;
  46. std::vector<std::string> mResults;
  47. std::vector<InteractiveServerResultLog> mLogs;
  48. void Setup() { mEnabled = true; }
  49. void Reset()
  50. {
  51. mEnabled = false;
  52. mResults.clear();
  53. mLogs.clear();
  54. }
  55. void MaybeAddLog(const char * module, uint8_t category, const char * base64Message)
  56. {
  57. VerifyOrReturn(mEnabled);
  58. const char * messageType = nullptr;
  59. switch (category)
  60. {
  61. case chip::Logging::kLogCategory_Error:
  62. messageType = kCategoryError;
  63. break;
  64. case chip::Logging::kLogCategory_Progress:
  65. messageType = kCategoryProgress;
  66. break;
  67. case chip::Logging::kLogCategory_Detail:
  68. messageType = kCategoryDetail;
  69. break;
  70. case chip::Logging::kLogCategory_Automation:
  71. messageType = kCategoryAutomation;
  72. break;
  73. default:
  74. chipDie();
  75. break;
  76. }
  77. mLogs.push_back(InteractiveServerResultLog({ module, base64Message, messageType }));
  78. }
  79. void MaybeAddResult(const char * result)
  80. {
  81. VerifyOrReturn(mEnabled);
  82. mResults.push_back(result);
  83. }
  84. std::string AsJsonString() const
  85. {
  86. std::string resultsStr;
  87. if (mResults.size())
  88. {
  89. for (const auto & result : mResults)
  90. {
  91. resultsStr = resultsStr + result + ",";
  92. }
  93. // Remove last comma.
  94. resultsStr.pop_back();
  95. }
  96. std::string logsStr;
  97. if (mLogs.size())
  98. {
  99. // Log messages are encoded in base64 already, so it is safe to append the message
  100. // between double quotes, even if the original log message contains some.
  101. for (const auto & log : mLogs)
  102. {
  103. logsStr = logsStr + "{";
  104. logsStr = logsStr + " \"module\": \"" + log.module + "\",";
  105. logsStr = logsStr + " \"category\": \"" + log.messageType + "\",";
  106. logsStr = logsStr + " \"message\": \"" + log.message + "\"";
  107. logsStr = logsStr + "},";
  108. }
  109. // Remove last comma.
  110. logsStr.pop_back();
  111. }
  112. std::string jsonLog;
  113. jsonLog = jsonLog + "{";
  114. jsonLog = jsonLog + " \"results\": [" + resultsStr + "],";
  115. jsonLog = jsonLog + " \"logs\": [" + logsStr + "]";
  116. jsonLog = jsonLog + "}";
  117. return jsonLog;
  118. }
  119. };
  120. InteractiveServerResult gInteractiveServerResult;
  121. void ENFORCE_FORMAT(3, 0) InteractiveServerLoggingCallback(const char * module, uint8_t category, const char * msg, va_list args)
  122. {
  123. va_list args_copy;
  124. va_copy(args_copy, args);
  125. chip::Logging::Platform::LogV(module, category, msg, args);
  126. char message[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE];
  127. vsnprintf(message, sizeof(message), msg, args_copy);
  128. va_end(args_copy);
  129. char base64Message[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE * 2] = {};
  130. chip::Base64Encode(chip::Uint8::from_char(message), static_cast<uint16_t>(strlen(message)), base64Message);
  131. gInteractiveServerResult.MaybeAddLog(module, category, base64Message);
  132. }
  133. std::string JsonToString(Json::Value & json)
  134. {
  135. Json::FastWriter writer;
  136. writer.omitEndingLineFeed();
  137. return writer.write(json);
  138. }
  139. void OnPlatformEvent(const ChipDeviceEvent * event, intptr_t arg);
  140. void OnCommissioningComplete(intptr_t context)
  141. {
  142. PlatformMgr().RemoveEventHandler(OnPlatformEvent);
  143. InteractiveServer::GetInstance().CommissioningComplete();
  144. }
  145. void OnPlatformEvent(const ChipDeviceEvent * event, intptr_t arg)
  146. {
  147. switch (event->Type)
  148. {
  149. case DeviceEventType::kCommissioningComplete:
  150. PlatformMgr().ScheduleWork(OnCommissioningComplete, arg);
  151. break;
  152. }
  153. }
  154. } // namespace
  155. InteractiveServer * InteractiveServer::instance = nullptr;
  156. InteractiveServer & InteractiveServer::GetInstance()
  157. {
  158. if (instance == nullptr)
  159. {
  160. instance = new InteractiveServer();
  161. }
  162. return *instance;
  163. }
  164. void InteractiveServer::Run(const chip::Optional<uint16_t> port)
  165. {
  166. mIsReady = false;
  167. wsThread = std::thread(&WebSocketServer::Run, &mWebSocketServer, port, this);
  168. chip::Logging::SetLogRedirectCallback(InteractiveServerLoggingCallback);
  169. }
  170. bool InteractiveServer::OnWebSocketMessageReceived(char * msg)
  171. {
  172. ChipLogError(chipTool, "Receive message: %s", msg);
  173. gInteractiveServerResult.Setup();
  174. if (strcmp(msg, kWaitForCommissioningCommand) == 0)
  175. {
  176. mIsReady = false;
  177. PlatformMgr().AddEventHandler(OnPlatformEvent);
  178. }
  179. else
  180. {
  181. mIsReady = true;
  182. }
  183. return true;
  184. }
  185. bool InteractiveServer::Command(const chip::app::ConcreteCommandPath & path)
  186. {
  187. VerifyOrReturnValue(mIsReady, false);
  188. Json::Value value;
  189. value[kClusterIdKey] = path.mClusterId;
  190. value[kEndpointIdKey] = path.mEndpointId;
  191. value[kCommandIdKey] = path.mCommandId;
  192. auto valueStr = JsonToString(value);
  193. gInteractiveServerResult.MaybeAddResult(valueStr.c_str());
  194. mWebSocketServer.Send(gInteractiveServerResult.AsJsonString().c_str());
  195. gInteractiveServerResult.Reset();
  196. return mIsReady;
  197. }
  198. bool InteractiveServer::ReadAttribute(const chip::app::ConcreteAttributePath & path)
  199. {
  200. VerifyOrReturnValue(mIsReady, false);
  201. Json::Value value;
  202. value[kClusterIdKey] = path.mClusterId;
  203. value[kEndpointIdKey] = path.mEndpointId;
  204. value[kAttributeIdKey] = path.mAttributeId;
  205. value[kWaitTypeKey] = kAttributeReadKey;
  206. auto valueStr = JsonToString(value);
  207. gInteractiveServerResult.MaybeAddResult(valueStr.c_str());
  208. mWebSocketServer.Send(gInteractiveServerResult.AsJsonString().c_str());
  209. gInteractiveServerResult.Reset();
  210. return mIsReady;
  211. }
  212. bool InteractiveServer::WriteAttribute(const chip::app::ConcreteAttributePath & path)
  213. {
  214. VerifyOrReturnValue(mIsReady, false);
  215. Json::Value value;
  216. value[kClusterIdKey] = path.mClusterId;
  217. value[kEndpointIdKey] = path.mEndpointId;
  218. value[kAttributeIdKey] = path.mAttributeId;
  219. value[kWaitTypeKey] = kAttributeWriteKey;
  220. auto valueStr = JsonToString(value);
  221. gInteractiveServerResult.MaybeAddResult(valueStr.c_str());
  222. mWebSocketServer.Send(gInteractiveServerResult.AsJsonString().c_str());
  223. gInteractiveServerResult.Reset();
  224. return mIsReady;
  225. }
  226. void InteractiveServer::CommissioningComplete()
  227. {
  228. VerifyOrReturn(!mIsReady);
  229. mIsReady = true;
  230. Json::Value value = Json::objectValue;
  231. auto valueStr = JsonToString(value);
  232. gInteractiveServerResult.MaybeAddResult(valueStr.c_str());
  233. mWebSocketServer.Send(gInteractiveServerResult.AsJsonString().c_str());
  234. gInteractiveServerResult.Reset();
  235. }