RocketLogger  2.0.1
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 
95 char 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 
128 void *zmq_status_context = NULL;
130 void *zmq_status_publisher = NULL;
131 
139 static void print_config_line(char const *const description, char const *format,
140  ...);
141 
142 void 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) {
191  case RL_FILE_FORMAT_RLD:
192  print_config_line("File format", "RLD binary file");
193  break;
194 
195  case RL_FILE_FORMAT_CSV:
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 
211 void 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 
275 void 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 
280 char *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) {
333  case RL_FILE_FORMAT_RLD:
334  snprintfcat(buffer, RL_JSON_BUFFER_SIZE, "\"format\": \"rld\", ");
335  break;
336  case RL_FILE_FORMAT_CSV:
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 
360 void 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 
414 int 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 
431 int 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 
508 pid_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 
521 int 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 
535 void 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 
559  zmq_status_publisher = NULL;
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 
602 int 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(
632  RL_LOG_ERROR,
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 
641 int 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(
669  RL_LOG_ERROR,
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 
709 void 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 
731 void 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 
736 char *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 
788 static 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
const rl_config_t rl_config_default
Definition: rl.c:55
#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
char * rl_config_get_json(rl_config_t const *const config)
Definition: rl.c:280
int rl_status_shm_deinit(void)
Definition: rl.c:579
void * zmq_status_context
The ZeroMQ context for status publishing.
Definition: rl.c:128
char * rl_status_get_json(rl_status_t const *const status)
Definition: rl.c:736
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