1#include "core/aqm_db.h"
2#include "core/aqm_platform.h"
3#include "core/globals.h"
4#include "data/insert_data.h"
5#include "sensor/sensor_loader.h"
6#include "sensor/sensor_utils.h"
14static void fill_timestamp(
char *buf,
size_t len);
16void interval_collection(
void) {
18 int use_multi_sensor = 0;
19 if (active_sensor_count > 0) {
24 int enabled_module_count = 0;
28 if (use_multi_sensor) {
29 printf(
"Multi-sensor mode: %d sensor(s) configured.\n", active_sensor_count);
32 for (
int i = 0; i < active_sensor_count; i++) {
33 if (!sensor_configs[i].enabled) {
37 const char *mode = sensor_configs[i].
mode;
38 const char *plugin_path = sensor_configs[i].
plugin_path[0] ? sensor_configs[i].
plugin_path : resolve_default_plugin_path(mode);
41 printf(
"Warning: Could not resolve plugin path for sensor %s. Skipping.\n", mode);
45 if (sensor_module_load(plugin_path, &modules[enabled_module_count]) == 0) {
46 if (modules[enabled_module_count].plugin->init() == 0) {
47 printf(
"Sensor %s (%s) initialized successfully.\n",
48 modules[enabled_module_count].plugin->name, mode);
49 enabled_module_count++;
51 fprintf(stderr,
"Plugin init failed for %s; ignoring.\n", plugin_path);
52 sensor_module_unload(&modules[enabled_module_count]);
55 fprintf(stderr,
"Could not load plugin from %s. Skipping.\n", plugin_path);
59 if (enabled_module_count == 0) {
60 printf(
"No sensors could be initialized. Falling back to single sensor mode.\n");
65 if (!use_multi_sensor) {
67 const char *mode = sensor_mode[0] ? sensor_mode :
"mock";
68 const char *plugin_path_env = sensor_plugin_path[0] ? sensor_plugin_path : NULL;
70 const char *plugin_path = plugin_path_env && plugin_path_env[0] ? plugin_path_env : resolve_default_plugin_path(mode);
72 if (sensor_plugins_enabled && plugin_path) {
73 if (sensor_module_load(plugin_path, &single_module) == 0) {
74 if (single_module.plugin->
init() == 0) {
77 fprintf(stderr,
"Plugin init failed; ignoring plugin: %s\n", plugin_path);
78 sensor_module_unload(&single_module);
81 fprintf(stderr,
"Could not load plugin from %s. Continuing with builtin sensors.\n", plugin_path);
85 printf(
"Samples to collect (>=1). Interval between samples: %d s.\n", collection_interval);
87 printf(
"Sensor mode: plugin (%s)\n", single_module.plugin->
name);
89 printf(
"Sensor mode: mock\n");
91 printf(
"Samples to collect (>=1). Interval between samples: %d s.\n", collection_interval);
92 printf(
"Collecting from %d sensor(s).\n", enabled_module_count);
95 printf(
"How many samples? ");
98 if (!fgets(input,
sizeof(input), stdin) || !aqm_parse_int(input, &count) || count < 1) {
99 printf(
"Invalid count; aborting.\n");
100 if (use_multi_sensor) {
101 for (
int i = 0; i < enabled_module_count; i++) {
103 sensor_module_unload(&modules[i]);
105 }
else if (use_plugin) {
107 sensor_module_unload(&single_module);
112 if (collection_interval < 1) {
113 printf(
"collection_interval must be >= 1 (check configuration).\n");
114 if (use_multi_sensor) {
115 for (
int i = 0; i < enabled_module_count; i++) {
117 sensor_module_unload(&modules[i]);
119 }
else if (use_plugin) {
121 sensor_module_unload(&single_module);
127 if (aqm_db_open(&db) != 0) {
128 fprintf(stderr,
"Unable to open database for data collection.\n");
129 if (use_multi_sensor) {
130 for (
int i = 0; i < enabled_module_count; i++) {
132 sensor_module_unload(&modules[i]);
134 }
else if (use_plugin) {
136 sensor_module_unload(&single_module);
141 if (sqlite3_exec(db,
"BEGIN TRANSACTION;", NULL, NULL, NULL) != SQLITE_OK) {
142 fprintf(stderr,
"Failed to start database transaction: %s\n", sqlite3_errmsg(db));
145 for (
int i = 0; i < count; i++) {
146 if (use_multi_sensor) {
148 for (
int j = 0; j < enabled_module_count; j++) {
150 if (modules[j].plugin->read_sample(&data) != 0) {
151 fprintf(stderr,
"Plugin read failed (%s); using mock sample.\n", modules[j].plugin->name);
152 mock_generate_air_quality(&data);
153 }
else if (!data.
model[0]) {
154 snprintf(data.
model,
sizeof(data.
model),
"%s", modules[j].plugin->name);
156 insert_data_sqlite(db, &data);
162 if (single_module.plugin->
read_sample(&data) != 0) {
163 fprintf(stderr,
"Plugin read failed (%s); using mock sample.\n", single_module.plugin->
name);
164 mock_generate_air_quality(&data);
165 }
else if (!data.
model[0]) {
166 snprintf(data.
model,
sizeof(data.
model),
"%s", single_module.plugin->
name);
169 mock_generate_air_quality(&data);
171 insert_data_sqlite(db, &data);
175 aqm_sleep_seconds((
unsigned)collection_interval);
178 if (sqlite3_exec(db,
"COMMIT;", NULL, NULL, NULL) != SQLITE_OK) {
179 fprintf(stderr,
"Failed to commit database transaction: %s\n", sqlite3_errmsg(db));
180 sqlite3_exec(db,
"ROLLBACK;", NULL, NULL, NULL);
185 if (use_multi_sensor) {
186 printf(
"Data collection finished (%d sample(s) from %d sensor(s)).\n", count, enabled_module_count);
187 for (
int i = 0; i < enabled_module_count; i++) {
189 sensor_module_unload(&modules[i]);
192 printf(
"Data collection finished (%d sample(s)).\n", count);
195 sensor_module_unload(&single_module);
202 snprintf(data->
model,
sizeof(data->
model),
"mock");
205 data->pm25 = (float)((rand() % 1000) / 10.0);
206 data->pm10 = (float)((rand() % 1000) / 10.0);
207 data->co = (float)((rand() % 1000) / 10.0);
208 data->no2 = (float)((rand() % 1000) / 1000.0);
209 data->o3 = (float)((rand() % 1000) / 1000.0);
210 data->so2 = (float)((rand() % 1000) / 1000.0);
213static void fill_timestamp(
char *buf,
size_t len) {
214 time_t t = time(NULL);
215 const struct tm *ptm = localtime(&t);
217 snprintf(buf, len,
"1970-01-01 00:00:00");
220 if (strftime(buf, len,
"%Y-%m-%d %H:%M:%S", ptm) == 0) {
221 snprintf(buf, len,
"1970-01-01 00:00:00");
One air quality reading: pollutant values plus sensor/timestamp metadata.
char timestamp[32]
Reading timestamp, formatted "%Y-%m-%d %H:%M:%S".
char model[64]
Sensor model name, e.g.
int sensor_id
Numeric sensor identifier, matches SensorPlugin::sensor_id.
char mode[32]
Sensor mode identifier, e.g.
char plugin_path[256]
Path to the plugin library; empty string means use the default resolved by resolve_default_plugin_pat...
A dynamically loaded sensor plugin and its OS-level handle.
int(* read_sample)(AirQualityData *out)
Reads one sample into *out.
int(* shutdown)(void)
Called once before unloading, for cleanup.
const char * name
Human-readable sensor name, e.g.
int(* init)(void)
Called once after loading.