1
0

added some improved classes from my custom backend and introduced DAOs

This commit is contained in:
Jose
2025-05-09 20:48:45 +02:00
parent b6f13cfff8
commit 02a2a2ce07
53 changed files with 4462 additions and 3239 deletions

View File

@@ -18,6 +18,13 @@
<version>4.5.13</version>
</dependency>
<!-- Vert.X MariaDB/MySQL Client -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-mysql-client</artifactId>
<version>4.5.13</version>
</dependency>
<!-- Vert.X Web -->
<dependency>
<groupId>io.vertx</groupId>
@@ -46,13 +53,6 @@
<version>4.5.13</version>
</dependency>
<!-- JDBC Driver -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.5.2</version>
</dependency>
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
@@ -60,11 +60,17 @@
<version>2.12.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.quarkus/quarkus-agroal -->
<!-- SLF4J + Logback -->
<dependency>
<groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager</artifactId>
<version>3.1.1.Final</version> <!-- O la versión más reciente -->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.13</version>
</dependency>
</dependencies>

View File

@@ -0,0 +1,113 @@
package net.miarma.contaminus.common;
import java.lang.reflect.Field;
import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Row;
public abstract class AbstractEntity {
public AbstractEntity() {}
public AbstractEntity(Row row) {
populateFromRow(row);
}
private void populateFromRow(Row row) {
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
Class<?> type = field.getType();
String name = field.getName();
Object value;
if (type.isEnum()) {
Integer intValue = row.getInteger(name);
if (intValue != null) {
try {
var method = type.getMethod("fromInt", int.class);
value = method.invoke(null, intValue);
} catch (Exception e) {
value = null;
}
} else {
value = null;
}
} else {
value = switch (type.getSimpleName()) {
case "Integer" -> row.getInteger(name);
case "String" -> row.getString(name);
case "Double" -> row.getDouble(name);
case "Long" -> row.getLong(name);
case "Boolean" -> row.getBoolean(name);
case "int" -> row.getInteger(name);
case "double" -> row.getDouble(name);
case "long" -> row.getLong(name);
case "boolean" -> row.getBoolean(name);
case "LocalDateTime" -> row.getLocalDateTime(name);
case "BigDecimal" -> {
try {
var numeric = row.get(io.vertx.sqlclient.data.Numeric.class, row.getColumnIndex(name));
yield numeric != null ? numeric.bigDecimalValue() : null;
} catch (Exception e) {
yield null;
}
}
default -> {
System.err.println("Type not supported yet: " + type.getName() + " for field " + name);
yield null;
}
};
}
field.set(this, value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public String encode() {
JsonObject json = new JsonObject();
Class<?> clazz = this.getClass();
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
try {
Object value = field.get(this);
if (value instanceof ValuableEnum ve) {
json.put(field.getName(), ve.getValue());
} else {
json.put(field.getName(), value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
clazz = clazz.getSuperclass();
}
return json.encode();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getSimpleName()).append(" [ ");
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
sb.append(field.getName()).append("= ").append(field.get(this)).append(", ");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
sb.append("]");
return sb.toString();
}
}

View File

@@ -1,55 +1,40 @@
package net.miarma.contaminus.common;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Constants {
public static final String APP_NAME = "ContaminUS";
public static final String API_PREFIX = "/api/v1";
public static final String RAW_API_PREFIX = "/api/raw/v1";
public static final String CONTAMINUS_EB = "contaminus.eventbus";
public static Logger LOGGER = LoggerFactory.getLogger(Constants.APP_NAME);
/* API Endpoints */
public static final String POST_PAYLOAD = RAW_API_PREFIX + "/device-payload";
public static final String GROUPS = RAW_API_PREFIX + "/groups";
public static final String GROUP = RAW_API_PREFIX + "/groups/:groupId";
public static final String GET_GROUPS = RAW_API_PREFIX + "/groups";
public static final String POST_GROUPS = RAW_API_PREFIX + "/groups";
public static final String PUT_GROUP_BY_ID = RAW_API_PREFIX + "/groups/:groupId";
public static final String DEVICES = RAW_API_PREFIX + "/groups/:groupId/devices";
public static final String DEVICE = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId";
public static final String LATEST_VALUES = API_PREFIX + "/groups/:groupId/devices/:deviceId/latest-values";
public static final String POLLUTION_MAP = API_PREFIX + "/groups/:groupId/devices/:deviceId/pollution-map";
public static final String HISTORY = API_PREFIX + "/groups/:groupId/devices/:deviceId/history";
public static final String GET_DEVICES = RAW_API_PREFIX + "/devices";
public static final String POST_DEVICES = RAW_API_PREFIX + "/devices";
public static final String PUT_DEVICE_BY_ID = RAW_API_PREFIX + "/devices/:deviceId";
public static final String SENSORS = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors";
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 GET_SENSORS = RAW_API_PREFIX + "/sensors";
public static final String POST_SENSORS = RAW_API_PREFIX + "/sensors";
public static final String PUT_SENSOR_BY_ID = RAW_API_PREFIX + "/sensors/:sensorId";
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 GET_ACTUATORS = RAW_API_PREFIX + "/actuators";
public static final String POST_ACTUATORS = RAW_API_PREFIX + "/actuators";
public static final String PUT_ACTUATOR_BY_ID = RAW_API_PREFIX + "/actuators/:actuatorId";
public static final String GET_CO_BY_DEVICE_VIEW = RAW_API_PREFIX + "/v_co_by_device";
public static final String GET_GPS_BY_DEVICE_VIEW = RAW_API_PREFIX + "/v_gps_by_device";
public static final String GET_LATEST_VALUES_VIEW = RAW_API_PREFIX + "/v_latest_values";
public static final String GET_POLLUTION_MAP_VIEW = RAW_API_PREFIX + "/v_pollution_map";
public static final String GET_SENSOR_HISTORY_BY_DEVICE_VIEW = RAW_API_PREFIX + "/v_sensor_history_by_device";
public static final String GET_SENSOR_VALUES_VIEW = RAW_API_PREFIX + "/v_sensor_values";
public static final String GET_WEATHER_BY_DEVICE_VIEW = RAW_API_PREFIX + "/v_weather_by_device";
/* Bussiness Logic API */
public static final String GET_GROUP_BY_ID = API_PREFIX + "/groups/:groupId";
public static final String GET_GROUP_DEVICES = API_PREFIX + "/groups/:groupId/devices";
public static final String GET_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId";
public static final String GET_DEVICE_SENSORS = API_PREFIX + "/devices/:deviceId/sensors";
public static final String GET_DEVICE_ACTUATORS = API_PREFIX + "/devices/:deviceId/actuators";
public static final String GET_DEVICE_LATEST_VALUES = API_PREFIX + "/devices/:deviceId/latest";
public static final String GET_DEVICE_POLLUTION_MAP = API_PREFIX + "/devices/:deviceId/pollution-map";
public static final String GET_DEVICE_HISTORY = API_PREFIX + "/devices/:deviceId/history";
public static final String GET_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId";
public static final String GET_SENSOR_VALUES = API_PREFIX + "/sensors/:sensorId/values";
public static final String GET_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
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.");

View File

@@ -0,0 +1,5 @@
package net.miarma.contaminus.common;
public interface ValuableEnum {
int getValue();
}

View File

@@ -0,0 +1,129 @@
package net.miarma.contaminus.dao;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.Actuator;
public class ActuatorDAO implements DataAccessObject<Actuator, Integer>{
private final DatabaseManager db;
public ActuatorDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<Actuator>> getAll() {
Promise<List<Actuator>> promise = Promise.promise();
String query = QueryBuilder.select(Actuator.class).build();
db.execute(query, Actuator.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
public Future<List<Actuator>> getAllByDeviceId(String deviceId) {
Promise<List<Actuator>> promise = Promise.promise();
Actuator actuator = new Actuator();
actuator.setDeviceId(deviceId);
String query = QueryBuilder
.select(Actuator.class)
.where(actuator)
.build();
db.execute(query, Actuator.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<Actuator> getById(Integer id) {
Promise<Actuator> promise = Promise.promise();
Actuator actuator = new Actuator();
actuator.setActuatorId(id);
String query = QueryBuilder
.select(Actuator.class)
.where(actuator)
.build();
db.execute(query, Actuator.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
public Future<Actuator> getByIdAndDeviceId(Integer actuatorId, String deviceId) {
Promise<Actuator> promise = Promise.promise();
Actuator actuator = new Actuator();
actuator.setDeviceId(deviceId);
actuator.setActuatorId(actuatorId);
String query = QueryBuilder
.select(Actuator.class)
.where(actuator)
.build();
db.execute(query, Actuator.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Actuator> insert(Actuator t) {
Promise<Actuator> promise = Promise.promise();
String query = QueryBuilder.insert(t).build();
db.execute(query, Actuator.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Actuator> update(Actuator t) {
Promise<Actuator> promise = Promise.promise();
String query = QueryBuilder.update(t).build();
db.execute(query, Actuator.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Actuator> delete(Integer id) {
Promise<Actuator> promise = Promise.promise();
Actuator actuator = new Actuator();
actuator.setActuatorId(id);
String query = QueryBuilder.delete(actuator).build();
db.executeOne(query, Actuator.class,
_ -> promise.complete(actuator),
promise::fail
);
return promise.future();
}
}

View File

@@ -0,0 +1,82 @@
package net.miarma.contaminus.dao;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.COValue;
public class COValueDAO implements DataAccessObject<COValue, Integer> {
private final DatabaseManager db;
public COValueDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<COValue>> getAll() {
Promise<List<COValue>> promise = Promise.promise();
String query = QueryBuilder.select(COValue.class).build();
db.execute(query, COValue.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<COValue> getById(Integer id) {
Promise<COValue> promise = Promise.promise();
COValue coValue = new COValue();
coValue.setValueId(id);
String query = QueryBuilder
.select(COValue.class)
.where(coValue)
.build();
db.execute(query, COValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<COValue> insert(COValue t) {
Promise<COValue> promise = Promise.promise();
String query = QueryBuilder.insert(t).build();
db.execute(query, COValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<COValue> update(COValue t) {
Promise<COValue> promise = Promise.promise();
String query = QueryBuilder.update(t).build();
db.execute(query, COValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<COValue> delete(Integer id) {
throw new UnsupportedOperationException("Cannot delete samples");
}
}

View File

@@ -0,0 +1,128 @@
package net.miarma.contaminus.dao;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.Device;
public class DeviceDAO implements DataAccessObject<Device, String> {
private final DatabaseManager db;
public DeviceDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<Device>> getAll() {
Promise<List<Device>> promise = Promise.promise();
String query = QueryBuilder.select(Device.class).build();
db.execute(query, Device.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
public Future<List<Device>> getAllByGroupId(Integer groupId) {
Promise<List<Device>> promise = Promise.promise();
Device device = new Device();
device.setGroupId(groupId);
String query = QueryBuilder
.select(Device.class)
.where(device)
.build();
db.execute(query, Device.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<Device> getById(String id) {
Promise<Device> promise = Promise.promise();
Device device = new Device();
device.setDeviceId(id);
String query = QueryBuilder
.select(Device.class)
.where(device)
.build();
db.execute(query, Device.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
public Future<Device> getByIdAndGroupId(String id, Integer groupId) {
Promise<Device> promise = Promise.promise();
Device device = new Device();
device.setDeviceId(id);
device.setGroupId(groupId);
String query = QueryBuilder
.select(Device.class)
.where(device)
.build();
db.execute(query, Device.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Device> insert(Device t) {
Promise<Device> promise = Promise.promise();
String query = QueryBuilder.insert(t).build();
db.execute(query, Device.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Device> update(Device t) {
Promise<Device> promise = Promise.promise();
String query = QueryBuilder.update(t).build();
db.execute(query, Device.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Device> delete(String id) {
Promise<Device> promise = Promise.promise();
Device device = new Device();
device.setDeviceId(id);
String query = QueryBuilder.delete(device).build();
db.execute(query, Device.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
}

View File

@@ -0,0 +1,81 @@
package net.miarma.contaminus.dao;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.GpsValue;
public class GpsValueDAO implements DataAccessObject<GpsValue, Integer> {
private final DatabaseManager db;
public GpsValueDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<GpsValue>> getAll() {
Promise<List<GpsValue>> promise = Promise.promise();
String query = QueryBuilder.select(GpsValue.class).build();
db.execute(query, GpsValue.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<GpsValue> getById(Integer id) {
Promise<GpsValue> promise = Promise.promise();
GpsValue gpsValue = new GpsValue();
gpsValue.setValueId(id);
String query = QueryBuilder
.select(GpsValue.class)
.where(gpsValue)
.build();
db.execute(query, GpsValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<GpsValue> insert(GpsValue t) {
Promise<GpsValue> promise = Promise.promise();
String query = QueryBuilder.insert(t).build();
db.execute(query, GpsValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<GpsValue> update(GpsValue t) {
Promise<GpsValue> promise = Promise.promise();
String query = QueryBuilder.update(t).build();
db.execute(query, GpsValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<GpsValue> delete(Integer id) {
throw new UnsupportedOperationException("Cannot delete samples");
}
}

View File

@@ -0,0 +1,93 @@
package net.miarma.contaminus.dao;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.Group;
public class GroupDAO implements DataAccessObject<Group, Integer> {
private final DatabaseManager db;
public GroupDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<Group>> getAll() {
Promise<List<Group>> promise = Promise.promise();
String query = QueryBuilder.select(Group.class).build();
db.execute(query, Group.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<Group> getById(Integer id) {
Promise<Group> promise = Promise.promise();
Group group = new Group();
group.setGroupId(id);
String query = QueryBuilder
.select(Group.class)
.where(group)
.build();
db.execute(query, Group.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Group> insert(Group t) {
Promise<Group> promise = Promise.promise();
String query = QueryBuilder.insert(t).build();
db.execute(query, Group.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Group> update(Group t) {
Promise<Group> promise = Promise.promise();
String query = QueryBuilder.update(t).build();
db.execute(query, Group.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Group> delete(Integer id) {
Promise<Group> promise = Promise.promise();
Group group = new Group();
group.setGroupId(id);
String query = QueryBuilder.delete(group).build();
db.execute(query, Group.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
}

View File

@@ -0,0 +1,130 @@
package net.miarma.contaminus.dao;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.Sensor;
public class SensorDAO implements DataAccessObject<Sensor, Integer> {
private final DatabaseManager db;
public SensorDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<Sensor>> getAll() {
Promise<List<Sensor>> promise = Promise.promise();
String query = QueryBuilder.select(Sensor.class).build();
db.execute(query, Sensor.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
public Future<List<Sensor>> getAllByDeviceId(String deviceId) {
Promise<List<Sensor>> promise = Promise.promise();
Sensor sensor = new Sensor();
sensor.setDeviceId(deviceId);
String query = QueryBuilder
.select(Sensor.class)
.where(sensor)
.build();
db.execute(query, Sensor.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<Sensor> getById(Integer id) {
Promise<Sensor> promise = Promise.promise();
Sensor sensor = new Sensor();
sensor.setSensorId(id);
String query = QueryBuilder
.select(Sensor.class)
.where(sensor)
.build();
db.execute(query, Sensor.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
public Future<Sensor> getByIdAndDeviceId(Integer sensorId, String deviceId) {
Promise<Sensor> promise = Promise.promise();
Sensor sensor = new Sensor();
sensor.setDeviceId(deviceId);
sensor.setSensorId(sensorId);
String query = QueryBuilder
.select(Sensor.class)
.where(sensor)
.build();
db.execute(query, Sensor.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Sensor> insert(Sensor t) {
Promise<Sensor> promise = Promise.promise();
String query = QueryBuilder.insert(t).build();
db.execute(query, Sensor.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Sensor> update(Sensor t) {
Promise<Sensor> promise = Promise.promise();
String query = QueryBuilder.update(t).build();
db.execute(query, Sensor.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<Sensor> delete(Integer id) {
Promise<Sensor> promise = Promise.promise();
Sensor sensor = new Sensor();
sensor.setSensorId(id);
String query = QueryBuilder.delete(sensor).build();
db.execute(query, Sensor.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
}

View File

@@ -0,0 +1,81 @@
package net.miarma.contaminus.dao;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.WeatherValue;
public class WeatherValueDAO implements DataAccessObject<WeatherValue, Integer> {
private final DatabaseManager db;
public WeatherValueDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<WeatherValue>> getAll() {
Promise<List<WeatherValue>> promise = Promise.promise();
String query = QueryBuilder.select(WeatherValue.class).build();
db.execute(query, WeatherValue.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<WeatherValue> getById(Integer id) {
Promise<WeatherValue> promise = Promise.promise();
WeatherValue weatherValue = new WeatherValue();
weatherValue.setValueId(id);
String query = QueryBuilder
.select(WeatherValue.class)
.where(weatherValue)
.build();
db.execute(query, WeatherValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<WeatherValue> insert(WeatherValue t) {
Promise<WeatherValue> promise = Promise.promise();
String query = QueryBuilder.insert(t).build();
db.execute(query, WeatherValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<WeatherValue> update(WeatherValue t) {
Promise<WeatherValue> promise = Promise.promise();
String query = QueryBuilder.update(t).build();
db.execute(query, WeatherValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<WeatherValue> delete(Integer id) {
throw new UnsupportedOperationException("Cannot delete samples");
}
}

View File

@@ -0,0 +1,66 @@
package net.miarma.contaminus.dao.views;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.ViewLatestValues;
public class ViewLatestValuesDAO implements DataAccessObject<ViewLatestValues, String> {
private final DatabaseManager db;
public ViewLatestValuesDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<ViewLatestValues>> getAll() {
Promise<List<ViewLatestValues>> promise = Promise.promise();
String query = QueryBuilder.select(ViewLatestValues.class).build();
db.execute(query, ViewLatestValues.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<ViewLatestValues> getById(String id) {
Promise<ViewLatestValues> promise = Promise.promise();
ViewLatestValues view = new ViewLatestValues();
view.setDeviceId(id);
String query = QueryBuilder
.select(ViewLatestValues.class)
.where(view)
.build();
db.execute(query, ViewLatestValues.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<ViewLatestValues> insert(ViewLatestValues t) {
throw new UnsupportedOperationException("Insert not supported for views");
}
@Override
public Future<ViewLatestValues> update(ViewLatestValues t) {
throw new UnsupportedOperationException("Update not supported for views");
}
@Override
public Future<ViewLatestValues> delete(String id) {
throw new UnsupportedOperationException("Delete not supported for views");
}
}

View File

@@ -0,0 +1,66 @@
package net.miarma.contaminus.dao.views;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.common.Table;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.ViewPollutionMap;
@Table("v_pollution_map")
public class ViewPollutionMapDAO implements DataAccessObject<ViewPollutionMap, String> {
private final DatabaseManager db;
public ViewPollutionMapDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<ViewPollutionMap>> getAll() {
Promise<List<ViewPollutionMap>> promise = Promise.promise();
String query = QueryBuilder.select(ViewPollutionMap.class).build();
db.execute(query, ViewPollutionMap.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<ViewPollutionMap> getById(String id) {
Promise<ViewPollutionMap> promise = Promise.promise();
ViewPollutionMap view = new ViewPollutionMap();
view.setDeviceId(id);
String query = QueryBuilder
.select(ViewPollutionMap.class)
.where(view)
.build();
db.execute(query, ViewPollutionMap.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<ViewPollutionMap> insert(ViewPollutionMap t) {
throw new UnsupportedOperationException("Insert not supported for views");
}
@Override
public Future<ViewPollutionMap> update(ViewPollutionMap t) {
throw new UnsupportedOperationException("Update not supported for views");
}
@Override
public Future<ViewPollutionMap> delete(String id) {
throw new UnsupportedOperationException("Delete not supported for views");
}
}

View File

@@ -0,0 +1,67 @@
package net.miarma.contaminus.dao.views;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.ViewSensorHistory;
public class ViewSensorHistoryDAO implements DataAccessObject<ViewSensorHistory, String> {
private final DatabaseManager db;
public ViewSensorHistoryDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<ViewSensorHistory>> getAll() {
Promise<List<ViewSensorHistory>> promise = Promise.promise();
String query = QueryBuilder.select(ViewSensorHistory.class).build();
db.execute(query, ViewSensorHistory.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<ViewSensorHistory> getById(String id) {
Promise<ViewSensorHistory> promise = Promise.promise();
ViewSensorHistory viewSensorHistory = new ViewSensorHistory();
viewSensorHistory.setDeviceId(id);
String query = QueryBuilder
.select(ViewSensorHistory.class)
.where(viewSensorHistory)
.build();
db.execute(query, ViewSensorHistory.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<ViewSensorHistory> insert(ViewSensorHistory t) {
throw new UnsupportedOperationException("Insert not supported for views");
}
@Override
public Future<ViewSensorHistory> update(ViewSensorHistory t) {
throw new UnsupportedOperationException("Update not supported for views");
}
@Override
public Future<ViewSensorHistory> delete(String id) {
throw new UnsupportedOperationException("Delete not supported for views");
}
}

View File

@@ -0,0 +1,66 @@
package net.miarma.contaminus.dao.views;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.db.DataAccessObject;
import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.ViewSensorValue;
public class ViewSensorValueDAO implements DataAccessObject<ViewSensorValue, Integer> {
private final DatabaseManager db;
public ViewSensorValueDAO(Pool pool) {
this.db = DatabaseManager.getInstance(pool);
}
@Override
public Future<List<ViewSensorValue>> getAll() {
Promise<List<ViewSensorValue>> promise = Promise.promise();
String query = QueryBuilder.select(ViewSensorValue.class).build();
db.execute(query, ViewSensorValue.class,
list -> promise.complete(list.isEmpty() ? List.of() : list),
promise::fail
);
return promise.future();
}
@Override
public Future<ViewSensorValue> getById(Integer id) {
Promise<ViewSensorValue> promise = Promise.promise();
ViewSensorValue view = new ViewSensorValue();
view.setSensorId(id);
String query = QueryBuilder
.select(ViewSensorValue.class)
.where(view)
.build();
db.execute(query, ViewSensorValue.class,
list -> promise.complete(list.isEmpty() ? null : list.get(0)),
promise::fail
);
return promise.future();
}
@Override
public Future<ViewSensorValue> insert(ViewSensorValue t) {
throw new UnsupportedOperationException("Insert not supported for views");
}
@Override
public Future<ViewSensorValue> update(ViewSensorValue t) {
throw new UnsupportedOperationException("Update not supported for views");
}
@Override
public Future<ViewSensorValue> delete(Integer id) {
throw new UnsupportedOperationException("Delete not supported for views");
}
}

View File

@@ -1,58 +0,0 @@
package net.miarma.contaminus.database;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.jdbcclient.JDBCPool;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import net.miarma.contaminus.common.Constants;
public class DatabaseManager {
private static DatabaseManager instance;
private final JDBCPool pool;
private DatabaseManager(JDBCPool pool) {
this.pool = pool;
}
public static synchronized DatabaseManager getInstance(JDBCPool pool) {
if (instance == null) {
instance = new DatabaseManager(pool);
}
return instance;
}
public Future<RowSet<Row>> testConnection() {
return pool.query("SELECT 1").execute();
}
public <T> Future<List<T>> execute(String query, Class<T> clazz,
Handler<List<T>> onSuccess, Handler<Throwable> onFailure) {
return pool.query(query).execute()
.map(rows -> {
List<T> results = new ArrayList<>();
for (Row row : rows) {
try {
Constructor<T> constructor = clazz.getConstructor(Row.class);
results.add(constructor.newInstance(row));
} catch (NoSuchMethodException | InstantiationException |
IllegalAccessException | InvocationTargetException e) {
Constants.LOGGER.error("Error instantiating class: " + e.getMessage());
}
}
return results;
})
.onComplete(ar -> {
if (ar.succeeded()) {
onSuccess.handle(ar.result());
} else {
onFailure.handle(ar.cause());
}
});
}
}

View File

@@ -1,196 +0,0 @@
package net.miarma.contaminus.database;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.Table;
public class QueryBuilder {
private StringBuilder query;
private String sort;
private String order;
private String limit;
public QueryBuilder() {
this.query = new StringBuilder();
}
private static <T> String getTableName(Class<T> clazz) {
if (clazz == null) {
throw new IllegalArgumentException("Class cannot be null");
}
if (clazz.isAnnotationPresent(Table.class)) {
Table annotation = clazz.getAnnotation(Table.class);
return annotation.value();
}
throw new IllegalArgumentException("Class does not have @Table annotation");
}
public String getQuery() {
return query.toString();
}
public static <T> QueryBuilder select(Class<T> clazz, String... columns) {
if (clazz == null) {
throw new IllegalArgumentException("Class cannot be null");
}
QueryBuilder qb = new QueryBuilder();
String tableName = getTableName(clazz);
qb.query.append("SELECT ");
if (columns.length == 0) {
qb.query.append("* ");
} else {
StringJoiner joiner = new StringJoiner(", ");
for (String column : columns) {
if (column != null) {
joiner.add(column);
}
}
qb.query.append(joiner).append(" ");
}
qb.query.append("FROM ").append(tableName).append(" ");
return qb;
}
public static <T> QueryBuilder where(QueryBuilder qb, T object) {
if (qb == null || object == null) {
throw new IllegalArgumentException("QueryBuilder and object cannot be null");
}
List<String> conditions = new ArrayList<>();
Class<?> clazz = object.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
try {
Object value = field.get(object);
if (value != null) {
if (value instanceof String) {
conditions.add(field.getName() + " = '" + value + "'");
} else {
conditions.add(field.getName() + " = " + value);
}
}
} catch (IllegalAccessException e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
if (!conditions.isEmpty()) {
qb.query.append("WHERE ").append(String.join(" AND ", conditions)).append(" ");
}
return qb;
}
public static <T> QueryBuilder select(T object, String... columns) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
Class<?> clazz = object.getClass();
QueryBuilder qb = select(clazz, columns);
return where(qb, object);
}
public static <T> QueryBuilder insert(T object) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass());
qb.query.append("INSERT INTO ").append(table).append(" ");
qb.query.append("(");
StringJoiner columns = new StringJoiner(", ");
StringJoiner values = new StringJoiner(", ");
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
columns.add(field.getName());
Object fieldValue = field.get(object);
if (fieldValue != null) {
if (fieldValue instanceof String) {
values.add("'" + fieldValue + "'");
} else {
values.add(fieldValue.toString());
}
} else {
values.add("NULL");
}
} catch (IllegalArgumentException | IllegalAccessException e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
qb.query.append(columns).append(") ");
qb.query.append("VALUES (").append(values).append(") ");
return qb;
}
public static <T> QueryBuilder update(T object) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass());
qb.query.append("UPDATE ").append(table).append(" ");
qb.query.append("SET ");
StringJoiner joiner = new StringJoiner(", ");
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
Object fieldValue = field.get(object);
if (fieldValue != null) {
if (fieldValue instanceof String) {
joiner.add(field.getName() + " = '" + fieldValue + "'");
} else {
joiner.add(field.getName() + " = " + fieldValue.toString());
}
} else {
joiner.add(field.getName() + " = NULL");
}
} catch (IllegalArgumentException | IllegalAccessException e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
qb.query.append(joiner).append(" ");
return qb;
}
public QueryBuilder orderBy(Optional<String> column, Optional<String> order) {
column.ifPresent(c -> {
sort = "ORDER BY " + c + " ";
order.ifPresent(o -> {
sort += o.equalsIgnoreCase("asc") ? "ASC" : "DESC" + " ";
});
});
return this;
}
public QueryBuilder limit(Optional<Integer> limitParam) {
limitParam.ifPresent(param -> limit = "LIMIT " + param + " ");
return this;
}
public String build() {
if (order != null && !order.isEmpty()) {
query.append(order);
}
if (sort != null && !sort.isEmpty()) {
query.append(sort);
}
if (limit != null && !limit.isEmpty()) {
query.append(limit);
}
return query.toString().trim() + ";";
}
}

View File

@@ -1,67 +0,0 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
import net.miarma.contaminus.util.DateParser;
@Table("v_co_by_device")
public class DeviceCO {
private String deviceId;
private Float carbonMonoxide;
private Long timestamp;
public DeviceCO() {}
public DeviceCO(Row row) {
this.deviceId = row.getString("deviceId");
this.carbonMonoxide = row.getFloat("carbonMonoxide");
this.timestamp = DateParser.parseDate(row.getLocalDateTime("timestamp"));
}
public DeviceCO(String deviceId, Float carbonMonoxide, Long timestamp) {
super();
this.deviceId = deviceId;
this.carbonMonoxide = carbonMonoxide;
this.timestamp = timestamp;
}
public String getDeviceId() {
return deviceId;
}
public Float getCarbonMonoxide() {
return carbonMonoxide;
}
public Long getTimestamp() {
return timestamp;
}
@Override
public int hashCode() {
return Objects.hash(carbonMonoxide, deviceId, timestamp);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DeviceCO other = (DeviceCO) obj;
return Objects.equals(carbonMonoxide, other.carbonMonoxide) && Objects.equals(deviceId, other.deviceId)
&& Objects.equals(timestamp, other.timestamp);
}
@Override
public String toString() {
return "DeviceCO [deviceId=" + deviceId + ", carbonMonoxide=" + carbonMonoxide + ", timestamp=" + timestamp
+ "]";
}
}

View File

@@ -1,72 +0,0 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
import net.miarma.contaminus.util.DateParser;
@Table("v_gps_by_device")
public class DeviceGPS {
private String deviceId;
private Float lat;
private Float lon;
private Long timestamp;
public DeviceGPS() {}
public DeviceGPS(Row row) {
this.deviceId = row.getString("deviceId");
this.lat = row.getFloat("lat");
this.lon = row.getFloat("lon");
this.timestamp = DateParser.parseDate(row.getLocalDateTime("timestamp"));
}
public DeviceGPS(String deviceId, Float lat, Float lon) {
super();
this.deviceId = deviceId;
this.lat = lat;
this.lon = lon;
}
public String getDeviceId() {
return deviceId;
}
public Float getLat() {
return lat;
}
public Float getLon() {
return lon;
}
public Long getTimestamp() {
return timestamp;
}
@Override
public int hashCode() {
return Objects.hash(deviceId, lat, lon, timestamp);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DeviceGPS other = (DeviceGPS) obj;
return Objects.equals(deviceId, other.deviceId) && Objects.equals(lat, other.lat)
&& Objects.equals(lon, other.lon) && Objects.equals(timestamp, other.timestamp);
}
@Override
public String toString() {
return "DeviceGPS [deviceId=" + deviceId + ", lat=" + lat + ", lon=" + lon + ", timestamp=" + timestamp + "]";
}
}

View File

@@ -1,74 +0,0 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
import net.miarma.contaminus.util.DateParser;
@Table("v_weather_by_device")
public class DeviceWeather {
private String deviceId;
private Float temperature;
private Float humidity;
private Long timestamp;
public DeviceWeather() {}
public DeviceWeather(Row row) {
this.deviceId = row.getString("deviceId");
this.temperature = row.getFloat("temperature");
this.humidity = row.getFloat("humidity");
this.timestamp = DateParser.parseDate(row.getLocalDateTime("timestamp"));
}
public DeviceWeather(String deviceId, Float temperature, Float humidity, Long timestamp) {
super();
this.deviceId = deviceId;
this.temperature = temperature;
this.humidity = humidity;
this.timestamp = timestamp;
}
public String getDeviceId() {
return deviceId;
}
public Float getTemperature() {
return temperature;
}
public Float getHumidity() {
return humidity;
}
public Long getTimestamp() {
return timestamp;
}
@Override
public int hashCode() {
return Objects.hash(deviceId, humidity, temperature, timestamp);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DeviceWeather other = (DeviceWeather) obj;
return Objects.equals(deviceId, other.deviceId) && Objects.equals(humidity, other.humidity)
&& Objects.equals(temperature, other.temperature) && Objects.equals(timestamp, other.timestamp);
}
@Override
public String toString() {
return "DeviceWeather [deviceId=" + deviceId + ", temperature=" + temperature + ", humidity=" + humidity
+ ", timestamp=" + timestamp + "]";
}
}

View File

@@ -0,0 +1,13 @@
package net.miarma.contaminus.db;
import java.util.List;
import io.vertx.core.Future;
public interface DataAccessObject<T, ID> {
Future<List<T>> getAll();
Future<T> getById(ID id);
Future<T> insert(T t);
Future<T> update(T t);
Future<T> delete(ID id);
}

View File

@@ -0,0 +1,82 @@
package net.miarma.contaminus.db;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import net.miarma.contaminus.common.Constants;
public class DatabaseManager {
private static DatabaseManager instance;
private final Pool pool;
private DatabaseManager(Pool pool) {
this.pool = pool;
}
public static synchronized DatabaseManager getInstance(Pool pool) {
if (instance == null) {
instance = new DatabaseManager(pool);
}
return instance;
}
public Pool getPool() {
return pool;
}
public Future<RowSet<Row>> testConnection() {
return pool.query("SELECT 1").execute();
}
public <T> Future<List<T>> execute(String query, Class<T> clazz, Handler<List<T>> onSuccess,
Handler<Throwable> onFailure) {
return pool.query(query).execute().map(rows -> {
List<T> results = new ArrayList<>();
for (Row row : rows) {
try {
Constructor<T> constructor = clazz.getConstructor(Row.class);
results.add(constructor.newInstance(row));
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException
| InvocationTargetException e) {
Constants.LOGGER.error("Error instantiating class: " + e.getMessage());
}
}
return results;
}).onComplete(ar -> {
if (ar.succeeded()) {
onSuccess.handle(ar.result());
} else {
onFailure.handle(ar.cause());
}
});
}
public <T> Future<T> executeOne(String query, Class<T> clazz, Handler<T> onSuccess, Handler<Throwable> onFailure) {
return pool.query(query).execute().map(rows -> {
for (Row row : rows) {
try {
Constructor<T> constructor = clazz.getConstructor(Row.class);
return constructor.newInstance(row);
} catch (Exception e) {
Constants.LOGGER.error("Error instantiating class: " + e.getMessage());
}
}
return null; // Si no hay filas
}).onComplete(ar -> {
if (ar.succeeded()) {
onSuccess.handle(ar.result());
} else {
onFailure.handle(ar.cause());
}
});
}
}

View File

@@ -0,0 +1,21 @@
package net.miarma.contaminus.db;
import io.vertx.core.Vertx;
import io.vertx.mysqlclient.MySQLConnectOptions;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions;
import net.miarma.contaminus.common.ConfigManager;
public class DatabaseProvider {
public static Pool createPool(Vertx vertx, ConfigManager config) {
MySQLConnectOptions connectOptions = new MySQLConnectOptions()
.setPort(config.getIntProperty("db.port"))
.setHost(config.getStringProperty("db.host"))
.setDatabase(config.getStringProperty("db.name"))
.setUser(config.getStringProperty("db.user"))
.setPassword(config.getStringProperty("db.password"));
PoolOptions poolOptions = new PoolOptions().setMaxSize(10);
return Pool.pool(vertx, connectOptions, poolOptions);
}
}

View File

@@ -0,0 +1,357 @@
package net.miarma.contaminus.db;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.Table;
public class QueryBuilder {
private final StringBuilder query;
private String sort;
private String order;
private String limit;
private Class<?> entityClass;
public QueryBuilder() {
this.query = new StringBuilder();
}
private static <T> String getTableName(Class<T> clazz) {
if (clazz == null) {
throw new IllegalArgumentException("Class cannot be null");
}
if (clazz.isAnnotationPresent(Table.class)) {
Table annotation = clazz.getAnnotation(Table.class);
return annotation.value();
}
throw new IllegalArgumentException("Class does not have @Table annotation");
}
public String getQuery() {
return query.toString();
}
private static Object extractValue(Object fieldValue) {
if (fieldValue instanceof Enum<?>) {
try {
var method = fieldValue.getClass().getMethod("getValue");
return method.invoke(fieldValue);
} catch (Exception e) {
return ((Enum<?>) fieldValue).name();
}
}
return fieldValue;
}
public static <T> QueryBuilder select(Class<T> clazz, String... columns) {
if (clazz == null) {
throw new IllegalArgumentException("Class cannot be null");
}
QueryBuilder qb = new QueryBuilder();
qb.entityClass = clazz;
String tableName = getTableName(clazz);
qb.query.append("SELECT ");
if (columns.length == 0) {
qb.query.append("* ");
} else {
StringJoiner joiner = new StringJoiner(", ");
for (String column : columns) {
if (column != null) {
joiner.add(column);
}
}
qb.query.append(joiner).append(" ");
}
qb.query.append("FROM ").append(tableName).append(" ");
return qb;
}
public QueryBuilder where(Map<String, String> filters) {
if (filters == null || filters.isEmpty()) {
return this;
}
Set<String> validFields = entityClass != null
? Arrays.stream(entityClass.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet())
: Collections.emptySet();
List<String> conditions = new ArrayList<>();
for (Map.Entry<String, String> entry : filters.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (!validFields.contains(key)) {
Constants.LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: " + key);
continue;
}
if (value.startsWith("(") && value.endsWith(")")) {
conditions.add(key + " IN " + value);
} else if (value.matches("-?\\d+(\\.\\d+)?")) {
conditions.add(key + " = " + value);
} else {
conditions.add(key + " = '" + value + "'");
}
}
if (!conditions.isEmpty()) {
query.append("WHERE ").append(String.join(" AND ", conditions)).append(" ");
}
return this;
}
public <T> QueryBuilder where(T object) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
Set<String> validFields = entityClass != null
? Arrays.stream(entityClass.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet())
: Collections.emptySet();
List<String> conditions = new ArrayList<>();
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
Object fieldValue = field.get(object);
if (fieldValue != null) {
String key = field.getName();
if (!validFields.contains(key)) {
Constants.LOGGER.warn("[QueryBuilder] Ignorando campo invalido en WHERE: " + key);
continue;
}
Object value = extractValue(fieldValue);
if (value instanceof String || value instanceof LocalDateTime) {
conditions.add(key + " = '" + value + "'");
} else {
conditions.add(key + " = " + value);
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
if (!conditions.isEmpty()) {
query.append("WHERE ").append(String.join(" AND ", conditions)).append(" ");
}
return this;
}
public static <T> QueryBuilder insert(T object) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass());
qb.query.append("INSERT INTO ").append(table).append(" ");
qb.query.append("(");
StringJoiner columns = new StringJoiner(", ");
StringJoiner values = new StringJoiner(", ");
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
columns.add(field.getName());
Object fieldValue = field.get(object);
if (fieldValue != null) {
Object value = extractValue(fieldValue);
if (value instanceof String || value instanceof LocalDateTime) {
values.add("'" + value + "'");
} else {
values.add(value.toString());
}
} else {
values.add("NULL");
}
} catch (IllegalArgumentException | IllegalAccessException e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
qb.query.append(columns).append(") ");
qb.query.append("VALUES (").append(values).append(") RETURNING * ");
return qb;
}
public static <T> QueryBuilder update(T object) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass());
qb.query.append("UPDATE ").append(table).append(" SET ");
StringJoiner setJoiner = new StringJoiner(", ");
StringJoiner whereJoiner = new StringJoiner(" AND ");
Field idField = null;
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
Object fieldValue = field.get(object);
if (fieldValue == null) continue;
String fieldName = field.getName();
Object value = extractValue(fieldValue);
if (fieldName.endsWith("_id")) {
idField = field;
whereJoiner.add(fieldName + " = " + (value instanceof String
|| value instanceof LocalDateTime ? "'" + value + "'" : value));
continue;
}
setJoiner.add(fieldName + " = " + (value instanceof String
|| value instanceof LocalDateTime ? "'" + value + "'" : value));
} catch (Exception e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
if (idField == null) {
throw new IllegalArgumentException("No ID field (ending with _id) found for WHERE clause");
}
qb.query.append(setJoiner).append(" WHERE ").append(whereJoiner);
return qb;
}
public static <T> QueryBuilder updateWithNulls(T object) {
if (object == null) {
throw new IllegalArgumentException("Object cannot be null");
}
QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass());
qb.query.append("UPDATE ").append(table).append(" SET ");
StringJoiner setJoiner = new StringJoiner(", ");
StringJoiner whereJoiner = new StringJoiner(" AND ");
Field idField = null;
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
String fieldName = field.getName();
Object fieldValue = field.get(object);
if (fieldName.endsWith("_id")) {
idField = field;
Object value = extractValue(fieldValue);
whereJoiner.add(fieldName + " = " + (value instanceof String || value instanceof LocalDateTime ? "'" + value + "'" : value));
continue;
}
if (fieldValue == null) {
setJoiner.add(fieldName + " = NULL"); // ✅ esto lo borra en la BD
} else {
Object value = extractValue(fieldValue);
setJoiner.add(fieldName + " = " + (value instanceof String || value instanceof LocalDateTime ? "'" + value + "'" : value));
}
} catch (Exception e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
if (idField == null) {
throw new IllegalArgumentException("No ID field (ending with _id) found for WHERE clause");
}
qb.query.append(setJoiner).append(" WHERE ").append(whereJoiner);
return qb;
}
public static <T> QueryBuilder delete(T object) {
if (object == null) throw new IllegalArgumentException("Object cannot be null");
QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass());
qb.query.append("DELETE FROM ").append(table).append(" WHERE ");
StringJoiner joiner = new StringJoiner(" AND ");
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
Object fieldValue = field.get(object);
if (fieldValue != null) {
Object value = extractValue(fieldValue);
joiner.add(field.getName() + " = " + (value instanceof String
|| value instanceof LocalDateTime ? "'" + value + "'" : value.toString()));
}
} catch (Exception e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
qb.query.append(joiner).append(" ");
return qb;
}
public QueryBuilder orderBy(Optional<String> column, Optional<String> order) {
column.ifPresent(c -> {
if (entityClass != null) {
boolean isValid = Arrays.stream(entityClass.getDeclaredFields())
.map(Field::getName)
.anyMatch(f -> f.equals(c));
if (!isValid) {
Constants.LOGGER.warn("[QueryBuilder] Ignorando campo invalido en ORDER BY: " + c);
return;
}
}
sort = "ORDER BY " + c + " ";
order.ifPresent(o -> {
sort += o.equalsIgnoreCase("asc") ? "ASC" : "DESC" + " ";
});
});
return this;
}
public QueryBuilder limit(Optional<Integer> limitParam) {
limitParam.ifPresent(param -> limit = "LIMIT " + param + " ");
return this;
}
public QueryBuilder offset(Optional<Integer> offsetParam) {
offsetParam.ifPresent(param -> limit += "OFFSET " + param + " ");
return this;
}
public String build() {
if (order != null && !order.isEmpty()) {
query.append(order);
}
if (sort != null && !sort.isEmpty()) {
query.append(sort);
}
if (limit != null && !limit.isEmpty()) {
query.append(limit);
}
return query.toString().trim() + ";";
}
}

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
@@ -6,7 +6,8 @@ import net.miarma.contaminus.common.Table;
import net.miarma.contaminus.util.DateParser;
@Table("v_latest_values")
public class DeviceLatestValuesView {
public class ViewLatestValues {
private String deviceId;
private Integer sensorId;
private String sensorType;
@@ -15,14 +16,15 @@ public class DeviceLatestValuesView {
private Long sensorTimestamp;
private Float temperature;
private Float humidity;
private Float pressure;
private Float carbonMonoxide;
private Float lat;
private Float lon;
private Long airValuesTimestamp;
public DeviceLatestValuesView() {}
public ViewLatestValues() {}
public DeviceLatestValuesView(Row row) {
public ViewLatestValues(Row row) {
this.deviceId = row.getString("deviceId");
this.sensorId = row.getInteger("sensorId");
this.sensorType = row.getString("sensorType");
@@ -31,14 +33,15 @@ public class DeviceLatestValuesView {
this.sensorTimestamp = DateParser.parseDate(row.getLocalDateTime("sensorTimestamp"));
this.temperature = row.getFloat("temperature");
this.humidity = row.getFloat("humidity");
this.pressure = row.getFloat("pressure");
this.carbonMonoxide = row.getFloat("carbonMonoxide");
this.lat = row.getFloat("lat");
this.lon = row.getFloat("lon");
this.airValuesTimestamp = DateParser.parseDate(row.getLocalDateTime("airValuesTimestamp"));
}
public DeviceLatestValuesView(String deviceId, Integer sensorId, String sensorType, String unit, Integer sensorStatus,
Long sensorTimestamp, Float temperature, Float humidity, Float carbonMonoxide, Float lat, Float lon,
public ViewLatestValues(String deviceId, Integer sensorId, String sensorType, String unit, Integer sensorStatus,
Long sensorTimestamp, Float temperature, Float humidity, Float pressure, Float carbonMonoxide, Float lat, Float lon,
Long airValuesTimestamp) {
super();
this.deviceId = deviceId;
@@ -87,6 +90,10 @@ public class DeviceLatestValuesView {
return humidity;
}
public Float getPressure() {
return pressure;
}
public Float getCarbonMonoxide() {
return carbonMonoxide;
}
@@ -103,10 +110,62 @@ public class DeviceLatestValuesView {
return airValuesTimestamp;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public void setSensorId(Integer sensorId) {
this.sensorId = sensorId;
}
public void setSensorType(String sensorType) {
this.sensorType = sensorType;
}
public void setUnit(String unit) {
this.unit = unit;
}
public void setSensorStatus(Integer sensorStatus) {
this.sensorStatus = sensorStatus;
}
public void setSensorTimestamp(Long sensorTimestamp) {
this.sensorTimestamp = sensorTimestamp;
}
public void setTemperature(Float temperature) {
this.temperature = temperature;
}
public void setHumidity(Float humidity) {
this.humidity = humidity;
}
public void setPressure(Float pressure) {
this.pressure = pressure;
}
public void setCarbonMonoxide(Float carbonMonoxide) {
this.carbonMonoxide = carbonMonoxide;
}
public void setLat(Float lat) {
this.lat = lat;
}
public void setLon(Float lon) {
this.lon = lon;
}
public void setAirValuesTimestamp(Long airValuesTimestamp) {
this.airValuesTimestamp = airValuesTimestamp;
}
@Override
public int hashCode() {
return Objects.hash(airValuesTimestamp, carbonMonoxide, deviceId, humidity, lat, lon, sensorId, sensorStatus,
sensorTimestamp, sensorType, temperature, unit);
return Objects.hash(airValuesTimestamp, carbonMonoxide, deviceId, humidity, lat, lon, pressure, sensorId,
sensorStatus, sensorTimestamp, sensorType, temperature, unit);
}
@Override
@@ -117,12 +176,12 @@ public class DeviceLatestValuesView {
return false;
if (getClass() != obj.getClass())
return false;
DeviceLatestValuesView other = (DeviceLatestValuesView) obj;
ViewLatestValues other = (ViewLatestValues) obj;
return Objects.equals(airValuesTimestamp, other.airValuesTimestamp)
&& Objects.equals(carbonMonoxide, other.carbonMonoxide) && Objects.equals(deviceId, other.deviceId)
&& Objects.equals(humidity, other.humidity) && Objects.equals(lat, other.lat)
&& Objects.equals(lon, other.lon) && Objects.equals(sensorId, other.sensorId)
&& Objects.equals(sensorStatus, other.sensorStatus)
&& Objects.equals(lon, other.lon) && Objects.equals(pressure, other.pressure)
&& Objects.equals(sensorId, other.sensorId) && Objects.equals(sensorStatus, other.sensorStatus)
&& Objects.equals(sensorTimestamp, other.sensorTimestamp)
&& Objects.equals(sensorType, other.sensorType) && Objects.equals(temperature, other.temperature)
&& Objects.equals(unit, other.unit);
@@ -130,13 +189,13 @@ public class DeviceLatestValuesView {
@Override
public String toString() {
return "DeviceLatestValuesView [deviceId=" + deviceId + ", sensorId=" + sensorId + ", sensorType=" + sensorType
return "ViewLatestValues [deviceId=" + deviceId + ", sensorId=" + sensorId + ", sensorType=" + sensorType
+ ", unit=" + unit + ", sensorStatus=" + sensorStatus + ", sensorTimestamp=" + sensorTimestamp
+ ", temperature=" + temperature + ", humidity=" + humidity + ", carbonMonoxide=" + carbonMonoxide
+ ", lat=" + lat + ", lon=" + lon + ", airValuesTimestamp=" + airValuesTimestamp + "]";
+ ", temperature=" + temperature + ", humidity=" + humidity + ", pressure=" + pressure
+ ", carbonMonoxide=" + carbonMonoxide + ", lat=" + lat + ", lon=" + lon + ", airValuesTimestamp="
+ airValuesTimestamp + "]";
}
}

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;
@@ -7,7 +7,7 @@ import net.miarma.contaminus.common.Table;
import net.miarma.contaminus.util.DateParser;
@Table("v_pollution_map")
public class DevicePollutionMap {
public class ViewPollutionMap {
private String deviceId;
private String deviceName;
private Float lat;
@@ -15,9 +15,9 @@ public class DevicePollutionMap {
private Float carbonMonoxide;
private Long timestamp;
public DevicePollutionMap() {}
public ViewPollutionMap() {}
public DevicePollutionMap(Row row) {
public ViewPollutionMap(Row row) {
this.deviceId = row.getString("deviceId");
this.deviceName = row.getString("deviceName");
this.lat = row.getFloat("lat");
@@ -26,7 +26,7 @@ public class DevicePollutionMap {
this.timestamp = DateParser.parseDate(row.getLocalDateTime("timestamp"));
}
public DevicePollutionMap(String deviceId, String deviceName, Float lat, Float lon, Float carbonMonoxide,
public ViewPollutionMap(String deviceId, String deviceName, Float lat, Float lon, Float carbonMonoxide,
Long timestamp) {
super();
this.deviceId = deviceId;
@@ -61,6 +61,32 @@ public class DevicePollutionMap {
return timestamp;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public void setLat(Float lat) {
this.lat = lat;
}
public void setLon(Float lon) {
this.lon = lon;
}
public void setCarbonMonoxide(Float carbonMonoxide) {
this.carbonMonoxide = carbonMonoxide;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
@Override
public int hashCode() {
return Objects.hash(carbonMonoxide, deviceId, deviceName, lat, lon, timestamp);
@@ -74,7 +100,7 @@ public class DevicePollutionMap {
return false;
if (getClass() != obj.getClass())
return false;
DevicePollutionMap other = (DevicePollutionMap) obj;
ViewPollutionMap other = (ViewPollutionMap) obj;
return Objects.equals(carbonMonoxide, other.carbonMonoxide) && Objects.equals(deviceId, other.deviceId)
&& Objects.equals(deviceName, other.deviceName) && Objects.equals(lat, other.lat)
&& Objects.equals(lon, other.lon) && Objects.equals(timestamp, other.timestamp);

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;
@@ -7,16 +7,16 @@ import net.miarma.contaminus.common.Table;
import net.miarma.contaminus.util.DateParser;
@Table("v_sensor_history_by_device")
public class DeviceSensorHistory {
public class ViewSensorHistory {
private String deviceId;
private String deviceName;
private Float value;
private String valueType;
private Long timestamp;
public DeviceSensorHistory() {}
public ViewSensorHistory() {}
public DeviceSensorHistory(Row row) {
public ViewSensorHistory(Row row) {
this.deviceId = row.getString("deviceId");
this.deviceName = row.getString("deviceName");
this.value = row.getFloat("value");
@@ -24,7 +24,7 @@ public class DeviceSensorHistory {
this.timestamp = DateParser.parseDate(row.getLocalDateTime("timestamp"));
}
public DeviceSensorHistory(String deviceId, String deviceName, Float value, String valueType, Long timestamp) {
public ViewSensorHistory(String deviceId, String deviceName, Float value, String valueType, Long timestamp) {
super();
this.deviceId = deviceId;
this.deviceName = deviceName;
@@ -53,6 +53,28 @@ public class DeviceSensorHistory {
return timestamp;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public void setValue(Float value) {
this.value = value;
}
public void setValueType(String valueType) {
this.valueType = valueType;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
@Override
public int hashCode() {
return Objects.hash(deviceId, deviceName, timestamp, value, valueType);
@@ -66,7 +88,7 @@ public class DeviceSensorHistory {
return false;
if (getClass() != obj.getClass())
return false;
DeviceSensorHistory other = (DeviceSensorHistory) obj;
ViewSensorHistory other = (ViewSensorHistory) obj;
return Objects.equals(deviceId, other.deviceId) && Objects.equals(deviceName, other.deviceName)
&& Objects.equals(timestamp, other.timestamp) && Objects.equals(value, other.value)
&& Objects.equals(valueType, other.valueType);

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;
@@ -7,7 +7,7 @@ import net.miarma.contaminus.common.Table;
import net.miarma.contaminus.util.DateParser;
@Table("v_sensor_values")
public class DeviceSensorValue {
public class ViewSensorValue {
private Integer sensorId;
private String deviceId;
private String sensorType;
@@ -15,14 +15,15 @@ public class DeviceSensorValue {
private Integer sensorStatus;
private Float temperature;
private Float humidity;
private Float pressure;
private Float carbonMonoxide;
private Float lat;
private Float lon;
private Long timestamp;
public DeviceSensorValue() {}
public ViewSensorValue() {}
public DeviceSensorValue(Row row) {
public ViewSensorValue(Row row) {
this.sensorId = row.getInteger("sensorId");
this.deviceId = row.getString("deviceId");
this.sensorType = row.getString("sensorType");
@@ -30,14 +31,15 @@ public class DeviceSensorValue {
this.sensorStatus = row.getInteger("sensorStatus");
this.temperature = row.getFloat("temperature");
this.humidity = row.getFloat("humidity");
this.pressure = row.getFloat("pressure");
this.carbonMonoxide = row.getFloat("carbonMonoxide");
this.lat = row.getFloat("lat");
this.lon = row.getFloat("lon");
this.timestamp = DateParser.parseDate(row.getLocalDateTime("timestamp"));
}
public DeviceSensorValue(Integer sensorId, String deviceId, String sensorType, String unit, Integer sensorStatus,
Float temperature, Float humidity, Float carbonMonoxide, Float lat, Float lon, Long timestamp) {
public ViewSensorValue(Integer sensorId, String deviceId, String sensorType, String unit, Integer sensorStatus,
Float temperature, Float humidity, Float pressure, Float carbonMonoxide, Float lat, Float lon, Long timestamp) {
super();
this.sensorId = sensorId;
this.deviceId = deviceId;
@@ -46,6 +48,7 @@ public class DeviceSensorValue {
this.sensorStatus = sensorStatus;
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
this.carbonMonoxide = carbonMonoxide;
this.lat = lat;
this.lon = lon;
@@ -80,6 +83,10 @@ public class DeviceSensorValue {
return humidity;
}
public Float getPressure() {
return pressure;
}
public Float getCarbonMonoxide() {
return carbonMonoxide;
}
@@ -96,9 +103,59 @@ public class DeviceSensorValue {
return timestamp;
}
public void setSensorId(Integer sensorId) {
this.sensorId = sensorId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public void setSensorType(String sensorType) {
this.sensorType = sensorType;
}
public void setUnit(String unit) {
this.unit = unit;
}
public void setSensorStatus(Integer sensorStatus) {
this.sensorStatus = sensorStatus;
}
public void setTemperature(Float temperature) {
this.temperature = temperature;
}
public void setHumidity(Float humidity) {
this.humidity = humidity;
}
public void setPressure(Float pressure) {
this.pressure = pressure;
}
public void setCarbonMonoxide(Float carbonMonoxide) {
this.carbonMonoxide = carbonMonoxide;
}
public void setLat(Float lat) {
this.lat = lat;
}
public void setLon(Float lon) {
this.lon = lon;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
@Override
public int hashCode() {
return Objects.hash(carbonMonoxide, deviceId, humidity, lat, lon, sensorId, sensorStatus, sensorType,
return Objects.hash(carbonMonoxide, deviceId, humidity, lat, lon, pressure, sensorId, sensorStatus, sensorType,
temperature, timestamp, unit);
}
@@ -110,22 +167,23 @@ public class DeviceSensorValue {
return false;
if (getClass() != obj.getClass())
return false;
DeviceSensorValue other = (DeviceSensorValue) obj;
ViewSensorValue other = (ViewSensorValue) obj;
return Objects.equals(carbonMonoxide, other.carbonMonoxide) && Objects.equals(deviceId, other.deviceId)
&& Objects.equals(humidity, other.humidity) && Objects.equals(lat, other.lat)
&& Objects.equals(lon, other.lon) && Objects.equals(sensorId, other.sensorId)
&& Objects.equals(sensorStatus, other.sensorStatus) && Objects.equals(sensorType, other.sensorType)
&& Objects.equals(temperature, other.temperature) && Objects.equals(timestamp, other.timestamp)
&& Objects.equals(unit, other.unit);
&& Objects.equals(lon, other.lon) && Objects.equals(pressure, other.pressure)
&& Objects.equals(sensorId, other.sensorId) && Objects.equals(sensorStatus, other.sensorStatus)
&& Objects.equals(sensorType, other.sensorType) && Objects.equals(temperature, other.temperature)
&& Objects.equals(timestamp, other.timestamp) && Objects.equals(unit, other.unit);
}
@Override
public String toString() {
return "DeviceSensorValue [sensorId=" + sensorId + ", deviceId=" + deviceId + ", sensorType=" + sensorType
return "ViewSensorValue [sensorId=" + sensorId + ", deviceId=" + deviceId + ", sensorType=" + sensorType
+ ", unit=" + unit + ", sensorStatus=" + sensorStatus + ", temperature=" + temperature + ", humidity="
+ humidity + ", carbonMonoxide=" + carbonMonoxide + ", lat=" + lat + ", lon=" + lon + ", timestamp="
+ timestamp + "]";
+ humidity + ", pressure=" + pressure + ", carbonMonoxide=" + carbonMonoxide + ", lat=" + lat + ", lon="
+ lon + ", timestamp=" + timestamp + "]";
}
}

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.database.entities;
package net.miarma.contaminus.entities;
import java.util.Objects;

View File

@@ -1,529 +0,0 @@
package net.miarma.contaminus.server;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
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.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.jdbcclient.JDBCPool;
import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.SingleJsonResponse;
import net.miarma.contaminus.database.DatabaseManager;
import net.miarma.contaminus.database.QueryBuilder;
import net.miarma.contaminus.database.entities.Actuator;
import net.miarma.contaminus.database.entities.COValue;
import net.miarma.contaminus.database.entities.Device;
import net.miarma.contaminus.database.entities.DeviceLatestValuesView;
import net.miarma.contaminus.database.entities.DevicePayload;
import net.miarma.contaminus.database.entities.DevicePollutionMap;
import net.miarma.contaminus.database.entities.DeviceSensorHistory;
import net.miarma.contaminus.database.entities.DeviceSensorValue;
import net.miarma.contaminus.database.entities.GpsValue;
import net.miarma.contaminus.database.entities.Group;
import net.miarma.contaminus.database.entities.Sensor;
import net.miarma.contaminus.database.entities.WeatherValue;
/*
* This class is a Verticle that will handle the Data Layer API.
*/
@SuppressWarnings("unused")
public class DataLayerAPIVerticle extends AbstractVerticle {
private JDBCPool pool;
private DatabaseManager dbManager;
private ConfigManager configManager;
private final Gson gson = new GsonBuilder().serializeNulls().create();
@SuppressWarnings("deprecation")
public DataLayerAPIVerticle() {
this.configManager = ConfigManager.getInstance();
String jdbcUrl = configManager.getJdbcUrl();
String dbUser = configManager.getStringProperty("db.user");
String dbPwd = configManager.getStringProperty("db.pwd");
Integer poolSize = configManager.getIntProperty("db.poolSize");
JsonObject dbConfig = new JsonObject()
.put("url", jdbcUrl)
.put("user", dbUser)
.put("password", dbPwd)
.put("max_pool_size", poolSize != null ? poolSize : 10);
this.pool = JDBCPool.pool(Vertx.vertx(), dbConfig);
}
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("📡 Iniciando DataLayerAPIVerticle...");
dbManager = DatabaseManager.getInstance(pool);
Router router = Router.router(vertx);
Set<HttpMethod> allowedMethods = new HashSet<>(
Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.OPTIONS)); // Por ejemplo
Set<String> allowedHeaders = new HashSet<>(Arrays.asList("Content-Type", "Authorization"));
router.route().handler(CorsHandler.create()
.allowCredentials(true)
.allowedHeaders(allowedHeaders)
.allowedMethods(allowedMethods));
router.route().handler(BodyHandler.create());
// Payload
router.route(HttpMethod.POST, Constants.POST_PAYLOAD).handler(this::addDevicePayload);
// Group Routes
router.route(HttpMethod.GET, Constants.GET_GROUPS).handler(this::getAllGroups);
router.route(HttpMethod.GET, Constants.GET_GROUP_BY_ID).handler(this::getGroupById);
router.route(HttpMethod.POST, Constants.POST_GROUPS).handler(this::addGroup);
router.route(HttpMethod.PUT, Constants.PUT_GROUP_BY_ID).handler(this::updateGroup);
// Device Routes
router.route(HttpMethod.GET, Constants.GET_DEVICES).handler(this::getAllDevices);
router.route(HttpMethod.GET, Constants.GET_DEVICE_BY_ID).handler(this::getDeviceById);
router.route(HttpMethod.POST, Constants.POST_DEVICES).handler(this::addDevice);
router.route(HttpMethod.PUT, Constants.PUT_DEVICE_BY_ID).handler(this::updateDevice);
// Sensor Routes
router.route(HttpMethod.GET, Constants.GET_SENSORS).handler(this::getAllSensors);
router.route(HttpMethod.GET, Constants.GET_SENSOR_BY_ID).handler(this::getSensorById);
router.route(HttpMethod.POST, Constants.POST_SENSORS).handler(this::addSensor);
router.route(HttpMethod.PUT, Constants.PUT_SENSOR_BY_ID).handler(this::updateSensor);
// Actuator Routes
router.route(HttpMethod.GET, Constants.GET_ACTUATORS).handler(this::getAllActuators);
router.route(HttpMethod.GET, Constants.GET_ACTUATOR_BY_ID).handler(this::getActuatorById);
router.route(HttpMethod.POST, Constants.POST_ACTUATORS).handler(this::addActuator);
router.route(HttpMethod.PUT, Constants.PUT_ACTUATOR_BY_ID).handler(this::updateActuator);
// Views Routes
router.route(HttpMethod.GET, Constants.GET_LATEST_VALUES_VIEW).handler(this::getLatestValuesView);
router.route(HttpMethod.GET, Constants.GET_POLLUTION_MAP_VIEW).handler(this::getDevicePollutionMapView);
router.route(HttpMethod.GET, Constants.GET_SENSOR_VALUES_VIEW).handler(this::getSensorValuesView);
router.route(HttpMethod.GET, Constants.GET_SENSOR_HISTORY_BY_DEVICE_VIEW).handler(this::getSensorHistoryByDeviceView);
vertx.createHttpServer()
.requestHandler(router)
.listen(configManager.getDataApiPort(), configManager.getHost());
pool.query("SELECT 1").execute(ar -> {
if (ar.succeeded()) {
Constants.LOGGER.info("🟢 Connected to DB");
startPromise.complete();
} else {
Constants.LOGGER.error("🔴 Failed to connect to DB: " + ar.cause());
startPromise.fail(ar.cause());
}
});
}
private void addDevicePayload(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
DevicePayload devicePayload = gson.fromJson(body.toString(), DevicePayload.class);
COValue coValue = COValue.fromPayload(devicePayload);
GpsValue gpsValue = GpsValue.fromPayload(devicePayload);
WeatherValue weatherValue = WeatherValue.fromPayload(devicePayload);
String coQuery = QueryBuilder
.insert(coValue)
.build();
String gpsQuery = QueryBuilder
.insert(gpsValue)
.build();
String weatherQuery = QueryBuilder
.insert(weatherValue)
.build();
dbManager.execute(coQuery, COValue.class,
onSuccess -> {
dbManager.execute(gpsQuery, GpsValue.class,
onSuccess2 -> {
dbManager.execute(weatherQuery, WeatherValue.class,
onSuccess3 -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Payload added successfully")));
},
onFailure3 -> {
context.fail(500, onFailure3);
});
},
onFailure2 -> {
context.fail(500, onFailure2);
});
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getAllGroups(RoutingContext context) {
String query = QueryBuilder
.select(Group.class)
.build();
dbManager.execute(query, Group.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getGroupById(RoutingContext context) {
Integer groupId = Integer.parseInt(context.request().getParam("groupId"));
Group group = new Group(groupId, null);
String query = QueryBuilder
.select(group)
.build();
dbManager.execute(query, Group.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void addGroup(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Group group = gson.fromJson(body.toString(), Group.class);
String query = QueryBuilder
.insert(group)
.build();
dbManager.execute(query, Group.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Group added successfully")));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void updateGroup(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Group group = gson.fromJson(body.toString(), Group.class);
String query = QueryBuilder
.update(group)
.build();
dbManager.execute(query, Group.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Group updated successfully")));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getAllDevices(RoutingContext context) {
String query = QueryBuilder
.select(Device.class)
.build();
dbManager.execute(query, Device.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getDeviceById(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Device device = new Device(deviceId, null, null);
String query = QueryBuilder
.select(device)
.build();
dbManager.execute(query, Device.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void addDevice(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Device device = gson.fromJson(body.toString(), Device.class);
String query = QueryBuilder
.insert(device)
.build();
dbManager.execute(query, Device.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Device added successfully")));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void updateDevice(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Device device = gson.fromJson(body.toString(), Device.class);
String query = QueryBuilder
.update(device)
.build();
dbManager.execute(query, Device.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Device updated successfully")));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getAllSensors(RoutingContext context) {
String query = QueryBuilder
.select(Sensor.class)
.build();
dbManager.execute(query, Sensor.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getSensorById(RoutingContext context) {
Integer sensorId = Integer.parseInt(context.request().getParam("sensorId"));
Sensor sensor = new Sensor(sensorId, null, null, null, null, null);
String query = QueryBuilder
.select(sensor)
.build();
dbManager.execute(query, Sensor.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void addSensor(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Sensor sensor = gson.fromJson(body.toString(), Sensor.class);
String query = QueryBuilder
.insert(sensor)
.build();
dbManager.execute(query, Sensor.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Sensor added successfully")));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void updateSensor(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Sensor sensor = gson.fromJson(body.toString(), Sensor.class);
String query = QueryBuilder
.update(sensor)
.build();
dbManager.execute(query, Sensor.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Sensor updated successfully")));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getAllActuators(RoutingContext context) {
String query = QueryBuilder
.select(Actuator.class)
.build();
dbManager.execute(query, Actuator.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getActuatorById(RoutingContext context) {
Integer actuatorId = Integer.parseInt(context.request().getParam("actuatorId"));
Actuator actuator = new Actuator(actuatorId, null, null, null);
String query = QueryBuilder
.select(actuator)
.build();
dbManager.execute(query, Actuator.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void addActuator(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Actuator actuator = gson.fromJson(body.toString(), Actuator.class);
String query = QueryBuilder
.insert(actuator)
.build();
dbManager.execute(query, Actuator.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Actuator added successfully")));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void updateActuator(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Actuator actuator = gson.fromJson(body.toString(), Actuator.class);
String query = QueryBuilder
.update(actuator)
.build();
dbManager.execute(query, Actuator.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Actuator updated successfully")));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getLatestValuesView(RoutingContext context) {
String query = QueryBuilder
.select(DeviceLatestValuesView.class)
.build();
dbManager.execute(query, DeviceLatestValuesView.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getDevicePollutionMapView(RoutingContext context) {
String query = QueryBuilder
.select(DevicePollutionMap.class)
.build();
dbManager.execute(query, DevicePollutionMap.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getSensorValuesView(RoutingContext context) {
String query = QueryBuilder
.select(DeviceSensorValue.class)
.build();
dbManager.execute(query, DeviceSensorValue.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getSensorHistoryByDeviceView(RoutingContext context) {
String query = QueryBuilder
.select(DeviceSensorHistory.class)
.build();
dbManager.execute(query, DeviceSensorHistory.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
}

View File

@@ -1,225 +0,0 @@
package net.miarma.contaminus.server;
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;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
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.database.entities.Actuator;
import net.miarma.contaminus.database.entities.Device;
import net.miarma.contaminus.database.entities.DeviceLatestValuesView;
import net.miarma.contaminus.database.entities.DevicePollutionMap;
import net.miarma.contaminus.database.entities.DeviceSensorHistory;
import net.miarma.contaminus.database.entities.DeviceSensorValue;
import net.miarma.contaminus.database.entities.Sensor;
import net.miarma.contaminus.util.RestClientUtil;
public class LogicLayerAPIVerticle extends AbstractVerticle {
private ConfigManager configManager;
private final Gson gson = new GsonBuilder().serializeNulls().create();
private RestClientUtil restClient;
public LogicLayerAPIVerticle() {
this.configManager = ConfigManager.getInstance();
WebClientOptions options = new WebClientOptions()
.setUserAgent("ContaminUS");
this.restClient = new RestClientUtil(WebClient.create(Vertx.vertx(), options));
}
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("📡 Iniciando LogicApiVerticle...");
Router router = Router.router(vertx);
Set<HttpMethod> allowedMethods = new HashSet<>(
Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.OPTIONS)); // Por ejemplo
Set<String> allowedHeaders = new HashSet<>(Arrays.asList("Content-Type", "Authorization"));
router.route().handler(CorsHandler.create()
.allowCredentials(true)
.allowedHeaders(allowedHeaders)
.allowedMethods(allowedMethods));
router.route().handler(BodyHandler.create());
router.route(HttpMethod.GET, Constants.GET_GROUP_DEVICES).handler(this::getGroupDevices);
router.route(HttpMethod.GET, Constants.GET_DEVICE_SENSORS).handler(this::getDeviceSensors);
router.route(HttpMethod.GET, Constants.GET_DEVICE_ACTUATORS).handler(this::getDeviceActuators);
router.route(HttpMethod.GET, Constants.GET_DEVICE_LATEST_VALUES).handler(this::getDeviceLatestValues);
router.route(HttpMethod.GET, Constants.GET_DEVICE_POLLUTION_MAP).handler(this::getDevicePollutionMap);
router.route(HttpMethod.GET, Constants.GET_DEVICE_HISTORY).handler(this::getDeviceHistory);
router.route(HttpMethod.GET, Constants.GET_SENSOR_VALUES).handler(this::getSensorValues);
vertx.createHttpServer()
.requestHandler(router)
.listen(configManager.getLogicApiPort(), configManager.getHost());
startPromise.complete();
}
private void getGroupDevices(RoutingContext context) {
Integer groupId = Integer.parseInt(context.request().getParam("groupId"));
Promise<Device[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if(complete.succeeded()) {
List<Device> aux = Stream.of(complete.result())
.filter(d -> d.getGroupId() == groupId)
.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.GET_DEVICES, Device[].class, resultList);
}
private void getDeviceSensors(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<Sensor[]> resultList = Promise.promise();
resultList.future().onComplete(result -> {
if (result.succeeded()) {
Sensor[] sensors = result.result();
List<Sensor> aux = Arrays.stream(sensors)
.filter(s -> s.getDeviceId() == deviceId)
.toList();
context.response().putHeader("Content-Type", "application/json").end(gson.toJson(aux));
} else {
context.response().setStatusCode(500).end(result.cause().getMessage());
}
});
restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.GET_SENSORS, Sensor[].class, resultList);
}
private void getDeviceActuators(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<Actuator[]> resultList = Promise.promise();
resultList.future().onComplete(result -> {
if (result.succeeded()) {
Actuator[] devices = result.result();
List<Actuator> aux = Arrays.stream(devices)
.filter(a -> a.getDeviceId() == deviceId)
.toList();
context.response().putHeader("Content-Type", "application/json").end(gson.toJson(aux));
} else {
context.response().setStatusCode(500).end(result.cause().getMessage());
}
});
restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.GET_ACTUATORS, Actuator[].class, resultList);
}
private void getDeviceLatestValues(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<DeviceLatestValuesView[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<DeviceLatestValuesView> aux = Stream.of(complete.result())
.filter(elem -> elem.getDeviceId() == deviceId)
.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.GET_LATEST_VALUES_VIEW, DeviceLatestValuesView[].class, resultList);
}
private void getDevicePollutionMap(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<DevicePollutionMap[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<DevicePollutionMap> aux = Arrays.asList(complete.result()).stream()
.filter(elem -> elem.getDeviceId() == deviceId)
.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.GET_POLLUTION_MAP_VIEW, DevicePollutionMap[].class, resultList);
}
private void getDeviceHistory(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<DeviceSensorHistory[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<DeviceSensorHistory> aux = Arrays.asList(complete.result()).stream()
.filter(elem -> elem.getDeviceId() == deviceId)
.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.GET_SENSOR_HISTORY_BY_DEVICE_VIEW, DeviceSensorHistory[].class, resultList);
}
private void getSensorValues(RoutingContext context) {
Integer sensorId = Integer.parseInt(context.request().getParam("sensorId"));
Promise<DeviceSensorValue[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<DeviceSensorValue> aux = Arrays.asList(complete.result()).stream()
.filter(val -> val.getSensorId() == sensorId)
.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.GET_SENSOR_VALUES_VIEW, DeviceSensorValue[].class, resultList);
}
}

View File

@@ -0,0 +1,479 @@
package net.miarma.contaminus.verticles;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Promise;
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.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.sqlclient.Pool;
import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.SingleJsonResponse;
import net.miarma.contaminus.dao.ActuatorDAO;
import net.miarma.contaminus.dao.COValueDAO;
import net.miarma.contaminus.dao.DeviceDAO;
import net.miarma.contaminus.dao.GpsValueDAO;
import net.miarma.contaminus.dao.GroupDAO;
import net.miarma.contaminus.dao.SensorDAO;
import net.miarma.contaminus.dao.WeatherValueDAO;
import net.miarma.contaminus.dao.views.ViewLatestValuesDAO;
import net.miarma.contaminus.dao.views.ViewPollutionMapDAO;
import net.miarma.contaminus.dao.views.ViewSensorHistoryDAO;
import net.miarma.contaminus.dao.views.ViewSensorValueDAO;
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.Device;
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;
/*
* This class is a Verticle that will handle the Data Layer API.
*/
@SuppressWarnings("unused")
public class DataLayerAPIVerticle extends AbstractVerticle {
private DatabaseManager dbManager;
private ConfigManager configManager;
private final Gson gson = new GsonBuilder().serializeNulls().create();
private Pool pool;
private GroupDAO groupDAO;
private DeviceDAO deviceDAO;
private SensorDAO sensorDAO;
private ActuatorDAO actuatorDAO;
private COValueDAO coValueDAO;
private WeatherValueDAO weatherValueDAO;
private GpsValueDAO gpsValueDAO;
private ViewLatestValuesDAO viewLatestValuesDAO;
private ViewPollutionMapDAO viewPollutionMapDAO;
private ViewSensorHistoryDAO viewSensorHistoryDAO;
private ViewSensorValueDAO viewSensorValueDAO;
public DataLayerAPIVerticle() {
this.configManager = ConfigManager.getInstance();
}
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("📡 Iniciando DataLayerAPIVerticle...");
this.pool = DatabaseProvider.createPool(vertx, configManager);
this.dbManager = DatabaseManager.getInstance(pool);
this.groupDAO = new GroupDAO(pool);
this.deviceDAO = new DeviceDAO(pool);
this.sensorDAO = new SensorDAO(pool);
this.actuatorDAO = new ActuatorDAO(pool);
this.coValueDAO = new COValueDAO(pool);
this.weatherValueDAO = new WeatherValueDAO(pool);
this.gpsValueDAO = new GpsValueDAO(pool);
this.viewLatestValuesDAO = new ViewLatestValuesDAO(pool);
this.viewPollutionMapDAO = new ViewPollutionMapDAO(pool);
this.viewSensorHistoryDAO = new ViewSensorHistoryDAO(pool);
this.viewSensorValueDAO = new ViewSensorValueDAO(pool);
Router router = Router.router(vertx);
Set<HttpMethod> allowedMethods = new HashSet<>(
Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.OPTIONS)); // Por ejemplo
Set<String> allowedHeaders = new HashSet<>(Arrays.asList("Content-Type", "Authorization"));
router.route().handler(CorsHandler.create()
.allowCredentials(true)
.allowedHeaders(allowedHeaders)
.allowedMethods(allowedMethods));
router.route().handler(BodyHandler.create());
// Group Routes
router.route(HttpMethod.GET, Constants.GROUPS).handler(this::getAllGroups);
router.route(HttpMethod.GET, Constants.GROUP).handler(this::getGroupById);
router.route(HttpMethod.POST, Constants.GROUPS).handler(this::addGroup);
router.route(HttpMethod.PUT, Constants.GROUP).handler(this::updateGroup);
// Device Routes
router.route(HttpMethod.GET, Constants.DEVICES).handler(this::getAllDevices);
router.route(HttpMethod.GET, Constants.DEVICE).handler(this::getDeviceById);
router.route(HttpMethod.POST, Constants.DEVICES).handler(this::addDevice);
router.route(HttpMethod.PUT, Constants.DEVICE).handler(this::updateDevice);
// Sensor Routes
router.route(HttpMethod.GET, Constants.SENSORS).handler(this::getAllSensors);
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);
// Actuator Routes
router.route(HttpMethod.GET, Constants.ACTUATORS).handler(this::getAllActuators);
router.route(HttpMethod.GET, Constants.ACTUATOR).handler(this::getActuatorById);
router.route(HttpMethod.POST, Constants.ACTUATORS).handler(this::addActuator);
router.route(HttpMethod.PUT, Constants.ACTUATOR).handler(this::updateActuator);
// Views Routes
router.route(HttpMethod.GET, Constants.VIEW_LATEST_VALUES).handler(this::getLatestValuesView);
router.route(HttpMethod.GET, Constants.VIEW_POLLUTION_MAP).handler(this::getDevicePollutionMapView);
router.route(HttpMethod.GET, Constants.VIEW_SENSOR_VALUES).handler(this::getSensorValuesView);
router.route(HttpMethod.GET, Constants.VIEW_SENSOR_HISTORY).handler(this::getSensorHistoryByDeviceView);
vertx.createHttpServer()
.requestHandler(router)
.listen(configManager.getDataApiPort(), configManager.getHost());
pool.query("SELECT 1").execute(ar -> {
if (ar.succeeded()) {
Constants.LOGGER.info("🟢 Connected to DB");
startPromise.complete();
} else {
Constants.LOGGER.error("🔴 Failed to connect to DB: " + ar.cause());
startPromise.fail(ar.cause());
}
});
}
private void getAllGroups(RoutingContext context) {
groupDAO.getAll()
.onSuccess(groups -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(groups));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void getGroupById(RoutingContext context) {
Integer groupId = Integer.parseInt(context.request().getParam("groupId"));
groupDAO.getById(groupId)
.onSuccess(group -> {
if (group != null) {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(group));
} else {
context.response().setStatusCode(404).end();
}
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void addGroup(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Group group = gson.fromJson(body.toString(), Group.class);
groupDAO.insert(group)
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Group added successfully")));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void updateGroup(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Group group = gson.fromJson(body.toString(), Group.class);
groupDAO.update(group)
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Group updated successfully")));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void getAllDevices(RoutingContext context) {
Integer groupId = Integer.parseInt(context.request().getParam("groupId"));
deviceDAO.getAllByGroupId(groupId)
.onSuccess(devices -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(devices));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void getDeviceById(RoutingContext context) {
Integer groupId = Integer.parseInt(context.request().getParam("groupId"));
String deviceId = context.request().getParam("deviceId");
deviceDAO.getByIdAndGroupId(deviceId, groupId)
.onSuccess(device -> {
if (device != null) {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(device));
} else {
context.response().setStatusCode(404).end();
}
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void addDevice(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Device device = gson.fromJson(body.toString(), Device.class);
deviceDAO.insert(device)
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Device added successfully")));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void updateDevice(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Device device = gson.fromJson(body.toString(), Device.class);
deviceDAO.update(device)
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Device updated successfully")));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void getAllSensors(RoutingContext context) {
Integer groupId = Integer.parseInt(context.request().getParam("groupId"));
String deviceId = context.request().getParam("deviceId");
deviceDAO.getByIdAndGroupId(deviceId, groupId).compose(device -> {
if (device == null) {
return Future.succeededFuture(List.of());
}
return sensorDAO.getAllByDeviceId(device.getDeviceId());
}).onSuccess(sensors -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(sensors));
}).onFailure(err -> {
context.response().setStatusCode(500).end("Error: " + err.getMessage());
});
}
private void getSensorById(RoutingContext context) {
Integer sensorId = Integer.parseInt(context.request().getParam("sensorId"));
String deviceId = context.request().getParam("deviceId");
Integer groupId = Integer.parseInt(context.request().getParam("groupId"));
deviceDAO.getByIdAndGroupId(deviceId, groupId).compose(device -> {
if (device == null) {
return Future.succeededFuture(null);
}
return sensorDAO.getByIdAndDeviceId(sensorId, device.getDeviceId());
}).onSuccess(sensor -> {
if (sensor == null) {
context.response().setStatusCode(404).end("Sensor no encontrado");
return;
}
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(sensor));
}).onFailure(err -> {
context.response().setStatusCode(500).end("Error: " + err.getMessage());
});
}
private void addSensor(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Sensor sensor = gson.fromJson(body.toString(), Sensor.class);
sensorDAO.insert(sensor)
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Sensor added successfully")));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void updateSensor(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Sensor sensor = gson.fromJson(body.toString(), Sensor.class);
sensorDAO.update(sensor)
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Sensor updated successfully")));
})
.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");
deviceDAO.getByIdAndGroupId(deviceId, groupId).compose(device -> {
if (device == null) {
return Future.succeededFuture(List.of());
}
return actuatorDAO.getAllByDeviceId(device.getDeviceId());
}).onSuccess(actuators -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(actuators));
}).onFailure(err -> {
context.response().setStatusCode(500).end("Error: " + err.getMessage());
});
}
private void getActuatorById(RoutingContext context) {
Integer groupId = Integer.parseInt(context.request().getParam("groupId"));
String deviceId = context.request().getParam("deviceId");
Integer actuatorId = Integer.parseInt(context.request().getParam("actuatorId"));
deviceDAO.getByIdAndGroupId(deviceId, groupId).compose(device -> {
if (device == null) {
return Future.succeededFuture(null);
}
return actuatorDAO.getByIdAndDeviceId(actuatorId, device.getDeviceId());
}).onSuccess(actuator -> {
if (actuator == null) {
context.response().setStatusCode(404).end("Actuator no encontrado");
return;
}
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(actuator));
}).onFailure(err -> {
context.response().setStatusCode(500).end("Error: " + err.getMessage());
});
}
private void addActuator(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Actuator actuator = gson.fromJson(body.toString(), Actuator.class);
actuatorDAO.insert(actuator)
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Actuator added successfully")));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void updateActuator(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
Actuator actuator = gson.fromJson(body.toString(), Actuator.class);
actuatorDAO.update(actuator)
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Actuator updated successfully")));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void getLatestValuesView(RoutingContext context) {
String query = QueryBuilder
.select(ViewLatestValues.class)
.build();
dbManager.execute(query, ViewLatestValues.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getDevicePollutionMapView(RoutingContext context) {
String query = QueryBuilder
.select(ViewPollutionMap.class)
.build();
dbManager.execute(query, ViewPollutionMap.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getSensorValuesView(RoutingContext context) {
String query = QueryBuilder
.select(ViewSensorValue.class)
.build();
dbManager.execute(query, ViewSensorValue.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
private void getSensorHistoryByDeviceView(RoutingContext context) {
String query = QueryBuilder
.select(ViewSensorHistory.class)
.build();
dbManager.execute(query, ViewSensorHistory.class,
onSuccess -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(onSuccess));
},
onFailure -> {
context.fail(500, onFailure);
});
}
}

View File

@@ -0,0 +1,160 @@
package net.miarma.contaminus.verticles;
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;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
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.ViewLatestValues;
import net.miarma.contaminus.entities.ViewPollutionMap;
import net.miarma.contaminus.entities.ViewSensorHistory;
import net.miarma.contaminus.entities.ViewSensorValue;
import net.miarma.contaminus.util.RestClientUtil;
public class LogicLayerAPIVerticle extends AbstractVerticle {
private ConfigManager configManager;
private final Gson gson = new GsonBuilder().serializeNulls().create();
private RestClientUtil restClient;
public LogicLayerAPIVerticle() {
this.configManager = ConfigManager.getInstance();
WebClientOptions options = new WebClientOptions()
.setUserAgent("ContaminUS");
this.restClient = new RestClientUtil(WebClient.create(Vertx.vertx(), options));
}
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("📡 Iniciando LogicApiVerticle...");
Router router = Router.router(vertx);
Set<HttpMethod> allowedMethods = new HashSet<>(
Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.OPTIONS));
Set<String> allowedHeaders = new HashSet<>(Arrays.asList("Content-Type", "Authorization"));
router.route().handler(CorsHandler.create()
.allowCredentials(true)
.allowedHeaders(allowedHeaders)
.allowedMethods(allowedMethods));
router.route().handler(BodyHandler.create());
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);
router.route(HttpMethod.GET, Constants.SENSOR_VALUES).handler(this::getSensorValues);
vertx.createHttpServer()
.requestHandler(router)
.listen(configManager.getLogicApiPort(), configManager.getHost());
startPromise.complete();
}
private void getDeviceLatestValues(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<ViewLatestValues[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<ViewLatestValues> 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);
}
private void getDevicePollutionMap(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<ViewPollutionMap[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<ViewPollutionMap> 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);
}
private void getDeviceHistory(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<ViewSensorHistory[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<ViewSensorHistory> 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);
}
private void getSensorValues(RoutingContext context) {
Integer sensorId = Integer.parseInt(context.request().getParam("sensorId"));
Promise<ViewSensorValue[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<ViewSensorValue> aux = Arrays.asList(complete.result()).stream()
.filter(val -> val.getSensorId() == sensorId)
.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_VALUES, ViewSensorValue[].class, resultList);
}
}

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.server;
package net.miarma.contaminus.verticles;
import java.io.File;
import java.io.IOException;
@@ -52,7 +52,6 @@ public class MainVerticle extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) {
try {
System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager");
init();
deployVerticles(startPromise);
} catch (Exception e) {

View File

@@ -1,4 +1,4 @@
package net.miarma.contaminus.server;
package net.miarma.contaminus.verticles;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;

View File

@@ -4,7 +4,7 @@ db.host=localhost
db.port=3306
db.name=dad
db.user=root
db.pwd=root
db.password=root
dp.poolSize=5
# HTTP Server Configuration

View File

@@ -0,0 +1,20 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%cyan([%d{HH:mm:ss}]) %highlight(%-5level) %green(%logger{20}) - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
<logger name="io.netty" level="WARN"/>
<logger name="io.vertx" level="INFO"/>
<logger name="io.vertx.core.impl.launcher" level="INFO"/>
<logger name="io.vertx.core.logging" level="INFO"/>
</configuration>

View File

@@ -51,8 +51,8 @@ const SummaryCardsContent = () => {
let coData = data[1];
let tempData = data[2];
let lastTime = DateParser.timestampToString(coData.airValuesTimestamp);
let lastDate = new Date(coData.airValuesTimestamp);
let lastTime = DateParser.timestampToString(coData.timestamp);
let lastDate = new Date(coData.timestamp);
CardsData[0].content = tempData.temperature + "°C";
CardsData[0].status = "Temperatura actual";

View File

@@ -2,6 +2,9 @@
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include "BME280.hpp"
#include "MQ7v2.hpp"
#include "GPS.hpp"
String serializeSensorValue(
int sensorId,
@@ -12,15 +15,13 @@ String serializeSensorValue(
const BME280Data_t &bme,
const MQ7Data_t &mq7,
const GPSData_t &gps,
long timestamp
);
long timestamp);
String serializeActuatorStatus(
int actuatorId,
const String &deviceId,
int status,
long timestamp
);
long timestamp);
void deserializeSensorValue(HTTPClient &http, int httpResponseCode);
void deserializeActuatorStatus(HTTPClient &http, int httpResponseCode);

View File

@@ -9,8 +9,8 @@ String serializeSensorValue(
const BME280Data_t &bme,
const MQ7Data_t &mq7,
const GPSData_t &gps,
long timestamp
) {
long timestamp)
{
DynamicJsonDocument doc(1024);
doc["sensorId"] = sensorId;
@@ -32,7 +32,8 @@ String serializeSensorValue(
return output;
}
String serializeActuatorStatus(const int actuatorId, const String &deviceId, const int status, const long timestamp) {
String serializeActuatorStatus(const int actuatorId, const String &deviceId, const int status, const long timestamp)
{
DynamicJsonDocument doc(512);
doc["actuatorId"] = actuatorId;
@@ -46,7 +47,8 @@ String serializeActuatorStatus(const int actuatorId, const String &deviceId, con
return output;
}
String serializeDevice(const String &deviceId, int groupId, const String &deviceName) {
String serializeDevice(const String &deviceId, int groupId, const String &deviceName)
{
DynamicJsonDocument doc(512);
doc["deviceId"] = deviceId;
@@ -59,22 +61,26 @@ String serializeDevice(const String &deviceId, int groupId, const String &device
return output;
}
void deserializeSensorValue(HTTPClient &http, int httpResponseCode) {
if (httpResponseCode > 0) {
void deserializeSensorValue(HTTPClient &http, int httpResponseCode)
{
if (httpResponseCode > 0)
{
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String responseJson = http.getString();
DynamicJsonDocument doc(ESP.getMaxAllocHeap());
DeserializationError error = deserializeJson(doc, responseJson);
if (error) {
if (error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
JsonArray array = doc.as<JsonArray>();
for (JsonObject sensor : array) {
for (JsonObject sensor : array)
{
int sensorId = sensor["sensorId"];
String deviceId = sensor["deviceId"];
String sensorType = sensor["sensorType"];
@@ -92,28 +98,34 @@ void deserializeSensorValue(HTTPClient &http, int httpResponseCode) {
sensorId, deviceId.c_str(), sensorType.c_str(), unit.c_str(), sensorStatus,
temperature, humidity, carbonMonoxide, lat, lon, timestamp);
}
} else {
}
else
{
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
}
void deserializeActuatorStatus(HTTPClient &http, int httpResponseCode) {
if (httpResponseCode > 0) {
void deserializeActuatorStatus(HTTPClient &http, int httpResponseCode)
{
if (httpResponseCode > 0)
{
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String responseJson = http.getString();
DynamicJsonDocument doc(ESP.getMaxAllocHeap());
DeserializationError error = deserializeJson(doc, responseJson);
if (error) {
if (error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
JsonArray array = doc.as<JsonArray>();
for (JsonObject actuator : array) {
for (JsonObject actuator : array)
{
int actuatorId = actuator["actuatorId"];
String deviceId = actuator["deviceId"];
int status = actuator["status"];
@@ -123,28 +135,34 @@ void deserializeActuatorStatus(HTTPClient &http, int httpResponseCode) {
Serial.printf(" ID: %d\n Device: %s\n Status: %d\n Time: %ld\n\n",
actuatorId, deviceId.c_str(), status, timestamp);
}
} else {
}
else
{
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
}
void deserializeDevice(HTTPClient &http, int httpResponseCode) {
if (httpResponseCode > 0) {
void deserializeDevice(HTTPClient &http, int httpResponseCode)
{
if (httpResponseCode > 0)
{
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String responseJson = http.getString();
DynamicJsonDocument doc(ESP.getMaxAllocHeap());
DeserializationError error = deserializeJson(doc, responseJson);
if (error) {
if (error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
JsonArray array = doc.as<JsonArray>();
for (JsonObject device : array) {
for (JsonObject device : array)
{
String deviceId = device["deviceId"];
int groupId = device["groupId"];
String deviceName = device["deviceName"];
@@ -152,7 +170,9 @@ void deserializeDevice(HTTPClient &http, int httpResponseCode) {
Serial.println("Device deserialized:");
Serial.printf(" ID: %s\n Group: %d\n Name: %s\n\n", deviceId.c_str(), groupId, deviceName.c_str());
}
} else {
}
else
{
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}

View File

@@ -6,9 +6,12 @@ void getRequest(const String url, String &response)
{
httpClient.begin(url);
int httpCode = httpClient.GET();
if (httpCode > 0) {
if (httpCode > 0)
{
response = httpClient.getString();
} else {
}
else
{
response = "Error: " + String(httpCode);
}
httpClient.end();
@@ -19,9 +22,12 @@ void postRequest(const String url, String &payload, String &response)
httpClient.begin(url);
httpClient.addHeader("Content-Type", "application/json");
int httpCode = httpClient.POST(payload);
if (httpCode > 0) {
if (httpCode > 0)
{
response = httpClient.getString();
} else {
}
else
{
response = "Error: " + String(httpCode);
}
httpClient.end();

View File

@@ -12,7 +12,8 @@ void OnMqttReceived(char *topic, byte *payload, unsigned int length)
String content = "";
for (size_t i = 0; i < length; i++) {
for (size_t i = 0; i < length; i++)
{
content.concat((char)payload[i]);
}

View File

@@ -6,24 +6,28 @@
WiFiClient wifiClient;
void setColor(uint8_t r, uint8_t g, uint8_t b) {
void setColor(uint8_t r, uint8_t g, uint8_t b)
{
ledcWrite(0, r);
ledcWrite(1, g);
ledcWrite(2, b);
}
void setupLED() {
ledcAttachPin(PIN_R, 0);
ledcAttachPin(PIN_G, 1);
ledcAttachPin(PIN_B, 2);
void setupLED()
{
ledcSetup(0, 5000, 8);
ledcAttachPin(PIN_R, 0);
ledcSetup(1, 5000, 8);
ledcAttachPin(PIN_G, 1);
ledcSetup(2, 5000, 8);
ledcAttachPin(PIN_B, 2);
}
// hue cycle
void hueCycle(uint8_t pos) {
void hueCycle(uint8_t pos)
{
uint8_t r = (uint8_t)(sin((pos + 0) * 0.024) * 127 + 128);
uint8_t g = (uint8_t)(sin((pos + 85) * 0.024) * 127 + 128);
uint8_t b = (uint8_t)(sin((pos + 170) * 0.024) * 127 + 128);
@@ -80,4 +84,3 @@ int setupWifi()
return 1;
}
}

View File

@@ -20,7 +20,8 @@ void BME280_Init()
bme.setSettings(settings);
while (!bme.begin());
while (!bme.begin())
;
}
BME280Data_t BME280_Read()
@@ -31,4 +32,3 @@ BME280Data_t BME280_Read()
bme.read(p, t, h, tUnit, pUnit);
return {p, t, h};
}

View File

@@ -40,8 +40,10 @@ void loop()
{
uint32_t now = millis();
if (now - matrixTimer.lastRun >= matrixTimer.interval) {
if (MAX7219_Animate()) {
if (now - matrixTimer.lastRun >= matrixTimer.interval)
{
if (MAX7219_Animate())
{
MAX7219_ResetAnimation();
}
matrixTimer.lastRun = now;
@@ -69,11 +71,15 @@ void readMQ7()
AirQualityStatus newStatus = (mq7Data.co >= CO_THRESHOLD) ? BAD : GOOD;
if (newStatus != currentAirStatus) {
if (newStatus != currentAirStatus)
{
currentAirStatus = newStatus;
if (currentAirStatus == BAD) {
if (currentAirStatus == BAD)
{
writeMatrix(ELECTRIC_VEHICLES);
} else {
}
else
{
writeMatrix(ALL_VEHICLES);
}
}
@@ -91,7 +97,8 @@ void readGPS()
void writeMatrix(const char *message)
{
if (currentMessage == message) return;
if (currentMessage == message)
return;
currentMessage = message;
#ifdef DEBUG
@@ -105,17 +112,28 @@ void printAllData()
{
Serial.println("---------------------");
Serial.print("ID: "); Serial.println(DEVICE_ID, HEX);
Serial.print("ID: ");
Serial.println(DEVICE_ID, HEX);
Serial.print("Presión: "); Serial.print(bme280Data.pressure / 100); Serial.println(" hPa");
Serial.print("Temperatura: "); Serial.print(bme280Data.temperature); Serial.println(" °C");
Serial.print("Humedad: "); Serial.print(bme280Data.humidity); Serial.println(" %");
Serial.print("Presión: ");
Serial.print(bme280Data.pressure / 100);
Serial.println(" hPa");
Serial.print("Temperatura: ");
Serial.print(bme280Data.temperature);
Serial.println(" °C");
Serial.print("Humedad: ");
Serial.print(bme280Data.humidity);
Serial.println(" %");
Serial.print("Latitud: "); Serial.println(gpsData.lat);
Serial.print("Longitud: "); Serial.println(gpsData.lon);
Serial.print("Latitud: ");
Serial.println(gpsData.lat);
Serial.print("Longitud: ");
Serial.println(gpsData.lon);
Serial.print("CO: "); Serial.println(mq7Data.co);
Serial.print("D0: "); Serial.println(mq7Data.threshold);
Serial.print("CO: ");
Serial.println(mq7Data.co);
Serial.print("D0: ");
Serial.println(mq7Data.threshold);
}
uint32_t getChipID()
@@ -126,7 +144,8 @@ uint32_t getChipID()
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
}
#ifdef DEBUG
Serial.print("Chip ID: "); Serial.println(chipId, HEX);
Serial.print("Chip ID: ");
Serial.println(chipId, HEX);
#endif
return chipId;
}