| 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. |
| 15 | namespace application // NOLINT(modernize-concat-nested-namespaces) |
| 16 | { |
| 17 | //! @brief View-server-related functions in the application module. |
| 18 | namespace view |
| 19 | { |
| 20 | //! @brief Minimum port number. |
| 21 | inline constexpr std::uint16_t minPortNumber = 1024; |
| 22 | //! @brief Maximum port number. |
| 23 | inline constexpr std::uint16_t maxPortNumber = 65535; |
| 24 | //! @brief The internal symbol for exiting. |
| 25 | inline constexpr std::string_view exitSymbol = "stop" ; |
| 26 | |
| 27 | //! @brief Viewer. |
| 28 | class View final : public utility::fsm::FSM<View> |
| 29 | { |
| 30 | public: |
| 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 | |
| 129 | private: |
| 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 (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 | |
| 360 | protected: |
| 361 | friend std::ostream& operator<<(std::ostream& os, const State state); |
| 362 | }; |
| 363 | |
| 364 | //! @brief Instance information, if enabled. |
| 365 | namespace info |
| 366 | { |
| 367 | //! @brief Get the current supported viewer options. |
| 368 | //! @return current supported viewer options |
| 369 | inline 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 |
| 375 | inline 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 |
| 381 | inline 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 |
| 387 | inline 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 |
| 393 | inline std::uint16_t currentUDPPort() |
| 394 | { |
| 395 | return View::Access().getUDPPort(); |
| 396 | } |
| 397 | } // namespace info |
| 398 | } // namespace view |
| 399 | } // namespace application |
| 400 | |