Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

front: fix macro trainruns frequencies #10448

Merged
merged 3 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type {
LabelGroupDto,
NetzgrafikDto,
TimeLockDto,
TrainrunCategory,
TrainrunFrequency,
TrainrunTimeCategory,
} from '../NGE/types';

export const TRAINRUN_CATEGORY_HALTEZEITEN = {
HaltezeitIPV: { haltezeit: 0, no_halt: false },
HaltezeitA: { haltezeit: 0, no_halt: false },
HaltezeitB: { haltezeit: 0, no_halt: false },
HaltezeitC: { haltezeit: 0, no_halt: false },
HaltezeitD: { haltezeit: 0, no_halt: false },
HaltezeitUncategorized: { haltezeit: 0, no_halt: false },
};

export const TRAINRUN_LABEL_GROUP: LabelGroupDto = {
id: 1,
name: 'Default',
labelRef: 'Trainrun',
};
export const NODE_LABEL_GROUP: LabelGroupDto = {
id: 2,
name: 'Node',
labelRef: 'Node',
};

export const DEFAULT_TRAINRUN_CATEGORY: TrainrunCategory = {
id: 1, // In NGE, Trainrun.DEFAULT_TRAINRUN_CATEGORY
order: 0,
name: 'Default',
shortName: '', // TODO: find a better way to hide this in the graph
fachCategory: 'HaltezeitUncategorized',
colorRef: 'EC',
minimalTurnaroundTime: 0,
nodeHeadwayStop: 0,
nodeHeadwayNonStop: 0,
sectionHeadway: 0,
};

export const DEFAULT_TRAINRUN_FREQUENCIES: TrainrunFrequency[] = [
{
id: 2,
order: 0,
frequency: 30,
offset: 0,
name: 'Half-hourly',
shortName: '30',
linePatternRef: '30',
},
{
id: 3, // default NGE frequency takes id 3
order: 1,
frequency: 60,
offset: 0,
name: 'Hourly',
/** Short name, needs to be unique */
shortName: '60',
linePatternRef: '60',
},
{
id: 4,
order: 2,
frequency: 120,
offset: 0,
name: 'Two-hourly',
shortName: '120',
linePatternRef: '120',
},
];

export const DEFAULT_TRAINRUN_FREQUENCY: TrainrunFrequency = DEFAULT_TRAINRUN_FREQUENCIES[1];

export const DEFAULT_TRAINRUN_TIME_CATEGORY: TrainrunTimeCategory = {
id: 0, // In NGE, Trainrun.DEFAULT_TRAINRUN_TIME_CATEGORY
order: 0,
name: 'Default',
shortName: '7/24',
dayTimeInterval: [],
weekday: [1, 2, 3, 4, 5, 6, 7],
linePatternRef: '7/24',
};

export const DEFAULT_DTO: NetzgrafikDto = {
resources: [],
nodes: [],
trainruns: [],
trainrunSections: [],
metadata: {
netzgrafikColors: [],
trainrunCategories: [DEFAULT_TRAINRUN_CATEGORY],
trainrunFrequencies: [...DEFAULT_TRAINRUN_FREQUENCIES],
trainrunTimeCategories: [DEFAULT_TRAINRUN_TIME_CATEGORY],
},
freeFloatingTexts: [],
labels: [],
labelGroups: [],
filterData: {
filterSettings: [],
},
};

