Add protection domain foundation
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import type {
|
||||
BreakerTripCharacteristic,
|
||||
FuseProtectionDeviceType,
|
||||
FuseUtilizationCategory,
|
||||
RatedResidualCurrentMa,
|
||||
RcdType,
|
||||
} from "../../shared/constants/protection-device.js";
|
||||
|
||||
export interface FuseProtectionDeviceConfiguration {
|
||||
type: FuseProtectionDeviceType;
|
||||
ratedCurrentA: number;
|
||||
fuseUtilizationCategory: FuseUtilizationCategory;
|
||||
}
|
||||
|
||||
export interface BreakerProtectionDeviceConfiguration {
|
||||
type: "LS" | "AFDD";
|
||||
ratedCurrentA: number;
|
||||
tripCharacteristic: BreakerTripCharacteristic;
|
||||
}
|
||||
|
||||
export interface ResidualCurrentDeviceConfiguration {
|
||||
type: "FI";
|
||||
ratedCurrentA: number;
|
||||
rcdType: RcdType;
|
||||
ratedResidualCurrentMa: RatedResidualCurrentMa;
|
||||
}
|
||||
|
||||
export interface CombinedResidualCurrentBreakerConfiguration {
|
||||
type: "FI_LS";
|
||||
ratedCurrentA: number;
|
||||
tripCharacteristic: BreakerTripCharacteristic;
|
||||
rcdType: RcdType;
|
||||
ratedResidualCurrentMa: RatedResidualCurrentMa;
|
||||
}
|
||||
|
||||
export type ProtectionDeviceConfiguration =
|
||||
| FuseProtectionDeviceConfiguration
|
||||
| BreakerProtectionDeviceConfiguration
|
||||
| ResidualCurrentDeviceConfiguration
|
||||
| CombinedResidualCurrentBreakerConfiguration;
|
||||
@@ -0,0 +1,165 @@
|
||||
import {
|
||||
circuitGroupCategoryNumbers,
|
||||
type CircuitGroupCategory,
|
||||
} from "../../shared/constants/circuit-group.js";
|
||||
|
||||
export type GroupedEquipmentIdentifierKind =
|
||||
| "group_upstream_protection"
|
||||
| "group_rcd"
|
||||
| "circuit";
|
||||
|
||||
export interface ParsedGroupedEquipmentIdentifier {
|
||||
category: CircuitGroupCategory;
|
||||
groupNumber: number;
|
||||
kind: GroupedEquipmentIdentifierKind;
|
||||
circuitNumber: number | null;
|
||||
}
|
||||
|
||||
const categoryByNumber: Record<string, CircuitGroupCategory> = {
|
||||
"1": "lighting",
|
||||
"2": "single_phase",
|
||||
"3": "three_phase",
|
||||
};
|
||||
|
||||
function requirePositiveInteger(value: number, fieldName: string): void {
|
||||
if (!Number.isInteger(value) || value < 1) {
|
||||
throw new RangeError(`${fieldName} must be a positive integer`);
|
||||
}
|
||||
}
|
||||
|
||||
function getGroupStem(
|
||||
category: CircuitGroupCategory,
|
||||
functionLetter: "F" | "Q",
|
||||
groupNumber: number
|
||||
): string {
|
||||
requirePositiveInteger(groupNumber, "groupNumber");
|
||||
return `-${circuitGroupCategoryNumbers[category]}${functionLetter}${groupNumber}`;
|
||||
}
|
||||
|
||||
export function formatGroupUpstreamProtectionIdentifier(
|
||||
category: CircuitGroupCategory,
|
||||
groupNumber: number
|
||||
): string {
|
||||
return `${getGroupStem(category, "F", groupNumber)}.0`;
|
||||
}
|
||||
|
||||
export function formatGroupRcdIdentifier(
|
||||
category: CircuitGroupCategory,
|
||||
groupNumber: number
|
||||
): string {
|
||||
return `${getGroupStem(category, "Q", groupNumber)}.0`;
|
||||
}
|
||||
|
||||
export function formatGroupedCircuitIdentifier(
|
||||
category: CircuitGroupCategory,
|
||||
groupNumber: number,
|
||||
circuitNumber: number
|
||||
): string {
|
||||
requirePositiveInteger(circuitNumber, "circuitNumber");
|
||||
return `${getGroupStem(category, "F", groupNumber)}.${circuitNumber}`;
|
||||
}
|
||||
|
||||
export function parseGroupedEquipmentIdentifier(
|
||||
equipmentIdentifier: string
|
||||
): ParsedGroupedEquipmentIdentifier | null {
|
||||
const match = /^-(1|2|3)(F|Q)([1-9]\d*)\.(0|[1-9]\d*)$/.exec(
|
||||
equipmentIdentifier
|
||||
);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [, categoryNumber, functionLetter, groupNumberValue, suffixValue] =
|
||||
match;
|
||||
const category = categoryByNumber[categoryNumber];
|
||||
const groupNumber = Number(groupNumberValue);
|
||||
const suffix = Number(suffixValue);
|
||||
|
||||
if (functionLetter === "Q") {
|
||||
return suffix === 0
|
||||
? {
|
||||
category,
|
||||
groupNumber,
|
||||
kind: "group_rcd",
|
||||
circuitNumber: null,
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
if (suffix === 0) {
|
||||
return {
|
||||
category,
|
||||
groupNumber,
|
||||
kind: "group_upstream_protection",
|
||||
circuitNumber: null,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
category,
|
||||
groupNumber,
|
||||
kind: "circuit",
|
||||
circuitNumber: suffix,
|
||||
};
|
||||
}
|
||||
|
||||
export function getNextGroupedCircuitNumber(
|
||||
category: CircuitGroupCategory,
|
||||
groupNumber: number,
|
||||
equipmentIdentifiers: Iterable<string>
|
||||
): number {
|
||||
requirePositiveInteger(groupNumber, "groupNumber");
|
||||
let highestCircuitNumber = 0;
|
||||
|
||||
for (const equipmentIdentifier of equipmentIdentifiers) {
|
||||
const parsed = parseGroupedEquipmentIdentifier(equipmentIdentifier);
|
||||
if (
|
||||
parsed?.kind === "circuit" &&
|
||||
parsed.category === category &&
|
||||
parsed.groupNumber === groupNumber &&
|
||||
parsed.circuitNumber !== null
|
||||
) {
|
||||
highestCircuitNumber = Math.max(
|
||||
highestCircuitNumber,
|
||||
parsed.circuitNumber
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return highestCircuitNumber + 1;
|
||||
}
|
||||
|
||||
export function getNextGroupedCircuitIdentifier(
|
||||
category: CircuitGroupCategory,
|
||||
groupNumber: number,
|
||||
equipmentIdentifiers: Iterable<string>
|
||||
): string {
|
||||
return formatGroupedCircuitIdentifier(
|
||||
category,
|
||||
groupNumber,
|
||||
getNextGroupedCircuitNumber(
|
||||
category,
|
||||
groupNumber,
|
||||
equipmentIdentifiers
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function getNextCircuitGroupNumber(
|
||||
category: CircuitGroupCategory,
|
||||
groups: Iterable<{
|
||||
category: CircuitGroupCategory;
|
||||
groupNumber: number;
|
||||
}>
|
||||
): number {
|
||||
let highestGroupNumber = 0;
|
||||
|
||||
for (const group of groups) {
|
||||
if (group.category === category) {
|
||||
requirePositiveInteger(group.groupNumber, "groupNumber");
|
||||
highestGroupNumber = Math.max(highestGroupNumber, group.groupNumber);
|
||||
}
|
||||
}
|
||||
|
||||
return highestGroupNumber + 1;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { ProtectionDeviceConfiguration } from "../models/protection-device.model.js";
|
||||
import type { CircuitGroupCategory } from "../../shared/constants/circuit-group.js";
|
||||
|
||||
const defaultCircuitProtectionByCategory = {
|
||||
lighting: {
|
||||
type: "LS",
|
||||
ratedCurrentA: 10,
|
||||
tripCharacteristic: "B",
|
||||
},
|
||||
single_phase: {
|
||||
type: "FI_LS",
|
||||
ratedCurrentA: 16,
|
||||
tripCharacteristic: "B",
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
},
|
||||
three_phase: {
|
||||
type: "FI_LS",
|
||||
ratedCurrentA: 16,
|
||||
tripCharacteristic: "B",
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
},
|
||||
} as const satisfies Record<CircuitGroupCategory, ProtectionDeviceConfiguration>;
|
||||
|
||||
const defaultGroupRcd = {
|
||||
type: "FI",
|
||||
ratedCurrentA: 40,
|
||||
rcdType: "A",
|
||||
ratedResidualCurrentMa: 30,
|
||||
} as const satisfies ProtectionDeviceConfiguration;
|
||||
|
||||
export function createDefaultCircuitProtection(
|
||||
category: CircuitGroupCategory
|
||||
): ProtectionDeviceConfiguration {
|
||||
return { ...defaultCircuitProtectionByCategory[category] };
|
||||
}
|
||||
|
||||
export function createDefaultGroupRcd(): ProtectionDeviceConfiguration {
|
||||
return { ...defaultGroupRcd };
|
||||
}
|
||||
Reference in New Issue
Block a user