Include components in project snapshots

This commit is contained in:
2026-07-30 19:24:28 +02:00
parent e7607c90c4
commit 012308984d
13 changed files with 915 additions and 46 deletions
@@ -9,6 +9,7 @@ import {
legacyProjectStateSnapshotSchemaVersion,
previousProjectStateSnapshotSchemaVersion,
projectStateSnapshotSchemaVersion,
simultaneityFactorProjectStateSnapshotSchemaVersion,
supplyTypesProjectStateSnapshotSchemaVersion,
voltageProjectStateSnapshotSchemaVersion,
} from "../../domain/models/project-state-snapshot.model.js";
@@ -152,6 +153,8 @@ export class ProjectSnapshotRepository implements ProjectSnapshotStore {
supplyTypesProjectStateSnapshotSchemaVersion &&
stored.schemaVersion !==
voltageProjectStateSnapshotSchemaVersion &&
stored.schemaVersion !==
simultaneityFactorProjectStateSnapshotSchemaVersion &&
stored.schemaVersion !== projectStateSnapshotSchemaVersion
) {
throw new Error("Project snapshot schema version is not supported.");
@@ -15,9 +15,12 @@ import type {
import type { AppDatabase } from "../database-context.js";
import { circuitDeviceRows } from "../schema/circuit-device-rows.js";
import { circuitLists } from "../schema/circuit-lists.js";
import { circuitProtectionDevices } from "../schema/circuit-protection-devices.js";
import { circuitSections } from "../schema/circuit-sections.js";
import { circuits } from "../schema/circuits.js";
import { consumers } from "../schema/consumers.js";
import { distributionBoardComponentProtectionDevices } from "../schema/distribution-board-component-protection-devices.js";
import { distributionBoardComponents } from "../schema/distribution-board-components.js";
import { distributionBoards } from "../schema/distribution-boards.js";
import { floors } from "../schema/floors.js";
import { legacyConsumerCircuitMigrations } from "../schema/legacy-consumer-circuit-migrations.js";
@@ -141,6 +144,17 @@ function canonicalProjectState(state: ProjectStateSnapshot) {
distributionBoards: byId(state.distributionBoards),
circuitLists: byId(state.circuitLists),
circuitSections: byId(state.circuitSections),
distributionBoardComponents: byId(
state.distributionBoardComponents
),
circuitProtectionDevices: byKey(
state.circuitProtectionDevices,
"circuitId"
),
distributionBoardComponentProtectionDevices: byKey(
state.distributionBoardComponentProtectionDevices,
"componentId"
),
circuits: byId(
state.circuits.map((circuit) => ({
...circuit,
@@ -159,6 +173,15 @@ function byId<T extends { id: string }>(entries: readonly T[]) {
);
}
function byKey<
TKey extends string,
TEntry extends Record<TKey, string>,
>(entries: readonly TEntry[], key: TKey) {
return [...entries].sort((left, right) =>
left[key].localeCompare(right[key])
);
}
function replaceProjectState(
database: AppDatabase,
state: ProjectStateSnapshot
@@ -214,6 +237,21 @@ function replaceProjectState(
isReserve: circuit.isReserve ? 1 : 0,
}))
);
insertMany(
database,
distributionBoardComponents,
state.distributionBoardComponents
);
insertMany(
database,
circuitProtectionDevices,
state.circuitProtectionDevices
);
insertMany(
database,
distributionBoardComponentProtectionDevices,
state.distributionBoardComponentProtectionDevices
);
insertMany(
database,
circuitDeviceRows,
@@ -9,8 +9,11 @@ import {
import type { AppDatabase } from "../database-context.js";
import { circuitDeviceRows } from "../schema/circuit-device-rows.js";
import { circuitLists } from "../schema/circuit-lists.js";
import { circuitProtectionDevices } from "../schema/circuit-protection-devices.js";
import { circuitSections } from "../schema/circuit-sections.js";
import { circuits } from "../schema/circuits.js";
import { distributionBoardComponentProtectionDevices } from "../schema/distribution-board-component-protection-devices.js";
import { distributionBoardComponents } from "../schema/distribution-board-components.js";
import { distributionBoards } from "../schema/distribution-boards.js";
import { floors } from "../schema/floors.js";
import { projectDevices } from "../schema/project-devices.js";
@@ -69,6 +72,8 @@ export function readProjectStateSnapshot(
displayName: circuitSections.displayName,
prefix: circuitSections.prefix,
sortOrder: circuitSections.sortOrder,
category: circuitSections.category,
groupNumber: circuitSections.groupNumber,
})
.from(circuitSections)
.innerJoin(
@@ -82,6 +87,56 @@ export function readProjectStateSnapshot(
asc(circuitSections.id)
)
.all();
const componentRows = database
.select({ component: distributionBoardComponents })
.from(distributionBoardComponents)
.innerJoin(
circuitLists,
eq(circuitLists.id, distributionBoardComponents.circuitListId)
)
.where(eq(circuitLists.projectId, projectId))
.orderBy(
asc(distributionBoardComponents.circuitListId),
asc(distributionBoardComponents.placement),
asc(distributionBoardComponents.sortOrder),
asc(distributionBoardComponents.id)
)
.all()
.map((row) => row.component);
const circuitProtectionRows = database
.select({ protection: circuitProtectionDevices })
.from(circuitProtectionDevices)
.innerJoin(
circuits,
eq(circuits.id, circuitProtectionDevices.circuitId)
)
.innerJoin(circuitLists, eq(circuitLists.id, circuits.circuitListId))
.where(eq(circuitLists.projectId, projectId))
.orderBy(asc(circuitProtectionDevices.circuitId))
.all()
.map((row) => row.protection);
const componentProtectionRows = database
.select({
protection: distributionBoardComponentProtectionDevices,
})
.from(distributionBoardComponentProtectionDevices)
.innerJoin(
distributionBoardComponents,
eq(
distributionBoardComponents.id,
distributionBoardComponentProtectionDevices.componentId
)
)
.innerJoin(
circuitLists,
eq(circuitLists.id, distributionBoardComponents.circuitListId)
)
.where(eq(circuitLists.projectId, projectId))
.orderBy(
asc(distributionBoardComponentProtectionDevices.componentId)
)
.all()
.map((row) => row.protection);
const circuitRows = database
.select({
id: circuits.id,
@@ -190,6 +245,10 @@ export function readProjectStateSnapshot(
distributionBoards: boardRows,
circuitLists: listRows,
circuitSections: sectionRows,
distributionBoardComponents: componentRows,
circuitProtectionDevices: circuitProtectionRows,
distributionBoardComponentProtectionDevices:
componentProtectionRows,
circuits: circuitRows.map((circuit) => ({
...circuit,
isReserve: Boolean(circuit.isReserve),
@@ -10,8 +10,11 @@ import type { ProjectTransferStore } from "../../domain/ports/project-transfer.s
import type { AppDatabase } from "../database-context.js";
import { circuitDeviceRows } from "../schema/circuit-device-rows.js";
import { circuitLists } from "../schema/circuit-lists.js";
import { circuitProtectionDevices } from "../schema/circuit-protection-devices.js";
import { circuitSections } from "../schema/circuit-sections.js";
import { circuits } from "../schema/circuits.js";
import { distributionBoardComponentProtectionDevices } from "../schema/distribution-board-component-protection-devices.js";
import { distributionBoardComponents } from "../schema/distribution-board-components.js";
import { distributionBoards } from "../schema/distribution-boards.js";
import { floors } from "../schema/floors.js";
import { projectDevices } from "../schema/project-devices.js";
@@ -127,6 +130,21 @@ function insertProjectState(
isReserve: circuit.isReserve ? 1 : 0,
}))
);
insertMany(
database,
distributionBoardComponents,
state.distributionBoardComponents
);
insertMany(
database,
circuitProtectionDevices,
state.circuitProtectionDevices
);
insertMany(
database,
distributionBoardComponentProtectionDevices,
state.distributionBoardComponentProtectionDevices
);
insertMany(
database,
circuitDeviceRows,
+383 -34
View File
@@ -3,6 +3,21 @@ import {
defaultDistributionBoardSupplyTypes,
distributionBoardSupplyTypes,
} from "../../shared/constants/distribution-board.js";
import {
circuitGroupCategories,
type CircuitGroupCategory,
} from "../../shared/constants/circuit-group.js";
import {
distributionBoardComponentPlacements,
distributionBoardComponentRoles,
} from "../../shared/constants/distribution-board-component.js";
import { protectionDeviceConfigurationSchema } from "../../shared/validation/protection-device.schemas.js";
import {
breakerTripCharacteristics,
fuseUtilizationCategories,
protectionDeviceTypes,
rcdTypes,
} from "../../shared/constants/protection-device.js";
import {
resolveCircuitPhaseType,
resolveProjectVoltage,
@@ -14,7 +29,8 @@ export const previousProjectStateSnapshotSchemaVersion = 2 as const;
export const distributionBoardProjectStateSnapshotSchemaVersion = 3 as const;
export const supplyTypesProjectStateSnapshotSchemaVersion = 4 as const;
export const voltageProjectStateSnapshotSchemaVersion = 5 as const;
export const projectStateSnapshotSchemaVersion = 6 as const;
export const simultaneityFactorProjectStateSnapshotSchemaVersion = 6 as const;
export const projectStateSnapshotSchemaVersion = 7 as const;
const idSchema = z.string().trim().min(1);
const nullableStringSchema = z.string().nullable();
@@ -73,7 +89,7 @@ const circuitListSchema = z
})
.strict();
const circuitSectionSchema = z
const legacyCircuitSectionSchema = z
.object({
id: idSchema,
circuitListId: idSchema,
@@ -84,6 +100,22 @@ const circuitSectionSchema = z
})
.strict();
const circuitSectionSchema = legacyCircuitSectionSchema
.extend({
category: z.enum(circuitGroupCategories).nullable(),
groupNumber: z.number().int().positive().nullable(),
})
.strict()
.superRefine((section, context) => {
if ((section.category === null) !== (section.groupNumber === null)) {
context.addIssue({
code: "custom",
message:
"Circuit-section category and group number must both be set or both be null.",
});
}
});
const circuitDeviceRowSchema = z
.object({
id: idSchema,
@@ -173,21 +205,106 @@ const roomSchema = z
})
.strict();
const commonProjectStateSnapshotContents = {
const distributionBoardComponentSchema = z
.object({
id: idSchema,
circuitListId: idSchema,
sectionId: idSchema.nullable(),
equipmentIdentifier: z.string().trim().min(1),
name: z.string().trim().min(1),
role: z.enum(distributionBoardComponentRoles),
placement: z.enum(distributionBoardComponentPlacements),
sortOrder: finiteNumberSchema,
})
.strict();
const persistedProtectionDeviceFields = {
type: z.enum(protectionDeviceTypes),
ratedCurrentA: finiteNumberSchema.positive(),
fuseUtilizationCategory: z.enum(fuseUtilizationCategories).nullable(),
tripCharacteristic: z.enum(breakerTripCharacteristics).nullable(),
rcdType: z.enum(rcdTypes).nullable(),
ratedResidualCurrentMa: finiteNumberSchema.positive().nullable(),
};
function validatePersistedProtectionDevice(
value: {
type: (typeof protectionDeviceTypes)[number];
ratedCurrentA: number;
fuseUtilizationCategory:
| (typeof fuseUtilizationCategories)[number]
| null;
tripCharacteristic:
| (typeof breakerTripCharacteristics)[number]
| null;
rcdType: (typeof rcdTypes)[number] | null;
ratedResidualCurrentMa: number | null;
},
context: z.RefinementCtx
) {
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) {
context.addIssue({
code: "custom",
message: "Snapshot protection-device configuration is invalid.",
});
}
}
const circuitProtectionDeviceSchema = z
.object({
circuitId: idSchema,
...persistedProtectionDeviceFields,
})
.strict()
.superRefine(validatePersistedProtectionDevice);
const componentProtectionDeviceSchema = z
.object({
componentId: idSchema,
...persistedProtectionDeviceFields,
})
.strict()
.superRefine(validatePersistedProtectionDevice);
const legacyProjectStateSnapshotContents = {
circuitLists: z.array(circuitListSchema),
circuitSections: z.array(circuitSectionSchema),
circuitSections: z.array(legacyCircuitSectionSchema),
circuits: z.array(circuitSchema),
projectDevices: z.array(projectDeviceSchema),
floors: z.array(floorSchema),
rooms: z.array(roomSchema),
};
const currentProjectStateSnapshotContents = {
...legacyProjectStateSnapshotContents,
circuitSections: z.array(circuitSectionSchema),
distributionBoardComponents: z.array(distributionBoardComponentSchema),
circuitProtectionDevices: z.array(circuitProtectionDeviceSchema),
distributionBoardComponentProtectionDevices: z.array(
componentProtectionDeviceSchema
),
};
const legacyProjectStateSnapshotSchema = z
.object({
schemaVersion: z.literal(legacyProjectStateSnapshotSchemaVersion),
project: legacyProjectSchema,
distributionBoards: z.array(legacyDistributionBoardSchema),
...commonProjectStateSnapshotContents,
...legacyProjectStateSnapshotContents,
})
.strict();
@@ -196,7 +313,7 @@ const previousProjectStateSnapshotSchema = z
schemaVersion: z.literal(previousProjectStateSnapshotSchemaVersion),
project: projectSchema,
distributionBoards: z.array(legacyDistributionBoardSchema),
...commonProjectStateSnapshotContents,
...legacyProjectStateSnapshotContents,
})
.strict();
@@ -207,7 +324,7 @@ const distributionBoardProjectStateSnapshotSchema = z
),
project: projectSchema,
distributionBoards: z.array(distributionBoardSchema),
...commonProjectStateSnapshotContents,
...legacyProjectStateSnapshotContents,
})
.strict();
@@ -216,7 +333,7 @@ const supplyTypesProjectStateSnapshotSchema = z
schemaVersion: z.literal(supplyTypesProjectStateSnapshotSchemaVersion),
project: currentProjectSchema,
distributionBoards: z.array(distributionBoardSchema),
...commonProjectStateSnapshotContents,
...legacyProjectStateSnapshotContents,
})
.strict();
@@ -227,12 +344,20 @@ const voltageProjectStateSnapshotSchema =
),
});
export const projectStateSnapshotSchema =
const simultaneityFactorProjectStateSnapshotSchema =
voltageProjectStateSnapshotSchema.extend({
schemaVersion: z.literal(projectStateSnapshotSchemaVersion),
schemaVersion: z.literal(
simultaneityFactorProjectStateSnapshotSchemaVersion
),
distributionBoards: z.array(currentDistributionBoardSchema),
});
export const projectStateSnapshotSchema =
simultaneityFactorProjectStateSnapshotSchema.extend({
schemaVersion: z.literal(projectStateSnapshotSchemaVersion),
...currentProjectStateSnapshotContents,
});
export type ProjectStateSnapshot = z.infer<
typeof projectStateSnapshotSchema
>;
@@ -243,46 +368,65 @@ export function parseProjectStateSnapshot(
const version = isPlainObject(value) ? value.schemaVersion : undefined;
const parsedSnapshot =
version === legacyProjectStateSnapshotSchemaVersion
? upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
upgradePreviousProjectStateSnapshot(
upgradeLegacyProjectStateSnapshot(
legacyProjectStateSnapshotSchema.parse(value)
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
upgradePreviousProjectStateSnapshot(
upgradeLegacyProjectStateSnapshot(
legacyProjectStateSnapshotSchema.parse(value)
)
)
)
)
)
)
: version === previousProjectStateSnapshotSchemaVersion
? upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
upgradePreviousProjectStateSnapshot(
previousProjectStateSnapshotSchema.parse(value)
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
upgradePreviousProjectStateSnapshot(
previousProjectStateSnapshotSchema.parse(value)
)
)
)
)
)
: version === distributionBoardProjectStateSnapshotSchemaVersion
? upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
distributionBoardProjectStateSnapshotSchema.parse(value)
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
distributionBoardProjectStateSnapshotSchema.parse(
value
)
)
)
)
)
: version === supplyTypesProjectStateSnapshotSchemaVersion
? upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
supplyTypesProjectStateSnapshotSchema.parse(value)
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
supplyTypesProjectStateSnapshotSchema.parse(value)
)
)
)
: version === voltageProjectStateSnapshotSchemaVersion
? upgradeVoltageProjectStateSnapshot(
voltageProjectStateSnapshotSchema.parse(value)
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
voltageProjectStateSnapshotSchema.parse(value)
)
)
: projectStateSnapshotSchema.parse(value);
: version ===
simultaneityFactorProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
simultaneityFactorProjectStateSnapshotSchema.parse(
value
)
)
: projectStateSnapshotSchema.parse(value);
const snapshot = normalizeSnapshotPhaseTypes(parsedSnapshot);
assertProjectStateSnapshotRelations(snapshot);
return snapshot;
@@ -412,10 +556,10 @@ function upgradeSupplyTypesProjectStateSnapshot(
function upgradeVoltageProjectStateSnapshot(
snapshot: z.infer<typeof voltageProjectStateSnapshotSchema>
): ProjectStateSnapshot {
): z.infer<typeof simultaneityFactorProjectStateSnapshotSchema> {
return {
...snapshot,
schemaVersion: projectStateSnapshotSchemaVersion,
schemaVersion: simultaneityFactorProjectStateSnapshotSchemaVersion,
distributionBoards: snapshot.distributionBoards.map((board) => ({
...board,
simultaneityFactor: 1,
@@ -423,6 +567,39 @@ function upgradeVoltageProjectStateSnapshot(
};
}
function upgradeSimultaneityFactorProjectStateSnapshot(
snapshot: z.infer<typeof simultaneityFactorProjectStateSnapshotSchema>
): ProjectStateSnapshot {
return {
...snapshot,
schemaVersion: projectStateSnapshotSchemaVersion,
circuitSections: snapshot.circuitSections.map((section) => {
const category = initialCategoryBySectionKey(section.key);
return {
...section,
category,
groupNumber: category === null ? null : 1,
};
}),
distributionBoardComponents: [],
circuitProtectionDevices: [],
distributionBoardComponentProtectionDevices: [],
};
}
function initialCategoryBySectionKey(
key: string
): CircuitGroupCategory | null {
if (
key === "lighting" ||
key === "single_phase" ||
key === "three_phase"
) {
return key;
}
return null;
}
function isPlainObject(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
@@ -461,10 +638,22 @@ function assertProjectStateSnapshotRelations(
);
const floorIds = uniqueIds(snapshot.floors, "floor");
const roomIds = uniqueIds(snapshot.rooms, "room");
uniqueIds(snapshot.circuits, "circuit");
const circuitIds = uniqueIds(snapshot.circuits, "circuit");
const componentIds = uniqueIds(
snapshot.distributionBoardComponents,
"distribution board component"
);
const sectionById = new Map(
snapshot.circuitSections.map((entry) => [entry.id, entry])
);
const componentById = new Map(
snapshot.distributionBoardComponents.map((entry) => [
entry.id,
entry,
])
);
const groupKeys = new Set<string>();
const equipmentIdentifiersByList = new Map<string, Set<string>>();
for (const board of snapshot.distributionBoards) {
assertProjectOwnership(board.projectId, projectId, "distribution board");
@@ -500,6 +689,19 @@ function assertProjectStateSnapshotRelations(
section.circuitListId,
"circuit section list"
);
if (section.category !== null && section.groupNumber !== null) {
const groupKey = [
section.circuitListId,
section.category,
section.groupNumber,
].join(":");
if (groupKeys.has(groupKey)) {
throw new Error(
"Snapshot contains a duplicate circuit group number."
);
}
groupKeys.add(groupKey);
}
}
for (const device of snapshot.projectDevices) {
assertProjectOwnership(device.projectId, projectId, "project device");
@@ -553,6 +755,11 @@ function assertProjectStateSnapshotRelations(
"Snapshot circuit voltage must match project settings."
);
}
registerEquipmentIdentifier(
equipmentIdentifiersByList,
circuit.circuitListId,
circuit.equipmentIdentifier
);
for (const row of circuit.deviceRows) {
if (row.circuitId !== circuit.id) {
throw new Error(
@@ -575,6 +782,148 @@ function assertProjectStateSnapshotRelations(
}
}
}
const fixedComponentRolesByList = new Set<string>();
const groupComponentRolesBySection = new Set<string>();
for (const component of snapshot.distributionBoardComponents) {
assertReference(
circuitListIds,
component.circuitListId,
"distribution board component circuit list"
);
if (component.sectionId !== null) {
assertReference(
sectionIds,
component.sectionId,
"distribution board component section"
);
if (
sectionById.get(component.sectionId)?.circuitListId !==
component.circuitListId
) {
throw new Error(
"Snapshot distribution board component section belongs to a different circuit list."
);
}
}
assertComponentPlacement(component);
registerEquipmentIdentifier(
equipmentIdentifiersByList,
component.circuitListId,
component.equipmentIdentifier
);
if (
component.role === "main_switch" ||
component.role === "surge_protective_device"
) {
assertUniqueKey(
fixedComponentRolesByList,
`${component.circuitListId}:${component.role}`,
"Snapshot contains duplicate fixed distribution board components."
);
}
if (
component.role === "group_upstream_protection" ||
component.role === "group_residual_current_protection"
) {
assertUniqueKey(
groupComponentRolesBySection,
`${component.sectionId}:${component.role}`,
"Snapshot contains duplicate group protection components."
);
}
}
const protectedCircuitIds = new Set<string>();
for (const protection of snapshot.circuitProtectionDevices) {
assertReference(
circuitIds,
protection.circuitId,
"circuit protection device circuit"
);
assertUniqueKey(
protectedCircuitIds,
protection.circuitId,
"Snapshot contains duplicate circuit protection devices."
);
}
const protectedComponentIds = new Set<string>();
for (const protection of snapshot.distributionBoardComponentProtectionDevices) {
assertReference(
componentIds,
protection.componentId,
"component protection device component"
);
assertUniqueKey(
protectedComponentIds,
protection.componentId,
"Snapshot contains duplicate component protection devices."
);
const component = componentById.get(protection.componentId)!;
if (
component.role !== "group_upstream_protection" &&
component.role !== "group_residual_current_protection"
) {
throw new Error(
"Snapshot protection data belongs to a non-protection component."
);
}
}
}
function assertComponentPlacement(
component: ProjectStateSnapshot["distributionBoardComponents"][number]
) {
const isFixedHeader =
component.role === "main_switch" ||
component.role === "surge_protective_device";
const isGroupProtection =
component.role === "group_upstream_protection" ||
component.role === "group_residual_current_protection";
if (
(isFixedHeader &&
(component.placement !== "header" ||
component.sectionId !== null)) ||
(isGroupProtection &&
(component.placement !== "group" ||
component.sectionId === null)) ||
(component.role === "auxiliary" &&
(component.placement !== "footer" ||
component.sectionId !== null))
) {
throw new Error(
"Snapshot distribution board component placement is invalid."
);
}
}
function registerEquipmentIdentifier(
identifiersByList: Map<string, Set<string>>,
circuitListId: string,
equipmentIdentifier: string
) {
const identifiers =
identifiersByList.get(circuitListId) ?? new Set<string>();
const normalized = equipmentIdentifier.trim().toLocaleLowerCase("de");
if (identifiers.has(normalized)) {
throw new Error(
"Snapshot contains duplicate equipment identifiers in one circuit list."
);
}
identifiers.add(normalized);
identifiersByList.set(circuitListId, identifiers);
}
function assertUniqueKey(
keys: Set<string>,
key: string,
message: string
) {
if (keys.has(key)) {
throw new Error(message);
}
keys.add(key);
}
function uniqueIds(
@@ -79,6 +79,10 @@ export function remapProjectState(
const listIds = idMap(source.circuitLists, createId);
const sectionIds = idMap(source.circuitSections, createId);
const circuitIds = idMap(source.circuits, createId);
const componentIds = idMap(
source.distributionBoardComponents,
createId
);
const deviceIds = idMap(source.projectDevices, createId);
const floorIds = idMap(source.floors, createId);
const roomIds = idMap(source.rooms, createId);
@@ -110,6 +114,35 @@ export function remapProjectState(
id: requiredId(sectionIds, section.id),
circuitListId: requiredId(listIds, section.circuitListId),
})),
distributionBoardComponents:
source.distributionBoardComponents.map((component) => ({
...component,
id: requiredId(componentIds, component.id),
circuitListId: requiredId(
listIds,
component.circuitListId
),
sectionId:
component.sectionId === null
? null
: requiredId(sectionIds, component.sectionId),
})),
circuitProtectionDevices: source.circuitProtectionDevices.map(
(protection) => ({
...protection,
circuitId: requiredId(circuitIds, protection.circuitId),
})
),
distributionBoardComponentProtectionDevices:
source.distributionBoardComponentProtectionDevices.map(
(protection) => ({
...protection,
componentId: requiredId(
componentIds,
protection.componentId
),
})
),
circuits: source.circuits.map((circuit) => ({
...circuit,
id: requiredId(circuitIds, circuit.id),