mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 10:52:53 +00:00
- Added TrainSetLocomotive entity to link multiple locomotives to a train set. - Updated TrainSet entity to include a OneToMany relationship with TrainSetLocomotive. - Modified the train scheduling logic to require at least two locomotives for a train set. - Enhanced the UI components to support selecting multiple locomotives. - Introduced new permissions for viewing customs clearance. - Updated migrations to create the train_set_locomotives table and backfill existing data. - Implemented utility functions for managing train numbers based on cargo type and direction. - Added tests for train number utilities to ensure correct functionality.
110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
/** Physical dimensions used when deriving how many wagons a locomotive can pull. */
|
|
export type WagonTypeDimensions = {
|
|
lengthMeters: number;
|
|
capacityTons: number;
|
|
};
|
|
|
|
export type LocomotiveLimits = {
|
|
maxPullWeightTons: number;
|
|
maxTrainLengthMeters: number;
|
|
};
|
|
|
|
export type DerivedTrainCapacity = {
|
|
maxWeightTons: number;
|
|
maxLengthMeters: number;
|
|
maxWagonSlots: number;
|
|
};
|
|
|
|
const DEFAULT_WAGON_LENGTH_M = 14;
|
|
const DEFAULT_WAGON_CAPACITY_T = 70;
|
|
|
|
/**
|
|
* Derive train capacity from locomotive pull weight and train length.
|
|
* Wagon count is NOT a fixed 53 — it is the minimum of:
|
|
* - floor(maxLength / shortest wagon type length)
|
|
* - floor(maxWeight / lightest wagon type capacity)
|
|
*/
|
|
export function deriveTrainCapacityFromLocomotive(
|
|
locomotive: LocomotiveLimits,
|
|
wagonTypes: WagonTypeDimensions[],
|
|
ruleCaps?: { maxTrainWeightTons?: number; maxTrainLengthMeters?: number },
|
|
): DerivedTrainCapacity {
|
|
const maxWeightTons = Math.min(
|
|
Number(locomotive.maxPullWeightTons) || Infinity,
|
|
ruleCaps?.maxTrainWeightTons ?? Infinity,
|
|
);
|
|
const maxLengthMeters = Math.min(
|
|
Number(locomotive.maxTrainLengthMeters) || Infinity,
|
|
ruleCaps?.maxTrainLengthMeters ?? Infinity,
|
|
);
|
|
|
|
const types =
|
|
wagonTypes.length > 0
|
|
? wagonTypes
|
|
: [{ lengthMeters: DEFAULT_WAGON_LENGTH_M, capacityTons: DEFAULT_WAGON_CAPACITY_T }];
|
|
|
|
const minLength = Math.min(...types.map((w) => Number(w.lengthMeters) || DEFAULT_WAGON_LENGTH_M));
|
|
const minCapacity = Math.min(
|
|
...types.map((w) => Number(w.capacityTons) || DEFAULT_WAGON_CAPACITY_T),
|
|
);
|
|
|
|
const byLength =
|
|
minLength > 0 && Number.isFinite(maxLengthMeters)
|
|
? Math.floor(maxLengthMeters / minLength)
|
|
: 0;
|
|
const byWeight =
|
|
minCapacity > 0 && Number.isFinite(maxWeightTons)
|
|
? Math.floor(maxWeightTons / minCapacity)
|
|
: byLength;
|
|
|
|
const maxWagonSlots = Math.max(0, Math.min(byLength, byWeight));
|
|
|
|
return {
|
|
maxWeightTons: Number.isFinite(maxWeightTons) ? maxWeightTons : MAX_FALLBACK_WEIGHT,
|
|
maxLengthMeters: Number.isFinite(maxLengthMeters) ? maxLengthMeters : MAX_FALLBACK_LENGTH,
|
|
maxWagonSlots,
|
|
};
|
|
}
|
|
|
|
export const MAX_FALLBACK_WEIGHT = 3500;
|
|
export const MAX_FALLBACK_LENGTH = 760;
|
|
|
|
/**
|
|
* Effective pull limits for a train set with multiple locomotives: the weakest
|
|
* locomotive caps the train, so take the minimum pull weight and minimum length
|
|
* across all assigned locomotives. Returns null when no locomotives are given.
|
|
*/
|
|
export function minLocomotiveLimits(
|
|
locomotives: Array<Pick<LocomotiveLimits, 'maxPullWeightTons' | 'maxTrainLengthMeters'>>,
|
|
): LocomotiveLimits | null {
|
|
if (!locomotives.length) return null;
|
|
return {
|
|
maxPullWeightTons: Math.min(
|
|
...locomotives.map((l) => Number(l.maxPullWeightTons) || Infinity),
|
|
),
|
|
maxTrainLengthMeters: Math.min(
|
|
...locomotives.map((l) => Number(l.maxTrainLengthMeters) || Infinity),
|
|
),
|
|
};
|
|
}
|
|
|
|
/** Per-booking train length from wagon count and freight-specific wagon type length. */
|
|
export function bookingTrainLengthMeters(
|
|
freightType: string | null | undefined,
|
|
wagonCount: number,
|
|
lengths: { container: number; bulk: number },
|
|
): number {
|
|
const perWagon = freightType === 'BULK' ? lengths.bulk : lengths.container;
|
|
return wagonCount * perWagon;
|
|
}
|
|
|
|
export function wagonTypeDimensionsFromEntity(wt: {
|
|
lengthMeters?: number | string | null;
|
|
capacityTons?: number | string | null;
|
|
}): WagonTypeDimensions {
|
|
return {
|
|
lengthMeters: Number(wt.lengthMeters) || DEFAULT_WAGON_LENGTH_M,
|
|
capacityTons: Number(wt.capacityTons) || DEFAULT_WAGON_CAPACITY_T,
|
|
};
|
|
}
|