fix(prediction): 使用真实历史字段请求预测
This commit is contained in:
@@ -74,4 +74,19 @@ describe('prediction client helpers', () => {
|
|||||||
test('returns null for history-insufficient prediction requests', () => {
|
test('returns null for history-insufficient prediction requests', () => {
|
||||||
expect(createPredictionRequest(battery, [battery, battery, battery, battery])).toBeNull()
|
expect(createPredictionRequest(battery, [battery, battery, battery, battery])).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('uses only real battery history fields in prediction requests', () => {
|
||||||
|
const request = createPredictionRequest(battery, [battery, battery, battery, battery, battery])
|
||||||
|
const firstHistory = request?.history[0]
|
||||||
|
|
||||||
|
expect(firstHistory).toEqual({
|
||||||
|
id: battery.id,
|
||||||
|
power: battery.power,
|
||||||
|
power_status: battery.powerStatus,
|
||||||
|
is_low_power: MYSQL_BOOLEAN.FALSE,
|
||||||
|
timestamp: battery.createTime,
|
||||||
|
})
|
||||||
|
expect(Object.hasOwn(firstHistory ?? {}, 'charge_capacity_ah')).toBe(false)
|
||||||
|
expect(Object.hasOwn(firstHistory ?? {}, 'coulombic_efficiency_pct')).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
import { LRUCache } from 'lru-cache'
|
import { LRUCache } from 'lru-cache'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import {
|
import { type BatteryInfo, type BatteryPrediction, type PowerStatus, toMysqlBoolean } from '@/domain/battery'
|
||||||
type BatteryInfo,
|
|
||||||
type BatteryPrediction,
|
|
||||||
POWER_STATUS,
|
|
||||||
type PowerStatus,
|
|
||||||
toMysqlBoolean,
|
|
||||||
} from '@/domain/battery'
|
|
||||||
import { env } from '@/env'
|
import { env } from '@/env'
|
||||||
import { getLogger } from '@/server/logger'
|
import { getLogger } from '@/server/logger'
|
||||||
|
|
||||||
@@ -26,14 +20,10 @@ export const sohPredictionSchema = z.object({
|
|||||||
export type SohPrediction = BatteryPrediction & z.infer<typeof sohPredictionSchema>
|
export type SohPrediction = BatteryPrediction & z.infer<typeof sohPredictionSchema>
|
||||||
|
|
||||||
type PredictionHistoryItem = {
|
type PredictionHistoryItem = {
|
||||||
cycle: number
|
id: number
|
||||||
charge_capacity_ah: number
|
power: number
|
||||||
discharge_capacity_ah: number
|
power_status: PowerStatus
|
||||||
charge_energy_wh: number
|
is_low_power: string
|
||||||
discharge_energy_wh: number
|
|
||||||
charge_time: string
|
|
||||||
discharge_time: string
|
|
||||||
coulombic_efficiency_pct: number
|
|
||||||
timestamp: string
|
timestamp: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,8 +68,6 @@ const negativeCache = new LRUCache<string, true>({
|
|||||||
})
|
})
|
||||||
const inFlightRequests = new Map<string, Promise<SohPrediction | null>>()
|
const inFlightRequests = new Map<string, Promise<SohPrediction | null>>()
|
||||||
|
|
||||||
const round2 = (value: number) => Math.round(value * 100) / 100
|
|
||||||
|
|
||||||
function normalizeMysqlDateTime(value: string) {
|
function normalizeMysqlDateTime(value: string) {
|
||||||
if (!value.includes('T')) return value
|
if (!value.includes('T')) return value
|
||||||
|
|
||||||
@@ -94,23 +82,12 @@ function createCacheKey(battery: BatteryInfo, history: BatteryInfo[]) {
|
|||||||
return `${battery.mac}:${latestId}:${latestTime}`
|
return `${battery.mac}:${latestId}:${latestTime}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function createHistoryItem(item: BatteryInfo, index: number): PredictionHistoryItem {
|
function createHistoryItem(item: BatteryInfo): PredictionHistoryItem {
|
||||||
const sohRatio = Math.max(0.5, Math.min(1, item.power / 100))
|
|
||||||
const chargeCapacity = round2(3.2 * sohRatio)
|
|
||||||
const efficiency = round2(Math.max(80, Math.min(99, 92 + item.power / 12 - (item.isLowPower ? 4 : 0))))
|
|
||||||
const dischargeCapacity = round2(chargeCapacity * (efficiency / 100))
|
|
||||||
const chargeEnergy = round2(chargeCapacity * 3.75)
|
|
||||||
const dischargeEnergy = round2(dischargeCapacity * 3.7)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cycle: index + 1,
|
id: item.id,
|
||||||
charge_capacity_ah: chargeCapacity,
|
power: item.power,
|
||||||
discharge_capacity_ah: dischargeCapacity,
|
power_status: item.powerStatus,
|
||||||
charge_energy_wh: chargeEnergy,
|
is_low_power: toMysqlBoolean(item.isLowPower),
|
||||||
discharge_energy_wh: dischargeEnergy,
|
|
||||||
charge_time: item.powerStatus === POWER_STATUS.CHARGING ? '01:20:00' : '01:18:00',
|
|
||||||
discharge_time: item.isLowPower ? '00:58:00' : '01:10:00',
|
|
||||||
coulombic_efficiency_pct: efficiency,
|
|
||||||
timestamp: normalizeMysqlDateTime(item.createTime),
|
timestamp: normalizeMysqlDateTime(item.createTime),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user