feat: add decoded fact model
This commit is contained in:
@@ -0,0 +1,215 @@
|
|||||||
|
package com.lingniu.ingest.facts;
|
||||||
|
|
||||||
|
import com.lingniu.ingest.api.ProtocolId;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public sealed interface DecodedFact
|
||||||
|
permits DecodedFact.Location,
|
||||||
|
DecodedFact.Realtime,
|
||||||
|
DecodedFact.Alarm,
|
||||||
|
DecodedFact.Session,
|
||||||
|
DecodedFact.Register,
|
||||||
|
DecodedFact.Extension {
|
||||||
|
|
||||||
|
String factId();
|
||||||
|
String frameId();
|
||||||
|
ProtocolId protocol();
|
||||||
|
String vehicleKey();
|
||||||
|
String vin();
|
||||||
|
String phone();
|
||||||
|
Instant eventTime();
|
||||||
|
Instant receivedAt();
|
||||||
|
String rawUri();
|
||||||
|
Map<String, String> metadata();
|
||||||
|
|
||||||
|
record Location(
|
||||||
|
String factId,
|
||||||
|
String frameId,
|
||||||
|
ProtocolId protocol,
|
||||||
|
String vehicleKey,
|
||||||
|
String vin,
|
||||||
|
String phone,
|
||||||
|
Instant eventTime,
|
||||||
|
Instant receivedAt,
|
||||||
|
String rawUri,
|
||||||
|
Map<String, String> metadata,
|
||||||
|
Double longitude,
|
||||||
|
Double latitude,
|
||||||
|
Double altitudeM,
|
||||||
|
Double speedKmh,
|
||||||
|
Double directionDeg,
|
||||||
|
Long alarmFlag,
|
||||||
|
Long statusFlag,
|
||||||
|
Double totalMileageKm
|
||||||
|
) implements DecodedFact {
|
||||||
|
public Location {
|
||||||
|
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||||
|
vin = BaseFields.clean(vin);
|
||||||
|
phone = BaseFields.clean(phone);
|
||||||
|
metadata = BaseFields.metadata(metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record Realtime(
|
||||||
|
String factId,
|
||||||
|
String frameId,
|
||||||
|
ProtocolId protocol,
|
||||||
|
String vehicleKey,
|
||||||
|
String vin,
|
||||||
|
String phone,
|
||||||
|
Instant eventTime,
|
||||||
|
Instant receivedAt,
|
||||||
|
String rawUri,
|
||||||
|
Map<String, String> metadata,
|
||||||
|
Map<String, String> fields
|
||||||
|
) implements DecodedFact {
|
||||||
|
public Realtime {
|
||||||
|
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||||
|
vin = BaseFields.clean(vin);
|
||||||
|
phone = BaseFields.clean(phone);
|
||||||
|
metadata = BaseFields.metadata(metadata);
|
||||||
|
fields = fields == null ? Map.of() : Map.copyOf(fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record Alarm(
|
||||||
|
String factId,
|
||||||
|
String frameId,
|
||||||
|
ProtocolId protocol,
|
||||||
|
String vehicleKey,
|
||||||
|
String vin,
|
||||||
|
String phone,
|
||||||
|
Instant eventTime,
|
||||||
|
Instant receivedAt,
|
||||||
|
String rawUri,
|
||||||
|
Map<String, String> metadata,
|
||||||
|
String alarmLevel,
|
||||||
|
Long alarmCode,
|
||||||
|
String alarmName,
|
||||||
|
Map<String, String> fields
|
||||||
|
) implements DecodedFact {
|
||||||
|
public Alarm {
|
||||||
|
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||||
|
vin = BaseFields.clean(vin);
|
||||||
|
phone = BaseFields.clean(phone);
|
||||||
|
alarmLevel = BaseFields.clean(alarmLevel);
|
||||||
|
alarmName = BaseFields.clean(alarmName);
|
||||||
|
metadata = BaseFields.metadata(metadata);
|
||||||
|
fields = fields == null ? Map.of() : Map.copyOf(fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record Session(
|
||||||
|
String factId,
|
||||||
|
String frameId,
|
||||||
|
ProtocolId protocol,
|
||||||
|
String vehicleKey,
|
||||||
|
String vin,
|
||||||
|
String phone,
|
||||||
|
Instant eventTime,
|
||||||
|
Instant receivedAt,
|
||||||
|
String rawUri,
|
||||||
|
Map<String, String> metadata,
|
||||||
|
String sessionType,
|
||||||
|
Integer resultCode,
|
||||||
|
Map<String, String> fields
|
||||||
|
) implements DecodedFact {
|
||||||
|
public Session {
|
||||||
|
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||||
|
vin = BaseFields.clean(vin);
|
||||||
|
phone = BaseFields.clean(phone);
|
||||||
|
sessionType = BaseFields.clean(sessionType);
|
||||||
|
metadata = BaseFields.metadata(metadata);
|
||||||
|
fields = fields == null ? Map.of() : Map.copyOf(fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record Register(
|
||||||
|
String factId,
|
||||||
|
String frameId,
|
||||||
|
ProtocolId protocol,
|
||||||
|
String vehicleKey,
|
||||||
|
String vin,
|
||||||
|
String phone,
|
||||||
|
Instant eventTime,
|
||||||
|
Instant receivedAt,
|
||||||
|
String rawUri,
|
||||||
|
Map<String, String> metadata,
|
||||||
|
Map<String, String> registrationFields
|
||||||
|
) implements DecodedFact {
|
||||||
|
public Register {
|
||||||
|
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||||
|
vin = BaseFields.clean(vin);
|
||||||
|
phone = BaseFields.clean(phone);
|
||||||
|
metadata = BaseFields.metadata(metadata);
|
||||||
|
registrationFields = registrationFields == null ? Map.of() : Map.copyOf(registrationFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record Extension(
|
||||||
|
String factId,
|
||||||
|
String frameId,
|
||||||
|
ProtocolId protocol,
|
||||||
|
String vehicleKey,
|
||||||
|
String vin,
|
||||||
|
String phone,
|
||||||
|
Instant eventTime,
|
||||||
|
Instant receivedAt,
|
||||||
|
String rawUri,
|
||||||
|
Map<String, String> metadata,
|
||||||
|
String extensionType,
|
||||||
|
Map<String, String> fields
|
||||||
|
) implements DecodedFact {
|
||||||
|
public Extension {
|
||||||
|
BaseFields.validate(factId, frameId, protocol, vehicleKey, eventTime, receivedAt, rawUri);
|
||||||
|
vin = BaseFields.clean(vin);
|
||||||
|
phone = BaseFields.clean(phone);
|
||||||
|
extensionType = BaseFields.clean(extensionType);
|
||||||
|
metadata = BaseFields.metadata(metadata);
|
||||||
|
fields = fields == null ? Map.of() : Map.copyOf(fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class BaseFields {
|
||||||
|
private BaseFields() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void validate(String factId,
|
||||||
|
String frameId,
|
||||||
|
ProtocolId protocol,
|
||||||
|
String vehicleKey,
|
||||||
|
Instant eventTime,
|
||||||
|
Instant receivedAt,
|
||||||
|
String rawUri) {
|
||||||
|
required(factId, "factId");
|
||||||
|
required(frameId, "frameId");
|
||||||
|
if (protocol == null) {
|
||||||
|
throw new IllegalArgumentException("protocol must not be null");
|
||||||
|
}
|
||||||
|
required(vehicleKey, "vehicleKey");
|
||||||
|
if (eventTime == null) {
|
||||||
|
throw new IllegalArgumentException("eventTime must not be null");
|
||||||
|
}
|
||||||
|
if (receivedAt == null) {
|
||||||
|
throw new IllegalArgumentException("receivedAt must not be null");
|
||||||
|
}
|
||||||
|
required(rawUri, "rawUri");
|
||||||
|
}
|
||||||
|
|
||||||
|
static String clean(String value) {
|
||||||
|
return value == null ? "" : value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, String> metadata(Map<String, String> metadata) {
|
||||||
|
return metadata == null ? Map.of() : Map.copyOf(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void required(String value, String field) {
|
||||||
|
if (clean(value).isBlank()) {
|
||||||
|
throw new IllegalArgumentException(field + " must not be blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.lingniu.ingest.facts;
|
||||||
|
|
||||||
|
import com.lingniu.ingest.api.ProtocolId;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class DecodedFactTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void locationFactCarriesRawReferenceAndMetadata() {
|
||||||
|
DecodedFact.Location location = new DecodedFact.Location(
|
||||||
|
"fact-1",
|
||||||
|
"frame-1",
|
||||||
|
ProtocolId.JT808,
|
||||||
|
"jt808:013912345678",
|
||||||
|
"",
|
||||||
|
"013912345678",
|
||||||
|
Instant.parse("2026-06-29T00:00:00Z"),
|
||||||
|
Instant.parse("2026-06-29T00:00:01Z"),
|
||||||
|
"archive://jt808/frame-1.bin",
|
||||||
|
Map.of("messageId", "0x0200"),
|
||||||
|
120.123456,
|
||||||
|
30.123456,
|
||||||
|
10.0,
|
||||||
|
42.0,
|
||||||
|
180.0,
|
||||||
|
0L,
|
||||||
|
2L,
|
||||||
|
1000.5);
|
||||||
|
|
||||||
|
assertThat(location.factId()).isEqualTo("fact-1");
|
||||||
|
assertThat(location.rawUri()).isEqualTo("archive://jt808/frame-1.bin");
|
||||||
|
assertThat(location.metadata()).containsEntry("messageId", "0x0200");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user