diff --git a/das_client/assets/icons/icon_balise.svg b/das_client/assets/icons/icon_balise.svg new file mode 100644 index 00000000..7075001a --- /dev/null +++ b/das_client/assets/icons/icon_balise.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/das_client/assets/icons/icon_km_indicator.svg b/das_client/assets/icons/icon_km_indicator.svg new file mode 100644 index 00000000..cbe7719a --- /dev/null +++ b/das_client/assets/icons/icon_km_indicator.svg @@ -0,0 +1,4 @@ + + + + diff --git a/das_client/assets/icons/icon_tram_area.svg b/das_client/assets/icons/icon_tram_area.svg new file mode 100644 index 00000000..2b994738 --- /dev/null +++ b/das_client/assets/icons/icon_tram_area.svg @@ -0,0 +1,4 @@ + + + + diff --git a/das_client/assets/icons/icon_whistle.svg b/das_client/assets/icons/icon_whistle.svg new file mode 100644 index 00000000..14674ab4 --- /dev/null +++ b/das_client/assets/icons/icon_whistle.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 8fe991f7..fc1f2445 100644 --- a/das_client/integration_test/test/train_journey_table_test.dart +++ b/das_client/integration_test/test/train_journey_table_test.dart @@ -1,4 +1,5 @@ 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/balise_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/cells/bracket_station_cell_body.dart'; import 'package:das_client/app/pages/journey/train_journey/widgets/table/cells/graduated_speeds_cell_body.dart'; @@ -8,6 +9,8 @@ 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/table/tram_area_row.dart'; +import 'package:das_client/app/pages/journey/train_journey/widgets/table/whistle_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'; @@ -20,6 +23,94 @@ import '../util/test_utils.dart'; void main() { group('train journey table test', () { + + testWidgets('test balise multiple level crossings', (tester) async { + await prepareAndStartApp(tester); + + // load train journey by filling out train selection page + await _loadTrainJourney(tester, trainNumber: 'T7'); + + final scrollableFinder = find.byType(ListView); + expect(scrollableFinder, findsOneWidget); + + final baliseMultiLevelCrossing = findDASTableRowByText('(2 ${l10n.p_train_journey_table_level_crossing})'); + expect(baliseMultiLevelCrossing, findsOneWidget); + + final baliseIcon = find.descendant(of: baliseMultiLevelCrossing, matching: find.byKey(BaliseRow.baliseIconKey)); + expect(baliseIcon, findsOneWidget); + }); + + testWidgets('test whistle and tram area', (tester) async { + await prepareAndStartApp(tester); + + // load train journey by filling out train selection page + await _loadTrainJourney(tester, trainNumber: 'T7'); + + final scrollableFinder = find.byType(ListView); + expect(scrollableFinder, findsOneWidget); + + final whistleRow = findDASTableRowByText('39.6'); + expect(whistleRow, findsOneWidget); + + final whistleIcon = find.descendant(of: whistleRow, matching: find.byKey(WhistleRow.whistleIconKey)); + expect(whistleIcon, findsOneWidget); + + final tramAreaRow = findDASTableRowByText('km 37.8-36.8'); + expect(tramAreaRow, findsOneWidget); + + final tramAreaIcon = find.descendant(of: tramAreaRow, matching: find.byKey(TramAreaRow.tramAreaIconKey)); + expect(tramAreaIcon, findsOneWidget); + + final tramAreaDescription = find.descendant(of: tramAreaRow, matching: find.text('6 TS')); + expect(tramAreaDescription, findsOneWidget); + }); + + testWidgets('test balise and level crossing groups expand / collapse', (tester) async { + await prepareAndStartApp(tester); + + // load train journey by filling out train selection page + await _loadTrainJourney(tester, trainNumber: 'T7'); + + final scrollableFinder = find.byType(ListView); + expect(scrollableFinder, findsOneWidget); + + final groupOf5BaliseRow = findDASTableRowByText('41.6'); + expect(groupOf5BaliseRow, findsOneWidget); + + final countText = find.descendant(of: groupOf5BaliseRow, matching: find.text('5')); + expect(countText, findsOneWidget); + + final levelCrossingText = find.descendant(of: groupOf5BaliseRow, matching: find.text(l10n.p_train_journey_table_level_crossing)); + expect(levelCrossingText, findsOneWidget); + + var detailRowBalise = findDASTableRowByText('41.552'); + var detailRowLevelCrossing = findDASTableRowByText('41.492'); + + expect(detailRowLevelCrossing, findsNothing); + expect(detailRowBalise, findsNothing); + + // expand group + await tapElement(tester, groupOf5BaliseRow); + + detailRowBalise = findDASTableRowByText('41.552'); + detailRowLevelCrossing = findDASTableRowByText('41.492'); + + expect(detailRowLevelCrossing, findsOneWidget); + expect(detailRowBalise, findsOneWidget); + + expect(find.descendant(of: detailRowBalise, matching: find.byKey(BaliseRow.baliseIconKey)), findsOneWidget); + expect(find.descendant(of: detailRowLevelCrossing, matching: find.text(l10n.p_train_journey_table_level_crossing)), findsOneWidget); + + // collapse group + await tapElement(tester, groupOf5BaliseRow); + + detailRowBalise = findDASTableRowByText('41.552'); + detailRowLevelCrossing = findDASTableRowByText('41.492'); + + expect(detailRowLevelCrossing, findsNothing); + expect(detailRowBalise, findsNothing); + }); + testWidgets('test breaking series defaults to ??', (tester) async { await prepareAndStartApp(tester); diff --git a/das_client/l10n/strings_de.arb b/das_client/l10n/strings_de.arb index 7bddbe1d..994e8246 100644 --- a/das_client/l10n/strings_de.arb +++ b/das_client/l10n/strings_de.arb @@ -17,6 +17,8 @@ "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", + "p_train_journey_table_level_crossing": "BUe", + "p_train_journey_table_tram_area": "TS", "w_navigation_drawer_fahrtinfo_title": "Fahrtinfo", "w_navigation_drawer_links_title": "Links", "w_navigation_drawer_settings_title": "Einstellungen", diff --git a/das_client/lib/app/bloc/train_journey_cubit.dart b/das_client/lib/app/bloc/train_journey_cubit.dart index 7261e76c..965a34a0 100644 --- a/das_client/lib/app/bloc/train_journey_cubit.dart +++ b/das_client/lib/app/bloc/train_journey_cubit.dart @@ -117,6 +117,10 @@ class TrainJourneyCubit extends Cubit { void updateBreakSeries(BreakSeries selectedBreakSeries) { _settingsSubject.add(_settingsSubject.value.copyWith(selectedBreakSeries: selectedBreakSeries)); } + + void updateExpandedGroups(List expandedGroups) { + _settingsSubject.add(_settingsSubject.value.copyWith(expandedGroups: expandedGroups)); + } } 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 index 0df6482f..ffbdc124 100644 --- a/das_client/lib/app/model/train_journey_settings.dart +++ b/das_client/lib/app/model/train_journey_settings.dart @@ -3,13 +3,16 @@ import 'package:das_client/model/journey/break_series.dart'; class TrainJourneySettings { TrainJourneySettings({ this.selectedBreakSeries, + this.expandedGroups = const [], }); final BreakSeries? selectedBreakSeries; + final List expandedGroups; - TrainJourneySettings copyWith({BreakSeries? selectedBreakSeries}) { + TrainJourneySettings copyWith({BreakSeries? selectedBreakSeries, List? expandedGroups}) { return TrainJourneySettings( selectedBreakSeries: selectedBreakSeries ?? this.selectedBreakSeries, + expandedGroups: expandedGroups ?? this.expandedGroups, ); } } diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/balise_level_crossing_group_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/balise_level_crossing_group_row.dart new file mode 100644 index 00000000..f6a75c17 --- /dev/null +++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/balise_level_crossing_group_row.dart @@ -0,0 +1,68 @@ +import 'package:das_client/app/i18n/i18n.dart'; +import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.dart'; +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/balise.dart'; +import 'package:das_client/model/journey/balise_level_crossing_group.dart'; +import 'package:das_client/model/journey/level_crossing.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart'; + +class BaliseLevelCrossingGroupRow extends BaseRowBuilder { + static const Key baliseIconKey = Key('balise_icon_key'); + + BaliseLevelCrossingGroupRow({ + required super.metadata, + required super.data, + required super.settings, + super.trackEquipmentRenderData, + super.onTap, + }); + + @override + DASTableCell informationCell(BuildContext context) { + if (_baliseCount == 0) { + return DASTableCell( + child: Text('$_levelCrossingCount ${context.l10n.p_train_journey_table_level_crossing}'), + alignment: Alignment.centerLeft, + ); + } else if (_baliseCount == 1 && _levelCrossingCount > 1) { + return DASTableCell( + child: Text('($_levelCrossingCount ${context.l10n.p_train_journey_table_level_crossing})'), + alignment: Alignment.centerRight, + ); + } else { + return DASTableCell( + child: Row( + children: [ + Text(_levelCrossingCount > 0 ? context.l10n.p_train_journey_table_level_crossing : ''), + Spacer(), + Text(_baliseCount.toString()), + ], + ), + alignment: Alignment.centerRight, + ); + } + } + + @override + DASTableCell iconsCell2(BuildContext context) { + if (_baliseCount > 0) { + return DASTableCell( + padding: EdgeInsets.all(sbbDefaultSpacing * 0.25), + child: SvgPicture.asset( + AppAssets.iconBalise, + key: baliseIconKey, + ), + alignment: Alignment.centerLeft, + ); + } else { + return DASTableCell.empty(); + } + } + + int get _baliseCount => data.groupedElements.whereType().length; + + int get _levelCrossingCount => data.groupedElements.whereType().length; +} diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/balise_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/balise_row.dart new file mode 100644 index 00000000..3a129eea --- /dev/null +++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/balise_row.dart @@ -0,0 +1,60 @@ +import 'package:das_client/app/i18n/i18n.dart'; +import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.dart'; +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/balise.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart'; + +class BaliseRow extends BaseRowBuilder { + static const Key baliseIconKey = Key('balise_icon_key'); + + BaliseRow({ + required super.metadata, + required super.data, + required super.settings, + super.trackEquipmentRenderData, + super.isGrouped, + }); + + @override + DASTableCell kilometreCell(BuildContext context) { + return isGrouped ? DASTableCell.empty() : super.kilometreCell(context); + } + + @override + DASTableCell timeCell(BuildContext context) { + if (!isGrouped) { + return DASTableCell.empty(); + } + + if (data.kilometre.isEmpty) { + return DASTableCell.empty(color: specialCellColor); + } else { + return DASTableCell(color: specialCellColor, child: Text(data.kilometre[0].toStringAsFixed(3))); + } + } + + @override + DASTableCell informationCell(BuildContext context) { + return DASTableCell( + child: Text(data.amountLevelCrossings > 1 + ? '(${data.amountLevelCrossings} ${context.l10n.p_train_journey_table_level_crossing})' + : ''), + alignment: Alignment.centerRight, + ); + } + + @override + DASTableCell iconsCell2(BuildContext context) { + return DASTableCell( + padding: EdgeInsets.all(sbbDefaultSpacing * 0.25), + child: SvgPicture.asset( + AppAssets.iconBalise, + key: baliseIconKey, + ), + alignment: Alignment.centerLeft, + ); + } +} diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/base_row_builder.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/base_row_builder.dart index 5f1d96fd..45f1793f 100644 --- a/das_client/lib/app/pages/journey/train_journey/widgets/table/base_row_builder.dart +++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/base_row_builder.dart @@ -21,6 +21,8 @@ class BaseRowBuilder extends DASTableRowBuilder { this.trackEquipmentRenderData = const TrackEquipmentRenderData(), this.defaultAlignment = Alignment.bottomCenter, this.rowColor, + this.onTap, + this.isGrouped = false, }); final Alignment defaultAlignment; @@ -29,12 +31,15 @@ class BaseRowBuilder extends DASTableRowBuilder { final T data; final TrackEquipmentRenderData trackEquipmentRenderData; final TrainJourneySettings settings; + final VoidCallback? onTap; + final bool isGrouped; @override DASTableRow build(BuildContext context) { return DASTableRow( height: height, color: rowColor, + onTap: onTap, cells: [ kilometreCell(context), timeCell(context), diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/track_equipment_render_data.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/track_equipment_render_data.dart index 3f603d0a..d9d4fb29 100644 --- a/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/track_equipment_render_data.dart +++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/track_equipment_render_data.dart @@ -4,7 +4,7 @@ import 'package:das_client/app/pages/journey/train_journey/widgets/table/service import 'package:das_client/model/journey/base_data.dart'; import 'package:das_client/model/journey/cab_signaling.dart'; import 'package:das_client/model/journey/datatype.dart'; -import 'package:das_client/model/journey/journey.dart'; +import 'package:das_client/model/journey/metadata.dart'; import 'package:das_client/model/journey/track_equipment.dart'; /// Data class to hold all the information to visualize the track equipment. @@ -23,16 +23,16 @@ class TrackEquipmentRenderData { final bool isConventionalExtendedSpeedBorder; final TrackEquipmentType? trackEquipmentType; - factory TrackEquipmentRenderData.from(Journey journey, int index) { - final data = journey.data[index]; - final nonStandardTrackEquipmentSegments = journey.metadata.nonStandardTrackEquipmentSegments; + factory TrackEquipmentRenderData.from(List rowData, Metadata metadata, int index) { + final data = rowData[index]; + final nonStandardTrackEquipmentSegments = metadata.nonStandardTrackEquipmentSegments; final trackEquipment = nonStandardTrackEquipmentSegments.appliesToOrder(data.order).firstOrNull; if (trackEquipment == null || !trackEquipment.isEtcsL2Segment) return TrackEquipmentRenderData(); return TrackEquipmentRenderData( trackEquipmentType: trackEquipment.type, - cumulativeHeight: _calculateTrackEquipmentCumulativeHeight(journey, trackEquipment, index), - isConventionalExtendedSpeedBorder: _isConventionalExtendedSpeedBorder(journey, index), + cumulativeHeight: _calculateTrackEquipmentCumulativeHeight(rowData, metadata, trackEquipment, index), + isConventionalExtendedSpeedBorder: _isConventionalExtendedSpeedBorder(rowData, metadata, index), isCABStart: data is CABSignaling ? data.isStart : false, isCABEnd: data is CABSignaling ? data.isEnd : false, ); @@ -40,13 +40,12 @@ class TrackEquipmentRenderData { /// calculates the cumulative height of the track equipment "line" of previous rows with the same type as given [trackEquipment]. static double _calculateTrackEquipmentCumulativeHeight( - Journey journey, NonStandardTrackEquipmentSegment trackEquipment, int index) { + List rowData, Metadata metadata, NonStandardTrackEquipmentSegment trackEquipment, int index) { var cumulativeHeight = 0.0; var searchIndex = index - 1; while (searchIndex >= 0) { - final data = journey.data[searchIndex]; - final testTrackEquipment = - journey.metadata.nonStandardTrackEquipmentSegments.appliesToOrder(data.order).firstOrNull; + final data = rowData[searchIndex]; + final testTrackEquipment = metadata.nonStandardTrackEquipmentSegments.appliesToOrder(data.order).firstOrNull; if (testTrackEquipment == null || testTrackEquipment.type != trackEquipment.type) { break; @@ -55,7 +54,7 @@ class TrackEquipmentRenderData { cumulativeHeight += _rowHeight(data); // if is conventional extended speed border, reduce by it's height as it is not part of the dashed line. - if (_isConventionalExtendedSpeedBorder(journey, searchIndex)) { + if (_isConventionalExtendedSpeedBorder(rowData, metadata, searchIndex)) { cumulativeHeight -= TrackEquipmentCellBody.conventionalExtendedSpeedBorderSpace; } @@ -77,12 +76,12 @@ class TrackEquipmentRenderData { } /// checks if between current and previous track equipment is a border between extended and conventional speed. - static bool _isConventionalExtendedSpeedBorder(Journey journey, int index) { + static bool _isConventionalExtendedSpeedBorder(List rowData, Metadata metadata, int index) { if (index < 1) return false; - final nonStandardTrackEquipmentSegments = journey.metadata.nonStandardTrackEquipmentSegments; - final currentData = journey.data[index]; - final previousData = journey.data[index - 1]; + final nonStandardTrackEquipmentSegments = metadata.nonStandardTrackEquipmentSegments; + final currentData = rowData[index]; + final previousData = rowData[index - 1]; final trackEquipment = nonStandardTrackEquipmentSegments.appliesToOrder(currentData.order).firstOrNull; final previousTrackEquipment = nonStandardTrackEquipmentSegments.appliesToOrder(previousData.order).firstOrNull; diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/level_crossing_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/level_crossing_row.dart new file mode 100644 index 00000000..dbce46b6 --- /dev/null +++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/level_crossing_row.dart @@ -0,0 +1,41 @@ +import 'package:das_client/app/i18n/i18n.dart'; +import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.dart'; +import 'package:das_client/app/widgets/table/das_table_cell.dart'; +import 'package:das_client/model/journey/level_crossing.dart'; +import 'package:flutter/material.dart'; + +class LevelCrossingRow extends BaseRowBuilder { + LevelCrossingRow({ + required super.metadata, + required super.data, + required super.settings, + super.trackEquipmentRenderData, + super.isGrouped, + }); + + @override + DASTableCell kilometreCell(BuildContext context) { + return isGrouped ? DASTableCell.empty() : super.kilometreCell(context); + } + + @override + DASTableCell timeCell(BuildContext context) { + if (!isGrouped) { + return DASTableCell.empty(); + } + + if (data.kilometre.isEmpty) { + return DASTableCell.empty(color: specialCellColor); + } else { + return DASTableCell(color: specialCellColor, child: Text(data.kilometre[0].toStringAsFixed(3))); + } + } + + @override + DASTableCell informationCell(BuildContext context) { + return DASTableCell( + child: Text(context.l10n.p_train_journey_table_level_crossing), + alignment: Alignment.centerLeft, + ); + } +} 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 85e0eec7..a7dd616c 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 @@ -1,9 +1,14 @@ import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.dart'; +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/speed_change.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart'; class SpeedChangeRow extends BaseRowBuilder { + static const Key kmIndicatorKey = Key('km_indicator_key'); + SpeedChangeRow({ required super.metadata, required super.data, @@ -17,4 +22,16 @@ class SpeedChangeRow extends BaseRowBuilder { child: Text(data.text ?? ''), ); } + + @override + DASTableCell iconsCell2(BuildContext context) { + return DASTableCell( + padding: EdgeInsets.all(sbbDefaultSpacing * 0.25), + child: SvgPicture.asset( + AppAssets.iconKmIndicator, + key: kmIndicatorKey, + ), + alignment: Alignment.centerLeft, + ); + } } diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/tram_area_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/tram_area_row.dart new file mode 100644 index 00000000..fa522188 --- /dev/null +++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/tram_area_row.dart @@ -0,0 +1,46 @@ +import 'package:das_client/app/i18n/i18n.dart'; +import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.dart'; +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/tram_area.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart'; + +class TramAreaRow extends BaseRowBuilder { + static const Key tramAreaIconKey = Key('tram_area_icon_key'); + + TramAreaRow({ + required super.metadata, + required super.data, + required super.settings, + super.trackEquipmentRenderData, + }); + + @override + DASTableCell informationCell(BuildContext context) { + return DASTableCell( + child: Row( + children: [ + Text( + '${context.l10n.p_train_journey_table_kilometre_label} ${data.kilometre[0].toStringAsFixed(1)}-${data.endKilometre.toStringAsFixed(1)}'), + Spacer(), + Text( + '${data.amountTramSignals > 1 ? data.amountTramSignals : ''} ${context.l10n.p_train_journey_table_tram_area}'), + ], + ), + ); + } + + @override + DASTableCell iconsCell2(BuildContext context) { + return DASTableCell( + padding: EdgeInsets.all(sbbDefaultSpacing * 0.25), + child: SvgPicture.asset( + AppAssets.iconTramArea, + key: tramAreaIconKey, + ), + alignment: Alignment.centerLeft, + ); + } +} diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/whistle_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/whistle_row.dart new file mode 100644 index 00000000..9230430b --- /dev/null +++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/whistle_row.dart @@ -0,0 +1,30 @@ +import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.dart'; +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/whistles.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 WhistleRow extends BaseRowBuilder { + static const Key whistleIconKey = Key('whistle_icon_key'); + + WhistleRow({ + required super.metadata, + required super.data, + required super.settings, + super.trackEquipmentRenderData, + }); + + @override + DASTableCell iconsCell2(BuildContext context) { + return DASTableCell( + padding: EdgeInsets.all(sbbDefaultSpacing * 0.25), + child: SvgPicture.asset( + AppAssets.iconWhistle, + key: whistleIconKey, + ), + alignment: Alignment.centerLeft, + ); + } +} 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 29e7ed63..1c534dd9 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 @@ -3,28 +3,39 @@ 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/balise_level_crossing_group_row.dart'; +import 'package:das_client/app/pages/journey/train_journey/widgets/table/balise_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/cells/track_equipment_render_data.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/level_crossing_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'; import 'package:das_client/app/pages/journey/train_journey/widgets/table/signal_row.dart'; import 'package:das_client/app/pages/journey/train_journey/widgets/table/speed_change_row.dart'; +import 'package:das_client/app/pages/journey/train_journey/widgets/table/tram_area_row.dart'; +import 'package:das_client/app/pages/journey/train_journey/widgets/table/whistle_row.dart'; 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/balise.dart'; +import 'package:das_client/model/journey/balise_level_crossing_group.dart'; +import 'package:das_client/model/journey/base_data_extension.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'; +import 'package:das_client/model/journey/level_crossing.dart'; 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:das_client/model/journey/tram_area.dart'; +import 'package:das_client/model/journey/whistles.dart'; import 'package:flutter/material.dart'; import 'package:rxdart/rxdart.dart'; import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart'; @@ -64,10 +75,15 @@ class TrainJourney extends StatelessWidget { } List _rows(BuildContext context, Journey journey, TrainJourneySettings settings) { - return List.generate(journey.data.length, (index) { - final rowData = journey.data[index]; + final rows = journey.data.groupBaliseAndLeveLCrossings(settings); - final renderData = TrackEquipmentRenderData.from(journey, index); + final groupedRows = + rows.whereType().map((it) => it.groupedElements).expand((it) => it).toList(); + + return List.generate(rows.length, (index) { + final rowData = rows[index]; + + final renderData = TrackEquipmentRenderData.from(rows, journey.metadata, index); switch (rowData.type) { case Datatype.servicePoint: return ServicePointRow( @@ -125,6 +141,44 @@ class TrainJourney extends StatelessWidget { settings: settings, trackEquipmentRenderData: renderData) .build(context); + case Datatype.balise: + return BaliseRow( + metadata: journey.metadata, + data: rowData as Balise, + settings: settings, + trackEquipmentRenderData: renderData, + isGrouped: groupedRows.contains(rowData)) + .build(context); + case Datatype.whistle: + return WhistleRow( + metadata: journey.metadata, + data: rowData as Whistle, + settings: settings, + trackEquipmentRenderData: renderData) + .build(context); + case Datatype.levelCrossing: + return LevelCrossingRow( + metadata: journey.metadata, + data: rowData as LevelCrossing, + settings: settings, + trackEquipmentRenderData: renderData, + isGrouped: groupedRows.contains(rowData)) + .build(context); + case Datatype.tramArea: + return TramAreaRow( + metadata: journey.metadata, + data: rowData as TramArea, + settings: settings, + trackEquipmentRenderData: renderData) + .build(context); + case Datatype.baliseLevelCrossingGroup: + return BaliseLevelCrossingGroupRow( + metadata: journey.metadata, + data: rowData as BaliseLevelCrossingGroup, + settings: settings, + trackEquipmentRenderData: renderData, + onTap: () => _onBaliseLevelCrossingGroupTap(context, rowData, settings), + ).build(context); } }); } @@ -166,6 +220,20 @@ class TrainJourney extends StatelessWidget { ]; } + void _onBaliseLevelCrossingGroupTap( + BuildContext context, BaliseLevelCrossingGroup group, TrainJourneySettings settings) { + final trainJourneyCubit = context.trainJourneyCubit; + + final newList = List.from(settings.expandedGroups); + if (settings.expandedGroups.contains(group.order)) { + newList.remove(group.order); + } else { + newList.add(group.order); + } + + trainJourneyCubit.updateExpandedGroups(newList); + } + void _onBreakSeriesTap(BuildContext context, Journey journey, TrainJourneySettings settings) { final trainJourneyCubit = context.trainJourneyCubit; diff --git a/das_client/lib/app/widgets/assets.dart b/das_client/lib/app/widgets/assets.dart index 5f6020b1..c90d5a5c 100644 --- a/das_client/lib/app/widgets/assets.dart +++ b/das_client/lib/app/widgets/assets.dart @@ -16,4 +16,8 @@ class AppAssets { static const iconCabStart = '$_iconsDir/icon_cab_start.svg'; static const iconCabEnd = '$_iconsDir/icon_cab_end.svg'; static const iconIndicatorChecked = '$_iconsDir/icon_indicator_checked.svg'; + static const iconBalise = '$_iconsDir/icon_balise.svg'; + static const iconKmIndicator = '$_iconsDir/icon_km_indicator.svg'; + static const iconWhistle = '$_iconsDir/icon_whistle.svg'; + static const iconTramArea = '$_iconsDir/icon_tram_area.svg'; } diff --git a/das_client/lib/app/widgets/table/das_table.dart b/das_client/lib/app/widgets/table/das_table.dart index 3655c7d6..735539c4 100644 --- a/das_client/lib/app/widgets/table/das_table.dart +++ b/das_client/lib/app/widgets/table/das_table.dart @@ -140,13 +140,16 @@ class DASTable extends StatelessWidget { Widget _dataRow(DASTableRow row) { final visibleColumns = columns.where((column) => column.isVisible).toList(growable: false); final visibleCells = row.cells.whereIndexed((index, _) => columns[index].isVisible).toList(growable: false); - return _FixedHeightRow( - height: row.height, - children: List.generate(visibleColumns.length, (index) { - final cell = visibleCells[index]; - final column = visibleColumns[index]; - return _dataCell(cell, column, row, isLast: visibleColumns.length - 1 == index); - }), + return InkWell( + onTap: row.onTap, + child: _FixedHeightRow( + height: row.height, + children: List.generate(visibleColumns.length, (index) { + final cell = visibleCells[index]; + final column = visibleColumns[index]; + return _dataCell(cell, column, row, isLast: visibleColumns.length - 1 == index); + }), + ), ); } diff --git a/das_client/lib/app/widgets/table/das_table_row.dart b/das_client/lib/app/widgets/table/das_table_row.dart index 4509c879..dbe2be25 100644 --- a/das_client/lib/app/widgets/table/das_table_row.dart +++ b/das_client/lib/app/widgets/table/das_table_row.dart @@ -13,7 +13,7 @@ abstract class DASTableRowBuilder { /// Represents a row in the [DASTable] containing cells. @immutable class DASTableRow { - const DASTableRow({required this.cells, required this.height, this.color}); + const DASTableRow({required this.cells, required this.height, this.color, this.onTap}); /// Height of the row final double height; @@ -22,4 +22,6 @@ class DASTableRow { final Color? color; final List cells; + + final VoidCallback? onTap; } diff --git a/das_client/lib/model/journey/balise.dart b/das_client/lib/model/journey/balise.dart new file mode 100644 index 00000000..a4da10d4 --- /dev/null +++ b/das_client/lib/model/journey/balise.dart @@ -0,0 +1,28 @@ +import 'package:das_client/model/journey/base_data.dart'; +import 'package:das_client/model/journey/datatype.dart'; +import 'package:das_client/model/journey/level_crossing.dart'; + +class Balise extends BaseData { + Balise({ + required super.order, + required super.kilometre, + required this.amountLevelCrossings, + super.speedData, + }) : super(type: Datatype.balise); + + final int amountLevelCrossings; + + @override + bool get canGroup => true; + + @override + bool canGroupWith(BaseData other) { + if (other is LevelCrossing) { + return true; + } else if (other is Balise) { + return amountLevelCrossings == 1 && other.amountLevelCrossings == 1; + } else { + return false; + } + } +} diff --git a/das_client/lib/model/journey/balise_level_crossing_group.dart b/das_client/lib/model/journey/balise_level_crossing_group.dart new file mode 100644 index 00000000..0f452f71 --- /dev/null +++ b/das_client/lib/model/journey/balise_level_crossing_group.dart @@ -0,0 +1,13 @@ +import 'package:das_client/model/journey/base_data.dart'; +import 'package:das_client/model/journey/datatype.dart'; + +class BaliseLevelCrossingGroup extends BaseData { + BaliseLevelCrossingGroup({ + required super.order, + required super.kilometre, + required this.groupedElements, + super.speedData, + }) : super(type: Datatype.baliseLevelCrossingGroup); + + final List groupedElements; +} diff --git a/das_client/lib/model/journey/base_data.dart b/das_client/lib/model/journey/base_data.dart index 03f81fca..4cc013d4 100644 --- a/das_client/lib/model/journey/base_data.dart +++ b/das_client/lib/model/journey/base_data.dart @@ -1,3 +1,4 @@ +import 'package:das_client/model/journey/base_data_extension.dart'; import 'package:das_client/model/journey/datatype.dart'; import 'package:das_client/model/journey/speed_data.dart'; @@ -27,4 +28,16 @@ abstract class BaseData implements Comparable { /// Used for comparing if [order] is equal. /// If [orderPriority] is smaller, this is ordered before other, a bigger value is ordered after other. int get orderPriority => 0; + + /// Used to indicate that this element can be grouped together + /// [canGroup] and [canGroupWith] needs to be overridden for elements to be able to be grouped + /// Grouping is done in [BaseDataExtension] + bool get canGroup => false; + + /// Used to check if the current element is allowed to be grouped with the other element + /// Only gets checked if [canGroup] is already true + /// Grouping is done in [BaseDataExtension] + bool canGroupWith(BaseData other) { + return false; + } } diff --git a/das_client/lib/model/journey/base_data_extension.dart b/das_client/lib/model/journey/base_data_extension.dart new file mode 100644 index 00000000..b8a428a4 --- /dev/null +++ b/das_client/lib/model/journey/base_data_extension.dart @@ -0,0 +1,50 @@ +import 'package:das_client/app/model/train_journey_settings.dart'; +import 'package:das_client/model/journey/balise_level_crossing_group.dart'; +import 'package:das_client/model/journey/base_data.dart'; +import 'package:das_client/model/journey/datatype.dart'; + +extension BaseDataExtension on List { + List groupBaliseAndLeveLCrossings(TrainJourneySettings settings) { + final List resultList = []; + + for (int i = 0; i < length; i++) { + final currentElement = this[i]; + if (!currentElement.canGroup) { + // Just add elements to the result that are unable to be grouped + resultList.add(currentElement); + continue; + } + + final groupedElements = [currentElement]; + // check the next elements if they can be grouped with the currentElement. + for (int j = i + 1; j < length; j++) { + final nextElement = this[j]; + if (nextElement.canGroup && currentElement.canGroupWith(nextElement)) { + groupedElements.add(nextElement); + } else { + // Stop once we reach a element that is unable to be grouped + break; + } + } + + if (groupedElements.length > 1 && [Datatype.balise, Datatype.levelCrossing].contains(currentElement.type)) { + // Add a group header if we have more then 1 element + final group = BaliseLevelCrossingGroup( + order: groupedElements[0].order, kilometre: groupedElements[0].kilometre, groupedElements: groupedElements); + resultList.add(group); + + // Add all the elements if the group is currently expanded + if (settings.expandedGroups.contains(group.order)) { + resultList.addAll(groupedElements); + } + + // skip already checked elements + i += groupedElements.length - 1; + } else { + resultList.add(currentElement); + } + } + + return resultList; + } +} diff --git a/das_client/lib/model/journey/datatype.dart b/das_client/lib/model/journey/datatype.dart index e5771a55..a68d676f 100644 --- a/das_client/lib/model/journey/datatype.dart +++ b/das_client/lib/model/journey/datatype.dart @@ -6,5 +6,10 @@ enum Datatype { cabSignaling, additionalSpeedRestriction, connectionTrack, - speedChange; + speedChange, + whistle, + levelCrossing, + tramArea, + balise, + baliseLevelCrossingGroup; } diff --git a/das_client/lib/model/journey/level_crossing.dart b/das_client/lib/model/journey/level_crossing.dart new file mode 100644 index 00000000..856bf5ae --- /dev/null +++ b/das_client/lib/model/journey/level_crossing.dart @@ -0,0 +1,18 @@ +import 'package:das_client/model/journey/base_data.dart'; +import 'package:das_client/model/journey/datatype.dart'; + +class LevelCrossing extends BaseData { + LevelCrossing({ + required super.order, + required super.kilometre, + super.speedData, + }) : super(type: Datatype.levelCrossing); + + @override + bool get canGroup => true; + + @override + bool canGroupWith(BaseData other) { + return [Datatype.balise, Datatype.levelCrossing].contains(other.type); + } +} diff --git a/das_client/lib/model/journey/tram_area.dart b/das_client/lib/model/journey/tram_area.dart new file mode 100644 index 00000000..99de7395 --- /dev/null +++ b/das_client/lib/model/journey/tram_area.dart @@ -0,0 +1,15 @@ +import 'package:das_client/model/journey/base_data.dart'; +import 'package:das_client/model/journey/datatype.dart'; + +class TramArea extends BaseData { + TramArea( + {required super.order, + required super.kilometre, + required this.endKilometre, + required this.amountTramSignals, + super.speedData}) + : super(type: Datatype.tramArea); + + final double endKilometre; + final int amountTramSignals; +} diff --git a/das_client/lib/model/journey/whistles.dart b/das_client/lib/model/journey/whistles.dart new file mode 100644 index 00000000..18ce7d43 --- /dev/null +++ b/das_client/lib/model/journey/whistles.dart @@ -0,0 +1,6 @@ +import 'package:das_client/model/journey/base_data.dart'; +import 'package:das_client/model/journey/datatype.dart'; + +class Whistle extends BaseData { + Whistle({required super.order, required super.kilometre, super.speedData}) : super(type: Datatype.whistle); +} 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 1bf9d776..f085b889 100644 --- a/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart +++ b/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart @@ -1,6 +1,7 @@ import 'package:collection/collection.dart'; 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/balise.dart' as journey_balise; 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'; @@ -10,6 +11,7 @@ import 'package:das_client/model/journey/curve_point.dart'; import 'package:das_client/model/journey/datatype.dart'; import 'package:das_client/model/journey/graduated_station_speeds.dart'; import 'package:das_client/model/journey/journey.dart'; +import 'package:das_client/model/journey/level_crossing.dart'; import 'package:das_client/model/journey/metadata.dart'; import 'package:das_client/model/journey/protection_section.dart'; import 'package:das_client/model/journey/service_point.dart'; @@ -19,7 +21,9 @@ import 'package:das_client/model/journey/speed_data.dart'; import 'package:das_client/model/journey/station_speed_data.dart'; import 'package:das_client/model/journey/track_equipment.dart'; import 'package:das_client/model/journey/train_series.dart'; +import 'package:das_client/model/journey/tram_area.dart'; import 'package:das_client/model/journey/velocity.dart'; +import 'package:das_client/model/journey/whistles.dart'; import 'package:das_client/model/localized_string.dart'; import 'package:das_client/sfera/src/mapper/track_equipment_mapper.dart'; import 'package:das_client/sfera/src/model/enums/length_type.dart'; @@ -88,6 +92,15 @@ class SferaModelMapper { journeyData.addAll(connectionTracks); journeyData.addAll(newLineSpeeds); + final balises = _parseBalise(segmentProfile, segmentIndex, kilometreMap); + journeyData.addAll(balises); + + final whistles = _parseWhistle(segmentProfile, segmentIndex, kilometreMap); + journeyData.addAll(whistles); + + final levelCrossings = _parseLevelCrossings(segmentProfile, segmentIndex, kilometreMap); + journeyData.addAll(levelCrossings); + final timingPoints = segmentProfile.points?.timingPoints.toList() ?? []; for (final tpConstraint in segmentProfileList.timingPointsContraints) { @@ -116,6 +129,9 @@ class SferaModelMapper { _parseAndAddProtectionSections(journeyData, segmentIndex, segmentProfile, kilometreMap); } + final tramAreas = _parseTramAreas(segmentProfiles); + journeyData.addAll(tramAreas); + final additionalSpeedRestrictions = _parseAdditionalSpeedRestrictions(journeyProfile, segmentProfiles); for (final restriction in additionalSpeedRestrictions) { journeyData.add(AdditionalSpeedRestrictionData( @@ -440,4 +456,112 @@ class SferaModelMapper { it.versionMajor == firstTrainRef.versionMajor && it.versionMinor == firstTrainRef.versionMinor); } + + static Iterable _parseBalise( + SegmentProfile segmentProfile, int segmentIndex, Map> kilometreMap) { + final balises = segmentProfile.points?.balise ?? []; + return balises.map((balise) { + return journey_balise.Balise( + order: calculateOrder(segmentIndex, balise.location), + kilometre: kilometreMap[balise.location] ?? [], + amountLevelCrossings: balise.amountLevelCrossings, + ); + }); + } + + static Iterable _parseWhistle( + SegmentProfile segmentProfile, int segmentIndex, Map> kilometreMap) { + final whistleNsps = segmentProfile.points?.whistleNsp ?? []; + return whistleNsps.map((whistle) { + return Whistle( + order: calculateOrder(segmentIndex, whistle.location), + kilometre: kilometreMap[whistle.location] ?? [], + ); + }); + } + + static Iterable _parseLevelCrossings( + SegmentProfile segmentProfile, int segmentIndex, Map> kilometreMap) { + final levelCrossings = segmentProfile.contextInformation?.levelCrossings ?? []; + return levelCrossings.map((levelCrossing) { + return LevelCrossing( + order: calculateOrder(segmentIndex, levelCrossing.startLocation), + kilometre: kilometreMap[levelCrossing.startLocation] ?? [], + ); + }); + } + + static List _parseTramAreas(List segmentProfiles) { + final List result = []; + + int? startSegmentIndex; + int? endSegmentIndex; + double? startLocation; + double? endLocation; + int? amountTramSignals; + + for (int segmentIndex = 0; segmentIndex < segmentProfiles.length; segmentIndex++) { + final segmentProfile = segmentProfiles[segmentIndex]; + + if (segmentProfile.areas == null) continue; + + for (final tramArea in segmentProfile.areas!.tramAreas) { + switch (tramArea.startEndQualifier) { + case StartEndQualifier.starts: + startLocation = tramArea.startLocation; + startSegmentIndex = segmentIndex; + amountTramSignals = tramArea.amountTramSignals?.amountTramSignals; + break; + case StartEndQualifier.startsEnds: + startLocation = tramArea.startLocation; + startSegmentIndex = segmentIndex; + amountTramSignals = tramArea.amountTramSignals?.amountTramSignals; + continue next; + next: + case StartEndQualifier.ends: + endLocation = tramArea.endLocation; + endSegmentIndex = segmentIndex; + break; + case StartEndQualifier.wholeSp: + break; + case null: + Fimber.w('Received tramArea with startEndQualifier=null'); + break; + } + + if (startSegmentIndex != null && + endSegmentIndex != null && + startLocation != null && + endLocation != null && + amountTramSignals != null) { + final startSegment = segmentProfiles[startSegmentIndex]; + final endSegment = segmentProfiles[endSegmentIndex]; + + final startKilometreMap = parseKilometre(startSegment); + final endKilometreMap = parseKilometre(endSegment); + + result.add(TramArea( + order: calculateOrder(startSegmentIndex, startLocation), + kilometre: startKilometreMap[startLocation]!, + endKilometre: endKilometreMap[endLocation]!.first, + amountTramSignals: amountTramSignals, + )); + + startSegmentIndex = null; + endSegmentIndex = null; + startLocation = null; + endLocation = null; + amountTramSignals = null; + } + } + } + + if (startSegmentIndex != null || endSegmentIndex != null || startLocation != null || endLocation != null) { + Fimber.w('Incomplete tram area found: ' + 'startSegmentIndex: $startSegmentIndex, endSegmentIndex: $endSegmentIndex, ' + 'startLocation: $startLocation, endLocation: $endLocation, amountTramSignals: $amountTramSignals'); + } + + return result; + } } diff --git a/das_client/lib/sfera/src/model/amount_tram_signals.dart b/das_client/lib/sfera/src/model/amount_tram_signals.dart new file mode 100644 index 00000000..c25efad9 --- /dev/null +++ b/das_client/lib/sfera/src/model/amount_tram_signals.dart @@ -0,0 +1,14 @@ +import 'package:das_client/sfera/src/model/network_specific_parameter.dart'; + +class AmountTramSignals extends NetworkSpecificParameter { + static const String elementName = 'amountTramSignals'; + + AmountTramSignals({super.type, super.attributes, super.children, super.value}); + + int get amountTramSignals => int.parse(attributes['value']!); + + @override + bool validate() { + return validateHasAttributeInt('value') && super.validate(); + } +} diff --git a/das_client/lib/sfera/src/model/balise.dart b/das_client/lib/sfera/src/model/balise.dart new file mode 100644 index 00000000..6dec831a --- /dev/null +++ b/das_client/lib/sfera/src/model/balise.dart @@ -0,0 +1,10 @@ +import 'package:das_client/sfera/src/model/sp_generic_point.dart'; +import 'package:das_client/util/util.dart'; + +class Balise extends SpGenericPoint { + static const String elementType = 'Balise'; + + Balise({super.type = elementType, super.attributes, super.children, super.value}); + + int get amountLevelCrossings => Util.tryParseInt(attributes['amountLevelCrossings']) ?? 1; +} diff --git a/das_client/lib/sfera/src/model/level_crossing_area.dart b/das_client/lib/sfera/src/model/level_crossing_area.dart new file mode 100644 index 00000000..38a2035a --- /dev/null +++ b/das_client/lib/sfera/src/model/level_crossing_area.dart @@ -0,0 +1,22 @@ +import 'package:das_client/sfera/src/model/enums/start_end_qualifier.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/util/util.dart'; + +class LevelCrossingArea extends SferaXmlElement { + static const String elementType = 'LevelCrossingArea'; + + LevelCrossingArea({super.type = elementType, super.attributes, super.children, super.value}); + + StartEndQualifier? get startEndQualifier => + XmlEnum.valueOf(StartEndQualifier.values, attributes['startEndQualifier']); + + double get startLocation => double.parse(attributes['startLocation']!); + + double? get endLocation => Util.tryParseDouble(attributes['endLocation']); + + @override + bool validate() { + return validateHasAttribute('startEndQualifier') && validateHasAttributeDouble('startLocation') && super.validate(); + } +} diff --git a/das_client/lib/sfera/src/model/network_specific_area.dart b/das_client/lib/sfera/src/model/network_specific_area.dart index f042c64e..d572c9df 100644 --- a/das_client/lib/sfera/src/model/network_specific_area.dart +++ b/das_client/lib/sfera/src/model/network_specific_area.dart @@ -1,8 +1,10 @@ +import 'package:das_client/sfera/src/model/amount_tram_signals.dart'; import 'package:das_client/sfera/src/model/enums/start_end_qualifier.dart'; import 'package:das_client/sfera/src/model/enums/xml_enum.dart'; import 'package:das_client/sfera/src/model/network_specific_parameter.dart'; import 'package:das_client/sfera/src/model/sfera_xml_element.dart'; import 'package:das_client/sfera/src/model/track_equipment_type_wrapper.dart'; +import 'package:das_client/util/util.dart'; class NetworkSpecificArea extends SferaXmlElement { static const String elementType = 'NetworkSpecificArea'; @@ -14,17 +16,17 @@ class NetworkSpecificArea extends SferaXmlElement { StartEndQualifier? get startEndQualifier => XmlEnum.valueOf(StartEndQualifier.values, attributes['startEndQualifier']); - double? get startLocation => _parseOrNull(attributes['startLocation']); + double? get startLocation => Util.tryParseDouble(attributes['startLocation']); - double? get endLocation => _parseOrNull(attributes['endLocation']); + double? get endLocation => Util.tryParseDouble(attributes['endLocation']); Iterable get networkSpecificParameters => children.whereType(); - TrackEquipmentTypeWrapper? get trackEquipmentTypeWrapper => children.whereType().firstOrNull; + TrackEquipmentTypeWrapper? get trackEquipmentTypeWrapper => + children.whereType().firstOrNull; - double? _parseOrNull(String? source) { - return source != null ? double.parse(source) : null; - } + AmountTramSignals? get amountTramSignals => + children.whereType().firstOrNull; @override bool validate() { 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 0f6aa60b..76214435 100644 --- a/das_client/lib/sfera/src/model/network_specific_parameter.dart +++ b/das_client/lib/sfera/src/model/network_specific_parameter.dart @@ -1,3 +1,4 @@ +import 'package:das_client/sfera/src/model/amount_tram_signals.dart'; import 'package:das_client/sfera/src/model/sfera_xml_element.dart'; import 'package:das_client/sfera/src/model/track_equipment_type_wrapper.dart'; import 'package:das_client/sfera/src/model/xml_curve_speed.dart'; @@ -22,6 +23,8 @@ class NetworkSpecificParameter extends SferaXmlElement { return XmlStationSpeed(attributes: attributes, children: children, value: value); } else if (attributes?['name'] == XmlGraduatedSpeedInfo.elementName) { return XmlGraduatedSpeedInfo(attributes: attributes, children: children, value: value); + } else if (attributes?['name'] == AmountTramSignals.elementName) { + return AmountTramSignals(attributes: attributes, children: children, value: value); } return NetworkSpecificParameter(attributes: attributes, children: children, value: value); } 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 8229ca3c..6c26427e 100644 --- a/das_client/lib/sfera/src/model/network_specific_point.dart +++ b/das_client/lib/sfera/src/model/network_specific_point.dart @@ -3,6 +3,7 @@ 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'; import 'package:das_client/sfera/src/model/sp_generic_point.dart'; +import 'package:das_client/sfera/src/model/whistle_network_specific_point.dart'; class NetworkSpecificPoint extends SpGenericPoint { static const String elementType = 'NetworkSpecificPoint'; @@ -14,6 +15,8 @@ class NetworkSpecificPoint extends SpGenericPoint { return NewLineSpeedNetworkSpecificPoint(attributes: attributes, children: children, value: value); } else if (attributes?['name'] == CurvePointNetworkSpecificPoint.elementName) { return CurvePointNetworkSpecificPoint(attributes: attributes, children: children, value: value); + } else if (attributes?['name'] == WhistleNetworkSpecificPoint.elementName) { + return WhistleNetworkSpecificPoint(attributes: attributes, children: children, value: value); } return NetworkSpecificPoint(attributes: attributes, children: children, value: value); } diff --git a/das_client/lib/sfera/src/model/sp_areas.dart b/das_client/lib/sfera/src/model/sp_areas.dart index 431a94d6..e6419d51 100644 --- a/das_client/lib/sfera/src/model/sp_areas.dart +++ b/das_client/lib/sfera/src/model/sp_areas.dart @@ -5,6 +5,7 @@ import 'package:das_client/sfera/src/model/taf_tap_location.dart'; class SpAreas extends SferaXmlElement { static const String elementType = 'SP_Areas'; static const String _nonStandardTrackEquipmentName = 'nonStandardTrackEquipment'; + static const String _tramAreaName = 'tramArea'; SpAreas({super.type = elementType, super.attributes, super.children, super.value}); @@ -12,4 +13,7 @@ class SpAreas extends SferaXmlElement { Iterable get nonStandardTrackEquipments => children.whereType().where((it) => it.name == _nonStandardTrackEquipmentName); + + Iterable get tramAreas => + children.whereType().where((it) => it.name == _tramAreaName); } diff --git a/das_client/lib/sfera/src/model/sp_context_information.dart b/das_client/lib/sfera/src/model/sp_context_information.dart index e018e3e9..80ef75e3 100644 --- a/das_client/lib/sfera/src/model/sp_context_information.dart +++ b/das_client/lib/sfera/src/model/sp_context_information.dart @@ -1,5 +1,6 @@ import 'package:das_client/sfera/src/model/connection_track.dart'; import 'package:das_client/sfera/src/model/kilometre_reference_point.dart'; +import 'package:das_client/sfera/src/model/level_crossing_area.dart'; import 'package:das_client/sfera/src/model/sfera_xml_element.dart'; class SpContextInformation extends SferaXmlElement { @@ -10,4 +11,6 @@ class SpContextInformation extends SferaXmlElement { Iterable get kilometreReferencePoints => children.whereType(); Iterable get connectionTracks => children.whereType(); + + Iterable get levelCrossings => children.whereType(); } diff --git a/das_client/lib/sfera/src/model/sp_points.dart b/das_client/lib/sfera/src/model/sp_points.dart index 67ba0f89..9f7b2c8b 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/balise.dart'; 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'; @@ -5,6 +6,7 @@ import 'package:das_client/sfera/src/model/sfera_xml_element.dart'; import 'package:das_client/sfera/src/model/signal.dart'; import 'package:das_client/sfera/src/model/timing_point.dart'; import 'package:das_client/sfera/src/model/virtual_balise.dart'; +import 'package:das_client/sfera/src/model/whistle_network_specific_point.dart'; class SpPoints extends SferaXmlElement { static const String elementType = 'SP_Points'; @@ -16,7 +18,9 @@ class SpPoints extends SferaXmlElement { Iterable get signals => children.whereType(); - Iterable get balise => children.whereType(); + Iterable get virtualBalise => children.whereType(); + + Iterable get balise => children.whereType(); Iterable get protectionSectionNsp => children.whereType().where((it) => it.name == _protectionSectionNspName); @@ -24,6 +28,7 @@ class SpPoints extends SferaXmlElement { Iterable get newLineSpeedsNsp => children.whereType(); - Iterable get curvePointsNsp => - children.whereType(); + Iterable get curvePointsNsp => children.whereType(); + + Iterable get whistleNsp => children.whereType(); } diff --git a/das_client/lib/sfera/src/model/whistle_network_specific_point.dart b/das_client/lib/sfera/src/model/whistle_network_specific_point.dart new file mode 100644 index 00000000..b1d66eaa --- /dev/null +++ b/das_client/lib/sfera/src/model/whistle_network_specific_point.dart @@ -0,0 +1,7 @@ +import 'package:das_client/sfera/src/model/network_specific_point.dart'; + +class WhistleNetworkSpecificPoint extends NetworkSpecificPoint { + static const String elementName = 'whistle'; + + WhistleNetworkSpecificPoint({super.type, super.attributes, super.children, super.value}); +} diff --git a/das_client/lib/sfera/src/sfera_reply_parser.dart b/das_client/lib/sfera/src/sfera_reply_parser.dart index 04c52b08..5f2c0f60 100644 --- a/das_client/lib/sfera/src/sfera_reply_parser.dart +++ b/das_client/lib/sfera/src/sfera_reply_parser.dart @@ -1,4 +1,5 @@ import 'package:das_client/sfera/src/model/additional_speed_restriction.dart'; +import 'package:das_client/sfera/src/model/balise.dart'; 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'; @@ -13,6 +14,7 @@ import 'package:das_client/sfera/src/model/handshake_reject.dart'; import 'package:das_client/sfera/src/model/journey_profile.dart'; import 'package:das_client/sfera/src/model/kilometre_reference_point.dart'; import 'package:das_client/sfera/src/model/km_reference.dart'; +import 'package:das_client/sfera/src/model/level_crossing_area.dart'; import 'package:das_client/sfera/src/model/line_speed.dart'; import 'package:das_client/sfera/src/model/location_ident.dart'; import 'package:das_client/sfera/src/model/message_header.dart'; @@ -206,6 +208,10 @@ class SferaReplyParser { return GraduatedSpeedInfoEntity(type: type, attributes: attributes, children: children, value: value); case GraduatedSpeedInfo.elementType: return GraduatedSpeedInfo(type: type, attributes: attributes, children: children, value: value); + case Balise.elementType: + return Balise(type: type, attributes: attributes, children: children, value: value); + case LevelCrossingArea.elementType: + return LevelCrossingArea(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 index 33670724..fa1c30b4 100644 --- a/das_client/lib/util/util.dart +++ b/das_client/lib/util/util.dart @@ -2,4 +2,8 @@ class Util { static int? tryParseInt(String? value) { return value != null ? int.tryParse(value) : null; } + + static double? tryParseDouble(String? value) { + return value != null ? double.tryParse(value) : null; + } } diff --git a/das_client/test/app/pages/journey/train_journey/widgets/table/cells/track_equipment_render_data_test.dart b/das_client/test/app/pages/journey/train_journey/widgets/table/cells/track_equipment_render_data_test.dart index d8c1d67d..98a39339 100644 --- a/das_client/test/app/pages/journey/train_journey/widgets/table/cells/track_equipment_render_data_test.dart +++ b/das_client/test/app/pages/journey/train_journey/widgets/table/cells/track_equipment_render_data_test.dart @@ -29,9 +29,9 @@ void main() { ); // WHEN - final cabSignalingStart = TrackEquipmentRenderData.from(journey, 0); - final signal = TrackEquipmentRenderData.from(journey, 1); - final cabSignalingEnd = TrackEquipmentRenderData.from(journey, 2); + final cabSignalingStart = TrackEquipmentRenderData.from(journey.data, journey.metadata, 0); + final signal = TrackEquipmentRenderData.from(journey.data, journey.metadata, 1); + final cabSignalingEnd = TrackEquipmentRenderData.from(journey.data, journey.metadata, 2); // THEN expect(cabSignalingStart.isCABStart, isTrue); @@ -59,11 +59,11 @@ void main() { ); // WHEN - final curvePoint = TrackEquipmentRenderData.from(journey, 0); - final cabSignalingStart = TrackEquipmentRenderData.from(journey, 1); - final servicePoint = TrackEquipmentRenderData.from(journey, 2); - final cabSignalingEnd = TrackEquipmentRenderData.from(journey, 3); - final signal = TrackEquipmentRenderData.from(journey, 4); + final curvePoint = TrackEquipmentRenderData.from(journey.data, journey.metadata, 0); + final cabSignalingStart = TrackEquipmentRenderData.from(journey.data, journey.metadata, 1); + final servicePoint = TrackEquipmentRenderData.from(journey.data, journey.metadata, 2); + final cabSignalingEnd = TrackEquipmentRenderData.from(journey.data, journey.metadata, 3); + final signal = TrackEquipmentRenderData.from(journey.data, journey.metadata, 4); // THEN var expectedHeight = 0.0; @@ -92,11 +92,11 @@ void main() { ); // WHEN - final curvePoint = TrackEquipmentRenderData.from(journey, 0); - final cabSignaling = TrackEquipmentRenderData.from(journey, 1); - final servicePoint = TrackEquipmentRenderData.from(journey, 2); - final signal = TrackEquipmentRenderData.from(journey, 3); - final connectionTrack = TrackEquipmentRenderData.from(journey, 4); + final curvePoint = TrackEquipmentRenderData.from(journey.data, journey.metadata, 0); + final cabSignaling = TrackEquipmentRenderData.from(journey.data, journey.metadata, 1); + final servicePoint = TrackEquipmentRenderData.from(journey.data, journey.metadata, 2); + final signal = TrackEquipmentRenderData.from(journey.data, journey.metadata, 3); + final connectionTrack = TrackEquipmentRenderData.from(journey.data, journey.metadata, 4); // THEN expect(curvePoint.trackEquipmentType, isNull); @@ -127,12 +127,12 @@ void main() { ); // WHEN - final curvePoint = TrackEquipmentRenderData.from(journey, 0); - final cabSignaling = TrackEquipmentRenderData.from(journey, 1); - final servicePoint = TrackEquipmentRenderData.from(journey, 2); - final signal = TrackEquipmentRenderData.from(journey, 3); - final connectionTrack = TrackEquipmentRenderData.from(journey, 4); - final servicePoint2 = TrackEquipmentRenderData.from(journey, 5); + final curvePoint = TrackEquipmentRenderData.from(journey.data, journey.metadata, 0); + final cabSignaling = TrackEquipmentRenderData.from(journey.data, journey.metadata, 1); + final servicePoint = TrackEquipmentRenderData.from(journey.data, journey.metadata, 2); + final signal = TrackEquipmentRenderData.from(journey.data, journey.metadata, 3); + final connectionTrack = TrackEquipmentRenderData.from(journey.data, journey.metadata, 4); + final servicePoint2 = TrackEquipmentRenderData.from(journey.data, journey.metadata, 5); // THEN expect(curvePoint.isConventionalExtendedSpeedBorder, isFalse); diff --git a/das_client/test/model/journey/base_data_extension_test.dart b/das_client/test/model/journey/base_data_extension_test.dart new file mode 100644 index 00000000..3bacd009 --- /dev/null +++ b/das_client/test/model/journey/base_data_extension_test.dart @@ -0,0 +1,104 @@ +import 'package:das_client/app/model/train_journey_settings.dart'; +import 'package:das_client/model/journey/balise.dart'; +import 'package:das_client/model/journey/balise_level_crossing_group.dart'; +import 'package:das_client/model/journey/base_data.dart'; +import 'package:das_client/model/journey/base_data_extension.dart'; +import 'package:das_client/model/journey/level_crossing.dart'; +import 'package:das_client/model/journey/whistles.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('Test balise and level crossing grouping', () { + final originalRows = [ + Balise(order: 100, kilometre: [0.1], amountLevelCrossings: 1), + LevelCrossing(order: 101, kilometre: [0.11]), + Balise(order: 200, kilometre: [0.2], amountLevelCrossings: 1), + LevelCrossing(order: 202, kilometre: [0.22]), + ]; + + final notExpandedSettings = TrainJourneySettings(); + final groupedRowsNotExpanded = originalRows.groupBaliseAndLeveLCrossings(notExpandedSettings); + + expect(groupedRowsNotExpanded, hasLength(1)); + expect(groupedRowsNotExpanded[0], isA()); + expect((groupedRowsNotExpanded[0] as BaliseLevelCrossingGroup).groupedElements, hasLength(4)); + + final expandedSettings = TrainJourneySettings(expandedGroups: [100]); + final groupedRowsExpanded = originalRows.groupBaliseAndLeveLCrossings(expandedSettings); + + expect(groupedRowsExpanded, hasLength(5)); + expect(groupedRowsExpanded[0], isA()); + expect((groupedRowsExpanded[0] as BaliseLevelCrossingGroup).groupedElements, hasLength(4)); + expect(groupedRowsExpanded[1], isA()); + expect(groupedRowsExpanded[2], isA()); + expect(groupedRowsExpanded[3], isA()); + expect(groupedRowsExpanded[4], isA()); + }); + + test('Test balise and level crossing grouping with different amounts', () { + final originalRows = [ + Balise(order: 100, kilometre: [0.1], amountLevelCrossings: 1), + LevelCrossing(order: 101, kilometre: [0.11]), + Balise(order: 200, kilometre: [0.2], amountLevelCrossings: 2), + LevelCrossing(order: 202, kilometre: [0.22]), + LevelCrossing(order: 203, kilometre: [0.23]), + ]; + + final notExpandedSettings = TrainJourneySettings(); + final groupedRowsNotExpanded = originalRows.groupBaliseAndLeveLCrossings(notExpandedSettings); + + expect(groupedRowsNotExpanded, hasLength(2)); + expect(groupedRowsNotExpanded[0], isA()); + expect((groupedRowsNotExpanded[0] as BaliseLevelCrossingGroup).groupedElements, hasLength(2)); + expect(groupedRowsNotExpanded[1], isA()); + expect((groupedRowsNotExpanded[1] as BaliseLevelCrossingGroup).groupedElements, hasLength(3)); + + final expandedSettings = TrainJourneySettings(expandedGroups: [200]); + final groupedRowsExpanded = originalRows.groupBaliseAndLeveLCrossings(expandedSettings); + + expect(groupedRowsExpanded, hasLength(5)); + expect(groupedRowsExpanded[0], isA()); + expect((groupedRowsExpanded[0] as BaliseLevelCrossingGroup).groupedElements, hasLength(2)); + expect(groupedRowsExpanded[1], isA()); + expect((groupedRowsExpanded[1] as BaliseLevelCrossingGroup).groupedElements, hasLength(3)); + expect(groupedRowsExpanded[2], isA()); + expect(groupedRowsExpanded[3], isA()); + expect(groupedRowsExpanded[4], isA()); + }); + + + test('Test balise and level crossing grouping with elements between', () { + final originalRows = [ + Balise(order: 100, kilometre: [0.1], amountLevelCrossings: 1), + LevelCrossing(order: 101, kilometre: [0.11]), + Whistle(order: 155, kilometre: [0.22]), + Balise(order: 200, kilometre: [0.2], amountLevelCrossings: 1), + LevelCrossing(order: 202, kilometre: [0.22]), + ]; + + final notExpandedSettings = TrainJourneySettings(); + final groupedRowsNotExpanded = originalRows.groupBaliseAndLeveLCrossings(notExpandedSettings); + + expect(groupedRowsNotExpanded, hasLength(3)); + expect(groupedRowsNotExpanded[0], isA()); + expect((groupedRowsNotExpanded[0] as BaliseLevelCrossingGroup).groupedElements, hasLength(2)); + expect(groupedRowsNotExpanded[1], isA()); + expect(groupedRowsNotExpanded[2], isA()); + expect((groupedRowsNotExpanded[2] as BaliseLevelCrossingGroup).groupedElements, hasLength(2)); + }); + + test('Test level crossing grouping', () { + final originalRows = [ + LevelCrossing(order: 101, kilometre: [0.11]), + LevelCrossing(order: 202, kilometre: [0.22]), + LevelCrossing(order: 303, kilometre: [0.33]), + ]; + + final notExpandedSettings = TrainJourneySettings(); + final groupedRowsNotExpanded = originalRows.groupBaliseAndLeveLCrossings(notExpandedSettings); + + expect(groupedRowsNotExpanded, hasLength(1)); + expect(groupedRowsNotExpanded[0], isA()); + expect((groupedRowsNotExpanded[0] as BaliseLevelCrossingGroup).groupedElements, hasLength(3)); + }); +} diff --git a/das_client/test/sfera/mapper/sfera_mapper_test.dart b/das_client/test/sfera/mapper/sfera_mapper_test.dart index 49bfa48b..886e22f2 100644 --- a/das_client/test/sfera/mapper/sfera_mapper_test.dart +++ b/das_client/test/sfera/mapper/sfera_mapper_test.dart @@ -2,11 +2,13 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:das_client/model/journey/additional_speed_restriction_data.dart'; +import 'package:das_client/model/journey/balise.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'; +import 'package:das_client/model/journey/level_crossing.dart'; 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'; @@ -14,6 +16,8 @@ import 'package:das_client/model/journey/speed.dart'; import 'package:das_client/model/journey/speed_change.dart'; import 'package:das_client/model/journey/track_equipment.dart'; import 'package:das_client/model/journey/train_series.dart'; +import 'package:das_client/model/journey/tram_area.dart'; +import 'package:das_client/model/journey/whistles.dart'; 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'; @@ -582,6 +586,78 @@ void main() { expect(journey.metadata.breakSeries!.breakSeries, 115); }); + test('Test tram area parsed correctly', () async { + final journey = getJourney('T7', 1, tcCount: 1); + expect(journey.valid, true); + + final tramAreas = journey.data.where((it) => it.type == Datatype.tramArea).cast().toList(); + expect(tramAreas, hasLength(1)); + expect(tramAreas[0].order, 900); + expect(tramAreas[0].kilometre[0], 37.8); + expect(tramAreas[0].amountTramSignals, 6); + expect(tramAreas[0].endKilometre, 36.8); + }); + + test('Test whistle parsed correctly', () async { + final journey = getJourney('T7', 1, tcCount: 1); + expect(journey.valid, true); + + final whistles = journey.data.where((it) => it.type == Datatype.whistle).cast().toList(); + expect(whistles, hasLength(1)); + expect(whistles[0].order, 610); + expect(whistles[0].kilometre[0], 39.600); + }); + + test('Test balise parsed correctly', () async { + final journey = getJourney('T7', 1, tcCount: 1); + expect(journey.valid, true); + + final balises = journey.data.where((it) => it.type == Datatype.balise).cast().toList(); + expect(balises, hasLength(8)); + expect(balises[0].order, 600); + expect(balises[0].kilometre[0], 41.552); + expect(balises[0].amountLevelCrossings, 1); + expect(balises[1].order, 602); + expect(balises[1].kilometre[0], 41.190); + expect(balises[1].amountLevelCrossings, 1); + + expect(balises[2].order, 604); + expect(balises[2].amountLevelCrossings, 1); + expect(balises[3].order, 606); + expect(balises[3].amountLevelCrossings, 1); + expect(balises[4].order, 608); + expect(balises[4].amountLevelCrossings, 1); + + expect(balises[5].order, 611); + expect(balises[5].amountLevelCrossings, 1); + expect(balises[6].order, 613); + expect(balises[6].amountLevelCrossings, 2); + expect(balises[7].order, 616); + expect(balises[7].amountLevelCrossings, 1); + }); + + test('Test level crossing parsed correctly', () async { + final journey = getJourney('T7', 1, tcCount: 1); + expect(journey.valid, true); + + final levelCrossings = journey.data.where((it) => it.type == Datatype.levelCrossing).cast().toList(); + expect(levelCrossings, hasLength(12)); + expect(levelCrossings[0].order, 601); + expect(levelCrossings[0].kilometre[0], 41.492); + expect(levelCrossings[1].order, 603); + expect(levelCrossings[1].kilometre[0], 41.155); + expect(levelCrossings[2].order, 605); + expect(levelCrossings[3].order, 607); + expect(levelCrossings[4].order, 609); + expect(levelCrossings[5].order, 612); + expect(levelCrossings[6].order, 614); + expect(levelCrossings[7].order, 615); + expect(levelCrossings[8].order, 617); + expect(levelCrossings[9].order, 1600); + expect(levelCrossings[10].order, 1601); + expect(levelCrossings[11].order, 1602); + }); + test('Test graduated station speeds are parsed correctly', () async { final journey = getJourney('T8', 1); expect(journey.valid, true); diff --git a/das_client/test/sfera/model/sfera_document_test.dart b/das_client/test/sfera/model/sfera_document_test.dart index 30c6272e..f55beb47 100644 --- a/das_client/test/sfera/model/sfera_document_test.dart +++ b/das_client/test/sfera/model/sfera_document_test.dart @@ -67,10 +67,10 @@ void main() { expect(spPoint.signals.first.id.physicalId, '102346'); expect(spPoint.signals.first.id.location, 843.0); - expect(spPoint.balise, hasLength(3)); - expect(spPoint.balise.first.location, '0'); - expect(spPoint.balise.first.position.latitude, '51.48591'); - expect(spPoint.balise.first.position.longitude, '4.73459'); + expect(spPoint.virtualBalise, hasLength(3)); + expect(spPoint.virtualBalise.first.location, '0'); + expect(spPoint.virtualBalise.first.position.latitude, '51.48591'); + expect(spPoint.virtualBalise.first.position.longitude, '4.73459'); }); test('Test Sfera HandshakeRequest generation', () async { diff --git a/das_client/test_resources/T7_Balise_Whistle_TramArea/SFERA_JP_T7.xml b/das_client/test_resources/T7_Balise_Whistle_TramArea/SFERA_JP_T7.xml new file mode 100644 index 00000000..328dcad3 --- /dev/null +++ b/das_client/test_resources/T7_Balise_Whistle_TramArea/SFERA_JP_T7.xml @@ -0,0 +1,40 @@ + + + + + 1085 + T7 + 2022-01-04 + + + + + 0085 + + + + + + + + + + + + + + + + + + + + + + + 1185 + + + diff --git a/das_client/test_resources/T7_Balise_Whistle_TramArea/SFERA_SP_T7_1.xml b/das_client/test_resources/T7_Balise_Whistle_TramArea/SFERA_SP_T7_1.xml new file mode 100644 index 00000000..5b668811 --- /dev/null +++ b/das_client/test_resources/T7_Balise_Whistle_TramArea/SFERA_SP_T7_1.xml @@ -0,0 +1,232 @@ + + + + 0085 + + + + + + CH + 3002 + + + + + + + CH + 3003 + + + + + + + CH + 3004 + + + + + + + CH + 3005 + + + + + + + + + + + + + + + + + + + + + + CH + 3002 + + + + + + + CH + 3003 + + + + + + + CH + 3004 + + + + + + + CH + 3005 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/das_client/test_resources/T7_Balise_Whistle_TramArea/SFERA_TC_T7_1.xml b/das_client/test_resources/T7_Balise_Whistle_TramArea/SFERA_TC_T7_1.xml new file mode 100644 index 00000000..f01ae73d --- /dev/null +++ b/das_client/test_resources/T7_Balise_Whistle_TramArea/SFERA_TC_T7_1.xml @@ -0,0 +1,8 @@ + + + 1185 + + + diff --git a/sfera-mock/src/main/resources/static_sfera_resources/T7_Balise_Whistle_TramArea/SFERA_JP_T7.xml b/sfera-mock/src/main/resources/static_sfera_resources/T7_Balise_Whistle_TramArea/SFERA_JP_T7.xml index 328dcad3..fd79dde2 100644 --- a/sfera-mock/src/main/resources/static_sfera_resources/T7_Balise_Whistle_TramArea/SFERA_JP_T7.xml +++ b/sfera-mock/src/main/resources/static_sfera_resources/T7_Balise_Whistle_TramArea/SFERA_JP_T7.xml @@ -9,7 +9,7 @@ 2022-01-04 - + 0085 diff --git a/sfera-mock/src/main/resources/static_sfera_resources/T7_Balise_Whistle_TramArea/SFERA_SP_T7_1.xml b/sfera-mock/src/main/resources/static_sfera_resources/T7_Balise_Whistle_TramArea/SFERA_SP_T7_1.xml index ce795229..51b2d81c 100644 --- a/sfera-mock/src/main/resources/static_sfera_resources/T7_Balise_Whistle_TramArea/SFERA_SP_T7_1.xml +++ b/sfera-mock/src/main/resources/static_sfera_resources/T7_Balise_Whistle_TramArea/SFERA_SP_T7_1.xml @@ -1,7 +1,7 @@ + SP_ID="T7_1" SP_VersionMajor="1" SP_VersionMinor="3" SP_Length="2000" SP_Status="Valid"> 0085 @@ -183,11 +183,11 @@ - + - +