Add Revit initial import planning
This commit is contained in:
parent
c634570018
commit
c9e5fdd876
10 changed files with 345 additions and 4 deletions
164
src/external-model/application/external-initial-import-plan.ts
Normal file
164
src/external-model/application/external-initial-import-plan.ts
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
import type { ExternalCsvConfiguration } from "../csv/external-csv-contracts.js";
|
||||
import {
|
||||
createExternalRoomKey,
|
||||
normalizeExternalRoomPart,
|
||||
projectInitialExternalObject,
|
||||
} from "../domain/external-model-matching.js";
|
||||
import { createExternalCsvPreview } from "./external-csv-preview.js";
|
||||
|
||||
interface ExistingRoom {
|
||||
id: string;
|
||||
floorId: string | null;
|
||||
roomNumber: string;
|
||||
roomName: string;
|
||||
}
|
||||
|
||||
interface ExistingFloor {
|
||||
id: string;
|
||||
name: string;
|
||||
sortOrder: number;
|
||||
}
|
||||
|
||||
interface ExistingDistributionBoard {
|
||||
id: string;
|
||||
name: string;
|
||||
floorId: string | null;
|
||||
}
|
||||
|
||||
interface ExistingProjectDevice {
|
||||
id: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
category: string | null;
|
||||
connectionKind: string | null;
|
||||
powerPerUnit: number;
|
||||
}
|
||||
|
||||
export function createExternalInitialImportPlan(input: {
|
||||
fileName: string;
|
||||
bytes: Uint8Array;
|
||||
configurationVersion: number;
|
||||
configuration: ExternalCsvConfiguration;
|
||||
rooms: ExistingRoom[];
|
||||
floors: ExistingFloor[];
|
||||
distributionBoards: ExistingDistributionBoard[];
|
||||
projectDevices: ExistingProjectDevice[];
|
||||
}) {
|
||||
const preview = createExternalCsvPreview(input);
|
||||
const roomCandidatesByNumber = new Map<string, ExistingRoom[]>();
|
||||
for (const room of input.rooms) {
|
||||
const key = normalizeExternalRoomPart(room.roomNumber);
|
||||
if (!key) continue;
|
||||
const candidates = roomCandidatesByNumber.get(key) ?? [];
|
||||
candidates.push(room);
|
||||
roomCandidatesByNumber.set(key, candidates);
|
||||
}
|
||||
|
||||
const objects = preview.objects.map((object) => {
|
||||
const projected = projectInitialExternalObject(object, input.configuration);
|
||||
return {
|
||||
ifcGuid: projected.ifcGuid,
|
||||
sourceRoomKey: createExternalRoomKey(object.roomNumber, object.roomName),
|
||||
sourceValues: projected.sourceValues,
|
||||
suggestedPlanningValues: projected.planningValues,
|
||||
issues: projected.issues,
|
||||
};
|
||||
});
|
||||
const sourceRooms = new Map<string, {
|
||||
sourceRoomKey: string;
|
||||
roomNumber: string;
|
||||
roomName: string;
|
||||
objectCount: number;
|
||||
}>();
|
||||
for (const object of objects) {
|
||||
if (object.sourceRoomKey === null) continue;
|
||||
const existing = sourceRooms.get(object.sourceRoomKey);
|
||||
if (existing) {
|
||||
existing.objectCount += 1;
|
||||
} else {
|
||||
sourceRooms.set(object.sourceRoomKey, {
|
||||
sourceRoomKey: object.sourceRoomKey,
|
||||
roomNumber: object.sourceValues.roomNumber,
|
||||
roomName: object.sourceValues.roomName,
|
||||
objectCount: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
const roomPlans = [...sourceRooms.values()]
|
||||
.map((room) => {
|
||||
const candidates = roomCandidatesByNumber.get(
|
||||
normalizeExternalRoomPart(room.roomNumber)
|
||||
) ?? [];
|
||||
return {
|
||||
...room,
|
||||
suggestedRoomId: candidates.length === 1 ? candidates[0].id : null,
|
||||
matchStatus:
|
||||
candidates.length === 1
|
||||
? "exact-number"
|
||||
: candidates.length > 1
|
||||
? "ambiguous-number"
|
||||
: "new",
|
||||
candidateRoomIds: candidates.map((candidate) => candidate.id),
|
||||
} as const;
|
||||
})
|
||||
.sort((left, right) =>
|
||||
left.roomNumber.localeCompare(right.roomNumber, "de", { numeric: true }) ||
|
||||
left.roomName.localeCompare(right.roomName, "de")
|
||||
);
|
||||
|
||||
const familyGroups = new Map<string, {
|
||||
familyAndType: string;
|
||||
objectCount: number;
|
||||
classified: boolean;
|
||||
category: string | null;
|
||||
connectionKind: string | null;
|
||||
internalDeviceType: string | null;
|
||||
}>();
|
||||
for (const object of objects) {
|
||||
const key = object.sourceValues.familyAndType;
|
||||
const group = familyGroups.get(key);
|
||||
if (group) {
|
||||
group.objectCount += 1;
|
||||
} else {
|
||||
familyGroups.set(key, {
|
||||
familyAndType: key,
|
||||
objectCount: 1,
|
||||
classified: !object.issues.includes("unknown-family-and-type"),
|
||||
category: object.suggestedPlanningValues.category,
|
||||
connectionKind: object.suggestedPlanningValues.connectionKind,
|
||||
internalDeviceType: object.suggestedPlanningValues.internalDeviceType,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
configurationVersion: input.configurationVersion,
|
||||
fileName: preview.fileName,
|
||||
sha256: preview.sha256,
|
||||
byteLength: preview.byteLength,
|
||||
rowCount: preview.rowCount,
|
||||
objectCount: preview.objectCount,
|
||||
passthroughCount: preview.passthroughCount,
|
||||
suspectObjectCount: preview.suspectObjectCount,
|
||||
suspectRowNumbers: preview.suspectRowNumbers,
|
||||
sourceRooms: roomPlans,
|
||||
familyGroups: [...familyGroups.values()].sort((left, right) =>
|
||||
left.familyAndType.localeCompare(right.familyAndType, "de")
|
||||
),
|
||||
issueCounts: {
|
||||
unknownFamilyAndType: objects.filter((object) =>
|
||||
object.issues.includes("unknown-family-and-type")
|
||||
).length,
|
||||
invalidPower: objects.filter((object) => object.issues.includes("invalid-power")).length,
|
||||
invalidQuantity: objects.filter((object) => object.issues.includes("invalid-quantity")).length,
|
||||
missingRoom: objects.filter((object) => object.issues.includes("missing-room")).length,
|
||||
},
|
||||
objects,
|
||||
existing: {
|
||||
floors: input.floors,
|
||||
rooms: input.rooms,
|
||||
distributionBoards: input.distributionBoards,
|
||||
projectDevices: input.projectDevices,
|
||||
},
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue