| 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-2026 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 | |
| 25 | //! @brief Simplified interface. |
| 26 | namespace intf |
| 27 | { |
| 28 | //! @brief Connect from the client. |
| 29 | //! @tparam Sock - type of client |
| 30 | //! @param client - client to be connected |
| 31 | template <typename Sock> |
| 32 | void connectFromClient(std::shared_ptr<Sock>& client); |
| 33 | //! @brief Forward message by client. |
| 34 | //! @tparam Sock - type of client |
| 35 | //! @param client - client to be used for forwarding |
| 36 | //! @param inputs - input strings to be forwarded |
| 37 | template <typename Sock> |
| 38 | void forwardByClient(std::shared_ptr<Sock>& client, const std::vector<std::string>& inputs); |
| 39 | |
| 40 | template <> |
| 41 | void connectFromClient(std::shared_ptr<utility::socket::TCPSocket>& client); |
| 42 | template <> |
| 43 | void connectFromClient(std::shared_ptr<utility::socket::UDPSocket>& client); |
| 44 | template <> |
| 45 | void forwardByClient(std::shared_ptr<utility::socket::TCPSocket>& client, const std::vector<std::string>& inputs); |
| 46 | template <> |
| 47 | void forwardByClient(std::shared_ptr<utility::socket::UDPSocket>& client, const std::vector<std::string>& inputs); |
| 48 | } // namespace intf |
| 49 | |
| 50 | //! @brief Viewer. |
| 51 | class View final : public utility::fsm::FSM<View> |
| 52 | { |
| 53 | public: |
| 54 | friend class FSM<View>; |
| 55 | //! @brief Destroy the View object. |
| 56 | ~View() = default; |
| 57 | //! @brief Construct a new View object. |
| 58 | View(const View&) = delete; |
| 59 | //! @brief Construct a new View object. |
| 60 | View(View&&) = delete; |
| 61 | //! @brief The operator (=) overloading of View class. |
| 62 | //! @return reference of the View object |
| 63 | View& operator=(const View&) = delete; |
| 64 | //! @brief The operator (=) overloading of View class. |
| 65 | //! @return reference of the View object |
| 66 | View& operator=(View&&) = delete; |
| 67 | |
| 68 | //! @brief Instance name. |
| 69 | static constexpr std::string name{configure::field::viewer}; |
| 70 | //! @brief Get the View instance. |
| 71 | //! @return reference of the View object |
| 72 | static std::shared_ptr<View> getInstance(); |
| 73 | //! @brief Service for running. |
| 74 | void service(); |
| 75 | //! @brief Enumerate specific states for FSM. |
| 76 | enum State : std::uint8_t |
| 77 | { |
| 78 | //! @brief Initial. |
| 79 | initial, |
| 80 | //! @brief Active. |
| 81 | active, |
| 82 | //! @brief Established. |
| 83 | established, |
| 84 | //! @brief Inactive. |
| 85 | inactive, |
| 86 | //! @brief Idle. |
| 87 | idle |
| 88 | }; |
| 89 | |
| 90 | private: |
| 91 | //! @brief Construct a new View object. |
| 92 | View(); |
| 93 | |
| 94 | //! @brief TCP server host address. |
| 95 | const std::string tcpHost{configure::detail::tcpHost4Viewer()}; |
| 96 | //! @brief TCP server port number. |
| 97 | const std::uint16_t tcpPort{static_cast<std::uint16_t>(configure::detail::tcpPort4Viewer())}; |
| 98 | //! @brief UDP server host address. |
| 99 | const std::string udpHost{configure::detail::udpHost4Viewer()}; |
| 100 | //! @brief UDP server port number. |
| 101 | const std::uint16_t udpPort{static_cast<std::uint16_t>(configure::detail::udpPort4Viewer())}; |
| 102 | //! @brief Timeout period (ms) to waiting for the viewer to change to the target state. |
| 103 | const std::uint32_t timeoutPeriod{static_cast<std::uint32_t>(configure::detail::helperTimeout())}; |
| 104 | |
| 105 | //! @brief Alias for the option arguments. |
| 106 | using Args = std::vector<std::string>; |
| 107 | //! @brief Option attribute. |
| 108 | struct OptBase |
| 109 | { |
| 110 | //! @brief The option arguments. |
| 111 | const Args args; |
| 112 | }; |
| 113 | //! @brief Option attribute for the depend option. |
| 114 | struct OptDepend : public OptBase |
| 115 | { |
| 116 | //! @brief The option name. |
| 117 | static constexpr const char* const name{"depend" }; |
| 118 | //! @brief The option description. |
| 119 | static constexpr const char* const description{"list all associated libraries" }; |
| 120 | }; |
| 121 | //! @brief Option attribute for the execute option. |
| 122 | struct OptExecute : public OptBase |
| 123 | { |
| 124 | //! @brief The option name. |
| 125 | static constexpr const char* const name{"execute" }; |
| 126 | //! @brief The option description. |
| 127 | static constexpr const char* const description{"enter bash commands in quotes [inputs: 'CMD']" }; |
| 128 | }; |
| 129 | //! @brief Option attribute for the journal option. |
| 130 | struct OptJournal : public OptBase |
| 131 | { |
| 132 | //! @brief The option name. |
| 133 | static constexpr const char* const name{"journal" }; |
| 134 | //! @brief The option description. |
| 135 | static constexpr const char* const description{"view the log with highlights" }; |
| 136 | }; |
| 137 | //! @brief Option attribute for the monitor option. |
| 138 | struct OptMonitor : public OptBase |
| 139 | { |
| 140 | //! @brief The option name. |
| 141 | static constexpr const char* const name{"monitor" }; |
| 142 | //! @brief The option description. |
| 143 | static constexpr const char* const description{"query process status and stacks [inputs: NUM]" }; |
| 144 | }; |
| 145 | //! @brief Option attribute for the profile option. |
| 146 | struct OptProfile : public OptBase |
| 147 | { |
| 148 | //! @brief The option name. |
| 149 | static constexpr const char* const name{"profile" }; |
| 150 | //! @brief The option description. |
| 151 | static constexpr const char* const description{"display current configuration" }; |
| 152 | }; |
| 153 | friend std::map<std::string, std::string> obtainSupportedOptions(); |
| 154 | //! @brief Maximum size of the shared memory buffer. |
| 155 | static constexpr std::size_t shmLimitSize{static_cast<std::size_t>(65536 * 10)}; |
| 156 | //! @brief Memory that can be accessed by multiple programs simultaneously. |
| 157 | struct ShrMemBlock |
| 158 | { |
| 159 | //! @brief Data of the shared memory buffer. |
| 160 | alignas(64) char data[shmLimitSize]; |
| 161 | //! @brief Size of the shared memory buffer. |
| 162 | alignas(64) std::size_t size; |
| 163 | //! @brief Flag for operable. |
| 164 | alignas(64) std::atomic_bool signal; |
| 165 | }; |
| 166 | |
| 167 | //! @brief Alias for the buffer. |
| 168 | using Buffer = std::array<char, 1024>; |
| 169 | //! @brief Build the response message. |
| 170 | //! @param reqPlaintext - plaintext of the request |
| 171 | //! @param respBuffer - buffer to store the response |
| 172 | //! @return length of the response message |
| 173 | static std::size_t buildResponse(const std::string& reqPlaintext, Buffer& respBuffer); |
| 174 | //! @brief Alias for the option type. |
| 175 | using OptionType = std::variant<OptBase, OptDepend, OptExecute, OptJournal, OptMonitor, OptProfile>; |
| 176 | //! @brief Extract the option from the request. |
| 177 | //! @param reqPlaintext - plaintext of the request |
| 178 | //! @return option type |
| 179 | static OptionType (const std::string& reqPlaintext); |
| 180 | //! @brief Split string by space. |
| 181 | //! @param str - target string |
| 182 | //! @return strings after split |
| 183 | static std::vector<std::string> splitString(const std::string& str); |
| 184 | //! @brief Build the TLV packet of the response message to acknowledge only. |
| 185 | //! @param buffer - TLV packet buffer |
| 186 | //! @return buffer length |
| 187 | static std::size_t buildAckTLVPacket(Buffer& buffer); |
| 188 | //! @brief Build the TLV packet of the response message to stop connection. |
| 189 | //! @param buffer - TLV packet buffer |
| 190 | //! @return buffer length |
| 191 | static std::size_t buildFinTLVPacket(Buffer& buffer); |
| 192 | //! @brief Build the TLV packet of the response message in a customized way. |
| 193 | //! @tparam Opt - type of option attribute |
| 194 | //! @param args - container of arguments |
| 195 | //! @param buffer - TLV packet buffer |
| 196 | //! @return buffer length |
| 197 | template <typename Opt> |
| 198 | requires std::derived_from<Opt, OptBase> |
| 199 | static std::size_t buildCustomTLVPacket(const Args& args, Buffer& buffer); |
| 200 | //! @brief Fill the shared memory. |
| 201 | //! @param contents - contents to be filled |
| 202 | //! @return shm id |
| 203 | static int fillSharedMemory(const std::string_view contents); |
| 204 | //! @brief Fetch the shared memory. |
| 205 | //! @param shmId - shm id |
| 206 | //! @param contents - contents to be fetched |
| 207 | static void fetchSharedMemory(const int shmId, std::string& contents); |
| 208 | //! @brief Print the shared memory. |
| 209 | //! @param shmId - shm id |
| 210 | //! @param withoutPaging - whether output without paging |
| 211 | static void printSharedMemory(const int shmId, const bool withoutPaging = true); |
| 212 | //! @brief Segmented output. |
| 213 | //! @param cache - output cache |
| 214 | static void segmentedOutput(const std::string& cache); |
| 215 | //! @brief Log context preview. |
| 216 | //! @return log context |
| 217 | static std::string logContextPreview(); |
| 218 | //! @brief Status report preview. |
| 219 | //! @param frame - maximum frame |
| 220 | //! @return status report |
| 221 | static std::string statusReportPreview(const std::uint16_t frame); |
| 222 | |
| 223 | //! @brief The server (TCP) of the permanent session. |
| 224 | std::shared_ptr<utility::socket::TCPServer> permSessServer; |
| 225 | //! @brief The server (UDP) of the temporary session. |
| 226 | std::shared_ptr<utility::socket::UDPServer> tempSessServer; |
| 227 | //! @brief Mutex for controlling daemon. |
| 228 | mutable std::mutex daemonMtx; |
| 229 | //! @brief The synchronization condition for daemon. Use with daemonMtx. |
| 230 | std::condition_variable daemonCond; |
| 231 | //! @brief Flag to indicate whether it is viewing. |
| 232 | std::atomic_bool isOngoing{false}; |
| 233 | //! @brief Flag for rollback request. |
| 234 | std::atomic_bool inResetting{false}; |
| 235 | //! @brief Mutex for controlling output. |
| 236 | mutable std::mutex outputMtx; |
| 237 | //! @brief The synchronization condition for output. Use with outputMtx. |
| 238 | std::condition_variable outputCond; |
| 239 | //! @brief Flag to indicate whether the output is complete. |
| 240 | std::atomic_bool outputCompleted{true}; |
| 241 | //! @brief Renew the server. |
| 242 | //! @tparam Sock - type of server |
| 243 | template <typename Sock> |
| 244 | void renewServer(); |
| 245 | |
| 246 | template <typename Sock> |
| 247 | friend void intf::connectFromClient(std::shared_ptr<Sock>& client); |
| 248 | //! @brief Connect from the client. Simplified interface for external use. |
| 249 | //! @tparam Sock - type of client |
| 250 | //! @param client - client to be connected |
| 251 | template <typename Sock> |
| 252 | void onConnecting(std::shared_ptr<Sock>& client); |
| 253 | template <typename Sock> |
| 254 | friend void intf::forwardByClient(std::shared_ptr<Sock>& client, const std::vector<std::string>& inputs); |
| 255 | //! @brief Forward message by client. Simplified interface for external use. |
| 256 | //! @tparam Sock - type of client |
| 257 | //! @param client - client to be used for forwarding |
| 258 | //! @param inputs - input strings to be forwarded |
| 259 | template <typename Sock> |
| 260 | void onForwarding(std::shared_ptr<Sock>& client, const std::vector<std::string>& inputs); |
| 261 | //! @brief Parse the TLV packet of message. |
| 262 | //! @tparam Sock - type of client |
| 263 | //! @param client - client associated with parsing |
| 264 | //! @param bytes - message buffer |
| 265 | //! @param size - message length |
| 266 | template <typename Sock> |
| 267 | void parseTLVMessage(std::shared_ptr<Sock>& client, char* const bytes, const std::size_t size); |
| 268 | //! @brief Block the caller until the output task is marked as done. |
| 269 | void waitTaskDone(); |
| 270 | //! @brief Notify that the output task has been completed and unblock any waiters. |
| 271 | void notifyTaskDone(); |
| 272 | friend class configure::Controller<View>; |
| 273 | //! @brief Wait until the viewer reaches the target state. Access controller for external use. |
| 274 | //! @param state - target state |
| 275 | //! @param handling - handling if unexpected state |
| 276 | void syncWaitOr(const State state, const std::function<void()>& handling) const; |
| 277 | //! @brief Notify the viewer to change the state. Access controller for external use. |
| 278 | //! @param action - action to be executed |
| 279 | void syncNotifyVia(const std::function<void()>& action); |
| 280 | //! @brief Keep countdown if the viewer does not meet the condition in time. Access controller for external use. |
| 281 | //! @param condition - condition of countdown |
| 282 | //! @param handling - handling if timeout |
| 283 | void syncCountdownIf(const std::function<bool()>& condition, const std::function<void()>& handling) const; |
| 284 | |
| 285 | //! @brief Check whether it is in the uninterrupted serving state. |
| 286 | //! @param state - target state |
| 287 | //! @return in the uninterrupted serving state or not |
| 288 | bool isInServingState(const State state) const; |
| 289 | //! @brief FSM event. Create server. |
| 290 | struct CreateServer |
| 291 | { |
| 292 | }; |
| 293 | //! @brief FSM event. Destroy server. |
| 294 | struct DestroyServer |
| 295 | { |
| 296 | }; |
| 297 | //! @brief FSM event. Go viewing. |
| 298 | struct GoViewing |
| 299 | { |
| 300 | }; |
| 301 | //! @brief FSM event. NO viewing. |
| 302 | struct NoViewing |
| 303 | { |
| 304 | }; |
| 305 | //! @brief FSM event. Standby. |
| 306 | struct Standby |
| 307 | { |
| 308 | }; |
| 309 | //! @brief FSM event. Relaunch. |
| 310 | struct Relaunch |
| 311 | { |
| 312 | }; |
| 313 | //! @brief Create the view server. |
| 314 | void createViewServer(); |
| 315 | //! @brief Destroy the view server. |
| 316 | void destroyViewServer(); |
| 317 | //! @brief Start viewing. |
| 318 | void startViewing(); |
| 319 | //! @brief Stop viewing. |
| 320 | void stopViewing(); |
| 321 | //! @brief Do toggle. |
| 322 | void doToggle(); |
| 323 | //! @brief Do rollback. |
| 324 | void doRollback(); |
| 325 | // clang-format off |
| 326 | //! @brief Alias for the transition table of the viewer. |
| 327 | using TransitionTable = Table |
| 328 | < |
| 329 | // +------ Source ------+---- Event ----+------ Target ------+--------- Action ---------+- Guard -+ |
| 330 | // +--------------------+---------------+--------------------+--------------------------+---------+ |
| 331 | Row< State::initial , CreateServer , State::active , &View::createViewServer >, |
| 332 | Row< State::active , GoViewing , State::established , &View::startViewing >, |
| 333 | Row< State::established , DestroyServer , State::active , &View::destroyViewServer >, |
| 334 | Row< State::active , NoViewing , State::inactive , &View::stopViewing >, |
| 335 | Row< State::initial , Standby , State::idle , &View::doToggle >, |
| 336 | Row< State::active , Standby , State::idle , &View::doToggle >, |
| 337 | Row< State::established , Standby , State::idle , &View::doToggle >, |
| 338 | Row< State::established , Relaunch , State::initial , &View::doRollback >, |
| 339 | Row< State::inactive , Relaunch , State::initial , &View::doRollback >, |
| 340 | Row< State::idle , Relaunch , State::initial , &View::doRollback > |
| 341 | // +--------------------+---------------+--------------------+--------------------------+---------+ |
| 342 | >; |
| 343 | // clang-format on |
| 344 | //! @brief The notification loop. |
| 345 | void notificationLoop(); |
| 346 | //! @brief Await notification to proceed. |
| 347 | void awaitNotification2Proceed(); |
| 348 | //! @brief Await notification to retry. |
| 349 | //! @return whether retry is required or not |
| 350 | bool awaitNotification2Retry(); |
| 351 | |
| 352 | protected: |
| 353 | friend std::ostream& operator<<(std::ostream& os, const State state); |
| 354 | }; |
| 355 | |
| 356 | extern auto obtainSupportedOptions() -> std::map<std::string, std::string>; |
| 357 | extern std::string buildDisconnectRequest(); |
| 358 | extern void interactionLatency(); |
| 359 | } // namespace view |
| 360 | } // namespace application |
| 361 | |