1//! @file view.hpp
2//! @author ryftchen
3//! @brief The declarations (view) in the application module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2025 ryftchen. All rights reserved.
6
7#pragma once
8
9#include "configure.hpp"
10
11#include "utility/include/fsm.hpp"
12#include "utility/include/socket.hpp"
13
14//! @brief The application module.
15namespace application // NOLINT(modernize-concat-nested-namespaces)
16{
17//! @brief View-server-related functions in the application module.
18namespace view
19{
20//! @brief Minimum port number.
21inline constexpr std::uint16_t minPortNumber = 1024;
22//! @brief Maximum port number.
23inline constexpr std::uint16_t maxPortNumber = 65535;
24//! @brief The internal symbol for exiting.
25inline constexpr std::string_view exitSymbol = "stop";
26
27//! @brief Viewer.
28class View final : public utility::fsm::FSM<View>
29{
30public:
31 friend class FSM<View>;
32 //! @brief Destroy the View object.
33 ~View() = default;
34 //! @brief Construct a new View object.
35 View(const View&) = delete;
36 //! @brief Construct a new View object.
37 View(View&&) = delete;
38 //! @brief The operator (=) overloading of View class.
39 //! @return reference of the View object
40 View& operator=(const View&) = delete;
41 //! @brief The operator (=) overloading of View class.
42 //! @return reference of the View object
43 View& operator=(View&&) = delete;
44
45 //! @brief Instance name.
46 static constexpr std::string name{configure::field::viewer};
47 //! @brief Get the View instance.
48 //! @return reference of the View object
49 static std::shared_ptr<View> getInstance();
50 //! @brief Service for running.
51 void service();
52
53 //! @brief Enumerate specific states for FSM.
54 enum State : std::uint8_t
55 {
56 //! @brief Initial.
57 initial,
58 //! @brief Active.
59 active,
60 //! @brief Established.
61 established,
62 //! @brief Inactive.
63 inactive,
64 //! @brief Idle.
65 idle
66 };
67 //! @brief Access for the instance.
68 class Access
69 {
70 public:
71 //! @brief Wait for the viewer to start. Interface controller for external use.
72 void startup() const;
73 //! @brief Wait for the viewer to stop. Interface controller for external use.
74 void shutdown() const;
75 //! @brief Request to reset the viewer. Interface controller for external use.
76 void reload() const;
77
78 //! @brief Parse the TLV packet of message.
79 //! @param bytes - message buffer
80 //! @param size - message length
81 //! @return need to continue parsing or not
82 bool onParsing(char* const bytes, const std::size_t size) const;
83 //! @brief Get the supported options.
84 //! @return supported options
85 [[nodiscard]] auto getSupportedOptions() const noexcept { return inst->supportedOptions; }
86 //! @brief Get the TCP server host address.
87 //! @return TCP server host address
88 [[nodiscard]] std::string getTCPHost() const noexcept { return inst->tcpHost; }
89 //! @brief Get the TCP server port number.
90 //! @return TCP server port number
91 [[nodiscard]] std::uint16_t getTCPPort() const noexcept { return inst->tcpPort; }
92 //! @brief Get the UDP server host address.
93 //! @return UDP server host address
94 [[nodiscard]] std::string getUDPHost() const noexcept { return inst->udpHost; }
95 //! @brief Get the UDP server port number.
96 //! @return UDP server port number
97 [[nodiscard]] std::uint16_t getUDPPort() const noexcept { return inst->udpPort; }
98
99 private:
100 //! @brief Instance to be accessed.
101 const std::shared_ptr<View> inst{getInstance()};
102
103 //! @brief Wait until the viewer reaches the target state.
104 //! @param state - target state
105 //! @param handling - handling if unexpected state
106 void waitOr(const State state, const std::function<void()>& handling) const;
107 //! @brief Notify the viewer to change the state.
108 //! @param action - action to be executed
109 void notifyVia(const std::function<void()>& action) const;
110 //! @brief Keep countdown if the viewer does not meet the condition in time.
111 //! @param condition - condition of countdown
112 //! @param handling - handling if timeout
113 void countdownIf(const std::function<bool()>& condition, const std::function<void()>& handling) const;
114 };
115 //! @brief Synchronization for the instance.
116 class Sync
117 {
118 public:
119 //! @brief Block the caller until the output task is marked as done.
120 void waitTaskDone() const;
121 //! @brief Notify that the output task has been completed and unblock any waiters.
122 void notifyTaskDone() const;
123
124 private:
125 //! @brief Instance to be synchronized.
126 const std::shared_ptr<View> inst{getInstance()};
127 };
128
129private:
130 //! @brief Construct a new View object.
131 View();
132
133 //! @brief TCP server host address.
134 const std::string tcpHost{configure::detail::tcpHost4Viewer()};
135 //! @brief TCP server port number.
136 const std::uint16_t tcpPort{static_cast<std::uint16_t>(configure::detail::tcpPort4Viewer())};
137 //! @brief UDP server host address.
138 const std::string udpHost{configure::detail::udpHost4Viewer()};
139 //! @brief UDP server port number.
140 const std::uint16_t udpPort{static_cast<std::uint16_t>(configure::detail::udpPort4Viewer())};
141 //! @brief Timeout period (ms) to waiting for the viewer to change to the target state.
142 const std::uint32_t timeoutPeriod{static_cast<std::uint32_t>(configure::detail::helperTimeout())};
143
144 //! @brief Alias for the option arguments.
145 using Args = std::vector<std::string>;
146 //! @brief Option attribute.
147 struct OptBase
148 {
149 //! @brief The option arguments.
150 const Args args;
151 };
152 //! @brief Option attribute for the depend option.
153 struct OptDepend : public OptBase
154 {
155 //! @brief The option name.
156 static constexpr const char* const name{"depend"};
157 //! @brief The option description.
158 static constexpr const char* const description{"list all associated libraries"};
159 };
160 //! @brief Option attribute for the execute option.
161 struct OptExecute : public OptBase
162 {
163 //! @brief The option name.
164 static constexpr const char* const name{"execute"};
165 //! @brief The option description.
166 static constexpr const char* const description{"enter bash commands in quotes [inputs: 'CMD']"};
167 };
168 //! @brief Option attribute for the journal option.
169 struct OptJournal : public OptBase
170 {
171 //! @brief The option name.
172 static constexpr const char* const name{"journal"};
173 //! @brief The option description.
174 static constexpr const char* const description{"view the log with highlights"};
175 };
176 //! @brief Option attribute for the monitor option.
177 struct OptMonitor : public OptBase
178 {
179 //! @brief The option name.
180 static constexpr const char* const name{"monitor"};
181 //! @brief The option description.
182 static constexpr const char* const description{"query process status and stacks [inputs: NUM]"};
183 };
184 //! @brief Option attribute for the profile option.
185 struct OptProfile : public OptBase
186 {
187 //! @brief The option name.
188 static constexpr const char* const name{"profile"};
189 //! @brief The option description.
190 static constexpr const char* const description{"display current configuration"};
191 };
192 //! @brief Alias for the option type.
193 using OptionType = std::variant<OptBase, OptDepend, OptExecute, OptJournal, OptMonitor, OptProfile>;
194 //! @brief Alias for the map of option name and OptionAttr.
195 using OptionMap = std::map<std::string, std::string>;
196 //! @brief Mapping table of all viewer options.
197 const OptionMap supportedOptions{
198 {OptDepend::name, OptDepend::description},
199 {OptExecute::name, OptExecute::description},
200 {OptJournal::name, OptJournal::description},
201 {OptMonitor::name, OptMonitor::description},
202 {OptProfile::name, OptProfile::description}};
203 //! @brief Maximum size of the shared memory buffer.
204 static constexpr std::size_t shmLimitSize{static_cast<std::size_t>(65536 * 10)};
205 //! @brief Memory that can be accessed by multiple programs simultaneously.
206 struct ShrMemBlock
207 {
208 //! @brief Data of the shared memory buffer.
209 alignas(64) char data[shmLimitSize];
210 //! @brief Size of the shared memory buffer.
211 alignas(64) std::size_t size;
212 //! @brief Flag for operable.
213 alignas(64) std::atomic_bool signal;
214 };
215
216 //! @brief Alias for the buffer.
217 using Buffer = std::array<char, 1024>;
218 //! @brief Build the response message.
219 //! @param reqPlaintext - plaintext of the request
220 //! @param respBuffer - buffer to store the response
221 //! @return length of the response message
222 static std::size_t buildResponse(const std::string& reqPlaintext, Buffer& respBuffer);
223 //! @brief Extract the option from the request.
224 //! @param reqPlaintext - plaintext of the request
225 //! @return option type
226 static OptionType extractOption(const std::string& reqPlaintext);
227 //! @brief Split string by space.
228 //! @param str - target string
229 //! @return strings after split
230 static std::vector<std::string> splitString(const std::string& str);
231 //! @brief Build the TLV packet of the response message to acknowledge only.
232 //! @param buffer - TLV packet buffer
233 //! @return buffer length
234 static std::size_t buildAckTLVPacket(Buffer& buffer);
235 //! @brief Build the TLV packet of the response message to stop connection.
236 //! @param buffer - TLV packet buffer
237 //! @return buffer length
238 static std::size_t buildFinTLVPacket(Buffer& buffer);
239 //! @brief Build the TLV packet of the response message in a customized way.
240 //! @tparam Opt - type of option attribute
241 //! @param args - container of arguments
242 //! @param buffer - TLV packet buffer
243 //! @return buffer length
244 template <typename Opt>
245 requires std::derived_from<Opt, OptBase>
246 static std::size_t buildCustomTLVPacket(const Args& args, Buffer& buffer);
247 //! @brief Fill the shared memory.
248 //! @param contents - contents to be filled
249 //! @return shm id
250 static int fillSharedMemory(const std::string_view contents);
251 //! @brief Fetch the shared memory.
252 //! @param shmId - shm id
253 //! @param contents - contents to be fetched
254 static void fetchSharedMemory(const int shmId, std::string& contents);
255 //! @brief Print the shared memory.
256 //! @param shmId - shm id
257 //! @param withoutPaging - whether output without paging
258 static void printSharedMemory(const int shmId, const bool withoutPaging = true);
259 //! @brief Segmented output.
260 //! @param cache - output cache
261 static void segmentedOutput(const std::string& cache);
262 //! @brief Log contents preview.
263 //! @return log contents
264 static std::string logContentsPreview();
265 //! @brief Status reports preview.
266 //! @param frame - maximum frame
267 //! @return status reports
268 static std::string statusReportsPreview(const std::uint16_t frame);
269
270 //! @brief TCP server.
271 std::shared_ptr<utility::socket::TCPServer> tcpServer;
272 //! @brief UDP server.
273 std::shared_ptr<utility::socket::UDPServer> udpServer;
274 //! @brief Mutex for controlling daemon.
275 mutable std::mutex daemonMtx;
276 //! @brief The synchronization condition for daemon. Use with daemonMtx.
277 std::condition_variable daemonCond;
278 //! @brief Flag to indicate whether it is viewing.
279 std::atomic_bool isOngoing{false};
280 //! @brief Flag for rollback request.
281 std::atomic_bool inResetting{false};
282 //! @brief Mutex for controlling output.
283 mutable std::mutex outputMtx;
284 //! @brief The synchronization condition for output. Use with outputMtx.
285 std::condition_variable outputCond;
286 //! @brief Flag to indicate whether the output is complete.
287 std::atomic_bool outputCompleted{true};
288 //! @brief Renew the server.
289 //! @tparam Sock - type of server
290 template <typename Sock>
291 void renewServer();
292
293 //! @brief Check whether it is in the uninterrupted serving state.
294 //! @param state - target state
295 //! @return in the uninterrupted serving state or not
296 bool isInServingState(const State state) const;
297 //! @brief FSM event. Create server.
298 struct CreateServer
299 {
300 };
301 //! @brief FSM event. Destroy server.
302 struct DestroyServer
303 {
304 };
305 //! @brief FSM event. Go viewing.
306 struct GoViewing
307 {
308 };
309 //! @brief FSM event. NO viewing.
310 struct NoViewing
311 {
312 };
313 //! @brief FSM event. Standby.
314 struct Standby
315 {
316 };
317 //! @brief FSM event. Relaunch.
318 struct Relaunch
319 {
320 };
321 //! @brief Create the view server.
322 void createViewServer();
323 //! @brief Destroy the view server.
324 void destroyViewServer();
325 //! @brief Start viewing.
326 void startViewing();
327 //! @brief Stop viewing.
328 void stopViewing();
329 //! @brief Do toggle.
330 void doToggle();
331 //! @brief Do rollback.
332 void doRollback();
333 // clang-format off
334 //! @brief Alias for the transition table of the viewer.
335 using TransitionTable = Table
336 <
337 // +------ Source ------+---- Event ----+------ Target ------+--------- Action ---------+- Guard -+
338 // +--------------------+---------------+--------------------+--------------------------+---------+
339 Row< State::initial , CreateServer , State::active , &View::createViewServer >,
340 Row< State::active , GoViewing , State::established , &View::startViewing >,
341 Row< State::established , DestroyServer , State::active , &View::destroyViewServer >,
342 Row< State::active , NoViewing , State::inactive , &View::stopViewing >,
343 Row< State::initial , Standby , State::idle , &View::doToggle >,
344 Row< State::active , Standby , State::idle , &View::doToggle >,
345 Row< State::established , Standby , State::idle , &View::doToggle >,
346 Row< State::established , Relaunch , State::initial , &View::doRollback >,
347 Row< State::inactive , Relaunch , State::initial , &View::doRollback >,
348 Row< State::idle , Relaunch , State::initial , &View::doRollback >
349 // +--------------------+---------------+--------------------+--------------------------+---------+
350 >;
351 // clang-format on
352 //! @brief The notification loop.
353 void notificationLoop();
354 //! @brief Await notification to proceed.
355 void awaitNotification2Proceed();
356 //! @brief Await notification to retry.
357 //! @return whether retry is required or not
358 bool awaitNotification2Retry();
359
360protected:
361 friend std::ostream& operator<<(std::ostream& os, const State state);
362};
363
364//! @brief Instance information, if enabled.
365namespace info
366{
367//! @brief Get the current supported viewer options.
368//! @return current supported viewer options
369inline auto currentSupportedOptions()
370{
371 return View::Access().getSupportedOptions();
372}
373//! @brief Get the current TCP host address being used for viewing.
374//! @return current TCP host address being used for viewing
375inline std::string currentTCPHost()
376{
377 return View::Access().getTCPHost();
378}
379//! @brief Get the current TCP port number being used for viewing.
380//! @return current TCP port number being used for viewing
381inline std::uint16_t currentTCPPort()
382{
383 return View::Access().getTCPPort();
384}
385//! @brief Get the current UDP host address being used for viewing.
386//! @return current UDP host address being used for viewing
387inline std::string currentUDPHost()
388{
389 return View::Access().getUDPHost();
390}
391//! @brief Get the current UDP port number being used for viewing.
392//! @return current UDP port number being used for viewing
393inline std::uint16_t currentUDPPort()
394{
395 return View::Access().getUDPPort();
396}
397} // namespace info
398} // namespace view
399} // namespace application
400