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