Add meteo module API and websocket stream
This commit is contained in:
+6
-1
@@ -2,6 +2,7 @@ package com.litoralregas.backend.acquisition.scheduler;
|
||||
|
||||
import com.litoralregas.backend.acquisition.polling.AcquisitionPollResult;
|
||||
import com.litoralregas.backend.acquisition.block.BlockPollingService;
|
||||
import com.litoralregas.backend.modules.meteo.websocket.MeteoModuleWebSocketPublisher;
|
||||
import com.litoralregas.backend.websocket.dashboard.DashboardOverviewWebSocketPublisher;
|
||||
import com.litoralregas.backend.websocket.telemetry.TelemetryWebSocketPublisher;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
@@ -20,6 +21,7 @@ public class AcquisitionSchedulerService {
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final TelemetryWebSocketPublisher telemetryWebSocketPublisher;
|
||||
private final DashboardOverviewWebSocketPublisher dashboardOverviewWebSocketPublisher;
|
||||
private final MeteoModuleWebSocketPublisher meteoModuleWebSocketPublisher;
|
||||
|
||||
private final AcquisitionRuntimeStatus runtimeStatus = new AcquisitionRuntimeStatus();
|
||||
|
||||
@@ -30,13 +32,15 @@ public class AcquisitionSchedulerService {
|
||||
AcquisitionSchedulerProperties properties,
|
||||
@Qualifier("acquisitionTaskScheduler") TaskScheduler taskScheduler,
|
||||
TelemetryWebSocketPublisher telemetryWebSocketPublisher,
|
||||
DashboardOverviewWebSocketPublisher dashboardOverviewWebSocketPublisher
|
||||
DashboardOverviewWebSocketPublisher dashboardOverviewWebSocketPublisher,
|
||||
MeteoModuleWebSocketPublisher meteoModuleWebSocketPublisher
|
||||
) {
|
||||
this.blockPollingService = blockPollingService;
|
||||
this.properties = properties;
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.telemetryWebSocketPublisher = telemetryWebSocketPublisher;
|
||||
this.dashboardOverviewWebSocketPublisher = dashboardOverviewWebSocketPublisher;
|
||||
this.meteoModuleWebSocketPublisher = meteoModuleWebSocketPublisher;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
@@ -76,6 +80,7 @@ public class AcquisitionSchedulerService {
|
||||
|
||||
telemetryWebSocketPublisher.publishLatestTelemetry();
|
||||
dashboardOverviewWebSocketPublisher.publishOverview();
|
||||
meteoModuleWebSocketPublisher.publishLatest();
|
||||
|
||||
} catch (Exception exception) {
|
||||
runtimeStatus.setLastError(exception.getMessage());
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.litoralregas.backend.modules.meteo;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class MeteoModuleController {
|
||||
|
||||
private final MeteoModuleService meteoModuleService;
|
||||
|
||||
public MeteoModuleController(MeteoModuleService meteoModuleService) {
|
||||
this.meteoModuleService = meteoModuleService;
|
||||
}
|
||||
|
||||
@GetMapping("/api/modules/meteo")
|
||||
public MeteoModuleResponse getLatest() {
|
||||
return meteoModuleService.getLatest();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.litoralregas.backend.modules.meteo;
|
||||
|
||||
import com.litoralregas.backend.modules.shared.ModuleSensorResponse;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public record MeteoModuleResponse(
|
||||
Instant timestamp,
|
||||
Integer sensorCount,
|
||||
List<ModuleSensorResponse> sensors
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.litoralregas.backend.modules.meteo;
|
||||
|
||||
import com.litoralregas.backend.acquisition.telemetry.TelemetryCache;
|
||||
import com.litoralregas.backend.acquisition.telemetry.TelemetrySnapshot;
|
||||
import com.litoralregas.backend.modules.shared.ModuleSensorResponse;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class MeteoModuleService {
|
||||
|
||||
private final TelemetryCache telemetryCache;
|
||||
|
||||
public MeteoModuleService(TelemetryCache telemetryCache) {
|
||||
this.telemetryCache = telemetryCache;
|
||||
}
|
||||
|
||||
public MeteoModuleResponse getLatest() {
|
||||
List<ModuleSensorResponse> sensors = telemetryCache.getAll()
|
||||
.stream()
|
||||
.filter(this::isMeteoSensor)
|
||||
.sorted(Comparator.comparing(TelemetrySnapshot::sensorId))
|
||||
.map(this::toResponse)
|
||||
.toList();
|
||||
|
||||
return new MeteoModuleResponse(
|
||||
Instant.now(),
|
||||
sensors.size(),
|
||||
sensors
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isMeteoSensor(TelemetrySnapshot snapshot) {
|
||||
String name = normalize(snapshot.name());
|
||||
|
||||
return name.contains("exterior")
|
||||
|| name.contains("vento")
|
||||
|| name.contains("radiacao")
|
||||
|| name.contains("chuva");
|
||||
}
|
||||
|
||||
private ModuleSensorResponse toResponse(TelemetrySnapshot snapshot) {
|
||||
return new ModuleSensorResponse(
|
||||
snapshot.sensorId(),
|
||||
snapshot.name(),
|
||||
buildKey(snapshot.name()),
|
||||
snapshot.value(),
|
||||
snapshot.unit(),
|
||||
snapshot.modbusAddress(),
|
||||
snapshot.bitOffset(),
|
||||
snapshot.timestamp()
|
||||
);
|
||||
}
|
||||
|
||||
private String buildKey(String name) {
|
||||
return normalize(name)
|
||||
.replaceAll("[^a-z0-9]+", ".")
|
||||
.replaceAll("^\\.|\\.$", "");
|
||||
}
|
||||
|
||||
private String normalize(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return value
|
||||
.toLowerCase()
|
||||
.replace("ç", "c")
|
||||
.replace("ã", "a")
|
||||
.replace("á", "a")
|
||||
.replace("à", "a")
|
||||
.replace("é", "e")
|
||||
.replace("ê", "e")
|
||||
.replace("í", "i")
|
||||
.replace("ó", "o")
|
||||
.replace("õ", "o")
|
||||
.replace("ú", "u");
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.litoralregas.backend.modules.meteo.websocket;
|
||||
|
||||
import com.litoralregas.backend.modules.meteo.MeteoModuleResponse;
|
||||
import com.litoralregas.backend.modules.meteo.MeteoModuleService;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MeteoModuleWebSocketPublisher {
|
||||
|
||||
private static final String DESTINATION = "/topic/modules/meteo/latest";
|
||||
|
||||
private final SimpMessagingTemplate messagingTemplate;
|
||||
private final MeteoModuleService meteoModuleService;
|
||||
|
||||
public MeteoModuleWebSocketPublisher(
|
||||
SimpMessagingTemplate messagingTemplate,
|
||||
MeteoModuleService meteoModuleService
|
||||
) {
|
||||
this.messagingTemplate = messagingTemplate;
|
||||
this.meteoModuleService = meteoModuleService;
|
||||
}
|
||||
|
||||
public void publishLatest() {
|
||||
System.out.println("Publishing meteo module websocket");
|
||||
MeteoModuleResponse response = meteoModuleService.getLatest();
|
||||
messagingTemplate.convertAndSend(DESTINATION, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.litoralregas.backend.modules.shared;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public record ModuleSensorResponse(
|
||||
Integer sensorId,
|
||||
String name,
|
||||
String key,
|
||||
Object value,
|
||||
String unit,
|
||||
Integer modbusAddress,
|
||||
Integer bitOffset,
|
||||
Instant timestamp
|
||||
) {
|
||||
}
|
||||
Reference in New Issue
Block a user