Add manual telemetry reading and cache
This commit is contained in:
@@ -0,0 +1,82 @@
|
|||||||
|
package com.litoralregas.backend.acquisition;
|
||||||
|
|
||||||
|
import com.litoralregas.backend.modbus.LrModbusClient;
|
||||||
|
import com.litoralregas.backend.modbus.ModbusReadResult;
|
||||||
|
import com.litoralregas.backend.modbus.ModbusUnit;
|
||||||
|
import com.litoralregas.backend.sensor.SensorDefinition;
|
||||||
|
import com.litoralregas.backend.sensor.SensorDefinitionRepository;
|
||||||
|
import com.litoralregas.backend.sensor.SensorSourceType;
|
||||||
|
import com.litoralregas.backend.sensor.SensorValueType;
|
||||||
|
import jakarta.persistence.EntityNotFoundException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SensorTelemetryReader {
|
||||||
|
|
||||||
|
private final SensorDefinitionRepository sensorDefinitionRepository;
|
||||||
|
private final LrModbusClient modbusClient;
|
||||||
|
private final TelemetryCache telemetryCache;
|
||||||
|
|
||||||
|
public SensorTelemetryReader(
|
||||||
|
SensorDefinitionRepository sensorDefinitionRepository,
|
||||||
|
LrModbusClient modbusClient,
|
||||||
|
TelemetryCache telemetryCache
|
||||||
|
) {
|
||||||
|
this.sensorDefinitionRepository = sensorDefinitionRepository;
|
||||||
|
this.modbusClient = modbusClient;
|
||||||
|
this.telemetryCache = telemetryCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TelemetrySnapshot readSensor(Integer sensorId) {
|
||||||
|
SensorDefinition sensorDefinition = sensorDefinitionRepository.findById(sensorId)
|
||||||
|
.orElseThrow(() -> new EntityNotFoundException("Sensor definition not found: " + sensorId));
|
||||||
|
|
||||||
|
if (sensorDefinition.getSourceType() != SensorSourceType.MODBUS) {
|
||||||
|
throw new IllegalArgumentException("Only MODBUS sensors can be read directly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusReadResult result = modbusClient.readInputRegisters(
|
||||||
|
ModbusUnit.PC,
|
||||||
|
sensorDefinition.getModbusAddress(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
Integer rawValue = result.values().getFirst();
|
||||||
|
Object value = convertValue(sensorDefinition, rawValue);
|
||||||
|
|
||||||
|
TelemetrySnapshot snapshot = new TelemetrySnapshot(
|
||||||
|
sensorDefinition.getId(),
|
||||||
|
sensorDefinition.getName(),
|
||||||
|
sensorDefinition.getModbusAddress(),
|
||||||
|
sensorDefinition.getBitOffset(),
|
||||||
|
rawValue,
|
||||||
|
value,
|
||||||
|
sensorDefinition.getUnit(),
|
||||||
|
Instant.now()
|
||||||
|
);
|
||||||
|
|
||||||
|
telemetryCache.put(snapshot);
|
||||||
|
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object convertValue(SensorDefinition sensorDefinition, Integer rawValue) {
|
||||||
|
if (sensorDefinition.getValueType() == SensorValueType.BOOLEAN) {
|
||||||
|
Integer bitOffset = sensorDefinition.getBitOffset();
|
||||||
|
|
||||||
|
if (bitOffset == null) {
|
||||||
|
throw new IllegalStateException("BOOLEAN sensor requires bitOffset.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((rawValue >> bitOffset) & 1) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sensorDefinition.getValueType() == SensorValueType.DECIMAL) {
|
||||||
|
return rawValue / Math.pow(10, sensorDefinition.getDecimalPlaces());
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.litoralregas.backend.acquisition;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class TelemetryCache {
|
||||||
|
|
||||||
|
private final Map<Integer, TelemetrySnapshot> snapshotsBySensorId = new ConcurrentHashMap<>(); //Thread-safe ;)
|
||||||
|
|
||||||
|
public void put(TelemetrySnapshot snapshot) {
|
||||||
|
snapshotsBySensorId.put(snapshot.sensorId(), snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<TelemetrySnapshot> get(Integer sensorId) {
|
||||||
|
return Optional.ofNullable(snapshotsBySensorId.get(sensorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<TelemetrySnapshot> getAll() {
|
||||||
|
return snapshotsBySensorId.values()
|
||||||
|
.stream()
|
||||||
|
.sorted(Comparator.comparing(TelemetrySnapshot::sensorId))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
snapshotsBySensorId.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return snapshotsBySensorId.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.litoralregas.backend.acquisition;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class TelemetryController {
|
||||||
|
|
||||||
|
private final SensorTelemetryReader sensorTelemetryReader;
|
||||||
|
|
||||||
|
public TelemetryController(SensorTelemetryReader sensorTelemetryReader) {
|
||||||
|
this.sensorTelemetryReader = sensorTelemetryReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/api/telemetry/sensors/{sensorId}")
|
||||||
|
public TelemetrySnapshot readSensor(@PathVariable Integer sensorId) {
|
||||||
|
return sensorTelemetryReader.readSensor(sensorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.litoralregas.backend.acquisition;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
public record TelemetrySnapshot(
|
||||||
|
Integer sensorId,
|
||||||
|
String name,
|
||||||
|
Integer modbusAddress,
|
||||||
|
Integer bitOffset,
|
||||||
|
Integer rawValue,
|
||||||
|
Object value,
|
||||||
|
String unit,
|
||||||
|
Instant timestamp
|
||||||
|
) {
|
||||||
|
}
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
package com.litoralregas.backend.sensor.importer;
|
|
||||||
|
|
||||||
import com.litoralregas.backend.sensor.dto.SensorDefinitionImportRow;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
public class SensorDefinitionImportTestController {
|
|
||||||
|
|
||||||
private final SensorDefinitionMapParser parser;
|
|
||||||
|
|
||||||
public SensorDefinitionImportTestController(SensorDefinitionMapParser parser) {
|
|
||||||
this.parser = parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/api/sensor-definition-import/parse-line")
|
|
||||||
public SensorDefinitionImportRow parseLine(@RequestParam String line) {
|
|
||||||
return parser.parseLine(line)
|
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Line is empty."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user