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