RocketLogger 2.1.0
rl.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2016-2020, Swiss Federal Institute of Technology (ETH Zurich)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * * Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE
23 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <errno.h>
33#include <stdarg.h>
34#include <stdbool.h>
35#include <stdint.h>
36#include <stdio.h>
37#include <string.h>
38
39#include <sys/ipc.h>
40#include <sys/shm.h>
41#include <sys/types.h>
42#include <unistd.h>
43#include <zmq.h>
44
45#include "log.h"
46#include "sensor/sensor.h"
47
48#include "rl.h"
49
50#define RL_JSON_BUFFER_SIZE 10000
51
57 .background_enable = false,
58 .interactive_enable = false,
59 .sample_limit = 0,
60 .sample_rate = RL_SAMPLE_RATE_MIN,
61 .update_rate = 1,
62 .channel_enable = RL_CONFIG_CHANNEL_ENABLE_DEFAULT,
63 .channel_force_range = RL_CONFIG_CHANNEL_FORCE_RANGE_DEFAULT,
64 .aggregation_mode = RL_AGGREGATION_MODE_DOWNSAMPLE,
65 .digital_enable = true,
66 .web_enable = true,
67 .calibration_ignore = false,
68 .ambient_enable = false,
69 .file_enable = true,
70 .file_name = RL_CONFIG_FILE_DEFAULT,
71 .file_format = RL_FILE_FORMAT_RLD,
72 .file_size = RL_CONFIG_FILE_SIZE_DEFAULT,
73 .file_comment = RL_CONFIG_COMMENT_DEFAULT,
74};
75
80 .sampling = false,
81 .error = false,
82 .sample_count = 0,
83 .buffer_count = 0,
84 .calibration_time = 0,
85 .calibration_file = RL_CALIBRATION_SYSTEM_FILE,
86 .disk_free = 0,
87 .disk_free_permille = 0,
88 .disk_use_rate = 0,
89 .sensor_count = 0,
90 .sensor_available = {false},
91 .config = NULL,
92};
93
95char const *const RL_CHANNEL_NAMES[RL_CHANNEL_COUNT] = {
96 "V1", "V2", "V3", "V4", "I1L", "I1H", "I2L", "I2H", "DT"};
97
100 "I2H"};
101
104 "DI1", "DI2", "DI3", "DI4", "DI5", "DI6"};
105
108 "I1L_valid", "I2L_valid"};
109
111// rl_status_t rl_status = rl_status_default;
113 .sampling = false,
114 .error = false,
115 .sample_count = 0,
116 .buffer_count = 0,
117 .calibration_time = 0,
118 .calibration_file = RL_CALIBRATION_SYSTEM_FILE,
119 .disk_free = 0,
120 .disk_free_permille = 0,
121 .disk_use_rate = 0,
122 .sensor_count = 0,
123 .sensor_available = {false},
124 .config = NULL,
125};
126
131
139static void print_config_line(char const *const description, char const *format,
140 ...);
141
142void rl_config_print(rl_config_t const *const config) {
143 // sampling in background or interactively
144 print_config_line("Run in background",
145 config->background_enable ? "yes" : "no");
146 print_config_line("Interactive data display",
147 config->interactive_enable ? "yes" : "no");
148 print_config_line("Sample limit", "%llu", config->sample_limit);
149
150 // sample rate and aggregation
151 print_config_line("Sampling rate", "%u Sps", config->sample_rate);
152 switch (config->aggregation_mode) {
154 print_config_line("Data aggregation", "down sample");
155 break;
156
158 print_config_line("Data aggregation", "average samples");
159 break;
160 default:
161 print_config_line("Data aggregation", "undefined");
162 break;
163 }
164
165 // channel and force configuration
166 print_config_line(
167 "Channels enabled", "V1: %s V2: %s V3: %s V4: %s",
168 config->channel_enable[RL_CONFIG_CHANNEL_V1] ? "on " : "off",
169 config->channel_enable[RL_CONFIG_CHANNEL_V2] ? "on " : "off",
170 config->channel_enable[RL_CONFIG_CHANNEL_V3] ? "on " : "off",
171 config->channel_enable[RL_CONFIG_CHANNEL_V4] ? "on " : "off");
172 print_config_line(
173 "", "I1L: %s I1H: %s I2L: %s I2H: %s DT: %s",
174 config->channel_enable[RL_CONFIG_CHANNEL_I1L] ? "on " : "off",
175 config->channel_enable[RL_CONFIG_CHANNEL_I1H] ? "on " : "off",
176 config->channel_enable[RL_CONFIG_CHANNEL_I2L] ? "on " : "off",
177 config->channel_enable[RL_CONFIG_CHANNEL_I2H] ? "on " : "off",
178 config->channel_enable[RL_CONFIG_CHANNEL_DT] ? "on " : "off");
179 print_config_line(
180 "Force High Channels", "I1H: %s I2H: %s",
181 config->channel_force_range[RL_CONFIG_CHANNEL_I1] ? "on " : "off",
182 config->channel_force_range[RL_CONFIG_CHANNEL_I2] ? "on " : "off");
183
184 print_config_line("Digital inputs",
185 config->digital_enable ? "enabled" : "disabled");
186
187 print_config_line("File storing",
188 config->file_enable ? "enabled" : "disabled");
189 print_config_line("File name", config->file_name);
190 switch (config->file_format) {
192 print_config_line("File format", "RLD binary file");
193 break;
194
196 print_config_line("File format", "CSV text file");
197 break;
198 default:
199 print_config_line("File format", "undefined");
200 break;
201 }
202 print_config_line("Max. file size", "%llu Bytes", config->file_size);
203
204 print_config_line("Update rate", "%u Hz", config->update_rate);
205 print_config_line("Web server",
206 config->web_enable ? "enabled" : "disabled");
207 print_config_line("Calibration measurement",
208 config->calibration_ignore ? "enabled" : "disabled");
209}
210
211void rl_config_print_cmd(rl_config_t const *const config) {
212 // start appending command step by step
213 printf("rocketlogger start");
214
215 // sampling mode and limit
216 if (config->background_enable) {
217 printf(" --background");
218 }
219 if (config->interactive_enable) {
220 printf(" --interactive");
221 }
222
223 printf(" --samples=%llu", config->sample_limit);
224
225 // sample rate and aggregation
226 printf(" --rate=%u", config->sample_rate);
227 printf(" --update=%u", config->update_rate);
228
229 // channels
230 printf(" --channel=");
231 for (int i = 0; i < RL_CHANNEL_COUNT; i++) {
232 if (config->channel_enable[i]) {
233 printf("%s,", RL_CHANNEL_NAMES[i]);
234 }
235 }
236 printf(" --high-range=");
237 for (int i = 0; i < RL_CHANNEL_SWITCHED_COUNT; i++) {
238 if (config->channel_force_range[i]) {
239 printf("%s,", RL_CHANNEL_FORCE_NAMES[i]);
240 }
241 }
242 printf(" ");
243
244 switch (config->aggregation_mode) {
246 printf(" --aggregate=average");
247 break;
249 default:
250 printf(" --aggregate=downsample");
251 break;
252 }
253
254 // arguments
255 printf(" --ambient=%s", config->ambient_enable ? "true" : "false");
256 printf(" --digital=%s", config->digital_enable ? "true" : "false");
257 printf(" --web=%s", config->web_enable ? "true" : "false");
258
259 if (config->calibration_ignore) {
260 printf(" --calibration");
261 }
262
263 // file
264 if (config->file_enable) {
265 printf(" --output=%s", config->file_name);
266 printf(" --format=%s",
267 (config->file_format == RL_FILE_FORMAT_RLD) ? "rld" : "csv");
268 printf(" --size=%llu", config->file_size);
269 printf(" --comment='%s'\n", config->file_comment);
270 } else {
271 printf(" --output=0\n");
272 }
273}
274
275void rl_config_print_json(rl_config_t const *const config) {
276 char const *const config_json = rl_config_get_json(config);
277 printf("%s", config_json);
278}
279
280char *rl_config_get_json(rl_config_t const *const config) {
281 int count;
282 static char buffer[RL_JSON_BUFFER_SIZE];
283
284 snprintf(buffer, RL_JSON_BUFFER_SIZE, "{ ");
285 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"ambient_enable\": %s, ",
286 config->ambient_enable ? "true" : "false");
287 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"background_enable\": %s, ",
288 config->background_enable ? "true" : "false");
289 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"interactive_enable\": %s, ",
290 config->interactive_enable ? "true " : "false");
291 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"calibration_ignore\": %s, ",
292 config->calibration_ignore ? "true" : "false");
293
294 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"channel_enable\": [");
295 count = 0;
296 for (int i = 0; i < RL_CHANNEL_COUNT; i++) {
297 if (!config->channel_enable[i]) {
298 continue;
299 }
300 if (count > 0) {
301 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, ", ");
302 }
303 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"%s\"", RL_CHANNEL_NAMES[i]);
304 count++;
305 }
306 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "], ");
307
308 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"channel_force_range\": [");
309 count = 0;
310 for (int i = 0; i < RL_CHANNEL_SWITCHED_COUNT; i++) {
311 if (!config->channel_force_range[i]) {
312 continue;
313 }
314 if (count > 0) {
315 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, ", ");
316 }
317 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"%s\"",
319 count++;
320 }
321 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "], ");
322 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"digital_enable\": %s, ",
323 config->digital_enable ? "true" : "false");
324 if (config->file_enable == false) {
325 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"file\": null, ");
326 } else {
327 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"file\": { ");
328 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"comment\": \"%s\", ",
329 config->file_comment);
330 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"filename\": \"%s\", ",
331 config->file_name);
332 switch (config->file_format) {
334 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"format\": \"rld\", ");
335 break;
337 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"format\": \"csv\", ");
338 break;
339 default:
340 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"format\": null, ");
341 break;
342 }
343 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"size\": %llu",
344 config->file_size);
345 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, " }, ");
346 }
347 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"sample_limit\": %llu, ",
348 config->sample_limit);
349 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"sample_rate\": %u, ",
350 config->sample_rate);
351 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"update_rate\": %u, ",
352 config->update_rate);
353 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"web_enable\": %s",
354 config->web_enable ? "true" : "false");
355 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, " }");
356
357 return buffer;
358}
359
360void rl_config_reset(rl_config_t *const config) {
361 memcpy(config, &rl_config_default, sizeof(rl_config_t));
362}
363
365 char *config_file = NULL;
366 int ret;
367
368 // check if user/system config file existing
369 ret = access(RL_CONFIG_USER_FILE, R_OK);
370 if (ret == 0) {
371 config_file = RL_CONFIG_USER_FILE;
372 } else {
373 ret = access(RL_CONFIG_SYSTEM_FILE, R_OK);
374 if (ret == 0) {
375 config_file = RL_CONFIG_SYSTEM_FILE;
376 } else {
377 // no config file found
378 rl_config_reset(config);
379 return SUCCESS;
380 }
381 }
382
383 // open config file
384 FILE *file = fopen(config_file, "r");
385 if (file == NULL) {
387 "failed to open configuration file '%s'; %d message: %s",
388 config_file, errno, strerror(errno));
389 return ERROR;
390 }
391
392 // read values
393 fread(config, sizeof(rl_config_t), 1, file);
394
395 // close file
396 fclose(file);
397
398 // reset file comment, as it is not stored yet
401
402 // check version
403 if (config->config_version != RL_CONFIG_VERSION) {
405 "Old or invalid configuration file. Using default "
406 "config as fall back.");
407 rl_config_reset(config);
408 return SUCCESS;
409 }
410
411 return SUCCESS;
412}
413
414int rl_config_write_default(rl_config_t const *const config) {
415 // open config file
416 FILE *file = fopen(RL_CONFIG_USER_FILE, "w");
417 if (file == NULL) {
419 "failed to create configuration file; %d message: %s", errno,
420 strerror(errno));
421 return ERROR;
422 }
423 // write values
424 fwrite(config, sizeof(rl_config_t), 1, file);
425
426 // close file
427 fclose(file);
428 return SUCCESS;
429}
430
431int rl_config_validate(rl_config_t const *const config) {
432 // check individual arguments
433 if (config->config_version != RL_CONFIG_VERSION) {
434 rl_log(RL_LOG_ERROR, "invalid config version (%x, should be %x).",
436 return ERROR;
437 }
438
439 // check supported sample rate (non-zero, natively supported or divisor of
440 // lowest rate for aggregation)
441 bool sample_rate_native =
442 (config->sample_rate == 1000 || config->sample_rate == 2000 ||
443 config->sample_rate == 4000 || config->sample_rate == 8000 ||
444 config->sample_rate == 16000 || config->sample_rate == 32000 ||
445 config->sample_rate == 64000);
446 if (config->sample_rate == 0 ||
447 (!sample_rate_native &&
448 ((RL_SAMPLE_RATE_MIN % config->sample_rate) > 0))) {
450 "invalid sample rate (%u). Needs to be natively supported "
451 "value, or valid divisor of %u.",
453 return ERROR;
454 }
455
456 // check supported update rate (non-zero, divisor of the sample rate)
457 if (config->update_rate == 0 ||
458 ((config->sample_rate % config->update_rate) > 0)) {
460 "invalid update rate (%u). Needs to be a valid divisor of the "
461 "sample rate (%u).",
462 config->update_rate, config->sample_rate);
463 return ERROR;
464 }
465
466 // check supported file size (either zero or at least minimum value)
467 if (config->file_size > 0 && config->file_size < RL_CONFIG_FILE_SIZE_MIN) {
468 rl_log(RL_LOG_ERROR, "invalid update rate. Needs to be a valid divisor "
469 "of the sample rate.");
470 return ERROR;
471 }
472
473 // file comment allows only for printable or space characters
474 if (!is_printable_string(config->file_comment)) {
475 rl_log(RL_LOG_ERROR, "invalid character in file comment, supports only "
476 "printable and white space characters.");
477 return ERROR;
478 }
479
480 // sample limit: accept any positive integer, or zero -> no check needed
481 // .sample_limit = 0UL,
482
483 // checking boolean values not required:
484 // .background_enable = false,
485 // .interactive_enable = false,
486 // .channel_enable = RL_CONFIG_CHANNEL_ENABLE_DEFAULT,
487 // .channel_force_range = RL_CONFIG_CHANNEL_FORCE_RANGE_DEFAULT,
488 // .digital_enable = true,
489 // .web_enable = true,
490 // .calibration_ignore = false,
491 // .ambient_enable = false,
492 // .file_enable = true,
493
494 // checking enum values not required:
495 // .aggregation_mode = RL_AGGREGATION_MODE_DOWNSAMPLE,
496 // .file_format = RL_FILE_FORMAT_RLD,
497
498 // check incompatible/invalid combinations
499 if (config->background_enable && config->interactive_enable) {
501 "enabling both background and interactive is unsupported.");
502 return ERROR;
503 }
504
505 return SUCCESS;
506}
507
508pid_t rl_pid_get(void) {
509 pid_t pid;
510 FILE *file = fopen(RL_PID_FILE, "r");
511 if (file == NULL) {
512 return 0;
513 }
514
515 fscanf(file, "%d", &pid);
516 fclose(file);
517
518 return pid;
519}
520
521int rl_pid_set(pid_t pid) {
522 FILE *file = fopen(RL_PID_FILE, "w");
523 if (file == NULL) {
524 rl_log(RL_LOG_ERROR, "Failed to create pid file; %d message: %s", errno,
525 strerror(errno));
526 return ERROR;
527 }
528
529 fprintf(file, "%d\n", pid);
530 fclose(file);
531
532 return SUCCESS;
533}
534
535void rl_status_reset(rl_status_t *const status) {
536 memcpy(status, &rl_status_default, sizeof(rl_status_t));
537}
538
540 // open and bind zmq status publisher
541 zmq_status_context = zmq_ctx_new();
542 zmq_status_publisher = zmq_socket(zmq_status_context, ZMQ_PUB);
543 int zmq_res = zmq_bind(zmq_status_publisher, RL_ZMQ_STATUS_SOCKET);
544 if (zmq_res < 0) {
546 "failed binding zeromq status publisher; %d message: %s", errno,
547 strerror(errno));
548 return ERROR;
549 }
550
551 return SUCCESS;
552}
553
555 // close and destroy zmq status publisher
556 zmq_close(zmq_status_publisher);
557 zmq_ctx_destroy(zmq_status_context);
558
560 zmq_status_context = NULL;
561
562 return SUCCESS;
563}
564
566 // create shared memory
567 int shm_id = shmget(RL_SHMEM_STATUS_KEY, sizeof(rl_status_t),
568 IPC_CREAT | RL_SHMEM_PERMISSIONS);
569 if (shm_id == -1) {
571 "failed creating shared status memory; %d message: %s", errno,
572 strerror(errno));
573 return ERROR;
574 }
575
576 return SUCCESS;
577}
578
580 // get ID and attach shared memory
581 int shm_id =
583 if (shm_id == -1) {
585 "failed getting shared memory id for removal; %d message: %s",
586 errno, strerror(errno));
587 return ERROR;
588 }
589
590 // mark shared memory for deletion
591 int res = shmctl(shm_id, IPC_RMID, NULL);
592 if (res == -1) {
594 "failed removing shared status memory; %d message: %s", errno,
595 strerror(errno));
596 return ERROR;
597 }
598
599 return SUCCESS;
600}
601
602int rl_status_read(rl_status_t *const status) {
603 // get ID and attach shared memory
604 int shm_id =
606 if (shm_id == -1) {
608 "failed getting shared memory id for reading the "
609 "status; %d message: %s",
610 errno, strerror(errno));
611 return ERROR;
612 }
613
614 rl_status_t const *const shm_status =
615 (rl_status_t const *const)shmat(shm_id, NULL, 0);
616 if (shm_status == (void *)-1) {
618 "failed mapping shared memory for reading the "
619 "status; %d message: %s",
620 errno, strerror(errno));
621 return ERROR;
622 }
623
624 // copy status read from shared memory
625 memcpy(status, shm_status, sizeof(rl_status_t));
626 status->config = NULL;
627
628 // detach shared memory
629 int res = shmdt(shm_status);
630 if (res < 0) {
631 rl_log(
633 "failed detaching shared memory after status read; %d message: %s",
634 errno, strerror(errno));
635 return ERROR;
636 }
637
638 return SUCCESS;
639}
640
641int rl_status_write(rl_status_t *const status) {
642 // get ID and attach shared memory
643 int shm_id =
645 if (shm_id == -1) {
647 "failed getting shared memory id for writing the status; %d "
648 "message %s",
649 errno, strerror(errno));
650 return ERROR;
651 }
652
653 rl_status_t *const shm_status = (rl_status_t *const)shmat(shm_id, NULL, 0);
654 if (shm_status == (void *)-1) {
656 "failed mapping shared memory for writing the status; %d "
657 "message %s",
658 errno, strerror(errno));
659 return ERROR;
660 }
661
662 // write status
663 memcpy(shm_status, status, sizeof(rl_status_t));
664
665 // detach shared memory
666 int res = shmdt(shm_status);
667 if (res < 0) {
668 rl_log(
670 "failed detaching shared memory after status write; %d message: %s",
671 errno, strerror(errno));
672 return ERROR;
673 }
674
675 // write status json to zmq socket if enabled
676 if (zmq_status_publisher != NULL) {
677 // update file system state
678 int64_t disk_free = 0;
679 int64_t disk_total = 0;
680 if (status->config != NULL) {
681 disk_free = fs_space_free(status->config->file_name);
682 disk_total = fs_space_total(status->config->file_name);
683 } else {
686 }
687
688 status->disk_free = disk_free;
689 if (disk_total > 0) {
690 status->disk_free_permille = (1000 * disk_free) / disk_total;
691 } else {
692 status->disk_free_permille = 0;
693 }
694
695 // get status as json string and publish to zeromq
696 char const *const status_json = rl_status_get_json(status);
697 int zmq_res =
698 zmq_send(zmq_status_publisher, status_json, strlen(status_json), 0);
699 if (zmq_res < 0) {
700 rl_log(RL_LOG_ERROR, "failed publishing status; %d message: %s",
701 errno, strerror(errno));
702 return ERROR;
703 }
704 }
705
706 return SUCCESS;
707}
708
709void rl_status_print(rl_status_t const *const status) {
710 print_config_line("Sampling", status->sampling ? "yes" : "no");
711 print_config_line("Error", status->error ? "yes" : "no");
712 print_config_line("Sample count", "%llu", status->sample_count);
713 print_config_line("Buffer count", "%llu", status->buffer_count);
714 print_config_line("Calibration time", "%llu", status->calibration_time);
715 if (status->calibration_time > 0) {
716 print_config_line("Calibration file", status->calibration_file);
717 } else {
718 print_config_line("Calibration file", "calibration ignored!");
719 }
720 print_config_line("Disk free", "%llu Bytes", status->disk_free);
721 print_config_line("Disk free", "%u/1000", status->disk_free_permille);
722 print_config_line("Disk use rate", "%u Bytes/s", status->disk_use_rate);
723 print_config_line("Sensors found", "%u total", status->sensor_count);
724 for (uint16_t i = 0; i < SENSOR_REGISTRY_SIZE; i++) {
725 if (status->sensor_available[i]) {
726 print_config_line("", SENSOR_REGISTRY[i].name);
727 }
728 }
729}
730
731void rl_status_print_json(rl_status_t const *const status) {
732 char const *const status_json = rl_status_get_json(status);
733 printf("%s", status_json);
734}
735
736char *rl_status_get_json(rl_status_t const *const status) {
737 static char buffer[RL_JSON_BUFFER_SIZE];
738
739 snprintf(buffer, RL_JSON_BUFFER_SIZE, "{ ");
740 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"sampling\": %s, ",
741 status->sampling ? "true" : "false");
742 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"error\": %s, ",
743 status->error ? "true" : "false");
744 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"sample_count\": %llu, ",
745 status->sample_count);
746 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"buffer_count\": %llu, ",
747 status->buffer_count);
748 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"calibration_time\": %llu, ",
749 status->calibration_time);
750 if (status->calibration_time > 0) {
752 "\"calibration_file\": \"%s\", ", status->calibration_file);
753 } else {
755 "\"calibration_file\": null, ");
756 }
757 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"disk_free_bytes\": %llu, ",
758 status->disk_free);
759 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"disk_free_permille\": %u, ",
760 status->disk_free_permille);
761 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"disk_use_rate\": %u, ",
762 status->disk_use_rate);
763 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"sensor_count\": %u, ",
764 status->sensor_count);
765 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"sensor_names\": [");
766 for (uint16_t i = 0; i < SENSOR_REGISTRY_SIZE; i++) {
767 if (i > 0) {
768 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, ", ");
769 }
770 if (status->sensor_available[i]) {
771 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"%s\"",
772 SENSOR_REGISTRY[i].name);
773 } else {
774 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "null");
775 }
776 }
777 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "]");
778 if (status->config != NULL) {
779 char const *config_json = rl_config_get_json(status->config);
780 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, ", \"config\": %s",
781 config_json);
782 }
783 snprintfcat(buffer, RL_JSON_BUFFER_SIZE, " }");
784
785 return buffer;
786}
787
788static void print_config_line(char const *description, char const *format,
789 ...) {
790 va_list args;
791 va_start(args, format);
792 printf(" %24s - ", description);
793 vprintf(format, args);
794 printf("\n");
795 va_end(args);
796}
int rl_log(rl_log_level_t log_level, char const *const format,...)
Definition: log.c:82
@ RL_LOG_ERROR
Error.
Definition: log.h:49
@ RL_LOG_WARNING
Warning.
Definition: log.h:50
char const *const RL_CHANNEL_NAMES[RL_CHANNEL_COUNT]
RocketLogger channel names.
Definition: rl.c:95
void rl_status_print_json(rl_status_t const *const status)
Definition: rl.c:731
void rl_status_reset(rl_status_t *const status)
Definition: rl.c:535
char const *const RL_CHANNEL_FORCE_NAMES[RL_CHANNEL_SWITCHED_COUNT]
RocketLogger force range channel names.
Definition: rl.c:99
char const *const RL_CHANNEL_VALID_NAMES[RL_CHANNEL_SWITCHED_COUNT]
RocketLogger valid channel names.
Definition: rl.c:107
int rl_status_pub_init(void)
Definition: rl.c:539
int rl_status_write(rl_status_t *const status)
Definition: rl.c:641
void rl_config_print_json(rl_config_t const *const config)
Definition: rl.c:275
void rl_config_reset(rl_config_t *const config)
Definition: rl.c:360
int rl_status_read(rl_status_t *const status)
Definition: rl.c:602
int rl_status_shm_init(void)
Definition: rl.c:565
const rl_status_t rl_status_default
Definition: rl.c:79
int rl_pid_set(pid_t pid)
Definition: rl.c:521
void rl_config_print(rl_config_t const *const config)
Definition: rl.c:142
int rl_config_read_default(rl_config_t *const config)
Definition: rl.c:364
char * rl_config_get_json(rl_config_t const *const config)
Definition: rl.c:280
const rl_config_t rl_config_default
Definition: rl.c:55
char * rl_status_get_json(rl_status_t const *const status)
Definition: rl.c:736
#define RL_JSON_BUFFER_SIZE
Definition: rl.c:50
pid_t rl_pid_get(void)
Definition: rl.c:508
int rl_config_write_default(rl_config_t const *const config)
Definition: rl.c:414
int rl_config_validate(rl_config_t const *const config)
Definition: rl.c:431
void rl_status_print(rl_status_t const *const status)
Definition: rl.c:709
int rl_status_shm_deinit(void)
Definition: rl.c:579
void * zmq_status_context
The ZeroMQ context for status publishing.
Definition: rl.c:128
void * zmq_status_publisher
The ZeroMQ status publisher.
Definition: rl.c:130
void rl_config_print_cmd(rl_config_t const *const config)
Definition: rl.c:211
int rl_status_pub_deinit(void)
Definition: rl.c:554
char const *const RL_CHANNEL_DIGITAL_NAMES[RL_CHANNEL_DIGITAL_COUNT]
RocketLogger digital channel names.
Definition: rl.c:103
#define RL_SHMEM_STATUS_KEY
Key for status shared memory (used for creation)
Definition: rl.h:114
#define RL_CALIBRATION_SYSTEM_FILE
Default system wide calibration file path.
Definition: rl.h:72
#define RL_CONFIG_CHANNEL_I1H
Definition: rl.h:88
#define RL_CONFIG_CHANNEL_I2L
Definition: rl.h:89
#define RL_CONFIG_VERSION
Default system configuration file path.
Definition: rl.h:81
#define RL_CONFIG_CHANNEL_ENABLE_DEFAULT
Configuration channel enable default.
Definition: rl.h:93
@ RL_FILE_FORMAT_RLD
CSV format.
Definition: rl.h:143
@ RL_FILE_FORMAT_CSV
Definition: rl.h:142
#define RL_CONFIG_USER_FILE
User configuration file path.
Definition: rl.h:75
#define RL_CONFIG_SYSTEM_FILE
Default system configuration file path.
Definition: rl.h:78
#define RL_CONFIG_CHANNEL_I2H
Definition: rl.h:90
#define RL_ZMQ_STATUS_SOCKET
ZeroMQ socket identifier for status publishing.
Definition: rl.h:121
#define RL_PID_FILE
Process ID file for the RocketLogger process.
Definition: rl.h:53
#define RL_CONFIG_FILE_DIR_DEFAULT
Configuration data file directory default.
Definition: rl.h:103
#define RL_SAMPLE_RATE_MIN
Minimum native sample rate of the ADC in samples per second.
Definition: rl.h:62
#define RL_CONFIG_CHANNEL_V4
Definition: rl.h:86
#define ERROR
Function return value for errors (use errno to indicate the error)
Definition: rl.h:46
#define RL_CONFIG_CHANNEL_V3
Definition: rl.h:85
#define RL_SHMEM_PERMISSIONS
Permissions for shared memory.
Definition: rl.h:118
#define RL_CONFIG_COMMENT_DEFAULT
Configuration file comment default.
Definition: rl.h:111
#define RL_CONFIG_CHANNEL_FORCE_RANGE_DEFAULT
Configuration channel force range default.
Definition: rl.h:99
#define RL_CONFIG_FILE_DEFAULT
Configuration file name default.
Definition: rl.h:105
#define RL_CHANNEL_SWITCHED_COUNT
Number of RocketLogger switched channels (allowing to force range)
Definition: rl.h:58
#define SUCCESS
Function return value for successful completion.
Definition: rl.h:44
#define RL_CONFIG_FILE_SIZE_MIN
Minimum measurement split file size (5 MB to fit largest block at max rate)
Definition: rl.h:107
#define RL_CONFIG_CHANNEL_I2
Definition: rl.h:97
#define RL_CONFIG_CHANNEL_DT
Definition: rl.h:91
#define RL_CHANNEL_DIGITAL_COUNT
Number of RocketLogger digital channels.
Definition: rl.h:60
#define RL_CONFIG_CHANNEL_V2
Definition: rl.h:84
#define RL_CONFIG_CHANNEL_I1L
Definition: rl.h:87
#define RL_CONFIG_CHANNEL_I1
Configuration merged/forced channel indexes.
Definition: rl.h:96
#define RL_CHANNEL_COUNT
Number of RocketLogger analog channels.
Definition: rl.h:56
#define RL_CONFIG_FILE_SIZE_DEFAULT
Configuration file size default.
Definition: rl.h:109
#define RL_CONFIG_CHANNEL_V1
Configuration channel indexes.
Definition: rl.h:83
@ RL_AGGREGATION_MODE_AVERAGE
Aggregate using down sampling.
Definition: rl.h:130
@ RL_AGGREGATION_MODE_DOWNSAMPLE
Definition: rl.h:129
const rl_sensor_t SENSOR_REGISTRY[SENSOR_REGISTRY_SIZE]
Definition: sensor.c:63
#define SENSOR_REGISTRY_SIZE
Number of sensor registered.
Definition: sensor.h:47
Definition: rl.h:154
bool channel_force_range[RL_CHANNEL_SWITCHED_COUNT]
Current channels to force to high range.
Definition: rl.h:170
char file_name[PATH_MAX]
Data file name.
Definition: rl.h:184
bool interactive_enable
Display measurement data interactively in CLI while sampling.
Definition: rl.h:160
char const * file_comment
File comment.
Definition: rl.h:190
uint64_t file_size
Maximum data file size.
Definition: rl.h:188
rl_aggregation_mode_t aggregation_mode
Sample aggregation mode (for sampling rates below lowest native one)
Definition: rl.h:172
bool background_enable
Put the measurement process in background after successful start.
Definition: rl.h:158
bool ambient_enable
Enable logging of ambient sensor.
Definition: rl.h:180
uint64_t sample_limit
Sample limit (0 for continuous)
Definition: rl.h:162
bool web_enable
Enable web interface connection.
Definition: rl.h:176
rl_file_format_t file_format
File format.
Definition: rl.h:186
uint32_t sample_rate
Sampling rate.
Definition: rl.h:164
bool channel_enable[RL_CHANNEL_COUNT]
Channels to sample.
Definition: rl.h:168
uint8_t config_version
Configuration structure version.
Definition: rl.h:156
uint32_t update_rate
Data update rate.
Definition: rl.h:166
bool file_enable
Enable storing measurements to file.
Definition: rl.h:182
bool digital_enable
Enable digital inputs.
Definition: rl.h:174
bool calibration_ignore
Perform calibration measurement (ignore existing calibration)
Definition: rl.h:178
Definition: rl.h:201
uint64_t disk_free
Time stamp of last calibration run.
Definition: rl.h:215
rl_config_t const * config
(local) reference to current config
Definition: rl.h:225
uint64_t buffer_count
Number of buffers taken.
Definition: rl.h:209
uint16_t disk_free_permille
Time stamp of last calibration run.
Definition: rl.h:217
uint64_t calibration_time
Time stamp of last calibration run.
Definition: rl.h:211
bool error
Whether the logger is in an error state.
Definition: rl.h:205
bool sampling
Sampling state, true: sampling, false: idle.
Definition: rl.h:203
uint32_t disk_use_rate
Disk space in bytes required per minute when sampling.
Definition: rl.h:219
uint16_t sensor_count
Number of sensors found connected to the system.
Definition: rl.h:221
bool sensor_available[RL_SENSOR_COUNT_MAX]
Identifiers of sensors found.
Definition: rl.h:223
uint64_t sample_count
Number of samples taken.
Definition: rl.h:207
char calibration_file[PATH_MAX]
Time stamp of last calibration run.
Definition: rl.h:213
int64_t fs_space_free(char const *const path)
Definition: util.c:125
bool is_printable_string(char const *str)
Definition: util.c:158
int64_t fs_space_total(char const *const path)
Definition: util.c:137
int snprintfcat(char *const buffer, size_t length, char const *format,...)
Definition: util.c:191