Plan circuit group renumbering
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import type { CircuitGroupCategory } from "../../shared/constants/circuit-group.js";
|
||||
import {
|
||||
formatCircuitGroupPrefix,
|
||||
formatGroupRcdIdentifier,
|
||||
formatGroupedCircuitIdentifier,
|
||||
formatGroupUpstreamProtectionIdentifier,
|
||||
parseGroupedEquipmentIdentifier,
|
||||
} from "./circuit-group-numbering.js";
|
||||
|
||||
export interface RenumberableCircuitGroup {
|
||||
id: string;
|
||||
category: CircuitGroupCategory;
|
||||
groupNumber: number;
|
||||
prefix: string;
|
||||
circuits: Array<{
|
||||
id: string;
|
||||
equipmentIdentifier: string;
|
||||
}>;
|
||||
components: Array<{
|
||||
id: string;
|
||||
role:
|
||||
| "group_upstream_protection"
|
||||
| "group_residual_current_protection";
|
||||
equipmentIdentifier: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface CircuitGroupRenumberRequest {
|
||||
groupId: string;
|
||||
targetGroupNumber: number;
|
||||
}
|
||||
|
||||
export interface CircuitGroupRenumberPlan {
|
||||
groups: Array<{
|
||||
groupId: string;
|
||||
category: CircuitGroupCategory;
|
||||
expectedGroupNumber: number;
|
||||
targetGroupNumber: number;
|
||||
expectedPrefix: string;
|
||||
targetPrefix: string;
|
||||
circuits: Array<{
|
||||
circuitId: string;
|
||||
expectedEquipmentIdentifier: string;
|
||||
targetEquipmentIdentifier: string;
|
||||
}>;
|
||||
components: Array<{
|
||||
componentId: string;
|
||||
expectedEquipmentIdentifier: string;
|
||||
targetEquipmentIdentifier: string;
|
||||
}>;
|
||||
}>;
|
||||
}
|
||||
|
||||
export function createCircuitGroupRenumberPlan(
|
||||
groups: RenumberableCircuitGroup[],
|
||||
requests: CircuitGroupRenumberRequest[]
|
||||
): CircuitGroupRenumberPlan {
|
||||
if (requests.length === 0) {
|
||||
throw new Error("Circuit-group renumbering requires requests.");
|
||||
}
|
||||
const groupsById = new Map(groups.map((group) => [group.id, group]));
|
||||
if (groupsById.size !== groups.length) {
|
||||
throw new Error("Circuit-group renumbering contains duplicate group ids.");
|
||||
}
|
||||
const requestGroupIds = new Set<string>();
|
||||
const targetKeys = new Set<string>();
|
||||
const selected = requests.map((request) => {
|
||||
const group = groupsById.get(request.groupId);
|
||||
if (
|
||||
!group ||
|
||||
requestGroupIds.has(request.groupId) ||
|
||||
!Number.isInteger(request.targetGroupNumber) ||
|
||||
request.targetGroupNumber < 1
|
||||
) {
|
||||
throw new Error("Circuit-group renumber request is invalid.");
|
||||
}
|
||||
const targetKey = `${group.category}:${request.targetGroupNumber}`;
|
||||
if (targetKeys.has(targetKey)) {
|
||||
throw new Error(
|
||||
"Circuit-group renumbering contains duplicate target numbers."
|
||||
);
|
||||
}
|
||||
requestGroupIds.add(request.groupId);
|
||||
targetKeys.add(targetKey);
|
||||
return { group, targetGroupNumber: request.targetGroupNumber };
|
||||
});
|
||||
if (
|
||||
selected.every(
|
||||
({ group, targetGroupNumber }) =>
|
||||
group.groupNumber === targetGroupNumber
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
"Circuit-group renumbering must change at least one number."
|
||||
);
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
if (
|
||||
!requestGroupIds.has(group.id) &&
|
||||
targetKeys.has(`${group.category}:${group.groupNumber}`)
|
||||
) {
|
||||
throw new Error(
|
||||
"Target group number belongs to an unchanged group."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
groups: selected.map(({ group, targetGroupNumber }) => {
|
||||
assertCurrentGroupIdentity(group);
|
||||
return {
|
||||
groupId: group.id,
|
||||
category: group.category,
|
||||
expectedGroupNumber: group.groupNumber,
|
||||
targetGroupNumber,
|
||||
expectedPrefix: group.prefix,
|
||||
targetPrefix: formatCircuitGroupPrefix(
|
||||
group.category,
|
||||
targetGroupNumber
|
||||
),
|
||||
circuits: group.circuits.map((circuit) => {
|
||||
const parsed = parseGroupedEquipmentIdentifier(
|
||||
circuit.equipmentIdentifier
|
||||
);
|
||||
if (
|
||||
parsed?.kind !== "circuit" ||
|
||||
parsed.category !== group.category ||
|
||||
parsed.groupNumber !== group.groupNumber ||
|
||||
parsed.circuitNumber === null
|
||||
) {
|
||||
throw new Error(
|
||||
"Circuit BMK does not match its current group."
|
||||
);
|
||||
}
|
||||
return {
|
||||
circuitId: circuit.id,
|
||||
expectedEquipmentIdentifier:
|
||||
circuit.equipmentIdentifier,
|
||||
targetEquipmentIdentifier:
|
||||
formatGroupedCircuitIdentifier(
|
||||
group.category,
|
||||
targetGroupNumber,
|
||||
parsed.circuitNumber
|
||||
),
|
||||
};
|
||||
}),
|
||||
components: group.components.map((component) => {
|
||||
const expected =
|
||||
component.role === "group_upstream_protection"
|
||||
? formatGroupUpstreamProtectionIdentifier(
|
||||
group.category,
|
||||
group.groupNumber
|
||||
)
|
||||
: formatGroupRcdIdentifier(
|
||||
group.category,
|
||||
group.groupNumber
|
||||
);
|
||||
if (component.equipmentIdentifier !== expected) {
|
||||
throw new Error(
|
||||
"Group component BMK does not match its current group."
|
||||
);
|
||||
}
|
||||
return {
|
||||
componentId: component.id,
|
||||
expectedEquipmentIdentifier: expected,
|
||||
targetEquipmentIdentifier:
|
||||
component.role === "group_upstream_protection"
|
||||
? formatGroupUpstreamProtectionIdentifier(
|
||||
group.category,
|
||||
targetGroupNumber
|
||||
)
|
||||
: formatGroupRcdIdentifier(
|
||||
group.category,
|
||||
targetGroupNumber
|
||||
),
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function assertCurrentGroupIdentity(group: RenumberableCircuitGroup) {
|
||||
if (
|
||||
!Number.isInteger(group.groupNumber) ||
|
||||
group.groupNumber < 1 ||
|
||||
group.prefix !==
|
||||
formatCircuitGroupPrefix(group.category, group.groupNumber)
|
||||
) {
|
||||
throw new Error("Circuit group has an invalid current identity.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user