mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 12:18:11 +00:00
fix train builder and consolidation
This commit is contained in:
@@ -10,6 +10,7 @@ import { DataSource, EntityManager, ILike, In } from 'typeorm';
|
||||
import { Locomotive } from '../locomotives/entities/locomotive.entity';
|
||||
import { Yard } from '../rule-engine/entities/yard.entity';
|
||||
import { minLocomotiveLimits } from '../train-scheduling/train-capacity.util';
|
||||
import { WagonType } from '../wagon-types/entities/wagon-type.entity';
|
||||
import { WagonMovement } from '../wagons/entities/wagon-movement.entity';
|
||||
import { Wagon } from '../wagons/entities/wagon.entity';
|
||||
import { AssignTrainWagonsDto } from './dto/assign-train-wagons.dto';
|
||||
@@ -511,9 +512,13 @@ export class TrainBuilderService {
|
||||
startCount: number,
|
||||
): Promise<void> {
|
||||
const uniqueIds = [...new Set(wagonIds)];
|
||||
let sequence = startCount;
|
||||
const wagonRepo = manager.getRepository(Wagon);
|
||||
|
||||
// First pass: lock + validate every wagon so the length gate below sees
|
||||
// the full incoming set before any row is written.
|
||||
const toAttach: Wagon[] = [];
|
||||
for (const wagonId of uniqueIds) {
|
||||
const wagon = await manager.getRepository(Wagon).findOne({
|
||||
const wagon = await wagonRepo.findOne({
|
||||
where: { id: wagonId },
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
@@ -530,8 +535,16 @@ export class TrainBuilderService {
|
||||
`Wagon ${wagon.wagonNumber} is not in the train's yard; only wagons in the same yard can be attached`,
|
||||
);
|
||||
}
|
||||
toAttach.push(wagon);
|
||||
}
|
||||
if (!toAttach.length) return;
|
||||
|
||||
await this.assertConsistLengthWithinLimit(manager, train, toAttach);
|
||||
|
||||
let sequence = startCount;
|
||||
for (const wagon of toAttach) {
|
||||
sequence += 1;
|
||||
await manager.getRepository(Wagon).update(wagon.id, {
|
||||
await wagonRepo.update(wagon.id, {
|
||||
trainId: train.id,
|
||||
sequenceNumber: sequence,
|
||||
status: WagonStatus.Assigned,
|
||||
@@ -539,6 +552,58 @@ export class TrainBuilderService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The consist (already-attached wagons + the incoming ones) must fit the
|
||||
* train's locomotive length limit — the weakest locomotive of the set caps
|
||||
* the train, mirroring how scheduling derives capacity.
|
||||
*/
|
||||
private async assertConsistLengthWithinLimit(
|
||||
manager: EntityManager,
|
||||
train: Train,
|
||||
incoming: Wagon[],
|
||||
): Promise<void> {
|
||||
const links = await manager.getRepository(TrainLocomotive).find({
|
||||
where: { trainId: train.id },
|
||||
relations: { locomotive: true },
|
||||
});
|
||||
const limits = minLocomotiveLimits(
|
||||
links
|
||||
.map((link) => link.locomotive)
|
||||
.filter((loco): loco is Locomotive => Boolean(loco)),
|
||||
);
|
||||
const maxLengthMeters = Number(limits?.maxTrainLengthMeters ?? 0);
|
||||
if (!Number.isFinite(maxLengthMeters) || maxLengthMeters <= 0) return;
|
||||
|
||||
const existing = await manager.getRepository(Wagon).find({
|
||||
where: { trainId: train.id },
|
||||
relations: { wagonType: true },
|
||||
});
|
||||
const currentLength = existing.reduce(
|
||||
(sum, w) => sum + (Number(w.wagonType?.lengthMeters) || 0),
|
||||
0,
|
||||
);
|
||||
|
||||
const typeIds = [...new Set(incoming.map((w) => w.wagonTypeId).filter(Boolean))];
|
||||
const types = typeIds.length
|
||||
? await manager.getRepository(WagonType).find({ where: { id: In(typeIds) } })
|
||||
: [];
|
||||
const lengthByType = new Map(types.map((t) => [t.id, Number(t.lengthMeters) || 0]));
|
||||
const addedLength = incoming.reduce(
|
||||
(sum, w) => sum + (lengthByType.get(w.wagonTypeId) ?? 0),
|
||||
0,
|
||||
);
|
||||
|
||||
const totalLength = currentLength + addedLength;
|
||||
if (totalLength > maxLengthMeters) {
|
||||
throw new BadRequestException(
|
||||
`Cannot attach wagons — train length would be ${round(totalLength)} m ` +
|
||||
`(current ${round(currentLength)} m + ${round(addedLength)} m added), ` +
|
||||
`over the locomotive limit of ${round(maxLengthMeters)} m. ` +
|
||||
'Remove wagons from the consist or use locomotives with a higher length limit.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Compact wagon sequence numbers back to 1..n after a removal. */
|
||||
private async resequenceWagons(manager: EntityManager, trainId: string): Promise<void> {
|
||||
const wagons = await manager.getRepository(Wagon).find({
|
||||
|
||||
Reference in New Issue
Block a user