export const DEFAULT_TIME_LOCK: TimeLockDto = {
time: null,
consecutiveTime: null,
lock: false,
warning: null,
timeFormatter: null,
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ import type { AppDispatch } from 'store';
import { formatToIsoDate } from 'utils/date';
import { Duration } from 'utils/duration';

import { DEFAULT_TRAINRUN_FREQUENCIES, DEFAULT_TRAINRUN_FREQUENCY } from './consts';
import type MacroEditorState from './MacroEditorState';
import type { NodeIndexed } from './MacroEditorState';
import { DEFAULT_TRAINRUN_FREQUENCIES, DEFAULT_TRAINRUN_FREQUENCY } from './osrdToNge';
import { createMacroNode, deleteMacroNodeByNgeId, updateMacroNode } from './utils';
import {
createMacroNode,
deleteMacroNodeByNgeId,
trainrunFrequencyFromLabel,
updateMacroNode,
} from './utils';
import type {
NetzgrafikDto,
NGEEvent,
Expand Down Expand Up @@ -209,7 +214,7 @@ const createTrainSchedulePayload = async ({
}

const trainScheduleLabels =
trainSchedule?.labels?.filter((label) => label.match(/^frequency::(?!30$|60$|120$)\d+$/)) || [];
trainSchedule?.labels?.filter((label) => trainrunFrequencyFromLabel(label) !== null) || [];

trainrunLabels = uniq([...trainrunLabels, ...trainScheduleLabels]);

Expand All @@ -227,6 +232,8 @@ const createTrainSchedulePayload = async ({
);
const isNonStopTransit = transition?.isNonStopTransit ?? false;

// Note that arrival is the time the train arrives at the node
// and departure is the time the train leaves the node
let arrival = getTimeLockDate(section.targetArrival, startTimeLock, startDate);
const departure = nextSection
? getTimeLockDate(nextSection.sourceDeparture, startTimeLock, startDate)
Expand All @@ -242,7 +249,7 @@ const createTrainSchedulePayload = async ({
at: `${section.targetNodeId}-${index + 1}`,
arrival: formatDateDifferenceFrom(startDate, arrival),
stop_for:
departure && !isNonStopTransit ? formatDateDifferenceFrom(departure, arrival) : null,
departure && !isNonStopTransit ? formatDateDifferenceFrom(arrival, departure) : null,
};
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,124 +6,29 @@ import buildOpSearchQuery from 'modules/operationalPoint/helpers/buildOpSearchQu
import type { AppDispatch } from 'store';
import { Duration, addDurationToDate } from 'utils/duration';

import {
TRAINRUN_CATEGORY_HALTEZEITEN,
NODE_LABEL_GROUP,
DEFAULT_TRAINRUN_FREQUENCIES,
DEFAULT_TRAINRUN_CATEGORY,
DEFAULT_TRAINRUN_FREQUENCY,
DEFAULT_TRAINRUN_TIME_CATEGORY,
TRAINRUN_LABEL_GROUP,
DEFAULT_TIME_LOCK,
DEFAULT_DTO,
} from './consts';
import MacroEditorState, { type NodeIndexed } from './MacroEditorState';
import { deleteMacroNodeByDbId, getSavedMacroNodes } from './utils';
import { deleteMacroNodeByDbId, getSavedMacroNodes, trainrunFrequencyFromLabel } from './utils';
import {
type PortDto,
type TimeLockDto,
type TrainrunSectionDto,
type TrainrunCategory,
type TrainrunTimeCategory,
type TrainrunFrequency,
type NetzgrafikDto,
type LabelGroupDto,
PortAlignment,
type LabelDto,
} from '../NGE/types';

const TRAINRUN_CATEGORY_HALTEZEITEN = {
HaltezeitIPV: { haltezeit: 0, no_halt: false },
HaltezeitA: { haltezeit: 0, no_halt: false },
HaltezeitB: { haltezeit: 0, no_halt: false },
HaltezeitC: { haltezeit: 0, no_halt: false },
HaltezeitD: { haltezeit: 0, no_halt: false },
HaltezeitUncategorized: { haltezeit: 0, no_halt: false },
};

const TRAINRUN_LABEL_GROUP: LabelGroupDto = {
id: 1,
name: 'Default',
labelRef: 'Trainrun',
};
const NODE_LABEL_GROUP: LabelGroupDto = {
id: 2,
name: 'Node',
labelRef: 'Node',
};

const DEFAULT_TRAINRUN_CATEGORY: TrainrunCategory = {
id: 1, // In NGE, Trainrun.DEFAULT_TRAINRUN_CATEGORY
order: 0,
name: 'Default',
shortName: '', // TODO: find a better way to hide this in the graph
fachCategory: 'HaltezeitUncategorized',
colorRef: 'EC',
minimalTurnaroundTime: 0,
nodeHeadwayStop: 0,
nodeHeadwayNonStop: 0,
sectionHeadway: 0,
};

export const DEFAULT_TRAINRUN_FREQUENCIES: TrainrunFrequency[] = [
{
id: 2,
order: 0,
frequency: 30,
offset: 0,
name: 'Half-hourly',
shortName: '30',
linePatternRef: '30',
},
{
id: 3, // default NGE frequency takes id 3
order: 1,
frequency: 60,
offset: 0,
name: 'Hourly',
/** Short name, needs to be unique */
shortName: '60',
linePatternRef: '60',
},
{
id: 4,
order: 2,
frequency: 120,
offset: 0,
name: 'Two-hourly',
shortName: '120',
linePatternRef: '120',
},
];

export const DEFAULT_TRAINRUN_FREQUENCY: TrainrunFrequency = DEFAULT_TRAINRUN_FREQUENCIES[1];

const DEFAULT_TRAINRUN_TIME_CATEGORY: TrainrunTimeCategory = {
id: 0, // In NGE, Trainrun.DEFAULT_TRAINRUN_TIME_CATEGORY
order: 0,
name: 'Default',
shortName: '7/24',
dayTimeInterval: [],
weekday: [1, 2, 3, 4, 5, 6, 7],
linePatternRef: '7/24',
};

const DEFAULT_DTO: NetzgrafikDto = {
resources: [],
nodes: [],
trainruns: [],
trainrunSections: [],
metadata: {
netzgrafikColors: [],
trainrunCategories: [DEFAULT_TRAINRUN_CATEGORY],
trainrunFrequencies: [...DEFAULT_TRAINRUN_FREQUENCIES],
trainrunTimeCategories: [DEFAULT_TRAINRUN_TIME_CATEGORY],
},
freeFloatingTexts: [],
labels: [],
labelGroups: [],
filterData: {
filterSettings: [],
},
};

const DEFAULT_TIME_LOCK: TimeLockDto = {
time: null,
consecutiveTime: null,
lock: false,
warning: null,
timeFormatter: null,
};

/**
* Execute the search payload and collect all result pages.
*/
Expand Down Expand Up @@ -225,6 +130,24 @@ const castNodeToNge = (
),
});

/**
* NGE trainrun frequency is stored as OSRD labels (`"frequency::30"` or `"frequency::120"`).
* Update the current frequency if the new frequency is smaller.
*/
const getFrequencyFromLabels = (labels: string[]): TrainrunFrequency | null => {
let currentFrequency: TrainrunFrequency | null = null;
labels.forEach((label) => {
const newFrequency = trainrunFrequencyFromLabel(label);
if (
newFrequency &&
(!currentFrequency || newFrequency.frequency < currentFrequency.frequency)
) {
currentFrequency = newFrequency;
}
});
return currentFrequency;
};

/**
* Load & index the data of the train schedule for the given scenario
*/
Expand Down Expand Up @@ -298,7 +221,7 @@ export const loadAndIndexNge = async (
};

/**
* Translate the train schedule in NGE "trainruns".
* Translate the train schedule in NGE "trainrun".
*/
const getNgeTrainruns = (state: MacroEditorState, labels: LabelDto[]) =>
state.trainSchedules
Expand All @@ -307,11 +230,15 @@ const getNgeTrainruns = (state: MacroEditorState, labels: LabelDto[]) =>
id: trainSchedule.id,
name: trainSchedule.train_name,
categoryId: DEFAULT_TRAINRUN_CATEGORY.id,
frequencyId: DEFAULT_TRAINRUN_FREQUENCY.id,
frequencyId:
getFrequencyFromLabels(trainSchedule.labels || [])?.id ?? DEFAULT_TRAINRUN_FREQUENCY.id,
trainrunTimeCategoryId: DEFAULT_TRAINRUN_TIME_CATEGORY.id,
labelIds: (trainSchedule.labels || []).map((l) =>
labels.findIndex((e) => e.label === l && e.labelGroupId === TRAINRUN_LABEL_GROUP.id)
),
labelIds: (trainSchedule.labels || [])
// we keep only not handled frequencies as labels to be not redundant
.filter((l) => trainrunFrequencyFromLabel(l) === null)
.map((l) =>
labels.findIndex((e) => e.label === l && e.labelGroupId === TRAINRUN_LABEL_GROUP.id)
),
}));

