diff --git a/das_client/assets/icons/icon_indicator_checked.svg b/das_client/assets/icons/icon_indicator_checked.svg
new file mode 100644
index 00000000..2f755ac8
--- /dev/null
+++ b/das_client/assets/icons/icon_indicator_checked.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/das_client/integration_test/test/train_journey_table_test.dart b/das_client/integration_test/test/train_journey_table_test.dart
index 9bf97a06..6ef812b9 100644
--- a/das_client/integration_test/test/train_journey_table_test.dart
+++ b/das_client/integration_test/test/train_journey_table_test.dart
@@ -6,17 +6,159 @@ import 'package:das_client/app/pages/journey/train_journey/widgets/table/curve_p
import 'package:das_client/app/pages/journey/train_journey/widgets/table/protection_section_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/service_point_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/signal_row.dart';
+import 'package:das_client/app/pages/journey/train_journey/widgets/train_journey.dart';
import 'package:das_client/app/pages/profile/profile_page.dart';
import 'package:das_client/app/widgets/table/das_table.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import '../app_test.dart';
import '../util/test_utils.dart';
void main() {
group('train journey table test', () {
+ testWidgets('test breaking series defaults to ??', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: '4816');
+
+ final breakingSeriesHeaderCell = find.byKey(TrainJourney.breakingSeriesHeaderKey);
+ expect(breakingSeriesHeaderCell, findsOneWidget);
+ expect(find.descendant(of: breakingSeriesHeaderCell, matching: find.text('??')), findsNWidgets(1));
+ });
+
+ testWidgets('test default breaking series is taken from train characteristics (R115)', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: 'T5');
+
+ final breakingSeriesHeaderCell = find.byKey(TrainJourney.breakingSeriesHeaderKey);
+ expect(breakingSeriesHeaderCell, findsOneWidget);
+ expect(find.descendant(of: breakingSeriesHeaderCell, matching: find.text('R115')), findsNWidgets(1));
+ });
+
+ testWidgets('test all breakseries options are displayed', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: 'T5');
+
+ // Open break series bottom sheet
+ await tapElement(tester, find.byKey(TrainJourney.breakingSeriesHeaderKey));
+
+ final expectedCategories = {'R', 'A', 'D'};
+
+ for (final entry in expectedCategories) {
+ expect(find.text(entry), findsOneWidget);
+ }
+
+ final expectedOptions = {
+ 'R105',
+ 'R115',
+ 'R125',
+ 'R135',
+ 'R150',
+ 'A50',
+ 'A60',
+ 'A65',
+ 'A70',
+ 'A75',
+ 'A80',
+ 'A85',
+ 'A95',
+ 'A105',
+ 'A115',
+ 'D30'
+ };
+
+ for (final entry in expectedOptions) {
+ expect(find.text(entry), findsAtLeast(1));
+ }
+ });
+
+ testWidgets('test message when no breakseries are defined', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: '7839');
+
+ // Open break series bottom sheet
+ await tapElement(tester, find.byKey(TrainJourney.breakingSeriesHeaderKey));
+
+ expect(find.text(l10n.p_train_journey_break_series_empty), findsOneWidget);
+ });
+
+ testWidgets('test speed values of default breakSeries (R115)', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: 'T5');
+
+ final expectedSpeeds = {
+ 'Genève-Aéroport': '60',
+ '65.3': '44', // 1. Curve
+ 'New Line Speed All': '60',
+ 'Genève': '60',
+ 'New Line Speed A Missing': '60',
+ '42.5': '44', // 2. Curve
+ '40.5': null, // 3. Curve
+ 'Gland': '60',
+ };
+
+ for (final entry in expectedSpeeds.entries) {
+ final tableRow = findDASTableRowByText(entry.key);
+ expect(tableRow, findsOneWidget);
+
+ if (entry.value != null) {
+ final speedText = find.descendant(of: tableRow, matching: find.text(entry.value!));
+ expect(speedText, findsOneWidget);
+ } else {
+ final textWidgets = find.descendant(of: tableRow, matching: find.byWidgetPredicate((it) => it is Text));
+ expect(textWidgets, findsNWidgets(2)); // KM and Kurve text widgets
+ }
+ }
+ });
+
+ testWidgets('test speed values of missing break Series', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: 'T5');
+
+ await _selectBreakSeries(tester, breakSeries: 'A85');
+
+ final breakingSeriesHeaderCell = find.byKey(TrainJourney.breakingSeriesHeaderKey);
+ expect(breakingSeriesHeaderCell, findsOneWidget);
+ expect(find.descendant(of: breakingSeriesHeaderCell, matching: find.text('A85')), findsNWidgets(1));
+
+ final expectedSpeeds = {
+ 'Genève-Aéroport': '90',
+ '65.3': '55', // 1. Curve
+ 'New Line Speed All': '90',
+ 'Genève': 'XX',
+ 'New Line Speed A Missing': 'XX',
+ '42.5': 'XX', // 2. Curve
+ '40.5': null, // 3. Curve
+ 'Gland': '90',
+ };
+
+ for (final entry in expectedSpeeds.entries) {
+ final tableRow = findDASTableRowByText(entry.key);
+ expect(tableRow, findsOneWidget);
+
+ if (entry.value != null) {
+ final speedText = find.descendant(of: tableRow, matching: find.text(entry.value!));
+ expect(speedText, findsOneWidget);
+ } else {
+ final textWidgets = find.descendant(of: tableRow, matching: find.byWidgetPredicate((it) => it is Text));
+ expect(textWidgets, findsNWidgets(2)); // KM and Kurve text widgets
+ }
+ }
+ });
+
testWidgets('test connection track is displayed correctly', (tester) async {
await prepareAndStartApp(tester);
@@ -131,7 +273,6 @@ void main() {
l10n.p_train_journey_table_journey_information_label,
l10n.p_train_journey_table_time_label,
l10n.p_train_journey_table_advised_speed_label,
- l10n.p_train_journey_table_braked_weight_speed_label,
l10n.p_train_journey_table_graduated_speed_label,
];
@@ -437,7 +578,8 @@ void main() {
final rowsAtKm33_8 = findDASTableRowByText('33.8');
expect(rowsAtKm33_8, findsExactly(2));
final segment1CABStop = rowsAtKm33_8.last; // end should be after other elements at same location
- final segment1CABStopIcon = find.descendant(of: segment1CABStop, matching: find.byKey(CABSignalingRow.cabSignalingEndIconKey));
+ final segment1CABStopIcon =
+ find.descendant(of: segment1CABStop, matching: find.byKey(CABSignalingRow.cabSignalingEndIconKey));
expect(segment1CABStopIcon, findsOneWidget);
// Track equipment segment without ETCS level 2 should be ignored
@@ -449,7 +591,8 @@ void main() {
final rowsAtKm12_5 = findDASTableRowByText('12.5');
expect(rowsAtKm12_5, findsExactly(2));
final segment2CABStart = rowsAtKm12_5.first; // start should be before other elements at same location
- final segment2CABStartIcon = find.descendant(of: segment2CABStart, matching: find.byKey(CABSignalingRow.cabSignalingStartIconKey));
+ final segment2CABStartIcon =
+ find.descendant(of: segment2CABStart, matching: find.byKey(CABSignalingRow.cabSignalingStartIconKey));
expect(segment2CABStartIcon, findsOneWidget);
await tester.dragUntilVisible(find.text('75.3'), scrollableFinder, const Offset(0, -50));
final trackEquipmentTypeChange = findDASTableRowByText('56.8');
@@ -458,13 +601,15 @@ void main() {
final rothristServicePointRow = findDASTableRowByText('46.2');
expect(rothristServicePointRow, findsOneWidget); // no CAB signaling at connecting ETCS L2 segments
final segment2CABEnd = findDASTableRowByText('39.9');
- final segment2CABEndIcon = find.descendant(of: segment2CABEnd, matching: find.byKey(CABSignalingRow.cabSignalingEndIconKey));
+ final segment2CABEndIcon =
+ find.descendant(of: segment2CABEnd, matching: find.byKey(CABSignalingRow.cabSignalingEndIconKey));
expect(segment2CABEndIcon, findsOneWidget);
// CAB segment with end outside train journey and start at 8.3 km
await tester.dragUntilVisible(find.text('9.5'), scrollableFinder, const Offset(0, -50));
final segment3CABStart = findDASTableRowByText('8.3');
- final segment3CABStartIcon = find.descendant(of: segment3CABStart, matching: find.byKey(CABSignalingRow.cabSignalingStartIconKey));
+ final segment3CABStartIcon =
+ find.descendant(of: segment3CABStart, matching: find.byKey(CABSignalingRow.cabSignalingStartIconKey));
expect(segment3CABStartIcon, findsOneWidget);
});
});
@@ -487,3 +632,15 @@ Future _loadTrainJourney(WidgetTester tester, {required String trainNumber
// wait for train journey to load
await tester.pumpAndSettle();
}
+
+Future _selectBreakSeries(WidgetTester tester, {required String breakSeries}) async {
+ // Open break series bottom sheet
+ await tapElement(tester, find.byKey(TrainJourney.breakingSeriesHeaderKey));
+
+ // Check if the bottom sheeet is opened
+ expect(find.text(l10n.p_train_journey_break_series), findsOneWidget);
+ await tapElement(tester, find.text(breakSeries));
+
+ // confirm button
+ await tapElement(tester, find.text(l10n.c_button_confirm));
+}
diff --git a/das_client/integration_test/test/train_journey_test.dart b/das_client/integration_test/test/train_journey_test.dart
index cccd7f8d..31aeb7cd 100644
--- a/das_client/integration_test/test/train_journey_test.dart
+++ b/das_client/integration_test/test/train_journey_test.dart
@@ -1,5 +1,5 @@
import 'package:das_client/app/pages/journey/train_journey/widgets/header/header.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter_test/flutter_test.dart';
import '../app_test.dart';
diff --git a/das_client/integration_test/test/train_search_test.dart b/das_client/integration_test/test/train_search_test.dart
index 194ee324..acb52828 100644
--- a/das_client/integration_test/test/train_search_test.dart
+++ b/das_client/integration_test/test/train_search_test.dart
@@ -1,6 +1,6 @@
import 'package:das_client/util/error_code.dart';
import 'package:das_client/util/format.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
diff --git a/das_client/integration_test/util/test_utils.dart b/das_client/integration_test/util/test_utils.dart
index 11f6cf72..d0e26eee 100644
--- a/das_client/integration_test/util/test_utils.dart
+++ b/das_client/integration_test/util/test_utils.dart
@@ -1,5 +1,5 @@
import 'package:das_client/app/widgets/table/das_table.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
diff --git a/das_client/l10n/strings_de.arb b/das_client/l10n/strings_de.arb
index 2e74c26a..d4f8ae78 100644
--- a/das_client/l10n/strings_de.arb
+++ b/das_client/l10n/strings_de.arb
@@ -1,6 +1,5 @@
{
"c_app_name": "DAS Client",
- "p_train_selection_load": "Übernehmen",
"p_train_selection_trainnumber_description": "Zugnummer",
"p_train_selection_ru_description": "EVU",
"p_train_selection_date_description": "Datum",
@@ -13,7 +12,8 @@
"p_train_journey_table_journey_information_label": "Streckeninformationen",
"p_train_journey_table_advised_speed_label": "FE",
"p_train_journey_table_graduated_speed_label": "OG",
- "p_train_journey_table_braked_weight_speed_label": "R150",
+ "p_train_journey_break_series": "Bremsreihe",
+ "p_train_journey_break_series_empty": "Es sind keine Bremsreihen vorhanden",
"p_train_journey_table_curve_type_curve": "Kurve",
"p_train_journey_table_curve_type_station_exit_curve": "Kurve Ausfahrt",
"p_train_journey_table_curve_type_curve_after_halt": "Kurve nach Haltestelle",
@@ -44,5 +44,6 @@
"c_error_sfera_request_timeout": "Timeout bei der Anfrage",
"c_error_sfera_jp_unavailable": "Fahrordnung nicht vorhanden",
"c_error_sfera_sp_invalid": "Unvollständige Daten erhalten",
- "c_connection_track_weiche": "Weiche"
+ "c_connection_track_weiche": "Weiche",
+ "c_button_confirm": "Übernehmen"
}
\ No newline at end of file
diff --git a/das_client/lib/app.dart b/das_client/lib/app.dart
index 8c0724bd..a1f87929 100644
--- a/das_client/lib/app.dart
+++ b/das_client/lib/app.dart
@@ -1,6 +1,6 @@
import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/nav/app_router.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class App extends StatelessWidget {
diff --git a/das_client/lib/app/bloc/train_journey_cubit.dart b/das_client/lib/app/bloc/train_journey_cubit.dart
index 916e4da0..7261e76c 100644
--- a/das_client/lib/app/bloc/train_journey_cubit.dart
+++ b/das_client/lib/app/bloc/train_journey_cubit.dart
@@ -1,12 +1,15 @@
import 'dart:async';
import 'package:das_client/app/model/ru.dart';
+import 'package:das_client/app/model/train_journey_settings.dart';
+import 'package:das_client/model/journey/break_series.dart';
import 'package:das_client/model/journey/journey.dart';
import 'package:das_client/sfera/sfera_component.dart';
import 'package:das_client/util/error_code.dart';
import 'package:fimber/fimber.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:rxdart/rxdart.dart';
part 'train_journey_state.dart';
@@ -20,11 +23,16 @@ class TrainJourneyCubit extends Cubit {
Stream get journeyStream => _sferaService.journeyStream;
+ final _settingsSubject = BehaviorSubject.seeded(TrainJourneySettings());
+
+ Stream get settingsStream => _settingsSubject.stream;
+
StreamSubscription? _stateSubscription;
void loadTrainJourney() async {
final currentState = state;
if (currentState is SelectingTrainJourneyState) {
+ _resetSettings();
final date = currentState.date;
final ru = currentState.ru;
final trainNumber = currentState.trainNumber;
@@ -43,7 +51,7 @@ class TrainJourneyCubit extends Cubit {
case SferaServiceState.connecting:
case SferaServiceState.handshaking:
case SferaServiceState.loadingJourney:
- case SferaServiceState.loadingSegments:
+ case SferaServiceState.loadingAdditionalData:
emit(ConnectingState(ru, trainNumber, date));
break;
case SferaServiceState.disconnected:
@@ -57,6 +65,10 @@ class TrainJourneyCubit extends Cubit {
}
}
+ void _resetSettings() {
+ _settingsSubject.add(TrainJourneySettings());
+ }
+
void updateTrainNumber(String? trainNumber) {
if (state is SelectingTrainJourneyState) {
emit(SelectingTrainJourneyState(
@@ -101,6 +113,10 @@ class TrainJourneyCubit extends Cubit {
_stateSubscription?.cancel();
_stateSubscription = null;
}
+
+ void updateBreakSeries(BreakSeries selectedBreakSeries) {
+ _settingsSubject.add(_settingsSubject.value.copyWith(selectedBreakSeries: selectedBreakSeries));
+ }
}
extension ContextBlocExtension on BuildContext {
diff --git a/das_client/lib/app/model/train_journey_settings.dart b/das_client/lib/app/model/train_journey_settings.dart
new file mode 100644
index 00000000..0df6482f
--- /dev/null
+++ b/das_client/lib/app/model/train_journey_settings.dart
@@ -0,0 +1,15 @@
+import 'package:das_client/model/journey/break_series.dart';
+
+class TrainJourneySettings {
+ TrainJourneySettings({
+ this.selectedBreakSeries,
+ });
+
+ final BreakSeries? selectedBreakSeries;
+
+ TrainJourneySettings copyWith({BreakSeries? selectedBreakSeries}) {
+ return TrainJourneySettings(
+ selectedBreakSeries: selectedBreakSeries ?? this.selectedBreakSeries,
+ );
+ }
+}
diff --git a/das_client/lib/app/nav/das_navigation_drawer.dart b/das_client/lib/app/nav/das_navigation_drawer.dart
index aee710c8..96ee9fbd 100644
--- a/das_client/lib/app/nav/das_navigation_drawer.dart
+++ b/das_client/lib/app/nav/das_navigation_drawer.dart
@@ -3,7 +3,7 @@ import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/nav/app_router.dart';
import 'package:das_client/app/widgets/app_version_text.dart';
import 'package:das_client/app/widgets/device_id_text.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class DASNavigationDrawer extends StatelessWidget {
diff --git a/das_client/lib/app/pages/journey/journey_page.dart b/das_client/lib/app/pages/journey/journey_page.dart
index f7c4deb6..f5550acb 100644
--- a/das_client/lib/app/pages/journey/journey_page.dart
+++ b/das_client/lib/app/pages/journey/journey_page.dart
@@ -7,7 +7,7 @@ import 'package:das_client/app/pages/journey/train_journey/train_journey_overvie
import 'package:das_client/app/pages/journey/train_selection/train_selection.dart';
import 'package:das_client/auth/authentication_component.dart';
import 'package:das_client/di.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
diff --git a/das_client/lib/app/pages/journey/train_journey/train_journey_overview.dart b/das_client/lib/app/pages/journey/train_journey/train_journey_overview.dart
index 2ec6d6bb..23ae9000 100644
--- a/das_client/lib/app/pages/journey/train_journey/train_journey_overview.dart
+++ b/das_client/lib/app/pages/journey/train_journey/train_journey_overview.dart
@@ -1,7 +1,7 @@
import 'package:das_client/app/pages/journey/train_journey/widgets/header/adl_notification.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/header/header.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/train_journey.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
// TODO: handle extraLarge font sizes (diff to figma) globally.
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/break_series_selection.dart b/das_client/lib/app/pages/journey/train_journey/widgets/break_series_selection.dart
new file mode 100644
index 00000000..aff897ce
--- /dev/null
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/break_series_selection.dart
@@ -0,0 +1,115 @@
+import 'package:auto_route/auto_route.dart';
+import 'package:das_client/app/i18n/i18n.dart';
+import 'package:das_client/app/pages/journey/train_journey/widgets/break_series_selection_button.dart';
+import 'package:das_client/model/journey/break_series.dart';
+import 'package:das_client/model/journey/train_series.dart';
+import 'package:flutter/material.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
+
+class BreakSeriesSelection extends StatefulWidget {
+ const BreakSeriesSelection({required this.availableBreakSeries, required this.selectedBreakSeries, super.key});
+
+ final Set availableBreakSeries;
+ final BreakSeries? selectedBreakSeries;
+
+ @override
+ State createState() => _BreakSeriesSelectionState();
+}
+
+class _BreakSeriesSelectionState extends State {
+ BreakSeries? selectedBreakSeries;
+
+ @override
+ void initState() {
+ selectedBreakSeries = widget.selectedBreakSeries;
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (widget.availableBreakSeries.isEmpty) {
+ return SizedBox(
+ height: 100,
+ child: Center(
+ child: Text(context.l10n.p_train_journey_break_series_empty),
+ ),
+ );
+ }
+
+ final canConfirm = selectedBreakSeries != null && selectedBreakSeries != widget.selectedBreakSeries;
+
+ return Padding(
+ padding: const EdgeInsets.fromLTRB(sbbDefaultSpacing, 0, sbbDefaultSpacing, 21),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(sbbDefaultSpacing, 0, sbbDefaultSpacing, 0),
+ child: SizedBox(
+ width: double.infinity,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: _rows(context),
+ ),
+ ),
+ ),
+ ),
+ SizedBox(height: 46),
+ Padding(
+ padding: const EdgeInsets.fromLTRB(sbbDefaultSpacing, 0, sbbDefaultSpacing, 0),
+ child: SBBPrimaryButton(
+ label: context.l10n.c_button_confirm,
+ onPressed: canConfirm ? () => context.router.maybePop(selectedBreakSeries) : null),
+ )
+ ],
+ ),
+ );
+ }
+
+ List _rows(BuildContext context) {
+ return widget.availableBreakSeries
+ .map((it) => it.trainSeries)
+ .toSet()
+ .map((it) => _trainSeriesRows(context, it))
+ .expand((it) => it)
+ .toList();
+ }
+
+ List _trainSeriesRows(BuildContext context, TrainSeries trainSeries) {
+ final breakSeries = widget.availableBreakSeries.where((it) => it.trainSeries == trainSeries).toList();
+ breakSeries.sort((a, b) => a.breakSeries.compareTo(b.breakSeries));
+
+ return [
+ Padding(
+ padding: const EdgeInsets.fromLTRB(0, sbbDefaultSpacing, 0, sbbDefaultSpacing),
+ child: Text(
+ trainSeries.name,
+ style: SBBTextStyles.mediumBold,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.fromLTRB(0, 0, 0, sbbDefaultSpacing),
+ child: Wrap(
+ spacing: sbbDefaultSpacing * 0.75,
+ runSpacing: sbbDefaultSpacing,
+ children: List.generate(
+ breakSeries.length,
+ (index) {
+ final breakSerie = breakSeries[index];
+ return BreakSeriesSelectionButton(
+ label: breakSerie.toString(),
+ currentlySelected: breakSerie == selectedBreakSeries,
+ onTap: () {
+ setState(() {
+ selectedBreakSeries = breakSerie;
+ });
+ });
+ },
+ ),
+ ),
+ ),
+ ];
+ }
+}
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/break_series_selection_button.dart b/das_client/lib/app/pages/journey/train_journey/widgets/break_series_selection_button.dart
new file mode 100644
index 00000000..45425ba2
--- /dev/null
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/break_series_selection_button.dart
@@ -0,0 +1,40 @@
+import 'package:das_client/app/widgets/assets.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
+
+class BreakSeriesSelectionButton extends StatelessWidget {
+ const BreakSeriesSelectionButton(
+ {required this.label, required this.currentlySelected, required this.onTap, super.key});
+
+ final String label;
+ final bool currentlySelected;
+ final GestureTapCallback onTap;
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: onTap,
+ child: Stack(
+ clipBehavior: Clip.none,
+ children: [
+ Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ color: currentlySelected ? SBBColors.granite : SBBColors.cloud,
+ ),
+ width: 72,
+ height: 48,
+ child: Center(
+ child: Text(
+ label,
+ style: SBBTextStyles.mediumBold.copyWith(color: currentlySelected ? SBBColors.white : SBBColors.black),
+ ),
+ ),
+ ),
+ if (currentlySelected) Positioned(top: -6, right: -6, child: SvgPicture.asset(AppAssets.iconIndicatorChecked))
+ ],
+ ),
+ );
+ }
+}
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/header/adl_notification.dart b/das_client/lib/app/pages/journey/train_journey/widgets/header/adl_notification.dart
index 53c097f9..df382bac 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/header/adl_notification.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/header/adl_notification.dart
@@ -1,5 +1,5 @@
import 'package:das_client/app/i18n/i18n.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class ADLNotification extends StatelessWidget {
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/header/departure_authorization.dart b/das_client/lib/app/pages/journey/train_journey/widgets/header/departure_authorization.dart
index 0b766889..e5eafbd0 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/header/departure_authorization.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/header/departure_authorization.dart
@@ -1,4 +1,4 @@
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class DepartureAuthorization extends StatelessWidget {
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/header/header.dart b/das_client/lib/app/pages/journey/train_journey/widgets/header/header.dart
index b1629fcb..256e61fb 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/header/header.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/header/header.dart
@@ -1,6 +1,6 @@
import 'package:das_client/app/pages/journey/train_journey/widgets/header/main_container.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/header/time_container.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class Header extends StatelessWidget {
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/header/main_container.dart b/das_client/lib/app/pages/journey/train_journey/widgets/header/main_container.dart
index 9e79eaf8..1390e46a 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/header/main_container.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/header/main_container.dart
@@ -4,7 +4,7 @@ import 'package:das_client/app/pages/journey/train_journey/widgets/header/depart
import 'package:das_client/app/pages/journey/train_journey/widgets/header/radio_channel.dart';
import 'package:das_client/app/widgets/assets.dart';
import 'package:das_client/app/widgets/widget_extensions.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:das_client/model/journey/journey.dart';
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/header/radio_channel.dart b/das_client/lib/app/pages/journey/train_journey/widgets/header/radio_channel.dart
index c2f959e3..89f00d47 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/header/radio_channel.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/header/radio_channel.dart
@@ -1,4 +1,4 @@
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class RadioChannel extends StatelessWidget {
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/header/time_container.dart b/das_client/lib/app/pages/journey/train_journey/widgets/header/time_container.dart
index 131faa7a..20c2caaf 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/header/time_container.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/header/time_container.dart
@@ -1,4 +1,4 @@
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class TimeContainer extends StatelessWidget {
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart
index cab347ef..039ca951 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart
@@ -3,9 +3,9 @@ import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_ro
import 'package:das_client/app/widgets/assets.dart';
import 'package:das_client/app/widgets/table/das_table_cell.dart';
import 'package:das_client/model/journey/additional_speed_restriction_data.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
class AdditionalSpeedRestrictionRow extends BaseRowBuilder {
static const Key additionalSpeedRestrictionIconKey = Key('addition_speed_restriction_icon_key');
@@ -15,6 +15,7 @@ class AdditionalSpeedRestrictionRow extends BaseRowBuilder extends DASTableRowBuilder {
const BaseRowBuilder({
required this.metadata,
required this.data,
+ required this.settings,
super.height = rowHeight,
this.defaultAlignment = Alignment.bottomCenter,
this.rowColor,
@@ -22,6 +24,7 @@ class BaseRowBuilder extends DASTableRowBuilder {
final Color? rowColor;
final Metadata metadata;
final T data;
+ final TrainJourneySettings settings;
@override
DASTableRow build(BuildContext context) {
@@ -84,14 +87,7 @@ class BaseRowBuilder extends DASTableRowBuilder {
}
DASTableCell graduatedSpeedCell(BuildContext context) {
- if (data.speedData == null) {
- return DASTableCell.empty();
- }
-
- return DASTableCell(
- child: Text(data.speedData!.resolvedSpeed(metadata.trainSeries, metadata.breakSeries) ?? ''),
- alignment: Alignment.center,
- );
+ return DASTableCell.empty();
}
DASTableCell advisedSpeedCell(BuildContext context) {
@@ -99,7 +95,17 @@ class BaseRowBuilder extends DASTableRowBuilder {
}
DASTableCell brakedWeightSpeedCell(BuildContext context) {
- return DASTableCell.empty();
+ if (data.speedData == null) {
+ return DASTableCell.empty();
+ }
+
+ final currentTrainSeries = settings.selectedBreakSeries?.trainSeries ?? metadata.breakSeries?.trainSeries;
+ final currentBreakSeries = settings.selectedBreakSeries?.breakSeries ?? metadata.breakSeries?.breakSeries;
+
+ return DASTableCell(
+ child: Text(data.speedData!.resolvedSpeed(currentTrainSeries, currentBreakSeries) ?? 'XX'),
+ alignment: Alignment.center,
+ );
}
// TODO: clarify use of different icon cells and set appropriate name
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/cab_signaling_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/cab_signaling_row.dart
index 2a407c32..1600b78d 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/cab_signaling_row.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/cab_signaling_row.dart
@@ -12,6 +12,7 @@ class CABSignalingRow extends BaseRowBuilder {
CABSignalingRow({
required super.metadata,
required super.data,
+ required super.settings,
});
@override
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/bracket_station_body.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/bracket_station_body.dart
index 78ca13d8..8ce5df70 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/bracket_station_body.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/bracket_station_body.dart
@@ -1,5 +1,5 @@
import 'package:das_client/model/journey/bracket_station.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class BracketStationBody extends StatelessWidget {
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/route_cell_body.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/route_cell_body.dart
index f98fd752..d8df1811 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/route_cell_body.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/route_cell_body.dart
@@ -1,5 +1,5 @@
import 'package:das_client/app/widgets/table/das_table_theme.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class RouteCellBody extends StatelessWidget {
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/connection_track_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/connection_track_row.dart
index b0df0a83..55213881 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/connection_track_row.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/connection_track_row.dart
@@ -8,6 +8,7 @@ class ConnectionTrackRow extends BaseRowBuilder {
ConnectionTrackRow({
required super.metadata,
required super.data,
+ required super.settings,
});
@override
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/curve_point_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/curve_point_row.dart
index 75eb904f..3ce40ac0 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/curve_point_row.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/curve_point_row.dart
@@ -12,6 +12,7 @@ class CurvePointRow extends BaseRowBuilder {
CurvePointRow({
required super.metadata,
required super.data,
+ required super.settings,
});
@override
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/protection_section_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/protection_section_row.dart
index 21387a1d..3e787341 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/protection_section_row.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/protection_section_row.dart
@@ -3,7 +3,7 @@ import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_ro
import 'package:das_client/app/widgets/assets.dart';
import 'package:das_client/app/widgets/table/das_table_cell.dart';
import 'package:das_client/model/journey/protection_section.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -14,6 +14,7 @@ class ProtectionSectionRow extends BaseRowBuilder {
ProtectionSectionRow({
required super.metadata,
required super.data,
+ required super.settings,
super.height = rowHeight,
}) : super(rowColor: SBBColors.peach);
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/service_point_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/service_point_row.dart
index 9ae98616..00997af5 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/service_point_row.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/service_point_row.dart
@@ -4,7 +4,7 @@ import 'package:das_client/app/pages/journey/train_journey/widgets/table/cells/r
import 'package:das_client/app/widgets/assets.dart';
import 'package:das_client/app/widgets/table/das_table_cell.dart';
import 'package:das_client/model/journey/service_point.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -16,6 +16,7 @@ class ServicePointRow extends BaseRowBuilder {
ServicePointRow({
required super.metadata,
required super.data,
+ required super.settings,
super.height = rowHeight,
}) : super(rowColor: metadata.nextStop == data ? SBBColors.royal.withOpacity(0.2) : Colors.transparent);
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/signal_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/signal_row.dart
index e0de615f..03a993e6 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/signal_row.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/signal_row.dart
@@ -12,6 +12,7 @@ class SignalRow extends BaseRowBuilder {
SignalRow({
required super.metadata,
required super.data,
+ required super.settings,
});
@override
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/speed_change_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/speed_change_row.dart
index 141cba44..65b0f512 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/speed_change_row.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/speed_change_row.dart
@@ -7,6 +7,7 @@ class SpeedChangeRow extends BaseRowBuilder {
SpeedChangeRow({
required super.metadata,
required super.data,
+ required super.settings,
});
@override
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/train_journey.dart b/das_client/lib/app/pages/journey/train_journey/widgets/train_journey.dart
index c6d94c3c..c7c4ee5d 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/train_journey.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/train_journey.dart
@@ -1,8 +1,10 @@
import 'package:das_client/app/bloc/train_journey_cubit.dart';
import 'package:das_client/app/i18n/i18n.dart';
+import 'package:das_client/app/model/train_journey_settings.dart';
+import 'package:das_client/app/pages/journey/train_journey/widgets/break_series_selection.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart';
-import 'package:das_client/app/pages/journey/train_journey/widgets/table/connection_track_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/cab_signaling_row.dart';
+import 'package:das_client/app/pages/journey/train_journey/widgets/table/connection_track_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/curve_point_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/protection_section_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/service_point_row.dart';
@@ -12,8 +14,9 @@ import 'package:das_client/app/widgets/table/das_table.dart';
import 'package:das_client/app/widgets/table/das_table_column.dart';
import 'package:das_client/app/widgets/table/das_table_row.dart';
import 'package:das_client/model/journey/additional_speed_restriction_data.dart';
-import 'package:das_client/model/journey/connection_track.dart';
+import 'package:das_client/model/journey/break_series.dart';
import 'package:das_client/model/journey/cab_signaling.dart';
+import 'package:das_client/model/journey/connection_track.dart';
import 'package:das_client/model/journey/curve_point.dart';
import 'package:das_client/model/journey/datatype.dart';
import 'package:das_client/model/journey/journey.dart';
@@ -21,70 +24,83 @@ import 'package:das_client/model/journey/protection_section.dart';
import 'package:das_client/model/journey/service_point.dart';
import 'package:das_client/model/journey/signal.dart';
import 'package:das_client/model/journey/speed_change.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:flutter/material.dart';
+import 'package:rxdart/rxdart.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
class TrainJourney extends StatelessWidget {
const TrainJourney({super.key});
+ static const Key breakingSeriesHeaderKey = Key('breaking_series_header_key');
+
@override
Widget build(BuildContext context) {
final bloc = context.trainJourneyCubit;
- return StreamBuilder(
- stream: bloc.journeyStream,
+ return StreamBuilder>(
+ stream: CombineLatestStream.list([bloc.journeyStream, bloc.settingsStream]),
builder: (context, snapshot) {
- final Journey? journey = snapshot.data;
- if (journey == null) {
+ if (!snapshot.hasData || snapshot.data?[0] == null) {
return Container();
}
- return _body(context, journey);
+ final journey = snapshot.data![0] as Journey;
+ final settings = snapshot.data![1] as TrainJourneySettings;
+
+ return _body(context, journey, settings);
},
);
}
- Widget _body(BuildContext context, Journey journey) {
+ Widget _body(BuildContext context, Journey journey, TrainJourneySettings settings) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: sbbDefaultSpacing * 0.5),
child: DASTable(
- columns: _columns(context),
- rows: _rows(context, journey),
+ columns: _columns(context, journey, settings),
+ rows: _rows(context, journey, settings),
),
);
}
- List _rows(BuildContext context, Journey journey) {
+ List _rows(BuildContext context, Journey journey, TrainJourneySettings settings) {
return List.generate(journey.data.length, (index) {
final rowData = journey.data[index];
switch (rowData.type) {
case Datatype.servicePoint:
- return ServicePointRow(metadata: journey.metadata, data: rowData as ServicePoint).build(context);
+ return ServicePointRow(metadata: journey.metadata, data: rowData as ServicePoint, settings: settings)
+ .build(context);
case Datatype.protectionSection:
- return ProtectionSectionRow(metadata: journey.metadata, data: rowData as ProtectionSection).build(context);
+ return ProtectionSectionRow(
+ metadata: journey.metadata, data: rowData as ProtectionSection, settings: settings)
+ .build(context);
case Datatype.curvePoint:
- return CurvePointRow(metadata: journey.metadata, data: rowData as CurvePoint).build(context);
+ return CurvePointRow(metadata: journey.metadata, data: rowData as CurvePoint, settings: settings)
+ .build(context);
case Datatype.signal:
- return SignalRow(metadata: journey.metadata, data: rowData as Signal).build(context);
+ return SignalRow(metadata: journey.metadata, data: rowData as Signal, settings: settings).build(context);
case Datatype.additionalSpeedRestriction:
return AdditionalSpeedRestrictionRow(
- metadata: journey.metadata, data: rowData as AdditionalSpeedRestrictionData)
+ metadata: journey.metadata, data: rowData as AdditionalSpeedRestrictionData, settings: settings)
.build(context);
case Datatype.connectionTrack:
- return ConnectionTrackRow(metadata: journey.metadata, data: rowData as ConnectionTrack).build(context);
+ return ConnectionTrackRow(metadata: journey.metadata, data: rowData as ConnectionTrack, settings: settings)
+ .build(context);
case Datatype.speedChange:
- return SpeedChangeRow(metadata: journey.metadata, data: rowData as SpeedChange).build(context);
+ return SpeedChangeRow(metadata: journey.metadata, data: rowData as SpeedChange, settings: settings)
+ .build(context);
case Datatype.cabSignaling:
- return CABSignalingRow(
- metadata: journey.metadata,
- data: rowData as CABSignaling,
- ).build(context);
+ return CABSignalingRow(metadata: journey.metadata, data: rowData as CABSignaling, settings: settings)
+ .build(context);
}
});
}
- List _columns(BuildContext context) {
+ List _columns(BuildContext context, Journey journey, TrainJourneySettings settings) {
+ final speedLabel = settings.selectedBreakSeries != null
+ ? '${settings.selectedBreakSeries!.trainSeries.name}${settings.selectedBreakSeries!.breakSeries}'
+ : '${journey.metadata.breakSeries?.trainSeries.name ?? '?'}${journey.metadata.breakSeries?.breakSeries ?? '?'}';
+
return [
DASTableColumn(child: Text(context.l10n.p_train_journey_table_kilometre_label), width: 64.0),
DASTableColumn(child: Text(context.l10n.p_train_journey_table_time_label), width: 100.0),
@@ -105,9 +121,31 @@ class TrainJourney extends StatelessWidget {
end: BorderSide(color: SBBColors.cloud, width: 2.0),
),
),
- DASTableColumn(child: Text(context.l10n.p_train_journey_table_braked_weight_speed_label), width: 62.0),
+ // TODO: find out what to do when break series is not defined
+ DASTableColumn(
+ child: Text(speedLabel),
+ width: 62.0,
+ onTap: () => _onBreakSeriesTap(context, journey, settings),
+ headerKey: breakingSeriesHeaderKey),
DASTableColumn(child: Text(context.l10n.p_train_journey_table_advised_speed_label), width: 62.0),
DASTableColumn(width: 40.0), // actions
];
}
+
+ void _onBreakSeriesTap(BuildContext context, Journey journey, TrainJourneySettings settings) {
+ final trainJourneyCubit = context.trainJourneyCubit;
+
+ showSBBModalSheet(
+ context: context,
+ title: context.l10n.p_train_journey_break_series,
+ constraints: BoxConstraints(),
+ child: BreakSeriesSelection(
+ availableBreakSeries: journey.metadata.availableBreakSeries,
+ selectedBreakSeries: settings.selectedBreakSeries ?? journey.metadata.breakSeries))
+ .then(
+ (newValue) => {
+ if (newValue != null) {trainJourneyCubit.updateBreakSeries(newValue)}
+ },
+ );
+ }
}
diff --git a/das_client/lib/app/pages/journey/train_selection/train_selection.dart b/das_client/lib/app/pages/journey/train_selection/train_selection.dart
index 1e6a0361..4bb54aa2 100644
--- a/das_client/lib/app/pages/journey/train_selection/train_selection.dart
+++ b/das_client/lib/app/pages/journey/train_selection/train_selection.dart
@@ -4,7 +4,7 @@ import 'package:das_client/app/model/ru.dart';
import 'package:das_client/app/widgets/header.dart';
import 'package:das_client/util/error_code.dart';
import 'package:das_client/util/format.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -115,7 +115,7 @@ class _TrainSelectionState extends State {
return Padding(
padding: const EdgeInsets.symmetric(vertical: sbbDefaultSpacing, horizontal: sbbDefaultSpacing / 2),
child: SBBPrimaryButton(
- label: context.l10n.p_train_selection_load,
+ label: context.l10n.c_button_confirm,
onPressed: _canContinue(state) ? () => context.trainJourneyCubit.loadTrainJourney() : null,
),
);
diff --git a/das_client/lib/app/pages/links/links_page.dart b/das_client/lib/app/pages/links/links_page.dart
index dc678125..ffe96a05 100644
--- a/das_client/lib/app/pages/links/links_page.dart
+++ b/das_client/lib/app/pages/links/links_page.dart
@@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/nav/das_navigation_drawer.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
@RoutePage()
diff --git a/das_client/lib/app/pages/login/login_page.dart b/das_client/lib/app/pages/login/login_page.dart
index e9ab8012..6e63e0b7 100644
--- a/das_client/lib/app/pages/login/login_page.dart
+++ b/das_client/lib/app/pages/login/login_page.dart
@@ -4,7 +4,7 @@ import 'package:das_client/di.dart';
import 'package:das_client/flavor.dart';
import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/nav/app_router.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:fimber/fimber.dart';
import 'package:flutter/material.dart';
diff --git a/das_client/lib/app/pages/profile/profile_page.dart b/das_client/lib/app/pages/profile/profile_page.dart
index c1e1e668..8f52cda8 100644
--- a/das_client/lib/app/pages/profile/profile_page.dart
+++ b/das_client/lib/app/pages/profile/profile_page.dart
@@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/nav/das_navigation_drawer.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
@RoutePage()
diff --git a/das_client/lib/app/pages/settings/settings_page.dart b/das_client/lib/app/pages/settings/settings_page.dart
index 6e3b6438..68f1610f 100644
--- a/das_client/lib/app/pages/settings/settings_page.dart
+++ b/das_client/lib/app/pages/settings/settings_page.dart
@@ -1,7 +1,7 @@
import 'package:auto_route/auto_route.dart';
import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/nav/das_navigation_drawer.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
@RoutePage()
diff --git a/das_client/lib/app/widgets/app_version_text.dart b/das_client/lib/app/widgets/app_version_text.dart
index 802e361a..139670f1 100644
--- a/das_client/lib/app/widgets/app_version_text.dart
+++ b/das_client/lib/app/widgets/app_version_text.dart
@@ -1,4 +1,4 @@
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
diff --git a/das_client/lib/app/widgets/assets.dart b/das_client/lib/app/widgets/assets.dart
index ab95aca0..5f6020b1 100644
--- a/das_client/lib/app/widgets/assets.dart
+++ b/das_client/lib/app/widgets/assets.dart
@@ -15,4 +15,5 @@ class AppAssets {
static const iconSignalLaneChange = '$_iconsDir/icon_signal_line_change.svg';
static const iconCabStart = '$_iconsDir/icon_cab_start.svg';
static const iconCabEnd = '$_iconsDir/icon_cab_end.svg';
+ static const iconIndicatorChecked = '$_iconsDir/icon_indicator_checked.svg';
}
diff --git a/das_client/lib/app/widgets/device_id_text.dart b/das_client/lib/app/widgets/device_id_text.dart
index cb959d57..8b417bb3 100644
--- a/das_client/lib/app/widgets/device_id_text.dart
+++ b/das_client/lib/app/widgets/device_id_text.dart
@@ -1,5 +1,5 @@
import 'package:das_client/util/device_id_info.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class DeviceIdText extends StatelessWidget {
diff --git a/das_client/lib/app/widgets/header.dart b/das_client/lib/app/widgets/header.dart
index fa60d162..b4f4dbc8 100644
--- a/das_client/lib/app/widgets/header.dart
+++ b/das_client/lib/app/widgets/header.dart
@@ -1,4 +1,4 @@
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
class Header extends StatelessWidget {
diff --git a/das_client/lib/app/widgets/table/das_table.dart b/das_client/lib/app/widgets/table/das_table.dart
index c00244b4..0388bfd2 100644
--- a/das_client/lib/app/widgets/table/das_table.dart
+++ b/das_client/lib/app/widgets/table/das_table.dart
@@ -5,7 +5,7 @@ import 'package:das_client/app/widgets/table/das_table_cell.dart';
import 'package:das_client/app/widgets/table/das_table_column.dart';
import 'package:das_client/app/widgets/table/das_table_row.dart';
import 'package:das_client/app/widgets/table/das_table_theme.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
/// [DASTable] provides a the basic structure for a train journey table.
@@ -107,10 +107,11 @@ class DASTable extends StatelessWidget {
Widget _headerCell(DASTableColumn column) {
return Builder(builder: (context) {
final tableThemeData = DASTableTheme.of(context)?.data;
- return _TableCellWrapper(
+ final headerCell = _TableCellWrapper(
expanded: column.expanded,
width: column.width,
child: Container(
+ key: column.headerKey,
decoration: BoxDecoration(
border: tableThemeData?.headingRowBorder ?? column.border,
color: column.color ?? tableThemeData?.headingRowColor,
@@ -126,6 +127,12 @@ class DASTable extends StatelessWidget {
),
),
);
+ return column.onTap != null
+ ? GestureDetector(
+ onTap: column.onTap,
+ child: headerCell,
+ )
+ : headerCell;
});
}
diff --git a/das_client/lib/app/widgets/table/das_table_column.dart b/das_client/lib/app/widgets/table/das_table_column.dart
index 0af7e041..729acdf9 100644
--- a/das_client/lib/app/widgets/table/das_table_column.dart
+++ b/das_client/lib/app/widgets/table/das_table_column.dart
@@ -1,5 +1,5 @@
import 'package:das_client/app/widgets/table/das_table_cell.dart';
-import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
/// Represents a column in the [DASTable] with optional styling and width constraints.
@@ -17,6 +17,8 @@ class DASTableColumn {
this.width,
this.hidden = false,
this.alignment = Alignment.center,
+ this.onTap,
+ this.headerKey,
}) : assert((width != null && width > 0) || expanded);
/// The content of the column header as a widget.
@@ -42,5 +44,11 @@ class DASTableColumn {
/// Whether the column is visible or not.
final bool hidden;
+ /// Callback for tap events on the column header.
+ final GestureTapCallback? onTap;
+
+ /// Key for the header cell
+ final Key? headerKey;
+
get isVisible => !hidden;
}
diff --git a/das_client/lib/model/journey/break_series.dart b/das_client/lib/model/journey/break_series.dart
new file mode 100644
index 00000000..13536302
--- /dev/null
+++ b/das_client/lib/model/journey/break_series.dart
@@ -0,0 +1,27 @@
+import 'package:das_client/model/journey/train_series.dart';
+
+class BreakSeries {
+ BreakSeries({
+ required this.trainSeries,
+ required this.breakSeries,
+ });
+
+ final TrainSeries trainSeries;
+ final int breakSeries;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is BreakSeries &&
+ runtimeType == other.runtimeType &&
+ trainSeries == other.trainSeries &&
+ breakSeries == other.breakSeries;
+
+ @override
+ int get hashCode => trainSeries.hashCode ^ breakSeries.hashCode;
+
+ @override
+ String toString() {
+ return '${trainSeries.name}$breakSeries';
+ }
+}
diff --git a/das_client/lib/model/journey/curve_point.dart b/das_client/lib/model/journey/curve_point.dart
index aa6d9b0f..de4f7dcd 100644
--- a/das_client/lib/model/journey/curve_point.dart
+++ b/das_client/lib/model/journey/curve_point.dart
@@ -5,18 +5,21 @@ class CurvePoint extends BaseData {
CurvePoint({
required super.order,
required super.kilometre,
+ super.speedData,
this.curvePointType,
this.curveType,
+ this.text,
this.comment,
}) : super(type: Datatype.curvePoint);
final CurvePointType? curvePointType;
final CurveType? curveType;
+ final String? text;
final String? comment;
@override
String toString() {
- return "CurvePoint(order: $order, kilometre: $kilometre, curvePointType: $curvePointType, curveType: $curveType, comment: '$comment')";
+ return "CurvePoint(order: $order, kilometre: $kilometre, curvePointType: $curvePointType, curveType: $curveType, text: '$text', comment: '$comment')";
}
}
diff --git a/das_client/lib/model/journey/metadata.dart b/das_client/lib/model/journey/metadata.dart
index 09c7eab7..a4be5262 100644
--- a/das_client/lib/model/journey/metadata.dart
+++ b/das_client/lib/model/journey/metadata.dart
@@ -1,8 +1,8 @@
import 'package:das_client/model/journey/additional_speed_restriction.dart';
import 'package:das_client/model/journey/base_data.dart';
+import 'package:das_client/model/journey/break_series.dart';
import 'package:das_client/model/journey/service_point.dart';
import 'package:das_client/model/journey/track_equipment.dart';
-import 'package:das_client/model/journey/train_series.dart';
class Metadata {
Metadata({
@@ -10,10 +10,10 @@ class Metadata {
this.currentPosition,
this.routeStart,
this.routeEnd,
- this.trainSeries = TrainSeries.R,
- this.breakSeries = 150,
+ this.breakSeries,
this.additionalSpeedRestrictions = const [],
this.nonStandardTrackEquipmentSegments = const [],
+ this.availableBreakSeries = const {},
});
final ServicePoint? nextStop;
@@ -22,6 +22,6 @@ class Metadata {
final BaseData? routeStart;
final BaseData? routeEnd;
final List nonStandardTrackEquipmentSegments;
- final TrainSeries trainSeries;
- final int breakSeries;
+ final BreakSeries? breakSeries;
+ final Set availableBreakSeries;
}
diff --git a/das_client/lib/model/journey/service_point.dart b/das_client/lib/model/journey/service_point.dart
index 06e27a17..67f20859 100644
--- a/das_client/lib/model/journey/service_point.dart
+++ b/das_client/lib/model/journey/service_point.dart
@@ -11,6 +11,7 @@ class ServicePoint extends BaseData {
required this.isStation,
required super.order,
required super.kilometre,
+ super.speedData,
this.bracketStation,
}) : super(type: Datatype.servicePoint);
@@ -22,6 +23,6 @@ class ServicePoint extends BaseData {
@override
String toString() {
- return 'ServicePoint(order: $order, kilometre: $kilometre, name: $name, mandatoryStop: $mandatoryStop, isStop: $isStop, isStation: $isStation, bracketStation: $bracketStation)';
+ return 'ServicePoint(order: $order, kilometre: $kilometre, name: $name, mandatoryStop: $mandatoryStop, isStop: $isStop, isStation: $isStation, bracketStation: $bracketStation, speedData: $speedData)';
}
}
diff --git a/das_client/lib/model/journey/speed_data.dart b/das_client/lib/model/journey/speed_data.dart
index a47d9a56..9d37436d 100644
--- a/das_client/lib/model/journey/speed_data.dart
+++ b/das_client/lib/model/journey/speed_data.dart
@@ -7,7 +7,9 @@ class SpeedData {
final List velocities;
- String? resolvedSpeed(TrainSeries trainSeries, int breakSeries) {
+ String? resolvedSpeed(TrainSeries? trainSeries, int? breakSeries) {
+ if (trainSeries == null) return null;
+
final trainSeriesVelocities = velocities.where((it) => it.trainSeries == trainSeries);
final exactMatchingVelocity = trainSeriesVelocities.firstWhereOrNull((it) => it.breakSeries == breakSeries);
return exactMatchingVelocity?.speed ??
diff --git a/das_client/lib/model/journey/train_series.dart b/das_client/lib/model/journey/train_series.dart
index 80eba9f7..160646fb 100644
--- a/das_client/lib/model/journey/train_series.dart
+++ b/das_client/lib/model/journey/train_series.dart
@@ -1,3 +1,5 @@
+import 'package:collection/collection.dart';
+
enum TrainSeries {
A,
D,
@@ -12,4 +14,10 @@ enum TrainSeries {
(e) => e.name.toLowerCase() == value.toLowerCase(),
);
}
+
+ static TrainSeries? fromOptional(String? value) {
+ return values.firstWhereOrNull(
+ (e) => e.name.toLowerCase() == value?.toLowerCase(),
+ );
+ }
}
diff --git a/das_client/lib/mqtt/src/mqtt_service_impl.dart b/das_client/lib/mqtt/src/mqtt_service_impl.dart
index 79fa3578..3e571536 100644
--- a/das_client/lib/mqtt/src/mqtt_service_impl.dart
+++ b/das_client/lib/mqtt/src/mqtt_service_impl.dart
@@ -50,6 +50,7 @@ class MqttServiceImpl implements MqttService {
}
if (await _mqttClientConnector.connect(_client, company, train)) {
_client.subscribe('${prefix}90940/2/event/$company/$train', MqttQos.exactlyOnce);
+ _client.subscribe('${prefix}90940/2/event/$company/$train/$_deviceId', MqttQos.exactlyOnce);
_client.subscribe('${prefix}90940/2/G2B/$company/$train/$_deviceId', MqttQos.exactlyOnce);
Fimber.i("Subscribed to topic with prefix='$prefix'...");
_startUpdateListener();
diff --git a/das_client/lib/sfera/src/db/train_characteristics_entity.dart b/das_client/lib/sfera/src/db/train_characteristics_entity.dart
new file mode 100644
index 00000000..dbc99b54
--- /dev/null
+++ b/das_client/lib/sfera/src/db/train_characteristics_entity.dart
@@ -0,0 +1,36 @@
+import 'package:das_client/sfera/src/model/train_characteristics.dart';
+import 'package:das_client/sfera/src/sfera_reply_parser.dart';
+import 'package:isar/isar.dart';
+
+part 'train_characteristics_entity.g.dart';
+
+@Collection(accessor: 'trainCharacteristics')
+class TrainCharacteristicsEntity {
+ TrainCharacteristicsEntity(
+ {required this.id,
+ required this.tcId,
+ required this.majorVersion,
+ required this.minorVersion,
+ required this.xmlData});
+
+ final int id;
+ final String tcId;
+ final String majorVersion;
+ final String minorVersion;
+ final String xmlData;
+
+ TrainCharacteristics toDomain() {
+ return SferaReplyParser.parse(xmlData);
+ }
+}
+
+extension TrainCharacteristicsMapperX on TrainCharacteristics {
+ TrainCharacteristicsEntity toEntity({required int isarId}) {
+ return TrainCharacteristicsEntity(
+ id: isarId,
+ tcId: tcId,
+ majorVersion: versionMajor,
+ minorVersion: versionMinor,
+ xmlData: buildDocument().toString());
+ }
+}
diff --git a/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart b/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart
index 1284faec..d65e1941 100644
--- a/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart
+++ b/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart
@@ -3,6 +3,7 @@ import 'package:das_client/model/journey/additional_speed_restriction.dart';
import 'package:das_client/model/journey/additional_speed_restriction_data.dart';
import 'package:das_client/model/journey/base_data.dart';
import 'package:das_client/model/journey/bracket_station.dart';
+import 'package:das_client/model/journey/break_series.dart';
import 'package:das_client/model/journey/cab_signaling.dart';
import 'package:das_client/model/journey/connection_track.dart';
import 'package:das_client/model/journey/curve_point.dart';
@@ -29,6 +30,7 @@ import 'package:das_client/sfera/src/model/network_specific_parameter.dart';
import 'package:das_client/sfera/src/model/segment_profile.dart';
import 'package:das_client/sfera/src/model/speeds.dart';
import 'package:das_client/sfera/src/model/taf_tap_location.dart';
+import 'package:das_client/sfera/src/model/train_characteristics.dart';
import 'package:fimber/fimber.dart';
class SferaModelMapper {
@@ -40,16 +42,18 @@ class SferaModelMapper {
static const String _protectionSectionNspFacultativeName = 'facultative';
static const String _protectionSectionNspLengthTypeName = 'lengthType';
- static Journey mapToJourney(JourneyProfile journeyProfile, List segmentProfiles) {
+ static Journey mapToJourney(JourneyProfile journeyProfile, List segmentProfiles,
+ List trainCharacteristics) {
try {
- return _mapToJourney(journeyProfile, segmentProfiles);
+ return _mapToJourney(journeyProfile, segmentProfiles, trainCharacteristics);
} catch (e, s) {
Fimber.e('Error mapping journey-/segment profiles to journey:', ex: e, stacktrace: s);
return Journey.invalid();
}
}
- static Journey _mapToJourney(JourneyProfile journeyProfile, List segmentProfiles) {
+ static Journey _mapToJourney(JourneyProfile journeyProfile, List segmentProfiles,
+ List trainCharacteristics) {
final journeyData = [];
final segmentProfilesLists = journeyProfile.segmentProfilesLists.toList();
@@ -75,7 +79,8 @@ class SferaModelMapper {
// Remove new line speeds that are already present as connection tracks
newLineSpeeds.removeWhere((speedChange) =>
- connectionTracks.firstWhereOrNull((connectionTrack) => connectionTrack.speedData == speedChange.speedData) != null);
+ connectionTracks.firstWhereOrNull((connectionTrack) => connectionTrack.speedData == speedChange.speedData) !=
+ null);
journeyData.addAll(connectionTracks);
journeyData.addAll(newLineSpeeds);
@@ -92,14 +97,14 @@ class SferaModelMapper {
.first;
journeyData.add(ServicePoint(
- name: _localizedStringFromMultilingualText(tafTapLocation.locationNames),
- order: calculateOrder(segmentIndex, timingPoint.location),
- mandatoryStop: tpConstraint.stoppingPointInformation?.stopType?.mandatoryStop ?? true,
- isStop: tpConstraint.stopSkipPass == StopSkipPass.stoppingPoint,
- isStation: tafTapLocation.locationType != TafTapLocationType.stoppingLocation,
- bracketStation: _parseBracketStation(tafTapLocations, tafTapLocation),
- kilometre: kilometreMap[timingPoint.location] ?? [],
- ));
+ name: _localizedStringFromMultilingualText(tafTapLocation.locationNames),
+ order: calculateOrder(segmentIndex, timingPoint.location),
+ mandatoryStop: tpConstraint.stoppingPointInformation?.stopType?.mandatoryStop ?? true,
+ isStop: tpConstraint.stopSkipPass == StopSkipPass.stoppingPoint,
+ isStation: tafTapLocation.locationType != TafTapLocationType.stoppingLocation,
+ bracketStation: _parseBracketStation(tafTapLocations, tafTapLocation),
+ kilometre: kilometreMap[timingPoint.location] ?? [],
+ speedData: _speedDataFromSpeeds(tafTapLocation.stationSpeed?.xmlStationSpeed.element)));
}
_parseAndAddProtectionSections(journeyData, segmentIndex, segmentProfile, kilometreMap);
@@ -123,6 +128,7 @@ class SferaModelMapper {
journeyData.sort();
+ final trainCharacteristic = _resolveFirstTrainCharacteristics(journeyProfile, trainCharacteristics);
final servicePoints = journeyData.where((it) => it.type == Datatype.servicePoint).toList();
return Journey(
metadata: Metadata(
@@ -132,6 +138,13 @@ class SferaModelMapper {
routeStart: journeyData.firstOrNull,
routeEnd: journeyData.lastOrNull,
nonStandardTrackEquipmentSegments: trackEquipmentSegments,
+ availableBreakSeries: _parseAvailableBreakSeries(journeyData),
+ breakSeries: trainCharacteristic?.tcFeatures.trainCategoryCode != null &&
+ trainCharacteristic?.tcFeatures.brakedWeightPercentage != null
+ ? BreakSeries(
+ trainSeries: trainCharacteristic!.tcFeatures.trainCategoryCode!,
+ breakSeries: trainCharacteristic.tcFeatures.brakedWeightPercentage!)
+ : null,
),
data: journeyData,
);
@@ -281,9 +294,8 @@ class SferaModelMapper {
static BracketStation? _parseBracketStation(List allLocations, TafTapLocation tafTapLocation) {
for (final tafTapLocationNsp in tafTapLocation.nsp) {
if (tafTapLocationNsp.name == _bracketStationNspName) {
- final mainStationNsp = tafTapLocationNsp.networkSpecificParameters
- .where((it) => it.name == _bracketStationMainStationNspName)
- .firstOrNull;
+ final mainStationNsp =
+ tafTapLocationNsp.parameters.where((it) => it.name == _bracketStationMainStationNspName).firstOrNull;
if (mainStationNsp == null) {
Fimber.w('Encountered bracket station without main station NSP declaration: $tafTapLocation');
} else {
@@ -312,12 +324,15 @@ class SferaModelMapper {
return curvePointsNsp.map((curvePointNsp) {
final curvePointTypeValue = curvePointNsp.parameters.withName('curvePointType')?.nspValue;
final curveTypeValue = curvePointNsp.parameters.withName('curveType')?.nspValue;
+ final curveSpeed = curvePointNsp.xmlCurveSpeed?.element;
return CurvePoint(
order: calculateOrder(segmentIndex, curvePointNsp.location),
kilometre: kilometreMap[curvePointNsp.location] ?? [],
curvePointType: curvePointTypeValue != null ? CurvePointType.from(curvePointTypeValue) : null,
curveType: curveTypeValue != null ? CurveType.from(curveTypeValue) : null,
- comment: curvePointNsp.parameters.withName('comment')?.nspValue,
+ text: curveSpeed?.text,
+ comment: curveSpeed?.comment,
+ speedData: _speedDataFromSpeeds(curveSpeed?.speeds),
);
}).toList();
}
@@ -358,14 +373,34 @@ class SferaModelMapper {
}).toList();
}
- static SpeedData _speedDataFromSpeeds(Speeds? speeds) {
- if (speeds == null) {
- return SpeedData();
- }
+ static SpeedData? _speedDataFromSpeeds(Speeds? speeds) {
+ if (speeds == null) return null;
return SpeedData(
velocities: speeds.velocities
.map((it) => Velocity(
trainSeries: it.trainSeries, reduced: it.reduced, speed: it.speed, breakSeries: it.brakeSeries))
.toList());
}
+
+ static Set _parseAvailableBreakSeries(List journeyData) {
+ return journeyData
+ .where((it) => it.speedData != null)
+ .map((it) => it.speedData!.velocities)
+ .expand((it) => it)
+ .where((it) => it.breakSeries != null)
+ .map((it) => BreakSeries(trainSeries: it.trainSeries, breakSeries: it.breakSeries!))
+ .toSet();
+ }
+
+ static TrainCharacteristics? _resolveFirstTrainCharacteristics(
+ JourneyProfile journey, List trainCharacteristics) {
+ final firstTrainRef = journey.trainCharactericsRefSet.firstOrNull;
+ if (firstTrainRef == null) return null;
+
+ return trainCharacteristics.firstWhereOrNull((it) =>
+ it.tcId == firstTrainRef.tcId &&
+ it.ruId == firstTrainRef.ruId &&
+ it.versionMajor == firstTrainRef.versionMajor &&
+ it.versionMinor == firstTrainRef.versionMinor);
+ }
}
diff --git a/das_client/lib/sfera/src/model/b2g_request.dart b/das_client/lib/sfera/src/model/b2g_request.dart
index 4b9e348a..75d4ecee 100644
--- a/das_client/lib/sfera/src/model/b2g_request.dart
+++ b/das_client/lib/sfera/src/model/b2g_request.dart
@@ -1,6 +1,7 @@
import 'package:das_client/sfera/src/model/jp_request.dart';
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
import 'package:das_client/sfera/src/model/sp_request.dart';
+import 'package:das_client/sfera/src/model/tc_request.dart';
class B2gRequest extends SferaXmlElement {
static const String elementType = 'B2G_Request';
@@ -15,9 +16,13 @@ class B2gRequest extends SferaXmlElement {
factory B2gRequest.createSPRequest(List spRequests) {
final request = B2gRequest();
- for (final spRequest in spRequests) {
- request.children.add(spRequest);
- }
+ request.children.addAll(spRequests);
+ return request;
+ }
+
+ factory B2gRequest.createTCRequest(List tcRequests) {
+ final request = B2gRequest();
+ request.children.addAll(tcRequests);
return request;
}
}
diff --git a/das_client/lib/sfera/src/model/curve_point_network_specific_point.dart b/das_client/lib/sfera/src/model/curve_point_network_specific_point.dart
new file mode 100644
index 00000000..0a763678
--- /dev/null
+++ b/das_client/lib/sfera/src/model/curve_point_network_specific_point.dart
@@ -0,0 +1,10 @@
+import 'package:das_client/sfera/src/model/network_specific_point.dart';
+import 'package:das_client/sfera/src/model/xml_curve_speed.dart';
+
+class CurvePointNetworkSpecificPoint extends NetworkSpecificPoint {
+ static const String elementName = 'curvePoint';
+
+ CurvePointNetworkSpecificPoint({super.type, super.attributes, super.children, super.value});
+
+ XmlCurveSpeed? get xmlCurveSpeed => parameters.whereType().firstOrNull;
+}
diff --git a/das_client/lib/sfera/src/model/curve_speed.dart b/das_client/lib/sfera/src/model/curve_speed.dart
new file mode 100644
index 00000000..45475909
--- /dev/null
+++ b/das_client/lib/sfera/src/model/curve_speed.dart
@@ -0,0 +1,14 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/speeds.dart';
+
+class CurveSpeed extends SferaXmlElement {
+ static const String elementType = 'curveSpeed';
+
+ CurveSpeed({super.type = elementType, super.attributes, super.children, super.value});
+
+ Speeds? get speeds => children.whereType().firstOrNull;
+
+ String? get text => attributes['text'];
+
+ String? get comment => attributes['comment'];
+}
diff --git a/das_client/lib/sfera/src/model/g2b_reply_payload.dart b/das_client/lib/sfera/src/model/g2b_reply_payload.dart
index 1d7ecdc9..584d8095 100644
--- a/das_client/lib/sfera/src/model/g2b_reply_payload.dart
+++ b/das_client/lib/sfera/src/model/g2b_reply_payload.dart
@@ -1,6 +1,7 @@
import 'package:das_client/sfera/src/model/journey_profile.dart';
import 'package:das_client/sfera/src/model/segment_profile.dart';
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/train_characteristics.dart';
class G2bReplyPayload extends SferaXmlElement {
static const String elementType = 'G2B_ReplyPayload';
@@ -10,4 +11,6 @@ class G2bReplyPayload extends SferaXmlElement {
Iterable get journeyProfiles => children.whereType();
Iterable get segmentProfiles => children.whereType();
+
+ Iterable get trainCharacteristics => children.whereType();
}
diff --git a/das_client/lib/sfera/src/model/journey_profile.dart b/das_client/lib/sfera/src/model/journey_profile.dart
index b363682a..c27b3aac 100644
--- a/das_client/lib/sfera/src/model/journey_profile.dart
+++ b/das_client/lib/sfera/src/model/journey_profile.dart
@@ -2,6 +2,7 @@ import 'package:das_client/sfera/src/model/enums/jp_status.dart';
import 'package:das_client/sfera/src/model/enums/xml_enum.dart';
import 'package:das_client/sfera/src/model/segment_profile_list.dart';
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/train_characteristics_ref.dart';
import 'package:das_client/sfera/src/model/train_identification.dart';
class JourneyProfile extends SferaXmlElement {
@@ -13,6 +14,9 @@ class JourneyProfile extends SferaXmlElement {
Iterable get segmentProfilesLists => children.whereType();
+ Set get trainCharactericsRefSet =>
+ children.whereType().map((it) => it.trainCharacteristicsRef).expand((it) => it).toSet();
+
JpStatus get status => XmlEnum.valueOf(JpStatus.values, attributes['JP_Status']) ?? JpStatus.valid;
@override
diff --git a/das_client/lib/sfera/src/model/network_specific_parameter.dart b/das_client/lib/sfera/src/model/network_specific_parameter.dart
index f3d45810..ea536adf 100644
--- a/das_client/lib/sfera/src/model/network_specific_parameter.dart
+++ b/das_client/lib/sfera/src/model/network_specific_parameter.dart
@@ -1,6 +1,8 @@
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
-import 'package:das_client/sfera/src/model/xml_new_line_speed.dart';
import 'package:das_client/sfera/src/model/track_equipment_type_wrapper.dart';
+import 'package:das_client/sfera/src/model/xml_curve_speed.dart';
+import 'package:das_client/sfera/src/model/xml_new_line_speed.dart';
+import 'package:das_client/sfera/src/model/xml_station_speed.dart';
class NetworkSpecificParameter extends SferaXmlElement {
static const String elementType = 'NetworkSpecificParameter';
@@ -11,8 +13,12 @@ class NetworkSpecificParameter extends SferaXmlElement {
{Map? attributes, List? children, String? value}) {
if (attributes?['name'] == XmlNewLineSpeed.elementName) {
return XmlNewLineSpeed(attributes: attributes, children: children, value: value);
- } else if (attributes?['name'] == TrackEquipmentTypeWrapper.elementName) {
+ } else if (attributes?['name'] == TrackEquipmentTypeWrapper.elementName) {
return TrackEquipmentTypeWrapper(attributes: attributes, children: children, value: value);
+ } else if (attributes?['name'] == XmlCurveSpeed.elementName) {
+ return XmlCurveSpeed(attributes: attributes, children: children, value: value);
+ } else if (attributes?['name'] == XmlStationSpeed.elementName) {
+ return XmlStationSpeed(attributes: attributes, children: children, value: value);
}
return NetworkSpecificParameter(attributes: attributes, children: children, value: value);
}
@@ -31,4 +37,4 @@ class NetworkSpecificParameter extends SferaXmlElement {
extension NetworkSpecificParametersExtension on Iterable {
NetworkSpecificParameter? withName(String name) => where((it) => it.name == name).firstOrNull;
-}
\ No newline at end of file
+}
diff --git a/das_client/lib/sfera/src/model/network_specific_point.dart b/das_client/lib/sfera/src/model/network_specific_point.dart
index c8851bf6..8229ca3c 100644
--- a/das_client/lib/sfera/src/model/network_specific_point.dart
+++ b/das_client/lib/sfera/src/model/network_specific_point.dart
@@ -1,3 +1,4 @@
+import 'package:das_client/sfera/src/model/curve_point_network_specific_point.dart';
import 'package:das_client/sfera/src/model/network_specific_parameter.dart';
import 'package:das_client/sfera/src/model/new_line_speed_network_specific_point.dart';
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
@@ -11,6 +12,8 @@ class NetworkSpecificPoint extends SpGenericPoint {
factory NetworkSpecificPoint.from({Map? attributes, List? children, String? value}) {
if (attributes?['name'] == NewLineSpeedNetworkSpecificPoint.elementName) {
return NewLineSpeedNetworkSpecificPoint(attributes: attributes, children: children, value: value);
+ } else if (attributes?['name'] == CurvePointNetworkSpecificPoint.elementName) {
+ return CurvePointNetworkSpecificPoint(attributes: attributes, children: children, value: value);
}
return NetworkSpecificPoint(attributes: attributes, children: children, value: value);
}
diff --git a/das_client/lib/sfera/src/model/nsp.dart b/das_client/lib/sfera/src/model/nsp.dart
index a0e1b41e..b7f5dd46 100644
--- a/das_client/lib/sfera/src/model/nsp.dart
+++ b/das_client/lib/sfera/src/model/nsp.dart
@@ -8,7 +8,7 @@ abstract class Nsp extends SferaXmlElement {
String get name => attributes['name']!;
- Iterable get networkSpecificParameters => children.whereType();
+ Iterable get parameters => children.whereType();
@override
bool validate() {
diff --git a/das_client/lib/sfera/src/model/segment_profile_list.dart b/das_client/lib/sfera/src/model/segment_profile_list.dart
index 67851e12..7d6d372f 100644
--- a/das_client/lib/sfera/src/model/segment_profile_list.dart
+++ b/das_client/lib/sfera/src/model/segment_profile_list.dart
@@ -3,6 +3,7 @@ import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
import 'package:das_client/sfera/src/model/sp_zone.dart';
import 'package:das_client/sfera/src/model/temporary_constraints.dart';
import 'package:das_client/sfera/src/model/timing_point_constraints.dart';
+import 'package:das_client/sfera/src/model/train_characteristics_ref.dart';
class SegmentProfileList extends SferaXmlElement {
static const String elementType = 'SegmentProfileList';
@@ -19,6 +20,8 @@ class SegmentProfileList extends SferaXmlElement {
Iterable get timingPointsContraints => children.whereType();
+ Iterable get trainCharacteristicsRef => children.whereType();
+
Iterable get asrTemporaryConstrains => children
.whereType()
.where((it) => it.temporaryConstraintType == TemporaryConstraintType.asr);
diff --git a/das_client/lib/sfera/src/model/sp_points.dart b/das_client/lib/sfera/src/model/sp_points.dart
index 8fd11d63..67ba0f89 100644
--- a/das_client/lib/sfera/src/model/sp_points.dart
+++ b/das_client/lib/sfera/src/model/sp_points.dart
@@ -1,3 +1,4 @@
+import 'package:das_client/sfera/src/model/curve_point_network_specific_point.dart';
import 'package:das_client/sfera/src/model/network_specific_point.dart';
import 'package:das_client/sfera/src/model/new_line_speed_network_specific_point.dart';
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
@@ -8,7 +9,6 @@ import 'package:das_client/sfera/src/model/virtual_balise.dart';
class SpPoints extends SferaXmlElement {
static const String elementType = 'SP_Points';
static const String _protectionSectionNspName = 'protectionSection';
- static const String _curvePointName = 'curvePoint';
SpPoints({super.type = elementType, super.attributes, super.children, super.value});
@@ -16,9 +16,6 @@ class SpPoints extends SferaXmlElement {
Iterable get signals => children.whereType();
- Iterable get curvePointsNsp =>
- children.whereType().where((it) => it.name == _curvePointName);
-
Iterable get balise => children.whereType();
Iterable get protectionSectionNsp =>
@@ -26,4 +23,7 @@ class SpPoints extends SferaXmlElement {
Iterable get newLineSpeedsNsp =>
children.whereType();
+
+ Iterable get curvePointsNsp =>
+ children.whereType();
}
diff --git a/das_client/lib/sfera/src/model/station_speed.dart b/das_client/lib/sfera/src/model/station_speed.dart
new file mode 100644
index 00000000..0fa45716
--- /dev/null
+++ b/das_client/lib/sfera/src/model/station_speed.dart
@@ -0,0 +1,7 @@
+import 'package:das_client/sfera/src/model/speeds.dart';
+
+class StationSpeed extends Speeds {
+ static const String elementType = 'stationSpeed';
+
+ StationSpeed({super.type = elementType, super.attributes, super.children, super.value});
+}
diff --git a/das_client/lib/sfera/src/model/station_speed_nsp.dart b/das_client/lib/sfera/src/model/station_speed_nsp.dart
new file mode 100644
index 00000000..804c1c8f
--- /dev/null
+++ b/das_client/lib/sfera/src/model/station_speed_nsp.dart
@@ -0,0 +1,15 @@
+import 'package:das_client/sfera/src/model/taf_tap_location_nsp.dart';
+import 'package:das_client/sfera/src/model/xml_station_speed.dart';
+
+class StationSpeedNsp extends TafTapLocationNsp {
+ static const String elementName = 'stationSpeed';
+
+ StationSpeedNsp({super.type, super.attributes, super.children, super.value});
+
+ XmlStationSpeed get xmlStationSpeed => children.whereType().first;
+
+ @override
+ bool validate() {
+ return validateHasChildOfType() && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/taf_tap_location.dart b/das_client/lib/sfera/src/model/taf_tap_location.dart
index 576c3570..b66c7bd3 100644
--- a/das_client/lib/sfera/src/model/taf_tap_location.dart
+++ b/das_client/lib/sfera/src/model/taf_tap_location.dart
@@ -1,6 +1,7 @@
import 'package:das_client/sfera/src/model/enums/taf_tap_location_type.dart';
import 'package:das_client/sfera/src/model/enums/xml_enum.dart';
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/station_speed_nsp.dart';
import 'package:das_client/sfera/src/model/taf_tap_location_ident.dart';
import 'package:das_client/sfera/src/model/taf_tap_location_name.dart';
import 'package:das_client/sfera/src/model/taf_tap_location_nsp.dart';
@@ -21,6 +22,8 @@ class TafTapLocation extends SferaXmlElement {
Iterable get nsp => children.whereType();
+ StationSpeedNsp? get stationSpeed => children.whereType().firstOrNull;
+
@override
bool validate() {
return validateHasChildOfType() && super.validate();
diff --git a/das_client/lib/sfera/src/model/taf_tap_location_nsp.dart b/das_client/lib/sfera/src/model/taf_tap_location_nsp.dart
index 0063b020..bbc7b0d8 100644
--- a/das_client/lib/sfera/src/model/taf_tap_location_nsp.dart
+++ b/das_client/lib/sfera/src/model/taf_tap_location_nsp.dart
@@ -1,7 +1,16 @@
import 'package:das_client/sfera/src/model/nsp.dart';
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/station_speed_nsp.dart';
class TafTapLocationNsp extends Nsp {
static const String elementType = 'TAF_TAP_Location_NSP';
TafTapLocationNsp({super.type = elementType, super.attributes, super.children, super.value});
+
+ factory TafTapLocationNsp.from({Map? attributes, List? children, String? value}) {
+ if (attributes?['name'] == StationSpeedNsp.elementName) {
+ return StationSpeedNsp(attributes: attributes, children: children, value: value);
+ }
+ return TafTapLocationNsp(attributes: attributes, children: children, value: value);
+ }
}
diff --git a/das_client/lib/sfera/src/model/tc_features.dart b/das_client/lib/sfera/src/model/tc_features.dart
new file mode 100644
index 00000000..2e426704
--- /dev/null
+++ b/das_client/lib/sfera/src/model/tc_features.dart
@@ -0,0 +1,13 @@
+import 'package:das_client/model/journey/train_series.dart';
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/util/util.dart';
+
+class TcFeatures extends SferaXmlElement {
+ static const String elementType = 'TC_Features';
+
+ TcFeatures({super.type = elementType, super.attributes, super.children, super.value});
+
+ int? get brakedWeightPercentage => Util.tryParseInt(attributes['brakedWeightPercentage']);
+
+ TrainSeries? get trainCategoryCode => TrainSeries.fromOptional(attributes['trainCategoryCode']);
+}
diff --git a/das_client/lib/sfera/src/model/tc_request.dart b/das_client/lib/sfera/src/model/tc_request.dart
new file mode 100644
index 00000000..a7b12bbe
--- /dev/null
+++ b/das_client/lib/sfera/src/model/tc_request.dart
@@ -0,0 +1,16 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class TcRequest extends SferaXmlElement {
+ static const String elementType = 'TC_Request';
+
+ TcRequest({super.type = elementType, super.attributes, super.children, super.value});
+
+ factory TcRequest.create({required String id, required String versionMajor, required String versionMinor, required String ruId}) {
+ final request = TcRequest();
+ request.attributes['TC_ID'] = id;
+ request.attributes['TC_VersionMajor'] = versionMajor;
+ request.attributes['TC_VersionMinor'] = versionMinor;
+ request.children.add(SferaXmlElement(type: 'TC_RU_ID', value: ruId));
+ return request;
+ }
+}
diff --git a/das_client/lib/sfera/src/model/train_characteristics.dart b/das_client/lib/sfera/src/model/train_characteristics.dart
new file mode 100644
index 00000000..d19e8bc4
--- /dev/null
+++ b/das_client/lib/sfera/src/model/train_characteristics.dart
@@ -0,0 +1,28 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/tc_features.dart';
+
+class TrainCharacteristics extends SferaXmlElement {
+ static const String elementType = 'TrainCharacteristics';
+
+ TrainCharacteristics({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get tcId => attributes['TC_ID']!;
+
+ String get ruId => childrenWithType('TC_RU_ID').first.value!;
+
+ String get versionMajor => attributes['TC_VersionMajor']!;
+
+ String get versionMinor => attributes['TC_VersionMinor']!;
+
+ TcFeatures get tcFeatures => children.whereType().first;
+
+ @override
+ bool validate() {
+ return validateHasChildOfType() &&
+ validateHasAttribute('TC_ID') &&
+ validateHasChild('TC_RU_ID') &&
+ validateHasAttribute('TC_VersionMajor') &&
+ validateHasAttribute('TC_VersionMinor') &&
+ super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/train_characteristics_ref.dart b/das_client/lib/sfera/src/model/train_characteristics_ref.dart
new file mode 100644
index 00000000..9b2d8420
--- /dev/null
+++ b/das_client/lib/sfera/src/model/train_characteristics_ref.dart
@@ -0,0 +1,46 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class TrainCharacteristicsRef extends SferaXmlElement {
+ static const String elementType = 'TrainCharacteristicsRef';
+
+ TrainCharacteristicsRef({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get tcId => attributes['TC_ID']!;
+
+ String get ruId => childrenWithType('TC_RU_ID').first.value!;
+
+ String get versionMajor => attributes['TC_VersionMajor']!;
+
+ String get versionMinor => attributes['TC_VersionMinor']!;
+
+ double get location => double.parse(attributes['location']!);
+
+ @override
+ bool validate() {
+ return validateHasAttribute('TC_ID') &&
+ validateHasChild('TC_RU_ID') &&
+ validateHasAttributeDouble('location') &&
+ validateHasAttribute('TC_VersionMajor') &&
+ validateHasAttribute('TC_VersionMinor') &&
+ super.validate();
+ }
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other)) return true;
+
+ return other is TrainCharacteristicsRef &&
+ other.tcId == tcId &&
+ other.versionMajor == versionMajor &&
+ other.versionMinor == versionMinor &&
+ other.ruId == ruId;
+ }
+
+ @override
+ int get hashCode => tcId.hashCode ^ versionMajor.hashCode ^ versionMinor.hashCode ^ ruId.hashCode;
+
+ @override
+ String toString() {
+ return 'TrainCharacteristicsRef(tcId: $tcId, versionMajor: $versionMajor, versionMinor: $versionMinor, ruId: $ruId)';
+ }
+}
diff --git a/das_client/lib/sfera/src/model/xml_curve_speed.dart b/das_client/lib/sfera/src/model/xml_curve_speed.dart
new file mode 100644
index 00000000..6abc7094
--- /dev/null
+++ b/das_client/lib/sfera/src/model/xml_curve_speed.dart
@@ -0,0 +1,9 @@
+import 'package:das_client/sfera/src/model/curve_speed.dart';
+import 'package:das_client/sfera/src/model/network_specific_parameter.dart';
+import 'package:das_client/sfera/src/model/nsp_xml_element.dart';
+
+class XmlCurveSpeed extends NetworkSpecificParameter with NspXmlElement {
+ static const String elementName = 'xmlCurveSpeed';
+
+ XmlCurveSpeed({super.attributes, super.children, super.value});
+}
diff --git a/das_client/lib/sfera/src/model/xml_station_speed.dart b/das_client/lib/sfera/src/model/xml_station_speed.dart
new file mode 100644
index 00000000..c30dd931
--- /dev/null
+++ b/das_client/lib/sfera/src/model/xml_station_speed.dart
@@ -0,0 +1,9 @@
+import 'package:das_client/sfera/src/model/network_specific_parameter.dart';
+import 'package:das_client/sfera/src/model/nsp_xml_element.dart';
+import 'package:das_client/sfera/src/model/station_speed.dart';
+
+class XmlStationSpeed extends NetworkSpecificParameter with NspXmlElement {
+ static const String elementName = 'xmlStationSpeed';
+
+ XmlStationSpeed({super.attributes, super.children, super.value});
+}
diff --git a/das_client/lib/sfera/src/repo/sfera_repository.dart b/das_client/lib/sfera/src/repo/sfera_repository.dart
index 050c211a..70aa3dda 100644
--- a/das_client/lib/sfera/src/repo/sfera_repository.dart
+++ b/das_client/lib/sfera/src/repo/sfera_repository.dart
@@ -1,7 +1,9 @@
import 'package:das_client/sfera/src/db/journey_profile_entity.dart';
import 'package:das_client/sfera/src/db/segment_profile_entity.dart';
+import 'package:das_client/sfera/src/db/train_characteristics_entity.dart';
import 'package:das_client/sfera/src/model/journey_profile.dart';
import 'package:das_client/sfera/src/model/segment_profile.dart';
+import 'package:das_client/sfera/src/model/train_characteristics.dart';
abstract class SferaRepository {
const SferaRepository._();
@@ -10,10 +12,14 @@ abstract class SferaRepository {
Future saveSegmentProfile(SegmentProfile segmentProfile);
+ Future saveTrainCharacteristics(TrainCharacteristics trainCharacteristics);
+
Future findJourneyProfile(String company, String operationalTrainNumber, DateTime startDate);
Future findSegmentProfile(String spId, String majorVersion, String minorVersion);
+ Future findTrainCharacteristics(String tcId, String majorVersion, String minorVersion);
+
Stream> observeJourneyProfile(
String company, String operationalTrainNumber, DateTime startDate);
}
diff --git a/das_client/lib/sfera/src/repo/sfera_repository_impl.dart b/das_client/lib/sfera/src/repo/sfera_repository_impl.dart
index e65e5fd4..3fd00634 100644
--- a/das_client/lib/sfera/src/repo/sfera_repository_impl.dart
+++ b/das_client/lib/sfera/src/repo/sfera_repository_impl.dart
@@ -1,8 +1,10 @@
import 'package:das_client/sfera/sfera_component.dart';
import 'package:das_client/sfera/src/db/journey_profile_entity.dart';
import 'package:das_client/sfera/src/db/segment_profile_entity.dart';
+import 'package:das_client/sfera/src/db/train_characteristics_entity.dart';
import 'package:das_client/sfera/src/model/journey_profile.dart';
import 'package:das_client/sfera/src/model/segment_profile.dart';
+import 'package:das_client/sfera/src/model/train_characteristics.dart';
import 'package:fimber/fimber.dart';
import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';
@@ -19,7 +21,9 @@ class SferaRepositoryImpl implements SferaRepository {
Fimber.i('Initializing SferaStore...');
final dir = await getApplicationDocumentsDirectory();
_db = await Isar.openAsync(
- schemas: [JourneyProfileEntitySchema, SegmentProfileEntitySchema], directory: dir.path, name: 'das');
+ schemas: [JourneyProfileEntitySchema, SegmentProfileEntitySchema, TrainCharacteristicsEntitySchema],
+ directory: dir.path,
+ name: 'das');
}
@override
@@ -96,4 +100,36 @@ class SferaRepositoryImpl implements SferaRepository {
.startDateEqualTo(startDate)
.watch(fireImmediately: true);
}
+
+ @override
+ Future findTrainCharacteristics(
+ String tcId, String majorVersion, String minorVersion) async {
+ await _initialized;
+ return _db.trainCharacteristics
+ .where()
+ .tcIdEqualTo(tcId)
+ .majorVersionEqualTo(majorVersion)
+ .minorVersionEqualTo(minorVersion)
+ .findFirst();
+ }
+
+ @override
+ Future saveTrainCharacteristics(TrainCharacteristics trainCharacteristics) async {
+ await _initialized;
+
+ final existingTrainCharacteristics = await findTrainCharacteristics(
+ trainCharacteristics.tcId, trainCharacteristics.versionMajor, trainCharacteristics.versionMinor);
+ if (existingTrainCharacteristics == null) {
+ final trainCharacteristicsEntity =
+ trainCharacteristics.toEntity(isarId: _db.trainCharacteristics.autoIncrement());
+ Fimber.i(
+ 'Writing train characteristics to db tcId=${trainCharacteristicsEntity.tcId} majorVersion=${trainCharacteristicsEntity.majorVersion} minorVersion=${trainCharacteristicsEntity.minorVersion}');
+ _db.write((isar) {
+ isar.trainCharacteristics.put(trainCharacteristicsEntity);
+ });
+ } else {
+ Fimber.i(
+ 'train characteristics already exists in db tcId=${existingTrainCharacteristics.tcId} majorVersion=${existingTrainCharacteristics.majorVersion} minorVersion=${existingTrainCharacteristics.minorVersion}');
+ }
+ }
}
diff --git a/das_client/lib/sfera/src/service/sfera_service_impl.dart b/das_client/lib/sfera/src/service/sfera_service_impl.dart
index a75d33d2..a8821244 100644
--- a/das_client/lib/sfera/src/service/sfera_service_impl.dart
+++ b/das_client/lib/sfera/src/service/sfera_service_impl.dart
@@ -10,12 +10,14 @@ import 'package:das_client/sfera/src/model/enums/das_driving_mode.dart';
import 'package:das_client/sfera/src/model/journey_profile.dart';
import 'package:das_client/sfera/src/model/segment_profile.dart';
import 'package:das_client/sfera/src/model/sfera_g2b_reply_message.dart';
+import 'package:das_client/sfera/src/model/train_characteristics.dart';
import 'package:das_client/sfera/src/service/handler/journey_profile_reply_handler.dart';
import 'package:das_client/sfera/src/service/handler/segment_profile_reply_handler.dart';
import 'package:das_client/sfera/src/service/handler/sfera_message_handler.dart';
import 'package:das_client/sfera/src/service/task/handshake_task.dart';
import 'package:das_client/sfera/src/service/task/request_journey_profile_task.dart';
import 'package:das_client/sfera/src/service/task/request_segment_profiles_task.dart';
+import 'package:das_client/sfera/src/service/task/request_train_characteristics_task.dart';
import 'package:das_client/sfera/src/service/task/sfera_task.dart';
import 'package:das_client/util/error_code.dart';
import 'package:fimber/fimber.dart';
@@ -41,6 +43,7 @@ class SferaServiceImpl implements SferaService {
OtnId? _otnId;
JourneyProfile? _journeyProfile;
final List _segmentProfiles = [];
+ final List _trainCharacteristics = [];
@override
ErrorCode? lastErrorCode;
@@ -108,15 +111,22 @@ class SferaServiceImpl implements SferaService {
_messageHandlers.add(requestJourneyTask);
requestJourneyTask.execute(onTaskCompleted, onTaskFailed);
} else if (task is RequestJourneyProfileTask) {
- _stateSubject.add(SferaServiceState.loadingSegments);
+ _stateSubject.add(SferaServiceState.loadingAdditionalData);
final requestSegmentProfilesTask = RequestSegmentProfilesTask(
mqttService: _mqttService, sferaRepository: _sferaRepository, otnId: _otnId!, journeyProfile: data);
+ final requestTrainCharacteristicsTask = RequestTrainCharacteristicsTask(
+ mqttService: _mqttService, sferaRepository: _sferaRepository, otnId: _otnId!, journeyProfile: data);
_journeyProfile = data;
_messageHandlers.add(requestSegmentProfilesTask);
+ _messageHandlers.add(requestTrainCharacteristicsTask);
requestSegmentProfilesTask.execute(onTaskCompleted, onTaskFailed);
- } else if (task is RequestSegmentProfilesTask) {
+ requestTrainCharacteristicsTask.execute(onTaskCompleted, onTaskFailed);
+ }
+
+ if (_stateSubject.value == SferaServiceState.loadingAdditionalData && _allTaskedCompleted()) {
_addMessageHandlers();
await _refreshSegmentProfiles();
+ await _refreshTrainCharacteristics();
final success = _updateJourney();
if (success) {
_stateSubject.add(SferaServiceState.connected);
@@ -127,6 +137,10 @@ class SferaServiceImpl implements SferaService {
}
}
+ bool _allTaskedCompleted() {
+ return _messageHandlers.whereType().isEmpty;
+ }
+
Future _refreshSegmentProfiles() async {
if (_journeyProfile != null) {
_segmentProfiles.clear();
@@ -144,12 +158,29 @@ class SferaServiceImpl implements SferaService {
}
}
+ Future _refreshTrainCharacteristics() async {
+ if (_journeyProfile != null) {
+ _trainCharacteristics.clear();
+
+ for (final element in _journeyProfile!.trainCharactericsRefSet) {
+ final trainCharactericsEntity =
+ await _sferaRepository.findTrainCharacteristics(element.tcId, element.versionMajor, element.versionMinor);
+ final trainCharacterics = trainCharactericsEntity?.toDomain();
+ if (trainCharacterics != null && trainCharacterics.validate()) {
+ _trainCharacteristics.add(trainCharacterics);
+ } else {
+ Fimber.w('Could not find and validate $element');
+ }
+ }
+ }
+ }
+
bool _updateJourney() {
if (_journeyProfile != null && _segmentProfiles.isNotEmpty) {
Fimber.i('Updating journey stream...');
- final newJourney = SferaModelMapper.mapToJourney(_journeyProfile!, _segmentProfiles);
+ final newJourney = SferaModelMapper.mapToJourney(_journeyProfile!, _segmentProfiles, _trainCharacteristics);
if (newJourney.valid) {
- _journeyProfileSubject.add(SferaModelMapper.mapToJourney(_journeyProfile!, _segmentProfiles));
+ _journeyProfileSubject.add(newJourney);
Fimber.i('Journey updates successfully.');
return true;
} else {
@@ -168,7 +199,7 @@ class SferaServiceImpl implements SferaService {
_messageHandlers.remove(task);
lastErrorCode = errorCode;
Fimber.e('Task $task failed with error code $errorCode');
- if (task is HandshakeTask || task is RequestJourneyProfileTask || task is RequestSegmentProfilesTask) {
+ if (_stateSubject.value != SferaServiceState.connected) {
disconnect();
}
}
diff --git a/das_client/lib/sfera/src/service/sfera_service_state.dart b/das_client/lib/sfera/src/service/sfera_service_state.dart
index 9ff782aa..b24d61d7 100644
--- a/das_client/lib/sfera/src/service/sfera_service_state.dart
+++ b/das_client/lib/sfera/src/service/sfera_service_state.dart
@@ -3,7 +3,7 @@ enum SferaServiceState {
connecting,
handshaking,
loadingJourney,
- loadingSegments,
+ loadingAdditionalData,
connected,
offline
}
diff --git a/das_client/lib/sfera/src/service/task/request_journey_profile_task.dart b/das_client/lib/sfera/src/service/task/request_journey_profile_task.dart
index 7d2479d4..a955d351 100644
--- a/das_client/lib/sfera/src/service/task/request_journey_profile_task.dart
+++ b/das_client/lib/sfera/src/service/task/request_journey_profile_task.dart
@@ -61,13 +61,19 @@ class RequestJourneyProfileTask extends SferaTask {
}
Fimber.i(
- 'Received G2bReplyPayload response with ${replyMessage.payload!.journeyProfiles.length} JourneyProfiles and ${replyMessage.payload!.segmentProfiles.length} SegmentProfiles...',
+ 'Received G2bReplyPayload response with ${replyMessage.payload!.journeyProfiles.length} JourneyProfiles, '
+ '${replyMessage.payload!.segmentProfiles.length} SegmentProfiles and '
+ '${replyMessage.payload!.trainCharacteristics.length} TrainCharacteristics...',
);
for (final element in replyMessage.payload!.segmentProfiles) {
await _sferaRepository.saveSegmentProfile(element);
}
+ for (final trainCharacteristics in replyMessage.payload!.trainCharacteristics) {
+ await _sferaRepository.saveTrainCharacteristics(trainCharacteristics);
+ }
+
for (final journeyProfile in replyMessage.payload!.journeyProfiles) {
await _sferaRepository.saveJourneyProfile(journeyProfile);
}
diff --git a/das_client/lib/sfera/src/service/task/request_train_characteristics_task.dart b/das_client/lib/sfera/src/service/task/request_train_characteristics_task.dart
new file mode 100644
index 00000000..644e9023
--- /dev/null
+++ b/das_client/lib/sfera/src/service/task/request_train_characteristics_task.dart
@@ -0,0 +1,102 @@
+import 'package:das_client/mqtt/mqtt_component.dart';
+import 'package:das_client/sfera/src/model/b2g_request.dart';
+import 'package:das_client/sfera/src/model/journey_profile.dart';
+import 'package:das_client/sfera/src/model/otn_id.dart';
+import 'package:das_client/sfera/src/model/sfera_b2g_request_message.dart';
+import 'package:das_client/sfera/src/model/sfera_g2b_reply_message.dart';
+import 'package:das_client/sfera/src/model/tc_request.dart';
+import 'package:das_client/sfera/src/model/train_characteristics.dart';
+import 'package:das_client/sfera/src/model/train_characteristics_ref.dart';
+import 'package:das_client/sfera/src/model/train_identification.dart';
+import 'package:das_client/sfera/src/repo/sfera_repository.dart';
+import 'package:das_client/sfera/src/service/sfera_service.dart';
+import 'package:das_client/sfera/src/service/task/sfera_task.dart';
+import 'package:fimber/fimber.dart';
+
+class RequestTrainCharacteristicsTask extends SferaTask> {
+ RequestTrainCharacteristicsTask(
+ {required MqttService mqttService,
+ required SferaRepository sferaRepository,
+ required this.otnId,
+ required this.journeyProfile,
+ super.timeout})
+ : _mqttService = mqttService,
+ _sferaRepository = sferaRepository;
+
+ final MqttService _mqttService;
+ final OtnId otnId;
+ final SferaRepository _sferaRepository;
+ final JourneyProfile journeyProfile;
+
+ late TaskCompleted> _taskCompletedCallback;
+ late TaskFailed _taskFailedCallback;
+
+ @override
+ Future execute(TaskCompleted> onCompleted, TaskFailed onFailed) async {
+ _taskCompletedCallback = onCompleted;
+ _taskFailedCallback = onFailed;
+
+ await _requestSegmentProfiles();
+ }
+
+ Future _requestSegmentProfiles() async {
+ final missingTrainCharacteristics = await findMissingTrainCharacteristics();
+ if (missingTrainCharacteristics.isEmpty) {
+ Fimber.i('No missing train characteristics found...');
+ _taskCompletedCallback(this, []);
+ return;
+ }
+
+ final List tcRequests = [];
+ for (final missingTrainRef in missingTrainCharacteristics) {
+ tcRequests.add(TcRequest.create(
+ id: missingTrainRef.tcId,
+ versionMajor: missingTrainRef.versionMajor,
+ versionMinor: missingTrainRef.versionMinor,
+ ruId: missingTrainRef.ruId));
+ }
+
+ final trainIdentification = TrainIdentification.create(otnId: otnId);
+ final sferaB2gRequestMessage = SferaB2gRequestMessage.create(
+ await SferaService.messageHeader(trainIdentification: trainIdentification, sender: otnId.company),
+ b2gRequest: B2gRequest.createTCRequest(tcRequests));
+ Fimber.i('Sending train characteristics request...');
+
+ startTimeout(_taskFailedCallback);
+ _mqttService.publishMessage(otnId.company, SferaService.sferaTrain(otnId.operationalTrainNumber, otnId.startDate),
+ sferaB2gRequestMessage.buildDocument().toString());
+ }
+
+ Future> findMissingTrainCharacteristics() async {
+ final missingTrainCharacteristics = {};
+
+ for (final trainRef in journeyProfile.trainCharactericsRefSet) {
+ final existingTrainCharacteristic =
+ await _sferaRepository.findTrainCharacteristics(trainRef.tcId, trainRef.versionMajor, trainRef.versionMinor);
+ if (existingTrainCharacteristic == null) {
+ missingTrainCharacteristics.add(trainRef);
+ }
+ }
+
+ return missingTrainCharacteristics;
+ }
+
+ @override
+ Future handleMessage(SferaG2bReplyMessage replyMessage) async {
+ if (replyMessage.payload != null && replyMessage.payload!.trainCharacteristics.isNotEmpty) {
+ stopTimeout();
+ Fimber.i(
+ 'Received G2bReplyPayload response with ${replyMessage.payload!.trainCharacteristics.length} TrainCharacteristics...',
+ );
+
+ for (final element in replyMessage.payload!.trainCharacteristics) {
+ await _sferaRepository.saveTrainCharacteristics(element);
+ }
+
+ _taskCompletedCallback(this, replyMessage.payload!.trainCharacteristics.toList());
+
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/das_client/lib/sfera/src/sfera_reply_parser.dart b/das_client/lib/sfera/src/sfera_reply_parser.dart
index 9a184274..2a5dfdd1 100644
--- a/das_client/lib/sfera/src/sfera_reply_parser.dart
+++ b/das_client/lib/sfera/src/sfera_reply_parser.dart
@@ -3,6 +3,7 @@ import 'package:das_client/sfera/src/model/connection_track.dart';
import 'package:das_client/sfera/src/model/current_limitation.dart';
import 'package:das_client/sfera/src/model/current_limitation_change.dart';
import 'package:das_client/sfera/src/model/current_limitation_start.dart';
+import 'package:das_client/sfera/src/model/curve_speed.dart';
import 'package:das_client/sfera/src/model/das_operating_modes_selected.dart';
import 'package:das_client/sfera/src/model/g2b_reply_payload.dart';
import 'package:das_client/sfera/src/model/handshake_acknowledgement.dart';
@@ -32,6 +33,7 @@ import 'package:das_client/sfera/src/model/sp_context_information.dart';
import 'package:das_client/sfera/src/model/sp_points.dart';
import 'package:das_client/sfera/src/model/sp_zone.dart';
import 'package:das_client/sfera/src/model/speeds.dart';
+import 'package:das_client/sfera/src/model/station_speed.dart';
import 'package:das_client/sfera/src/model/stop_type.dart';
import 'package:das_client/sfera/src/model/stopping_point_information.dart';
import 'package:das_client/sfera/src/model/taf_tap_location.dart';
@@ -39,6 +41,7 @@ import 'package:das_client/sfera/src/model/taf_tap_location_ident.dart';
import 'package:das_client/sfera/src/model/taf_tap_location_name.dart';
import 'package:das_client/sfera/src/model/taf_tap_location_nsp.dart';
import 'package:das_client/sfera/src/model/taf_tap_location_reference.dart';
+import 'package:das_client/sfera/src/model/tc_features.dart';
import 'package:das_client/sfera/src/model/temporary_constraint_reason.dart';
import 'package:das_client/sfera/src/model/temporary_constraints.dart';
import 'package:das_client/sfera/src/model/timing_point.dart';
@@ -46,6 +49,8 @@ import 'package:das_client/sfera/src/model/timing_point_constraints.dart';
import 'package:das_client/sfera/src/model/timing_point_reference.dart';
import 'package:das_client/sfera/src/model/tp_id_reference.dart';
import 'package:das_client/sfera/src/model/tp_name.dart';
+import 'package:das_client/sfera/src/model/train_characteristics.dart';
+import 'package:das_client/sfera/src/model/train_characteristics_ref.dart';
import 'package:das_client/sfera/src/model/train_identification.dart';
import 'package:das_client/sfera/src/model/velocity.dart';
import 'package:das_client/sfera/src/model/virtual_balise.dart';
@@ -156,7 +161,7 @@ class SferaReplyParser {
case StopType.elementType:
return StopType(type: type, attributes: attributes, children: children, value: value);
case TafTapLocationNsp.elementType:
- return TafTapLocationNsp(type: type, attributes: attributes, children: children, value: value);
+ return TafTapLocationNsp.from(attributes: attributes, children: children, value: value);
case NetworkSpecificParameter.elementType:
return NetworkSpecificParameter.from(attributes: attributes, children: children, value: value);
case CurrentLimitation.elementType:
@@ -173,18 +178,28 @@ class SferaReplyParser {
return NetworkSpecificArea(type: type, attributes: attributes, children: children, value: value);
case AdditionalSpeedRestriction.elementType:
return AdditionalSpeedRestriction(type: type, attributes: attributes, children: children, value: value);
- case TemporaryConstraints.elementType:
+ case TemporaryConstraints.elementType:
return TemporaryConstraints(type: type, attributes: attributes, children: children, value: value);
- case TemporaryConstraintReason.elementType:
+ case TemporaryConstraintReason.elementType:
return TemporaryConstraintReason(type: type, attributes: attributes, children: children, value: value);
- case Velocity.elementType:
+ case Velocity.elementType:
return Velocity(type: type, attributes: attributes, children: children, value: value);
- case Speeds.elementType:
+ case Speeds.elementType:
return Speeds(type: type, attributes: attributes, children: children, value: value);
- case LineSpeed.elementType:
+ case LineSpeed.elementType:
return LineSpeed(type: type, attributes: attributes, children: children, value: value);
- case ConnectionTrack.elementType:
+ case ConnectionTrack.elementType:
return ConnectionTrack(type: type, attributes: attributes, children: children, value: value);
+ case CurveSpeed.elementType:
+ return CurveSpeed(type: type, attributes: attributes, children: children, value: value);
+ case StationSpeed.elementType:
+ return StationSpeed(type: type, attributes: attributes, children: children, value: value);
+ case TrainCharacteristicsRef.elementType:
+ return TrainCharacteristicsRef(type: type, attributes: attributes, children: children, value: value);
+ case TrainCharacteristics.elementType:
+ return TrainCharacteristics(type: type, attributes: attributes, children: children, value: value);
+ case TcFeatures.elementType:
+ return TcFeatures(type: type, attributes: attributes, children: children, value: value);
default:
return SferaXmlElement(type: type, attributes: attributes, children: children, value: value);
}
diff --git a/das_client/lib/util/util.dart b/das_client/lib/util/util.dart
new file mode 100644
index 00000000..33670724
--- /dev/null
+++ b/das_client/lib/util/util.dart
@@ -0,0 +1,5 @@
+class Util {
+ static int? tryParseInt(String? value) {
+ return value != null ? int.tryParse(value) : null;
+ }
+}
diff --git a/das_client/pubspec.lock b/das_client/pubspec.lock
index ab727e30..7f8c4506 100644
--- a/das_client/pubspec.lock
+++ b/das_client/pubspec.lock
@@ -293,15 +293,6 @@ packages:
relative: true
source: path
version: "0.0.1"
- design_system_flutter:
- dependency: "direct main"
- description:
- path: "."
- ref: "1.4.0"
- resolved-ref: "13197f60141d0bb0fb4ee512b5ff34e68e6c5e35"
- url: "https://github.com/SchweizerischeBundesbahnen/design_system_flutter.git"
- source: git
- version: "1.4.0"
device_info_plus:
dependency: "direct main"
description:
@@ -905,6 +896,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.28.0"
+ sbb_design_system_mobile:
+ dependency: "direct main"
+ description:
+ name: sbb_design_system_mobile
+ sha256: "9231baa5e51b5bda186e3bdfe2949ea932141557793ffb7aeb2062a9d88b790e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
sbb_oidc:
dependency: "direct main"
description:
diff --git a/das_client/pubspec.yaml b/das_client/pubspec.yaml
index a5b2b7d6..532aaea3 100644
--- a/das_client/pubspec.yaml
+++ b/das_client/pubspec.yaml
@@ -18,11 +18,8 @@ dependencies:
path: sbb_oidc
ref: 3.4.0
- design_system_flutter:
- git:
- url: https://github.com/SchweizerischeBundesbahnen/design_system_flutter.git
- ref: 1.4.0
-
+ # https://pub.dev/packages/sbb_design_system_mobile
+ sbb_design_system_mobile: ^2.1.1
# https://pub.dev/packages/collection
collection: ^1.18.0
# https://pub.dev/packages/meta
diff --git a/das_client/test/sfera/mapper/sfera_mapper_test.dart b/das_client/test/sfera/mapper/sfera_mapper_test.dart
index 70b8b711..861ec74f 100644
--- a/das_client/test/sfera/mapper/sfera_mapper_test.dart
+++ b/das_client/test/sfera/mapper/sfera_mapper_test.dart
@@ -16,6 +16,7 @@ import 'package:das_client/sfera/sfera_component.dart';
import 'package:das_client/sfera/src/mapper/sfera_model_mapper.dart';
import 'package:das_client/sfera/src/model/journey_profile.dart';
import 'package:das_client/sfera/src/model/segment_profile.dart';
+import 'package:das_client/sfera/src/model/train_characteristics.dart';
import 'package:fimber/fimber.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -30,11 +31,20 @@ void main() {
return files;
}
- Journey getJourney(String trainNumber, int spCount, {String? spTrainNumber}) {
+ List getFilesForTc(String baseName, int count) {
+ final files = [];
+ for (var i = 1; i <= count; i++) {
+ files.add(File('test_resources/tc/${baseName}_$i.xml'));
+ }
+ return files;
+ }
+
+ Journey getJourney(String trainNumber, int spCount, {String? spTrainNumber, int tcCount = 0}) {
final journeyFile = File('test_resources/jp/SFERA_JP_$trainNumber.xml');
final journeyProfile = SferaReplyParser.parse(journeyFile.readAsStringSync());
expect(journeyProfile.validate(), true);
final List segmentProfiles = [];
+ final List trainCharacteristics = [];
for (final File file in getFilesForSp('SFERA_SP_${spTrainNumber ?? trainNumber}', spCount)) {
final segmentProfile = SferaReplyParser.parse(file.readAsStringSync());
@@ -42,7 +52,13 @@ void main() {
segmentProfiles.add(segmentProfile);
}
- return SferaModelMapper.mapToJourney(journeyProfile, segmentProfiles);
+ for (final File file in getFilesForTc('SFERA_TC_$trainNumber', tcCount)) {
+ final trainCharacteristic = SferaReplyParser.parse(file.readAsStringSync());
+ expect(trainCharacteristic.validate(), true);
+ trainCharacteristics.add(trainCharacteristic);
+ }
+
+ return SferaModelMapper.mapToJourney(journeyProfile, segmentProfiles, trainCharacteristics);
}
test('Test invalid journey on SP missing', () async {
@@ -472,4 +488,54 @@ void main() {
expect(connectionTracks[2].speedData!.velocities[1].reduced, false);
expect(connectionTracks[2].speedData!.velocities[1].breakSeries, isNull);
});
+
+ test('Test available break series are parsed correctly', () async {
+ var journey = getJourney('9999', 5);
+ expect(journey.valid, true);
+ expect(journey.metadata.availableBreakSeries, hasLength(2));
+ expect(journey.metadata.availableBreakSeries.elementAt(0).trainSeries, TrainSeries.R);
+ expect(journey.metadata.availableBreakSeries.elementAt(0).breakSeries, 100);
+ expect(journey.metadata.availableBreakSeries.elementAt(1).trainSeries, TrainSeries.A);
+ expect(journey.metadata.availableBreakSeries.elementAt(1).breakSeries, 30);
+
+ journey = getJourney('T5', 1);
+ expect(journey.valid, true);
+ expect(journey.metadata.availableBreakSeries, hasLength(16));
+ expect(journey.metadata.availableBreakSeries.elementAt(0).trainSeries, TrainSeries.R);
+ expect(journey.metadata.availableBreakSeries.elementAt(0).breakSeries, 105);
+ expect(journey.metadata.availableBreakSeries.elementAt(5).trainSeries, TrainSeries.A);
+ expect(journey.metadata.availableBreakSeries.elementAt(5).breakSeries, 50);
+ expect(journey.metadata.availableBreakSeries.elementAt(15).trainSeries, TrainSeries.D);
+ expect(journey.metadata.availableBreakSeries.elementAt(15).breakSeries, 30);
+ });
+
+ test('Test station/curve speeds are parsed correctly', () async {
+ final journey = getJourney('T5', 1);
+ expect(journey.valid, true);
+
+ final curvePoints = journey.data.where((it) => it.type == Datatype.curvePoint).cast().toList();
+ expect(curvePoints, hasLength(3));
+ expect(curvePoints[0].speedData, isNotNull);
+ expect(curvePoints[0].speedData!.velocities, hasLength(3));
+ expect(curvePoints[1].speedData, isNotNull);
+ expect(curvePoints[1].speedData!.velocities, hasLength(2));
+ expect(curvePoints[2].speedData, isNull);
+
+ final servicePoints = journey.data.where((it) => it.type == Datatype.servicePoint).cast().toList();
+ expect(servicePoints, hasLength(3));
+ expect(servicePoints[0].speedData, isNotNull);
+ expect(servicePoints[0].speedData!.velocities, hasLength(16));
+ expect(servicePoints[1].speedData, isNotNull);
+ expect(servicePoints[1].speedData!.velocities, hasLength(6));
+ expect(servicePoints[2].speedData, isNotNull);
+ expect(servicePoints[2].speedData!.velocities, hasLength(16));
+ });
+
+ test('Test train characterists break series is parsed correctly', () async {
+ final journey = getJourney('T5', 1, tcCount: 1);
+ expect(journey.valid, true);
+ expect(journey.metadata.breakSeries, isNotNull);
+ expect(journey.metadata.breakSeries!.trainSeries, TrainSeries.R);
+ expect(journey.metadata.breakSeries!.breakSeries, 115);
+ });
}
diff --git a/das_client/test/sfera/service/sfera_request_journey_profile_task_test.dart b/das_client/test/sfera/service/sfera_request_journey_profile_task_test.dart
index 6c9ff625..6a3d4782 100644
--- a/das_client/test/sfera/service/sfera_request_journey_profile_task_test.dart
+++ b/das_client/test/sfera/service/sfera_request_journey_profile_task_test.dart
@@ -165,4 +165,27 @@ void main() {
await Future.delayed(const Duration(milliseconds: 1200));
expect(timeoutReached, true);
});
+
+ test('Test JP request saves train characteristic to sfera repository', () async {
+ when(mqttService.publishMessage(any, any, any)).thenReturn(true);
+
+ final file = File('test_resources/SFERA_G2B_Reply_TC_request_T5.xml');
+ final sferaG2bReplyMessage = SferaReplyParser.parse(file.readAsStringSync());
+
+ final journeyTask = RequestJourneyProfileTask(mqttService: mqttService, sferaRepository: sferaRepository, otnId: otnId);
+
+ await journeyTask.execute((task, data) {
+ expect(task, journeyTask);
+ }, (task, errorCode) {
+ fail('Task failed with error code $errorCode');
+ });
+
+ verify(mqttService.publishMessage(any, any, any)).called(1);
+
+ final result = await journeyTask.handleMessage(sferaG2bReplyMessage);
+ expect(result, true);
+
+ verify(sferaRepository.saveJourneyProfile(any)).called(1);
+ verify(sferaRepository.saveTrainCharacteristics(any)).called(1);
+ });
}
diff --git a/das_client/test/sfera/service/sfera_request_segment_profile_task_test.dart b/das_client/test/sfera/service/sfera_request_segment_profile_task_test.dart
index 84bfb1cd..7141178a 100644
--- a/das_client/test/sfera/service/sfera_request_segment_profile_task_test.dart
+++ b/das_client/test/sfera/service/sfera_request_segment_profile_task_test.dart
@@ -140,7 +140,7 @@ void main() {
final file = File('test_resources/SFERA_G2B_Reply_JP_request_9232.xml');
final sferaG2bReplyMessage = SferaReplyParser.parse(file.readAsStringSync());
- final journeyTask = RequestSegmentProfilesTask(
+ final spTask = RequestSegmentProfilesTask(
mqttService: mqttService,
sferaRepository: sferaRepository,
otnId: otnId,
@@ -149,7 +149,7 @@ void main() {
);
var timeoutReached = false;
- await journeyTask.execute((task, data) {
+ await spTask.execute((task, data) {
fail('Test should not call success');
}, (task, errorCode) {
expect(errorCode, ErrorCode.sferaRequestTimeout);
diff --git a/das_client/test/sfera/service/sfera_request_train_characteristic_task_test.dart b/das_client/test/sfera/service/sfera_request_train_characteristic_task_test.dart
new file mode 100644
index 00000000..7617ca2c
--- /dev/null
+++ b/das_client/test/sfera/service/sfera_request_train_characteristic_task_test.dart
@@ -0,0 +1,135 @@
+import 'dart:io';
+
+import 'package:das_client/mqtt/mqtt_component.dart';
+import 'package:das_client/sfera/sfera_component.dart';
+import 'package:das_client/sfera/src/model/sfera_g2b_reply_message.dart';
+import 'package:das_client/sfera/src/service/task/request_train_characteristics_task.dart';
+import 'package:das_client/util/error_code.dart';
+import 'package:fimber/fimber.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+
+import 'sfera_request_train_characteristic_task_test.mocks.dart';
+
+@GenerateNiceMocks([
+ MockSpec(),
+ MockSpec(),
+])
+void main() {
+ late MockMqttService mqttService;
+ late MockSferaRepository sferaRepository;
+ late OtnId otnId;
+ Fimber.plantTree(DebugTree());
+
+ setUp(() {
+ mqttService = MockMqttService();
+ sferaRepository = MockSferaRepository();
+ otnId = OtnId.create('1085', '719', DateTime.now());
+ });
+
+ test('Test TC request successful', () async {
+ when(mqttService.publishMessage(any, any, any)).thenReturn(true);
+
+ final file = File('test_resources/SFERA_G2B_Reply_TC_request_T5.xml');
+ final sferaG2bReplyMessage = SferaReplyParser.parse(file.readAsStringSync());
+
+ final tcTask = RequestTrainCharacteristicsTask(
+ mqttService: mqttService,
+ sferaRepository: sferaRepository,
+ otnId: otnId,
+ journeyProfile: sferaG2bReplyMessage.payload!.journeyProfiles.first);
+
+ await tcTask.execute((task, data) {
+ expect(task, tcTask);
+ expect(data, sferaG2bReplyMessage.payload!.trainCharacteristics);
+ }, (task, errorCode) {
+ fail('Task failed with error code $errorCode');
+ });
+
+ verify(mqttService.publishMessage(any, any, any)).called(1);
+
+ final result = await tcTask.handleMessage(sferaG2bReplyMessage);
+ expect(result, true);
+ });
+
+ test('Test TC request saves to sfera repository', () async {
+ when(mqttService.publishMessage(any, any, any)).thenReturn(true);
+
+ final file = File('test_resources/SFERA_G2B_Reply_TC_request_T5.xml');
+ final sferaG2bReplyMessage = SferaReplyParser.parse(file.readAsStringSync());
+
+ final tcTask = RequestTrainCharacteristicsTask(
+ mqttService: mqttService,
+ sferaRepository: sferaRepository,
+ otnId: otnId,
+ journeyProfile: sferaG2bReplyMessage.payload!.journeyProfiles.first);
+
+ await tcTask.execute((task, data) {
+ expect(task, tcTask);
+ }, (task, errorCode) {
+ fail('Task failed with error code $errorCode');
+ });
+
+ verify(mqttService.publishMessage(any, any, any)).called(1);
+
+ final result = await tcTask.handleMessage(sferaG2bReplyMessage);
+ expect(result, true);
+
+ verify(sferaRepository.saveTrainCharacteristics(any)).called(1);
+ });
+
+ test('Test TC request ignores other messages', () async {
+ when(mqttService.publishMessage(any, any, any)).thenReturn(true);
+
+ final file = File('test_resources/SFERA_G2B_Reply_TC_request_T5.xml');
+ final sferaG2bReplyMessage = SferaReplyParser.parse(file.readAsStringSync());
+
+ final tcTask = RequestTrainCharacteristicsTask(
+ mqttService: mqttService,
+ sferaRepository: sferaRepository,
+ otnId: otnId,
+ journeyProfile: sferaG2bReplyMessage.payload!.journeyProfiles.first);
+
+ await tcTask.execute((task, data) {
+ fail('Test should not call success');
+ }, (task, errorCode) {
+ fail('Test should not call error');
+ });
+
+ verify(mqttService.publishMessage(any, any, any)).called(1);
+
+ final handShakefile = File('test_resources/SFERA_G2B_ReplyMessage_handshake.xml');
+ final handshakeSferaG2bReplyMessage = SferaReplyParser.parse(handShakefile.readAsStringSync());
+ final result = await tcTask.handleMessage(handshakeSferaG2bReplyMessage);
+ expect(result, false);
+ });
+
+ test('Test request segment profile timeout', () async {
+ when(mqttService.publishMessage(any, any, any)).thenReturn(true);
+
+ final file = File('test_resources/SFERA_G2B_Reply_TC_request_T5.xml');
+ final sferaG2bReplyMessage = SferaReplyParser.parse(file.readAsStringSync());
+
+ final tcTask = RequestTrainCharacteristicsTask(
+ mqttService: mqttService,
+ sferaRepository: sferaRepository,
+ otnId: otnId,
+ journeyProfile: sferaG2bReplyMessage.payload!.journeyProfiles.first,
+ timeout: const Duration(seconds: 1),
+ );
+
+ var timeoutReached = false;
+ await tcTask.execute((task, data) {
+ fail('Test should not call success');
+ }, (task, errorCode) {
+ expect(errorCode, ErrorCode.sferaRequestTimeout);
+ timeoutReached = true;
+ });
+
+ verify(mqttService.publishMessage(any, any, any)).called(1);
+
+ await Future.delayed(const Duration(milliseconds: 1200));
+ expect(timeoutReached, true);
+ });
+}
diff --git a/das_client/test_resources/SFERA_G2B_Reply_TC_request_T5.xml b/das_client/test_resources/SFERA_G2B_Reply_TC_request_T5.xml
new file mode 100644
index 00000000..f2bfd6db
--- /dev/null
+++ b/das_client/test_resources/SFERA_G2B_Reply_TC_request_T5.xml
@@ -0,0 +1,45 @@
+
+
+
+ 0088
+ 1088
+
+
+
+
+
+ 1085
+ T5
+ 2022-01-04
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1185
+
+
+
+
+ 1185
+
+
+
+
diff --git a/das_client/test_resources/jp/SFERA_JP_T5.xml b/das_client/test_resources/jp/SFERA_JP_T5.xml
new file mode 100644
index 00000000..59c002c1
--- /dev/null
+++ b/das_client/test_resources/jp/SFERA_JP_T5.xml
@@ -0,0 +1,35 @@
+
+
+
+
+ 1085
+ T5
+ 2022-01-04
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1185
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_1.xml b/das_client/test_resources/sp/SFERA_SP_9999_1.xml
index 9fe83e57..67af050b 100644
--- a/das_client/test_resources/sp/SFERA_SP_9999_1.xml
+++ b/das_client/test_resources/sp/SFERA_SP_9999_1.xml
@@ -30,7 +30,10 @@
-
+
@@ -49,18 +52,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -81,6 +72,9 @@
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_2.xml b/das_client/test_resources/sp/SFERA_SP_9999_2.xml
index 96738c76..cb0ee7b2 100644
--- a/das_client/test_resources/sp/SFERA_SP_9999_2.xml
+++ b/das_client/test_resources/sp/SFERA_SP_9999_2.xml
@@ -29,7 +29,10 @@
-
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_3.xml b/das_client/test_resources/sp/SFERA_SP_9999_3.xml
index 5b25ddd9..8e2cedea 100644
--- a/das_client/test_resources/sp/SFERA_SP_9999_3.xml
+++ b/das_client/test_resources/sp/SFERA_SP_9999_3.xml
@@ -24,14 +24,20 @@
-
+
-
+
diff --git a/das_client/test_resources/sp/SFERA_SP_T5_1.xml b/das_client/test_resources/sp/SFERA_SP_T5_1.xml
new file mode 100644
index 00000000..778f64ca
--- /dev/null
+++ b/das_client/test_resources/sp/SFERA_SP_T5_1.xml
@@ -0,0 +1,252 @@
+
+
+
+ 0085
+
+
+
+
+
+ CH
+ 3002
+
+
+
+
+
+
+ CH
+ 3003
+
+
+
+
+
+
+ CH
+ 3004
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CH
+ 3002
+
+
+
+
+
+
+
+
+
+ CH
+ 3003
+
+
+
+
+
+
+
+
+
+ CH
+ 3004
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/tc/SFERA_TC_T5_1.xml b/das_client/test_resources/tc/SFERA_TC_T5_1.xml
new file mode 100644
index 00000000..51ce7bf2
--- /dev/null
+++ b/das_client/test_resources/tc/SFERA_TC_T5_1.xml
@@ -0,0 +1,8 @@
+
+
+ 1185
+
+
+
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_JP_9999.xml b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_JP_9999.xml
index 36902805..69ddc7b3 100644
--- a/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_JP_9999.xml
+++ b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_JP_9999.xml
@@ -18,6 +18,9 @@
+
+ 1085
+
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_1.xml b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_1.xml
index e3a0f659..67af050b 100644
--- a/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_1.xml
+++ b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_1.xml
@@ -30,7 +30,10 @@
-
+
@@ -49,18 +52,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -68,10 +59,6 @@
-
-
-
-
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_2.xml b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_2.xml
index 96738c76..cb0ee7b2 100644
--- a/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_2.xml
+++ b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_2.xml
@@ -29,7 +29,10 @@
-
+
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_3.xml b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_3.xml
index 5b25ddd9..8e2cedea 100644
--- a/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_3.xml
+++ b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_SP_9999_3.xml
@@ -24,14 +24,20 @@
-
+
-
+
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_TC_9999_1.xml b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_TC_9999_1.xml
new file mode 100644
index 00000000..3c4e44b9
--- /dev/null
+++ b/sfera-mock/src/main/resources/static_sfera_resources/9999_Mixed_journey/SFERA_TC_9999_1.xml
@@ -0,0 +1,8 @@
+
+
+ 1085
+
+
+
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/T5_breaking_series/SFERA_JP_T5.xml b/sfera-mock/src/main/resources/static_sfera_resources/T5_breaking_series/SFERA_JP_T5.xml
new file mode 100644
index 00000000..59c002c1
--- /dev/null
+++ b/sfera-mock/src/main/resources/static_sfera_resources/T5_breaking_series/SFERA_JP_T5.xml
@@ -0,0 +1,35 @@
+
+
+
+
+ 1085
+ T5
+ 2022-01-04
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1185
+
+
+
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/T5_breaking_series/SFERA_SP_T5_1.xml b/sfera-mock/src/main/resources/static_sfera_resources/T5_breaking_series/SFERA_SP_T5_1.xml
new file mode 100644
index 00000000..778f64ca
--- /dev/null
+++ b/sfera-mock/src/main/resources/static_sfera_resources/T5_breaking_series/SFERA_SP_T5_1.xml
@@ -0,0 +1,252 @@
+
+
+
+ 0085
+
+
+
+
+
+ CH
+ 3002
+
+
+
+
+
+
+ CH
+ 3003
+
+
+
+
+
+
+ CH
+ 3004
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CH
+ 3002
+
+
+
+
+
+
+
+
+
+ CH
+ 3003
+
+
+
+
+
+
+
+
+
+ CH
+ 3004
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/T5_breaking_series/SFERA_TC_T5_1.xml b/sfera-mock/src/main/resources/static_sfera_resources/T5_breaking_series/SFERA_TC_T5_1.xml
new file mode 100644
index 00000000..51ce7bf2
--- /dev/null
+++ b/sfera-mock/src/main/resources/static_sfera_resources/T5_breaking_series/SFERA_TC_T5_1.xml
@@ -0,0 +1,8 @@
+
+
+ 1185
+
+
+