diff --git a/backend/src/main/java/net/miarma/contaminus/common/Constants.java b/backend/src/main/java/net/miarma/contaminus/common/Constants.java index 6b85463..edbd5bc 100644 --- a/backend/src/main/java/net/miarma/contaminus/common/Constants.java +++ b/backend/src/main/java/net/miarma/contaminus/common/Constants.java @@ -10,7 +10,7 @@ public class Constants { public static final String CONTAMINUS_EB = "contaminus.eventbus"; public static Logger LOGGER = LoggerFactory.getLogger(Constants.APP_NAME); - /* API Endpoints */ + /* API Endpoints */ public static final String GROUPS = RAW_API_PREFIX + "/groups"; public static final String GROUP = RAW_API_PREFIX + "/groups/:groupId"; @@ -24,17 +24,19 @@ public class Constants { public static final String SENSOR = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId"; public static final String SENSOR_VALUES = API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId/values"; + public static final String BATCH = API_PREFIX + "/batch"; + public static final String ADD_GPS_VALUE = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId/gps_values"; + public static final String ADD_WEATHER_VALUE = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId/weather_values"; + public static final String ADD_CO_VALUE = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId/co_values"; + public static final String ACTUATORS = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/actuators"; public static final String ACTUATOR = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/actuators/:actuator_id"; public static final String ACTUATOR_STATUS = API_PREFIX + "/groups/:groupId/devices/:deviceId/actuators/:actuator_id/status"; - + public static final String VIEW_LATEST_VALUES = RAW_API_PREFIX + "/v_latest_values"; public static final String VIEW_POLLUTION_MAP = RAW_API_PREFIX + "/v_pollution_map"; public static final String VIEW_SENSOR_HISTORY = RAW_API_PREFIX + "/v_sensor_history_by_device"; public static final String VIEW_SENSOR_VALUES = RAW_API_PREFIX + "/v_sensor_values"; - public static final String VIEW_CO_BY_DEVICE = RAW_API_PREFIX + "/v_co_by_device"; - public static final String VIEW_GPS_BY_DEVICE = RAW_API_PREFIX + "/v_gps_by_device"; - public static final String VIEW_WEATHER_BY_DEVICE = RAW_API_PREFIX + "/v_weather_by_device"; private Constants() { throw new AssertionError("Utility class cannot be instantiated."); diff --git a/backend/src/main/java/net/miarma/contaminus/util/RestClientUtil.java b/backend/src/main/java/net/miarma/contaminus/util/RestClientUtil.java index 9831bd4..b386ecf 100644 --- a/backend/src/main/java/net/miarma/contaminus/util/RestClientUtil.java +++ b/backend/src/main/java/net/miarma/contaminus/util/RestClientUtil.java @@ -4,136 +4,54 @@ import java.util.Map; import com.google.gson.Gson; -import io.vertx.core.Promise; +import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.HttpRequest; import io.vertx.ext.web.client.WebClient; public class RestClientUtil { - - public WebClient client; - private Gson gson; - - public RestClientUtil(WebClient client) { - gson = new Gson(); - this.client = client; - } - /** - * Get request utility - * - * @param Type of result enveloped in JSON response - * @param port Port - * @param host Host address - * @param resource URI where resource is provided - * @param classType Type of result enveloped in JSON response - * @param promise Promise to be executed on call finish - */ - public void getRequest(Integer port, String host, String resource, Class classType, Promise promise) { - client.getAbs(host + ":" + port + resource).send(elem -> { - if (elem.succeeded()) { - promise.complete(gson.fromJson(elem.result().bodyAsString(), classType)); - } else { - promise.fail(elem.cause()); - } - }); + private final WebClient client; + private final Gson gson; - } + public RestClientUtil(WebClient client) { + this.client = client; + this.gson = new Gson(); + } - /** - * Get request utility - * - * @param Type of result enveloped in JSON response - * @param port Port - * @param host Host address - * @param resource URI where resource is provided - * @param classType Type of result enveloped in JSON response - * @param promise Promise to be executed on call finish - * @param params Map with key-value entries for call parameters - */ - public void getRequestWithParams(Integer port, String host, String resource, Class classType, - Promise promise, Map params) { - HttpRequest httpRequest = client.getAbs(host + ":" + port + "/" + resource); + public Future getRequest(int port, String host, String resource, Class classType) { + return client.getAbs(host + ":" + port + resource) + .send() + .map(response -> gson.fromJson(response.bodyAsString(), classType)); + } - params.forEach((key, value) -> { - httpRequest.addQueryParam(key, value); - }); + public Future getRequestWithParams(int port, String host, String resource, Class classType, + Map params) { + HttpRequest httpRequest = client.getAbs(host + ":" + port + "/" + resource); + params.forEach(httpRequest::addQueryParam); - httpRequest.send(elem -> { - if (elem.succeeded()) { - promise.complete(gson.fromJson(elem.result().bodyAsString(), classType)); - } else { - promise.fail(elem.cause()); - } - }); + return httpRequest.send() + .map(response -> gson.fromJson(response.bodyAsString(), classType)); + } - } + public Future postRequest(int port, String host, String resource, B body, Class classType) { + JsonObject jsonBody = new JsonObject(gson.toJson(body)); + return client.postAbs(host + ":" + port + "/" + resource) + .sendJsonObject(jsonBody) + .map(response -> gson.fromJson(response.bodyAsString(), classType)); + } - /** - * Post request utility - * - * @param Type of body enveloped in JSON request - * @param Type of result enveloped in JSON response - * @param port Port - * @param host Host address - * @param resource URI where resource is provided - * @param classType Type of result enveloped in JSON response - * @param promise Promise to be executed on call finish - */ - public void postRequest(Integer port, String host, String resource, Object body, Class classType, - Promise promise) { - JsonObject jsonBody = new JsonObject(gson.toJson(body)); - client.postAbs(host + ":" + port + "/" + resource).sendJsonObject(jsonBody, elem -> { - if (elem.succeeded()) { - Gson gson = new Gson(); - promise.complete(gson.fromJson(elem.result().bodyAsString(), classType)); - } else { - promise.fail(elem.cause()); - } - }); - } + public Future putRequest(int port, String host, String resource, B body, Class classType) { + JsonObject jsonBody = new JsonObject(gson.toJson(body)); + return client.putAbs(host + ":" + port + "/" + resource) + .sendJsonObject(jsonBody) + .map(response -> gson.fromJson(response.bodyAsString(), classType)); + } - /** - * Put request utility - * - * @param Type of body enveloped in JSON request - * @param Type of result enveloped in JSON response - * @param port Port - * @param host Host address - * @param resource URI where resource is provided - * @param classType Type of result enveloped in JSON response - * @param promise Promise to be executed on call finish - */ - public void putRequest(Integer port, String host, String resource, Object body, Class classType, - Promise promise) { - JsonObject jsonBody = new JsonObject(gson.toJson(body)); - client.putAbs(host + ":" + port + "/" + resource).sendJsonObject(jsonBody, elem -> { - if (elem.succeeded()) { - Gson gson = new Gson(); - promise.complete(gson.fromJson(elem.result().bodyAsString(), classType)); - } else { - promise.fail(elem.cause()); - } - }); - } - - /** - * Delete request utility - * - * @param port Port - * @param host Host address - * @param resource URI where resource is provided - * @param promise Promise to be executed on call finish - */ - public void deleteRequest(Integer port, String host, String resource, Promise promise) { - client.deleteAbs(host + ":" + port + "/" + resource).send(elem -> { - if (elem.succeeded()) { - promise.complete(elem.result().bodyAsString()); - } else { - promise.fail(elem.cause()); - } - }); - - } + public Future deleteRequest(int port, String host, String resource) { + return client.deleteAbs(host + ":" + port + "/" + resource) + .send() + .map(response -> response.bodyAsString()); + } } diff --git a/backend/src/main/java/net/miarma/contaminus/verticles/DataLayerAPIVerticle.java b/backend/src/main/java/net/miarma/contaminus/verticles/DataLayerAPIVerticle.java index d488fa9..a7b2091 100644 --- a/backend/src/main/java/net/miarma/contaminus/verticles/DataLayerAPIVerticle.java +++ b/backend/src/main/java/net/miarma/contaminus/verticles/DataLayerAPIVerticle.java @@ -36,13 +36,16 @@ import net.miarma.contaminus.db.DatabaseManager; import net.miarma.contaminus.db.DatabaseProvider; import net.miarma.contaminus.db.QueryBuilder; import net.miarma.contaminus.entities.Actuator; +import net.miarma.contaminus.entities.COValue; import net.miarma.contaminus.entities.Device; +import net.miarma.contaminus.entities.GpsValue; import net.miarma.contaminus.entities.Group; import net.miarma.contaminus.entities.Sensor; import net.miarma.contaminus.entities.ViewLatestValues; import net.miarma.contaminus.entities.ViewPollutionMap; import net.miarma.contaminus.entities.ViewSensorHistory; import net.miarma.contaminus.entities.ViewSensorValue; +import net.miarma.contaminus.entities.WeatherValue; /* @@ -119,6 +122,10 @@ public class DataLayerAPIVerticle extends AbstractVerticle { router.route(HttpMethod.GET, Constants.SENSOR).handler(this::getSensorById); router.route(HttpMethod.POST, Constants.SENSORS).handler(this::addSensor); router.route(HttpMethod.PUT, Constants.SENSOR).handler(this::updateSensor); + router.route(HttpMethod.POST, Constants.ADD_GPS_VALUE).handler(this::addGpsValue); + router.route(HttpMethod.POST, Constants.ADD_WEATHER_VALUE).handler(this::addWeatherValue); + router.route(HttpMethod.POST, Constants.ADD_CO_VALUE).handler(this::addCoValue); + // Actuator Routes router.route(HttpMethod.GET, Constants.ACTUATORS).handler(this::getAllActuators); @@ -185,7 +192,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle { .onSuccess(result -> { context.response() .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(SingleJsonResponse.of("Group added successfully"))); + .end(gson.toJson(result, Group.class)); }) .onFailure(err -> { context.fail(500, err); @@ -200,7 +207,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle { .onSuccess(result -> { context.response() .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(SingleJsonResponse.of("Group updated successfully"))); + .end(gson.toJson(result, Group.class)); }) .onFailure(err -> { context.fail(500, err); @@ -248,7 +255,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle { .onSuccess(result -> { context.response() .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(SingleJsonResponse.of("Device added successfully"))); + .end(gson.toJson(result, Device.class)); }) .onFailure(err -> { context.fail(500, err); @@ -263,7 +270,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle { .onSuccess(result -> { context.response() .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(SingleJsonResponse.of("Device updated successfully"))); + .end(gson.toJson(result, Device.class)); }) .onFailure(err -> { context.fail(500, err); @@ -319,7 +326,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle { .onSuccess(result -> { context.response() .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(SingleJsonResponse.of("Sensor added successfully"))); + .end(gson.toJson(result, Sensor.class)); }) .onFailure(err -> { context.fail(500, err); @@ -334,13 +341,61 @@ public class DataLayerAPIVerticle extends AbstractVerticle { .onSuccess(result -> { context.response() .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(SingleJsonResponse.of("Sensor updated successfully"))); + .end(gson.toJson(result, Sensor.class)); }) .onFailure(err -> { context.fail(500, err); }); } + private void addGpsValue(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + GpsValue gpsValue = gson.fromJson(body.toString(), GpsValue.class); + + gpsValueDAO.insert(gpsValue) + .onSuccess(result -> { + context.response() + .setStatusCode(201) + .putHeader("Content-Type", "application/json") + .end(gson.toJson(result, GpsValue.class)); + }) + .onFailure(err -> { + context.fail(500, err); + }); + } + + private void addWeatherValue(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + WeatherValue weatherValue = gson.fromJson(body.toString(), WeatherValue.class); + + weatherValueDAO.insert(weatherValue) + .onSuccess(result -> { + context.response() + .setStatusCode(201) + .putHeader("Content-Type", "application/json") + .end(gson.toJson(result, WeatherValue.class)); + }) + .onFailure(err -> { + context.fail(500, err); + }); + } + + private void addCoValue(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + COValue coValue = gson.fromJson(body.toString(), COValue.class); + + coValueDAO.insert(coValue) + .onSuccess(result -> { + context.response() + .setStatusCode(201) + .putHeader("Content-Type", "application/json") + .end(gson.toJson(result, COValue.class)); + }) + .onFailure(err -> { + context.fail(500, err); + }); + } + private void getAllActuators(RoutingContext context) { Integer groupId = Integer.parseInt(context.request().getParam("groupId")); String deviceId = context.request().getParam("deviceId"); @@ -390,7 +445,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle { .onSuccess(result -> { context.response() .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(SingleJsonResponse.of("Actuator added successfully"))); + .end(gson.toJson(result, Actuator.class)); }) .onFailure(err -> { context.fail(500, err); @@ -405,7 +460,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle { .onSuccess(result -> { context.response() .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(SingleJsonResponse.of("Actuator updated successfully"))); + .end(gson.toJson(result, Actuator.class)); }) .onFailure(err -> { context.fail(500, err); diff --git a/backend/src/main/java/net/miarma/contaminus/verticles/LogicLayerAPIVerticle.java b/backend/src/main/java/net/miarma/contaminus/verticles/LogicLayerAPIVerticle.java index f1f3215..71ce622 100644 --- a/backend/src/main/java/net/miarma/contaminus/verticles/LogicLayerAPIVerticle.java +++ b/backend/src/main/java/net/miarma/contaminus/verticles/LogicLayerAPIVerticle.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.stream.Stream; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -13,6 +12,7 @@ import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.client.WebClient; @@ -21,10 +21,13 @@ import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.CorsHandler; import net.miarma.contaminus.common.ConfigManager; import net.miarma.contaminus.common.Constants; +import net.miarma.contaminus.entities.COValue; +import net.miarma.contaminus.entities.GpsValue; import net.miarma.contaminus.entities.ViewLatestValues; import net.miarma.contaminus.entities.ViewPollutionMap; import net.miarma.contaminus.entities.ViewSensorHistory; import net.miarma.contaminus.entities.ViewSensorValue; +import net.miarma.contaminus.entities.WeatherValue; import net.miarma.contaminus.util.RestClientUtil; public class LogicLayerAPIVerticle extends AbstractVerticle { @@ -55,6 +58,7 @@ public class LogicLayerAPIVerticle extends AbstractVerticle { router.route().handler(BodyHandler.create()); + router.route(HttpMethod.POST, Constants.BATCH).handler(this::addBatch); router.route(HttpMethod.GET, Constants.LATEST_VALUES).handler(this::getDeviceLatestValues); router.route(HttpMethod.GET, Constants.POLLUTION_MAP).handler(this::getDevicePollutionMap); router.route(HttpMethod.GET, Constants.HISTORY).handler(this::getDeviceHistory); @@ -68,93 +72,100 @@ public class LogicLayerAPIVerticle extends AbstractVerticle { } private void getDeviceLatestValues(RoutingContext context) { - String deviceId = context.request().getParam("deviceId"); - - Promise resultList = Promise.promise(); - resultList.future().onComplete(complete -> { - if (complete.succeeded()) { - List aux = Stream.of(complete.result()) - .filter(elem -> deviceId.equals(elem.getDeviceId())) - .toList(); - - context.response() - .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(aux)); - } else { - context.fail(500, complete.cause()); - } - }); - - this.restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(), - Constants.VIEW_LATEST_VALUES, ViewLatestValues[].class, resultList); + String deviceId = context.request().getParam("deviceId"); + restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(), + Constants.VIEW_LATEST_VALUES, ViewLatestValues[].class) + .onSuccess(result -> { + List aux = Arrays.stream(result) + .filter(elem -> deviceId.equals(elem.getDeviceId())) + .toList(); + context.response().putHeader("content-type", "application/json; charset=utf-8").end(gson.toJson(aux)); + }) + .onFailure(err -> context.fail(500, err)); } - + private void getDevicePollutionMap(RoutingContext context) { - String deviceId = context.request().getParam("deviceId"); - - Promise resultList = Promise.promise(); - - resultList.future().onComplete(complete -> { - if (complete.succeeded()) { - List aux = Arrays.asList(complete.result()).stream() - .filter(elem -> deviceId.equals(elem.getDeviceId())) - .toList(); - - context.response() - .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(aux)); - } else { - context.fail(500, complete.cause()); - } - }); - - this.restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(), - Constants.VIEW_POLLUTION_MAP, ViewPollutionMap[].class, resultList); - } - + String deviceId = context.request().getParam("deviceId"); + restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(), + Constants.VIEW_POLLUTION_MAP, ViewPollutionMap[].class) + .onSuccess(result -> { + List aux = Arrays.asList(result).stream() + .filter(elem -> deviceId.equals(elem.getDeviceId())) + .toList(); + context.response().putHeader("content-type", "application/json; charset=utf-8").end(gson.toJson(aux)); + }) + .onFailure(err -> context.fail(500, err)); + } + private void getDeviceHistory(RoutingContext context) { - String deviceId = context.request().getParam("deviceId"); - - Promise resultList = Promise.promise(); - - resultList.future().onComplete(complete -> { - if (complete.succeeded()) { - List aux = Arrays.asList(complete.result()).stream() - .filter(elem -> deviceId.equals(elem.getDeviceId())) - .toList(); - - context.response() - .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(aux)); - } else { - context.fail(500, complete.cause()); - } - }); - - this.restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(), - Constants.VIEW_SENSOR_HISTORY, ViewSensorHistory[].class, resultList); + String deviceId = context.request().getParam("deviceId"); + restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(), + Constants.VIEW_SENSOR_HISTORY, ViewSensorHistory[].class) + .onSuccess(result -> { + List aux = Arrays.asList(result).stream() + .filter(elem -> deviceId.equals(elem.getDeviceId())) + .toList(); + context.response().putHeader("content-type", "application/json; charset=utf-8").end(gson.toJson(aux)); + }) + .onFailure(err -> context.fail(500, err)); + } + + private void getSensorValues(RoutingContext context) { + int sensorId = Integer.parseInt(context.request().getParam("sensorId")); + restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(), + Constants.VIEW_SENSOR_VALUES, ViewSensorValue[].class) + .onSuccess(result -> { + List aux = Arrays.asList(result).stream() + .filter(val -> val.getSensorId() == sensorId) + .toList(); + context.response().putHeader("content-type", "application/json; charset=utf-8").end(gson.toJson(aux)); + }) + .onFailure(err -> context.fail(500, err)); } - private void getSensorValues(RoutingContext context) { - Integer sensorId = Integer.parseInt(context.request().getParam("sensorId")); - - Promise resultList = Promise.promise(); - - resultList.future().onComplete(complete -> { - if (complete.succeeded()) { - List aux = Arrays.asList(complete.result()).stream() - .filter(val -> val.getSensorId() == sensorId) - .toList(); - + private void addBatch(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + if (body == null) { + context.response().setStatusCode(400).end("Missing JSON body"); + return; + } + + String groupId = body.getString("groupId"); + String deviceId = body.getString("deviceId"); + + JsonObject gps = body.getJsonObject("gps"); + JsonObject weather = body.getJsonObject("weather"); + JsonObject co = body.getJsonObject("co"); + + if (deviceId == null || gps == null || weather == null || co == null) { + context.response().setStatusCode(400).end("Missing required fields"); + return; + } + + GpsValue gpsValue = gson.fromJson(gps.toString(), GpsValue.class); + WeatherValue weatherValue = gson.fromJson(weather.toString(), WeatherValue.class); + COValue coValue = gson.fromJson(co.toString(), COValue.class); + + gpsValue.setDeviceId(deviceId); + weatherValue.setDeviceId(deviceId); + coValue.setDeviceId(deviceId); + + String host = "http://" + configManager.getHost(); + int port = configManager.getDataApiPort(); + + String gpsPath = Constants.ADD_GPS_VALUE.replace(":groupId", groupId).replace(":deviceId", deviceId); + String weatherPath = Constants.ADD_WEATHER_VALUE.replace(":groupId", groupId).replace(":deviceId", deviceId); + String coPath = Constants.ADD_CO_VALUE.replace(":groupId", groupId).replace(":deviceId", deviceId); + + restClient.postRequest(port, host, gpsPath, gpsValue, GpsValue.class) + .compose(_ -> restClient.postRequest(port, host, weatherPath, weatherValue, WeatherValue.class)) + .compose(_ -> restClient.postRequest(port, host, coPath, coValue, COValue.class)) + .onSuccess(_ -> { context.response() - .putHeader("content-type", "application/json; charset=utf-8") - .end(gson.toJson(aux)); - } else { - context.fail(500, complete.cause()); - } - }); - - this.restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(), - Constants.VIEW_SENSOR_VALUES, ViewSensorValue[].class, resultList); - } + .setStatusCode(201) + .putHeader("Content-Type", "application/json") + .end(new JsonObject().put("status", "success").put("inserted", 3).encode()); + }) + .onFailure(err -> context.fail(500, err)); + } } \ No newline at end of file diff --git a/hardware/include/JsonTools.hpp b/hardware/include/JsonTools.hpp index 61ff282..ad680ac 100644 --- a/hardware/include/JsonTools.hpp +++ b/hardware/include/JsonTools.hpp @@ -7,15 +7,14 @@ #include "GPS.hpp" String serializeSensorValue( - int sensorId, + int groupId, const String &deviceId, - const String &sensorType, - const String &unit, - int sensorStatus, + int gpsSensorId, + int weatherSensorId, + int coSensorId, const BME280Data_t &bme, const MQ7Data_t &mq7, - const GPSData_t &gps, - long timestamp); + const GPSData_t &gps); String serializeActuatorStatus( int actuatorId, diff --git a/hardware/include/RestClient.hpp b/hardware/include/RestClient.hpp index d3889e1..b63bb3e 100644 --- a/hardware/include/RestClient.hpp +++ b/hardware/include/RestClient.hpp @@ -2,5 +2,5 @@ #include -void getRequest(HTTPClient &httpClient, const String url, String &response); -void postRequest(HTTPClient &httpClient, const String url, const String &payload, String &response); \ No newline at end of file +void getRequest(const String url, String &response); +void postRequest(const String url, const String &payload, String &response); \ No newline at end of file diff --git a/hardware/include/main.hpp b/hardware/include/main.hpp index 44b90d6..5308fff 100644 --- a/hardware/include/main.hpp +++ b/hardware/include/main.hpp @@ -4,6 +4,8 @@ #define REST_PORT 443 #define MQTT_PORT 1883 +#define GROUP_ID 1 + #define MQ7_ID 1 #define BME280_ID 2 #define GPS_ID 3 @@ -48,4 +50,5 @@ void readBME280(); void readGPS(); void writeMatrix(const char *message); void printAllData(); +void sendSensorData(); uint32_t getChipID(); \ No newline at end of file diff --git a/hardware/src/lib/http/JsonTools.cpp b/hardware/src/lib/http/JsonTools.cpp index 20b4dcd..090ef82 100644 --- a/hardware/src/lib/http/JsonTools.cpp +++ b/hardware/src/lib/http/JsonTools.cpp @@ -1,30 +1,34 @@ #include "JsonTools.hpp" String serializeSensorValue( - int sensorId, + int groupId, const String &deviceId, - const String &sensorType, - const String &unit, - int sensorStatus, + int gpsSensorId, + int weatherSensorId, + int coSensorId, const BME280Data_t &bme, const MQ7Data_t &mq7, - const GPSData_t &gps, - long timestamp) + const GPSData_t &gps) { DynamicJsonDocument doc(1024); - doc["sensorId"] = sensorId; + doc["groupId"] = groupId; doc["deviceId"] = deviceId; - doc["sensorType"] = sensorType; - doc["unit"] = unit; - doc["sensorStatus"] = sensorStatus; - doc["temperature"] = bme.temperature; - doc["humidity"] = bme.humidity; - doc["pressure"] = bme.pressure; - doc["carbonMonoxide"] = mq7.co; - doc["lat"] = gps.lat; - doc["lon"] = gps.lon; - doc["timestamp"] = timestamp; + + JsonObject gpsObj = doc.createNestedObject("gps"); + gpsObj["sensorId"] = gpsSensorId; + gpsObj["lat"] = gps.lat; + gpsObj["lon"] = gps.lon; + + JsonObject weather = doc.createNestedObject("weather"); + weather["sensorId"] = weatherSensorId; + weather["temperature"] = bme.temperature; + weather["humidity"] = bme.humidity; + weather["pressure"] = bme.pressure; + + JsonObject co = doc.createNestedObject("co"); + co["sensorId"] = coSensorId; + co["value"] = mq7.co; String output; serializeJson(doc, output); @@ -68,6 +72,7 @@ void deserializeSensorValue(HTTPClient &http, int httpResponseCode) Serial.print("HTTP Response code: "); Serial.println(httpResponseCode); String responseJson = http.getString(); + DynamicJsonDocument doc(ESP.getMaxAllocHeap()); DeserializationError error = deserializeJson(doc, responseJson); @@ -78,26 +83,35 @@ void deserializeSensorValue(HTTPClient &http, int httpResponseCode) return; } - JsonArray array = doc.as(); - for (JsonObject sensor : array) - { - int sensorId = sensor["sensorId"]; - String deviceId = sensor["deviceId"]; - String sensorType = sensor["sensorType"]; - String unit = sensor["unit"]; - int sensorStatus = sensor["sensorStatus"]; - float temperature = sensor["temperature"]; - float humidity = sensor["humidity"]; - float carbonMonoxide = sensor["carbonMonoxide"]; - float lat = sensor["lat"]; - float lon = sensor["lon"]; - long timestamp = sensor["timestamp"]; + String groupId = doc["groupId"]; + String deviceId = doc["deviceId"]; - Serial.println("Sensor deserialized:"); - Serial.printf(" ID: %d\n Device: %s\n Type: %s\n Unit: %s\n Status: %d\n Temp: %.2f\n Hum: %.2f\n CO: %.2f\n Lat: %.6f\n Lon: %.6f\n Time: %ld\n\n", - sensorId, deviceId.c_str(), sensorType.c_str(), unit.c_str(), sensorStatus, - temperature, humidity, carbonMonoxide, lat, lon, timestamp); - } + JsonObject gps = doc["gps"]; + int gpsId = gps["sensorId"]; + float lat = gps["lat"]; + float lon = gps["lon"]; + + JsonObject weather = doc["weather"]; + int weatherId = weather["sensorId"]; + float temp = weather["temperature"]; + float hum = weather["humidity"]; + float pres = weather["pressure"]; + + JsonObject co = doc["co"]; + int coId = co["sensorId"]; + float coVal = co["value"]; + + Serial.println("🛰 GPS:"); + Serial.printf(" Sensor ID: %d\n Lat: %.6f Lon: %.6f\n", gpsId, lat, lon); + + Serial.println("🌤 Weather:"); + Serial.printf(" Sensor ID: %d\n Temp: %.2f°C Hum: %.2f%% Pressure: %.2f hPa\n", weatherId, temp, hum, pres); + + Serial.println("🧪 CO:"); + Serial.printf(" Sensor ID: %d\n CO: %.2f ppm\n", coId, coVal); + + Serial.printf("🧾 Group ID: %s\n", groupId.c_str()); + Serial.printf("🧾 Device ID: %s\n", deviceId.c_str()); } else { diff --git a/hardware/src/lib/http/RestClient.cpp b/hardware/src/lib/http/RestClient.cpp index 52c3229..e623529 100644 --- a/hardware/src/lib/http/RestClient.cpp +++ b/hardware/src/lib/http/RestClient.cpp @@ -17,7 +17,7 @@ void getRequest(const String url, String &response) httpClient.end(); } -void postRequest(const String url, String &payload, String &response) +void postRequest(const String url, const String &payload, String &response) { httpClient.begin(url); httpClient.addHeader("Content-Type", "application/json"); diff --git a/hardware/src/main.cpp b/hardware/src/main.cpp index 2f99c34..0726145 100644 --- a/hardware/src/main.cpp +++ b/hardware/src/main.cpp @@ -9,6 +9,7 @@ TaskTimer matrixTimer{0, 25}; TaskTimer globalTimer{0, 60000}; extern HTTPClient httpClient; +String response; extern MD_Parola display; MQ7Data_t mq7Data; @@ -59,6 +60,8 @@ void loop() printAllData(); #endif + sendSensorData(); + globalTimer.lastRun = now; } } @@ -136,6 +139,41 @@ void printAllData() Serial.println(mq7Data.threshold); } +void sendSensorData() +{ + const String deviceId = String(DEVICE_ID, HEX); + + // Validaciones básicas (puedes añadir más si quieres) + bool gpsValid = gpsData.lat != 0.0f && gpsData.lon != 0.0f; + bool weatherValid = bme280Data.temperature != 0.0f && + bme280Data.humidity != 0.0f && + bme280Data.pressure != 0.0f; + bool coValid = mq7Data.co >= 0.0f; + + if (!gpsValid || !weatherValid || !coValid) + { +#ifdef DEBUG + Serial.println("❌ Datos inválidos. No se envía el batch."); +#endif + return; + } + + String json = serializeSensorValue(GROUP_ID, deviceId, + GPS_ID, BME280_ID, MQ7_ID, + bme280Data, mq7Data, gpsData); + +#ifdef DEBUG + Serial.println("📤 Enviando datos al servidor..."); +#endif + + postRequest(String(SERVER_IP) + "/batch", json, response); + +#ifdef DEBUG + Serial.println("📬 Respuesta del servidor:"); + Serial.println(response); +#endif +} + uint32_t getChipID() { uint32_t chipId = 0;