/**
Expand Down Expand Up @@ -497,7 +424,7 @@ export const getNgeDto = (state: MacroEditorState): NetzgrafikDto => {
metadata: {
netzgrafikColors: [],
trainrunCategories: [DEFAULT_TRAINRUN_CATEGORY],
trainrunFrequencies: [DEFAULT_TRAINRUN_FREQUENCY],
trainrunFrequencies: DEFAULT_TRAINRUN_FREQUENCIES,
trainrunTimeCategories: [DEFAULT_TRAINRUN_TIME_CATEGORY],
},
trainruns: getNgeTrainruns(state, labels),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from 'common/api/osrdEditoastApi';
import type { AppDispatch } from 'store';

import { DEFAULT_TRAINRUN_FREQUENCIES } from './consts';
import type MacroEditorState from './MacroEditorState';
import type { NodeIndexed } from './MacroEditorState';

Expand Down Expand Up @@ -161,3 +162,13 @@ export const getSavedMacroNodes = async (
}
return result;
};

/**
* Match a frequency label to a NGE TrainrunFrequency, or `null` if not handled.
*/
export const trainrunFrequencyFromLabel = (label: string) => {
if (!label.startsWith('frequency::')) return null;
const n = parseInt(label.split('::', 2)[1], 10);
const frequency = DEFAULT_TRAINRUN_FREQUENCIES.find((freq) => freq.frequency === n);
return frequency ?? null;
};
Loading