import type { BreakerTripCharacteristic, FuseUtilizationCategory, ProtectionDeviceType, RcdType, } from "../../shared/constants/protection-device.js"; import { protectionDeviceConfigurationSchema } from "../../shared/validation/protection-device.schemas.js"; import { assertCircuitGroupSnapshot, type CircuitGroupSnapshot, } from "./circuit-group-structure-project-command.model.js"; import { assertCircuitInsertProjectCommand, circuitInsertCommandType, circuitStructureCommandSchemaVersion, type CircuitSnapshot, } from "./circuit-structure-project-command.model.js"; import { assertDistributionBoardComponentSnapshot, type DistributionBoardComponentSnapshot, } from "./distribution-board-component-structure-project-command.model.js"; export interface CircuitProtectionDeviceSnapshot { circuitId: string; type: ProtectionDeviceType; ratedCurrentA: number; fuseUtilizationCategory: FuseUtilizationCategory | null; tripCharacteristic: BreakerTripCharacteristic | null; rcdType: RcdType | null; ratedResidualCurrentMa: number | null; } export interface CircuitGroupSubtreeSnapshot { group: CircuitGroupSnapshot; components: DistributionBoardComponentSnapshot[]; circuits: Array<{ circuit: CircuitSnapshot; protectionDevice: CircuitProtectionDeviceSnapshot | null; }>; } export interface CircuitGroupDeletionSummary { hasUpstreamProtection: boolean; hasResidualCurrentProtection: boolean; circuitCount: number; deviceRowCount: number; } export function assertCircuitGroupSubtreeSnapshot( value: unknown ): asserts value is CircuitGroupSubtreeSnapshot { if ( !isPlainObject(value) || Object.keys(value).length !== 3 || !Array.isArray(value.components) || !Array.isArray(value.circuits) ) { throw new Error("Circuit-group subtree snapshot is invalid."); } assertCircuitGroupSnapshot(value.group); const group = value.group; const entityIds = new Set(); const equipmentIdentifiers = new Set(); for (const component of value.components) { assertDistributionBoardComponentSnapshot(component); if ( component.component.circuitListId !== group.circuitListId || component.component.sectionId !== group.id || entityIds.has(component.component.id) || equipmentIdentifiers.has(component.component.equipmentIdentifier) ) { throw new Error( "Circuit-group component snapshot has invalid ownership or duplicates." ); } entityIds.add(component.component.id); equipmentIdentifiers.add(component.component.equipmentIdentifier); } for (const entry of value.circuits) { if ( !isPlainObject(entry) || Object.keys(entry).length !== 2 || !isPlainObject(entry.circuit) ) { throw new Error("Circuit-group circuit snapshot is invalid."); } assertCircuitInsertProjectCommand({ schemaVersion: circuitStructureCommandSchemaVersion, type: circuitInsertCommandType, payload: { circuit: entry.circuit }, }); const circuit = entry.circuit as unknown as CircuitSnapshot; if ( circuit.circuitListId !== group.circuitListId || circuit.sectionId !== group.id || entityIds.has(circuit.id) || equipmentIdentifiers.has(circuit.equipmentIdentifier) ) { throw new Error( "Circuit-group circuit snapshot has invalid ownership or duplicates." ); } entityIds.add(circuit.id); equipmentIdentifiers.add(circuit.equipmentIdentifier); if (entry.protectionDevice !== null) { assertCircuitProtectionDeviceSnapshot( entry.protectionDevice, circuit.id ); } } } export function summarizeCircuitGroupDeletion( snapshot: CircuitGroupSubtreeSnapshot ): CircuitGroupDeletionSummary { assertCircuitGroupSubtreeSnapshot(snapshot); return { hasUpstreamProtection: snapshot.components.some( ({ component }) => component.role === "group_upstream_protection" ), hasResidualCurrentProtection: snapshot.components.some( ({ component }) => component.role === "group_residual_current_protection" ), circuitCount: snapshot.circuits.length, deviceRowCount: snapshot.circuits.reduce( (count, { circuit }) => count + circuit.deviceRows.length, 0 ), }; } function assertCircuitProtectionDeviceSnapshot( value: unknown, circuitId: string ): asserts value is CircuitProtectionDeviceSnapshot { if ( !isPlainObject(value) || Object.keys(value).length !== 7 || value.circuitId !== circuitId ) { throw new Error("Circuit protection-device snapshot is invalid."); } const result = protectionDeviceConfigurationSchema.safeParse({ type: value.type, ratedCurrentA: value.ratedCurrentA, ...(value.fuseUtilizationCategory === null ? {} : { fuseUtilizationCategory: value.fuseUtilizationCategory }), ...(value.tripCharacteristic === null ? {} : { tripCharacteristic: value.tripCharacteristic }), ...(value.rcdType === null ? {} : { rcdType: value.rcdType }), ...(value.ratedResidualCurrentMa === null ? {} : { ratedResidualCurrentMa: value.ratedResidualCurrentMa }), }); if (!result.success) { throw new Error( "Circuit protection-device configuration is invalid." ); } } function isPlainObject(value: unknown): value is Record { return value !== null && typeof value === "object" && !Array.isArray(value); }