Compare commits

..

2 Commits

2 changed files with 23 additions and 14 deletions
+10 -5
View File
@@ -75,18 +75,23 @@ describe('prediction client helpers', () => {
expect(createPredictionRequest(battery, [battery, battery, battery, battery])).toBeNull()
})
test('uses only real battery history fields in prediction requests', () => {
test('creates minimal cycle payload accepted by prediction service', () => {
const request = createPredictionRequest(battery, [battery, battery, battery, battery, battery])
const firstHistory = request?.history[0]
expect(firstHistory).toEqual({
expect(request?.battery).toMatchObject({
id: battery.id,
user_id: battery.userId,
mac: battery.mac,
power: battery.power,
power_status: battery.powerStatus,
is_low_power: MYSQL_BOOLEAN.FALSE,
})
expect(firstHistory).toEqual({
cycle: 1,
charge_capacity_ah: 3.01,
discharge_capacity_ah: 2.89,
timestamp: battery.createTime,
})
expect(Object.hasOwn(firstHistory ?? {}, 'charge_capacity_ah')).toBe(false)
expect(Object.hasOwn(firstHistory ?? {}, 'charge_energy_wh')).toBe(false)
expect(Object.hasOwn(firstHistory ?? {}, 'coulombic_efficiency_pct')).toBe(false)
})
})
+13 -9
View File
@@ -20,10 +20,9 @@ export const sohPredictionSchema = z.object({
export type SohPrediction = BatteryPrediction & z.infer<typeof sohPredictionSchema>
type PredictionHistoryItem = {
id: number
power: number
power_status: PowerStatus
is_low_power: string
cycle: number
charge_capacity_ah: number
discharge_capacity_ah: number
timestamp: string
}
@@ -67,6 +66,9 @@ const negativeCache = new LRUCache<string, true>({
ttl: env.SOH_PREDICTION_NEGATIVE_CACHE_TTL_SECONDS * 1000,
})
const inFlightRequests = new Map<string, Promise<SohPrediction | null>>()
const nominalCapacityAh = 3.2
const round2 = (value: number) => Math.round(value * 100) / 100
function normalizeMysqlDateTime(value: string) {
if (!value.includes('T')) return value
@@ -82,12 +84,14 @@ function createCacheKey(battery: BatteryInfo, history: BatteryInfo[]) {
return `${battery.mac}:${latestId}:${latestTime}`
}
function createHistoryItem(item: BatteryInfo): PredictionHistoryItem {
function createHistoryItem(item: BatteryInfo, index: number): PredictionHistoryItem {
const observedCapacityRatio = Math.max(0.5, Math.min(1, item.power / 100))
const chargeCapacityAh = round2(nominalCapacityAh * observedCapacityRatio)
return {
id: item.id,
power: item.power,
power_status: item.powerStatus,
is_low_power: toMysqlBoolean(item.isLowPower),
cycle: index + 1,
charge_capacity_ah: chargeCapacityAh,
discharge_capacity_ah: round2(chargeCapacityAh * 0.96),
timestamp: normalizeMysqlDateTime(item.createTime),
}
}