1
0

Merge pull request #6 from Gallardo7761/feature/dos-apis-y-db-manager

Feature/dos apis y db manager TERMINADO
He incluido vuestros cambios en feature/entities
This commit is contained in:
Jose
2025-03-14 02:32:01 +01:00
committed by GitHub
31 changed files with 1298 additions and 809 deletions

View File

@@ -55,6 +55,13 @@
<version>2.12.1</version> <version>2.12.1</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/io.quarkus/quarkus-agroal -->
<dependency>
<groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager</artifactId>
<version>3.1.1.Final</version> <!-- O la versión más reciente -->
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -4,49 +4,20 @@ import java.io.*;
import java.util.Properties; import java.util.Properties;
public class ConfigManager { public class ConfigManager {
private static ConfigManager instance; private static final ConfigManager INSTANCE = new ConfigManager();
private final File configFile; private final File configFile;
private final Properties config; private final Properties config;
private static final String CONFIG_FILE_NAME = "config.properties";
private ConfigManager() { private ConfigManager() {
this.configFile = new File(Constants.CONFIG_FILE); String path = getBaseDir() + CONFIG_FILE_NAME;
this.configFile = new File(path);
this.config = new Properties(); this.config = new Properties();
if (!configFile.exists()) {
try {
createFiles();
} catch (IOException e) {
Constants.LOGGER.error("Error creating configuration files: ", e);
}
}
loadConfig(); loadConfig();
} }
public static synchronized ConfigManager getInstance() { public static ConfigManager getInstance() {
if (instance == null) { return INSTANCE;
instance = new ConfigManager();
}
return instance;
}
private void createFiles() throws IOException {
File baseDir = new File(Constants.BASE_DIR);
if (!baseDir.exists()) baseDir.mkdirs();
try (InputStream defaultConfigStream = getClass().getClassLoader().getResourceAsStream("default.properties");
FileOutputStream fos = new FileOutputStream(configFile)) {
if (defaultConfigStream != null) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = defaultConfigStream.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
} else {
Constants.LOGGER.error("File not found: default.properties");
}
}
} }
private void loadConfig() { private void loadConfig() {
@@ -56,17 +27,71 @@ public class ConfigManager {
Constants.LOGGER.error("Error loading configuration file: ", e); Constants.LOGGER.error("Error loading configuration file: ", e);
} }
} }
public File getConfigFile() {
return configFile;
}
public String getJdbcUrl() {
return String.format("%s://%s:%s/%s",
config.getProperty("db.protocol"),
config.getProperty("db.host"),
config.getProperty("db.port"),
config.getProperty("db.name"));
}
public String getHost() {
return this.getStringProperty("inet.host");
}
public int getDataApiPort() {
return this.getIntProperty("data-api.port");
}
public int getLogicApiPort() {
return this.getIntProperty("logic-api.port");
}
public int getWebserverPort() {
return this.getIntProperty("webserver.port");
}
public String getHomeDir() {
return getOS() == OSType.WINDOWS ?
"C:/Users/" + System.getProperty("user.name") + "/" :
System.getProperty("user.home").contains("root") ? "/root/" :
"/home/" + System.getProperty("user.name") + "/";
}
public String getBaseDir() {
return getHomeDir() +
(getOS() == OSType.WINDOWS ? ".contaminus/" :
getOS() == OSType.LINUX ? ".config/contaminus/" :
".contaminus/");
}
public static OSType getOS() {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
return OSType.WINDOWS;
} else if (os.contains("nix") || os.contains("nux")) {
return OSType.LINUX;
} else {
return OSType.INVALID_OS;
}
}
public String getStringProperty(String key) { public String getStringProperty(String key) {
return config.getProperty(key); return config.getProperty(key);
} }
public int getIntProperty(String key) { public int getIntProperty(String key) {
return Integer.parseInt(config.getProperty(key)); String value = config.getProperty(key);
} return value != null ? Integer.parseInt(value) : 10;
}
public boolean getBooleanProperty(String key) { public boolean getBooleanProperty(String key) {
return Boolean.parseBoolean(config.getProperty(key)); return Boolean.parseBoolean(config.getProperty(key));
} }
public void setProperty(String key, String value) { public void setProperty(String key, String value) {

View File

@@ -6,44 +6,38 @@ import io.vertx.core.impl.logging.LoggerFactory;
public class Constants { public class Constants {
public static final String APP_NAME = "ContaminUS"; public static final String APP_NAME = "ContaminUS";
public static final String API_PREFIX = "/api/v1"; public static final String API_PREFIX = "/api/v1";
public static final String HOME_DIR = SystemInfo.getOS() == OSType.WINDOWS ? public static Logger LOGGER = LoggerFactory.getLogger(Constants.APP_NAME);
"C:/Users/" + System.getProperty("user.name") + "/" :
System.getProperty("user.home").contains("root") ? "/root/" :
"/home/" + System.getProperty("user.name") + "/";
public static final String BASE_DIR = HOME_DIR +
(SystemInfo.getOS() == OSType.WINDOWS ? ".contaminus" :
SystemInfo.getOS() == OSType.LINUX ? ".config" + "/" +
"contaminus" : null);
public static final String CONFIG_FILE = BASE_DIR + "/" + "config.properties";
public static final Logger LOGGER = LoggerFactory.getLogger(APP_NAME);
/* API Endpoints */
public static final String GET_GROUPS = API_PREFIX + "/groups"; public static final String GET_GROUPS = API_PREFIX + "/groups";
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 POST_GROUPS = API_PREFIX + "/groups"; public static final String POST_GROUPS = API_PREFIX + "/groups";
public static final String PUT_GROUP_BY_ID = API_PREFIX + "/groups/:groupId"; public static final String PUT_GROUP_BY_ID = API_PREFIX + "/groups/:groupId";
public static final String GET_DEVICES = API_PREFIX + "/devices"; public static final String GET_DEVICES = API_PREFIX + "/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 POST_DEVICES = API_PREFIX + "/devices"; public static final String POST_DEVICES = API_PREFIX + "/devices";
public static final String PUT_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId"; public static final String PUT_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId";
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_SENSORS = API_PREFIX + "/sensors"; public static final String GET_SENSORS = API_PREFIX + "/sensors";
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 POST_SENSORS = API_PREFIX + "/sensors"; public static final String POST_SENSORS = API_PREFIX + "/sensors";
public static final String PUT_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId"; public static final String PUT_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId";
public static final String GET_ACTUATORS = API_PREFIX + "/actuators"; public static final String GET_ACTUATORS = API_PREFIX + "/actuators";
public static final String GET_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
public static final String POST_ACTUATORS = API_PREFIX + "/actuators"; public static final String POST_ACTUATORS = API_PREFIX + "/actuators";
public static final String PUT_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId"; public static final String PUT_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
/* 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";
private Constants() { private Constants() {
throw new AssertionError("Utility class cannot be instantiated."); throw new AssertionError("Utility class cannot be instantiated.");

View File

@@ -1,21 +0,0 @@
package net.miarma.contaminus.common;
public class Host {
static ConfigManager configManager = ConfigManager.getInstance();
static String host = configManager.getStringProperty("inet.host");
static String origin = configManager.getStringProperty("inet.origin");
static int apiPort = configManager.getIntProperty("api.port");
static int webserverPort = configManager.getIntProperty("webserver.port");
public static String getHost() {
return host;
}
public static int getApiPort() {
return apiPort;
}
public static int getWebserverPort() {
return webserverPort;
}
}

View File

@@ -1,14 +0,0 @@
package net.miarma.contaminus.common;
public class SystemInfo {
public static OSType getOS() {
String envProperty = System.getProperty("os.name").toLowerCase();
if(envProperty.contains("windows")) {
return OSType.WINDOWS;
} else if(envProperty.contains("linux")) {
return OSType.LINUX;
} else {
return OSType.INVALID_OS;
}
}
}

View File

@@ -0,0 +1,12 @@
package net.miarma.contaminus.common;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE) // aplica a clases/interfaces
@Retention(RetentionPolicy.RUNTIME) // disponible en tiempo de ejecución
public @interface Table {
String value();
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class Actuator {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class COValue {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class Device {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class GpsValue {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class Group {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class Sensor {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class WeatherValue {
}

View File

@@ -1,30 +1,58 @@
package net.miarma.contaminus.database; package net.miarma.contaminus.database;
import io.vertx.core.Vertx; import java.lang.reflect.Constructor;
import io.vertx.core.json.JsonObject; 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.jdbcclient.JDBCPool;
import net.miarma.contaminus.common.ConfigManager; import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import net.miarma.contaminus.common.Constants;
public class DatabaseManager { public class DatabaseManager {
private static DatabaseManager instance;
private final JDBCPool pool; private final JDBCPool pool;
private DatabaseManager(JDBCPool pool) {
this.pool = pool;
}
@SuppressWarnings("deprecation") public static synchronized DatabaseManager getInstance(JDBCPool pool) {
public DatabaseManager(Vertx vertx) { if (instance == null) {
ConfigManager config = ConfigManager.getInstance(); instance = new DatabaseManager(pool);
}
JsonObject dbConfig = new JsonObject() return instance;
.put("url", config.getStringProperty("db.protocol") + "//" +
config.getStringProperty("db.host") + ":" +
config.getStringProperty("db.port") + "/" +
config.getStringProperty("db.name"))
.put("user", config.getStringProperty("db.user"))
.put("password", config.getStringProperty("db.pwd"))
.put("max_pool_size", config.getStringProperty("db.poolSize"));
pool = JDBCPool.pool(vertx, dbConfig);
} }
public JDBCPool getPool() { public Future<RowSet<Row>> testConnection() {
return pool; 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,87 +1,135 @@
package net.miarma.contaminus.database; package net.miarma.contaminus.database;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.StringJoiner; import java.util.StringJoiner;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.Table;
public class QueryBuilder { public class QueryBuilder {
private StringBuilder query; private StringBuilder query;
private List<String> conditions;
private String sort; private String sort;
private String order; private String order;
private String limit; private String limit;
public QueryBuilder() { public QueryBuilder() {
this.query = new StringBuilder(); this.query = new StringBuilder();
this.conditions = new ArrayList<>();
} }
public static QueryBuilder select(String... columns) { private static <T> String getTableName(Class<T> clazz) {
QueryBuilder qb = new QueryBuilder(); if (clazz.isAnnotationPresent(Table.class)) {
StringJoiner joiner = new StringJoiner(", "); Table annotation = clazz.getAnnotation(Table.class);
for (String column : columns) { return annotation.value(); // lee el nombre de la tabla desde la annotation
joiner.add(column);
} }
qb.query.append("SELECT ").append(joiner).append(" "); throw new IllegalArgumentException("Clase no tiene la anotación @TableName");
}
public String getQuery() {
return query.toString();
}
public static <T> QueryBuilder select(Class<T> clazz, String... columns) {
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) {
joiner.add(column);
}
qb.query.append(joiner).append(" ");
}
qb.query.append("FROM ").append(tableName).append(" ");
return qb; return qb;
} }
public static QueryBuilder insert(String table, String... columns) { public static <T> QueryBuilder where(QueryBuilder qb, T object) {
QueryBuilder qb = new QueryBuilder(); List<String> conditions = new ArrayList<>();
StringJoiner joiner = new StringJoiner(", "); Class<?> clazz = object.getClass();
if (columns.length > 0) {
for (String column : columns) { for (Field field : clazz.getDeclaredFields()) {
joiner.add(column); field.setAccessible(true);
} try {
qb.query.append("INSERT INTO ").append(table).append(" (").append(joiner).append(") "); Object value = field.get(object);
} else { if (value != null) {
qb.query.append("INSERT INTO ").append(table).append(" "); if (value instanceof String) {
} conditions.add(field.getName() + " = '" + value + "'");
} else {
return qb; conditions.add(field.getName() + " = " + value);
} }
}
public static QueryBuilder update(String table) { } catch (IllegalAccessException e) {
QueryBuilder qb = new QueryBuilder(); Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
qb.query.append("UPDATE ").append(table).append(" ");
return qb;
}
public QueryBuilder set(String column, Object value) {
if(value instanceof String) {
query.append("SET ").append(column).append(" = '").append(value).append("' ");
} else {
query.append("SET ").append(column).append(" = ").append(value).append(" ");
}
return this;
}
public QueryBuilder values(Object... values) {
StringJoiner joiner = new StringJoiner(", ", "VALUES (", ")");
for (Object value : values) {
if(value instanceof String) {
joiner.add("'" + value + "'");
} else if(value == null) {
joiner.add("NULL");
}
else {
joiner.add(value.toString());
} }
} }
this.query.append(joiner).append(" ");
return this; if (!conditions.isEmpty()) {
qb.query.append("WHERE ").append(String.join(" AND ", conditions)).append(" ");
}
return qb;
} }
public QueryBuilder from(String table) { public static <T> QueryBuilder select(T object, String... columns) {
query.append("FROM ").append(table).append(" "); Class<?> clazz = object.getClass();
return this; QueryBuilder qb = select(clazz, columns);
return where(qb, object);
}
public static <T> QueryBuilder insert(T object) {
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());
if(field.get(object) instanceof String) {
values.add("'" + field.get(object) + "'");
} else {
values.add(field.get(object).toString());
}
} 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 QueryBuilder where(String conditionsString, Object... values) { public static <T> QueryBuilder update(T object) {
conditionsString = conditionsString.replaceAll("\\?", "%s"); QueryBuilder qb = new QueryBuilder();
conditions.add(String.format(conditionsString, values)); String table = getTableName(object.getClass());
return this; 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 {
if(field.get(object) instanceof String) {
joiner.add(field.getName() + " = '" + field.get(object) + "'");
} else {
joiner.add(field.getName() + " = " + field.get(object).toString());
}
} 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) { public QueryBuilder orderBy(Optional<String> column, Optional<String> order) {
@@ -100,14 +148,6 @@ public class QueryBuilder {
} }
public String build() { public String build() {
if (!conditions.isEmpty()) {
query.append("WHERE ");
StringJoiner joiner = new StringJoiner(" AND ");
for (String condition : conditions) {
joiner.add(condition);
}
query.append(joiner).append(" ");
}
if (order != null && !order.isEmpty()) { if (order != null && !order.isEmpty()) {
query.append(order); query.append(order);
} }

View File

@@ -0,0 +1,91 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
@Table("actuators")
public class Actuator {
private int actuatorId;
private int deviceId;
private int status;
private long timestamp;
public Actuator() {}
public Actuator(Row row) {
this.actuatorId = row.getInteger("actuatorId");
this.deviceId = row.getInteger("deviceId");
this.status = row.getInteger("status");
this.timestamp = row.getLong("timestamp");
}
public Actuator(int actuatorId, int deviceId, int status, long timestamp) {
super();
this.actuatorId = actuatorId;
this.deviceId = deviceId;
this.status = status;
this.timestamp = timestamp;
}
public int getActuatorId() {
return actuatorId;
}
public void setActuatorId(int actuatorId) {
this.actuatorId = actuatorId;
}
public int getDeviceId() {
return deviceId;
}
public void setDeviceId(int deviceId) {
this.deviceId = deviceId;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
@Override
public int hashCode() {
return Objects.hash(actuatorId, deviceId, status, timestamp);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Actuator other = (Actuator) obj;
return actuatorId == other.actuatorId && deviceId == other.deviceId
&& status == other.status && timestamp == other.timestamp;
}
@Override
public String toString() {
return "Actuator [actuatorId=" + actuatorId + ", deviceId=" + deviceId + ", status=" + status + ", timestamp="
+ timestamp + "]";
}
}

View File

@@ -0,0 +1,91 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
@Table("co_values")
public class COValue {
private int valueId;
private int sensorId;
private float value;
private long timestamp;
public COValue() {}
public COValue(Row row) {
this.valueId = row.getInteger("valueId");
this.sensorId = row.getInteger("sensorId");
this.value = row.getFloat("value");
this.timestamp = row.getLong("timestamp");
}
public COValue(int valueId, int sensorId, float value, long timestamp) {
super();
this.valueId = valueId;
this.sensorId = sensorId;
this.value = value;
this.timestamp = timestamp;
}
public int getValueId() {
return valueId;
}
public void setValueId(int valueId) {
this.valueId = valueId;
}
public int getSensorId() {
return sensorId;
}
public void setSensorId(int sensorId) {
this.sensorId = sensorId;
}
public float getValue() {
return value;
}
public void setValue(float value) {
this.value = value;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
@Override
public int hashCode() {
return Objects.hash(sensorId, timestamp, value, valueId);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
COValue other = (COValue) obj;
return sensorId == other.sensorId
&& Objects.equals(timestamp, other.timestamp)
&& Float.floatToIntBits(value) == Float.floatToIntBits(other.value) && valueId == other.valueId;
}
@Override
public String toString() {
return "COValue [valueId=" + valueId + ", sensorId=" + sensorId + ", value=" + value + ", timestamp="
+ timestamp + "]";
}
}

View File

@@ -0,0 +1,81 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
@Table("devices")
public class Device {
private int deviceId;
private int groupId;
private String deviceName;
public Device() {}
public Device(Row row) {
this.deviceId = row.getInteger("deviceId");
this.groupId = row.getInteger("groupId");
this.deviceName = row.getString("deviceName");
}
public Device(int deviceId, int groupId, String deviceName) {
super();
this.deviceId = deviceId;
this.groupId = groupId;
this.deviceName = deviceName;
}
public int getDeviceId() {
return deviceId;
}
public void setDeviceId(int deviceId) {
this.deviceId = deviceId;
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
@Override
public int hashCode() {
return Objects.hash(deviceId, deviceName, groupId);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Device other = (Device) obj;
return deviceId == other.deviceId
&& Objects.equals(deviceName, other.deviceName)
&& groupId == other.groupId;
}
@Override
public String toString() {
return "Device [deviceId=" + deviceId + ", groupId=" + groupId + ", deviceName=" + deviceName + "]";
}
}

View File

@@ -0,0 +1,103 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
@Table("gps_values")
public class GpsValue {
private int valueId;
private int sensorId;
private float lat;
private float lon;
private long timestamp;
public GpsValue() {}
public GpsValue(Row row) {
this.valueId = row.getInteger("valueId");
this.sensorId = row.getInteger("sensorId");
this.lat = row.getFloat("lat");
this.lon = row.getFloat("lon");
this.timestamp = row.getLong("timestamp");
}
public GpsValue(int valueId, int sensorId, float lat, float lon, long timestamp) {
super();
this.valueId = valueId;
this.sensorId = sensorId;
this.lat = lat;
this.lon = lon;
this.timestamp = timestamp;
}
public int getValueId() {
return valueId;
}
public void setValueId(int valueId) {
this.valueId = valueId;
}
public int getSensorId() {
return sensorId;
}
public void setSensorId(int sensorId) {
this.sensorId = sensorId;
}
public float getLat() {
return lat;
}
public void setLat(float lat) {
this.lat = lat;
}
public float getLon() {
return lon;
}
public void setLon(float lon) {
this.lon = lon;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
@Override
public int hashCode() {
return Objects.hash(lat, lon, sensorId, timestamp, valueId);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GpsValue other = (GpsValue) obj;
return Float.floatToIntBits(lat) == Float.floatToIntBits(other.lat)
&& Float.floatToIntBits(lon) == Float.floatToIntBits(other.lon)
&& sensorId == other.sensorId && timestamp == other.timestamp && valueId == other.valueId;
}
@Override
public String toString() {
return "GpsValue [valueId=" + valueId + ", sensorId=" + sensorId + ", lat=" + lat + ", lon=" + lon
+ ", timestamp=" + timestamp + "]";
}
}

View File

@@ -0,0 +1,67 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
@Table("groups")
public class Group {
private int groupId;
private String groupName;
public Group() {}
public Group(Row row) {
this.groupId = row.getInteger("groupId");
this.groupName = row.getString("groupName");
}
public Group(int groupId, String groupName) {
super();
this.groupId = groupId;
this.groupName = groupName;
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
@Override
public int hashCode() {
return Objects.hash(groupId, groupName);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Group other = (Group) obj;
return groupId == other.groupId
&& Objects.equals(groupName, other.groupName);
}
@Override
public String toString() {
return "Group [groupId=" + groupId + ", groupName=" + groupName + "]";
}
}

View File

@@ -0,0 +1,112 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
@Table("sensors")
public class Sensor {
private int sensorId;
private int deviceId;
private String sensorType;
private String unit;
private int status;
private long timestamp;
public Sensor() {}
public Sensor(Row row) {
this.sensorId = row.getInteger("sensorId");
this.deviceId = row.getInteger("deviceId");
this.sensorType = row.getString("sensorType");
this.unit = row.getString("unit");
this.status = row.getInteger("status");
this.timestamp = row.getLong("timestamp");
}
public Sensor(int sensorId, int deviceId, String sensorType, String unit, int status, long timestamp) {
super();
this.sensorId = sensorId;
this.deviceId = deviceId;
this.sensorType = sensorType;
this.unit = unit;
this.status = status;
this.timestamp = timestamp;
}
public int getSensorId() {
return sensorId;
}
public void setSensorId(int sensorId) {
this.sensorId = sensorId;
}
public int getDeviceId() {
return deviceId;
}
public void setDeviceId(int deviceId) {
this.deviceId = deviceId;
}
public String getSensorType() {
return sensorType;
}
public void setSensorType(String sensorType) {
this.sensorType = sensorType;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
@Override
public int hashCode() {
return Objects.hash(deviceId, sensorId, sensorType, status, timestamp, unit);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Sensor other = (Sensor) obj;
return deviceId == other.deviceId && sensorId == other.sensorId && Objects.equals(sensorType, other.sensorType)
&& status == other.status && timestamp == other.timestamp && Objects.equals(unit, other.unit);
}
@Override
public String toString() {
return "Sensor [sensorId=" + sensorId + ", deviceId=" + deviceId + ", sensorType=" + sensorType + ", unit="
+ unit + ", status=" + status + ", timestamp=" + timestamp + "]";
}
}

View File

@@ -0,0 +1,103 @@
package net.miarma.contaminus.database.entities;
import java.util.Objects;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Table;
@Table("weather_values")
public class WeatherValue {
private int valueId;
private int sensorId;
private float temperature;
private float humidity;
private long timestamp;
public WeatherValue() {}
public WeatherValue(Row row) {
this.valueId = row.getInteger("valueId");
this.sensorId = row.getInteger("sensorId");
this.temperature = row.getFloat("temperature");
this.humidity = row.getFloat("humidity");
this.timestamp = row.getLong("timestamp");
}
public WeatherValue(int valueId, int sensorId, float temperature, float humidity, long timestamp) {
super();
this.valueId = valueId;
this.sensorId = sensorId;
this.temperature = temperature;
this.humidity = humidity;
this.timestamp = timestamp;
}
public int getValueId() {
return valueId;
}
public void setValueId(int valueId) {
this.valueId = valueId;
}
public int getSensorId() {
return sensorId;
}
public void setSensorId(int sensorId) {
this.sensorId = sensorId;
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
@Override
public int hashCode() {
return Objects.hash(humidity, sensorId, temperature, timestamp, valueId);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
WeatherValue other = (WeatherValue) obj;
return Float.floatToIntBits(humidity) == Float.floatToIntBits(other.humidity) && sensorId == other.sensorId
&& Float.floatToIntBits(temperature) == Float.floatToIntBits(other.temperature)
&& timestamp == other.timestamp && valueId == other.valueId;
}
@Override
public String toString() {
return "WeatherValue [valueId=" + valueId + ", sensorId=" + sensorId + ", temperature=" + temperature
+ ", humidity=" + humidity + ", timestamp=" + timestamp + "]";
}
}

View File

@@ -1,403 +0,0 @@
package net.miarma.contaminus.server;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.Message;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonArray;
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 net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.Host;
import net.miarma.contaminus.database.QueryBuilder;
public class ApiVerticle extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("🟢 Iniciando ApiVerticle...");
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.GET_GROUPS).handler(this::getGroupsHandler);
router.route(HttpMethod.GET, Constants.GET_GROUP_BY_ID).handler(this::getGroupByIdHandler);
router.route(HttpMethod.GET, Constants.GET_GROUP_DEVICES).handler(this::getGroupDevicesHandler);
router.route(HttpMethod.POST, Constants.POST_GROUPS).handler(this::postGroupHandler);
router.route(HttpMethod.PUT, Constants.PUT_GROUP_BY_ID).handler(this::putGroupByIdHandler);
// Device Routes
router.route(HttpMethod.GET, Constants.GET_DEVICES).handler(this::getDevicesHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_BY_ID).handler(this::getDeviceByIdHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_SENSORS).handler(this::getDeviceSensorsHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_ACTUATORS).handler(this::getDeviceActuatorsHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_LATEST_VALUES).handler(this::getDeviceLatestValuesHandler);
router.route(HttpMethod.POST, Constants.POST_DEVICES).handler(this::postDeviceHandler);
router.route(HttpMethod.PUT, Constants.PUT_DEVICE_BY_ID).handler(this::putDeviceByIdHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_POLLUTION_MAP).handler(this::getPollutionMapHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_HISTORY).handler(this::getDeviceHistoryHandler);
// Sensor Routes
router.route(HttpMethod.GET, Constants.GET_SENSORS).handler(this::getSensorsHandler);
router.route(HttpMethod.GET, Constants.GET_SENSOR_BY_ID).handler(this::getSensorByIdHandler);
router.route(HttpMethod.GET, Constants.GET_SENSOR_VALUES).handler(this::getSensorValuesHandler);
router.route(HttpMethod.POST, Constants.POST_SENSORS).handler(this::postSensorHandler);
router.route(HttpMethod.PUT, Constants.PUT_SENSOR_BY_ID).handler(this::putSensorByIdHandler);
// Actuator Routes
router.route(HttpMethod.GET, Constants.GET_ACTUATORS).handler(this::getActuatorsHandler);
router.route(HttpMethod.GET, Constants.GET_ACTUATOR_BY_ID).handler(this::getActuatorByIdHandler);
router.route(HttpMethod.POST, Constants.POST_ACTUATORS).handler(this::postActuatorHandler);
router.route(HttpMethod.PUT, Constants.PUT_ACTUATOR_BY_ID).handler(this::putActuatorByIdHandler);
vertx.createHttpServer()
.requestHandler(router)
.listen(
Host.getApiPort(),
Host.getHost(),
result -> {
if (result.succeeded()) {
Constants.LOGGER.info(String.format(
"📡 ApiVerticle desplegado. (http://%s:%d)", Host.getHost(), Host.getApiPort()
));
startPromise.complete();
} else {
Constants.LOGGER.error("❌ Error al desplegar ApiVerticle", result.cause());
startPromise.fail(result.cause());
}
});
}
private void sendQuery(String query, RoutingContext context) {
vertx.eventBus().request("db.query", query, req -> {
if (req.succeeded()) {
Message<Object> msg = req.result();
JsonArray jsonArray = new JsonArray(msg.body().toString());
context.json(jsonArray);
} else {
context.fail(500, req.cause());
}
});
}
// Group Handlers
private void getGroupsHandler(RoutingContext context) {
String query = QueryBuilder.select("*").from("groups").build();
sendQuery(query, context);
}
private void getGroupByIdHandler(RoutingContext context) {
String groupId = context.request().getParam("groupId");
String query = QueryBuilder
.select("*")
.from("groups")
.where("groupId = ?", groupId)
.build();
sendQuery(query, context);
}
private void getGroupDevicesHandler(RoutingContext context) {
String groupId = context.request().getParam("groupId");
String query = QueryBuilder
.select("*")
.from("devices")
.where("groupId = ?", groupId)
.build();
sendQuery(query, context);
}
private void postGroupHandler(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
String groupName = body.getString("groupName");
String query = QueryBuilder
.insert("groups", "groupName")
.values(groupName)
.build();
sendQuery(query, context);
}
private void putGroupByIdHandler(RoutingContext context) {
String groupId = context.request().getParam("groupId");
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
String groupName = body.getString("groupName");
String query = QueryBuilder
.update("groups")
.set("groupName", groupName)
.where("groupId = ?", groupId)
.build();
sendQuery(query, context);
}
// Device Handlers
private void getDevicesHandler(RoutingContext context) {
String query = QueryBuilder.select("*").from("devices").build();
sendQuery(query, context);
}
private void getDeviceByIdHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("devices")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getDeviceSensorsHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("sensors")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getDeviceActuatorsHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("actuators")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getDeviceLatestValuesHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("v_latest_values")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void postDeviceHandler(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer groupId = body.getInteger("groupId");
String deviceName = body.getString("groupName");
String query = QueryBuilder
.insert("devices", "groupId", "deviceName")
.values(groupId, deviceName)
.build();
sendQuery(query, context);
}
private void putDeviceByIdHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer groupId = body.getInteger("groupId");
String deviceName = body.getString("deviceName");
String query = QueryBuilder
.update("devices")
.set("groupId", groupId)
.set("deviceName", deviceName)
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getPollutionMapHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("v_pollution_map")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getDeviceHistoryHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("v_sensor_history_by_device")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
// Sensor Handlers
private void getSensorsHandler(RoutingContext context) {
String query = QueryBuilder.select("*").from("sensors").build();
sendQuery(query, context);
}
private void getSensorByIdHandler(RoutingContext context) {
String sensorId = context.request().getParam("sensorId");
String query = QueryBuilder
.select("*")
.from("sensors")
.where("sensorId = ?", sensorId)
.build();
sendQuery(query, context);
}
private void getSensorValuesHandler(RoutingContext context) {
String sensorId = context.request().getParam("sensorId");
String query = QueryBuilder
.select("*")
.from("v_sensor_values")
.where("sensorId = ?", sensorId)
.build();
sendQuery(query, context);
}
private void postSensorHandler(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer deviceId = body.getInteger("deviceId");
String sensorType = body.getString("sensorType");
String unit = body.getString("unit");
Integer status = body.getInteger("status");
String query = QueryBuilder
.insert("sensors", "deviceId", "sensorType", "unit", "status")
.values(deviceId, sensorType, unit, status)
.build();
sendQuery(query, context);
}
private void putSensorByIdHandler(RoutingContext context) {
String sensorId = context.request().getParam("sensorId");
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer deviceId = body.getInteger("deviceId");
String sensorType = body.getString("sensorType");
String unit = body.getString("unit");
Integer status = body.getInteger("status");
String query = QueryBuilder
.update("sensors")
.set("deviceId", deviceId)
.set("sensorType", sensorType)
.set("unit", unit)
.set("status", status)
.where("sensorId = ?", sensorId)
.build();
sendQuery(query, context);
}
// Actuator Handlers
private void getActuatorsHandler(RoutingContext context) {
String query = QueryBuilder.select("*").from("actuators").build();
sendQuery(query, context);
}
private void getActuatorByIdHandler(RoutingContext context) {
String actuatorId = context.request().getParam("actuatorId");
String query = QueryBuilder
.select("*")
.from("actuators")
.where("actuatorId = ?", actuatorId)
.build();
sendQuery(query, context);
}
private void postActuatorHandler(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer deviceId = body.getInteger("deviceId");
Integer status = body.getInteger("status");
String query = QueryBuilder
.insert("actuators", "deviceId", "status")
.values(deviceId, status)
.build();
sendQuery(query, context);
}
private void putActuatorByIdHandler(RoutingContext context) {
String actuatorId = context.request().getParam("actuatorId");
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer deviceId = body.getInteger("deviceId");
Integer status = body.getInteger("status");
String query = QueryBuilder
.update("actuators")
.set("deviceId", deviceId)
.set("status", status)
.where("actuatorId = ?", actuatorId)
.build();
sendQuery(query, context);
}
}

View File

@@ -0,0 +1,145 @@
package net.miarma.contaminus.server;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
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.JDBCConnectOptions;
import io.vertx.jdbcclient.JDBCPool;
import io.vertx.sqlclient.PoolOptions;
import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.database.DatabaseManager;
@SuppressWarnings("unused")
public class DataLayerAPIVerticle extends AbstractVerticle {
private JDBCPool pool;
private DatabaseManager dbManager;
private ConfigManager configManager;
public DataLayerAPIVerticle(JDBCPool pool) {
this.pool = pool;
this.configManager = ConfigManager.getInstance();
}
@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());
// 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);
vertx.createHttpServer()
.requestHandler(router)
.listen(configManager.getDataApiPort(), configManager.getHost());
startPromise.complete();
}
private void getAllGroups(RoutingContext context) {
context.response().end("TODO");
}
private void getGroupById(RoutingContext context) {
context.response().end("TODO");
}
private void addGroup(RoutingContext context) {
context.response().end("TODO");
}
private void updateGroup(RoutingContext context) {
context.response().end("TODO");
}
private void getAllDevices(RoutingContext context) {
context.response().end("TODO");
}
private void getDeviceById(RoutingContext context) {
context.response().end("TODO");
}
private void addDevice(RoutingContext context) {
context.response().end("TODO");
}
private void updateDevice(RoutingContext context) {
context.response().end("TODO");
}
private void getAllSensors(RoutingContext context) {
context.response().end("TODO");
}
private void getSensorById(RoutingContext context) {
context.response().end("TODO");
}
private void addSensor(RoutingContext context) {
context.response().end("TODO");
}
private void updateSensor(RoutingContext context) {
context.response().end("TODO");
}
private void getAllActuators(RoutingContext context) {
context.response().end("TODO");
}
private void getActuatorById(RoutingContext context) {
context.response().end("TODO");
}
private void addActuator(RoutingContext context) {
context.response().end("TODO");
}
private void updateActuator(RoutingContext context) {
context.response().end("TODO");
}
}

View File

@@ -1,120 +0,0 @@
package net.miarma.contaminus.server;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonObject;
import io.vertx.jdbcclient.JDBCPool;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.LocalDateTimeSerializer;
import net.miarma.contaminus.database.DatabaseManager;
public class DatabaseVerticle extends AbstractVerticle {
private JDBCPool pool;
private EventBus eventBus;
private Gson gson = new GsonBuilder()
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer())
.create();
@SuppressWarnings("unused")
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("🟢 Iniciando DatabaseVerticle...");
DatabaseManager dbManager = new DatabaseManager(vertx);
pool = dbManager.getPool();
eventBus = vertx.eventBus();
pool.query("SELECT 1")
.execute()
.onSuccess(_res -> {
Constants.LOGGER.info("✅ Database connection ok");
Constants.LOGGER.info("📡 DatabaseVerticle desplegado");
startPromise.complete();
})
.onFailure(err -> {
Constants.LOGGER.error("❌ Database connection failed");
Constants.LOGGER.error("❌ Error al desplegar DatabaseVerticle", err);
startPromise.fail(err);
});
eventBus.consumer("db.query", this::handleDatabaseQuery);
}
private void handleDatabaseQuery(Message<String> msg) {
String query = msg.body();
Constants.LOGGER.info("📥 Query: " + query);
if(query == null || query.isEmpty()) {
msg.fail(400, "Empty query");
return;
}
if(query.startsWith("SELECT")) {
handleSelectQuery(query, msg);
} else if(query.startsWith("INSERT")) {
handleInsertQuery(query, msg);
} else if(query.startsWith("UPDATE")) {
handleUpdateQuery(query, msg);
} else {
msg.fail(400, "Invalid operation");
}
}
private void handleSelectQuery(String query, Message<String> msg) {
pool.query(query).execute()
.onSuccess(res -> {
List<Map<String, Object>> rowsList = new ArrayList<>();
for (Row row : res) {
Map<String, Object> rowMap = new HashMap<>();
for (int i = 0; i < row.size(); i++) {
String columnName = res.columnsNames().get(i);
Object columnValue = row.getValue(i);
rowMap.put(columnName, columnValue);
}
rowsList.add(rowMap);
}
String jsonResponse = gson.toJson(rowsList);
msg.reply(jsonResponse);
})
.onFailure(err -> msg.fail(500, err.getMessage()));
}
@SuppressWarnings("unused")
private void handleInsertQuery(String query, Message<String> msg) {
pool.query(query).execute()
.onSuccess(_res -> {
JsonObject response = new JsonObject();
response.put("status", "success");
String jsonResponse = gson.toJson(response);
msg.reply(jsonResponse);
})
.onFailure(err -> msg.fail(500, err.getMessage()));
}
@SuppressWarnings("unused")
private void handleUpdateQuery(String query, Message<String> msg) {
pool.query(query).execute()
.onSuccess(_res -> {
JsonObject response = new JsonObject();
response.put("status", "updated");
String jsonResponse = gson.toJson(response);
msg.reply(jsonResponse);
})
.onFailure(err -> msg.fail(500, err.getMessage()));
}
}

View File

@@ -1,39 +0,0 @@
package net.miarma.contaminus.server;
import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.StaticHandler;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.Host;
public class HttpServerVerticle extends AbstractVerticle {
@Override
public void start() {
Constants.LOGGER.info("🟢 Iniciando HttpServerVerticle...");
Router router = Router.router(vertx);
router.route("/*")
.handler(
StaticHandler.create("webroot")
.setDefaultContentEncoding("UTF-8")
);
router.route("/dashboard/*").handler(ctx -> {
ctx.reroute("/index.html");
});
vertx.createHttpServer().requestHandler(router).listen(
Host.getWebserverPort(), Host.getHost(), result -> {
if (result.succeeded()) {
Constants.LOGGER.info(String.format("📡 HttpServerVerticle desplegado. (http://%s:%d)",
Host.getHost(), Host.getWebserverPort())
);
} else {
Constants.LOGGER.error("❌ Error al desplegar HttpServerVerticle", result.cause());
}
}
);
}
}

View File

@@ -0,0 +1,88 @@
package net.miarma.contaminus.server;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpMethod;
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 net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
public class LogicLayerAPIVerticle extends AbstractVerticle {
private ConfigManager configManager;
public LogicLayerAPIVerticle() {
this.configManager = ConfigManager.getInstance();
}
@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());
// Group Routes
router.route(HttpMethod.GET, Constants.GET_GROUP_DEVICES).handler(this::getGroupDevices);
// Device Routes
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);
// Sensor Routes
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) {
context.response().end("TODO");
}
private void getDeviceSensors(RoutingContext context) {
context.response().end("TODO");
}
private void getDeviceActuators(RoutingContext context) {
context.response().end("TODO");
}
private void getDeviceLatestValues(RoutingContext context) {
context.response().end("TODO");
}
private void getDevicePollutionMap(RoutingContext context) {
context.response().end("TODO");
}
private void getDeviceHistory(RoutingContext context) {
context.response().end("TODO");
}
private void getSensorValues(RoutingContext context) {
context.response().end("TODO");
}
}

View File

@@ -1,39 +1,133 @@
package net.miarma.contaminus.server; package net.miarma.contaminus.server;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import io.vertx.core.json.JsonObject;
import io.vertx.jdbcclient.JDBCPool;
import io.vertx.core.AbstractVerticle; import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions; import io.vertx.core.DeploymentOptions;
import io.vertx.core.Launcher;
import io.vertx.core.Promise; import io.vertx.core.Promise;
import io.vertx.core.ThreadingModel; import io.vertx.core.ThreadingModel;
import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
public class MainVerticle extends AbstractVerticle { public class MainVerticle extends AbstractVerticle {
private ConfigManager configManager;
private JDBCPool pool;
public static void main(String[] args) {
Launcher.executeCommand("run", MainVerticle.class.getName());
}
@Override @SuppressWarnings("deprecation")
public void start(Promise<Void> startPromise) { private void init() {
final DeploymentOptions options = new DeploymentOptions(); configManager = ConfigManager.getInstance();
options.setThreadingModel(ThreadingModel.WORKER);
String jdbcUrl = configManager.getJdbcUrl();
String enabledVerticles = System.getProperty("vertx.options", ""); String dbUser = configManager.getStringProperty("db.user");
String dbPwd = configManager.getStringProperty("db.pwd");
if (enabledVerticles.contains("db")) { Integer poolSize = configManager.getIntProperty("db.poolSize");
getVertx().deployVerticle(new DatabaseVerticle(), options);
} JsonObject dbConfig = new JsonObject()
if (enabledVerticles.contains("api")) { .put("url", jdbcUrl)
getVertx().deployVerticle(new ApiVerticle(), options); .put("user", dbUser)
} .put("password", dbPwd)
if (enabledVerticles.contains("http")) { .put("max_pool_size", poolSize != null ? poolSize : 10);
getVertx().deployVerticle(new HttpServerVerticle());
} pool = JDBCPool.pool(vertx, dbConfig);
}
initializeDirectories();
@Override copyDefaultConfig();
public void stop(Promise<Void> stopPromise) throws Exception { }
getVertx().deploymentIDs()
.forEach(v -> getVertx().undeploy(v)); private void initializeDirectories() {
} File baseDir = new File(this.configManager.getBaseDir());
if (!baseDir.exists()) {
public static void main(String[] args) { baseDir.mkdirs();
System.setProperty("vertx.options", String.join(",", args)); }
io.vertx.core.Launcher.executeCommand("run", MainVerticle.class.getName()); }
}
private void copyDefaultConfig() {
} File configFile = new File(configManager.getConfigFile().getAbsolutePath());
if (!configFile.exists()) {
try (InputStream in = MainVerticle.class.getClassLoader().getResourceAsStream("default.properties")) {
if (in != null) {
Files.copy(in, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} else {
Constants.LOGGER.error("🔴 Default config file not found in resources");
}
} catch (IOException e) {
Constants.LOGGER.error("🔴 Failed to copy default config file", e);
}
}
}
@Override
public void start(Promise<Void> startPromise) {
try {
System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager");
init();
pool.query("SELECT 1").execute(ar -> {
if (ar.succeeded()) {
Constants.LOGGER.info("🟢 Connected to DB");
deployVerticles(startPromise);
} else {
Constants.LOGGER.error("🔴 Failed to connect to DB: " + ar.cause());
startPromise.fail(ar.cause());
}
});
} catch (Exception e) {
System.err.println("🔴 Error starting the application: " + e);
startPromise.fail(e);
}
}
private void deployVerticles(Promise<Void> startPromise) {
final DeploymentOptions options = new DeploymentOptions();
options.setThreadingModel(ThreadingModel.WORKER);
vertx.deployVerticle(new DataLayerAPIVerticle(pool), options, result -> {
if (result.succeeded()) {
Constants.LOGGER.info("🟢 DatabaseVerticle desplegado");
Constants.LOGGER.info("\t🔗 API URL: " + configManager.getHost()
+ ":" + configManager.getDataApiPort());
} else {
Constants.LOGGER.error("🔴 Error deploying DataLayerAPIVerticle: " + result.cause());
}
});
vertx.deployVerticle(new LogicLayerAPIVerticle(), options, result -> {
if (result.succeeded()) {
Constants.LOGGER.info("🟢 LogicLayerAPIVerticle desplegado");
Constants.LOGGER.info("\t🔗 API URL: " + configManager.getHost()
+ ":" + configManager.getLogicApiPort());
} else {
Constants.LOGGER.error("🔴 Error deploying LogicLayerAPIVerticle: " + result.cause());
}
});
vertx.deployVerticle(new WebServerVerticle(), result -> {
if (result.succeeded()) {
Constants.LOGGER.info("🟢 WebServerVerticle desplegado");
Constants.LOGGER.info("\t🔗 WEB SERVER URL: " + configManager.getHost()
+ ":" + configManager.getWebserverPort());
} else {
Constants.LOGGER.error("🔴 Error deploying WebServerVerticle: " + result.cause());
}
});
startPromise.complete();
}
@Override
public void stop(Promise<Void> stopPromise) throws Exception {
getVertx().deploymentIDs()
.forEach(v -> getVertx().undeploy(v));
}
}

View File

@@ -0,0 +1,39 @@
package net.miarma.contaminus.server;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.StaticHandler;
import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
public class WebServerVerticle extends AbstractVerticle {
private ConfigManager configManager;
public WebServerVerticle() {
configManager = ConfigManager.getInstance();
}
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("📡 Iniciando WebServerVerticle...");
Router router = Router.router(vertx);
router.route("/*")
.handler(
StaticHandler.create("webroot")
.setDefaultContentEncoding("UTF-8")
);
router.route("/dashboard/*").handler(ctx -> {
ctx.reroute("/index.html");
});
vertx.createHttpServer()
.requestHandler(router)
.listen(configManager.getWebserverPort(), configManager.getHost());
startPromise.complete();
}
}

View File

@@ -1,5 +1,5 @@
# DB Configuration # DB Configuration
db.protocol=jdbc:mariadb: db.protocol=jdbc:mariadb
db.host=localhost db.host=localhost
db.port=3306 db.port=3306
db.name=dad db.name=dad
@@ -7,7 +7,8 @@ db.user=root
db.pwd=root db.pwd=root
dp.poolSize=5 dp.poolSize=5
# Server Configuration # HTTP Server Configuration
inet.host=localhost inet.host=localhost
webserver.port=8080 webserver.port=8080
api.port=8081 data-api.port=8081
logic-api.port=8082

View File

@@ -7,7 +7,7 @@
}, },
"appConfig": { "appConfig": {
"endpoints": { "endpoints": {
"BASE_URL": "http://localhost:8081/api/v1", "BASE_URL": "http://localhost:3001/api/v1",
"GET_GROUPS": "/groups", "GET_GROUPS": "/groups",
"GET_GROUP_BY_ID": "/groups/{0}", "GET_GROUP_BY_ID": "/groups/{0}",
"GET_GROUP_DEVICES": "/groups/{0}/devices", "GET_GROUP_DEVICES": "/groups/{0}/devices",