diff --git a/das_client/assets/icons/icon_header_stop.svg b/das_client/assets/icons/icon_header_stop.svg
new file mode 100644
index 00000000..d4d94ab9
--- /dev/null
+++ b/das_client/assets/icons/icon_header_stop.svg
@@ -0,0 +1,5 @@
+
diff --git a/das_client/assets/icons/icon_stop_on_request.svg b/das_client/assets/icons/icon_stop_on_request.svg
new file mode 100644
index 00000000..a488e00e
--- /dev/null
+++ b/das_client/assets/icons/icon_stop_on_request.svg
@@ -0,0 +1,15 @@
+
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 72d179ad..4ef7fbaf 100644
--- a/das_client/integration_test/test/train_journey_table_test.dart
+++ b/das_client/integration_test/test/train_journey_table_test.dart
@@ -1,3 +1,6 @@
+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/service_point_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';
@@ -40,24 +43,20 @@ void main() {
expect(scrollableFinder, findsOneWidget);
// check first train station
- expect(find.text('ZUE'), findsOneWidget);
+ expect(find.text('Zürich HB'), findsOneWidget);
// Scroll to last train station
- await tester.dragUntilVisible(
- find.text('AAR'),
- find.byType(ListView),
- const Offset(0, -300)
- );
+ await tester.dragUntilVisible(find.text('Aarau'), find.byType(ListView), const Offset(0, -300));
});
- testWidgets('test fahrbild stays loaded after navigation', (tester) async {
+ testWidgets('test if train journey stays loaded after navigation', (tester) async {
await prepareAndStartApp(tester);
// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: '4816');
// check first train station
- expect(find.text('ZUE'), findsOneWidget);
+ expect(find.text('Zürich HB'), findsOneWidget);
await openDrawer(tester);
await tapElement(tester, find.text(l10n.w_navigation_drawer_profile_title));
@@ -69,7 +68,109 @@ void main() {
await tapElement(tester, find.text(l10n.w_navigation_drawer_fahrtinfo_title));
// check first train station is still visible
- expect(find.text('ZUE'), findsOneWidget);
+ expect(find.text('Zürich HB'), findsOneWidget);
+ });
+
+ testWidgets('test both kilometres are displayed', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: '4816');
+
+ final scrollableFinder = find.byType(ListView);
+ expect(scrollableFinder, findsOneWidget);
+
+ final hardbruckeRow = findDASTableRowByText('Hardbrücke');
+ expect(hardbruckeRow, findsOneWidget);
+ expect(find.descendant(of: hardbruckeRow, matching: find.text('1.9')), findsOneWidget);
+ expect(find.descendant(of: hardbruckeRow, matching: find.text('23.5')), findsOneWidget);
+ });
+
+ testWidgets('test bracket stations is displayed correctly', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: '9999');
+
+ final bracketStationD = findDASTableRowByText('Klammerbahnhof D');
+ final bracketStationD1 = findDASTableRowByText('Klammerbahnhof D1');
+ expect(bracketStationD, findsOneWidget);
+ expect(bracketStationD1, findsOneWidget);
+
+ // check if the bracket station widget is displayed
+ final bracketStationDWidget =
+ find.descendant(of: bracketStationD, matching: find.byKey(BracketStationBody.bracketStationKey));
+ final bracketStationD1Widget =
+ find.descendant(of: bracketStationD1, matching: find.byKey(BracketStationBody.bracketStationKey));
+ expect(bracketStationDWidget, findsOneWidget);
+ expect(bracketStationD1Widget, findsOneWidget);
+
+ // check that the abbreviation is displayed correctly
+ expect(find.descendant(of: bracketStationDWidget, matching: find.text('D')), findsNothing);
+ expect(find.descendant(of: bracketStationD1Widget, matching: find.text('D')), findsOneWidget);
+ });
+
+ testWidgets('test halt on request is displayed correctly', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: '9999');
+
+ final stopOnDemandRow = findDASTableRowByText('Halt auf Verlangen C');
+ expect(stopOnDemandRow, findsOneWidget);
+
+ final stopOnRequestIcon =
+ find.descendant(of: stopOnDemandRow, matching: find.byKey(ServicePointRow.stopOnRequestKey));
+ expect(stopOnRequestIcon, findsOneWidget);
+
+ final stopOnRequestRoute =
+ find.descendant(of: stopOnDemandRow, matching: find.byKey(RouteCellBody.stopOnRequestKey));
+ final stopRoute = find.descendant(of: stopOnDemandRow, matching: find.byKey(RouteCellBody.stopKey));
+ expect(stopOnRequestRoute, findsOneWidget);
+ expect(stopRoute, findsNothing);
+ });
+
+ testWidgets('test route is displayed correctly', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: '9999');
+
+ final stopRouteRow = findDASTableRowByText('Bahnhof A');
+ final nonStoppingPassRouteRow = findDASTableRowByText('Haltestelle B');
+ expect(stopRouteRow, findsOneWidget);
+ expect(nonStoppingPassRouteRow, findsOneWidget);
+
+ // 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));
+ expect(stopRoute, findsOneWidget);
+ expect(nonStoppingPassRoute, findsNothing);
+
+ // check route start
+ final startStationRow = findDASTableRowByText('Bahnhof A');
+ final routeStart = find.descendant(of: startStationRow, matching: find.byKey(RouteCellBody.routeStartKey));
+ expect(routeStart, findsOneWidget);
+
+ // check route end
+ final endStationRow = findDASTableRowByText('Klammerbahnhof D1');
+ final routeEnd = find.descendant(of: endStationRow, matching: find.byKey(RouteCellBody.routeEndKey));
+ expect(routeEnd, findsOneWidget);
+ });
+
+ testWidgets('test halt is displayed italic', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: '4816');
+
+ final glanzenbergText = find
+ .byWidgetPredicate((it) => it is Text && it.data == 'Glanzenberg' && it.style?.fontStyle == FontStyle.italic);
+ expect(glanzenbergText, findsOneWidget);
+
+ final schlierenText = find
+ .byWidgetPredicate((it) => it is Text && it.data == 'Schlieren' && it.style?.fontStyle != FontStyle.italic);
+ expect(schlierenText, findsOneWidget);
});
});
}
diff --git a/das_client/integration_test/test/train_journey_test.dart b/das_client/integration_test/test/train_journey_test.dart
index ba5a87b8..6ffe7d28 100644
--- a/das_client/integration_test/test/train_journey_test.dart
+++ b/das_client/integration_test/test/train_journey_test.dart
@@ -26,7 +26,7 @@ void main() {
await tester.pumpAndSettle();
// check if station is present
- expect(find.text('SO_W'), findsOneWidget);
+ expect(find.text('Solothurn'), findsOneWidget);
await tester.pumpAndSettle();
});
diff --git a/das_client/integration_test/util/test_utils.dart b/das_client/integration_test/util/test_utils.dart
index 7bffcf25..11f6cf72 100644
--- a/das_client/integration_test/util/test_utils.dart
+++ b/das_client/integration_test/util/test_utils.dart
@@ -1,3 +1,4 @@
+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';
@@ -24,3 +25,7 @@ Finder findTextFieldByLabel(String label) {
final sbbTextField = find.byWidgetPredicate((widget) => widget is SBBTextField && widget.labelText == label);
return find.descendant(of: sbbTextField, matching: find.byType(TextField));
}
+
+Finder findDASTableRowByText(String text) {
+ return find.ancestor(of: find.text(text), matching: find.byKey(DASTable.rowKey));
+}
diff --git a/das_client/lib/app/bloc/train_journey_cubit.dart b/das_client/lib/app/bloc/train_journey_cubit.dart
index 97591cc0..a9988c0e 100644
--- a/das_client/lib/app/bloc/train_journey_cubit.dart
+++ b/das_client/lib/app/bloc/train_journey_cubit.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:das_client/app/model/ru.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';
@@ -17,9 +18,7 @@ class TrainJourneyCubit extends Cubit {
final SferaService _sferaService;
- Stream get journeyStream => _sferaService.journeyStream;
-
- Stream> get segmentStream => _sferaService.segmentStream;
+ Stream get journeyStream => _sferaService.journeyStream;
StreamSubscription? _stateSubscription;
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 01bf96c8..5b9f4c91 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
@@ -1,9 +1,11 @@
import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/header/departure_authorization.dart';
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:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
class MainContainer extends StatelessWidget {
const MainContainer({super.key});
@@ -56,8 +58,7 @@ class MainContainer extends StatelessWidget {
height: 48.0,
child: Row(
children: [
- // TODO: Replace with custom icon from figma
- const Icon(SBBIcons.route_circle_end_small),
+ SvgPicture.asset(AppAssets.iconHeaderStop),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: sbbDefaultSpacing * 0.5),
@@ -71,27 +72,25 @@ class MainContainer extends StatelessWidget {
}
Widget _buttonArea() {
- return Builder(
- builder: (context) {
- return Row(
- children: [
- SBBTertiaryButtonLarge(
- label: context.l10n.p_train_journey_header_button_dark_theme,
- icon: SBBIcons.moon_small,
- onPressed: () {},
- ),
- SBBTertiaryButtonLarge(
- label: context.l10n.p_train_journey_header_button_pause,
- icon: SBBIcons.pause_small,
- onPressed: () {},
- ),
- SBBIconButtonLarge(
- icon: SBBIcons.context_menu_small,
- onPressed: () {},
- ),
- ].withSpacing(width: sbbDefaultSpacing * 0.5),
- );
- }
- );
+ return Builder(builder: (context) {
+ return Row(
+ children: [
+ SBBTertiaryButtonLarge(
+ label: context.l10n.p_train_journey_header_button_dark_theme,
+ icon: SBBIcons.moon_small,
+ onPressed: () {},
+ ),
+ SBBTertiaryButtonLarge(
+ label: context.l10n.p_train_journey_header_button_pause,
+ icon: SBBIcons.pause_small,
+ onPressed: () {},
+ ),
+ SBBIconButtonLarge(
+ icon: SBBIcons.context_menu_small,
+ onPressed: () {},
+ ),
+ ].withSpacing(width: sbbDefaultSpacing * 0.5),
+ );
+ });
}
}
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 0af81b82..c874233e 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
@@ -10,13 +10,11 @@ abstract class BaseRowBuilder extends DASTableRowBuilder {
this.defaultAlignment = Alignment.centerLeft,
this.rowColor,
this.isCurrentPosition = false,
- this.isServicePointStop = false,
});
- final double? kilometre;
+ final List? kilometre;
final Alignment defaultAlignment;
final Color? rowColor;
- final bool isServicePointStop;
final bool isCurrentPosition;
@override
@@ -41,13 +39,20 @@ abstract class BaseRowBuilder extends DASTableRowBuilder {
}
DASTableCell kilometreCell() {
- if (kilometre == null) {
+ if (kilometre == null || kilometre!.isEmpty) {
return DASTableCell.empty();
}
- var kilometreAsString = kilometre!.toStringAsFixed(3);
- kilometreAsString = kilometreAsString.replaceAll(RegExp(r'0*$'), '');
- return DASTableCell(child: Text(kilometreAsString), alignment: defaultAlignment);
+ return DASTableCell(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.end,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(kilometre![0].toStringAsFixed(1)),
+ if (kilometre!.length > 1) Text(kilometre![1].toStringAsFixed(1))
+ ],
+ ),
+ alignment: Alignment.centerLeft);
}
DASTableCell timeCell() {
@@ -58,10 +63,7 @@ abstract class BaseRowBuilder extends DASTableRowBuilder {
return DASTableCell(
padding: EdgeInsets.all(0.0),
alignment: null,
- child: RouteCellBody(
- showCircle: isServicePointStop,
- showChevron: isCurrentPosition,
- ),
+ child: RouteCellBody(isCurrentPosition: isCurrentPosition),
);
}
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
new file mode 100644
index 00000000..869e42ee
--- /dev/null
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/cells/bracket_station_body.dart
@@ -0,0 +1,46 @@
+import 'package:das_client/model/journey/bracket_station.dart';
+import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:flutter/material.dart';
+
+class BracketStationBody extends StatelessWidget {
+ static const Key bracketStationKey = Key('bracketStationKey');
+ static const double _bracketStationWidth = 16.0;
+ static const double _bracketStationFontSize = 12.0;
+
+ const BracketStationBody({
+ super.key,
+ required this.bracketStation,
+ required this.height
+ });
+
+ final BracketStation bracketStation;
+ final double height;
+
+ @override
+ Widget build(BuildContext context) {
+ return Positioned(
+ top: -sbbDefaultSpacing,
+ bottom: -sbbDefaultSpacing,
+ right: 0,
+ child: Container(
+ key: bracketStationKey,
+ height: height,
+ width: _bracketStationWidth,
+ color: SBBColors.black,
+ child: Align(
+ alignment: Alignment.center,
+ child: RotatedBox(
+ quarterTurns: -1,
+ child: Text(
+ bracketStation.mainStationAbbreviation ?? '',
+ style: SBBTextStyles.extraSmallBold.copyWith(
+ color: SBBColors.white,
+ fontSize: _bracketStationFontSize,
+ height: _bracketStationWidth / _bracketStationFontSize),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
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 6563cec1..c195b6e1 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,15 +1,24 @@
+import 'package:das_client/app/widgets/table/das_table_theme.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:flutter/material.dart';
class RouteCellBody extends StatelessWidget {
+ static const Key stopKey = Key('stopRouteCell');
+ static const Key stopOnRequestKey = Key('stopOnRequestRouteCell');
+ static const Key routeStartKey = Key('startRouteCell');
+ static const Key routeEndKey = Key('endRouteCell');
+
const RouteCellBody({
super.key,
this.chevronHeight = 8.0,
this.chevronWidth = 16.0,
this.circleSize = 14.0,
this.lineThickness = 2.0,
- this.showCircle = false,
- this.showChevron = false,
+ this.isStop = false,
+ this.isStopOnRequest = false,
+ this.isCurrentPosition = false,
+ this.isRouteStart = false,
+ this.isRouteEnd = false,
});
final double chevronHeight;
@@ -17,28 +26,37 @@ class RouteCellBody extends StatelessWidget {
final double circleSize;
final double lineThickness;
- final bool showChevron;
- final bool showCircle;
+ final bool isCurrentPosition;
+ final bool isStop;
+ final bool isStopOnRequest;
+ final bool isRouteStart;
+ final bool isRouteEnd;
@override
Widget build(BuildContext context) {
- return Stack(
- clipBehavior: Clip.none,
- alignment: Alignment.center,
- children: [
- if (showChevron) _chevron(context),
- if (showCircle) _circle(context),
- _routeLine(context),
- ],
+ return LayoutBuilder(
+ builder: (context, constraints) {
+ final height = constraints.maxHeight;
+ return Stack(
+ clipBehavior: Clip.none,
+ alignment: Alignment.center,
+ children: [
+ _routeLine(context, height),
+ if (isCurrentPosition) _chevron(context),
+ if (isStop) _circle(context),
+ ],
+ );
+ },
);
}
- Positioned _routeLine(BuildContext context) {
+ Positioned _routeLine(BuildContext context, double height) {
final isDarkTheme = SBBBaseStyle.of(context).brightness == Brightness.dark;
final lineColor = isDarkTheme ? SBBColors.white : SBBColors.black;
return Positioned(
- top: -sbbDefaultSpacing,
- bottom: -sbbDefaultSpacing,
+ key: isRouteStart ? routeStartKey : isRouteEnd ? routeEndKey : null,
+ top: isRouteStart ? height - sbbDefaultSpacing : -sbbDefaultSpacing,
+ bottom: isRouteEnd ? sbbDefaultSpacing : -sbbDefaultSpacing,
right: 0,
left: 0,
child: VerticalDivider(thickness: lineThickness, color: lineColor),
@@ -50,14 +68,7 @@ class RouteCellBody extends StatelessWidget {
final circleColor = isDarkTheme ? SBBColors.sky : SBBColors.black;
return Positioned(
bottom: sbbDefaultSpacing,
- child: Container(
- width: circleSize,
- height: circleSize,
- decoration: BoxDecoration(
- color: circleColor,
- shape: BoxShape.circle,
- ),
- ),
+ child: _RouteCircle(size: circleSize, color: circleColor, isStopOnRequest: isStopOnRequest),
);
}
@@ -65,7 +76,7 @@ class RouteCellBody extends StatelessWidget {
final isDarkTheme = SBBBaseStyle.of(context).brightness == Brightness.dark;
final chevronColor = isDarkTheme ? SBBColors.sky : SBBColors.black;
return Positioned(
- bottom: showCircle ? sbbDefaultSpacing + circleSize : sbbDefaultSpacing,
+ bottom: isStop ? sbbDefaultSpacing + circleSize : sbbDefaultSpacing,
child: CustomPaint(
size: Size(chevronWidth, chevronHeight),
painter: _ChevronPainter(color: chevronColor),
@@ -74,6 +85,40 @@ class RouteCellBody extends StatelessWidget {
}
}
+class _RouteCircle extends StatelessWidget {
+ const _RouteCircle({
+ required this.size,
+ required this.color,
+ this.isStopOnRequest = false,
+ });
+
+ final bool isStopOnRequest;
+ final double size;
+ final Color color;
+
+ @override
+ Widget build(BuildContext context) {
+ final tableThemeData = DASTableTheme.of(context)?.data;
+ final tableBackgroundColor = tableThemeData?.backgroundColor ?? SBBColors.white;
+ return Container(
+ key: isStopOnRequest ? RouteCellBody.stopOnRequestKey : RouteCellBody.stopKey,
+ width: size,
+ height: size,
+ decoration: isStopOnRequest ? _stopOnRequestDecoration(backgroundColor: tableBackgroundColor) : _stopDecoration(),
+ );
+ }
+
+ BoxDecoration _stopDecoration() => BoxDecoration(color: color, shape: BoxShape.circle);
+
+ BoxDecoration _stopOnRequestDecoration({required Color backgroundColor}) => BoxDecoration(
+ color: backgroundColor,
+ shape: BoxShape.circle,
+ border: Border.all(
+ color: color, // Set the border color
+ width: 2.0, // Set the border width
+ ));
+}
+
class _ChevronPainter extends CustomPainter {
_ChevronPainter({this.color = SBBColors.black});
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 79f3113e..87edfd3a 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
@@ -1,42 +1,93 @@
import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.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/widgets/assets.dart';
import 'package:das_client/app/widgets/table/das_table_cell.dart';
-import 'package:das_client/sfera/sfera_component.dart';
+import 'package:das_client/model/journey/metadata.dart';
+import 'package:das_client/model/journey/service_point.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
// TODO: Extract real values from SFERA objects.
class ServicePointRow extends BaseRowBuilder {
+ static const Key stopOnRequestKey = Key('stop_on_request_key');
+
ServicePointRow({
super.height = 64.0,
super.defaultAlignment = _defaultAlignment,
- super.kilometre,
- super.isCurrentPosition,
- super.isServicePointStop,
- this.timingPoint,
- this.timingPointConstraints,
- bool nextStop = false,
+ this.isRouteStart = false,
+ this.isRouteEnd = false,
+ required this.metadata,
+ required this.servicePoint,
}) : super(
- rowColor: nextStop ? SBBColors.royal.withOpacity(0.2) : Colors.transparent,
- );
+ rowColor: metadata.nextStop == servicePoint ? SBBColors.royal.withOpacity(0.2) : Colors.transparent,
+ kilometre: servicePoint.kilometre,
+ isCurrentPosition: metadata.currentPosition == servicePoint);
- final TimingPoint? timingPoint;
- final TimingPointConstraints? timingPointConstraints;
+ final Metadata metadata;
+ final ServicePoint servicePoint;
static const Alignment _defaultAlignment = Alignment.bottomCenter;
+ final bool isRouteStart;
+ final bool isRouteEnd;
+
@override
DASTableCell informationCell() {
- final servicePointName = timingPoint?.names.first.name ?? 'Unknown';
+ final servicePointName = servicePoint.name.localized;
+ final textStyle = servicePoint.isStation
+ ? SBBTextStyles.largeBold.copyWith(fontSize: 24.0)
+ : SBBTextStyles.largeLight.copyWith(fontSize: 24.0, fontStyle: FontStyle.italic);
return DASTableCell(
alignment: _defaultAlignment,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
- Text(servicePointName, style: SBBTextStyles.largeBold.copyWith(fontSize: 24.0)),
+ Text(servicePointName, style: textStyle),
Spacer(),
Text('B12'),
],
),
);
}
+
+ @override
+ DASTableCell iconsCell1() {
+ return DASTableCell(
+ padding: EdgeInsets.fromLTRB(0, sbbDefaultSpacing * 0.5, 0, sbbDefaultSpacing * 0.5),
+ child: Stack(
+ clipBehavior: Clip.none,
+ children: [
+ if (servicePoint.bracketStation != null)
+ BracketStationBody(
+ bracketStation: servicePoint.bracketStation!,
+ height: height!,
+ ),
+ if (!servicePoint.mandatoryStop)
+ Align(
+ alignment: Alignment.bottomCenter,
+ child: SvgPicture.asset(
+ AppAssets.iconStopOnRequest,
+ key: stopOnRequestKey,
+ ))
+ ],
+ ),
+ );
+ }
+
+ @override
+ DASTableCell routeCell() {
+ return DASTableCell(
+ padding: EdgeInsets.all(0.0),
+ alignment: null,
+ child: RouteCellBody(
+ isStop: servicePoint.isStop,
+ isCurrentPosition: isCurrentPosition,
+ isRouteStart: isRouteStart,
+ isRouteEnd: isRouteEnd,
+ isStopOnRequest: !servicePoint.mandatoryStop,
+ ),
+ );
+ }
}
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 bbe7c764..46d4bbba 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,10 +3,11 @@ import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/service_point_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/sfera/sfera_component.dart';
+import 'package:das_client/model/journey/datatype.dart';
+import 'package:das_client/model/journey/journey.dart';
+import 'package:das_client/model/journey/service_point.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:flutter/material.dart';
-import 'package:rxdart/rxdart.dart';
class TrainJourney extends StatelessWidget {
const TrainJourney({super.key});
@@ -15,49 +16,40 @@ class TrainJourney extends StatelessWidget {
Widget build(BuildContext context) {
final bloc = context.trainJourneyCubit;
- return StreamBuilder>(
- stream: CombineLatestStream.list([bloc.journeyStream, bloc.segmentStream]),
+ return StreamBuilder(
+ stream: bloc.journeyStream,
builder: (context, snapshot) {
- final JourneyProfile? journeyProfile = snapshot.data?[0];
- final List segmentProfiles = snapshot.data?[1] ?? [];
- if (journeyProfile == null) {
+ final Journey? journey = snapshot.data;
+ if (journey == null) {
return Container();
}
- return _body(context, journeyProfile, segmentProfiles);
+ return _body(context, journey);
},
);
}
Widget _body(
BuildContext context,
- JourneyProfile journeyProfile,
- List segmentProfiles,
+ Journey journey,
) {
- final timingPoints = journeyProfile.segmentProfilesLists
- .expand((it) => it.timingPoints.toList().sublist(it == journeyProfile.segmentProfilesLists.first ? 0 : 1))
- .toList();
-
- final points = segmentProfiles.expand((it) => it.points?.timingPoints.toList() ?? []);
-
return Padding(
padding: const EdgeInsets.symmetric(horizontal: sbbDefaultSpacing * 0.5),
child: DASTable(
columns: _columns(context),
rows: [
- ...List.generate(timingPoints.length, (index) {
- final timingPoint = timingPoints[index];
- final tpId = timingPoint.timingPointReference.children.whereType().firstOrNull?.tpId;
- final tp = points.where((point) => point.id == tpId).firstOrNull;
+ ...List.generate(journey.data.length, (index) {
+ final rowData = journey.data[index];
- return ServicePointRow(
- kilometre: 10.2,
- timingPoint: tp,
- timingPointConstraints: timingPoint,
- nextStop: index == 1,
- isServicePointStop: index != 3 && index != 4,
- isCurrentPosition: index == 0 || index == 3,
- ).build(context);
+ switch (rowData.type) {
+ case Datatype.servicePoint:
+ return ServicePointRow(
+ metadata: journey.metadata,
+ servicePoint: rowData as ServicePoint,
+ isRouteStart: index == 0,
+ isRouteEnd: index == journey.data.length - 1,
+ ).build(context);
+ }
})
],
),
diff --git a/das_client/lib/app/widgets/assets.dart b/das_client/lib/app/widgets/assets.dart
new file mode 100644
index 00000000..96509497
--- /dev/null
+++ b/das_client/lib/app/widgets/assets.dart
@@ -0,0 +1,12 @@
+import 'package:flutter/material.dart';
+
+@immutable
+class AppAssets {
+ const AppAssets._();
+
+ static const String _dir = 'assets';
+ static const String _iconsDir = '$_dir/icons';
+
+ static const iconHeaderStop = '$_iconsDir/icon_header_stop.svg';
+ static const iconStopOnRequest = '$_iconsDir/icon_stop_on_request.svg';
+}
diff --git a/das_client/lib/app/widgets/table/das_table.dart b/das_client/lib/app/widgets/table/das_table.dart
index ee9ef72a..b07cf77d 100644
--- a/das_client/lib/app/widgets/table/das_table.dart
+++ b/das_client/lib/app/widgets/table/das_table.dart
@@ -1,3 +1,5 @@
+import 'dart:math';
+
import 'package:collection/collection.dart';
import 'package:das_client/app/widgets/table/das_table_cell.dart';
import 'package:das_client/app/widgets/table/das_table_column.dart';
@@ -11,6 +13,8 @@ import 'package:flutter/material.dart';
/// The [columns] parameter must not be empty, and all rows must have the same number of cells as columns.
@immutable
class DASTable extends StatelessWidget {
+ static const Key rowKey = Key('DAS-Table-row');
+
DASTable({
super.key,
required this.columns,
@@ -140,6 +144,8 @@ class DASTable extends StatelessWidget {
return Builder(builder: (context) {
final tableThemeData = DASTableTheme.of(context)?.data;
final effectiveAlignment = cell.alignment ?? column.alignment;
+ final BoxBorder? cellBorder =
+ cell.border ?? column.border ?? tableThemeData?.tableBorder?.toBoxBorder(isLastCell: isLast);
return _TableCellWrapper(
expanded: column.expanded,
width: column.width,
@@ -147,10 +153,11 @@ class DASTable extends StatelessWidget {
onTap: cell.onTap,
child: Container(
decoration: BoxDecoration(
- border: cell.border ?? column.border ?? tableThemeData?.tableBorder?.toBoxBorder(isLastCell: isLast),
+ border: cellBorder,
color: cell.color ?? row.color ?? column.color ?? tableThemeData?.dataRowColor,
),
- padding: cell.padding ?? column.padding ?? EdgeInsets.all(sbbDefaultSpacing * 0.5),
+ padding: _adjustPaddingToBorder(
+ cell.padding ?? column.padding ?? EdgeInsets.all(sbbDefaultSpacing * 0.5), cellBorder),
clipBehavior: cell.clipBehaviour,
child: DefaultTextStyle(
style: DefaultTextStyle.of(context).style.merge(tableThemeData?.dataTextStyle),
@@ -161,6 +168,18 @@ class DASTable extends StatelessWidget {
);
});
}
+
+ EdgeInsets? _adjustPaddingToBorder(EdgeInsets? padding, BoxBorder? border) {
+ if (padding == null || border == null) {
+ return padding;
+ }
+
+ final borderSideStart = border is BorderDirectional ? border.start : (border as Border).left;
+ final borderSideEnd = border is BorderDirectional ? border.end : (border as Border).right;
+
+ return EdgeInsets.fromLTRB(max(padding.left - borderSideStart.width, 0), max(padding.top - border.top.width, 0),
+ max(padding.right - borderSideEnd.width, 0), max(padding.bottom - border.bottom.width, 0));
+ }
}
extension _TableBorderExtension on TableBorder {
@@ -180,7 +199,7 @@ class _FlexibleHeightRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final row = Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: children);
+ final row = Row(key: DASTable.rowKey, crossAxisAlignment: CrossAxisAlignment.stretch, children: children);
if (fixedHeight != null) {
return SizedBox(height: fixedHeight, child: row);
}
diff --git a/das_client/lib/app/widgets/table/das_table_cell.dart b/das_client/lib/app/widgets/table/das_table_cell.dart
index 99bc78cc..8e065f6d 100644
--- a/das_client/lib/app/widgets/table/das_table_cell.dart
+++ b/das_client/lib/app/widgets/table/das_table_cell.dart
@@ -24,7 +24,7 @@ class DASTableCell {
final Widget child;
final VoidCallback? onTap;
final Color? color;
- final EdgeInsetsGeometry? padding;
+ final EdgeInsets? padding;
final Clip clipBehaviour;
/// If provided, wraps child in Align widget. Can also be defined in [DASTableColumn]
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 bf5e9796..0af7e041 100644
--- a/das_client/lib/app/widgets/table/das_table_column.dart
+++ b/das_client/lib/app/widgets/table/das_table_column.dart
@@ -28,7 +28,7 @@ class DASTableColumn {
/// The background color for the heading and data cells
final Color? color;
- final EdgeInsetsGeometry? padding;
+ final EdgeInsets? padding;
/// Whether the column should expand to fill available space.
final bool expanded;
diff --git a/das_client/lib/model/journey/base_data.dart b/das_client/lib/model/journey/base_data.dart
new file mode 100644
index 00000000..8035118c
--- /dev/null
+++ b/das_client/lib/model/journey/base_data.dart
@@ -0,0 +1,9 @@
+import 'package:das_client/model/journey/datatype.dart';
+
+abstract class BaseData {
+ BaseData({required this.type, required this.order, required this.kilometre});
+
+ final Datatype type;
+ final int order;
+ final List kilometre;
+}
diff --git a/das_client/lib/model/journey/bracket_station.dart b/das_client/lib/model/journey/bracket_station.dart
new file mode 100644
index 00000000..3a3ccada
--- /dev/null
+++ b/das_client/lib/model/journey/bracket_station.dart
@@ -0,0 +1,7 @@
+
+
+class BracketStation {
+ BracketStation({this.mainStationAbbreviation});
+
+ final String? mainStationAbbreviation;
+}
diff --git a/das_client/lib/model/journey/datatype.dart b/das_client/lib/model/journey/datatype.dart
new file mode 100644
index 00000000..4ceefff2
--- /dev/null
+++ b/das_client/lib/model/journey/datatype.dart
@@ -0,0 +1,3 @@
+enum Datatype {
+ servicePoint;
+}
diff --git a/das_client/lib/model/journey/journey.dart b/das_client/lib/model/journey/journey.dart
new file mode 100644
index 00000000..3219d4fd
--- /dev/null
+++ b/das_client/lib/model/journey/journey.dart
@@ -0,0 +1,13 @@
+import 'package:das_client/model/journey/base_data.dart';
+import 'package:das_client/model/journey/metadata.dart';
+
+class Journey {
+ const Journey({required this.metadata, required this.data, this.valid = true});
+
+ final Metadata metadata;
+ final List data;
+ final bool valid;
+
+ Journey.invalid({Metadata? metadata, List? data})
+ : this(metadata: metadata ?? Metadata(), data: data ?? [], valid: false);
+}
diff --git a/das_client/lib/model/journey/metadata.dart b/das_client/lib/model/journey/metadata.dart
new file mode 100644
index 00000000..949a3fb2
--- /dev/null
+++ b/das_client/lib/model/journey/metadata.dart
@@ -0,0 +1,9 @@
+import 'package:das_client/model/journey/base_data.dart';
+import 'package:das_client/model/journey/service_point.dart';
+
+class Metadata {
+ const Metadata({this.nextStop, this.currentPosition});
+
+ final ServicePoint? nextStop;
+ final BaseData? currentPosition;
+}
diff --git a/das_client/lib/model/journey/service_point.dart b/das_client/lib/model/journey/service_point.dart
new file mode 100644
index 00000000..ed829a03
--- /dev/null
+++ b/das_client/lib/model/journey/service_point.dart
@@ -0,0 +1,22 @@
+import 'package:das_client/model/journey/base_data.dart';
+import 'package:das_client/model/journey/bracket_station.dart';
+import 'package:das_client/model/journey/datatype.dart';
+import 'package:das_client/model/localized_string.dart';
+
+class ServicePoint extends BaseData {
+ ServicePoint(
+ {required this.name,
+ required this.mandatoryStop,
+ required this.isStop,
+ required this.isStation,
+ this.bracketStation,
+ required super.order,
+ required super.kilometre})
+ : super(type: Datatype.servicePoint);
+
+ final LocalizedString name;
+ final bool mandatoryStop;
+ final bool isStop;
+ final bool isStation;
+ final BracketStation? bracketStation;
+}
diff --git a/das_client/lib/model/localized_string.dart b/das_client/lib/model/localized_string.dart
new file mode 100644
index 00000000..6c6b1b5f
--- /dev/null
+++ b/das_client/lib/model/localized_string.dart
@@ -0,0 +1,26 @@
+import 'dart:io';
+
+class LocalizedString {
+ LocalizedString({
+ this.de,
+ this.fr,
+ this.it,
+ });
+
+ late String? de;
+ late String? fr;
+ late String? it;
+
+ String get localized {
+ final localeName = Platform.localeName;
+ if (localeName.startsWith('fr') && fr != null) {
+ return fr!;
+ } else if (localeName.startsWith('it') && it != null) {
+ return it!;
+ } else if (localeName.startsWith('de') && de != null) {
+ return de!;
+ } else {
+ return de ?? fr ?? it ?? '';
+ }
+ }
+}
diff --git a/das_client/lib/sfera/sfera_component.dart b/das_client/lib/sfera/sfera_component.dart
index d1d71cc4..777be722 100644
--- a/das_client/lib/sfera/sfera_component.dart
+++ b/das_client/lib/sfera/sfera_component.dart
@@ -6,36 +6,7 @@ import 'package:das_client/sfera/src/service/sfera_auth_service.dart';
import 'package:das_client/sfera/src/service/sfera_service.dart';
import 'package:das_client/sfera/src/service/sfera_service_impl.dart';
-export 'package:das_client/sfera/src/model/b2g_request.dart';
-export 'package:das_client/sfera/src/model/das_operating_modes_selected.dart';
-export 'package:das_client/sfera/src/model/das_operating_modes_supported.dart';
-export 'package:das_client/sfera/src/model/g2b_reply_payload.dart';
-export 'package:das_client/sfera/src/model/handshake_acknowledgement.dart';
-export 'package:das_client/sfera/src/model/handshake_reject.dart';
-export 'package:das_client/sfera/src/model/handshake_request.dart';
-export 'package:das_client/sfera/src/model/journey_profile.dart';
-export 'package:das_client/sfera/src/model/jp_request.dart';
-export 'package:das_client/sfera/src/model/message_header.dart';
export 'package:das_client/sfera/src/model/otn_id.dart';
-export 'package:das_client/sfera/src/model/segment_profile.dart';
-export 'package:das_client/sfera/src/model/segment_profile_list.dart';
-export 'package:das_client/sfera/src/model/sfera_b2g_request_message.dart';
-export 'package:das_client/sfera/src/model/sfera_g2b_reply_message.dart';
-export 'package:das_client/sfera/src/model/sfera_xml_element.dart';
-export 'package:das_client/sfera/src/model/signal.dart';
-export 'package:das_client/sfera/src/model/signal_id.dart';
-export 'package:das_client/sfera/src/model/sp_points.dart';
-export 'package:das_client/sfera/src/model/sp_request.dart';
-export 'package:das_client/sfera/src/model/sp_zone.dart';
-export 'package:das_client/sfera/src/model/stopping_point_information.dart';
-export 'package:das_client/sfera/src/model/timing_point.dart';
-export 'package:das_client/sfera/src/model/timing_point_constraints.dart';
-export 'package:das_client/sfera/src/model/timing_point_reference.dart';
-export 'package:das_client/sfera/src/model/tp_id_reference.dart';
-export 'package:das_client/sfera/src/model/tp_name.dart';
-export 'package:das_client/sfera/src/model/train_identification.dart';
-export 'package:das_client/sfera/src/model/virtual_balise.dart';
-export 'package:das_client/sfera/src/model/virtual_balise_position.dart';
export 'package:das_client/sfera/src/repo/sfera_repository.dart';
export 'package:das_client/sfera/src/service/sfera_auth_service.dart';
export 'package:das_client/sfera/src/service/sfera_service.dart';
diff --git a/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart b/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart
new file mode 100644
index 00000000..46f82445
--- /dev/null
+++ b/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart
@@ -0,0 +1,132 @@
+import 'package:das_client/model/journey/base_data.dart';
+import 'package:das_client/model/journey/bracket_station.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/service_point.dart';
+import 'package:das_client/model/localized_string.dart';
+import 'package:das_client/sfera/src/model/enums/stop_skip_pass.dart';
+import 'package:das_client/sfera/src/model/enums/taf_tap_location_type.dart';
+import 'package:das_client/sfera/src/model/journey_profile.dart';
+import 'package:das_client/sfera/src/model/multilingual_text.dart';
+import 'package:das_client/sfera/src/model/segment_profile.dart';
+import 'package:das_client/sfera/src/model/taf_tap_location.dart';
+import 'package:fimber/fimber.dart';
+
+class SferaModelMapper {
+ SferaModelMapper._();
+
+ static const int _hundredThousand = 100000;
+ static const String _bracketStationNspName = 'bracketStation';
+ static const String _bracketStationMainStationNspName = 'mainStation';
+
+ static Journey mapToJourney(JourneyProfile journeyProfile, List segmentProfiles) {
+ try {
+ return _mapToJourney(journeyProfile, segmentProfiles);
+ } 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) {
+ final journeyData = [];
+
+ final segmentProfilesLists = journeyProfile.segmentProfilesLists.toList();
+
+ final tafTapLocations = segmentProfiles.expand((it) => it.areas).expand((it) => it.tafTapLocations).toList();
+
+ for (int segmentIndex = 0; segmentIndex < segmentProfilesLists.length; segmentIndex++) {
+ final segmentProfileList = segmentProfilesLists[segmentIndex];
+ final segmentProfile = segmentProfiles
+ .where((it) =>
+ it.id == segmentProfileList.spId &&
+ it.versionMajor == segmentProfileList.versionMajor &&
+ it.versionMinor == segmentProfileList.versionMinor)
+ .first;
+
+ final kilometreMap = _parseKilometre(segmentProfile);
+ final timingPoints = segmentProfile.points.expand((it) => it.timingPoints).toList();
+
+ for (final tpConstraint in segmentProfileList.timingPointsContraints) {
+ final tpId = tpConstraint.timingPointReference.tpIdReference.tpId;
+ final timingPoint = timingPoints.where((it) => it.id == tpId).first;
+ final tafTapLocation = tafTapLocations
+ .where((it) =>
+ it.locationIdent.countryCodeISO == timingPoint.locationReference?.countryCodeISO &&
+ it.locationIdent.locationPrimaryCode == timingPoint.locationReference?.locationPrimaryCode)
+ .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] ?? []));
+ }
+ }
+
+ journeyData.sort((a, b) => a.order.compareTo(b.order));
+ final servicePoints = journeyData.where((it) => it.type == Datatype.servicePoint).toList();
+ return Journey(
+ metadata: Metadata(
+ nextStop: servicePoints.length > 1 ? servicePoints[1] as ServicePoint : null,
+ currentPosition: journeyData.first),
+ data: journeyData);
+ }
+
+ static int _calculateOrder(int segmentIndex, double location) {
+ return (segmentIndex * _hundredThousand + location).toInt();
+ }
+
+ static LocalizedString _localizedStringFromMultilingualText(Iterable multilingualText) {
+ return LocalizedString(
+ de: multilingualText.where((it) => it.language == 'de').firstOrNull?.messageString,
+ fr: multilingualText.where((it) => it.language == 'fr').firstOrNull?.messageString,
+ it: multilingualText.where((it) => it.language == 'it').firstOrNull?.messageString,
+ );
+ }
+
+ static Map> _parseKilometre(SegmentProfile segmentProfile) {
+ final kilometreMap = >{};
+ for (final point in segmentProfile.contextInformation) {
+ for (final kilometreReferencePoint in point.kilometreReferencePoints) {
+ if (!kilometreMap.containsKey(kilometreReferencePoint.location)) {
+ kilometreMap[kilometreReferencePoint.location] = [];
+ }
+ kilometreMap[kilometreReferencePoint.location]!.add(kilometreReferencePoint.kmReference.kmRef);
+ }
+ }
+ return kilometreMap;
+ }
+
+ 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;
+ if (mainStationNsp == null) {
+ Fimber.w('Encountered bracket station without main station NSP declaration: $tafTapLocation');
+ } else {
+ final countryCode = mainStationNsp.nspValue.substring(0, 2);
+ final primaryCode = int.parse(mainStationNsp.nspValue.substring(2, 6));
+ final mainStation = allLocations
+ .where((it) =>
+ it.locationIdent.countryCodeISO == countryCode && it.locationIdent.locationPrimaryCode == primaryCode)
+ .firstOrNull;
+ if (mainStation == null) {
+ Fimber.w('Failed to resolve main station for bracket station: $tafTapLocation');
+ } else {
+ return BracketStation(
+ mainStationAbbreviation: mainStation != tafTapLocation ? mainStation.abbreviation : null);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/das_client/lib/sfera/src/model/enums/stop_skip_pass.dart b/das_client/lib/sfera/src/model/enums/stop_skip_pass.dart
new file mode 100644
index 00000000..60eda705
--- /dev/null
+++ b/das_client/lib/sfera/src/model/enums/stop_skip_pass.dart
@@ -0,0 +1,14 @@
+import 'package:das_client/sfera/src/model/enums/xml_enum.dart';
+
+enum StopSkipPass implements XmlEnum {
+ stoppingPoint(xmlValue: 'Stopping_Point'),
+ skippedStoppingPoint(xmlValue: 'Skipped_Stopping_Point'),
+ passingPoint(xmlValue: 'Passing_Point');
+
+ const StopSkipPass({
+ required this.xmlValue,
+ });
+
+ @override
+ final String xmlValue;
+}
diff --git a/das_client/lib/sfera/src/model/enums/taf_tap_location_type.dart b/das_client/lib/sfera/src/model/enums/taf_tap_location_type.dart
new file mode 100644
index 00000000..da2e3be0
--- /dev/null
+++ b/das_client/lib/sfera/src/model/enums/taf_tap_location_type.dart
@@ -0,0 +1,13 @@
+import 'package:das_client/sfera/src/model/enums/xml_enum.dart';
+
+enum TafTapLocationType implements XmlEnum {
+ station(xmlValue: 'station'),
+ stoppingLocation(xmlValue: 'stopping location');
+
+ const TafTapLocationType({
+ required this.xmlValue,
+ });
+
+ @override
+ final String xmlValue;
+}
diff --git a/das_client/lib/sfera/src/model/enums/xml_enum.dart b/das_client/lib/sfera/src/model/enums/xml_enum.dart
index 6b59ad07..77b94016 100644
--- a/das_client/lib/sfera/src/model/enums/xml_enum.dart
+++ b/das_client/lib/sfera/src/model/enums/xml_enum.dart
@@ -3,6 +3,6 @@ abstract interface class XmlEnum {
String get xmlValue;
static T? valueOf(List allValues, String? xmlValue) {
- return allValues.where((it) => it.xmlValue == xmlValue).firstOrNull;
+ return allValues.where((it) => it.xmlValue.toLowerCase() == xmlValue?.toLowerCase()).firstOrNull;
}
}
diff --git a/das_client/lib/sfera/src/model/handshake_acknowledgement.dart b/das_client/lib/sfera/src/model/handshake_acknowledgement.dart
index a4708357..5fc87d70 100644
--- a/das_client/lib/sfera/src/model/handshake_acknowledgement.dart
+++ b/das_client/lib/sfera/src/model/handshake_acknowledgement.dart
@@ -10,6 +10,6 @@ class HandshakeAcknowledgement extends SferaXmlElement {
@override
bool validate() {
- return validateHasChild('DAS_OperatingModeSelected') && super.validate();
+ return validateHasChildOfType() && super.validate();
}
}
diff --git a/das_client/lib/sfera/src/model/journey_profile.dart b/das_client/lib/sfera/src/model/journey_profile.dart
index 0cd8dc85..b363682a 100644
--- a/das_client/lib/sfera/src/model/journey_profile.dart
+++ b/das_client/lib/sfera/src/model/journey_profile.dart
@@ -17,6 +17,6 @@ class JourneyProfile extends SferaXmlElement {
@override
bool validate() {
- return validateHasChild('TrainIdentification') && super.validate();
+ return validateHasChildOfType() && super.validate();
}
}
diff --git a/das_client/lib/sfera/src/model/kilometre_reference_point.dart b/das_client/lib/sfera/src/model/kilometre_reference_point.dart
new file mode 100644
index 00000000..a0de91bf
--- /dev/null
+++ b/das_client/lib/sfera/src/model/kilometre_reference_point.dart
@@ -0,0 +1,15 @@
+import 'package:das_client/sfera/src/model/km_reference.dart';
+import 'package:das_client/sfera/src/model/sp_generic_point.dart';
+
+class KilometreReferencePoint extends SpGenericPoint {
+ static const String elementType = 'KilometreReferencePoint';
+
+ KilometreReferencePoint({super.type = elementType, super.attributes, super.children, super.value});
+
+ KmReference get kmReference => children.whereType().first;
+
+ @override
+ bool validate() {
+ return validateHasChildOfType() && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/km_reference.dart b/das_client/lib/sfera/src/model/km_reference.dart
new file mode 100644
index 00000000..5f670798
--- /dev/null
+++ b/das_client/lib/sfera/src/model/km_reference.dart
@@ -0,0 +1,14 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class KmReference extends SferaXmlElement {
+ static const String elementType = 'KM_Reference';
+
+ KmReference({super.type = elementType, super.attributes, super.children, super.value});
+
+ double get kmRef => double.parse(attributes['kmRef']!);
+
+ @override
+ bool validate() {
+ return validateHasAttributeDouble('kmRef') && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/location_ident.dart b/das_client/lib/sfera/src/model/location_ident.dart
new file mode 100644
index 00000000..e8aae6ab
--- /dev/null
+++ b/das_client/lib/sfera/src/model/location_ident.dart
@@ -0,0 +1,16 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class LocationIdent extends SferaXmlElement {
+ static const String elementType = 'LocationIdent';
+
+ LocationIdent({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get countryCodeISO => childrenWithType('CountryCodeISO').first.value!;
+
+ int get locationPrimaryCode => int.parse(childrenWithType('LocationPrimaryCode').first.value!);
+
+ @override
+ bool validate() {
+ return validateHasChild('CountryCodeISO') && validateHasChildInt('LocationPrimaryCode') && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/multilingual_text.dart b/das_client/lib/sfera/src/model/multilingual_text.dart
new file mode 100644
index 00000000..54c99caa
--- /dev/null
+++ b/das_client/lib/sfera/src/model/multilingual_text.dart
@@ -0,0 +1,16 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class MultilingualText extends SferaXmlElement {
+ static const String elementType = 'MultilingualText';
+
+ MultilingualText({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get language => attributes['language']!;
+
+ String get messageString => attributes['messageString']!;
+
+ @override
+ bool validate() {
+ return validateHasAttribute('language') && validateHasAttribute('messageString') && super.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
new file mode 100644
index 00000000..aa1b3f8d
--- /dev/null
+++ b/das_client/lib/sfera/src/model/network_specific_parameter.dart
@@ -0,0 +1,16 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class NetworkSpecificParameter extends SferaXmlElement {
+ static const String elementType = 'NetworkSpecificParameter';
+
+ NetworkSpecificParameter({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get name => attributes['name']!;
+
+ String get nspValue => attributes['value']!;
+
+ @override
+ bool validate() {
+ return validateHasAttribute('name') && validateHasAttribute('value') && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/nsp.dart b/das_client/lib/sfera/src/model/nsp.dart
new file mode 100644
index 00000000..a0e1b41e
--- /dev/null
+++ b/das_client/lib/sfera/src/model/nsp.dart
@@ -0,0 +1,17 @@
+import 'package:das_client/sfera/src/model/network_specific_parameter.dart';
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+abstract class Nsp extends SferaXmlElement {
+ static const String elementType = 'NSP';
+
+ Nsp({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get name => attributes['name']!;
+
+ Iterable get networkSpecificParameters => children.whereType();
+
+ @override
+ bool validate() {
+ return validateHasAttribute('name') && validateHasChildOfType() && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/segment_profile.dart b/das_client/lib/sfera/src/model/segment_profile.dart
index 60714fdf..c1706fcd 100644
--- a/das_client/lib/sfera/src/model/segment_profile.dart
+++ b/das_client/lib/sfera/src/model/segment_profile.dart
@@ -1,6 +1,8 @@
import 'package:das_client/sfera/src/model/enums/sp_status.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/sp_areas.dart';
+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';
@@ -21,7 +23,11 @@ class SegmentProfile extends SferaXmlElement {
SpZone? get zone => children.whereType().firstOrNull;
- SpPoints? get points => children.whereType().firstOrNull;
+ Iterable get points => children.whereType();
+
+ Iterable get contextInformation => children.whereType();
+
+ Iterable get areas => 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 9152756a..c34ca5dd 100644
--- a/das_client/lib/sfera/src/model/segment_profile_list.dart
+++ b/das_client/lib/sfera/src/model/segment_profile_list.dart
@@ -15,7 +15,7 @@ class SegmentProfileList extends SferaXmlElement {
SpZone get spZone => children.whereType().first;
- Iterable get timingPoints => children.whereType();
+ Iterable get timingPointsContraints => children.whereType();
@override
bool validate() {
diff --git a/das_client/lib/sfera/src/model/sfera_xml_element.dart b/das_client/lib/sfera/src/model/sfera_xml_element.dart
index 675f315f..a6c794c3 100644
--- a/das_client/lib/sfera/src/model/sfera_xml_element.dart
+++ b/das_client/lib/sfera/src/model/sfera_xml_element.dart
@@ -1,5 +1,6 @@
import 'package:das_client/sfera/src/model/enums/xml_enum.dart';
import 'package:fimber/fimber.dart';
+import 'package:flutter/material.dart';
import 'package:xml/xml.dart';
class SferaXmlElement {
@@ -12,6 +13,7 @@ class SferaXmlElement {
: attributes = attributes ?? {},
children = children ?? [];
+ @mustCallSuper
bool validate() {
return children.every((it) => it.validate());
}
@@ -25,6 +27,21 @@ class SferaXmlElement {
return true;
}
+ bool validateHasAttributeDouble(String attribute) {
+ if (!attributes.containsKey(attribute)) {
+ Fimber.w('Validation failed for $type because attribute=$attribute is missing');
+ return false;
+ }
+
+ if (double.tryParse(attributes[attribute]!) == null) {
+ Fimber.w(
+ 'Validation failed for $type because attribute=$attribute with value=${attributes[attribute]} could not be parsed to double');
+ return false;
+ }
+
+ return true;
+ }
+
bool validateHasChild(String type) {
if (childrenWithType(type).isEmpty) {
Fimber.w('Validation failed for ${this.type} because it has no child of type $type');
@@ -34,6 +51,22 @@ class SferaXmlElement {
return true;
}
+ bool validateHasChildInt(String type) {
+ if (!validateHasChild(type)) {
+ return false;
+ }
+
+ final childValue = childrenWithType(type).first.value;
+
+ if (childValue == null || int.tryParse(childValue) == null) {
+ Fimber.w(
+ 'Validation failed for ${this.type} because child of type=$type with value=$childValue could not be parsed to int');
+ return false;
+ }
+
+ return true;
+ }
+
bool validateHasChildOfType() {
if (children.whereType().isEmpty) {
Fimber.w('Validation failed for $type because it has no child of type ${T.toString()}');
@@ -88,4 +121,9 @@ class SferaXmlElement {
}
});
}
+
+ @override
+ String toString() {
+ return buildDocument().toString();
+ }
}
diff --git a/das_client/lib/sfera/src/model/signal.dart b/das_client/lib/sfera/src/model/signal.dart
index d0eaa9ee..ef335c70 100644
--- a/das_client/lib/sfera/src/model/signal.dart
+++ b/das_client/lib/sfera/src/model/signal.dart
@@ -10,6 +10,6 @@ class Signal extends SferaXmlElement {
@override
bool validate() {
- return validateHasChild('Signal_ID') && super.validate();
+ return validateHasChildOfType() && super.validate();
}
}
diff --git a/das_client/lib/sfera/src/model/sp_areas.dart b/das_client/lib/sfera/src/model/sp_areas.dart
new file mode 100644
index 00000000..a3b1b89e
--- /dev/null
+++ b/das_client/lib/sfera/src/model/sp_areas.dart
@@ -0,0 +1,10 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/taf_tap_location.dart';
+
+class SpAreas extends SferaXmlElement {
+ static const String elementType = 'SP_Areas';
+
+ SpAreas({super.type = elementType, super.attributes, super.children, super.value});
+
+ Iterable get tafTapLocations => children.whereType();
+}
diff --git a/das_client/lib/sfera/src/model/sp_context_information.dart b/das_client/lib/sfera/src/model/sp_context_information.dart
new file mode 100644
index 00000000..3a63a9cd
--- /dev/null
+++ b/das_client/lib/sfera/src/model/sp_context_information.dart
@@ -0,0 +1,10 @@
+import 'package:das_client/sfera/src/model/kilometre_reference_point.dart';
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class SpContextInformation extends SferaXmlElement {
+ static const String elementType = 'SP_ContextInformation';
+
+ SpContextInformation({super.type = elementType, super.attributes, super.children, super.value});
+
+ Iterable get kilometreReferencePoints => children.whereType();
+}
diff --git a/das_client/lib/sfera/src/model/sp_generic_point.dart b/das_client/lib/sfera/src/model/sp_generic_point.dart
new file mode 100644
index 00000000..dbd92169
--- /dev/null
+++ b/das_client/lib/sfera/src/model/sp_generic_point.dart
@@ -0,0 +1,12 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+abstract class SpGenericPoint extends SferaXmlElement {
+ SpGenericPoint({required super.type, super.attributes, super.children, super.value});
+
+ double get location => double.parse(attributes['location']!);
+
+ @override
+ bool validate() {
+ return validateHasAttributeDouble('location') && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/sp_zone.dart b/das_client/lib/sfera/src/model/sp_zone.dart
index 70dd8298..cfde7a4a 100644
--- a/das_client/lib/sfera/src/model/sp_zone.dart
+++ b/das_client/lib/sfera/src/model/sp_zone.dart
@@ -11,6 +11,6 @@ class SpZone extends SferaXmlElement {
@override
bool validate() {
- return validateHasChild('IM_ID') || validateHasChild('NID_C');
+ return (validateHasChild('IM_ID') || validateHasChild('NID_C')) && super.validate();
}
}
diff --git a/das_client/lib/sfera/src/model/stop_type.dart b/das_client/lib/sfera/src/model/stop_type.dart
new file mode 100644
index 00000000..4f7f9a95
--- /dev/null
+++ b/das_client/lib/sfera/src/model/stop_type.dart
@@ -0,0 +1,20 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class StopType extends SferaXmlElement {
+ static const String elementType = 'StopType';
+
+ StopType({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get stopTypePurpose => attributes['stopTypePurpose']!;
+
+ String? get stopTypeDetails => attributes['stopTypeDetails'];
+
+ bool? get plannedStop => bool.tryParse(attributes['plannedStop'] ?? '');
+
+ bool? get mandatoryStop => bool.tryParse(attributes['mandatoryStop'] ?? '');
+
+ @override
+ bool validate() {
+ return validateHasAttribute('stopTypePurpose') && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/stopping_point_information.dart b/das_client/lib/sfera/src/model/stopping_point_information.dart
index 8e671d77..735e003d 100644
--- a/das_client/lib/sfera/src/model/stopping_point_information.dart
+++ b/das_client/lib/sfera/src/model/stopping_point_information.dart
@@ -1,11 +1,14 @@
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/stop_type.dart';
class StoppingPointInformation extends SferaXmlElement {
static const String elementType = 'StoppingPointInformation';
StoppingPointInformation({super.type = elementType, super.attributes, super.children, super.value});
- String get departureTime => attributes['departureTime']!;
+ DateTime get departureTime => DateTime.parse(attributes['departureTime']!);
+
+ StopType? get stopType => children.whereType().firstOrNull;
@override
bool 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
new file mode 100644
index 00000000..576c3570
--- /dev/null
+++ b/das_client/lib/sfera/src/model/taf_tap_location.dart
@@ -0,0 +1,28 @@
+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/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';
+
+class TafTapLocation extends SferaXmlElement {
+ static const String elementType = 'TAF_TAP_Location';
+
+ TafTapLocation({super.type = elementType, super.attributes, super.children, super.value});
+
+ TafTapLocationIdent get locationIdent => children.whereType().first;
+
+ Iterable get locationNames => children.whereType();
+
+ TafTapLocationType? get locationType =>
+ XmlEnum.valueOf(TafTapLocationType.values, attributes['TAF_TAP_location_type']);
+
+ String get abbreviation => attributes['TAF_TAP_location_abbreviation'] ?? '';
+
+ Iterable get nsp => children.whereType();
+
+ @override
+ bool validate() {
+ return validateHasChildOfType() && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/taf_tap_location_ident.dart b/das_client/lib/sfera/src/model/taf_tap_location_ident.dart
new file mode 100644
index 00000000..00fe550a
--- /dev/null
+++ b/das_client/lib/sfera/src/model/taf_tap_location_ident.dart
@@ -0,0 +1,7 @@
+import 'package:das_client/sfera/src/model/location_ident.dart';
+
+class TafTapLocationIdent extends LocationIdent {
+ static const String elementType = 'TAF_TAP_LocationIdent';
+
+ TafTapLocationIdent({super.type = elementType, super.attributes, super.children, super.value});
+}
diff --git a/das_client/lib/sfera/src/model/taf_tap_location_name.dart b/das_client/lib/sfera/src/model/taf_tap_location_name.dart
new file mode 100644
index 00000000..3775ec45
--- /dev/null
+++ b/das_client/lib/sfera/src/model/taf_tap_location_name.dart
@@ -0,0 +1,7 @@
+import 'package:das_client/sfera/src/model/multilingual_text.dart';
+
+class TafTapLocationName extends MultilingualText {
+ static const String elementType = 'TAF_TAP_LocationName';
+
+ TafTapLocationName({super.type = elementType, super.attributes, super.children, super.value});
+}
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
new file mode 100644
index 00000000..0063b020
--- /dev/null
+++ b/das_client/lib/sfera/src/model/taf_tap_location_nsp.dart
@@ -0,0 +1,7 @@
+import 'package:das_client/sfera/src/model/nsp.dart';
+
+class TafTapLocationNsp extends Nsp {
+ static const String elementType = 'TAF_TAP_Location_NSP';
+
+ TafTapLocationNsp({super.type = elementType, super.attributes, super.children, super.value});
+}
diff --git a/das_client/lib/sfera/src/model/taf_tap_location_reference.dart b/das_client/lib/sfera/src/model/taf_tap_location_reference.dart
new file mode 100644
index 00000000..0ff4efef
--- /dev/null
+++ b/das_client/lib/sfera/src/model/taf_tap_location_reference.dart
@@ -0,0 +1,16 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class TafTapLocationReference extends SferaXmlElement {
+ static const String elementType = 'TAF_TAP_LocationReference';
+
+ TafTapLocationReference({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get countryCodeISO => childrenWithType('CountryCodeISO').first.value!;
+
+ int get locationPrimaryCode => int.parse(childrenWithType('LocationPrimaryCode').first.value!);
+
+ @override
+ bool validate() {
+ return validateHasChild('CountryCodeISO') && validateHasChildInt('LocationPrimaryCode') && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/timing_point.dart b/das_client/lib/sfera/src/model/timing_point.dart
index 4d63e09b..f569ecc2 100644
--- a/das_client/lib/sfera/src/model/timing_point.dart
+++ b/das_client/lib/sfera/src/model/timing_point.dart
@@ -1,4 +1,5 @@
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/taf_tap_location_reference.dart';
import 'package:das_client/sfera/src/model/tp_name.dart';
class TimingPoint extends SferaXmlElement {
@@ -8,12 +9,14 @@ class TimingPoint extends SferaXmlElement {
String get id => attributes['TP_ID']!;
- String get location => attributes['location']!;
+ double get location => double.parse(attributes['location']!);
Iterable get names => children.whereType();
+ TafTapLocationReference? get locationReference => children.whereType().firstOrNull;
+
@override
bool validate() {
- return validateHasAttribute('TP_ID') && validateHasAttribute('location') && super.validate();
+ return validateHasAttribute('TP_ID') && validateHasAttributeDouble('location') && super.validate();
}
}
diff --git a/das_client/lib/sfera/src/model/timing_point_constraints.dart b/das_client/lib/sfera/src/model/timing_point_constraints.dart
index db99abe6..c4fae387 100644
--- a/das_client/lib/sfera/src/model/timing_point_constraints.dart
+++ b/das_client/lib/sfera/src/model/timing_point_constraints.dart
@@ -1,4 +1,7 @@
+import 'package:das_client/sfera/src/model/enums/stop_skip_pass.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/stopping_point_information.dart';
import 'package:das_client/sfera/src/model/timing_point_reference.dart';
class TimingPointConstraints extends SferaXmlElement {
@@ -8,6 +11,11 @@ class TimingPointConstraints extends SferaXmlElement {
TimingPointReference get timingPointReference => children.whereType().first;
+ StoppingPointInformation? get stoppingPointInformation => children.whereType().firstOrNull;
+
+ StopSkipPass get stopSkipPass =>
+ XmlEnum.valueOf(StopSkipPass.values, attributes['TP_StopSkipPass']) ?? StopSkipPass.stoppingPoint;
+
@override
bool validate() {
return validateHasChildOfType() && super.validate();
diff --git a/das_client/lib/sfera/src/model/timing_point_reference.dart b/das_client/lib/sfera/src/model/timing_point_reference.dart
index db670a2c..0ecc8160 100644
--- a/das_client/lib/sfera/src/model/timing_point_reference.dart
+++ b/das_client/lib/sfera/src/model/timing_point_reference.dart
@@ -1,7 +1,16 @@
+
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/tp_id_reference.dart';
class TimingPointReference extends SferaXmlElement {
static const String elementType = 'TimingPointReference';
+ TpIdReference get tpIdReference => children.whereType().first;
+
TimingPointReference({super.type = elementType, super.attributes, super.children, super.value});
+
+ @override
+ bool validate() {
+ return validateHasChildOfType() && super.validate();
+ }
}
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 2a1aa298..e65e5fd4 100644
--- a/das_client/lib/sfera/src/repo/sfera_repository_impl.dart
+++ b/das_client/lib/sfera/src/repo/sfera_repository_impl.dart
@@ -1,6 +1,8 @@
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/model/journey_profile.dart';
+import 'package:das_client/sfera/src/model/segment_profile.dart';
import 'package:fimber/fimber.dart';
import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';
diff --git a/das_client/lib/sfera/src/service/sfera_service.dart b/das_client/lib/sfera/src/service/sfera_service.dart
index e7f264fd..9741db50 100644
--- a/das_client/lib/sfera/src/service/sfera_service.dart
+++ b/das_client/lib/sfera/src/service/sfera_service.dart
@@ -1,6 +1,9 @@
import 'dart:core';
+import 'package:das_client/model/journey/journey.dart';
import 'package:das_client/sfera/sfera_component.dart';
+import 'package:das_client/sfera/src/model/message_header.dart';
+import 'package:das_client/sfera/src/model/train_identification.dart';
import 'package:das_client/util/device_id_info.dart';
import 'package:das_client/util/error_code.dart';
import 'package:das_client/util/format.dart';
@@ -11,9 +14,7 @@ abstract class SferaService {
Stream get stateStream;
- Stream get journeyStream;
-
- Stream> get segmentStream;
+ Stream get journeyStream;
ErrorCode? get lastErrorCode;
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 bf337a6a..e7682c46 100644
--- a/das_client/lib/sfera/src/service/sfera_service_impl.dart
+++ b/das_client/lib/sfera/src/service/sfera_service_impl.dart
@@ -2,9 +2,14 @@ import 'dart:async';
import 'dart:core';
import 'package:das_client/auth/authentication_component.dart';
+import 'package:das_client/model/journey/journey.dart';
import 'package:das_client/mqtt/mqtt_component.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/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/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';
@@ -28,18 +33,14 @@ class SferaServiceImpl implements SferaService {
@override
Stream get stateStream => _stateSubject.stream;
-
- final _journeyProfileSubject = BehaviorSubject.seeded(null);
-
- @override
- Stream get journeyStream => _journeyProfileSubject.stream;
-
- final _segmentProfilesSubject = BehaviorSubject>();
+ final _journeyProfileSubject = BehaviorSubject.seeded(null);
@override
- Stream> get segmentStream => _segmentProfilesSubject.stream;
+ Stream get journeyStream => _journeyProfileSubject.stream;
- OtnId? otnId;
+ OtnId? _otnId;
+ JourneyProfile? _journeyProfile;
+ final List _segmentProfiles = [];
@override
ErrorCode? lastErrorCode;
@@ -75,7 +76,7 @@ class SferaServiceImpl implements SferaService {
@override
Future connect(OtnId otnId) async {
Fimber.i('Starting new connection for $otnId');
- this.otnId = otnId;
+ _otnId = otnId;
_messageHandlers.clear();
lastErrorCode = null;
_stateSubject.add(SferaServiceState.connecting);
@@ -90,7 +91,7 @@ class SferaServiceImpl implements SferaService {
_messageHandlers.add(handshakeTask);
handshakeTask.execute(onTaskCompleted, onTaskFailed);
} else {
- this.otnId = null;
+ _otnId = null;
lastErrorCode = ErrorCode.connectionFailed;
_stateSubject.add(SferaServiceState.disconnected);
}
@@ -102,37 +103,50 @@ class SferaServiceImpl implements SferaService {
if (task is HandshakeTask) {
_stateSubject.add(SferaServiceState.loadingJourney);
final requestJourneyTask =
- RequestJourneyProfileTask(mqttService: _mqttService, sferaRepository: _sferaRepository, otnId: otnId!);
+ RequestJourneyProfileTask(mqttService: _mqttService, sferaRepository: _sferaRepository, otnId: _otnId!);
_messageHandlers.add(requestJourneyTask);
requestJourneyTask.execute(onTaskCompleted, onTaskFailed);
} else if (task is RequestJourneyProfileTask) {
_stateSubject.add(SferaServiceState.loadingSegments);
final requestSegmentProfilesTask = RequestSegmentProfilesTask(
- mqttService: _mqttService, sferaRepository: _sferaRepository, otnId: otnId!, journeyProfile: data);
- _journeyProfileSubject.add(data);
+ mqttService: _mqttService, sferaRepository: _sferaRepository, otnId: _otnId!, journeyProfile: data);
+ _journeyProfile = data;
_messageHandlers.add(requestSegmentProfilesTask);
requestSegmentProfilesTask.execute(onTaskCompleted, onTaskFailed);
} else if (task is RequestSegmentProfilesTask) {
_addMessageHandlers();
await _refreshSegmentProfiles();
+ _updateJourney();
_stateSubject.add(SferaServiceState.connected);
}
}
Future _refreshSegmentProfiles() async {
- if (_journeyProfileSubject.value != null) {
- final segments = [];
+ if (_journeyProfile != null) {
+ _segmentProfiles.clear();
- for (final element in _journeyProfileSubject.value!.segmentProfilesLists) {
+ for (final element in _journeyProfile!.segmentProfilesLists) {
final segmentProfileEntity =
await _sferaRepository.findSegmentProfile(element.spId, element.versionMajor, element.versionMinor);
if (segmentProfileEntity != null) {
- segments.add(segmentProfileEntity.toDomain());
+ _segmentProfiles.add(segmentProfileEntity.toDomain());
} else {
Fimber.w('Could not find segment profile for ${element.spId}');
}
}
- _segmentProfilesSubject.add(segments);
+ }
+ }
+
+ void _updateJourney() {
+ if (_journeyProfile != null && _segmentProfiles.isNotEmpty) {
+ Fimber.i('Updating journey stream...');
+ final newJourney = SferaModelMapper.mapToJourney(_journeyProfile!, _segmentProfiles);
+ if (newJourney.valid) {
+ _journeyProfileSubject.add(SferaModelMapper.mapToJourney(_journeyProfile!, _segmentProfiles));
+ Fimber.i('Journey updates successfully.');
+ } else {
+ Fimber.w('Failed to update journey as it is not valid');
+ }
}
}
diff --git a/das_client/lib/sfera/src/sfera_reply_parser.dart b/das_client/lib/sfera/src/sfera_reply_parser.dart
index 97076363..1339a320 100644
--- a/das_client/lib/sfera/src/sfera_reply_parser.dart
+++ b/das_client/lib/sfera/src/sfera_reply_parser.dart
@@ -3,7 +3,12 @@ import 'package:das_client/sfera/src/model/g2b_reply_payload.dart';
import 'package:das_client/sfera/src/model/handshake_acknowledgement.dart';
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/location_ident.dart';
import 'package:das_client/sfera/src/model/message_header.dart';
+import 'package:das_client/sfera/src/model/multilingual_text.dart';
+import 'package:das_client/sfera/src/model/network_specific_parameter.dart';
import 'package:das_client/sfera/src/model/otn_id.dart';
import 'package:das_client/sfera/src/model/segment_profile.dart';
import 'package:das_client/sfera/src/model/segment_profile_list.dart';
@@ -11,9 +16,17 @@ import 'package:das_client/sfera/src/model/sfera_g2b_reply_message.dart';
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/signal_id.dart';
+import 'package:das_client/sfera/src/model/sp_areas.dart';
+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/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';
+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/timing_point.dart';
import 'package:das_client/sfera/src/model/timing_point_constraints.dart';
import 'package:das_client/sfera/src/model/timing_point_reference.dart';
@@ -101,6 +114,32 @@ class SferaReplyParser {
return HandshakeReject(type: type, attributes: attributes, children: children, value: value);
case DasOperatingModesSelected.elementType:
return DasOperatingModesSelected(type: type, attributes: attributes, children: children, value: value);
+ case SpContextInformation.elementType:
+ return SpContextInformation(type: type, attributes: attributes, children: children, value: value);
+ case KilometreReferencePoint.elementType:
+ return KilometreReferencePoint(type: type, attributes: attributes, children: children, value: value);
+ case KmReference.elementType:
+ return KmReference(type: type, attributes: attributes, children: children, value: value);
+ case SpAreas.elementType:
+ return SpAreas(type: type, attributes: attributes, children: children, value: value);
+ case TafTapLocation.elementType:
+ return TafTapLocation(type: type, attributes: attributes, children: children, value: value);
+ case LocationIdent.elementType:
+ return LocationIdent(type: type, attributes: attributes, children: children, value: value);
+ case TafTapLocationIdent.elementType:
+ return TafTapLocationIdent(type: type, attributes: attributes, children: children, value: value);
+ case MultilingualText.elementType:
+ return MultilingualText(type: type, attributes: attributes, children: children, value: value);
+ case TafTapLocationName.elementType:
+ return TafTapLocationName(type: type, attributes: attributes, children: children, value: value);
+ case TafTapLocationReference.elementType:
+ return TafTapLocationReference(type: type, attributes: attributes, children: children, value: value);
+ 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);
+ case NetworkSpecificParameter.elementType:
+ return NetworkSpecificParameter(type: type, attributes: attributes, children: children, value: value);
default:
return SferaXmlElement(type: type, attributes: attributes, children: children, value: value);
}
diff --git a/das_client/pubspec.lock b/das_client/pubspec.lock
index 55493976..c764109f 100644
--- a/das_client/pubspec.lock
+++ b/das_client/pubspec.lock
@@ -469,6 +469,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
+ flutter_svg:
+ dependency: "direct main"
+ description:
+ name: flutter_svg
+ sha256: "578bd8c508144fdaffd4f77b8ef2d8c523602275cd697cc3db284dbd762ef4ce"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.14"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -769,6 +777,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.0"
+ path_parsing:
+ dependency: transitive
+ description:
+ name: path_parsing
+ sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
path_provider:
dependency: "direct main"
description:
@@ -1074,6 +1090,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.5.1"
+ vector_graphics:
+ dependency: transitive
+ description:
+ name: vector_graphics
+ sha256: "773c9522d66d523e1c7b25dfb95cc91c26a1e17b107039cfe147285e92de7878"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.14"
+ vector_graphics_codec:
+ dependency: transitive
+ description:
+ name: vector_graphics_codec
+ sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.12"
+ vector_graphics_compiler:
+ dependency: transitive
+ description:
+ name: vector_graphics_compiler
+ sha256: ab9ff38fc771e9ee1139320adbe3d18a60327370c218c60752068ebee4b49ab1
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.15"
vector_math:
dependency: transitive
description:
diff --git a/das_client/pubspec.yaml b/das_client/pubspec.yaml
index b5315470..8b12709b 100644
--- a/das_client/pubspec.yaml
+++ b/das_client/pubspec.yaml
@@ -1,32 +1,11 @@
name: das_client
-description: "A new Flutter project."
-# The following line prevents the package from being accidentally published to
-# pub.dev using `flutter pub publish`. This is preferred for private packages.
+description: "DAS (Driver Advisory System) is a mobile application that provides all the required journey data to the train driver."
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
-
-# The following defines the version and build number for your application.
-# A version number is three numbers separated by dots, like 1.2.43
-# followed by an optional build number separated by a +.
-# Both the version and the builder number may be overridden in flutter
-# build by specifying --build-name and --build-number, respectively.
-# In Android, build-name is used as versionName while build-number used as versionCode.
-# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
-# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
-# Read more about iOS versioning at
-# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-# In Windows, build-name is used as the major, minor, and patch parts
-# of the product and file versions while build-number is used as the build suffix.
version: 0.2.1+37
environment:
sdk: '>=3.3.0 <4.0.0'
-# Dependencies specify other packages that your package needs in order to work.
-# To automatically upgrade your package dependencies to the latest versions
-# consider running `flutter pub upgrade --major-versions`. Alternatively,
-# dependencies can be manually updated by changing the version numbers below to
-# the latest version available on pub.dev. To see which dependencies have newer
-# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
@@ -82,6 +61,8 @@ dependencies:
package_info_plus: ^8.0.3
# https://pub.dev/packages/synchronized
synchronized: ^3.3.0
+ # https://pub.dev/packages/flutter_svg
+ flutter_svg: ^2.0.14
dev_dependencies:
integration_test:
@@ -95,11 +76,6 @@ dev_dependencies:
path_provider_platform_interface: any
plugin_platform_interface: any
- # The "flutter_lints" package below contains a set of recommended lints to
- # encourage good coding practices. The lint set provided by the package is
- # activated in the `analysis_options.yaml` file located at the root of your
- # package. See that file for information about deactivating specific lint
- # rules and activating additional ones.
flutter_lints: ^5.0.0
custom_lint:
das_custom_lints:
@@ -107,45 +83,9 @@ dev_dependencies:
flutter_launcher_icons: ^0.14.0
-# For information on the generic Dart part of this file, see the
-# following page: https://dart.dev/tools/pub/pubspec
-
-# The following section is specific to Flutter packages.
flutter:
-
generate: true
- # The following line ensures that the Material Icons font is
- # included with your application, so that you can use the icons in
- # the material Icons class.
uses-material-design: true
-
- # To add assets to your application, add an assets section, like this:
- # assets:
- # - images/a_dot_burr.jpeg
- # - images/a_dot_ham.jpeg
-
- # An image asset can refer to one or more resolution-specific "variants", see
- # https://flutter.dev/assets-and-images/#resolution-aware
-
- # For details regarding adding assets from package dependencies, see
- # https://flutter.dev/assets-and-images/#from-packages
-
- # To add custom fonts to your application, add a fonts section here,
- # in this "flutter" section. Each entry in this list should have a
- # "family" key with the font family name, and a "fonts" key with a
- # list giving the asset and other descriptors for the font. For
- # example:
- # fonts:
- # - family: Schyler
- # fonts:
- # - asset: fonts/Schyler-Regular.ttf
- # - asset: fonts/Schyler-Italic.ttf
- # style: italic
- # - family: Trajan Pro
- # fonts:
- # - asset: fonts/TrajanPro.ttf
- # - asset: fonts/TrajanPro_Bold.ttf
- # weight: 700
- #
- # For details regarding fonts from package dependencies,
- # see https://flutter.dev/custom-fonts/#from-packages
+ assets:
+ - assets/
+ - assets/icons/
diff --git a/das_client/test/sfera/mapper/sfera_mapper_test.dart b/das_client/test/sfera/mapper/sfera_mapper_test.dart
new file mode 100644
index 00000000..b1d9e735
--- /dev/null
+++ b/das_client/test/sfera/mapper/sfera_mapper_test.dart
@@ -0,0 +1,137 @@
+import 'dart:io';
+
+import 'package:das_client/model/journey/datatype.dart';
+import 'package:das_client/model/journey/journey.dart';
+import 'package:das_client/model/journey/service_point.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';
+import 'package:das_client/sfera/src/model/segment_profile.dart';
+import 'package:fimber/fimber.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ Fimber.plantTree(DebugTree());
+
+ List getFilesForSp(String baseName, int count) {
+ final files = [];
+ for (var i = 1; i <= count; i++) {
+ files.add(File('test_resources/sp/${baseName}_$i.xml'));
+ }
+ return files;
+ }
+
+ Journey getJourney(String trainNumber, int spCount) {
+ final journeyFile = File('test_resources/jp/SFERA_JP_$trainNumber.xml');
+ final journeyProfile = SferaReplyParser.parse(journeyFile.readAsStringSync());
+ expect(journeyProfile.validate(), true);
+ final List segmentProfiles = [];
+
+ for (final File file in getFilesForSp('SFERA_SP_$trainNumber', spCount)) {
+ final segmentProfile = SferaReplyParser.parse(file.readAsStringSync());
+ expect(segmentProfile.validate(), true);
+ segmentProfiles.add(segmentProfile);
+ }
+
+ return SferaModelMapper.mapToJourney(journeyProfile, segmentProfiles);
+ }
+
+ test('Test invalid journey on SP missing', () async {
+ final journey = getJourney('9999', 4);
+
+ expect(journey.valid, false);
+ });
+
+ test('Test service point names are resolved correctly', () async {
+ final journey = getJourney('9999', 5);
+
+ final servicePoints = journey.data.where((it) => it.type == Datatype.servicePoint).cast().toList();
+
+ expect(journey.valid, true);
+ expect(servicePoints, hasLength(5));
+ expect(servicePoints[0].name.de, 'Bahnhof A');
+ expect(servicePoints[1].name.de, 'Haltestelle B');
+ expect(servicePoints[2].name.de, 'Halt auf Verlangen C');
+ expect(servicePoints[3].name.de, 'Klammerbahnhof D');
+ expect(servicePoints[4].name.de, 'Klammerbahnhof D1');
+ });
+
+ test('Test kilometre are parsed correctly', () async {
+ final journey = getJourney('9999', 5);
+
+ expect(journey.valid, true);
+ expect(journey.data, hasLength(5));
+ expect(journey.data[0].kilometre[0], 0.5);
+ expect(journey.data[1].kilometre[0], 1.5);
+ expect(journey.data[2].kilometre[0], 2.4);
+ expect(journey.data[3].kilometre[0], 3.7);
+ expect(journey.data[3].kilometre[1], 0);
+ expect(journey.data[4].kilometre[0], 0.6);
+ });
+
+ test('Test order is generated correctly', () async {
+ final journey = getJourney('9999', 5);
+
+ expect(journey.valid, true);
+ expect(journey.data, hasLength(5));
+ expect(journey.data[0].order, 000500);
+ expect(journey.data[1].order, 100500);
+ expect(journey.data[2].order, 200400);
+ expect(journey.data[3].order, 300700);
+ expect(journey.data[4].order, 400300);
+ });
+
+ test('Test stop on demand is parsed correctly', () async {
+ final journey = getJourney('9999', 5);
+ final servicePoints = journey.data.where((it) => it.type == Datatype.servicePoint).cast().toList();
+
+ expect(journey.valid, true);
+ expect(servicePoints, hasLength(5));
+ expect(servicePoints[0].mandatoryStop, true);
+ expect(servicePoints[1].mandatoryStop, true);
+ expect(servicePoints[2].mandatoryStop, false);
+ expect(servicePoints[3].mandatoryStop, true);
+ expect(servicePoints[4].mandatoryStop, true);
+ });
+
+ test('Test passing point is parsed correctly', () async {
+ final journey = getJourney('9999', 5);
+ final servicePoints = journey.data.where((it) => it.type == Datatype.servicePoint).cast().toList();
+
+ expect(journey.valid, true);
+ expect(servicePoints, hasLength(5));
+ expect(servicePoints[0].isStop, true);
+ expect(servicePoints[1].isStop, false);
+ expect(servicePoints[2].isStop, true);
+ expect(servicePoints[3].isStop, true);
+ expect(servicePoints[4].isStop, true);
+ });
+
+ test('Test station point is parsed correctly', () async {
+ final journey = getJourney('9999', 5);
+ final servicePoints = journey.data.where((it) => it.type == Datatype.servicePoint).cast().toList();
+
+ expect(journey.valid, true);
+ expect(servicePoints, hasLength(5));
+ expect(servicePoints[0].isStation, true);
+ expect(servicePoints[1].isStation, true);
+ expect(servicePoints[2].isStation, false);
+ expect(servicePoints[3].isStation, true);
+ expect(servicePoints[4].isStation, true);
+ });
+
+ test('Test bracket stations is parsed correctly', () async {
+ final journey = getJourney('9999', 5);
+ final servicePoints = journey.data.where((it) => it.type == Datatype.servicePoint).cast().toList();
+
+ expect(journey.valid, true);
+ expect(servicePoints, hasLength(5));
+ expect(servicePoints[0].bracketStation, isNull);
+ expect(servicePoints[1].bracketStation, isNull);
+ expect(servicePoints[2].bracketStation, isNull);
+ expect(servicePoints[3].bracketStation, isNotNull);
+ expect(servicePoints[3].bracketStation!.mainStationAbbreviation, isNull);
+ expect(servicePoints[4].bracketStation, isNotNull);
+ expect(servicePoints[4].bracketStation!.mainStationAbbreviation, 'D');
+ });
+}
diff --git a/das_client/test/sfera/model/sfera_document_test.dart b/das_client/test/sfera/model/sfera_document_test.dart
index ad6dbdf4..e44ffa43 100644
--- a/das_client/test/sfera/model/sfera_document_test.dart
+++ b/das_client/test/sfera/model/sfera_document_test.dart
@@ -56,19 +56,21 @@ void main() {
expect(payload.segmentProfiles.first.zone, isNotNull);
expect(payload.segmentProfiles.first.zone!.imId, '0088');
- expect(payload.segmentProfiles.first.points!.timingPoints, hasLength(3));
- expect(payload.segmentProfiles.first.points!.timingPoints.first.id, '1837');
- expect(payload.segmentProfiles.first.points!.timingPoints.first.location, '0');
- expect(payload.segmentProfiles.first.points!.timingPoints.first.names.first.name, 'MEER-GRENS');
-
- expect(payload.segmentProfiles.first.points!.signals, hasLength(9));
- expect(payload.segmentProfiles.first.points!.signals.first.id.physicalId, '102346');
- expect(payload.segmentProfiles.first.points!.signals.first.id.location, '843');
-
- expect(payload.segmentProfiles.first.points!.balise, hasLength(3));
- expect(payload.segmentProfiles.first.points!.balise.first.location, '0');
- expect(payload.segmentProfiles.first.points!.balise.first.position.latitude, '51.48591');
- expect(payload.segmentProfiles.first.points!.balise.first.position.longitude, '4.73459');
+ final spPoint = payload.segmentProfiles.first.points.first;
+
+ expect(spPoint.timingPoints, hasLength(3));
+ expect(spPoint.timingPoints.first.id, '1837');
+ expect(spPoint.timingPoints.first.location, 0);
+ expect(spPoint.timingPoints.first.names.first.name, 'MEER-GRENS');
+
+ expect(spPoint.signals, hasLength(9));
+ expect(spPoint.signals.first.id.physicalId, '102346');
+ expect(spPoint.signals.first.id.location, '843');
+
+ 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');
});
test('Test Sfera HandshakeRequest generation', () async {
diff --git a/das_client/test/sfera/service/sfera_handshake_task_test.dart b/das_client/test/sfera/service/sfera_handshake_task_test.dart
index 683feef1..7061eb66 100644
--- a/das_client/test/sfera/service/sfera_handshake_task_test.dart
+++ b/das_client/test/sfera/service/sfera_handshake_task_test.dart
@@ -3,6 +3,7 @@ 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/enums/das_driving_mode.dart';
+import 'package:das_client/sfera/src/model/sfera_g2b_reply_message.dart';
import 'package:das_client/sfera/src/service/task/handshake_task.dart';
import 'package:das_client/util/error_code.dart';
import 'package:fimber/fimber.dart';
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 0205f6f9..6c9ff625 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
@@ -2,6 +2,7 @@ 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_journey_profile_task.dart';
import 'package:das_client/util/error_code.dart';
import 'package:fimber/fimber.dart';
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 3cc5d1e3..84bfb1cd 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
@@ -2,6 +2,7 @@ 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_segment_profiles_task.dart';
import 'package:das_client/util/error_code.dart';
import 'package:fimber/fimber.dart';
diff --git a/das_client/test_resources/jp/SFERA_JP_9999.xml b/das_client/test_resources/jp/SFERA_JP_9999.xml
new file mode 100644
index 00000000..f9ab6e13
--- /dev/null
+++ b/das_client/test_resources/jp/SFERA_JP_9999.xml
@@ -0,0 +1,65 @@
+
+
+
+
+ 1085
+ 9999
+ 2022-01-04
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_1.xml b/das_client/test_resources/sp/SFERA_SP_9999_1.xml
new file mode 100644
index 00000000..cd66a541
--- /dev/null
+++ b/das_client/test_resources/sp/SFERA_SP_9999_1.xml
@@ -0,0 +1,89 @@
+
+
+
+ 0085
+
+
+
+
+
+
+ CH
+ 9991
+
+
+
+
+
+ block
+
+
+
+
+ laneChange
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CH
+ 9991
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_2.xml b/das_client/test_resources/sp/SFERA_SP_9999_2.xml
new file mode 100644
index 00000000..418f201d
--- /dev/null
+++ b/das_client/test_resources/sp/SFERA_SP_9999_2.xml
@@ -0,0 +1,68 @@
+
+
+
+ 0085
+
+
+
+
+
+ CH
+ 9992
+
+
+
+
+
+
+ entry
+
+
+
+
+ exit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CH
+ 9992
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_3.xml b/das_client/test_resources/sp/SFERA_SP_9999_3.xml
new file mode 100644
index 00000000..0266583b
--- /dev/null
+++ b/das_client/test_resources/sp/SFERA_SP_9999_3.xml
@@ -0,0 +1,62 @@
+
+
+
+ 0085
+
+
+
+
+
+ CH
+ 9993
+
+
+
+
+
+ intermediate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CH
+ 9993
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_4.xml b/das_client/test_resources/sp/SFERA_SP_9999_4.xml
new file mode 100644
index 00000000..7e08be86
--- /dev/null
+++ b/das_client/test_resources/sp/SFERA_SP_9999_4.xml
@@ -0,0 +1,56 @@
+
+
+
+ 0085
+
+
+
+
+
+ CH
+ 9994
+
+
+
+
+
+ protection
+
+
+
+
+
+
+
+
+ CH
+ 9994
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_5.xml b/das_client/test_resources/sp/SFERA_SP_9999_5.xml
new file mode 100644
index 00000000..1c8eb51b
--- /dev/null
+++ b/das_client/test_resources/sp/SFERA_SP_9999_5.xml
@@ -0,0 +1,61 @@
+
+
+
+ 0085
+
+
+
+
+
+
+ CH
+ 9995
+
+
+
+
+ block
+ intermediate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CH
+ 9995
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_1.xml b/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_1.xml
index 2aedc0c0..3e76cb2a 100644
--- a/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_1.xml
+++ b/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_1.xml
@@ -47,10 +47,6 @@
9991
-
-
-
-
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_3.xml b/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_3.xml
index c70c3ec1..0266583b 100644
--- a/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_3.xml
+++ b/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_3.xml
@@ -31,7 +31,7 @@
+ TAF_TAP_location_type="stopping location">
CH
9993
diff --git a/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_5.xml b/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_5.xml
index 23d32f55..1c8eb51b 100644
--- a/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_5.xml
+++ b/sfera-mock/src/main/resources/static_sfera_resources/sp/SFERA_SP_9999_5.xml
@@ -49,13 +49,13 @@
-
+
-
+
-
+