mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 17:45:42 +00:00
test batch system
This commit is contained in:
@@ -52,6 +52,12 @@ export type DerivedTrainCapacity = {
|
||||
maxLengthMeters: number;
|
||||
/** Length-derived slot count. Weight is enforced separately against real cargo. */
|
||||
maxWagonSlots: number;
|
||||
/** Caps WITHOUT the overage tolerance — what batch filling budgets against. */
|
||||
baseWeightTons: number;
|
||||
baseLengthMeters: number;
|
||||
/** Overage spendable only by admitting a booking whole, never by a split. */
|
||||
toleranceTons: number;
|
||||
toleranceMeters: number;
|
||||
};
|
||||
|
||||
/** What a consist currently uses, and what is left on each axis. */
|
||||
@@ -88,28 +94,48 @@ export function grossWagonWeightTons(slot: Pick<ConsistSlot, 'tareWeightTons' |
|
||||
/**
|
||||
* Hard caps for a train: the locomotive's own limits, floored by the global rule
|
||||
* caps, then widened by the locomotive's overage tolerance.
|
||||
*
|
||||
* `base*` are the caps BEFORE the tolerance is added. The tolerance is not
|
||||
* general-purpose headroom: batch filling budgets against the base caps and may
|
||||
* spend the tolerance only to admit a booking WHOLE (never to size a split), so
|
||||
* both figures are returned. `base + tolerance === max` always holds, including
|
||||
* the fallback path.
|
||||
*/
|
||||
export function trainHardCaps(
|
||||
locomotive: LocomotiveLimits,
|
||||
ruleCaps?: { maxTrainWeightTons?: number; maxTrainLengthMeters?: number },
|
||||
): { maxWeightTons: number; maxLengthMeters: number } {
|
||||
): {
|
||||
maxWeightTons: number;
|
||||
maxLengthMeters: number;
|
||||
baseWeightTons: number;
|
||||
baseLengthMeters: number;
|
||||
toleranceTons: number;
|
||||
toleranceMeters: number;
|
||||
} {
|
||||
const overageTons = num(locomotive.overageToleranceTons);
|
||||
const overageMeters = num(locomotive.overageToleranceMeters);
|
||||
|
||||
const weight =
|
||||
Math.min(
|
||||
num(locomotive.maxPullWeightTons, Infinity) || Infinity,
|
||||
ruleCaps?.maxTrainWeightTons ?? Infinity,
|
||||
) + overageTons;
|
||||
const length =
|
||||
Math.min(
|
||||
num(locomotive.maxTrainLengthMeters, Infinity) || Infinity,
|
||||
ruleCaps?.maxTrainLengthMeters ?? Infinity,
|
||||
) + overageMeters;
|
||||
const baseWeight = Math.min(
|
||||
num(locomotive.maxPullWeightTons, Infinity) || Infinity,
|
||||
ruleCaps?.maxTrainWeightTons ?? Infinity,
|
||||
);
|
||||
const baseLength = Math.min(
|
||||
num(locomotive.maxTrainLengthMeters, Infinity) || Infinity,
|
||||
ruleCaps?.maxTrainLengthMeters ?? Infinity,
|
||||
);
|
||||
|
||||
const baseWeightTons = Number.isFinite(baseWeight) ? baseWeight : MAX_FALLBACK_WEIGHT;
|
||||
const baseLengthMeters = Number.isFinite(baseLength) ? baseLength : MAX_FALLBACK_LENGTH;
|
||||
const toleranceTons = Number.isFinite(baseWeight) ? overageTons : 0;
|
||||
const toleranceMeters = Number.isFinite(baseLength) ? overageMeters : 0;
|
||||
|
||||
return {
|
||||
maxWeightTons: Number.isFinite(weight) ? weight : MAX_FALLBACK_WEIGHT,
|
||||
maxLengthMeters: Number.isFinite(length) ? length : MAX_FALLBACK_LENGTH,
|
||||
maxWeightTons: baseWeightTons + toleranceTons,
|
||||
maxLengthMeters: baseLengthMeters + toleranceMeters,
|
||||
baseWeightTons,
|
||||
baseLengthMeters,
|
||||
toleranceTons,
|
||||
toleranceMeters,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -129,7 +155,7 @@ export function deriveTrainCapacityFromLocomotive(
|
||||
wagonTypes: WagonTypeDimensions[],
|
||||
ruleCaps?: { maxTrainWeightTons?: number; maxTrainLengthMeters?: number },
|
||||
): DerivedTrainCapacity {
|
||||
const { maxWeightTons, maxLengthMeters } = trainHardCaps(locomotive, ruleCaps);
|
||||
const caps = trainHardCaps(locomotive, ruleCaps);
|
||||
|
||||
const lengths = wagonTypes
|
||||
.map((w) => num(w.lengthMeters))
|
||||
@@ -137,9 +163,9 @@ export function deriveTrainCapacityFromLocomotive(
|
||||
const minLength = lengths.length ? Math.min(...lengths) : DEFAULT_WAGON_LENGTH_M;
|
||||
|
||||
const maxWagonSlots =
|
||||
minLength > 0 ? Math.max(0, Math.floor(maxLengthMeters / minLength)) : 0;
|
||||
minLength > 0 ? Math.max(0, Math.floor(caps.maxLengthMeters / minLength)) : 0;
|
||||
|
||||
return { maxWeightTons, maxLengthMeters, maxWagonSlots };
|
||||
return { ...caps, maxWagonSlots };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,17 +291,33 @@ export function bookingGrossWeightTons(
|
||||
* returns the count that maximizes the cargo carried, with the cargo cap the
|
||||
* caller should apply. Null when not even one part-loaded wagon fits. The
|
||||
* offer is a strict subset of the booking: never all `bookingWagons`.
|
||||
*
|
||||
* `fullWagonsOnly` (bulk): every offered wagon rides at its full rated payload,
|
||||
* so each wagon costs `capacityTons + tareWeightTons` of gross weight room and
|
||||
* the offer is the largest whole-wagon count whose gross fits — never a
|
||||
* part-loaded last wagon squeezed into leftover pull weight.
|
||||
*/
|
||||
export function sizePartialOfferWagons(
|
||||
room: { wagons: number; weightTons: number; lengthMeters: number },
|
||||
bookingWagons: number,
|
||||
perWagon: { capacityTons: number; tareWeightTons: number; lengthMeters: number },
|
||||
opts?: { fullWagonsOnly?: boolean },
|
||||
): { wagons: number; maxCargoTons: number } | null {
|
||||
const maxByLength =
|
||||
perWagon.lengthMeters > 0
|
||||
? Math.floor(room.lengthMeters / perWagon.lengthMeters)
|
||||
: room.wagons;
|
||||
const ceiling = Math.min(room.wagons, maxByLength, bookingWagons - 1);
|
||||
|
||||
if (opts?.fullWagonsOnly) {
|
||||
const grossPerWagon = perWagon.capacityTons + perWagon.tareWeightTons;
|
||||
const maxByWeight =
|
||||
grossPerWagon > 0 ? Math.floor(room.weightTons / grossPerWagon) : 0;
|
||||
const wagons = Math.min(ceiling, maxByWeight);
|
||||
if (wagons < 1) return null;
|
||||
return { wagons, maxCargoTons: round3(wagons * perWagon.capacityTons) };
|
||||
}
|
||||
|
||||
let wagons = 0;
|
||||
let bestCargoTons = 0;
|
||||
for (let w = 1; w <= ceiling; w += 1) {
|
||||
|
||||
Reference in New Issue
Block a user