VOXL OpenVINS Server 1.0
Visual Inertial Odometry Server for VOXL Platform
Loading...
Searching...
No Matches
VoxlSpinner.cpp
Go to the documentation of this file.
1/**
2 * @file VoxlSpinner.cpp
3 * @brief Main server implementation for VOXL OpenVINS
4 * @author Zauberflote
5 * @date 2025
6 * @version 1.0
7 *
8 * This file contains the main server implementation for the VOXL OpenVINS system.
9 * It handles initialization, pipe management, signal handling, and the main event loop.
10 *
11 * The server provides:
12 * - VIO system initialization and configuration
13 * - Pipe-based communication with sensors and clients
14 * - Signal handling for graceful shutdown
15 * - Resource management and cleanup
16 * - Main event loop for continuous operation
17 *
18 * The server architecture follows a producer-consumer pattern where:
19 * - IMU and camera data are received through pipe clients
20 * - VIO processing is performed by the OpenVINS library
21 * - Results are published through pipe servers to external clients
22 * - System state is managed through atomic variables and mutexes
23 */
24
25#include <core/VioManager.h>
26#include <core/VioManagerOptions.h>
27#include "ImuMinimal.h"
28#include "CameraManager.h"
29#include <modal_pipe.h>
30#include <modal_json.h>
31#include <modal_flow.h>
32#include <voxl_common_config.h>
33#include "VoxlCommon.h"
34#include "VoxlVars.h"
35#include "VoxlConfigure.h"
36#include <unistd.h>
37#include <signal.h>
38#include <sched.h>
39#include <getopt.h>
40
41// ============================================================================
42// FORWARD DECLARATIONS
43// ============================================================================
44
45/**
46 * @brief Clean shutdown function for the server
47 *
48 * Performs a clean shutdown of the VOXL OpenVINS server, including
49 * thread cancellation, pipe cleanup, camera shutdown, and resource
50 * deallocation.
51 *
52 * The shutdown process is designed to be robust and handle various
53 * failure scenarios gracefully. It ensures that all resources are
54 * properly cleaned up to prevent system resource leaks.
55 *
56 * @param ret Exit code for the process (0 for success, non-zero for error)
57 */
58static void _quit(int ret);
59
60/**
61 * @brief Connect to client pipes (IMU and cameras)
62 *
63 * Establishes connections to the IMU service and camera services,
64 * initializes the camera manager, and sets up the necessary pipe
65 * channels for data communication.
66 *
67 * This function is critical for the data acquisition pipeline and
68 * must complete successfully before the main processing loop can begin.
69 *
70 * @return 0 on success, -1 on failure
71 * @see connect_imu_service()
72 * @see voxl::CameraManager::initialize()
73 */
74static int connect_client_pipes(void);
75
76/**
77 * @brief Create server pipes for data output
78 *
79 * Creates the server pipes that will be used to output VIO data
80 * to clients. Sets up extended VIO data, simple VIO data, and
81 * status pipes with appropriate configurations.
82 *
83 * The pipes are configured with optimal buffer sizes and metadata
84 * to ensure efficient data transmission to client applications.
85 *
86 * @return 0 on success, -1 on failure
87 */
88static int create_server_pipes(void);
89
90/**
91 * @brief Print usage information for command line options
92 *
93 * Displays help information about available command line options
94 * and their usage. This function is called when invalid options
95 * are provided or when the help option is requested.
96 *
97 * @param program_name Name of the program (argv[0])
98 */
99static void print_usage(const char *program_name);
100
101// ============================================================================
102// UTILITY FUNCTIONS
103// ============================================================================
104
105/**
106 * @brief Clean shutdown function for the server
107 *
108 * This function performs a clean shutdown of the VOXL OpenVINS server,
109 * including thread cancellation, pipe cleanup, camera shutdown, and
110 * resource deallocation.
111 *
112 * The shutdown process includes:
113 * - Enabling thread cancellation for all threads
114 * - Closing all pipe connections (client and server)
115 * - Shutting down the camera manager
116 * - Removing the PID file
117 * - Cleaning up image ring buffer
118 * - Forcing process exit
119 *
120 * The function uses _exit() to ensure immediate termination and
121 * prevent any cleanup issues that might occur during normal exit.
122 *
123 * @param ret Exit code for the process (0 for success, non-zero for error)
124 */
125static void _quit(int ret)
126{
127 printf("QUIT REQUESTED\n");
128
129 // FORCE ALL THREADS TO BE CANCELABLE IMMEDIATELY
130 if (en_debug)
131 printf("Setting process-wide thread cancelation policy...\n");
132 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
133 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
134
135 // FORCE KILL THREADS CREATED BY THE PIPE CLIENT
136 if (en_debug)
137 printf("Forcibly closing all pipe connections...\n");
138 pipe_server_close_all();
139 pipe_client_close_all();
140
141 // WAIT FOR THREADS TO TERMINATE
142 if (en_debug)
143 printf("Waiting for threads to terminate...\n");
144 usleep(100000); // 100ms
145
146 printf("Shutting down cameras...\n");
148 printf("Camera shutdown complete\n");
149
150 // REMOVE PID
151 remove_pid_file(PROCESS_NAME);
152
153 // DELETE IMG RINGBUF -- THIS MAY BE UNNECESSARY AS WE ARE NOT DOING OVERLAYS
154 if (img_ringbuf)
155 delete img_ringbuf;
156
157 if (ret == 0)
158 printf("Exiting Cleanly\n");
159 else
160 printf("error code: %d\n", ret);
161
162 if (en_debug)
163 printf("Forcibly exiting process now!\n");
164 _exit(ret); // FORCE EXIT
165 return;
166}
167
168/**
169 * @brief Connect to client pipes (IMU and cameras)
170 *
171 * This function establishes connections to the IMU service and camera
172 * services, initializes the camera manager, and sets up the necessary
173 * pipe channels for data communication.
174 *
175 * The function performs the following operations:
176 * - Connects to the IMU service and validates the connection
177 * - Determines the number of cameras to initialize
178 * - Initializes the camera manager with camera configurations
179 * - Stores channel IDs for each camera for later reference
180 *
181 * The function is designed to fail fast if any critical component
182 * cannot be initialized, ensuring system stability.
183 *
184 * @return 0 on success, -1 on failure
185 * @see connect_imu_service()
186 * @see voxl::CameraManager::initialize()
187 */
188static int connect_client_pipes(void)
189{
190 fprintf(stderr, "connecting client pipes\n");
191
192 // CONNECT TO IMU SERVICE
193 int ret = connect_imu_service();
194 if (ret != 0)
195 {
196 fprintf(stderr, "failed to connect imu service\n");
197 _quit(-1);
198 }
199 fprintf(stderr, "imu pipe name: %s\n", imu_name);
200
201 // CHECK HOW MANY CAMERAS WE SHOULD INITIALIZE
202 cameras_used = cam_info_vec.size();
203 printf("Initializing camera system with %d cameras\n", cameras_used);
204
205 // CONNECT CAMERAS AND INITIALIZE THEM
207 if (ret != 0)
208 {
209 fprintf(stderr, "failed to connect cam service\n");
210 _quit(-1);
211 }
212
213 if (ret == 0)
214 {
215 // STORE CHANNEL ID FOR EACH CAMERA ID
216 size_t i = 0;
217 for (const auto &camera : voxl::CameraManager::getInstance().getAllCameras())
218 {
219 if (i < MAX_CAM_CNT)
220 { // 6 CAMERAS HARDCODED MAXIMUM
221 camera_pipe_channels[i] = camera->get_channel();
222 if (en_debug)
223 {
224 printf("Camera %zu using channel %d\n", i, camera_pipe_channels[i]);
225 }
226 i++;
227 }
228 }
229 }
230
231 return 0;
232}
233
234/**
235 * @brief Create server pipes for data output
236 *
237 * This function creates the server pipes that will be used to output
238 * VIO data to clients. It sets up extended VIO data, simple VIO data,
239 * and status pipes with appropriate configurations.
240 *
241 * The function creates three main pipes:
242 * - Extended VIO data pipe: Comprehensive VIO data with full state information
243 * - Simple VIO data pipe: Essential pose and velocity information
244 * - Status pipe: System status and health information
245 *
246 * Each pipe is configured with:
247 * - Appropriate buffer sizes for data throughput
248 * - JSON metadata including IMU service information
249 * - Control command support (currently disabled)
250 *
251 * The pipes are designed to handle high-frequency data transmission
252 * with minimal latency for real-time applications.
253 *
254 * @return 0 on success, -1 on failure
255 */
256static int create_server_pipes(void)
257{
258 // CREATE RE-USABLE FLAG FOR CONTROL PIPES
259 int flags = SERVER_FLAG_EN_CONTROL_PIPE;
260
261 // OV EXTENDED
262 pipe_info_t info_extended =
263 {
264 OV_VIO_EXTENDED_NAME, // name
265 OV_VIO_EXTENDED_LOCATION, // location
266 "ext_vio_data_t", // type
267 PROCESS_NAME, // server_name
268 EXT_VIO_RECOMMENDED_READ_BUF_SIZE, // size_bytes
269 0 // server_pid
270 };
271
272 if (pipe_server_create(EXTENDED_CH, info_extended, 0))
273 {
274 printf("pipe_server_create(EXTENDED_CH, info_extended, flags) failed\n");
275 _quit(-1);
276 }
277
278 // ADD OPTIONAL IMU FIELD IN JSON
279 cJSON *json = pipe_server_get_info_json_ptr(EXTENDED_CH);
280 cJSON_AddStringToObject(json, "imu", imu_name);
281 pipe_server_update_info(EXTENDED_CH);
282
283 // SET CONTROL CALLBACKS FOR EXTENDED PIPE
284 pipe_server_set_control_cb(EXTENDED_CH, voxl::Publisher::getInstance().ov_vio_control_pipe_cb, NULL);
285 pipe_server_set_available_control_commands(EXTENDED_CH, OV_VIO_CONTROL_COMMANDS);
286
287 // OV SIMPLE
288 pipe_info_t info_simple =
289 {
290 OV_VIO_SIMPLE_NAME, // name
291 OV_VIO_SIMPLE_LOCATION, // location
292 "vio_data_t", // type
293 PROCESS_NAME, // server_name
294 VIO_RECOMMENDED_PIPE_SIZE, // size_bytes
295 0 // server_pid
296 };
297
298 if (pipe_server_create(SIMPLE_CH, info_simple, flags))
299 {
300 printf("pipe_server_create(SIMPLE_CH, info_simple, flags) failed\n");
301 _quit(-1);
302 }
303
304 // ADD OPTIONAL IMU FIELD IN JSON
305 json = pipe_server_get_info_json_ptr(SIMPLE_CH);
306 cJSON_AddStringToObject(json, "imu", imu_name);
307 pipe_server_update_info(SIMPLE_CH);
308
309 // SET CONTROL CALLBACKS FOR SIMPLE PIPE
310 pipe_server_set_control_cb(SIMPLE_CH, voxl::Publisher::getInstance().ov_vio_control_pipe_cb, NULL);
311 pipe_server_set_available_control_commands(SIMPLE_CH, OV_VIO_CONTROL_COMMANDS);
312
313 // OV STATUS
314 pipe_info_t info_status =
315 {
316 OV_STATUS_NAME, // name
317 OV_STATUS_LOCATION, // location
318 "ov_status_t", // type
319 PROCESS_NAME, // server_name
320 VIO_RECOMMENDED_PIPE_SIZE, // size_bytes
321 0 // server_pid
322 };
323
324 if (pipe_server_create(OVSTATUS_CH, info_status, flags))
325 {
326 printf("pipe_server_create(SIMPLE_CH, info_status, flags) failed\n");
327 _quit(-1);
328 }
329
330 return 0;
331}
332
333/**
334 * @brief Print usage information for command line options
335 *
336 * Displays help information about available command line options
337 * and their usage. This function is called when invalid options
338 * are provided or when the help option is requested.
339 *
340 * @param program_name Name of the program (argv[0])
341 */
342static void print_usage(const char *program_name)
343{
344 printf("Usage: %s [OPTIONS]\n", program_name);
345 printf("VOXL OpenVINS Server - Visual Inertial Odometry System\n\n");
346 printf("Options:\n");
347 printf(" -d, --debug Enable debug output and detailed logging\n");
348 printf(" -v, --verbose Enable verbose output and status information\n");
349 printf(" -c, --config Configuration only mode (load and validate config)\n");
350 printf(" -i, --imu-body Enable IMU body measurements\n");
351 printf(" -h, --help Display this help message\n\n");
352 printf("Examples:\n");
353 printf(" %s Run server with default settings\n", program_name);
354 printf(" %s -d Run server with debug output enabled\n", program_name);
355 printf(" %s -v -c Load configuration only with verbose output\n", program_name);
356 printf(" %s --imu-body Run with IMU body measurements enabled\n", program_name);
357 printf(" %s --debug --verbose Run with both debug and verbose output\n", program_name);
358}
359
360// ============================================================================
361// MAIN FUNCTION
362// ============================================================================
363
364using namespace ov_msckf;
365
366/**
367 * @brief Main entry point for VOXL OpenVINS server
368 *
369 * This function initializes the VIO system, sets up pipe communications,
370 * configures signal handling, and runs the main event loop. It handles
371 * the complete lifecycle of the VIO server from startup to shutdown.
372 *
373 * The main function performs the following operations:
374 * - Loads and validates server configuration files
375 * - Synchronizes camera configurations with system services
376 * - Creates and configures the VIO manager with OpenVINS
377 * - Sets up process management (PID file, signal handling)
378 * - Configures process priority and CPU affinity
379 * - Creates server pipes for data output
380 * - Connects to client pipes (IMU and cameras)
381 * - Runs the main event loop until shutdown is requested
382 *
383 * The function implements a robust initialization sequence that ensures
384 * all components are properly configured before starting the main loop.
385 * It also handles graceful shutdown through signal handling.
386 *
387 * @param argc Number of command line arguments (currently unused)
388 * @param argv Array of command line argument strings (currently unused)
389 * @return 0 on successful execution, non-zero on error
390 *
391 * @note The function uses _exit() for termination, so normal return paths
392 * are not reached. This ensures immediate process termination.
393 */
394int main(int argc, char *argv[])
395{
396 // Parse command line options
397 int opt;
398 const char *short_options = "dvhci";
399 struct option long_options[] = {
400 {"debug", no_argument, 0, 'd'},
401 {"verbose", no_argument, 0, 'v'},
402 {"help", no_argument, 0, 'h'},
403 {"config", no_argument, 0, 'c'},
404 {"imu-body", no_argument, 0, 'i'},
405 {0, 0, 0, 0}
406 };
407
408 while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
409 switch (opt) {
410 case 'd':
411 en_debug = 1;
412 if (en_verbose) {
413 printf("Debug mode enabled\n");
414 }
415 break;
416 case 'v':
417 en_verbose = 1;
418 if (en_verbose) {
419 printf("Verbose mode enabled\n");
420 }
421 break;
422 case 'c':
423 config_only = 1;
424 if (en_verbose) {
425 printf("Configuration only mode enabled\n");
426 }
427 break;
428 case 'i':
429 en_imu_body = true;
430 if (en_verbose) {
431 printf("IMU body measurements enabled\n");
432 }
433 break;
434 case 'h':
435 print_usage(argv[0]);
436 _quit(-1);
437 break; // This break is unreachable but prevents compiler warning
438 default:
439 print_usage(argv[0]);
440 break;
441 }
442 }
443
444 // Check for any non-option arguments
445 if (optind < argc) {
446 printf("Error: Unexpected arguments provided\n");
447 print_usage(argv[0]);
448 return 1;
449 }
450
451 // Load the config files
452 if (voxl::read_server_config() < 0)
453 {
454 printf("config_file_read() < 0\n");
455 _quit(-1);
456 }
457
458 // If configuration only mode is enabled, exit after loading config
459 if (config_only) {
460 if (en_verbose) {
461 printf("Configuration loaded successfully. Exiting due to config-only mode.\n");
462 }
463 return 0;
464 }
465
466 // read camera multicam setup and configs
467 if (voxl::sync_cam_config() < 0)
468 {
469 fprintf(stderr, "ERROR cam_config_file_read\n");
470 _quit(-1);
471 }
472 if (en_verbose) {
473 printf("Camera configuration synchronized successfully\n");
474 }
475
476 auto& oclMgr = VoxlOCLManager::instance();
477 if (oclMgr.init()) {
478 std::cerr << "OpenCL init failed!\n";
479 return -1;
480 }
481
482 std::string config_path = std::string(folder_base) + "/estimator_config.yaml"; // PLACEHOLDER --> CHECK LOCATION OF YAMLS POST FLASH
483
484 // Create the VIO Manager -- Core OpenVINS state
485 vio_manager_options = VioManagerOptions();
486 auto parser = std::make_shared<ov_core::YamlParser>(config_path);
487
488 // THIS GETS OVERWRITTEN BY THE YAML
489 std::string verbosity = "SILENT";
490 parser->parse_config("verbosity", verbosity);
491 vio_manager_options.print_and_load(parser);
492 if (en_verbose) {
493 ov_core::Printer::setPrintLevel(ov_core::Printer::DEBUG);
494 } else {
495 ov_core::Printer::setPrintLevel(ov_core::Printer::SILENT);
496 }
497
498 vio_manager = std::unique_ptr<VioManager>(new VioManager(vio_manager_options));
499
500
501
502 /* make sure another instance isn't running
503 * if return value is -3 then a background process is running with
504 * higher privaledges and we couldn't kill it, in which case we should
505 * not continue or there may be hardware conflicts. If it returned -4
506 * then there was an invalid argument that needs to be fixed.
507 */
508 if (kill_existing_process(PROCESS_NAME, 2.0) < -2)
509 {
510 std::cerr << "ERROR: could not kill existing process" << std::endl;
511 _quit(-1);
512 }
513
514 // start signal handler so we can exit cleanly
515 if (enable_signal_handler() == -1)
516 {
517 std::cerr << "ERROR: failed to start signal handler" << std::endl;
518 _quit(-1);
519 kill(getpid(), SIGKILL);
520 }
521
522 // make PID file to indicate your project is running
523 // due to the check made on the call to rc_kill_existing_process() above
524 // we can be fairly confident there is no PID file already and we can
525 // make our own safely.
526 make_pid_file(PROCESS_NAME);
527
528 // This is a heavy multithreaded process, set to medium priority so we don't
529 // starve more time sensitive things like drivers and VFC
530 pipe_set_process_priority(THREAD_PRIORITY_RT_MED);
531
532 // on qrb5165 keep this process on the larger cores
533 set_cpu_affinity(cpu_set_big_cores_and_gold_core());
534 print_cpu_affinity();
535
536 // NOW TELL THAT VIO IS INITIALIZING -- THREAD SAFE
537 vio_state = VIO_STATE_INITIALIZING;
538 vio_error_codes = 0;
539
540 // NOW CREATE THE PIPES
541 if (create_server_pipes() < 0)
542 {
543 printf("connect_client_pipes < 0\n");
544 _quit(0);
545 }
546
547 // Connect to the client pipes and start getting data
548 if (connect_client_pipes() < 0)
549 {
550 printf("connect_client_pipes < 0\n");
551 _quit(0);
552 }
553
554 main_running = 1;
555
556 // zeroTimeOut = boost::posix_time::microsec_clock::local_time();
557
558 usleep(100000); // 100ms
560
561 while (main_running)
562 {
563 usleep(1e6);
564 }
565
566 printf("Quiting OpenVINS server\n");
567 // Shutdown Nicely
568 _quit(0);
569
570 // This code will never be reached due to exit() in _quit()
571 return 0;
572}
Camera management system for VOXL OpenVINS.
int connect_imu_service(void)
Creates IMU pipe client and associated callbacks.
IMU interface and data handling for VOXL OpenVINS.
Common definitions and utilities for the VOXL OpenVINS server.
Configuration management for VOXL OpenVINS server.
int main(int argc, char *argv[])
Main entry point for VOXL OpenVINS server.
int en_verbose
Enable verbose output.
Definition VoxlVars.cpp:151
int cameras_used
Number of cameras currently in use.
Definition VoxlVars.cpp:202
int camera_pipe_channels[MAX_CAM_CNT]
Camera pipe channels array.
Definition VoxlVars.cpp:224
volatile int main_running
Main process running flag.
Definition VoxlVars.cpp:38
char folder_base[CHAR_BUF_SIZE]
Base folder for yaml configuration files.
Definition VoxlVars.cpp:145
ov_msckf::VioManagerOptions vio_manager_options
VIO manager options.
Definition VoxlVars.cpp:28
char imu_name[64]
IMU device name.
Definition VoxlVars.cpp:164
voxl::img_ringbuf_packet * img_ringbuf
Image ring buffer for processing.
Definition VoxlVars.cpp:231
bool en_imu_body
Enable IMU body measurements.
Definition VoxlVars.cpp:157
std::unique_ptr< ov_msckf::VioManager > vio_manager
Main VIO manager instance.
Definition VoxlVars.cpp:31
int en_debug
Enable debug output.
Definition VoxlVars.cpp:148
std::vector< cam_info > cam_info_vec
Vector of camera configuration information.
Definition VoxlVars.cpp:170
int config_only
Configuration only mode.
Definition VoxlVars.cpp:154
Global variable declarations and constants for VOXL OpenVINS server.
#define OV_VIO_EXTENDED_NAME
Name for extended VIO data pipe.
Definition VoxlVars.h:226
#define SIMPLE_CH
MPA server channel for simple VIO data output.
Definition VoxlVars.h:154
std::atomic< uint8_t > vio_state
Current VIO system state.
#define EXTENDED_CH
MPA server channel for extended VIO data output.
Definition VoxlVars.h:146
std::atomic< uint32_t > vio_error_codes
VIO error codes.
#define OV_VIO_SIMPLE_LOCATION
Location for simple VIO data pipe.
Definition VoxlVars.h:248
#define OV_STATUS_LOCATION
Location for status information pipe.
Definition VoxlVars.h:277
#define OV_STATUS_NAME
Name for status information pipe.
Definition VoxlVars.h:270
#define OV_VIO_SIMPLE_NAME
Name for simple VIO data pipe.
Definition VoxlVars.h:241
#define OV_VIO_EXTENDED_LOCATION
Location for extended VIO data pipe.
Definition VoxlVars.h:233
#define PROCESS_NAME
Process name for the VOXL OpenVINS server.
Definition VoxlVars.h:288
#define OV_VIO_CONTROL_COMMANDS
Control commands available for VIO system.
Definition VoxlVars.h:218
#define OVSTATUS_CH
MPA server channel for status information.
Definition VoxlVars.h:170
#define MAX_CAM_CNT
Maximum number of cameras supported by VOXL2.
Definition VoxlVars.h:296
void shutdown()
Shut down all cameras and clean up resources.
bool initialize(const std::vector< cam_info > &camera_configs)
Initialize the camera manager with camera configurations.
static CameraManager & getInstance()
Get the singleton instance of the CameraManager.
static Publisher & getInstance()
Get singleton instance.
Definition VoxlHK.h:142
void start()
Start the publisher.
Main namespace for VOXL OpenVINS server components.
int sync_cam_config(void)
Synchronize camera configuration with system services.
int read_server_config(void)
Read and parse server configuration file.