1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16
17#include "CommandLineInterface.h"
18
19#include <chrono>
20#include <regex>
21
22#include "CommandLine.h"
23#include "CommandLineFactory.h"
24#include "ModelManager.h"
25#include "PreviewerEngineLog.h"
26#include "VirtualScreen.h"
27#include "CommandParser.h"
28
29const std::string CommandLineInterface::COMMAND_VERSION = "1.0.1";
30bool CommandLineInterface::isFirstWsSend = true;
31bool CommandLineInterface::isPipeConnected = false;
32CommandLineInterface::CommandLineInterface() : socket(nullptr) {}
33
34CommandLineInterface::~CommandLineInterface() {}
35
36void CommandLineInterface::InitPipe(const std::string name)
37{
38    if (socket != nullptr) {
39        socket.reset();
40        ELOG("CommandLineInterface::InitPipe socket is not null");
41    }
42
43    socket = std::make_unique<LocalSocket>();
44    if (socket == nullptr) {
45        FLOG("CommandLineInterface::Connect socket memory allocation failed!");
46    }
47
48    if (!socket->ConnectToServer(socket->GetCommandPipeName(name), LocalSocket::READ_WRITE)) {
49        FLOG("CommandLineInterface command pipe connect failed");
50    }
51    isPipeConnected  = true;
52}
53
54CommandLineInterface& CommandLineInterface::GetInstance()
55{
56    static CommandLineInterface instance; /* NOLINT */
57    return instance;
58}
59
60void CommandLineInterface::SendJsonData(const Json2::Value& value)
61{
62    *(GetInstance().socket) << value.ToStyledString();
63}
64
65void CommandLineInterface::SendJSHeapMemory(size_t total, size_t alloc, size_t peak) const
66{
67    Json2::Value result = JsonReader::CreateObject();
68    result.Add("version", COMMAND_VERSION.c_str());
69    result.Add("property", "memoryUsage");
70    Json2::Value memory = JsonReader::CreateObject();
71    memory.Add("totalBytes", static_cast<double>(total));
72    memory.Add("allocBytes", static_cast<double>(alloc));
73    memory.Add("peakAllocBytes", static_cast<double>(peak));
74    result.Add("result", memory);
75    if (socket == nullptr) {
76        ELOG("CommandLineInterface::SendJSHeapMemory socket is null");
77        return;
78    }
79    *socket << result.ToStyledString();
80}
81
82void CommandLineInterface::SendWebsocketStartupSignal() const
83{
84    Json2::Value result = JsonReader::CreateObject();
85    Json2::Value args = JsonReader::CreateObject();
86    result.Add("MessageType", "imageWebsocket");
87    args.Add("port", VirtualScreen::webSocketPort.c_str());
88    result.Add("args", args);
89    *socket << result.ToStyledString();
90}
91
92void CommandLineInterface::ProcessCommand() const
93{
94    std::string message; /* NOLINT */
95    if (socket == nullptr) {
96        ELOG("CommandLineInterface::ProcessCommand socket is null");
97        return;
98    }
99    if (isPipeConnected && VirtualScreen::isWebSocketListening && isFirstWsSend) {
100        isFirstWsSend = false;
101        SendWebsocketStartupSignal();
102    }
103    *socket >> message;
104    if (message.empty()) {
105        return;
106    }
107
108    ProcessCommandMessage(message);
109}
110
111void CommandLineInterface::ProcessCommandMessage(std::string message) const
112{
113    ILOG("***cmd*** message:%s", message.c_str());
114    Json2::Value jsonData = JsonReader::ParseJsonData2(message);
115    std::string errors; /* NOLINT */
116    bool parsingSuccessful = jsonData.IsNull() ? false : true;
117    if (!parsingSuccessful) {
118        errors = JsonReader::GetErrorPtr();
119    }
120
121    if (!ProcessCommandValidate(parsingSuccessful, jsonData, errors)) {
122        return;
123    }
124
125    CommandLine::CommandType type = GetCommandType(jsonData["type"].AsString());
126    if (type == CommandLine::CommandType::INVALID) {
127        return;
128    }
129
130    std::string command = jsonData["command"].AsString();
131    if (CommandParser::GetInstance().IsStaticCard() && IsStaticIgnoreCmd(command)) {
132        return;
133    }
134    Json2::Value val = jsonData["args"];
135    std::unique_ptr<CommandLine> commandLine =
136        CommandLineFactory::CreateCommandLine(command, type, val, *socket);
137    if (commandLine == nullptr) {
138        ELOG("Unsupported command");
139        return;
140    }
141    commandLine->CheckAndRun();
142}
143
144bool CommandLineInterface::ProcessCommandValidate(bool parsingSuccessful,
145                                                  const Json2::Value& jsonData,
146                                                  const std::string& errors) const
147{
148    if (!parsingSuccessful) {
149        ELOG("Failed to parse the JSON, errors: %s", errors.c_str());
150        return false;
151    }
152
153    if (!jsonData.IsObject()) {
154        ELOG("Command is not a object!");
155        return false;
156    }
157
158    if (!jsonData.IsMember("type") || !jsonData.IsMember("command") || !jsonData.IsMember("version")) {
159        ELOG("Command error!");
160        return false;
161    }
162
163    if (!regex_match(jsonData["version"].AsString(),
164        std::regex("(([0-9]|([1-9]([0-9]*))).){2}([0-9]|([1-9]([0-9]*)))"))) {
165        ELOG("Invalid command version!");
166        return false;
167    }
168    return true;
169}
170
171CommandLine::CommandType CommandLineInterface::GetCommandType(std::string name) const
172{
173    CommandLine::CommandType type = CommandLine::CommandType::INVALID;
174    if (name == "set") {
175        type = CommandLine::CommandType::SET;
176    } else if (name == "get") {
177        type = CommandLine::CommandType::GET;
178    } else if (name == "action") {
179        type = CommandLine::CommandType::ACTION;
180    } else {
181        ELOG("Command type invalid!");
182    }
183    return type;
184}
185
186void CommandLineInterface::ApplyConfig(const Json2::Value& val) const
187{
188    const std::string set("setting");
189    if (val.IsMember(set.c_str())) {
190        Json2::Value versionMembers = val[set];
191        if (!versionMembers.IsObject()) {
192            return;
193        }
194
195        Json2::Value::Members versions = versionMembers.GetMemberNames();
196
197        for (Json2::Value::Members::iterator viter = versions.begin(); viter != versions.end(); viter++) {
198            std::string version = *viter;
199            Json2::Value commands = versionMembers[version];
200            if (!commands.IsObject()) {
201                continue;
202            }
203            Json2::Value::Members members = commands.GetMemberNames();
204
205            ApplyConfigMembers(commands, members);
206        }
207    }
208}
209
210void CommandLineInterface::ApplyConfigMembers(const Json2::Value& commands,
211                                              const Json2::Value::Members& members) const
212{
213    for (Json2::Value::Members::const_iterator iter = members.begin(); iter != members.end(); iter++)  {
214        std::string key = *iter;
215        if (!commands[key].IsObject() || !commands[key].IsMember("args") || !commands[key]["args"].IsObject()) {
216            ELOG("Invalid JSON: %s", commands[key].AsString().c_str());
217            continue;
218        }
219        Json2::Value val = commands[key]["args"];
220        std::unique_ptr<CommandLine> command =
221            CommandLineFactory::CreateCommandLine(key, CommandLine::CommandType::SET, val, *socket);
222        ApplyConfigCommands(key, command);
223    }
224}
225
226void CommandLineInterface::ApplyConfigCommands(const std::string& key,
227                                               const std::unique_ptr<CommandLine>& command) const
228{
229    if (command == nullptr) {
230        ELOG("Unsupported configuration: %s", key.c_str());
231        return;
232    }
233
234    if (command->IsArgValid()) {
235        command->RunSet();
236    }
237}
238
239void CommandLineInterface::Init(std::string pipeBaseName)
240{
241    CommandLineFactory::InitCommandMap();
242    InitPipe(pipeBaseName);
243}
244
245void CommandLineInterface::ReadAndApplyConfig(std::string path) const
246{
247    if (path.empty()) {
248        return;
249    }
250    std::string jsonStr = JsonReader::ReadFile(path);
251    Json2::Value val = JsonReader::ParseJsonData2(jsonStr);
252    ApplyConfig(val);
253}
254
255void CommandLineInterface::CreatCommandToSendData(const std::string commandName,
256                                                  const Json2::Value& jsonData,
257                                                  const std::string type) const
258{
259    CommandLine::CommandType commandType = GetCommandType(type);
260    std::unique_ptr<CommandLine> commandLine =
261        CommandLineFactory::CreateCommandLine(commandName, commandType, jsonData, *socket);
262    if (commandLine == nullptr) {
263        ELOG("Unsupported CreatCommandToSendData: %s", commandName.c_str());
264        return;
265    }
266    commandLine->RunAndSendResultToManager();
267}
268
269bool CommandLineInterface::IsStaticIgnoreCmd(const std::string cmd) const
270{
271    auto it = std::find(staticIgnoreCmd.begin(), staticIgnoreCmd.end(), cmd);
272    if (it != staticIgnoreCmd.end()) {
273        return false;
274    } else {
275        return true;
276    }
277}
278