Derive device voltages from project settings
This commit is contained in:
@@ -4,6 +4,10 @@ import {
|
||||
calculateCircuitTotalPower,
|
||||
calculateRowTotalPower,
|
||||
} from "../src/domain/calculations/circuit-power-calculation.js";
|
||||
import {
|
||||
resolveCircuitPhaseType,
|
||||
resolveProjectVoltage,
|
||||
} from "../src/domain/services/project-voltage.service.js";
|
||||
|
||||
describe("circuit power calculation", () => {
|
||||
it("calculates row and circuit totals from device rows", () => {
|
||||
@@ -16,3 +20,28 @@ describe("circuit power calculation", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("project voltage derivation", () => {
|
||||
const settings = {
|
||||
singlePhaseVoltageV: 240,
|
||||
threePhaseVoltageV: 415,
|
||||
};
|
||||
|
||||
it("derives device voltage exclusively from phase and project settings", () => {
|
||||
assert.equal(resolveProjectVoltage("single_phase", settings), 240);
|
||||
assert.equal(resolveProjectVoltage("three_phase", settings), 415);
|
||||
});
|
||||
|
||||
it("uses section phase and handles unassigned circuits conservatively", () => {
|
||||
assert.equal(resolveCircuitPhaseType("lighting", ["three_phase"]), "single_phase");
|
||||
assert.equal(resolveCircuitPhaseType("single_phase", ["three_phase"]), "single_phase");
|
||||
assert.equal(resolveCircuitPhaseType("three_phase", ["single_phase"]), "three_phase");
|
||||
assert.equal(resolveCircuitPhaseType("unassigned", ["three_phase"]), "three_phase");
|
||||
assert.equal(
|
||||
resolveCircuitPhaseType("unassigned", ["three_phase", "single_phase"]),
|
||||
"single_phase"
|
||||
);
|
||||
assert.equal(resolveCircuitPhaseType("unassigned", []), "single_phase");
|
||||
assert.equal(resolveCircuitPhaseType("unassigned", [null]), "single_phase");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ describe("circuit project-command repository", () => {
|
||||
const circuit = getCircuit(fixture.context);
|
||||
assert.equal(circuit.displayName, null);
|
||||
assert.equal(circuit.protectionRatedCurrent, 16);
|
||||
assert.equal(circuit.voltage, 400);
|
||||
assert.equal(circuit.voltage, 230);
|
||||
assert.equal(circuit.controlRequirement, "DALI");
|
||||
assert.equal(circuit.isReserve, 1);
|
||||
assert.equal(executed.revision.revisionNumber, 1);
|
||||
@@ -138,7 +138,6 @@ describe("circuit project-command repository", () => {
|
||||
changes: [
|
||||
{ field: "displayName", value: "Licht Bestand" },
|
||||
{ field: "protectionRatedCurrent", value: 10 },
|
||||
{ field: "voltage", value: 230 },
|
||||
{ field: "controlRequirement", value: "none" },
|
||||
{ field: "isReserve", value: false },
|
||||
],
|
||||
@@ -149,7 +148,15 @@ describe("circuit project-command repository", () => {
|
||||
.from(projectChangeSets)
|
||||
.get();
|
||||
assert.ok(changeSet);
|
||||
assert.deepEqual(deserializeProjectCommand(changeSet.forwardPayloadJson), command);
|
||||
assert.deepEqual(
|
||||
deserializeProjectCommand(changeSet.forwardPayloadJson),
|
||||
createCircuitUpdateProjectCommand("circuit-1", {
|
||||
displayName: null,
|
||||
protectionRatedCurrent: 16,
|
||||
controlRequirement: "DALI",
|
||||
isReserve: true,
|
||||
})
|
||||
);
|
||||
assert.deepEqual(
|
||||
deserializeProjectCommand(changeSet.inversePayloadJson),
|
||||
executed.inverse
|
||||
|
||||
@@ -41,6 +41,20 @@ describe("project device circuit-first schema", () => {
|
||||
assert.equal(result.success, false);
|
||||
});
|
||||
|
||||
it("rejects manually supplied device voltages", () => {
|
||||
const result = createProjectDeviceSchema.safeParse({
|
||||
name: "Pumpe",
|
||||
displayName: "Pumpe",
|
||||
phaseType: "three_phase",
|
||||
quantity: 1,
|
||||
powerPerUnit: 2,
|
||||
simultaneityFactor: 1,
|
||||
voltageV: 500,
|
||||
});
|
||||
|
||||
assert.equal(result.success, false);
|
||||
});
|
||||
|
||||
it("requires a project revision for create and update commands", () => {
|
||||
const device = {
|
||||
name: "E-Line Pro",
|
||||
|
||||
@@ -12,6 +12,11 @@ import { ProjectSettingsProjectCommandRepository } from "../src/db/repositories/
|
||||
import { projectRevisions } from "../src/db/schema/project-revisions.js";
|
||||
import { projects } from "../src/db/schema/projects.js";
|
||||
import { distributionBoards } from "../src/db/schema/distribution-boards.js";
|
||||
import { circuitLists } from "../src/db/schema/circuit-lists.js";
|
||||
import { circuitSections } from "../src/db/schema/circuit-sections.js";
|
||||
import { circuits } from "../src/db/schema/circuits.js";
|
||||
import { projectDevices } from "../src/db/schema/project-devices.js";
|
||||
import { DistributionBoardFixtureRepository } from "./support/distribution-board-fixture.js";
|
||||
import { ProjectRevisionConflictError } from "../src/domain/errors/project-revision-conflict.error.js";
|
||||
import { createProjectSettingsUpdateProjectCommand } from "../src/domain/models/project-settings-project-command.model.js";
|
||||
import { updateProjectSettings } from "../src/frontend/utils/api.js";
|
||||
@@ -78,6 +83,86 @@ function settings(
|
||||
};
|
||||
}
|
||||
|
||||
function seedDerivedVoltageRecords(context: DatabaseContext) {
|
||||
context.db.insert(projectDevices).values([
|
||||
{
|
||||
id: "device-single",
|
||||
projectId: "project-1",
|
||||
name: "Leuchte",
|
||||
displayName: "Leuchte",
|
||||
phaseType: "single_phase",
|
||||
quantity: 1,
|
||||
powerPerUnit: 0.1,
|
||||
simultaneityFactor: 1,
|
||||
voltageV: 230,
|
||||
},
|
||||
{
|
||||
id: "device-three",
|
||||
projectId: "project-1",
|
||||
name: "Pumpe",
|
||||
displayName: "Pumpe",
|
||||
phaseType: "three_phase",
|
||||
quantity: 1,
|
||||
powerPerUnit: 2,
|
||||
simultaneityFactor: 1,
|
||||
voltageV: 400,
|
||||
},
|
||||
]).run();
|
||||
const board = new DistributionBoardFixtureRepository(
|
||||
context.db
|
||||
).createWithCircuitListAndDefaultSections("project-1", "UV-01");
|
||||
const list = context.db
|
||||
.select()
|
||||
.from(circuitLists)
|
||||
.where(eq(circuitLists.distributionBoardId, board.id))
|
||||
.get();
|
||||
assert.ok(list);
|
||||
const sections = context.db
|
||||
.select()
|
||||
.from(circuitSections)
|
||||
.where(eq(circuitSections.circuitListId, list.id))
|
||||
.all();
|
||||
const singleSection = sections.find((section) => section.key === "single_phase");
|
||||
const threeSection = sections.find((section) => section.key === "three_phase");
|
||||
assert.ok(singleSection);
|
||||
assert.ok(threeSection);
|
||||
context.db.insert(circuits).values([
|
||||
{
|
||||
id: "circuit-single",
|
||||
circuitListId: list.id,
|
||||
sectionId: singleSection.id,
|
||||
equipmentIdentifier: "-2F1",
|
||||
sortOrder: 10,
|
||||
voltage: 230,
|
||||
isReserve: 1,
|
||||
},
|
||||
{
|
||||
id: "circuit-three",
|
||||
circuitListId: list.id,
|
||||
sectionId: threeSection.id,
|
||||
equipmentIdentifier: "-3F1",
|
||||
sortOrder: 10,
|
||||
voltage: 400,
|
||||
isReserve: 1,
|
||||
},
|
||||
]).run();
|
||||
}
|
||||
|
||||
function getDerivedVoltages(context: DatabaseContext) {
|
||||
return {
|
||||
devices: context.db
|
||||
.select({ id: projectDevices.id, voltageV: projectDevices.voltageV })
|
||||
.from(projectDevices)
|
||||
.all()
|
||||
.sort((left, right) => left.id.localeCompare(right.id)),
|
||||
circuits: context.db
|
||||
.select({ id: circuits.id, voltage: circuits.voltage })
|
||||
.from(circuits)
|
||||
.all()
|
||||
.sort((left, right) => left.id.localeCompare(right.id)),
|
||||
};
|
||||
}
|
||||
|
||||
describe("project settings project-command repository", () => {
|
||||
it("validates settings commands and sends the expected revision from the frontend", async () => {
|
||||
assert.throws(
|
||||
@@ -137,6 +222,7 @@ describe("project settings project-command repository", () => {
|
||||
it("updates both voltages and supports persisted undo and redo", () => {
|
||||
const context = createTestDatabase();
|
||||
try {
|
||||
seedDerivedVoltageRecords(context);
|
||||
const repository = new ProjectSettingsProjectCommandRepository(
|
||||
context.db
|
||||
);
|
||||
@@ -171,6 +257,16 @@ describe("project settings project-command repository", () => {
|
||||
}),
|
||||
currentRevision: 1,
|
||||
});
|
||||
assert.deepEqual(getDerivedVoltages(context), {
|
||||
devices: [
|
||||
{ id: "device-single", voltageV: 240 },
|
||||
{ id: "device-three", voltageV: 415 },
|
||||
],
|
||||
circuits: [
|
||||
{ id: "circuit-single", voltage: 240 },
|
||||
{ id: "circuit-three", voltage: 415 },
|
||||
],
|
||||
});
|
||||
assert.deepEqual(forward.inverse.payload, {
|
||||
name: "Testprojekt",
|
||||
internalProjectNumber: "INT-1",
|
||||
@@ -216,6 +312,16 @@ describe("project settings project-command repository", () => {
|
||||
],
|
||||
currentRevision: 2,
|
||||
});
|
||||
assert.deepEqual(getDerivedVoltages(context), {
|
||||
devices: [
|
||||
{ id: "device-single", voltageV: 230 },
|
||||
{ id: "device-three", voltageV: 400 },
|
||||
],
|
||||
circuits: [
|
||||
{ id: "circuit-single", voltage: 230 },
|
||||
{ id: "circuit-three", voltage: 400 },
|
||||
],
|
||||
});
|
||||
|
||||
const redoStep = history.getNextCommand("project-1", "redo");
|
||||
assert.ok(redoStep);
|
||||
@@ -244,6 +350,16 @@ describe("project settings project-command repository", () => {
|
||||
],
|
||||
currentRevision: 3,
|
||||
});
|
||||
assert.deepEqual(getDerivedVoltages(context), {
|
||||
devices: [
|
||||
{ id: "device-single", voltageV: 240 },
|
||||
{ id: "device-three", voltageV: 415 },
|
||||
],
|
||||
circuits: [
|
||||
{ id: "circuit-single", voltage: 240 },
|
||||
{ id: "circuit-three", voltage: 415 },
|
||||
],
|
||||
});
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ function createTestDatabase(): DatabaseContext {
|
||||
quantity: 1,
|
||||
powerPerUnit: 2.5,
|
||||
simultaneityFactor: 0.8,
|
||||
voltageV: 400,
|
||||
})
|
||||
.run();
|
||||
context.db
|
||||
@@ -112,6 +113,7 @@ function createTestDatabase(): DatabaseContext {
|
||||
equipmentIdentifier: "-1F1",
|
||||
displayName: "Ausgang",
|
||||
sortOrder: 10,
|
||||
voltage: 230,
|
||||
isReserve: 0,
|
||||
})
|
||||
.run();
|
||||
@@ -165,7 +167,10 @@ function changeCompleteProjectState(context: DatabaseContext) {
|
||||
.run();
|
||||
context.db
|
||||
.update(circuits)
|
||||
.set({ displayName: "Geänderter Stromkreis" })
|
||||
.set({
|
||||
displayName: "Geänderter Stromkreis",
|
||||
voltage: 240,
|
||||
})
|
||||
.where(eq(circuits.id, "circuit-1"))
|
||||
.run();
|
||||
context.db
|
||||
@@ -198,8 +203,14 @@ function changeCompleteProjectState(context: DatabaseContext) {
|
||||
quantity: 2,
|
||||
powerPerUnit: 0.05,
|
||||
simultaneityFactor: 1,
|
||||
voltageV: 240,
|
||||
})
|
||||
.run();
|
||||
context.db
|
||||
.update(projectDevices)
|
||||
.set({ voltageV: 415 })
|
||||
.where(eq(projectDevices.id, "device-1"))
|
||||
.run();
|
||||
new DistributionBoardFixtureRepository(
|
||||
context.db
|
||||
).createWithCircuitListAndDefaultSections("project-1", "UV-02");
|
||||
|
||||
@@ -128,6 +128,36 @@ describe("project state snapshot model", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("normalizes version-four device voltages to project settings", () => {
|
||||
const previous = {
|
||||
...minimalSnapshot(),
|
||||
schemaVersion: 4 as const,
|
||||
projectDevices: [
|
||||
{
|
||||
id: "device-1",
|
||||
projectId: "project-1",
|
||||
name: "Pumpe",
|
||||
displayName: "Pumpe",
|
||||
phaseType: "three_phase" as const,
|
||||
connectionKind: null,
|
||||
costGroup: null,
|
||||
category: null,
|
||||
quantity: 1,
|
||||
powerPerUnit: 2,
|
||||
simultaneityFactor: 1,
|
||||
cosPhi: null,
|
||||
remark: null,
|
||||
voltageV: 500,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const snapshot = parseProjectStateSnapshot(previous);
|
||||
|
||||
assert.equal(snapshot.schemaVersion, projectStateSnapshotSchemaVersion);
|
||||
assert.equal(snapshot.projectDevices[0]?.voltageV, 400);
|
||||
});
|
||||
|
||||
it("rejects duplicate ids and cross-project ownership", () => {
|
||||
assert.throws(
|
||||
() =>
|
||||
|
||||
@@ -156,11 +156,11 @@ describe("project device modal presentation", () => {
|
||||
"Anzahl",
|
||||
"Leistung je Stück [kW]",
|
||||
"Gleichzeitigkeitsfaktor",
|
||||
"Spannung [V]",
|
||||
"Bemerkung",
|
||||
]) {
|
||||
assert.match(markup, new RegExp(label.replace("[", "\\[").replace("]", "\\]")));
|
||||
}
|
||||
assert.doesNotMatch(markup, /Spannung \[V\]/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user