Files
edr-platform/apps/edr-freight-api/src/modules/train-scheduling/train-number.util.ts
Marshal e0c3044933 feat(train-sets): implement multi-locomotive support for train sets
- 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.
2026-06-24 23:54:45 +00:00

69 lines
2.2 KiB
TypeScript

/**
* Fixed train-number pools assigned to a train on dispatch.
*
* The prefix encodes cargo type (8 = container, 1 = bulk) and the parity encodes
* trade direction (odd = export, even = import). Numbers are finite and recycle:
* a number is "in use" only while its train is DISPATCHED and not yet ARRIVED.
*/
export const CONTAINER_EXPORT_NUMBERS = [
'8001', '8101', '8201', '8301', '8401', '8501', '8601', '8701', '8801', '8901',
] as const;
export const CONTAINER_IMPORT_NUMBERS = [
'8002', '8102', '8202', '8302', '8402', '8502', '8602', '8702', '8802', '8902',
] as const;
export const BULK_EXPORT_NUMBERS = ['1101', '1103', '1105', '1107'] as const;
export const BULK_IMPORT_NUMBERS = ['1002', '1004', '1006', '1008'] as const;
export type CargoKind = 'CONTAINER' | 'BULK';
export type PoolDirection = 'IMPORT' | 'EXPORT';
export interface TrainNumberPool {
cargo: CargoKind;
/** EXPORT = odd numbers, IMPORT = even numbers. */
direction: PoolDirection;
numbers: readonly string[];
}
/**
* Resolve which fixed pool a train draws from.
*
* - Cargo: container vs bulk by dominant wagon count; ties resolve to container.
* - Direction: EXPORT → odd pool, IMPORT → even pool. DOMESTIC (neither end is
* Djibouti) has no dedicated pool, so it defaults to the export/odd pool.
*/
export function pickTrainNumberPool(
containerWagons: number,
bulkWagons: number,
direction: 'IMPORT' | 'EXPORT' | 'DOMESTIC' | null | undefined,
): TrainNumberPool {
const cargo: CargoKind = bulkWagons > containerWagons ? 'BULK' : 'CONTAINER';
const poolDirection: PoolDirection = direction === 'IMPORT' ? 'IMPORT' : 'EXPORT';
const numbers =
cargo === 'CONTAINER'
? poolDirection === 'IMPORT'
? CONTAINER_IMPORT_NUMBERS
: CONTAINER_EXPORT_NUMBERS
: poolDirection === 'IMPORT'
? BULK_IMPORT_NUMBERS
: BULK_EXPORT_NUMBERS;
return { cargo, direction: poolDirection, numbers };
}
/** Lowest pool number not currently in use, or null when the pool is exhausted. */
export function pickLowestFreeNumber(
pool: readonly string[],
usedNumbers: Iterable<string>,
): string | null {
const used = new Set(usedNumbers);
for (const number of pool) {
if (!used.has(number)) return number;
}
return null;
}