From 3f48b74bae331a27156705d18016e8f0205ac01c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:46:30 +0000 Subject: [PATCH 1/4] Initial plan From 5363fcfb973eb88ec9f0623e113e51c96a504a3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:51:00 +0000 Subject: [PATCH 2/4] Add database seeding script with tsx support Co-authored-by: justin-phxm <113923596+justin-phxm@users.noreply.github.com> --- package.json | 4 +- scripts/seed.ts | 667 ++++++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 3 +- 3 files changed, 672 insertions(+), 2 deletions(-) create mode 100644 scripts/seed.ts diff --git a/package.json b/package.json index 993891f3..9d8d41b0 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "migrate:revert": "lerna run migrate:revert --scope=db", "migrate:reset": "lerna run migrate:reset --scope=db", "schema:sync": "lerna run schema:sync --scope=db", - "schema:drop": "lerna run schema:drop --scope=db" + "schema:drop": "lerna run schema:drop --scope=db", + "seed": "tsx scripts/seed.ts" }, "dependencies": { "@mantine/dates": "^8.0.1", @@ -53,6 +54,7 @@ "lint-staged": "^13.2.2", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.6", + "tsx": "^4.21.0", "typescript": "^5.5.4" }, "workspaces": [ diff --git a/scripts/seed.ts b/scripts/seed.ts new file mode 100644 index 00000000..a3b6a521 --- /dev/null +++ b/scripts/seed.ts @@ -0,0 +1,667 @@ +#!/usr/bin/env tsx + +/** + * Database Seeding Script + * + * This script seeds the database with fake telemetry data for development and testing. + * It generates: + * - 5 Driver records + * - 1,000 TelemetryPacket records + * - 100 Lap records (derived from packets, approximately 1 lap per 10 packets) + * + * Usage: npm run seed or yarn seed + */ + +import "reflect-metadata"; +import { AppDataSource } from "../packages/db/src/data-source"; +import { Driver } from "../packages/db/src/entities/Driver.entity"; +import { TelemetryPacket } from "../packages/db/src/entities/TelemetryPacket.entity"; +import { Lap } from "../packages/db/src/entities/Lap.entity"; +import { generateFakeTelemetryData } from "../packages/shared/src/functions"; +import { faker } from "@faker-js/faker"; + +// Constants +const NUM_DRIVERS = 5; +const NUM_PACKETS = 1000; +const NUM_LAPS = 100; +const PACKETS_PER_LAP = Math.floor(NUM_PACKETS / NUM_LAPS); + +/** + * Generate fake driver records + */ +function generateDrivers(count: number): Driver[] { + const drivers: Driver[] = []; + for (let i = 0; i < count; i++) { + const driver = new Driver(); + driver.Rfid = faker.string.numeric(10); // Generate 10-digit RFID + driver.Name = faker.person.fullName(); + drivers.push(driver); + } + return drivers; +} + +/** + * Convert ITelemetryData to TelemetryPacket entity + */ +function convertToTelemetryPacket( + data: ReturnType, + rfid: string, + timestamp: Date +): TelemetryPacket { + const packet = new TelemetryPacket(); + + // Set primary keys + packet.Timestamp = timestamp; + packet.Rfid = rfid; + + // Set metadata + packet.RaceName = "Test Race"; + packet.Title = data.Title; + + // B3 Fields + packet.Acceleration = data.B3.Acceleration; + packet.B3Heartbeat = data.B3.B3Heartbeat; + packet.BrakeLightSignalStatus = data.B3.BrakeLightSignalStatus; + packet.BrakeSwitchDigital = data.B3.BrakeSwitchDigital; + packet.DaytimeRunningLightSignalStatus = data.B3.DaytimeRunningLightSignalStatus; + packet.ForwardDigital = data.B3.ForwardDigital; + packet.HandbrakeSwitchDigital = data.B3.HandbrakeSwitchDigital; + packet.HazardLightsInput = data.B3.HazardLightsInput; + packet.HeadlightsSwitchInput = data.B3.HeadightsSwitchInput; + packet.HeadlightSignalStatus = data.B3.HeadlightSignalStatus; + packet.HornSignalStatus = data.B3.HornSignalStatus; + packet.HornSwitchDigital = data.B3.HornSwitchDigital; + packet.LapDigital = data.B3.LapDigital; + packet.LeftSignalInput = data.B3.LeftSignalInput; + packet.LeftSignalStatus = data.B3.LeftSignalStatus; + packet.MotorResetDigital = data.B3.MotorResetDigital; + packet.NeutralDigital = data.B3.NeutralDigital; + packet.RaceModeDigital = data.B3.RaceModeDigital; + packet.RegenBraking = data.B3.RegenBraking; + packet.ReverseDigital = data.B3.ReverseDigital; + packet.RightSignalInput = data.B3.RightSignalInput; + packet.RightSignalStatus = data.B3.RightSignalStatus; + + // Battery Fields + packet.AlwaysOnSignalStatus = data.Battery.AlwaysOnSignalStatus; + packet.AverageCellVoltage = data.Battery.AverageCellVoltage; + packet.AverageTemperature = data.Battery.AverageTemperature; + packet.BmuAlive = data.Battery.BmuAlive; + packet.ChargeRelayEnabled = data.Battery.ChargeRelayEnabled; + packet.ChargerSafetyEnabled = data.Battery.ChargerSafetyEnabled; + packet.DischargeRelayEnabled = data.Battery.DischargeRelayEnabled; + packet.FanSpeed = data.Battery.FanSpeed; + packet.FanVoltage = data.Battery.FanVoltage; + packet.HighCellVoltage = data.Battery.HighCellVoltage; + packet.HighCellVoltageId = data.Battery.HighCellVoltageId; + packet.HighTemperature = data.Battery.HighTemperature; + packet.HighThermistorId = data.Battery.HighThermistorId; + packet.Input12v = data.Battery.Input12V; + packet.InternalTemperature = data.Battery.InternalTemperature; + packet.IsChargingSignalStatus = data.Battery.IsChargingSignalStatus; + packet.IsReadySignalStatus = data.Battery.IsReadySignalStatus; + packet.LowCellVoltage = data.Battery.LowCellVoltage; + packet.LowCellVoltageId = data.Battery.LowCellVoltageId; + packet.LowTemperature = data.Battery.LowTemperature; + packet.LowThermistorId = data.Battery.LowThermistorId; + packet.MalfunctionIndicatorActive = data.Battery.MalfunctionIndicatorActive; + packet.MaximumCellVoltage = data.Battery.MaximumCellVoltage; + packet.MaximumPackVoltage = data.Battery.MaximumPackVoltage; + packet.MinimumCellVoltage = data.Battery.MinimumCellVoltage; + packet.MinimumPackVoltage = data.Battery.MinimumPackVoltage; + packet.MultiPurposeInputSignalStatus = data.Battery.MultiPurposeInputSignalStatus; + packet.PackAmphours = data.Battery.PackAmphours; + packet.PackCurrent = data.Battery.PackCurrent; + packet.PackDepthOfDischarge = data.Battery.PackDepthOfDischarge; + packet.PackStateOfCharge = data.Battery.PackStateOfCharge; + packet.PackVoltage = data.Battery.PackVoltage; + packet.PopulatedCells = data.Battery.PopulatedCells; + packet.RequestedFanSpeed = data.Battery.RequestedFanSpeed; + + // Battery Faults - Errors + packet.ErrorAlwaysOnSupplyFault = data.BatteryFaults.Errors.AlwaysOnSupplyFault; + packet.ErrorCanbusCommunicationFault = data.BatteryFaults.Errors.CanbusCommunicationFault; + packet.ErrorChargeLimitEnforcementFault = data.BatteryFaults.Errors.ChargeLimitEnforcementFault; + packet.ErrorChargerSafetyRelayFault = data.BatteryFaults.Errors.ChargerSafetyRelayFault; + packet.ErrorCurrentSensorFault = data.BatteryFaults.Errors.CurrentSensorFault; + packet.ErrorDischargeLimitEnforcementFault = data.BatteryFaults.Errors.DischargeLimitEnforcementFault; + packet.ErrorFanMonitorFault = data.BatteryFaults.Errors.FanMonitorFault; + packet.ErrorHighVoltageIsolationFault = data.BatteryFaults.Errors.HighVoltageIsolationFault; + packet.ErrorInternalCommunicationFault = data.BatteryFaults.Errors.InternalCommunicationFault; + packet.ErrorInternalConversionFault = data.BatteryFaults.Errors.InternalConversionFault; + packet.ErrorInternalLogicFault = data.BatteryFaults.Errors.InternalLogicFault; + packet.ErrorInternalMemoryFault = data.BatteryFaults.Errors.InternalMemoryFault; + packet.ErrorInternalThermistorFault = data.BatteryFaults.Errors.InternalThermistorFault; + packet.ErrorLowCellVoltageFault = data.BatteryFaults.Errors.LowCellVoltageFault; + packet.ErrorOpenWiringFault = data.BatteryFaults.Errors.OpenWiringFault; + packet.ErrorPackVoltageSensorFault = data.BatteryFaults.Errors.PackVoltageSensorFault; + packet.ErrorPowerSupply12vFault = data.BatteryFaults.Errors.PowerSupply12VFault; + packet.ErrorThermistorFault = data.BatteryFaults.Errors.ThermistorFault; + packet.ErrorVoltageRedundancyFault = data.BatteryFaults.Errors.VoltageRedundancyFault; + packet.ErrorWeakCellFault = data.BatteryFaults.Errors.WeakCellFault; + packet.ErrorWeakPackFault = data.BatteryFaults.Errors.WeakPackFault; + + // Battery Faults - Warnings + packet.WarningCclReducedDueToAlternateCurrentLimit = data.BatteryFaults.Warnings.CclReducedDueToAlternateCurrentLimit; + packet.WarningCclReducedDueToChargerLatch = data.BatteryFaults.Warnings.CclReducedDueToChargerLatch; + packet.WarningCclReducedDueToHighCellResistance = data.BatteryFaults.Warnings.CclReducedDueToHighCellResistance; + packet.WarningCclReducedDueToHighCellVoltage = data.BatteryFaults.Warnings.CclReducedDueToHighCellVoltage; + packet.WarningCclReducedDueToHighPackVoltage = data.BatteryFaults.Warnings.CclReducedDueToHighPackVoltage; + packet.WarningCclReducedDueToHighSoc = data.BatteryFaults.Warnings.CclReducedDueToHighSoc; + packet.WarningCclReducedDueToTemperature = data.BatteryFaults.Warnings.CclReducedDueToTemperature; + packet.WarningDclAndCclReducedDueToCommunicationFailsafe = data.BatteryFaults.Warnings.DclAndCclReducedDueToCommunicationFailsafe; + packet.WarningDclAndCclReducedDueToVoltageFailsafe = data.BatteryFaults.Warnings.DclAndCclReducedDueToVoltageFailsafe; + packet.WarningDclReducedDueToHighCellResistance = data.BatteryFaults.Warnings.DclReducedDueToHighCellResistance; + packet.WarningDclReducedDueToLowCellVoltage = data.BatteryFaults.Warnings.DclReducedDueToLowCellVoltage; + packet.WarningDclReducedDueToLowPackVoltage = data.BatteryFaults.Warnings.DclReducedDueToLowPackVoltage; + packet.WarningDclReducedDueToLowSoc = data.BatteryFaults.Warnings.DclReducedDueToLowSoc; + packet.WarningDclReducedDueToTemperature = data.BatteryFaults.Warnings.DclReducedDueToTemperature; + + // Contactor Fields + packet.ArrayBpsError = data.Contactor.ArrayBPSError; + packet.ArrayChargeCurrent = data.Contactor.ArrayChargeCurrent; + packet.ArrayContactorClosed = data.Contactor.ArrayContactorClosed; + packet.ArrayContactorClosing = data.Contactor.ArrayContactorClosing; + packet.ArrayContactorError = data.Contactor.ArrayContactorError; + packet.ArrayHeartbeat = data.Contactor.ArrayHeartbeat; + packet.ArrayLineCurrent = data.Contactor.ArrayLineCurrent; + packet.ArrayPrechargerClosed = data.Contactor.ArrayPrechargerClosed; + packet.ArrayPrechargerClosing = data.Contactor.ArrayPrechargerClosing; + packet.ArrayPrechargerError = data.Contactor.ArrayPrechargerError; + + packet.ChargeBpsError = data.Contactor.ChargeBPSError; + packet.ChargeChargeCurrent = data.Contactor.ChargeChargeCurrent; + packet.ChargeContactorClosed = data.Contactor.ChargeContactorClosed; + packet.ChargeContactorClosing = data.Contactor.ChargeContactorClosing; + packet.ChargeContactorError = data.Contactor.ChargeContactorError; + packet.ChargeHeartbeat = data.Contactor.ChargeHeartbeat; + packet.ChargeLineCurrent = data.Contactor.ChargeLineCurrent; + packet.ChargePrechargerClosed = data.Contactor.ChargePrechargerClosed; + packet.ChargePrechargerClosing = data.Contactor.ChargePrechargerClosing; + packet.ChargePrechargerError = data.Contactor.ChargePrechargerError; + + packet.CommonChargeCurrent = data.Contactor.CommonChargeCurrent; + packet.CommonContactorClosed = data.Contactor.CommonContactorClosed; + packet.CommonContactorClosing = data.Contactor.CommonContactorClosing; + packet.CommonContactorError = data.Contactor.CommonContactorError; + packet.CommonContactorOpeningError = data.Contactor.CommonContactorOpeningError; + packet.CommonHeartbeat = data.Contactor.CommonHeartbeat; + packet.CommonLineCurrent = data.Contactor.CommonLineCurrent; + packet.CommonPrechargerClosed = data.Contactor.CommonPrechargerClosed; + packet.CommonPrechargerClosing = data.Contactor.CommonPrechargerClosing; + packet.CommonPrechargerError = data.Contactor.CommonPrechargerError; + + packet.LvBpsError = data.Contactor.LvBpsError; + packet.LvChargeCurrent = data.Contactor.LvChargeCurrent; + packet.LvContactorClosed = data.Contactor.LvContactorClosed; + packet.LvContactorClosing = data.Contactor.LvContactorClosing; + packet.LvContactorError = data.Contactor.LvContactorError; + packet.LvHeartbeat = data.Contactor.LvHeartbeat; + packet.LvLineCurrent = data.Contactor.LvLineCurrent; + packet.LvPrechargerClosed = data.Contactor.LvPrechargerClosed; + packet.LvPrechargerClosing = data.Contactor.LvPrechargerClosing; + packet.LvPrechargerError = data.Contactor.LvPrechargerError; + + packet.MotorBpsError = data.Contactor.MotorBPSError; + packet.MotorChargeCurrent = data.Contactor.MotorChargeCurrent; + packet.MotorContactorClosed = data.Contactor.MotorContactorClosed; + packet.MotorContactorClosing = data.Contactor.MotorContactorClosing; + packet.MotorContactorError = data.Contactor.MotorContactorError; + packet.MotorHeartbeat = data.Contactor.MotorHeartbeat; + packet.MotorLineCurrent = data.Contactor.MotorLineCurrent; + packet.MotorPrechargerClosed = data.Contactor.MotorPrechargerClosed; + packet.MotorPrechargerClosing = data.Contactor.MotorPrechargerClosing; + packet.MotorPrechargerError = data.Contactor.MotorPrechargerError; + + // KeyMotor Fields + packet.BusCurrentOut = data.KeyMotor.BusCurrentOut; + packet.KeyMotorVelocity = data.KeyMotor.KeyMotorVelocity; + packet.MotorCurrent = data.KeyMotor.MotorCurrent; + + // MBMS Fields + packet.AbattDisable = data.MBMS.AbattDisable; + packet.ArrayContactorCommand = data.MBMS.ArrayContactorCommand; + packet.ArrayHeartbeatDeadTrip = data.MBMS.ArrayHeartbeatDeadTrip; + packet.ArrayHighCurrentTrip = data.MBMS.ArrayHighCurrentTrip; + packet.ArrayHighCurrentWarning = data.MBMS.ArrayHighCurrentWarning; + packet.AuxiliaryBatteryVoltage = data.MBMS.AuxiliaryBatteryVoltage; + packet.CanOc12vWarning = data.MBMS.CanOc12VWarning; + packet.ChargeContactorCommand = data.MBMS.ChargeContactorCommand; + packet.ChargeEnable = data.MBMS.ChargeEnable; + packet.ChargeHeartbeatDeadTrip = data.MBMS.ChargeHeartbeatDeadTrip; + packet.ChargeHighCurrentTrip = data.MBMS.ChargeHighCurrentTrip; + packet.ChargeHighCurrentWarning = data.MBMS.ChargeHighCurrentWarning; + packet.ChargeSafety = data.MBMS.ChargeSafety; + packet.ChargeShouldTrip = data.MBMS.ChargeShouldTrip; + packet.ChgFault = data.MBMS.ChgFault; + packet.ChgLvEn = data.MBMS.ChgLvEn; + packet.ChgOn = data.MBMS.ChgOn; + packet.CommonContactorCommand = data.MBMS.CommonContactorCommand; + packet.CommonHeartbeatDeadTrip = data.MBMS.CommonHeartbeatDeadTrip; + packet.CommonHighCurrentTrip = data.MBMS.CommonHighCurrentTrip; + packet.CommonHighCurrentWarning = data.MBMS.CommonHighCurrentWarning; + packet.ContactorConnectedUnexpectedlyTrip = data.MBMS.ContactorConnectedUnexpectedlyTrip; + packet.ContactorDisconnectedUnexpectedlyTrip = data.MBMS.ContactorDisconnectedUnexpectedlyTrip; + packet.DcdcFault = data.MBMS.DcdcFault; + packet.DcdcOn = data.MBMS.DcdcOn; + packet.DischargeEnable = data.MBMS.DischargeEnable; + packet.DischargeShouldTrip = data.MBMS.DischargeShouldTrip; + packet.En1 = data.MBMS.En1; + packet.EsdEnabledTrip = data.MBMS.EsdEnabledTrip; + packet.ExternalShutdown = data.MBMS.ExternalShutdown; + packet.Heartbeat = data.MBMS.Heartbeat; + packet.HighCellVoltageTrip = data.MBMS.HighCellVoltageTrip; + packet.HighCellVoltageWarning = data.MBMS.HighCellVoltageWarning; + packet.HighTemperatureTrip = data.MBMS.HighTemperatureTrip; + packet.HighTemperatureWarning = data.MBMS.HighTemperatureWarning; + packet.Key = data.MBMS.Key; + packet.LowCellVoltageTrip = data.MBMS.LowCellVoltageTrip; + packet.LowCellVoltageWarning = data.MBMS.LowCellVoltageWarning; + packet.LowTemperatureTrip = data.MBMS.LowTemperatureTrip; + packet.LowTemperatureWarning = data.MBMS.LowTemperatureWarning; + packet.LvContactorCommand = data.MBMS.LvContactorCommand; + packet.LvHeartbeatDeadTrip = data.MBMS.LvHeartbeatDeadTrip; + packet.LvHighCurrentTrip = data.MBMS.LvHighCurrentTrip; + packet.LvHighCurrentWarning = data.MBMS.LvHighCurrentWarning; + packet.MainPowerSwitch = data.MBMS.MainPowerSwitch; + packet.MotorContactorCommand = data.MBMS.MotorContactorCommand; + packet.MotorHeartbeatDeadTrip = data.MBMS.MotorHeartbeatDeadTrip; + packet.MotorHighCurrentTrip = data.MBMS.MotorHighCurrentTrip; + packet.MotorHighCurrentWarning = data.MBMS.MotorHighCurrentWarning; + packet.MpsDisabledTrip = data.MBMS.MpsDisabledTrip; + packet.OrionCanReceivedRecently = data.MBMS.OrionCanReceivedRecently; + packet.OrionMessageTimeoutTrip = data.MBMS.OrionMessageTimeoutTrip; + packet.ProtectionTrip = data.MBMS.ProtectionTrip; + packet.StartupState = data.MBMS.StartupState; + packet.StrobeBmsLight = data.MBMS.StrobeBmsLight; + packet.SystemState = data.MBMS.SystemState; + packet.ThreeAOc = data.MBMS.ThreeAOc; + + // MotorDetails0 Fields + packet.Motor0ActiveMotor = data.MotorDetails0.ActiveMotor; + packet.Motor0BemfD = data.MotorDetails0.BEMF_D; + packet.Motor0BemfQ = data.MotorDetails0.BEMF_Q; + packet.Motor0BusCurrent = data.MotorDetails0.BusCurrent; + packet.Motor0BusVoltage = data.MotorDetails0.BusVoltage; + packet.Motor0DcBusAh = data.MotorDetails0.DC_Bus_Ah; + packet.Motor0DspBoardTemperature = data.MotorDetails0.DspBoardTemperature; + packet.Motor0ErrorFlags = data.MotorDetails0.ErrorFlags; + packet.Motor0HeatsinkTemperature = data.MotorDetails0.HeatsinkTemperature; + packet.Motor0Id = data.MotorDetails0.Id; + packet.Motor0Iq = data.MotorDetails0.Iq; + packet.Motor0LimitFlags = data.MotorDetails0.LimitFlags; + packet.Motor0MotorTemperature = data.MotorDetails0.MotorTemperature; + packet.Motor0MotorVelocity = data.MotorDetails0.MotorVelocity; + packet.Motor0Odometer = data.MotorDetails0.Odometer; + packet.Motor0PhaseCurrentB = data.MotorDetails0.PhaseCurrentB; + packet.Motor0PhaseCurrentC = data.MotorDetails0.PhaseCurrentC; + packet.Motor0RxErrorCount = data.MotorDetails0.RxErrorCount; + packet.Motor0SerialNumber = data.MotorDetails0.SerialNumber; + packet.Motor0SlipSpeed = data.MotorDetails0.SlipSpeed; + packet.Motor0Supply15v = data.MotorDetails0.Supply15V; + packet.Motor0Supply1v9 = data.MotorDetails0.Supply1V9; + packet.Motor0Supply3v3 = data.MotorDetails0.Supply3V3; + packet.Motor0TritiumId = data.MotorDetails0.TritiumId; + packet.Motor0TxErrorCount = data.MotorDetails0.TxErrorCount; + packet.Motor0Vd = data.MotorDetails0.Vd; + packet.Motor0VehicleVelocity = data.MotorDetails0.VehicleVelocity; + packet.Motor0Vq = data.MotorDetails0.Vq; + + // MotorDetails1 Fields + packet.Motor1ActiveMotor = data.MotorDetails1.ActiveMotor; + packet.Motor1BemfD = data.MotorDetails1.BEMF_D; + packet.Motor1BemfQ = data.MotorDetails1.BEMF_Q; + packet.Motor1BusCurrent = data.MotorDetails1.BusCurrent; + packet.Motor1BusVoltage = data.MotorDetails1.BusVoltage; + packet.Motor1DcBusAh = data.MotorDetails1.DC_Bus_Ah; + packet.Motor1DspBoardTemperature = data.MotorDetails1.DspBoardTemperature; + packet.Motor1ErrorFlags = data.MotorDetails1.ErrorFlags; + packet.Motor1HeatsinkTemperature = data.MotorDetails1.HeatsinkTemperature; + packet.Motor1Id = data.MotorDetails1.Id; + packet.Motor1Iq = data.MotorDetails1.Iq; + packet.Motor1LimitFlags = data.MotorDetails1.LimitFlags; + packet.Motor1MotorTemperature = data.MotorDetails1.MotorTemperature; + packet.Motor1MotorVelocity = data.MotorDetails1.MotorVelocity; + packet.Motor1Odometer = data.MotorDetails1.Odometer; + packet.Motor1PhaseCurrentB = data.MotorDetails1.PhaseCurrentB; + packet.Motor1PhaseCurrentC = data.MotorDetails1.PhaseCurrentC; + packet.Motor1RxErrorCount = data.MotorDetails1.RxErrorCount; + packet.Motor1SerialNumber = data.MotorDetails1.SerialNumber; + packet.Motor1SlipSpeed = data.MotorDetails1.SlipSpeed; + packet.Motor1Supply15v = data.MotorDetails1.Supply15V; + packet.Motor1Supply1v9 = data.MotorDetails1.Supply1V9; + packet.Motor1Supply3v3 = data.MotorDetails1.Supply3V3; + packet.Motor1TritiumId = data.MotorDetails1.TritiumId; + packet.Motor1TxErrorCount = data.MotorDetails1.TxErrorCount; + packet.Motor1Vd = data.MotorDetails1.Vd; + packet.Motor1VehicleVelocity = data.MotorDetails1.VehicleVelocity; + packet.Motor1Vq = data.MotorDetails1.Vq; + + // MPPT Fields + packet.Mppt0Ch0ArrayCurrent = data.MPPT.Mppt0Ch0ArrayCurrent; + packet.Mppt0Ch0ArrayVoltage = data.MPPT.Mppt0Ch0ArrayVoltage; + packet.Mppt0Ch0BatteryVoltage = data.MPPT.Mppt0Ch0BatteryVoltage; + packet.Mppt0Ch0UnitTemperature = data.MPPT.Mppt0Ch0UnitTemperature; + packet.Mppt0Ch1ArrayCurrent = data.MPPT.Mppt0Ch1ArrayCurrent; + packet.Mppt0Ch1ArrayVoltage = data.MPPT.Mppt0Ch1ArrayVoltage; + packet.Mppt0Ch1BatteryVoltage = data.MPPT.Mppt0Ch1BatteryVoltage; + packet.Mppt0Ch1UnitTemperature = data.MPPT.Mppt0Ch1UnitTemperature; + + packet.Mppt1Ch0ArrayCurrent = data.MPPT.Mppt1Ch0ArrayCurrent; + packet.Mppt1Ch0ArrayVoltage = data.MPPT.Mppt1Ch0ArrayVoltage; + packet.Mppt1Ch0BatteryVoltage = data.MPPT.Mppt1Ch0BatteryVoltage; + packet.Mppt1Ch0UnitTemperature = data.MPPT.Mppt1Ch0UnitTemperature; + packet.Mppt1Ch1ArrayCurrent = data.MPPT.Mppt1Ch1ArrayCurrent; + packet.Mppt1Ch1ArrayVoltage = data.MPPT.Mppt1Ch1ArrayVoltage; + packet.Mppt1Ch1BatteryVoltage = data.MPPT.Mppt1Ch1BatteryVoltage; + packet.Mppt1Ch1UnitTemperature = data.MPPT.Mppt1Ch1UnitTemperature; + + packet.Mppt2Ch0ArrayCurrent = data.MPPT.Mppt2Ch0ArrayCurrent; + packet.Mppt2Ch0ArrayVoltage = data.MPPT.Mppt2Ch0ArrayVoltage; + packet.Mppt2Ch0BatteryVoltage = data.MPPT.Mppt2Ch0BatteryVoltage; + packet.Mppt2Ch0UnitTemperature = data.MPPT.Mppt2Ch0UnitTemperature; + packet.Mppt2Ch1ArrayCurrent = data.MPPT.Mppt2Ch1ArrayCurrent; + packet.Mppt2Ch1ArrayVoltage = data.MPPT.Mppt2Ch1ArrayVoltage; + packet.Mppt2Ch1BatteryVoltage = data.MPPT.Mppt2Ch1BatteryVoltage; + packet.Mppt2Ch1UnitTemperature = data.MPPT.Mppt2Ch1UnitTemperature; + + packet.Mppt3Ch0ArrayCurrent = data.MPPT.Mppt3Ch0ArrayCurrent; + packet.Mppt3Ch0ArrayVoltage = data.MPPT.Mppt3Ch0ArrayVoltage; + packet.Mppt3Ch0BatteryVoltage = data.MPPT.Mppt3Ch0BatteryVoltage; + packet.Mppt3Ch0UnitTemperature = data.MPPT.Mppt3Ch0UnitTemperature; + packet.Mppt3Ch1ArrayCurrent = data.MPPT.Mppt3Ch1ArrayCurrent; + packet.Mppt3Ch1ArrayVoltage = data.MPPT.Mppt3Ch1ArrayVoltage; + packet.Mppt3Ch1BatteryVoltage = data.MPPT.Mppt3Ch1BatteryVoltage; + packet.Mppt3Ch1UnitTemperature = data.MPPT.Mppt3Ch1UnitTemperature; + + // ProximitySensors Fields + packet.ProximitySensor1 = data.ProximitySensors.ProximitySensor1; + packet.ProximitySensor2 = data.ProximitySensors.ProximitySensor2; + packet.ProximitySensor3 = data.ProximitySensors.ProximitySensor3; + packet.ProximitySensor4 = data.ProximitySensors.ProximitySensor4; + + // Telemetry Fields (GPS and MPU) + packet.GpsAdditionalFlags = data.Telemetry.GpsAdditionalFlags; + packet.GpsDay = data.Telemetry.GpsDay; + packet.GpsFixStatusFlags = data.Telemetry.GpsFixStatusFlags; + packet.GpsHour = data.Telemetry.GpsHour; + packet.GpsLatitude = data.Telemetry.GpsLatitude; + packet.GpsLongitude = data.Telemetry.GpsLongitude; + packet.GpsMinute = data.Telemetry.GpsMinute; + packet.GpsMonth = data.Telemetry.GpsMonth; + packet.GpsSecond = data.Telemetry.GpsSecond; + packet.GpsValidityFlags = data.Telemetry.GpsValidityFlags; + packet.GpsYear = data.Telemetry.GpsYear; + + packet.MpuAccelerationX = data.Telemetry.MpuAccelerationX; + packet.MpuAccelerationY = data.Telemetry.MpuAccelerationY; + packet.MpuAccelerationZ = data.Telemetry.MpuAccelerationZ; + packet.MpuRotationX = data.Telemetry.MpuRotationX; + packet.MpuRotationY = data.Telemetry.MpuRotationY; + packet.MpuRotationZ = data.Telemetry.MpuRotationZ; + packet.MpuTemperature = data.Telemetry.MpuTemperature; + + return packet; +} + +/** + * Generate telemetry packets with sequential timestamps + */ +function generateTelemetryPackets( + drivers: Driver[], + count: number +): TelemetryPacket[] { + const packets: TelemetryPacket[] = []; + const baseTime = new Date(); + baseTime.setHours(baseTime.getHours() - 2); // Start 2 hours ago + + for (let i = 0; i < count; i++) { + // Select random driver + const driver = drivers[i % drivers.length]; + + // Generate sequential timestamp (1 second apart) + const timestamp = new Date(baseTime.getTime() + i * 1000); + + // Generate fake telemetry data + const fakeData = generateFakeTelemetryData(); + + // Convert to entity + const packet = convertToTelemetryPacket(fakeData, driver.Rfid, timestamp); + + packets.push(packet); + } + + return packets; +} + +/** + * Calculate lap statistics from a group of packets + * This mimics the logic in LapController.ts + */ +function calculateLapStats(packets: TelemetryPacket[]) { + if (packets.length === 0) { + return { + LapTime: 0, + TotalPowerIn: 0, + TotalPowerOut: 0, + NetPowerOut: 0, + Distance: 0, + EnergyConsumed: 0, + AmpHours: 0, + AveragePackCurrent: 0, + BatterySecondsRemaining: 0, + AverageSpeed: 0, + }; + } + + // Calculate lap time (milliseconds) + const startTime = packets[0].Timestamp.getTime(); + const endTime = packets[packets.length - 1].Timestamp.getTime(); + const lapTime = endTime - startTime; + + // Calculate average pack current + const totalPackCurrent = packets.reduce((sum, p) => { + return sum + (p.PackCurrent || 0); + }, 0); + const averagePackCurrent = totalPackCurrent / packets.length; + + // Calculate average speed (simplified) + const totalSpeed = packets.reduce((sum, p) => { + const motor0Speed = p.Motor0VehicleVelocity || 0; + const motor1Speed = p.Motor1VehicleVelocity || 0; + return sum + (motor0Speed + motor1Speed) / 2; + }, 0); + const averageSpeed = totalSpeed / packets.length; + + // Calculate distance (simplified - using average speed * time) + const distance = (averageSpeed * lapTime) / 3600000; // Convert ms to hours + + // Get latest amp hours + const ampHours = packets[packets.length - 1].PackAmphours || 0; + + // Simplified power calculations + const totalPowerOut = Math.abs( + packets.reduce((sum, p) => { + const packCurrent = p.PackCurrent || 0; + const packVoltage = p.PackVoltage || 0; + return sum + packCurrent * packVoltage; + }, 0) / packets.length + ); + + const totalPowerIn = Math.abs( + packets.reduce((sum, p) => { + // Simplified MPPT power calculation + let mpptPower = 0; + mpptPower += (p.Mppt0Ch0ArrayVoltage || 0) * (p.Mppt0Ch0ArrayCurrent || 0); + mpptPower += (p.Mppt1Ch0ArrayVoltage || 0) * (p.Mppt1Ch0ArrayCurrent || 0); + mpptPower += (p.Mppt2Ch0ArrayVoltage || 0) * (p.Mppt2Ch0ArrayCurrent || 0); + mpptPower += (p.Mppt3Ch0ArrayVoltage || 0) * (p.Mppt3Ch0ArrayCurrent || 0); + return sum + mpptPower; + }, 0) / packets.length + ); + + const netPowerOut = totalPowerIn - totalPowerOut; + const energyConsumed = lapTime * netPowerOut; + + // Calculate battery seconds remaining + let batterySecondsRemaining = -1; + if (averagePackCurrent !== 0) { + const amphoursLeft = averagePackCurrent >= 0 ? ampHours : 165.6 - ampHours; + const hoursRemaining = amphoursLeft / Math.abs(averagePackCurrent); + batterySecondsRemaining = Math.round(hoursRemaining * 3600); + } + + return { + LapTime: lapTime, + TotalPowerIn: totalPowerIn, + TotalPowerOut: totalPowerOut, + NetPowerOut: netPowerOut, + Distance: distance, + EnergyConsumed: energyConsumed, + AmpHours: ampHours, + AveragePackCurrent: averagePackCurrent, + BatterySecondsRemaining: batterySecondsRemaining, + AverageSpeed: averageSpeed, + }; +} + +/** + * Generate lap records derived from telemetry packets + * Creates approximately 1 lap per N packets + */ +function generateLaps(packets: TelemetryPacket[], count: number): Lap[] { + const laps: Lap[] = []; + const packetsPerLap = Math.floor(packets.length / count); + + for (let i = 0; i < count; i++) { + const startIdx = i * packetsPerLap; + const endIdx = Math.min(startIdx + packetsPerLap, packets.length); + const lapPackets = packets.slice(startIdx, endIdx); + + if (lapPackets.length === 0) continue; + + const lap = new Lap(); + + // Use the last packet's timestamp and RFID for the lap + const lastPacket = lapPackets[lapPackets.length - 1]; + lap.Timestamp = lastPacket.Timestamp; + lap.Rfid = lastPacket.Rfid; + + // Calculate lap statistics + const stats = calculateLapStats(lapPackets); + lap.LapTime = stats.LapTime; + lap.TotalPowerIn = stats.TotalPowerIn; + lap.TotalPowerOut = stats.TotalPowerOut; + lap.NetPowerOut = stats.NetPowerOut; + lap.Distance = stats.Distance; + lap.EnergyConsumed = stats.EnergyConsumed; + lap.AmpHours = stats.AmpHours; + lap.AveragePackCurrent = stats.AveragePackCurrent; + lap.BatterySecondsRemaining = stats.BatterySecondsRemaining; + lap.AverageSpeed = stats.AverageSpeed; + + laps.push(lap); + } + + return laps; +} + +/** + * Clear all data from the database (in proper dependency order) + */ +async function clearData() { + console.log("Clearing existing data..."); + + const driverRepo = AppDataSource.getRepository(Driver); + const packetRepo = AppDataSource.getRepository(TelemetryPacket); + const lapRepo = AppDataSource.getRepository(Lap); + + // Clear in dependency order (children first, then parents) + await lapRepo.delete({}); + console.log(" ✓ Cleared Lap table"); + + await packetRepo.delete({}); + console.log(" ✓ Cleared TelemetryPacket table"); + + await driverRepo.delete({}); + console.log(" ✓ Cleared Driver table"); +} + +/** + * Seed the database with fake data + */ +async function seedData() { + console.log(`\nGenerating seed data...`); + console.log(` - ${NUM_DRIVERS} drivers`); + console.log(` - ${NUM_PACKETS} telemetry packets`); + console.log(` - ${NUM_LAPS} laps`); + + // Generate drivers + console.log("\nGenerating drivers..."); + const drivers = generateDrivers(NUM_DRIVERS); + + // Generate packets + console.log("Generating telemetry packets..."); + const packets = generateTelemetryPackets(drivers, NUM_PACKETS); + + // Generate laps from packets + console.log("Generating laps from packets..."); + const laps = generateLaps(packets, NUM_LAPS); + + console.log("\nInserting data into database..."); + + // Insert in dependency order (parents first) + const driverRepo = AppDataSource.getRepository(Driver); + await driverRepo.save(drivers); + console.log(` ✓ Inserted ${drivers.length} drivers`); + + const packetRepo = AppDataSource.getRepository(TelemetryPacket); + // Insert packets in batches to avoid overwhelming the database + const batchSize = 100; + for (let i = 0; i < packets.length; i += batchSize) { + const batch = packets.slice(i, i + batchSize); + await packetRepo.save(batch); + console.log(` ✓ Inserted packets ${i + 1}-${Math.min(i + batchSize, packets.length)} of ${packets.length}`); + } + + const lapRepo = AppDataSource.getRepository(Lap); + await lapRepo.save(laps); + console.log(` ✓ Inserted ${laps.length} laps`); +} + +/** + * Main function + */ +async function main() { + try { + console.log("=".repeat(60)); + console.log("Database Seeding Script"); + console.log("=".repeat(60)); + + // Initialize database connection + console.log("\nConnecting to database..."); + await AppDataSource.initialize(); + console.log(" ✓ Connected to database"); + + // Clear existing data + await clearData(); + + // Seed new data + await seedData(); + + console.log("\n" + "=".repeat(60)); + console.log("✓ Seeding completed successfully!"); + console.log("=".repeat(60)); + + process.exit(0); + } catch (error) { + console.error("\n" + "=".repeat(60)); + console.error("✗ Seeding failed!"); + console.error("=".repeat(60)); + console.error(error); + process.exit(1); + } +} + +// Run the script +main(); diff --git a/yarn.lock b/yarn.lock index d7ce4532..7077e033 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22481,6 +22481,7 @@ __metadata: lint-staged: "npm:^13.2.2" prettier: "npm:^3.3.3" prettier-plugin-tailwindcss: "npm:^0.6.6" + tsx: "npm:^4.21.0" typescript: "npm:^5.5.4" uuid: "npm:^11.0.5" languageName: unknown @@ -24312,7 +24313,7 @@ __metadata: languageName: node linkType: hard -"tsx@npm:^4.16.2, tsx@npm:^4.6.1": +"tsx@npm:^4.16.2, tsx@npm:^4.21.0, tsx@npm:^4.6.1": version: 4.21.0 resolution: "tsx@npm:4.21.0" dependencies: From a068e46172363e7bb8bb887a2fa5f41e702071eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:52:58 +0000 Subject: [PATCH 3/4] Add seeding script documentation and ESLint ignore rule Co-authored-by: justin-phxm <113923596+justin-phxm@users.noreply.github.com> --- .eslintignore | 3 +- scripts/README.md | 114 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 scripts/README.md diff --git a/.eslintignore b/.eslintignore index b6369400..61e922f9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ -packages/db/src/entities/*.ts \ No newline at end of file +packages/db/src/entities/*.ts +scripts/*.ts \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..1144f102 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,114 @@ +# Database Seeding Script + +This directory contains scripts for seeding the development/test database with fake data. + +## Overview + +The `seed.ts` script populates the database with: +- **5 Driver records** with fake names and RFID numbers +- **1,000 TelemetryPacket records** with realistic fake sensor data +- **100 Lap records** derived from the telemetry packets (approximately 1 lap per 10 packets) + +The script is designed to be **idempotent**, meaning it can be run multiple times safely. Each run will clear existing data before inserting new seed data. + +## Prerequisites + +Before running the seeding script, ensure you have: + +1. **Database Running**: Start the TimescaleDB database: + ```bash + yarn db:up + ``` + +2. **Environment Variables**: Set up the required database environment variables. Create a `.env` file in the project root or set these variables: + ``` + DATABASE_HOST=localhost + DATABASE_PORT=5432 + DATABASE_USERNAME=your_username + DATABASE_PASSWORD=your_password + ``` + +3. **Database Schema**: Run migrations to create the necessary tables: + ```bash + yarn migrate:run + ``` + +## Usage + +To seed the database with fake data: + +```bash +yarn seed +``` + +This command will: +1. Connect to the database using TypeORM +2. Clear all existing data from the Lap, TelemetryPacket, and Driver tables +3. Generate and insert new fake data +4. Display progress information + +## Data Generation + +### Drivers +- 5 drivers are created with randomly generated names and 10-digit RFID numbers +- Each driver's RFID is used to associate packets and laps + +### Telemetry Packets +- 1,000 packets are generated using the `generateFakeTelemetryData()` function from `packages/shared/src/functions.ts` +- Packets are distributed across the 5 drivers (200 packets per driver) +- Timestamps are sequential, starting from 2 hours ago, with 1 second intervals +- All sensor fields are populated with realistic fake data + +### Laps +- 100 laps are derived from the telemetry packets +- Each lap is calculated from approximately 10 consecutive packets +- Lap statistics are computed using similar logic to the production `LapController`: + - LapTime: Time between first and last packet in the lap + - AverageSpeed: Average vehicle velocity across all packets + - Distance: Calculated from average speed and lap time + - Power metrics: TotalPowerIn, TotalPowerOut, NetPowerOut + - Battery metrics: AmpHours, AveragePackCurrent, BatterySecondsRemaining + - EnergyConsumed: Lap time × net power output + +## Implementation Details + +The script follows these key principles: + +1. **Dependency Order**: Data is deleted and inserted in the correct order to respect foreign key constraints: + - Deletion: Lap → TelemetryPacket → Driver + - Insertion: Driver → TelemetryPacket → Lap + +2. **Batch Insertion**: Telemetry packets are inserted in batches of 100 to avoid overwhelming the database + +3. **Realistic Data**: Lap calculations mirror the production `LapController` logic to ensure data is realistic for testing + +4. **Error Handling**: The script includes comprehensive error handling and will exit with an appropriate status code + +## Troubleshooting + +### Cannot Connect to Database +- Ensure the database is running: `yarn db:up` +- Check that environment variables are set correctly +- Verify the database is listening on the correct port (default: 5432) + +### Type Errors +- The script uses TypeORM entities which require `reflect-metadata` +- Ensure all dependencies are installed: `yarn install` + +### Foreign Key Violations +- The script clears tables in dependency order to avoid FK violations +- If you encounter FK errors, ensure no other processes are modifying the database during seeding + +## Development and Testing Only + +**⚠️ WARNING**: This script is intended for development and testing environments only. It will delete all existing data in the Lap, TelemetryPacket, and Driver tables. **Do NOT run this script in production**. + +## Related Files + +- Entity Definitions: + - `/packages/db/src/entities/Driver.entity.ts` + - `/packages/db/src/entities/TelemetryPacket.entity.ts` + - `/packages/db/src/entities/Lap.entity.ts` +- Data Generation: `/packages/shared/src/functions.ts` +- Database Configuration: `/packages/db/src/data-source.ts` +- Production Lap Logic: `/packages/server/src/controllers/LapController/LapController.ts` From b2b720830aafb9b60298c3d198a15af9732babb2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:54:15 +0000 Subject: [PATCH 4/4] Add clarifying comment for netPowerOut calculation Co-authored-by: justin-phxm <113923596+justin-phxm@users.noreply.github.com> --- scripts/seed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/seed.ts b/scripts/seed.ts index a3b6a521..e54fd7ee 100644 --- a/scripts/seed.ts +++ b/scripts/seed.ts @@ -500,7 +500,7 @@ function calculateLapStats(packets: TelemetryPacket[]) { }, 0) / packets.length ); - const netPowerOut = totalPowerIn - totalPowerOut; + const netPowerOut = totalPowerIn - totalPowerOut; // Note: This matches LapController.netPower() calculation const energyConsumed = lapTime * netPowerOut; // Calculate battery seconds remaining