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.polling.AcquisitionPollResult;
|
||||||
import com.litoralregas.backend.acquisition.block.BlockPollingService;
|
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.dashboard.DashboardOverviewWebSocketPublisher;
|
||||||
import com.litoralregas.backend.websocket.telemetry.TelemetryWebSocketPublisher;
|
import com.litoralregas.backend.websocket.telemetry.TelemetryWebSocketPublisher;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
@@ -20,6 +21,7 @@ public class AcquisitionSchedulerService {
|
|||||||
private final TaskScheduler taskScheduler;
|
private final TaskScheduler taskScheduler;
|
||||||
private final TelemetryWebSocketPublisher telemetryWebSocketPublisher;
|
private final TelemetryWebSocketPublisher telemetryWebSocketPublisher;
|
||||||
private final DashboardOverviewWebSocketPublisher dashboardOverviewWebSocketPublisher;
|
private final DashboardOverviewWebSocketPublisher dashboardOverviewWebSocketPublisher;
|
||||||
|
private final MeteoModuleWebSocketPublisher meteoModuleWebSocketPublisher;
|
||||||
|
|
||||||
private final AcquisitionRuntimeStatus runtimeStatus = new AcquisitionRuntimeStatus();
|
private final AcquisitionRuntimeStatus runtimeStatus = new AcquisitionRuntimeStatus();
|
||||||
|
|
||||||
@@ -30,13 +32,15 @@ public class AcquisitionSchedulerService {
|
|||||||
AcquisitionSchedulerProperties properties,
|
AcquisitionSchedulerProperties properties,
|
||||||
@Qualifier("acquisitionTaskScheduler") TaskScheduler taskScheduler,
|
@Qualifier("acquisitionTaskScheduler") TaskScheduler taskScheduler,
|
||||||
TelemetryWebSocketPublisher telemetryWebSocketPublisher,
|
TelemetryWebSocketPublisher telemetryWebSocketPublisher,
|
||||||
DashboardOverviewWebSocketPublisher dashboardOverviewWebSocketPublisher
|
DashboardOverviewWebSocketPublisher dashboardOverviewWebSocketPublisher,
|
||||||
|
MeteoModuleWebSocketPublisher meteoModuleWebSocketPublisher
|
||||||
) {
|
) {
|
||||||
this.blockPollingService = blockPollingService;
|
this.blockPollingService = blockPollingService;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.taskScheduler = taskScheduler;
|
this.taskScheduler = taskScheduler;
|
||||||
this.telemetryWebSocketPublisher = telemetryWebSocketPublisher;
|
this.telemetryWebSocketPublisher = telemetryWebSocketPublisher;
|
||||||
this.dashboardOverviewWebSocketPublisher = dashboardOverviewWebSocketPublisher;
|
this.dashboardOverviewWebSocketPublisher = dashboardOverviewWebSocketPublisher;
|
||||||
|
this.meteoModuleWebSocketPublisher = meteoModuleWebSocketPublisher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@@ -76,6 +80,7 @@ public class AcquisitionSchedulerService {
|
|||||||
|
|
||||||
telemetryWebSocketPublisher.publishLatestTelemetry();
|
telemetryWebSocketPublisher.publishLatestTelemetry();
|
||||||
dashboardOverviewWebSocketPublisher.publishOverview();
|
dashboardOverviewWebSocketPublisher.publishOverview();
|
||||||
|
meteoModuleWebSocketPublisher.publishLatest();
|
||||||
|
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
runtimeStatus.setLastError(exception.getMessage());
|
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