YODAU 1.0
YEAR OF THE DEPEND ADULT UNDERGARMENT
Loading...
Searching...
No Matches
cli_client.cpp
Go to the documentation of this file.
1#include "cli_client.hpp"
3#include <iostream>
4
6 : stream_mgr(mgr) {
7#ifdef YODAU_OPENCV
8 stream_mgr.set_daemon_start_hook(opencv_daemon_start);
9 stream_mgr.set_frame_processor(opencv_motion_processor);
10#endif
11}
12
13int yodau::backend::cli_client::run() const {
14 std::string line;
15 while (true) {
16 std::cout << "yodau> " << std::flush;
17 if (!std::getline(std::cin, line)) {
18 return 1;
19 }
20 auto tokens = tokenize(line);
21 if (tokens.empty()) {
22 continue;
23 }
24 const auto& cmd = tokens[0];
25 std::vector args(tokens.begin() + 1, tokens.end());
26 if (cmd == "quit" || cmd == "q" || cmd == "exit") {
27 break;
28 }
29 dispatch_command(cmd, args);
30 }
31 return 0;
32}
33
34std::vector<std::string>
35yodau::backend::cli_client::tokenize(const std::string& line) {
36 std::vector<std::string> tokens;
37 std::istringstream stream(line);
38 std::string token;
39 while (stream >> token) {
40 tokens.push_back(token);
41 }
42 return tokens;
43}
44
46 const std::string& cmd, const std::vector<std::string>& args
47) const {
48 static const std::unordered_map<
49 std::string,
50 void (cli_client::*)(const std::vector<std::string>& args) const>
51 command_map = { { "list-streams", &cli_client::cmd_list_streams },
52 { "add-stream", &cli_client::cmd_add_stream },
53 { "start-stream", &cli_client::cmd_start_stream },
54 { "stop-stream", &cli_client::cmd_stop_stream },
55 { "list-lines", &cli_client::cmd_list_lines },
56 { "add-line", &cli_client::cmd_add_line },
57 { "set-line", &cli_client::cmd_set_line } };
58 const auto it = command_map.find(cmd);
59 if (it == command_map.end()) {
60 std::cerr << "unknown command: " << cmd << std::endl;
61 return;
62 }
63 try {
64 const auto method = it->second;
65 (this->*method)(args);
66 } catch (const std::exception& e) {
67 std::cerr << "error executing command '" << cmd << "': " << e.what()
68 << std::endl;
69 }
70}
71
72cxxopts::ParseResult yodau::backend::cli_client::parse_with_cxxopts(
73 const std::string& cmd, const std::vector<std::string>& args,
74 cxxopts::Options& options
75) {
76 std::vector<char*> argv;
77 argv.reserve(args.size() + 1);
78 argv.push_back(const_cast<char*>(cmd.data()));
79 for (const auto& arg : args) {
80 argv.push_back(const_cast<char*>(arg.data()));
81 }
82 const int argc = static_cast<int>(argv.size());
83 char** argv_ptr = argv.data();
84 return options.parse(argc, argv_ptr);
85}
86
88 const std::vector<std::string>& args
89) const {
90 const std::string cmd = "list-streams";
91 cxxopts::Options options(cmd, "List all streams");
92 options.allow_unrecognised_options();
93 options.add_options()("h,help", "Print help")(
94 "c,connections", "Show connected lines",
95 cxxopts::value<bool>()->default_value("false")
96 );
97 try {
98 const auto result = parse_with_cxxopts(cmd, args, options);
99 if (result.count("help")) {
100 std::cout << options.help() << std::endl;
101 return;
102 }
103 const bool show_connections = result["connections"].as<bool>();
104 stream_mgr.dump_stream(std::cout, show_connections);
105 std::cout << std::endl;
106 } catch (const cxxopts::exceptions::exception& e) {
107 std::cerr << "Error parsing command '" << cmd << "': " << e.what()
108 << std::endl;
109 std::cout << options.help() << std::endl;
110 }
111}
112
114 const std::vector<std::string>& args
115) const {
116 const std::string cmd = "add-stream";
117 cxxopts::Options options(cmd, "Add a new stream");
118 options.allow_unrecognised_options();
119 options.positional_help("<path> [<name>] [<type>] [<loop>]");
120 options.add_options()
121 ("h,help", "Print help")
122 ("path", "Path to the device, media file or stream URL", cxxopts::value<std::string>())
123 ("name", "Name of the stream", cxxopts::value<std::string>()->default_value(""))
124 ("type", "Type of the stream (local/file/rtsp/http)", cxxopts::value<std::string>()->default_value(""))
125 ("loop", "Whether to loop the stream (true/false)", cxxopts::value<bool>()->default_value("true"));
126 options.parse_positional({ "path", "name", "type", "loop" });
127 try {
128 const auto result = parse_with_cxxopts(cmd, args, options);
129 if (result.count("help")) {
130 std::cout << options.help() << std::endl;
131 return;
132 }
133 if (!result.count("path")) {
134 std::cerr << "Error: 'path' argument is required." << std::endl;
135 return;
136 }
137 const std::string path = result["path"].as<std::string>();
138 const std::string name = result["name"].as<std::string>();
139 const std::string type = result["type"].as<std::string>();
140 const bool loop = result["loop"].as<bool>();
141 const auto& stream = stream_mgr.add_stream(path, name, type, loop);
142 stream.dump(std::cout, true);
143 std::cout << std::endl;
144 } catch (const cxxopts::exceptions::exception& e) {
145 std::cerr << "Error parsing command '" << cmd << "': " << e.what()
146 << std::endl;
147 std::cout << options.help() << std::endl;
148 }
149}
150
152 const std::vector<std::string>& args
153) const {
154 const std::string cmd = "start-stream";
155 cxxopts::Options options("start-stream", "Start a stream");
156 options.allow_unrecognised_options();
157 options.add_options()("h,help", "Print help")(
158 "name", "Name of the stream to start", cxxopts::value<std::string>()
159 );
160 options.parse_positional({ "name" });
161 try {
162 const auto result = parse_with_cxxopts("start-stream", args, options);
163 if (result.count("help")) {
164 std::cout << options.help() << std::endl;
165 return;
166 }
167 if (!result.count("name")) {
168 std::cerr << "Error: 'name' argument is required." << std::endl;
169 return;
170 }
171 const std::string name = result["name"].as<std::string>();
172 stream_mgr.start_stream(name);
173 } catch (const cxxopts::exceptions::exception& e) {
174 std::cerr << "Error parsing command '" << cmd << "': " << e.what()
175 << std::endl;
176 std::cout << options.help() << std::endl;
177 }
178}
179
181 const std::vector<std::string>& args
182) const {
183 const std::string cmd = "stop-stream";
184 cxxopts::Options options("stop-stream", "Stop a stream");
185 options.allow_unrecognised_options();
186 options.add_options()("h,help", "Print help")(
187 "name", "Name of the stream to stop", cxxopts::value<std::string>()
188 );
189 options.parse_positional({ "name" });
190 try {
191 const auto result = parse_with_cxxopts("stop-stream", args, options);
192 if (result.count("help")) {
193 std::cout << options.help() << std::endl;
194 return;
195 }
196 if (!result.count("name")) {
197 std::cerr << "Error: 'name' argument is required." << std::endl;
198 return;
199 }
200 const std::string name = result["name"].as<std::string>();
201 stream_mgr.stop_stream(name);
202 } catch (const cxxopts::exceptions::exception& e) {
203 std::cerr << "Error parsing command '" << cmd << "': " << e.what()
204 << std::endl;
205 std::cout << options.help() << std::endl;
206 }
207}
208
210 const std::vector<std::string>& args
211) const {
212 const std::string cmd = "list-lines";
213 cxxopts::Options options("list-lines", "List all lines in a stream");
214 options.allow_unrecognised_options();
215 options.add_options()("h,help", "Print help");
216 try {
217 const auto result = parse_with_cxxopts("list-lines", args, options);
218 if (result.count("help")) {
219 std::cout << options.help() << std::endl;
220 return;
221 }
222 stream_mgr.dump_lines(std::cout);
223 std::cout << std::endl;
224 } catch (const cxxopts::exceptions::exception& e) {
225 std::cerr << "Error parsing command '" << cmd << "': " << e.what()
226 << std::endl;
227 std::cout << options.help() << std::endl;
228 }
229}
230
231static yodau::backend::tripwire_dir parse_tripwire_dir(const std::string& s) {
232 if (s == "neg_to_pos") {
234 }
235 if (s == "pos_to_neg") {
237 }
238 if (s == "any") {
240 }
241 throw std::runtime_error("unknown dir: " + s);
242}
243
245 const std::vector<std::string>& args
246) const {
247 const std::string cmd = "add-line";
248 cxxopts::Options options("add-line", "Add a new line to a stream");
249 options.allow_unrecognised_options();
250 options.positional_help("<path> [<name>] [<close>]");
251 options.add_options()
252 ("h,help", "Print help")
253 ("path", "Line coordinates, e.g. 0,0;100,100", cxxopts::value<std::string>())
254 ("name", "Name of the line", cxxopts::value<std::string>()->default_value(""))
255 ("close", "Whether the line is closed (true/false)", cxxopts::value<bool>()->default_value("false"))
256 ("d,dir", "Tripwire direction (any/neg_to_pos/pos_to_neg)",
257 cxxopts::value<std::string>()->default_value("any"));
258 options.parse_positional({ "path", "name", "close" });
259 try {
260 const auto result = parse_with_cxxopts("add-line", args, options);
261 if (result.count("help")) {
262 std::cout << options.help() << std::endl;
263 return;
264 }
265 if (!result.count("path")) {
266 std::cerr << "Error: 'path' argument is required." << std::endl;
267 return;
268 }
269 const std::string path = result["path"].as<std::string>();
270 const std::string name = result["name"].as<std::string>();
271 const bool close = result["close"].as<bool>();
272 const std::string dir_str = result["dir"].as<std::string>();
273
274 const auto& line = stream_mgr.add_line(path, close, name);
275
276 try {
277 const auto dir = parse_tripwire_dir(dir_str);
278 stream_mgr.set_line_dir(line->name, dir);
279 } catch (const std::exception& e) {
280 std::cerr << "bad dir: " << e.what() << std::endl;
281 }
282
283 line->dump(std::cout);
284 std::cout << std::endl;
285
286 } catch (const cxxopts::exceptions::exception& e) {
287 std::cerr << "Error parsing command '" << cmd << "': " << e.what()
288 << std::endl;
289 std::cout << options.help() << std::endl;
290 }
291}
292
294 const std::vector<std::string>& args
295) const {
296 const std::string cmd = "set-line";
297 cxxopts::Options options("set-line", "Set a new line to a stream");
298 options.allow_unrecognised_options();
299 options
300 .add_options()("h,help", "Print help")("stream", "Stream name", cxxopts::value<std::string>())(
301 "line", "Line name", cxxopts::value<std::string>()
302 );
303 options.parse_positional({ "stream", "line" });
304 try {
305 const auto result = parse_with_cxxopts("set-line", args, options);
306 if (result.count("help")) {
307 std::cout << options.help() << std::endl;
308 return;
309 }
310 if (!result.count("stream") || !result.count("line")) {
311 std::cerr << "Error: 'stream' and 'line' arguments are required."
312 << std::endl;
313 return;
314 }
315 const std::string stream_name = result["stream"].as<std::string>();
316 const std::string line_name = result["line"].as<std::string>();
317 const auto& stream = stream_mgr.set_line(stream_name, line_name);
318 stream.dump(std::cout, true);
319 std::cout << std::endl;
320 } catch (const cxxopts::exceptions::exception& e) {
321 std::cerr << "Error parsing command '" << cmd << "': " << e.what()
322 << std::endl;
323 std::cout << options.help() << std::endl;
324 }
325}
Simple interactive CLI (REPL) for controlling a stream_manager.
void cmd_start_stream(const std::vector< std::string > &args) const
Handler for start-stream.
void dispatch_command(const std::string &cmd, const std::vector< std::string > &args) const
Dispatch a command to its handler.
void cmd_set_line(const std::vector< std::string > &args) const
Handler for set-line.
int run() const
Run the interactive command loop.
void cmd_add_line(const std::vector< std::string > &args) const
Handler for add-line.
backend::stream_manager & stream_mgr
Stream manager controlled by this CLI.
void cmd_stop_stream(const std::vector< std::string > &args) const
Handler for stop-stream.
void cmd_list_streams(const std::vector< std::string > &args) const
Handler for list-streams.
void cmd_add_stream(const std::vector< std::string > &args) const
Handler for add-stream.
void cmd_list_lines(const std::vector< std::string > &args) const
Handler for list-lines.
cli_client(backend::stream_manager &mgr)
Construct a CLI client operating on an existing manager.
Definition cli_client.cpp:5
Central coordinator for streams, geometry, frame processing and events.
static yodau::backend::tripwire_dir parse_tripwire_dir(const std::string &s)
tripwire_dir
Allowed crossing direction for a tripwire.
Definition geometry.hpp:62