105 lines
2.9 KiB
TypeScript
105 lines
2.9 KiB
TypeScript
import { eq, and } from 'drizzle-orm'
|
|
import { useDrizzle } from '@/lib/db.js'
|
|
import { chargePoint, connector, connectorStatusHistory } from '@/db/schema.js'
|
|
import type {
|
|
StatusNotificationRequest,
|
|
StatusNotificationResponse,
|
|
OcppConnectionContext,
|
|
} from '../types.ts'
|
|
|
|
// Mirror the enum values defined in ocpp-schema.ts for type safety
|
|
type ConnectorStatus =
|
|
| 'Available'
|
|
| 'Preparing'
|
|
| 'Charging'
|
|
| 'SuspendedEVSE'
|
|
| 'SuspendedEV'
|
|
| 'Finishing'
|
|
| 'Reserved'
|
|
| 'Unavailable'
|
|
| 'Faulted'
|
|
|
|
type ConnectorErrorCode =
|
|
| 'NoError'
|
|
| 'ConnectorLockFailure'
|
|
| 'EVCommunicationError'
|
|
| 'GroundFailure'
|
|
| 'HighTemperature'
|
|
| 'InternalError'
|
|
| 'LocalListConflict'
|
|
| 'OtherError'
|
|
| 'OverCurrentFailure'
|
|
| 'OverVoltage'
|
|
| 'PowerMeterFailure'
|
|
| 'PowerSwitchFailure'
|
|
| 'ReaderFailure'
|
|
| 'ResetFailure'
|
|
| 'UnderVoltage'
|
|
| 'WeakSignal'
|
|
|
|
export async function handleStatusNotification(
|
|
payload: StatusNotificationRequest,
|
|
ctx: OcppConnectionContext,
|
|
): Promise<StatusNotificationResponse> {
|
|
const db = useDrizzle()
|
|
|
|
// Retrieve the internal charge point id
|
|
const [cp] = await db
|
|
.select({ id: chargePoint.id })
|
|
.from(chargePoint)
|
|
.where(eq(chargePoint.chargePointIdentifier, ctx.chargePointIdentifier))
|
|
.limit(1)
|
|
|
|
if (!cp) {
|
|
throw new Error(`ChargePoint not found: ${ctx.chargePointIdentifier}`)
|
|
}
|
|
|
|
const statusTimestamp = payload.timestamp ? new Date(payload.timestamp) : new Date()
|
|
const connStatus = payload.status as ConnectorStatus
|
|
const connErrorCode = payload.errorCode as ConnectorErrorCode
|
|
|
|
// Upsert connector and return the internal id for history insertion
|
|
const [upsertedConnector] = await db
|
|
.insert(connector)
|
|
.values({
|
|
id: crypto.randomUUID(),
|
|
chargePointId: cp.id,
|
|
connectorId: payload.connectorId,
|
|
status: connStatus,
|
|
errorCode: connErrorCode,
|
|
info: payload.info ?? null,
|
|
vendorId: payload.vendorId ?? null,
|
|
vendorErrorCode: payload.vendorErrorCode ?? null,
|
|
lastStatusAt: statusTimestamp,
|
|
})
|
|
.onConflictDoUpdate({
|
|
target: [connector.chargePointId, connector.connectorId],
|
|
set: {
|
|
status: connStatus,
|
|
errorCode: connErrorCode,
|
|
info: payload.info ?? null,
|
|
vendorId: payload.vendorId ?? null,
|
|
vendorErrorCode: payload.vendorErrorCode ?? null,
|
|
lastStatusAt: statusTimestamp,
|
|
updatedAt: new Date(),
|
|
},
|
|
})
|
|
.returning({ id: connector.id })
|
|
|
|
if (upsertedConnector) {
|
|
await db.insert(connectorStatusHistory).values({
|
|
id: crypto.randomUUID(),
|
|
connectorId: upsertedConnector.id,
|
|
connectorNumber: payload.connectorId,
|
|
status: connStatus,
|
|
errorCode: connErrorCode,
|
|
info: payload.info ?? null,
|
|
vendorId: payload.vendorId ?? null,
|
|
vendorErrorCode: payload.vendorErrorCode ?? null,
|
|
statusTimestamp,
|
|
})
|
|
}
|
|
|
|
return {}
|
|
}
|