diff --git a/packages/db/.env.example b/packages/db/.env.example deleted file mode 100644 index 017d25ca..00000000 --- a/packages/db/.env.example +++ /dev/null @@ -1,6 +0,0 @@ -POSTGRES_PASSWORD=postgres -POSTGRES_USER=postgres -DB_NAME=postgres -DB_HOST=localhost -DB_PORT=5432 -NODE_ENV=development \ No newline at end of file diff --git a/packages/db/src/entities/B3.entity.ts b/packages/db/src/entities/B3.entity.ts deleted file mode 100644 index f890f550..00000000 --- a/packages/db/src/entities/B3.entity.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * B3 (Button Board 3) sensor data hypertable - * Related to telemetry_metadata via timestamp + rfid - */ -@Entity("b3") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -export class B3 { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - @Column({ type: "float" }) - Acceleration!: number; - - @Column({ type: "boolean" }) - B3Heartbeat!: boolean; - - @Column({ type: "boolean" }) - BrakeLightSignalStatus!: boolean; - - @Column({ type: "boolean" }) - BrakeSwitchDigital!: boolean; - - @Column({ type: "boolean" }) - DaytimeRunningLightSignalStatus!: boolean; - - @Column({ type: "boolean" }) - ForwardDigital!: boolean; - - @Column({ type: "boolean" }) - HandbrakeSwitchDigital!: boolean; - - @Column({ type: "boolean" }) - HazardLightsInput!: boolean; - - @Column({ type: "boolean" }) - HeadlightsSwitchInput!: boolean; - - @Column({ type: "boolean" }) - HeadlightSignalStatus!: boolean; - - @Column({ type: "boolean" }) - HornSignalStatus!: boolean; - - @Column({ type: "boolean" }) - HornSwitchDigital!: boolean; - - @Column({ type: "boolean" }) - LapDigital!: boolean; - - @Column({ type: "boolean" }) - LeftSignalInput!: boolean; - - @Column({ type: "boolean" }) - LeftSignalStatus!: boolean; - - @Column({ type: "boolean" }) - MotorResetDigital!: boolean; - - @Column({ type: "boolean" }) - NeutralDigital!: boolean; - - @Column({ type: "boolean" }) - RaceModeDigital!: boolean; - - @Column({ type: "float" }) - RegenBraking!: number; - - @Column({ type: "boolean" }) - ReverseDigital!: boolean; - - @Column({ type: "boolean" }) - RightSignalInput!: boolean; - - @Column({ type: "boolean" }) - RightSignalStatus!: boolean; -} diff --git a/packages/db/src/entities/Battery.entity.ts b/packages/db/src/entities/Battery.entity.ts deleted file mode 100644 index 609b6660..00000000 --- a/packages/db/src/entities/Battery.entity.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * Battery sensor data hypertable - * Related to telemetry_metadata via timestamp + rfid - */ -@Entity("battery") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -export class Battery { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - @Column({ type: "boolean" }) - AlwaysOnSignalStatus!: boolean; - - @Column({ type: "float" }) - AverageCellVoltage!: number; - - @Column({ type: "float" }) - AverageTemperature!: number; - - @Column({ type: "integer" }) - BmuAlive!: number; - - @Column({ type: "boolean" }) - ChargeRelayEnabled!: boolean; - - @Column({ type: "boolean" }) - ChargerSafetyEnabled!: boolean; - - @Column({ type: "boolean" }) - DischargeRelayEnabled!: boolean; - - @Column({ type: "float" }) - FanSpeed!: number; - - @Column({ type: "float" }) - FanVoltage!: number; - - @Column({ type: "float" }) - HighCellVoltage!: number; - - @Column({ type: "integer" }) - HighCellVoltageId!: number; - - @Column({ type: "float" }) - HighTemperature!: number; - - @Column({ type: "integer" }) - HighThermistorId!: number; - - @Column({ type: "float" }) - Input12v!: number; - - @Column({ type: "float" }) - InternalTemperature!: number; - - @Column({ type: "boolean" }) - IsChargingSignalStatus!: boolean; - - @Column({ type: "boolean" }) - IsReadySignalStatus!: boolean; - - @Column({ type: "float" }) - LowCellVoltage!: number; - - @Column({ type: "integer" }) - LowCellVoltageId!: number; - - @Column({ type: "float" }) - LowTemperature!: number; - - @Column({ type: "integer" }) - LowThermistorId!: number; - - @Column({ type: "boolean" }) - MalfunctionIndicatorActive!: boolean; - - @Column({ type: "float" }) - MaximumCellVoltage!: number; - - @Column({ type: "float" }) - MaximumPackVoltage!: number; - - @Column({ type: "float" }) - MinimumCellVoltage!: number; - - @Column({ type: "float" }) - MinimumPackVoltage!: number; - - @Column({ type: "boolean" }) - MultiPurposeInputSignalStatus!: boolean; - - @Column({ type: "float" }) - PackAmphours!: number; - - @Column({ type: "float" }) - PackCurrent!: number; - - @Column({ type: "float" }) - PackDepthOfDischarge!: number; - - @Column({ type: "float" }) - PackStateOfCharge!: number; - - @Column({ type: "float" }) - PackVoltage!: number; - - @Column({ type: "integer" }) - PopulatedCells!: number; - - @Column({ type: "float" }) - RequestedFanSpeed!: number; -} diff --git a/packages/db/src/entities/BatteryFaults.entity.ts b/packages/db/src/entities/BatteryFaults.entity.ts deleted file mode 100644 index c69381a5..00000000 --- a/packages/db/src/entities/BatteryFaults.entity.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * Battery Faults data hypertable (Errors and Warnings) - * Related to telemetry_metadata via timestamp + rfid - */ -@Entity("battery_faults") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -export class BatteryFaults { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - // Errors - @Column({ type: "boolean" }) - ErrorAlwaysOnSupplyFault!: boolean; - - @Column({ type: "boolean" }) - ErrorCanbusCommunicationFault!: boolean; - - @Column({ type: "boolean" }) - ErrorChargeLimitEnforcementFault!: boolean; - - @Column({ type: "boolean" }) - ErrorChargerSafetyRelayFault!: boolean; - - @Column({ type: "boolean" }) - ErrorCurrentSensorFault!: boolean; - - @Column({ type: "boolean" }) - ErrorDischargeLimitEnforcementFault!: boolean; - - @Column({ type: "boolean" }) - ErrorFanMonitorFault!: boolean; - - @Column({ type: "boolean" }) - ErrorHighVoltageIsolationFault!: boolean; - - @Column({ type: "boolean" }) - ErrorInternalCommunicationFault!: boolean; - - @Column({ type: "boolean" }) - ErrorInternalConversionFault!: boolean; - - @Column({ type: "boolean" }) - ErrorInternalLogicFault!: boolean; - - @Column({ type: "boolean" }) - ErrorInternalMemoryFault!: boolean; - - @Column({ type: "boolean" }) - ErrorInternalThermistorFault!: boolean; - - @Column({ type: "boolean" }) - ErrorLowCellVoltageFault!: boolean; - - @Column({ type: "boolean" }) - ErrorOpenWiringFault!: boolean; - - @Column({ type: "boolean" }) - ErrorPackVoltageSensorFault!: boolean; - - @Column({ type: "boolean" }) - ErrorPowerSupply12vFault!: boolean; - - @Column({ type: "boolean" }) - ErrorThermistorFault!: boolean; - - @Column({ type: "boolean" }) - ErrorVoltageRedundancyFault!: boolean; - - @Column({ type: "boolean" }) - ErrorWeakCellFault!: boolean; - - @Column({ type: "boolean" }) - ErrorWeakPackFault!: boolean; - - // Warnings - @Column({ type: "boolean" }) - WarningCclReducedDueToAlternateCurrentLimit!: boolean; - - @Column({ type: "boolean" }) - WarningCclReducedDueToChargerLatch!: boolean; - - @Column({ type: "boolean" }) - WarningCclReducedDueToHighCellResistance!: boolean; - - @Column({ type: "boolean" }) - WarningCclReducedDueToHighCellVoltage!: boolean; - - @Column({ type: "boolean" }) - WarningCclReducedDueToHighPackVoltage!: boolean; - - @Column({ type: "boolean" }) - WarningCclReducedDueToHighSoc!: boolean; - - @Column({ type: "boolean" }) - WarningCclReducedDueToTemperature!: boolean; - - @Column({ type: "boolean" }) - WarningDclAndCclReducedDueToCommunicationFailsafe!: boolean; - - @Column({ type: "boolean" }) - WarningDclAndCclReducedDueToVoltageFailsafe!: boolean; - - @Column({ type: "boolean" }) - WarningDclReducedDueToHighCellResistance!: boolean; - - @Column({ type: "boolean" }) - WarningDclReducedDueToLowCellVoltage!: boolean; - - @Column({ type: "boolean" }) - WarningDclReducedDueToLowPackVoltage!: boolean; - - @Column({ type: "boolean" }) - WarningDclReducedDueToLowSoc!: boolean; - - @Column({ type: "boolean" }) - WarningDclReducedDueToTemperature!: boolean; -} diff --git a/packages/db/src/entities/Contactor.entity.ts b/packages/db/src/entities/Contactor.entity.ts deleted file mode 100644 index 3cf0d940..00000000 --- a/packages/db/src/entities/Contactor.entity.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * Contactor sensor data hypertable - * Related to telemetry_metadata via timestamp + rfid - */ -@Entity("contactor") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -export class Contactor { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - // Array Contactor - @Column({ type: "boolean" }) - ArrayBpsError!: boolean; - - @Column({ type: "float" }) - ArrayChargeCurrent!: number; - - @Column({ type: "boolean" }) - ArrayContactorClosed!: boolean; - - @Column({ type: "boolean" }) - ArrayContactorClosing!: boolean; - - @Column({ type: "boolean" }) - ArrayContactorError!: boolean; - - @Column({ type: "boolean" }) - ArrayHeartbeat!: boolean; - - @Column({ type: "float" }) - ArrayLineCurrent!: number; - - @Column({ type: "boolean" }) - ArrayPrechargerClosed!: boolean; - - @Column({ type: "boolean" }) - ArrayPrechargerClosing!: boolean; - - @Column({ type: "boolean" }) - ArrayPrechargerError!: boolean; - - // Charge Contactor - @Column({ type: "boolean" }) - ChargeBpsError!: boolean; - - @Column({ type: "float" }) - ChargeChargeCurrent!: number; - - @Column({ type: "boolean" }) - ChargeContactorClosed!: boolean; - - @Column({ type: "boolean" }) - ChargeContactorClosing!: boolean; - - @Column({ type: "boolean" }) - ChargeContactorError!: boolean; - - @Column({ type: "boolean" }) - ChargeHeartbeat!: boolean; - - @Column({ type: "float" }) - ChargeLineCurrent!: number; - - @Column({ type: "boolean" }) - ChargePrechargerClosed!: boolean; - - @Column({ type: "boolean" }) - ChargePrechargerClosing!: boolean; - - @Column({ type: "boolean" }) - ChargePrechargerError!: boolean; - - // Common Contactor - @Column({ type: "float" }) - CommonChargeCurrent!: number; - - @Column({ type: "boolean" }) - CommonContactorClosed!: boolean; - - @Column({ type: "boolean" }) - CommonContactorClosing!: boolean; - - @Column({ type: "boolean" }) - CommonContactorError!: boolean; - - @Column({ type: "boolean" }) - CommonContactorOpeningError!: boolean; - - @Column({ type: "boolean" }) - CommonHeartbeat!: boolean; - - @Column({ type: "float" }) - CommonLineCurrent!: number; - - @Column({ type: "boolean" }) - CommonPrechargerClosed!: boolean; - - @Column({ type: "boolean" }) - CommonPrechargerClosing!: boolean; - - @Column({ type: "boolean" }) - CommonPrechargerError!: boolean; - - // LV Contactor - @Column({ type: "boolean" }) - LvBpsError!: boolean; - - @Column({ type: "float" }) - LvChargeCurrent!: number; - - @Column({ type: "boolean" }) - LvContactorClosed!: boolean; - - @Column({ type: "boolean" }) - LvContactorClosing!: boolean; - - @Column({ type: "boolean" }) - LvContactorError!: boolean; - - @Column({ type: "boolean" }) - LvHeartbeat!: boolean; - - @Column({ type: "float" }) - LvLineCurrent!: number; - - @Column({ type: "boolean" }) - LvPrechargerClosed!: boolean; - - @Column({ type: "boolean" }) - LvPrechargerClosing!: boolean; - - @Column({ type: "boolean" }) - LvPrechargerError!: boolean; - - // Motor Contactor - @Column({ type: "boolean" }) - MotorBpsError!: boolean; - - @Column({ type: "float" }) - MotorChargeCurrent!: number; - - @Column({ type: "boolean" }) - MotorContactorClosed!: boolean; - - @Column({ type: "boolean" }) - MotorContactorClosing!: boolean; - - @Column({ type: "boolean" }) - MotorContactorError!: boolean; - - @Column({ type: "boolean" }) - MotorHeartbeat!: boolean; - - @Column({ type: "float" }) - MotorLineCurrent!: number; - - @Column({ type: "boolean" }) - MotorPrechargerClosed!: boolean; - - @Column({ type: "boolean" }) - MotorPrechargerClosing!: boolean; - - @Column({ type: "boolean" }) - MotorPrechargerError!: boolean; -} diff --git a/packages/db/src/entities/KeyMotor.entity.ts b/packages/db/src/entities/KeyMotor.entity.ts deleted file mode 100644 index 3bb06444..00000000 --- a/packages/db/src/entities/KeyMotor.entity.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * KeyMotor sensor data hypertable - * Related to telemetry_metadata via timestamp + rfid - */ -@Entity("key_motor") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -export class KeyMotor { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - @Column({ type: "float" }) - BusCurrentOut!: number; - - @Column({ type: "float" }) - KeyMotorVelocity!: number; - - @Column({ type: "float" }) - MotorCurrent!: number; -} diff --git a/packages/db/src/entities/MBMS.entity.ts b/packages/db/src/entities/MBMS.entity.ts deleted file mode 100644 index 5873ca25..00000000 --- a/packages/db/src/entities/MBMS.entity.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * MBMS (Master Battery Management System) sensor data hypertable - * Related to telemetry_metadata via timestamp + rfid - */ -@Entity("mbms") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -export class MBMS { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - @Column({ type: "boolean" }) - AbattDisable!: boolean; - - @Column({ type: "boolean" }) - ArrayContactorCommand!: boolean; - - @Column({ type: "boolean" }) - ArrayHeartbeatDeadTrip!: boolean; - - @Column({ type: "boolean" }) - ArrayHighCurrentTrip!: boolean; - - @Column({ type: "boolean" }) - ArrayHighCurrentWarning!: boolean; - - @Column({ type: "float" }) - AuxiliaryBatteryVoltage!: number; - - @Column({ type: "boolean" }) - CanOc12vWarning!: boolean; - - @Column({ type: "boolean" }) - ChargeContactorCommand!: boolean; - - @Column({ type: "boolean" }) - ChargeEnable!: boolean; - - @Column({ type: "boolean" }) - ChargeHeartbeatDeadTrip!: boolean; - - @Column({ type: "boolean" }) - ChargeHighCurrentTrip!: boolean; - - @Column({ type: "boolean" }) - ChargeHighCurrentWarning!: boolean; - - @Column({ type: "boolean" }) - ChargeSafety!: boolean; - - @Column({ type: "boolean" }) - ChargeShouldTrip!: boolean; - - @Column({ type: "boolean" }) - ChgFault!: boolean; - - @Column({ type: "boolean" }) - ChgLvEn!: boolean; - - @Column({ type: "boolean" }) - ChgOn!: boolean; - - @Column({ type: "boolean" }) - CommonContactorCommand!: boolean; - - @Column({ type: "boolean" }) - CommonHeartbeatDeadTrip!: boolean; - - @Column({ type: "boolean" }) - CommonHighCurrentTrip!: boolean; - - @Column({ type: "boolean" }) - CommonHighCurrentWarning!: boolean; - - @Column({ type: "boolean" }) - ContactorConnectedUnexpectedlyTrip!: boolean; - - @Column({ type: "boolean" }) - ContactorDisconnectedUnexpectedlyTrip!: boolean; - - @Column({ type: "boolean" }) - DcdcFault!: boolean; - - @Column({ type: "boolean" }) - DcdcOn!: boolean; - - @Column({ type: "boolean" }) - DischargeEnable!: boolean; - - @Column({ type: "boolean" }) - DischargeShouldTrip!: boolean; - - @Column({ type: "boolean" }) - En1!: boolean; - - @Column({ type: "boolean" }) - EsdEnabledTrip!: boolean; - - @Column({ type: "boolean" }) - ExternalShutdown!: boolean; - - @Column({ type: "boolean" }) - Heartbeat!: boolean; - - @Column({ type: "boolean" }) - HighCellVoltageTrip!: boolean; - - @Column({ type: "boolean" }) - HighCellVoltageWarning!: boolean; - - @Column({ type: "boolean" }) - HighTemperatureTrip!: boolean; - - @Column({ type: "boolean" }) - HighTemperatureWarning!: boolean; - - @Column({ type: "boolean" }) - Key!: boolean; - - @Column({ type: "boolean" }) - LowCellVoltageTrip!: boolean; - - @Column({ type: "boolean" }) - LowCellVoltageWarning!: boolean; - - @Column({ type: "boolean" }) - LowTemperatureTrip!: boolean; - - @Column({ type: "boolean" }) - LowTemperatureWarning!: boolean; - - @Column({ type: "boolean" }) - LvContactorCommand!: boolean; - - @Column({ type: "boolean" }) - LvHeartbeatDeadTrip!: boolean; - - @Column({ type: "boolean" }) - LvHighCurrentTrip!: boolean; - - @Column({ type: "boolean" }) - LvHighCurrentWarning!: boolean; - - @Column({ type: "boolean" }) - MainPowerSwitch!: boolean; - - @Column({ type: "boolean" }) - MotorContactorCommand!: boolean; - - @Column({ type: "boolean" }) - MotorHeartbeatDeadTrip!: boolean; - - @Column({ type: "boolean" }) - MotorHighCurrentTrip!: boolean; - - @Column({ type: "boolean" }) - MotorHighCurrentWarning!: boolean; - - @Column({ type: "boolean" }) - MpsDisabledTrip!: boolean; - - @Column({ type: "boolean" }) - OrionCanReceivedRecently!: boolean; - - @Column({ type: "boolean" }) - OrionMessageTimeoutTrip!: boolean; - - @Column({ type: "boolean" }) - ProtectionTrip!: boolean; - - @Column({ type: "float" }) - StartupState!: number; - - @Column({ type: "boolean" }) - StrobeBmsLight!: boolean; - - @Column({ type: "float" }) - SystemState!: number; - - @Column({ type: "boolean" }) - ThreeAOc!: boolean; -} diff --git a/packages/db/src/entities/MPPT.entity.ts b/packages/db/src/entities/MPPT.entity.ts deleted file mode 100644 index e4ae88e3..00000000 --- a/packages/db/src/entities/MPPT.entity.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * MPPT (Maximum Power Point Tracker) sensor data hypertable - * Related to telemetry_metadata via timestamp + rfid - */ -@Entity("mppt") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -export class MPPT { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - @Column({ type: "float" }) - Mppt0Ch0ArrayCurrent!: number; - - @Column({ type: "float" }) - Mppt0Ch0ArrayVoltage!: number; - - @Column({ type: "float" }) - Mppt0Ch0BatteryVoltage!: number; - - @Column({ type: "float" }) - Mppt0Ch0UnitTemperature!: number; - - @Column({ type: "float" }) - Mppt0Ch1ArrayCurrent!: number; - - @Column({ type: "float" }) - Mppt0Ch1ArrayVoltage!: number; - - @Column({ type: "float" }) - Mppt0Ch1BatteryVoltage!: number; - - @Column({ type: "float" }) - Mppt0Ch1UnitTemperature!: number; - - @Column({ type: "float" }) - Mppt1Ch0ArrayCurrent!: number; - - @Column({ type: "float" }) - Mppt1Ch0ArrayVoltage!: number; - - @Column({ type: "float" }) - Mppt1Ch0BatteryVoltage!: number; - - @Column({ type: "float" }) - Mppt1Ch0UnitTemperature!: number; - - @Column({ type: "float" }) - Mppt1Ch1ArrayCurrent!: number; - - @Column({ type: "float" }) - Mppt1Ch1ArrayVoltage!: number; - - @Column({ type: "float" }) - Mppt1Ch1BatteryVoltage!: number; - - @Column({ type: "float" }) - Mppt1Ch1UnitTemperature!: number; - - @Column({ type: "float" }) - Mppt2Ch0ArrayCurrent!: number; - - @Column({ type: "float" }) - Mppt2Ch0ArrayVoltage!: number; - - @Column({ type: "float" }) - Mppt2Ch0BatteryVoltage!: number; - - @Column({ type: "float" }) - Mppt2Ch0UnitTemperature!: number; - - // MPPT 2 Channel 1 - @Column({ type: "float" }) - Mppt2Ch1ArrayCurrent!: number; - - @Column({ type: "float" }) - Mppt2Ch1ArrayVoltage!: number; - - @Column({ type: "float" }) - Mppt2Ch1BatteryVoltage!: number; - - @Column({ type: "float" }) - Mppt2Ch1UnitTemperature!: number; - - // MPPT 3 Channel 0 - @Column({ type: "float" }) - Mppt3Ch0ArrayCurrent!: number; - - @Column({ type: "float" }) - Mppt3Ch0ArrayVoltage!: number; - - @Column({ type: "float" }) - Mppt3Ch0BatteryVoltage!: number; - - @Column({ type: "float" }) - Mppt3Ch0UnitTemperature!: number; - - // MPPT 3 Channel 1 - @Column({ type: "float" }) - Mppt3Ch1ArrayCurrent!: number; - - @Column({ type: "float" }) - Mppt3Ch1ArrayVoltage!: number; - - @Column({ type: "float" }) - Mppt3Ch1BatteryVoltage!: number; - - @Column({ type: "float" }) - Mppt3Ch1UnitTemperature!: number; -} diff --git a/packages/db/src/entities/MotorDetails.entity.ts b/packages/db/src/entities/MotorDetails.entity.ts deleted file mode 100644 index fdc8122b..00000000 --- a/packages/db/src/entities/MotorDetails.entity.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * Motor details sensor data hypertable - * Related to telemetry_metadata via timestamp + rfid - * Note: We have MotorDetails0 and MotorDetails1, so motor_id will differentiate them - */ -@Entity("motor_details") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid, motor_id", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -@Index(["motor_id", "timestamp"]) -export class MotorDetails { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - @PrimaryColumn({ type: "int" }) - MotorId!: number; - - @Column({ type: "float" }) - ActiveMotor!: number; - - @Column({ type: "float" }) - BemfD!: number; - - @Column({ type: "float" }) - BemfQ!: number; - - @Column({ type: "float" }) - BusCurrent!: number; - - @Column({ type: "float" }) - BusVoltage!: number; - - @Column({ type: "float" }) - DcBusAh!: number; - - @Column({ type: "float" }) - DspBoardTemperature!: number; - - @Column({ type: "float" }) - ErrorFlags!: number; - - @Column({ type: "float" }) - HeatsinkTemperature!: number; - - @Column({ type: "float" }) - Id!: number; - - @Column({ type: "float" }) - Iq!: number; - - @Column({ type: "float" }) - LimitFlags!: number; - - @Column({ type: "float" }) - MotorTemperature!: number; - - @Column({ type: "float" }) - MotorVelocity!: number; - - @Column({ type: "float" }) - Odometer!: number; - - @Column({ type: "float" }) - PhaseCurrentB!: number; - - @Column({ type: "float" }) - PhaseCurrentC!: number; - - @Column({ type: "float" }) - RxErrorCount!: number; - - @Column({ type: "float" }) - SerialNumber!: number; - - @Column({ type: "float" }) - SlipSpeed!: number; - - @Column({ type: "float" }) - Supply15v!: number; - - @Column({ type: "float" }) - Supply1v9!: number; - - @Column({ type: "float" }) - Supply3v3!: number; - - @Column({ type: "float" }) - TritiumId!: number; - - @Column({ type: "float" }) - TxErrorCount!: number; - - @Column({ type: "float" }) - Vd!: number; - - @Column({ type: "float" }) - VehicleVelocity!: number; - - @Column({ type: "float" }) - Vq!: number; -} diff --git a/packages/db/src/entities/ProximitySensors.entity.ts b/packages/db/src/entities/ProximitySensors.entity.ts deleted file mode 100644 index 3a9c2ce7..00000000 --- a/packages/db/src/entities/ProximitySensors.entity.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * Proximity Sensors data hypertable - * Related to telemetry_metadata via timestamp + rfid - */ -@Entity("proximity_sensors") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -export class ProximitySensors { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - @Column({ type: "float" }) - ProximitySensor1!: number; - - @Column({ type: "float" }) - ProximitySensor2!: number; - - @Column({ type: "float" }) - ProximitySensor3!: number; - - @Column({ type: "float" }) - ProximitySensor4!: number; -} diff --git a/packages/db/src/entities/Telemetry.entity.ts b/packages/db/src/entities/Telemetry.entity.ts deleted file mode 100644 index 8b34a8c3..00000000 --- a/packages/db/src/entities/Telemetry.entity.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * Telemetry sensor data hypertable (GPS and MPU data) - * Related to telemetry_metadata via timestamp + rfid - */ -@Entity("telemetry") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", - compression: { - compress: true, - compress_segmentby: "rfid", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -export class Telemetry { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - // GPS Data - @Column({ type: "float" }) - GpsAdditionalFlags!: number; - - @Column({ type: "float" }) - GpsDay!: number; - - @Column({ type: "float" }) - GpsFixStatusFlags!: number; - - @Column({ type: "float" }) - GpsHour!: number; - - @Column({ type: "float" }) - GpsLatitude!: number; - - @Column({ type: "float" }) - GpsLongitude!: number; - - @Column({ type: "float" }) - GpsMinute!: number; - - @Column({ type: "float" }) - GpsMonth!: number; - - @Column({ type: "float" }) - GpsSecond!: number; - - @Column({ type: "float" }) - GpsValidityFlags!: number; - - @Column({ type: "float" }) - GpsYear!: number; - - // MPU (Motion Processing Unit) Data - @Column({ type: "float" }) - MpuAccelerationX!: number; - - @Column({ type: "float" }) - MpuAccelerationY!: number; - - @Column({ type: "float" }) - MpuAccelerationZ!: number; - - @Column({ type: "float" }) - MpuRotationX!: number; - - @Column({ type: "float" }) - MpuRotationY!: number; - - @Column({ type: "float" }) - MpuRotationZ!: number; - - @Column({ type: "float" }) - MpuTemperature!: number; -} diff --git a/packages/db/src/entities/TelemetryMetadata.entity.ts b/packages/db/src/entities/TelemetryMetadata.entity.ts deleted file mode 100644 index ec660332..00000000 --- a/packages/db/src/entities/TelemetryMetadata.entity.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Entity, Column, Index, PrimaryColumn } from "typeorm"; -import { Hypertable, TimeColumn } from "@timescaledb/typeorm"; - -/** - * Metadata table for all telemetry packets - * This is the main reference table that links to all sensor-specific hypertables - * via timestamp and RFID - */ -@Entity("telemetry_metadata") -@Hypertable({ - timeColumnName: "timestamp", - chunkTimeInterval: "1 month", // Adjust based on race frequency - compression: { - compress: true, - compress_segmentby: "rfid, race_name", - compress_orderby: "timestamp DESC", - }, -}) -@Index(["rfid", "timestamp"]) -@Index(["race_name", "timestamp"]) -export class TelemetryMetadata { - @TimeColumn() - @PrimaryColumn({ type: "timestamptz" }) - Timestamp!: Date; - - @PrimaryColumn({ type: "text" }) - Rfid!: string; - - @Column({ type: "text", nullable: true }) - RaceName?: string; - - @Column({ type: "text", nullable: true }) - Title?: string; -} diff --git a/packages/db/src/interfaces/repositories.interface.ts b/packages/db/src/interfaces/repositories.interface.ts index f22d28b8..1e5d2bb5 100644 --- a/packages/db/src/interfaces/repositories.interface.ts +++ b/packages/db/src/interfaces/repositories.interface.ts @@ -1,17 +1,8 @@ import { DeepPartial, FindManyOptions, FindOneOptions } from "typeorm"; -import { B3 } from "../entities/B3.entity"; -import { BatteryFaults } from "../entities/BatteryFaults.entity"; -import { Battery } from "../entities/Battery.entity"; -import { Contactor } from "../entities/Contactor.entity"; + import { Driver } from "../entities/Driver.entity"; -import { KeyMotor } from "../entities/KeyMotor.entity"; import { Lap } from "../entities/Lap.entity"; -import { MBMS } from "../entities/MBMS.entity"; -import { MotorDetails } from "../entities/MotorDetails.entity"; -import { MPPT } from "../entities/MPPT.entity"; -import { ProximitySensors } from "../entities/ProximitySensors.entity"; -import { Telemetry } from "../entities/Telemetry.entity"; -import { TelemetryMetadata } from "../entities/TelemetryMetadata.entity"; + import { TelemetryPacket } from "../entities/TelemetryPacket.entity"; export interface IBaseRepository { @@ -24,35 +15,8 @@ export interface IBaseRepository { count(options?: FindManyOptions): Promise; } -export interface IB3Repository extends IBaseRepository {} - -export interface IBatteryRepository extends IBaseRepository {} - -export interface IBatteryFaultsRepository - extends IBaseRepository {} - -export interface IContactorRepository extends IBaseRepository {} - export interface IDriverRepository extends IBaseRepository {} -export interface IKeyMotorRepository extends IBaseRepository {} - export interface ILapRepository extends IBaseRepository {} -export interface IMBMSRepository extends IBaseRepository {} - -export interface IMotorDetailsRepository - extends IBaseRepository {} - -export interface IMPPTRepository extends IBaseRepository {} - -export interface IProximitySensorsRepository - extends IBaseRepository {} - -export interface ITelemetryRepository extends IBaseRepository {} - -export interface ITelemetryMetadataRepository - extends IBaseRepository {} - -export interface ITelemetryPacketRepository - extends IBaseRepository {} +export interface ITelemetryPacketRepository extends IBaseRepository {} diff --git a/packages/db/src/repositories/B3Repository.ts b/packages/db/src/repositories/B3Repository.ts deleted file mode 100644 index 299cc96b..00000000 --- a/packages/db/src/repositories/B3Repository.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { B3 } from "../entities/B3.entity"; -import { IB3Repository } from "../interfaces/repositories.interface"; - -export class B3Repository extends BaseRepository implements IB3Repository { - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/BatteryFaultsRepository.ts b/packages/db/src/repositories/BatteryFaultsRepository.ts deleted file mode 100644 index 9ab72103..00000000 --- a/packages/db/src/repositories/BatteryFaultsRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { BatteryFaults } from "../entities/BatteryFaults.entity"; -import { IBatteryFaultsRepository } from "../interfaces/repositories.interface"; - -export class BatteryFaultsRepository - extends BaseRepository - implements IBatteryFaultsRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/BatteryRepository.ts b/packages/db/src/repositories/BatteryRepository.ts deleted file mode 100644 index cfcdb7d0..00000000 --- a/packages/db/src/repositories/BatteryRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { Battery } from "../entities/Battery.entity"; -import { IBatteryRepository } from "../interfaces/repositories.interface"; - -export class BatteryRepository - extends BaseRepository - implements IBatteryRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/ContactorRepository.ts b/packages/db/src/repositories/ContactorRepository.ts deleted file mode 100644 index fdf5c591..00000000 --- a/packages/db/src/repositories/ContactorRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { Contactor } from "../entities/Contactor.entity"; -import { IContactorRepository } from "../interfaces/repositories.interface"; - -export class ContactorRepository - extends BaseRepository - implements IContactorRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/KeyMotorRepository.ts b/packages/db/src/repositories/KeyMotorRepository.ts deleted file mode 100644 index 834244e5..00000000 --- a/packages/db/src/repositories/KeyMotorRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { KeyMotor } from "../entities/KeyMotor.entity"; -import { IKeyMotorRepository } from "../interfaces/repositories.interface"; - -export class KeyMotorRepository - extends BaseRepository - implements IKeyMotorRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/MBMSRepository.ts b/packages/db/src/repositories/MBMSRepository.ts deleted file mode 100644 index c50ef9a5..00000000 --- a/packages/db/src/repositories/MBMSRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { MBMS } from "../entities/MBMS.entity"; -import { IMBMSRepository } from "../interfaces/repositories.interface"; - -export class MBMSRepository - extends BaseRepository - implements IMBMSRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/MPPTRepository.ts b/packages/db/src/repositories/MPPTRepository.ts deleted file mode 100644 index b933f626..00000000 --- a/packages/db/src/repositories/MPPTRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { MPPT } from "../entities/MPPT.entity"; -import { IMPPTRepository } from "../interfaces/repositories.interface"; - -export class MPPTRepository - extends BaseRepository - implements IMPPTRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/MotorDetailsRepository.ts b/packages/db/src/repositories/MotorDetailsRepository.ts deleted file mode 100644 index 655a617c..00000000 --- a/packages/db/src/repositories/MotorDetailsRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { MotorDetails } from "../entities/MotorDetails.entity"; -import { IMotorDetailsRepository } from "../interfaces/repositories.interface"; - -export class MotorDetailsRepository - extends BaseRepository - implements IMotorDetailsRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/ProximitySensorsRepository.ts b/packages/db/src/repositories/ProximitySensorsRepository.ts deleted file mode 100644 index 987fb1a1..00000000 --- a/packages/db/src/repositories/ProximitySensorsRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { ProximitySensors } from "../entities/ProximitySensors.entity"; -import { IProximitySensorsRepository } from "../interfaces/repositories.interface"; - -export class ProximitySensorsRepository - extends BaseRepository - implements IProximitySensorsRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/TelemetryMetadataRepository.ts b/packages/db/src/repositories/TelemetryMetadataRepository.ts deleted file mode 100644 index e46cfe99..00000000 --- a/packages/db/src/repositories/TelemetryMetadataRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { TelemetryMetadata } from "../entities/TelemetryMetadata.entity"; -import { ITelemetryMetadataRepository } from "../interfaces/repositories.interface"; - -export class TelemetryMetadataRepository - extends BaseRepository - implements ITelemetryMetadataRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/repositories/TelemetryRepository.ts b/packages/db/src/repositories/TelemetryRepository.ts deleted file mode 100644 index 073abba6..00000000 --- a/packages/db/src/repositories/TelemetryRepository.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Repository } from "typeorm"; -import { BaseRepository } from "./BaseRepository"; -import { Telemetry } from "../entities/Telemetry.entity"; -import { ITelemetryRepository } from "../interfaces/repositories.interface"; - -export class TelemetryRepository - extends BaseRepository - implements ITelemetryRepository -{ - constructor(repository: Repository) { - super(repository); - } -} diff --git a/packages/db/src/services/DatabaseService.ts b/packages/db/src/services/DatabaseService.ts index e01dc827..e1e89583 100644 --- a/packages/db/src/services/DatabaseService.ts +++ b/packages/db/src/services/DatabaseService.ts @@ -1,15 +1,22 @@ -import { Repository } from "typeorm"; +import { Between, Repository } from "typeorm"; import { AppDataSource } from "../data-source"; import { ITelemetryData } from "@shared/helios-types"; import { TelemetryPacket } from "../entities/TelemetryPacket.entity"; +import { Driver } from "../entities/Driver.entity"; +import { Lap } from "../entities/Lap.entity"; +import { GenericResponse } from "./DatabaseService.types"; export class DatabaseService { private isConnected = false; private static instance: DatabaseService; private telemetryPacketRepo: Repository; + private driverRepo: Repository; + private lapRepo: Repository; constructor() { this.telemetryPacketRepo = AppDataSource.getRepository(TelemetryPacket); + this.driverRepo = AppDataSource.getRepository(Driver); + this.lapRepo = AppDataSource.getRepository(Lap); } async initialize() { @@ -26,9 +33,6 @@ export class DatabaseService { return DatabaseService.instance; } - /** - * Helper function to flatten ITelemetryData into TelemetryPacket format - */ private flattenTelemetryData( packet: ITelemetryData, ): Partial { @@ -133,11 +137,75 @@ export class DatabaseService { }; } - async insertPacketData(message: ITelemetryData): Promise { - if (this.isConnected) { - console.log("Inserting packet data into database"); - const flattenedData = this.flattenTelemetryData(message); - await this.telemetryPacketRepo.save(flattenedData); + public async getDrivers() { + try { + const drivers = await this.driverRepo.find(); + return drivers.map((driver) => ({ + Rfid: driver.Rfid, + driver: driver.Name, + })); + } catch (error: unknown) { + console.error("Error getting drivers"); + } + } + + public async getDriverNameUsingRfid(Rfid: string) { + try { + const driver = await this.driverRepo.findOne({ + where: { Rfid }, + }); + + if (!driver) { + console.error(`No driver found for Rfid: ${Rfid}`); + return "Driver not found"; + } + + return driver.Name; + } catch (error: unknown) { + console.error("Error getting driver name using the given Rfid"); + throw new Error((error as Error).message); + } + } + + public async getDriverLaps(Rfid: string) { + try { + const laps = await this.lapRepo.find({ + order: { Timestamp: "DESC" }, + where: { Rfid }, + }); + return laps; + } catch (error: unknown) { + console.error("Error getting lap data for driver", error); + throw new Error( + (error as Error).message || "Failed to fetch driver laps", + ); + } + } + + public async updateDriverInfo(Rfid: string, name: string) { + try { + if (typeof Rfid !== "string") { + throw new Error("Rfid must be a string"); + } + + const existingDriver = await this.driverRepo.findOne({ + where: { Rfid }, + }); + + if (!existingDriver) { + return { message: "Driver Rfid not found in driver table" }; + } + + const oldName = existingDriver.Name; + existingDriver.Name = name; + await this.driverRepo.save(existingDriver); + + return { + message: `Driver name updated from ${oldName} to ${name}`, + }; + } catch (error: unknown) { + console.error("Error updating driver info: " + (error as Error).message); + throw new Error((error as Error).message); } } @@ -145,6 +213,154 @@ export class DatabaseService { if (this.isConnected) { await AppDataSource.destroy(); console.log("Database connection closed"); + this.isConnected = false; + } + } + + public async getPacketData(timestamp: string) { + if (!this.isConnected) { + throw new Error("Database not connected"); + } + + try { + const packet = await this.telemetryPacketRepo.findOneBy({ + Timestamp: new Date(timestamp), + }); + return packet; + } catch (error: unknown) { + throw new Error( + "Failed to retrieve packet date: " + (error as Error).message, + ); + } + } + + public async scanPacketDataBetweenDates( + startUTCDate: number, + endUTCDate: number, + ) { + if (!this.isConnected) { + throw new Error("Database not connected"); + } + + try { + const packets = await this.telemetryPacketRepo.find({ + where: { + Timestamp: Between(new Date(startUTCDate), new Date(endUTCDate)), + }, + }); + return packets; + } catch (error: unknown) { + throw new Error( + "Failed to scan packets between dates: " + (error as Error).message, + ); + } + } + + public async insertPacketData( + packet: ITelemetryData, + ): Promise { + if (!this.isConnected) { + throw new Error("Database not connected"); + } + try { + const flattenedData = this.flattenTelemetryData(packet); + await this.telemetryPacketRepo.save(flattenedData); + return { + httpStatusCode: 201, + message: "Packet data inserted successfully", + }; + } catch (error: unknown) { + throw new Error( + "Failed to insert packet data: " + (error as Error).message, + ); + } + } + + public async getFirstAndLastPacketDates(): Promise<{ + firstDateUTC: number | null; + lastDateUTC: number | null; + }> { + if (!this.isConnected) { + throw new Error("Database not connected"); + } + + try { + const firstPacket = await this.telemetryPacketRepo.findOne({ + order: { Timestamp: "ASC" }, + }); + + const lastPacket = await this.telemetryPacketRepo.findOne({ + order: { Timestamp: "DESC" }, + }); + + return { + firstDateUTC: firstPacket + ? Number(firstPacket.Timestamp.getTime()) + : null, + lastDateUTC: lastPacket ? Number(lastPacket.Timestamp.getTime()) : null, + }; + } catch (error: unknown) { + throw new Error( + "Failed to retrieve first and last packet dates: " + + (error as Error).message, + ); + } + } + + public async insertLapData(lapData: Partial): Promise { + if (!this.isConnected) { + throw new Error("Database not connected"); + } + + try { + await this.lapRepo.save(lapData); + return { + httpStatusCode: 201, + message: "Lap data inserted successfully", + }; + } catch (error: unknown) { + throw new Error("Failed to insert lap data: " + (error as Error).message); + } + } + + public async getLapData(): Promise { + if (!this.isConnected) { + throw new Error("Database not connected"); + } + + try { + const laps = await this.lapRepo.find(); + return laps; + } catch (error: unknown) { + throw new Error( + "Failed to retrieve lap data: " + (error as Error).message, + ); + } + } + + public async insertIntoGpsLapCountTable( + rfid: string, + timestamp: number, + ): Promise { + if (!this.isConnected) { + throw new Error("Database not connected"); + } + + try { + await this.lapRepo.save({ + Rfid: rfid ?? "unknown driver", + Timestamp: timestamp ?? new Date().getTime(), + Type: "gps-lap", + }); + return { + httpStatusCode: 201, + message: "Inserted into GPS lap count table successfully", + }; + } catch (error: unknown) { + throw new Error( + "Failed to insert into GPS lap count table: " + + (error as Error).message, + ); } } } diff --git a/packages/db/src/services/DatabaseService.types.ts b/packages/db/src/services/DatabaseService.types.ts new file mode 100644 index 00000000..08ed132c --- /dev/null +++ b/packages/db/src/services/DatabaseService.types.ts @@ -0,0 +1,5 @@ +export interface GenericResponse { + httpStatusCode?: number; + requestId?: string; + message?: string; +} diff --git a/packages/server/src/controllers/BackendController/BackendController.ts b/packages/server/src/controllers/BackendController/BackendController.ts index 4f5834cc..717a225e 100644 --- a/packages/server/src/controllers/BackendController/BackendController.ts +++ b/packages/server/src/controllers/BackendController/BackendController.ts @@ -4,7 +4,6 @@ import type { IncomingMessage, Server, ServerResponse } from "http"; import type { BackendControllerTypes } from "@/controllers/BackendController/BackendController.types"; import { LapController } from "@/controllers/LapController/LapController"; -import DynamoDB from "@/datasources/DynamoDB/DynamoDB"; import { SocketIO } from "@/datasources/SocketIO/SocketIO"; import { SolarMQTTClient } from "@/datasources/SolarMQTTClient/SolarMQTTClient"; import { options } from "@/datasources/SolarMQTTClient/SolarMQTTClient.types"; @@ -14,20 +13,18 @@ import { type ITelemetryData } from "@shared/helios-types"; //getDriverInfo export class BackendController implements BackendControllerTypes { - public dynamoDB: DynamoDB; public socketIO: SocketIO; public lapController: LapController; public mqtt: SolarMQTTClient; - public databaseService: DatabaseService; + public timescaleDB: DatabaseService; public carLatency: number; constructor( httpsServer: Server, ) { - this.dynamoDB = new DynamoDB(this); this.socketIO = new SocketIO(httpsServer, this); this.mqtt = new SolarMQTTClient(options, this); this.lapController = new LapController(this); - this.databaseService = DatabaseService.getInstance(); + this.timescaleDB = DatabaseService.getInstance(); this.establishCarPinging(); this.carLatency = 0; this.initializeDatabase(); @@ -36,7 +33,7 @@ export class BackendController implements BackendControllerTypes { private async initializeDatabase() { try { - await this.databaseService.initialize(); + await this.timescaleDB.initialize(); logger.info("Database connection established successfully"); } catch (error) { logger.error("Failed to initialize database:", error); @@ -61,9 +58,7 @@ export class BackendController implements BackendControllerTypes { public async handlePacketReceive(message: ITelemetryData) { // Insert the packet into the database - this.dynamoDB.insertPacketData(message); - - this.databaseService.insertPacketData(message); + this.timescaleDB.insertPacketData(message); // Broadcast the packet to the frontend this.socketIO.broadcastPacket(message); @@ -85,7 +80,7 @@ export class BackendController implements BackendControllerTypes { public async cleanup() { try { - await this.databaseService.close(); + await this.timescaleDB.close(); logger.info("Database connection closed successfully"); } catch (error) { logger.error("Error closing database connection:", error); diff --git a/packages/server/src/controllers/BackendController/BackendController.types.ts b/packages/server/src/controllers/BackendController/BackendController.types.ts index 46dffef4..8ba6fede 100644 --- a/packages/server/src/controllers/BackendController/BackendController.types.ts +++ b/packages/server/src/controllers/BackendController/BackendController.types.ts @@ -1,13 +1,14 @@ +import { DatabaseService } from "db"; + import type { LapController } from "@/controllers/LapController/LapController"; -import DynamoDB from "@/datasources/DynamoDB/DynamoDB"; import type { SocketIO } from "@/datasources/SocketIO/SocketIO"; import type { SolarMQTTClient } from "@/datasources/SolarMQTTClient/SolarMQTTClient"; import type { ITelemetryData } from "@shared/helios-types"; export interface BackendControllerTypes { - dynamoDB: DynamoDB; + timescaleDB: DatabaseService; establishCarPinging(): void; handleTelemetryToCar(carLatency: number): void; handlePacketReceive(message: ITelemetryData): void; diff --git a/packages/server/src/controllers/LapController/LapController.ts b/packages/server/src/controllers/LapController/LapController.ts index 46132b32..8d8d4e91 100644 --- a/packages/server/src/controllers/LapController/LapController.ts +++ b/packages/server/src/controllers/LapController/LapController.ts @@ -159,12 +159,12 @@ export class LapController implements LapControllerType { public async handleLapData(lapData: ILapData) { await this.backendController.socketIO.broadcastLapData(lapData); await this.backendController.mqtt.publishLapData(lapData); - await this.backendController.dynamoDB.insertLapData(lapData); + await this.backendController.timescaleDB.insertLapData(lapData); } // this function is for calling when lap completes via geofence public async handleGeofenceLap(rfid: string, timestamp: number) { - await this.backendController.dynamoDB.insertIntoGpsLapCountTable( + await this.backendController.timescaleDB.insertIntoGpsLapCountTable( rfid, timestamp, ); diff --git a/packages/server/src/controllers/routeControllers/driver.controller.ts b/packages/server/src/controllers/routeControllers/driver.controller.ts index fe3f4622..9af27ba5 100644 --- a/packages/server/src/controllers/routeControllers/driver.controller.ts +++ b/packages/server/src/controllers/routeControllers/driver.controller.ts @@ -15,7 +15,7 @@ export const getDrivers = async (request: Request, response: Response) => { ); try { - const driverData = await backendController.dynamoDB.getDrivers(); + const driverData = await backendController.timescaleDB.getDrivers(); logger.info(`ENTRY - ${request.method} ${request.url}`); const data = { @@ -44,7 +44,7 @@ export const getDriverLaps = async (request: Request, response: Response) => { try { const Rfid = request.params.Rfid; - const driverLaps = await backendController.dynamoDB.getDriverLaps(Rfid); + const driverLaps = await backendController.timescaleDB.getDriverLaps(Rfid); logger.info(`ENTRY - ${request.method} ${request.url}`); const data = { @@ -82,7 +82,7 @@ export const updateDriverInfo = async ( request: Request, response: Response, ) => { - const { name, Rfid } = request.body; + const { Rfid, name } = request.body; if (!name || !Rfid) { return response @@ -100,10 +100,8 @@ export const updateDriverInfo = async ( ); try { - const responseMessage = await backendController.dynamoDB.updateDriverInfo( - Rfid, - name, - ); + const responseMessage = + await backendController.timescaleDB.updateDriverInfo(Rfid, name); logger.info(`ENTRY - ${request.method} ${request.url}`); const data = { diff --git a/packages/server/src/controllers/routeControllers/lap.controller.ts b/packages/server/src/controllers/routeControllers/lap.controller.ts index 80a131b2..203f09f3 100644 --- a/packages/server/src/controllers/routeControllers/lap.controller.ts +++ b/packages/server/src/controllers/routeControllers/lap.controller.ts @@ -14,7 +14,7 @@ export const getLapData = async (request: Request, response: Response) => { response, ); try { - const lapData = await backendController.dynamoDB.getLapData(); + const lapData = await backendController.timescaleDB.getLapData(); logger.info(`ENTRY - ${request.method} ${request.url}`); const data = { diff --git a/packages/server/src/controllers/routeControllers/playback.controller.ts b/packages/server/src/controllers/routeControllers/playback.controller.ts index f753c0f3..f98dc9ec 100644 --- a/packages/server/src/controllers/routeControllers/playback.controller.ts +++ b/packages/server/src/controllers/routeControllers/playback.controller.ts @@ -17,7 +17,7 @@ export const getPacket = async (request: Request, response: Response) => { const timestamp = request.params.timestamp; const packetData = - await backendController.dynamoDB.getPacketData(timestamp); + await backendController.timescaleDB.getPacketData(timestamp); logger.info(`ENTRY - ${request.method} ${request.url}`); const data = { data: packetData, message: "OK" }; @@ -48,9 +48,9 @@ export const getPacketDataBetweenDates = async ( const endTime = Number(request.query.endTime); - // Fetch data from DynamoDB + // Fetch data from timescaleDB const packetData = - await backendController.dynamoDB.scanPacketDataBetweenDates( + await backendController.timescaleDB.scanPacketDataBetweenDates( startTime, endTime, ); @@ -78,7 +78,7 @@ export const getFirstAndLastPacket = async ( ); try { const { firstDateUTC, lastDateUTC } = - await backendController.dynamoDB.getFirstAndLastPacketDates(); + await backendController.timescaleDB.getFirstAndLastPacketDates(); logger.info(`ENTRY - ${request.method} ${request.url}`); const data = { diff --git a/packages/server/src/datasources/DynamoDB/DynamoDB.ts b/packages/server/src/datasources/DynamoDB/DynamoDB.ts deleted file mode 100644 index 7c05eb0d..00000000 --- a/packages/server/src/datasources/DynamoDB/DynamoDB.ts +++ /dev/null @@ -1,348 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; - -import { type BackendController } from "@/controllers/BackendController/BackendController"; - -import { - type DynamoDBtypes, - GenericResponse, -} from "@/datasources/DynamoDB/DynamoDB.types"; - -import { createLightweightApplicationLogger } from "@/utils/logger"; - -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import type { QueryCommandInput } from "@aws-sdk/lib-dynamodb"; -import { - DynamoDBDocumentClient, - GetCommand, - PutCommand, - QueryCommand, - ScanCommand, - UpdateCommand, -} from "@aws-sdk/lib-dynamodb"; -import type { ILapData, ITelemetryData } from "@shared/helios-types"; - -if (!process.env.LAP_TABLE_NAME) { - throw new Error("Lap table name not defined"); -} - -if (!process.env.PACKET_TABLE_NAME) { - throw new Error("Packet table name not defined"); -} - -if (!process.env.DRIVER_TABLE_NAME) { - throw new Error("Driver table name not defined"); -} - -if (!process.env.GPS_CALCULATED_LAP_DATA_TABLE) { - throw new Error("GPS lap table name not defined"); -} - -const packetTableName = process.env.PACKET_TABLE_NAME; -const lapTableName = process.env.LAP_TABLE_NAME; -const gpsTableName = process.env.GPS_CALCULATED_LAP_DATA_TABLE; -const driverTableName = process.env.DRIVER_TABLE_NAME; - -const logger = createLightweightApplicationLogger("DynamoDB.ts"); -export class DynamoDB implements DynamoDBtypes { - public client: DynamoDBClient; - backendController: BackendController; - lapTableName: string; - packetTableName: string; - gpsTableName: string; - driverTableName: string; - packetTableIndexName: string = "type-timestamp-index"; - - constructor(backendController: BackendController) { - try { - this.backendController = backendController; - const rawClient = new DynamoDBClient({ region: "ca-central-1" }); - this.client = DynamoDBDocumentClient.from(rawClient); - this.lapTableName = lapTableName; - this.packetTableName = packetTableName; - this.gpsTableName = gpsTableName; - this.driverTableName = driverTableName; - } catch (error) { - logger.error("Error connecting to dynamo client"); - throw new Error(error); - } - } - - // Helper function to get playback table data - public async getPacketData(timestamp: string) { - const params: QueryCommandInput = { - ExpressionAttributeNames: { "#ts": "timestamp" }, - ExpressionAttributeValues: { ":tsVal": Number(timestamp) }, - IndexName: this.packetTableIndexName, - KeyConditionExpression: "#ts = :tsVal", - TableName: this.packetTableName, - }; - - try { - const { Items } = await this.client.send(new QueryCommand(params)); - return Items[0]; - } catch (error) { - logger.error("Error getting playback table data: " + error.message); - throw new Error(error.message); - } - } - - public async scanPacketDataBetweenDates( - startUTCDate: number, - endUTCDate: number, - ) { - try { - const queryParams: QueryCommandInput = { - ExpressionAttributeNames: { - "#pk": "type", - "#ts": "timestamp", - }, - ExpressionAttributeValues: { - ":end": endUTCDate, - ":start": startUTCDate, - ":type": "packet", - }, - IndexName: this.packetTableIndexName, - KeyConditionExpression: "#pk = :type AND #ts BETWEEN :start AND :end", - TableName: this.packetTableName, - }; - - const data = await this.client.send(new QueryCommand(queryParams)); - return data.Items ?? []; - } catch (error) { - logger.error("Error Scanning Packets between Dates", error); - throw new Error("Error Scanning Packets between Dates"); - } - } - - public async getDrivers() { - try { - const command = new ScanCommand({ TableName: this.driverTableName }); - const response = await this.client.send(command); - return response.Items; - } catch (error) { - logger.error("Error getting drivers"); - throw new Error(error); - } - } - - public async getDriverNameUsingRfid(Rfid: string) { - try { - const command = new GetCommand({ - Key: { Rfid: Rfid }, - TableName: this.driverTableName, - }); - const response = await this.client.send(command); - - if (!response.Item) { - logger.warn(`No item found for Rfid: ${Rfid}`); - return "Driver not found"; - } - - return response.Item.driver; - } catch (error) { - logger.error("Error getting driver name using the given Rfid"); - throw new Error(error.message); - } - } - - public async getDriverLaps(Rfid: string) { - try { - const lapCommand = new QueryCommand({ - ExpressionAttributeNames: { "#ts": "timestamp" }, - ExpressionAttributeValues: { ":Rfid": Rfid, ":minTimestamp": 0 }, - KeyConditionExpression: "Rfid = :Rfid AND #ts >= :minTimestamp", - ScanIndexForward: false, - TableName: this.lapTableName, - }); - - const lapResponse = await this.client.send(lapCommand); - return lapResponse.Items || []; - } catch (error) { - logger.error("Error getting lap data for driver", error); - throw new Error(error.message || "Failed to fetch driver laps"); - } - } - - // Helper function to get lap table data - public async getLapData() { - try { - const command = new ScanCommand({ TableName: this.lapTableName }); - - const response = await this.client.send(command); - return response.Items; - } catch (error) { - logger.error("Error getting all lap table data"); - throw new Error(error); - } - } - - // // Helper function to put data into the packet table - public async insertPacketData( - packet: ITelemetryData, - ): Promise { - try { - const command = new PutCommand({ - Item: { - data: packet, - id: uuidv4(), - timestamp: packet.TimeStamp, - type: "packet", - }, - TableName: this.packetTableName, - }); - const response = await this.client.send(command); - return { - httpsStatusCode: response.$metadata.httpStatusCode, - requestId: response.$metadata.requestId, - }; - } catch (error) { - logger.error("Error inserting playback table data"); - throw new Error(error); - } - } - - // function for inserting lap timestamp via geofence into its table - public async insertIntoGpsLapCountTable( - rfid: string, - timestamp: number, - ): Promise { - try { - const command = new PutCommand({ - Item: { - Rfid: rfid ?? "unknown driver", - timestamp: timestamp, - type: "gps-lap", - }, - TableName: this.gpsTableName, - }); - const response = await this.client.send(command); - return { - httpsStatusCode: response.$metadata.httpStatusCode, - requestId: response.$metadata.requestId, - }; - } catch (error) { - logger.error("Error inserting gps table data"); - throw new Error(error); - } - } - - // // Helper function to put data into the lap table - public async insertLapData(packet: ILapData): Promise { - try { - const command = new PutCommand({ - Item: { - Rfid: packet.Rfid ?? "unknown driver", - data: packet.data, - id: uuidv4(), - timestamp: packet.timestamp, - type: "lap", - }, - TableName: this.lapTableName, - }); - const response = await this.client.send(command); - return { - httpsStatusCode: response.$metadata.httpStatusCode, - requestId: response.$metadata.requestId, - }; - } catch (error) { - logger.error("Error inserting lap table data"); - throw new Error(error); - } - } - - // // Helper function getting first and last playback packets - public async getFirstAndLastPacketDates(): Promise<{ - firstDateUTC: number | null; - lastDateUTC: number | null; - }> { - try { - const firstCommand = new QueryCommand({ - ExpressionAttributeNames: { - "#pk": "type", - }, - ExpressionAttributeValues: { - ":pkValue": "packet", - }, - IndexName: this.packetTableIndexName, - KeyConditionExpression: "#pk = :pkValue", - Limit: 1, - ScanIndexForward: true, - TableName: this.packetTableName, - }); - - const lastCommand = new QueryCommand({ - ExpressionAttributeNames: { - "#pk": "type", - }, - ExpressionAttributeValues: { - ":pkValue": "packet", - }, - IndexName: this.packetTableIndexName, - KeyConditionExpression: "#pk = :pkValue", - Limit: 1, - ScanIndexForward: false, - TableName: this.packetTableName, - }); - - const firstResponse = await this.client.send(firstCommand); - const lastResponse = await this.client.send(lastCommand); - - if (!firstResponse.Items?.length || !lastResponse.Items?.length) { - throw new Error("No packet data found"); - } - - const firstDateUTC = Number(firstResponse.Items[0].timestamp); - const lastDateUTC = Number(lastResponse.Items[0].timestamp); - - return { firstDateUTC, lastDateUTC }; - } catch (error) { - throw new Error(error.message); - } - } - - //Close the connection to the database - public close(): Promise { - return new Promise((resolve, reject) => { - this.client = null; - resolve(); - }); - } - - public async updateDriverInfo(Rfid: string, name: string) { - try { - // Ensure Rfid is a string (DynamoDB is type-sensitive) - if (typeof Rfid !== "string") { - throw new Error("Rfid must be a string"); - } - - // Check if the Rfid exists in the driver table - const getCommand = new GetCommand({ - Key: { Rfid: Rfid }, - TableName: this.driverTableName, - }); - const rfidCheckReposonse = await this.client.send(getCommand); - - if (!rfidCheckReposonse.Item) { - return { message: "Driver Rfid not found in driver table" }; - } - - // Update only the 'driver' field, keeping the existing Rfid - const updateCommand = new UpdateCommand({ - ExpressionAttributeValues: { ":name": name }, - Key: { Rfid: Rfid }, - TableName: this.driverTableName, - UpdateExpression: "SET driver = :name", - }); - - await this.client.send(updateCommand); - return { - message: `Driver name updated from ${rfidCheckReposonse.Item.driver} to ${name}`, - }; - } catch (error) { - logger.error("Error updating driver info: " + error.message); - throw new Error(error.message); - } - } -} - -export default DynamoDB; diff --git a/packages/server/src/datasources/DynamoDB/DynamoDB.types.ts b/packages/server/src/datasources/DynamoDB/DynamoDB.types.ts deleted file mode 100644 index f7d875ae..00000000 --- a/packages/server/src/datasources/DynamoDB/DynamoDB.types.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { type ILapData, type ITelemetryData } from "@shared/helios-types"; - -export interface DynamoDBtypes { - close(): Promise; - client: DynamoDBClient; - getLapData(timestamp: string): void; - getPacketData(timestamp: string): void; - scanPacketDataBetweenDates(startUTCDate: number, endUTCDate: number): void; - insertLapData(packet: ILapData): Promise; - insertPacketData(packet: ITelemetryData): Promise; - getDrivers(): void; - getDriverNameUsingRfid(Rfid: string): Promise; - getDriverLaps(Rfid: string): void; -} - -export interface GenericResponse { - httpsStatusCode: number; - requestId: string; -} diff --git a/packages/server/src/datasources/SolarMQTTClient/SolarMQTTClient.ts b/packages/server/src/datasources/SolarMQTTClient/SolarMQTTClient.ts index aa0a0009..3831a9c5 100644 --- a/packages/server/src/datasources/SolarMQTTClient/SolarMQTTClient.ts +++ b/packages/server/src/datasources/SolarMQTTClient/SolarMQTTClient.ts @@ -51,7 +51,7 @@ export class SolarMQTTClient implements SolarMQTTClientType { this.backendController.carLatency?.toString() || "-1"; try { const driverName = this.latestRfid - ? await this.backendController.dynamoDB.getDriverNameUsingRfid( + ? await this.backendController.timescaleDB.getDriverNameUsingRfid( this.latestRfid, ) : "Rfid not scanned";