Skip to content

Commit

Permalink
feat: added curve points and main signals to train journey (#82)
Browse files Browse the repository at this point in the history
* feat: mapped signals, track equipments and curves from SFERA (#82)

* chore: some fixes after merge

* feat: implement additional speed restrictions

* chore: add mock data for additional speed restrictions

* chore: fix display

* add missing icon

* chore: fix height for base row

* chore: fix ui tests

* chore: refactor routestart and end

* chore: revert flavor change

* review changes

* feat: visualize curves and signals in journey table (#82).

* chore: fixed SFERA mapper test

* chore: added UI tests and refactored mock data.

* chore: fixed UI test

* chore: fixed wrong merge conflict resolve

* chore: inputs from code review

---------

Co-authored-by: u221711 <[email protected]>
  • Loading branch information
rawi-coding and Grodien authored Nov 29, 2024
1 parent 0f8390e commit 523ecca
Show file tree
Hide file tree
Showing 20 changed files with 345 additions and 141 deletions.
6 changes: 6 additions & 0 deletions das_client/assets/icons/icon_curve_start.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions das_client/assets/icons/icon_signal_line_change.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 61 additions & 3 deletions das_client/integration_test/test/train_journey_table_test.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
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/cells/bracket_station_body.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/cells/route_cell_body.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';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/signal_row.dart';
import 'package:das_client/app/pages/profile/profile_page.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -108,8 +110,7 @@ void main() {

// check stop circles
final stopRoute = find.descendant(of: stopRouteRow, matching: find.byKey(RouteCellBody.stopKey));
final nonStoppingPassRoute =
find.descendant(of: nonStoppingPassRouteRow, matching: find.byKey(RouteCellBody.stopKey));
final nonStoppingPassRoute = find.descendant(of: nonStoppingPassRouteRow, matching: find.byKey(RouteCellBody.stopKey));
expect(stopRoute, findsOneWidget);
expect(nonStoppingPassRoute, findsNothing);

Expand All @@ -124,7 +125,7 @@ void main() {
expect(routeEnd, findsOneWidget);
});

testWidgets('test protection secions are displayed correctly', (tester) async {
testWidgets('test protection sections are displayed correctly', (tester) async {
await prepareAndStartApp(tester);

// load train journey by filling out train selection page
Expand Down Expand Up @@ -310,6 +311,63 @@ void main() {
.byWidgetPredicate((it) => it is Text && it.data == 'Schlieren' && it.style?.fontStyle != FontStyle.italic);
expect(schlierenText, findsOneWidget);
});

testWidgets('test curves are displayed correctly', (tester) async {
await prepareAndStartApp(tester);

// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: '9999');

final scrollableFinder = find.byType(ListView);
expect(scrollableFinder, findsOneWidget);

await tester.dragUntilVisible(find.text('Kurve').first, scrollableFinder, const Offset(0, -50));

final curveRows = findDASTableRowByText('Kurve');
expect(curveRows, findsAtLeast(1));

final curveIcon = find.descendant(of: curveRows.first, matching: find.byKey(CurvePointRow.curvePointIconKey));
expect(curveIcon, findsOneWidget);

await tester.dragUntilVisible(find.text('Kurve nach Haltestelle'), scrollableFinder, const Offset(0, -50));

final curveAfterHaltRow = findDASTableRowByText('Kurve nach Haltestelle');
expect(curveAfterHaltRow, findsOneWidget);
});

testWidgets('test signals are displayed correctly', (tester) async {
await prepareAndStartApp(tester);

// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: '9999');

final scrollableFinder = find.byType(ListView);
expect(scrollableFinder, findsOneWidget);

// check if signals with both functions laneChange, block are correct
await tester.dragUntilVisible(find.text('S1'), scrollableFinder, const Offset(0, -50));
final langeChangeBlockSignalRow = findDASTableRowByText('S1');
expect(langeChangeBlockSignalRow, findsOneWidget);
expect(find.descendant(of: langeChangeBlockSignalRow, matching: find.text('Block')), findsOneWidget);
final laneChangeIcon = find.descendant(of: langeChangeBlockSignalRow, matching: find.byKey(SignalRow.signalLineChangeIconKey));
expect(laneChangeIcon, findsOneWidget);

// check if basic signal is rendered correctly
await tester.dragUntilVisible(find.text('Deckungssignal'), scrollableFinder, const Offset(0, -50));
final protectionSignalRow = findDASTableRowByText('Deckungssignal');
expect(protectionSignalRow, findsOneWidget);
expect(find.descendant(of: protectionSignalRow, matching: find.text('D1')), findsOneWidget);
final noLaneChangeIcon = find.descendant(of: protectionSignalRow, matching: find.byKey(SignalRow.signalLineChangeIconKey));
expect(noLaneChangeIcon, findsNothing);

// check if signals with multiple functions are rendered correctly
await tester.dragUntilVisible(find.text('Block/Abschnittsignal'), scrollableFinder, const Offset(0, -50));
final blockIntermediateSignalRow = findDASTableRowByText('Block/Abschnittsignal');
expect(blockIntermediateSignalRow, findsOneWidget);
expect(find.descendant(of: blockIntermediateSignalRow, matching: find.text('BAB1')), findsOneWidget);
final noLaneChangeIcon2 = find.descendant(of: blockIntermediateSignalRow, matching: find.byKey(SignalRow.signalLineChangeIconKey));
expect(noLaneChangeIcon2, findsNothing);
});
});
}

Expand Down
10 changes: 10 additions & 0 deletions das_client/l10n/strings_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"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_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",
"w_navigation_drawer_fahrtinfo_title": "Fahrtinfo",
"w_navigation_drawer_links_title": "Links",
"w_navigation_drawer_settings_title": "Einstellungen",
Expand All @@ -28,6 +31,13 @@
"c_ru_bls_p": "BLS",
"c_ru_bls_c": "BLS Cargo",
"c_ru_sob": "SOB",
"c_unknown": "Unbekannt",
"c_main_signal_function_entry": "Einfahrsignal",
"c_main_signal_function_exit": "Ausfahrsignal",
"c_main_signal_function_intermediate": "Abschnittsignal",
"c_main_signal_function_block": "Block",
"c_main_signal_function_protection": "Deckungssignal",
"c_main_signal_function_laneChange": "Spurwechsel",
"c_error_connection_failed": "Verbindung fehlgeschlagen",
"c_error_sfera_validation_failed": "Validierung der Daten fehlgeschlagen",
"c_error_sfera_handshake_rejected": "Server hat die Verbindung abgelehnt",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ class BaseRowBuilder<T extends BaseData> extends DASTableRowBuilder {

DASTableCell kilometreCell(BuildContext context) {
if (data.kilometre.isEmpty) {
return DASTableCell.empty();
return DASTableCell.empty(color: specialCellColor);
}

return DASTableCell(
color: getSpecialCellColor(),
color: specialCellColor,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
Expand All @@ -60,13 +60,9 @@ class BaseRowBuilder<T extends BaseData> extends DASTableRowBuilder {
alignment: Alignment.centerLeft);
}

DASTableCell timeCell(BuildContext context) {
return DASTableCell(color: getSpecialCellColor(), child: Text('06:05:52'), alignment: defaultAlignment);
}

DASTableCell routeCell(BuildContext context) {
return DASTableCell(
color: getSpecialCellColor(),
color: specialCellColor,
padding: EdgeInsets.all(0.0),
alignment: null,
child: RouteCellBody(
Expand All @@ -77,6 +73,10 @@ class BaseRowBuilder<T extends BaseData> extends DASTableRowBuilder {
);
}

DASTableCell timeCell(BuildContext context) {
return DASTableCell.empty(color: specialCellColor);
}

DASTableCell informationCell(BuildContext context) {
return DASTableCell.empty();
}
Expand Down Expand Up @@ -112,11 +112,8 @@ class BaseRowBuilder<T extends BaseData> extends DASTableRowBuilder {
return DASTableCell.empty();
}

Color? getSpecialCellColor() {
return getAdditionalSpeedRestriction() != null
? AdditionalSpeedRestrictionRow.additionalSpeedRestrictionColor
: null;
}
Color? get specialCellColor =>
getAdditionalSpeedRestriction() != null ? AdditionalSpeedRestrictionRow.additionalSpeedRestrictionColor : null;

AdditionalSpeedRestriction? getAdditionalSpeedRestriction() {
return metadata.additionalSpeedRestrictions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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/curve_point.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';

class CurvePointRow extends BaseRowBuilder<CurvePoint> {
static const Key curvePointIconKey = Key('curve_point_icon_key');

CurvePointRow({
required super.metadata,
required super.data,
});

@override
DASTableCell informationCell(BuildContext context) {
return DASTableCell(
child: Text(data.curveType?.localizedName(context) ?? ''),
);
}

@override
DASTableCell iconsCell1(BuildContext context) {
return DASTableCell(
child: SvgPicture.asset(
AppAssets.iconCurveStart,
key: curvePointIconKey,
),
alignment: Alignment.center,
);
}
}

extension _CurveTypeExtension on CurveType {
String localizedName(BuildContext context) {
switch (this) {
case CurveType.curve:
return context.l10n.p_train_journey_table_curve_type_curve;
case CurveType.curveAfterHalt:
return context.l10n.p_train_journey_table_curve_type_curve_after_halt;
case CurveType.stationExitCurve:
return context.l10n.p_train_journey_table_curve_type_station_exit_curve;
case CurveType.unknown:
return context.l10n.c_unknown;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ class ServicePointRow extends BaseRowBuilder<ServicePoint> {
);
}

@override
DASTableCell timeCell(BuildContext context) {
return DASTableCell(child: Text('06:05:52'), alignment: defaultAlignment, color: specialCellColor);
}

@override
DASTableCell iconsCell1(BuildContext context) {
return DASTableCell(
Expand Down Expand Up @@ -63,7 +68,7 @@ class ServicePointRow extends BaseRowBuilder<ServicePoint> {
@override
DASTableCell routeCell(BuildContext context) {
return DASTableCell(
color: getSpecialCellColor(),
color: specialCellColor,
padding: EdgeInsets.all(0.0),
alignment: null,
child: RouteCellBody(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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/signal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';

class SignalRow extends BaseRowBuilder<Signal> {
static const Key signalLineChangeIconKey = Key('signal_line_change_icon_key');

SignalRow({
required super.metadata,
required super.data,
});

@override
DASTableCell informationCell(BuildContext context) {
return DASTableCell(
child: Row(
children: [
_signalFunctions(context),
Spacer(),
if (data.visualIdentifier != null) Text(data.visualIdentifier!),
],
),
);
}

/// Signal functions displayed as string. Type [SignalFunction.laneChange] is ignored if other functions are given as this is displayed with an icon.
Widget _signalFunctions(BuildContext context) {
Iterable<SignalFunction> signalFunctions = data.functions;
if (signalFunctions.length > 1) {
signalFunctions = signalFunctions
.where((function) => function != SignalFunction.laneChange && function != SignalFunction.unknown);
}
return Text(signalFunctions.map((function) => function.localizedName(context)).join('/'));
}

@override
DASTableCell iconsCell1(BuildContext context) {
if (data.functions.contains(SignalFunction.laneChange)) {
return DASTableCell(
child: SvgPicture.asset(
key: signalLineChangeIconKey,
AppAssets.iconSignalLaneChange,
),
alignment: Alignment.center,
);
}
return DASTableCell.empty();
}
}

// extensions

extension _SignalFunctionExtension on SignalFunction {
String localizedName(BuildContext context) {
switch (this) {
case SignalFunction.entry:
return context.l10n.c_main_signal_function_entry;
case SignalFunction.block:
return context.l10n.c_main_signal_function_block;
case SignalFunction.exit:
return context.l10n.c_main_signal_function_exit;
case SignalFunction.laneChange:
return context.l10n.c_main_signal_function_laneChange;
case SignalFunction.intermediate:
return context.l10n.c_main_signal_function_intermediate;
case SignalFunction.protection:
return context.l10n.c_main_signal_function_protection;
case SignalFunction.unknown:
return context.l10n.c_unknown;
}
}
}
Loading

0 comments on commit 523ecca

Please sign in to comment.