diff --git a/das_client/assets/icons/icon_additional_speed_restriction.svg b/das_client/assets/icons/icon_additional_speed_restriction.svg
new file mode 100644
index 00000000..f327dec5
--- /dev/null
+++ b/das_client/assets/icons/icon_additional_speed_restriction.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/das_client/integration_test/test/train_journey_table_test.dart b/das_client/integration_test/test/train_journey_table_test.dart
index 36d362cc..5f3990a7 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,4 @@
+import 'package:das_client/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/cells/bracket_station_body.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/cells/route_cell_body.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/protection_section_row.dart';
@@ -12,6 +13,63 @@ import '../util/test_utils.dart';
void main() {
group('train journey table test', () {
+ testWidgets('test additional speed restriction row is displayed correctly', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: '500');
+
+ final scrollableFinder = find.byType(ListView);
+ expect(scrollableFinder, findsOneWidget);
+
+ final asrRow = findDASTableRowByText('km 64.200 - km 47.200');
+ expect(asrRow, findsOneWidget);
+
+ final asrIcon = find.descendant(
+ of: asrRow, matching: find.byKey(AdditionalSpeedRestrictionRow.additionalSpeedRestrictionIconKey));
+ expect(asrIcon, findsOneWidget);
+
+ final asrSpeed = find.descendant(of: asrRow, matching: find.text('60'));
+ expect(asrSpeed, findsOneWidget);
+
+ // check all cells are colored
+ final coloredCells = find.descendant(
+ of: asrRow,
+ matching: find.byWidgetPredicate((it) =>
+ it is Container &&
+ it.decoration is BoxDecoration &&
+ (it.decoration as BoxDecoration).color == AdditionalSpeedRestrictionRow.additionalSpeedRestrictionColor));
+ expect(coloredCells, findsNWidgets(11));
+ });
+
+ testWidgets('test other rows are displayed correctly', (tester) async {
+ await prepareAndStartApp(tester);
+
+ // load train journey by filling out train selection page
+ await _loadTrainJourney(tester, trainNumber: '500');
+
+ final scrollableFinder = find.byType(ListView);
+ expect(scrollableFinder, findsOneWidget);
+
+ final testRows = ['Genève', 'km 32.2', 'Lengnau', 'WANZ'];
+
+ for (final rowText in testRows) {
+ await tester.dragUntilVisible(find.text(rowText), scrollableFinder, const Offset(0, -50));
+
+ final testRow = findDASTableRowByText(rowText);
+ expect(testRow, findsOneWidget);
+
+ // check first 3 cells are colored
+ final coloredCells = find.descendant(
+ of: testRow,
+ matching: find.byWidgetPredicate((it) =>
+ it is Container &&
+ it.decoration is BoxDecoration &&
+ (it.decoration as BoxDecoration).color == AdditionalSpeedRestrictionRow.additionalSpeedRestrictionColor));
+ expect(coloredCells, findsNWidgets(3));
+ }
+ });
+
testWidgets('check if all table columns with header are present', (tester) async {
await prepareAndStartApp(tester);
@@ -34,6 +92,38 @@ void main() {
}
});
+ 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 scrollableFinder = find.byType(ListView);
+ expect(scrollableFinder, findsOneWidget);
+
+ 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 routeStart = find.byKey(RouteCellBody.routeStartKey);
+ expect(routeStart, findsOneWidget);
+
+ await tester.dragUntilVisible(find.byKey(RouteCellBody.routeEndKey), scrollableFinder, const Offset(0, -50));
+
+ // check route end
+ final routeEnd = find.byKey(RouteCellBody.routeEndKey);
+ expect(routeEnd, findsOneWidget);
+ });
+
testWidgets('test protection secions are displayed correctly', (tester) async {
await prepareAndStartApp(tester);
@@ -54,7 +144,8 @@ void main() {
expect(find.descendant(of: protectionSectionRow, matching: find.text('FL')), findsOneWidget);
expect(find.descendant(of: protectionSectionRow, matching: find.text('32.2')), findsOneWidget);
// Verify icon is displayed
- expect(find.descendant(of: protectionSectionRow, matching: find.byKey(ProtectionSectionRow.protectionSectionKey)), findsOneWidget);
+ expect(find.descendant(of: protectionSectionRow, matching: find.byKey(ProtectionSectionRow.protectionSectionKey)),
+ findsOneWidget);
// Scroll to next protection section
await tester.dragUntilVisible(find.text('Yverdon-les-Bains'), scrollableFinder, const Offset(0, -20));
@@ -157,6 +248,11 @@ void main() {
// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: '9999');
+ final scrollableFinder = find.byType(ListView);
+ expect(scrollableFinder, findsOneWidget);
+
+ await tester.dragUntilVisible(find.text('Klammerbahnhof D1'), scrollableFinder, const Offset(0, -50));
+
final bracketStationD = findDASTableRowByText('Klammerbahnhof D');
final bracketStationD1 = findDASTableRowByText('Klammerbahnhof D1');
expect(bracketStationD, findsOneWidget);
@@ -181,6 +277,11 @@ void main() {
// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: '9999');
+ final scrollableFinder = find.byType(ListView);
+ expect(scrollableFinder, findsOneWidget);
+
+ await tester.dragUntilVisible(find.text('Halt auf Verlangen C'), scrollableFinder, const Offset(0, -50));
+
final stopOnDemandRow = findDASTableRowByText('Halt auf Verlangen C');
expect(stopOnDemandRow, findsOneWidget);
@@ -195,34 +296,6 @@ void main() {
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);
@@ -237,8 +310,6 @@ void main() {
.byWidgetPredicate((it) => it is Text && it.data == 'Schlieren' && it.style?.fontStyle != FontStyle.italic);
expect(schlierenText, findsOneWidget);
});
-
-
});
}
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart
new file mode 100644
index 00000000..ac3f72bf
--- /dev/null
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart
@@ -0,0 +1,50 @@
+import 'package:das_client/app/i18n/i18n.dart';
+import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.dart';
+import 'package:das_client/app/widgets/assets.dart';
+import 'package:das_client/app/widgets/table/das_table_cell.dart';
+import 'package:das_client/model/journey/additional_speed_restriction_data.dart';
+import 'package:design_system_flutter/design_system_flutter.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class AdditionalSpeedRestrictionRow extends BaseRowBuilder {
+ static const Key additionalSpeedRestrictionIconKey = Key('addition_speed_restrction_icon_key');
+ static const Color additionalSpeedRestrictionColor = SBBColors.orange;
+
+ AdditionalSpeedRestrictionRow({
+ super.height = 44.0,
+ required super.metadata,
+ required super.data,
+ }) : super(rowColor: additionalSpeedRestrictionColor);
+
+ @override
+ DASTableCell informationCell(BuildContext context) {
+ return DASTableCell(
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ Text('${context.l10n.p_train_journey_table_kilometre_label} ${data.restriction.kmFrom.toStringAsFixed(3)} '
+ '- ${context.l10n.p_train_journey_table_kilometre_label} ${data.restriction.kmTo.toStringAsFixed(3)}'),
+ ],
+ ),
+ );
+ }
+
+ @override
+ DASTableCell iconsCell2(BuildContext context) {
+ return DASTableCell(
+ child: SvgPicture.asset(
+ AppAssets.iconAdditionalSpeedRestriction,
+ key: additionalSpeedRestrictionIconKey,
+ ),
+ alignment: Alignment.center);
+ }
+
+ @override
+ DASTableCell graduatedSpeedCell(BuildContext context) {
+ return DASTableCell(
+ child: Text(data.restriction.speed.toString()),
+ alignment: Alignment.center,
+ );
+ }
+}
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 2862e0df..00cbe211 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
@@ -1,21 +1,25 @@
+import 'package:das_client/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/cells/route_cell_body.dart';
import 'package:das_client/app/widgets/table/das_table_cell.dart';
import 'package:das_client/app/widgets/table/das_table_row.dart';
+import 'package:das_client/model/journey/additional_speed_restriction.dart';
+import 'package:das_client/model/journey/base_data.dart';
+import 'package:das_client/model/journey/metadata.dart';
import 'package:flutter/material.dart';
-abstract class BaseRowBuilder extends DASTableRowBuilder {
+class BaseRowBuilder extends DASTableRowBuilder {
const BaseRowBuilder({
- super.height,
- this.kilometre,
+ super.height = 44.0,
this.defaultAlignment = Alignment.bottomCenter,
this.rowColor,
- this.isCurrentPosition = false,
+ required this.metadata,
+ required this.data,
});
- final List? kilometre;
final Alignment defaultAlignment;
final Color? rowColor;
- final bool isCurrentPosition;
+ final Metadata metadata;
+ final T data;
@override
DASTableRow build(BuildContext context) {
@@ -39,31 +43,37 @@ abstract class BaseRowBuilder extends DASTableRowBuilder {
}
DASTableCell kilometreCell(BuildContext context) {
- if (kilometre == null || kilometre!.isEmpty) {
+ if (data.kilometre.isEmpty) {
return DASTableCell.empty();
}
return DASTableCell(
+ color: getSpecialCellColor(),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(kilometre![0].toStringAsFixed(1)),
- if (kilometre!.length > 1) Text(kilometre![1].toStringAsFixed(1))
+ Text(data.kilometre[0].toStringAsFixed(1)),
+ if (data.kilometre.length > 1) Text(data.kilometre[1].toStringAsFixed(1))
],
),
alignment: Alignment.centerLeft);
}
DASTableCell timeCell(BuildContext context) {
- return DASTableCell(child: Text('06:05:52'), alignment: defaultAlignment);
+ return DASTableCell(color: getSpecialCellColor(), child: Text('06:05:52'), alignment: defaultAlignment);
}
DASTableCell routeCell(BuildContext context) {
return DASTableCell(
+ color: getSpecialCellColor(),
padding: EdgeInsets.all(0.0),
alignment: null,
- child: RouteCellBody(isCurrentPosition: isCurrentPosition),
+ child: RouteCellBody(
+ isCurrentPosition: metadata.currentPosition == data,
+ isRouteStart: metadata.routeStart == data,
+ isRouteEnd: metadata.routeEnd == data,
+ ),
);
}
@@ -72,15 +82,15 @@ abstract class BaseRowBuilder extends DASTableRowBuilder {
}
DASTableCell graduatedSpeedCell(BuildContext context) {
- return DASTableCell(child: Text('85'), alignment: defaultAlignment);
+ return DASTableCell.empty();
}
DASTableCell advisedSpeedCell(BuildContext context) {
- return DASTableCell(child: Text('100'), alignment: defaultAlignment);
+ return DASTableCell.empty();
}
DASTableCell brakedWeightSpeedCell(BuildContext context) {
- return DASTableCell(child: Text('95'), alignment: defaultAlignment);
+ return DASTableCell.empty();
}
// TODO: clarify use of different icon cells and set appropriate name
@@ -101,4 +111,16 @@ abstract class BaseRowBuilder extends DASTableRowBuilder {
DASTableCell actionsCell(BuildContext context) {
return DASTableCell.empty();
}
+
+ Color? getSpecialCellColor() {
+ return getAdditionalSpeedRestriction() != null
+ ? AdditionalSpeedRestrictionRow.additionalSpeedRestrictionColor
+ : null;
+ }
+
+ AdditionalSpeedRestriction? getAdditionalSpeedRestriction() {
+ return metadata.additionalSpeedRestrictions
+ .where((it) => it.orderFrom <= data.order && it.orderTo >= data.order)
+ .firstOrNull;
+ }
}
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 c195b6e1..05de9df6 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
@@ -54,7 +54,11 @@ class RouteCellBody extends StatelessWidget {
final isDarkTheme = SBBBaseStyle.of(context).brightness == Brightness.dark;
final lineColor = isDarkTheme ? SBBColors.white : SBBColors.black;
return Positioned(
- key: isRouteStart ? routeStartKey : isRouteEnd ? routeEndKey : null,
+ key: isRouteStart
+ ? routeStartKey
+ : isRouteEnd
+ ? routeEndKey
+ : null,
top: isRouteStart ? height - sbbDefaultSpacing : -sbbDefaultSpacing,
bottom: isRouteEnd ? sbbDefaultSpacing : -sbbDefaultSpacing,
right: 0,
diff --git a/das_client/lib/app/pages/journey/train_journey/widgets/table/protection_section_row.dart b/das_client/lib/app/pages/journey/train_journey/widgets/table/protection_section_row.dart
index e8c8dcea..32645ed5 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/table/protection_section_row.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/table/protection_section_row.dart
@@ -2,23 +2,19 @@ import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.dart';
import 'package:das_client/app/widgets/assets.dart';
import 'package:das_client/app/widgets/table/das_table_cell.dart';
-import 'package:das_client/model/journey/metadata.dart';
import 'package:das_client/model/journey/protection_section.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
-class ProtectionSectionRow extends BaseRowBuilder {
+class ProtectionSectionRow extends BaseRowBuilder {
static const Key protectionSectionKey = Key('protection_section_key');
ProtectionSectionRow({
super.height = 44.0,
- required this.metadata,
- required this.protectionSection,
- }) : super(rowColor: SBBColors.peach, kilometre: protectionSection.kilometre);
-
- final Metadata metadata;
- final ProtectionSection protectionSection;
+ required super.metadata,
+ required super.data,
+ }) : super(rowColor: SBBColors.peach);
@override
DASTableCell informationCell(BuildContext context) {
@@ -26,10 +22,9 @@ class ProtectionSectionRow extends BaseRowBuilder {
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
- Text(
- '${context.l10n.p_train_journey_table_kilometre_label} ${protectionSection.kilometre[0].toStringAsFixed(1)}'),
+ Text('${context.l10n.p_train_journey_table_kilometre_label} ${data.kilometre[0].toStringAsFixed(1)}'),
Spacer(),
- Text('${protectionSection.isOptional ? 'F' : ''}${protectionSection.isLong ? 'L' : ''}'),
+ Text('${data.isOptional ? 'F' : ''}${data.isLong ? 'L' : ''}'),
],
),
);
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 38c35ea2..511dee8b 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
@@ -3,38 +3,24 @@ import 'package:das_client/app/pages/journey/train_journey/widgets/table/cells/b
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/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 {
+class ServicePointRow extends BaseRowBuilder {
static const Key stopOnRequestKey = Key('stop_on_request_key');
ServicePointRow({
super.height = 64.0,
- this.isRouteStart = false,
- this.isRouteEnd = false,
- required this.metadata,
- required this.servicePoint,
- }) : super(
- rowColor: metadata.nextStop == servicePoint ? SBBColors.royal.withOpacity(0.2) : Colors.transparent,
- kilometre: servicePoint.kilometre,
- isCurrentPosition: metadata.currentPosition == servicePoint);
-
- final Metadata metadata;
- final ServicePoint servicePoint;
-
-
- final bool isRouteStart;
- final bool isRouteEnd;
+ required super.metadata,
+ required super.data,
+ }) : super(rowColor: metadata.nextStop == data ? SBBColors.royal.withOpacity(0.2) : Colors.transparent);
@override
DASTableCell informationCell(BuildContext context) {
- final servicePointName = servicePoint.name.localized;
- final textStyle = servicePoint.isStation
+ final servicePointName = data.name.localized;
+ final textStyle = data.isStation
? SBBTextStyles.largeBold.copyWith(fontSize: 24.0)
: SBBTextStyles.largeLight.copyWith(fontSize: 24.0, fontStyle: FontStyle.italic);
return DASTableCell(
@@ -57,12 +43,12 @@ class ServicePointRow extends BaseRowBuilder {
child: Stack(
clipBehavior: Clip.none,
children: [
- if (servicePoint.bracketStation != null)
+ if (data.bracketStation != null)
BracketStationBody(
- bracketStation: servicePoint.bracketStation!,
+ bracketStation: data.bracketStation!,
height: height!,
),
- if (!servicePoint.mandatoryStop)
+ if (!data.mandatoryStop)
Align(
alignment: Alignment.bottomCenter,
child: SvgPicture.asset(
@@ -77,14 +63,15 @@ class ServicePointRow extends BaseRowBuilder {
@override
DASTableCell routeCell(BuildContext context) {
return DASTableCell(
+ color: getSpecialCellColor(),
padding: EdgeInsets.all(0.0),
alignment: null,
child: RouteCellBody(
- isStop: servicePoint.isStop,
- isCurrentPosition: isCurrentPosition,
- isRouteStart: isRouteStart,
- isRouteEnd: isRouteEnd,
- isStopOnRequest: !servicePoint.mandatoryStop,
+ isStop: data.isStop,
+ isCurrentPosition: metadata.currentPosition == data,
+ isRouteStart: metadata.routeStart == data,
+ isRouteEnd: metadata.routeEnd == data,
+ isStopOnRequest: !data.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 ed914d23..613d31cb 100644
--- a/das_client/lib/app/pages/journey/train_journey/widgets/train_journey.dart
+++ b/das_client/lib/app/pages/journey/train_journey/widgets/train_journey.dart
@@ -1,9 +1,12 @@
import 'package:das_client/app/bloc/train_journey_cubit.dart';
import 'package:das_client/app/i18n/i18n.dart';
+import 'package:das_client/app/pages/journey/train_journey/widgets/table/additional_speed_restriction_row.dart';
+import 'package:das_client/app/pages/journey/train_journey/widgets/table/base_row_builder.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/protection_section_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/service_point_row.dart';
import 'package:das_client/app/widgets/table/das_table.dart';
import 'package:das_client/app/widgets/table/das_table_column.dart';
+import 'package:das_client/model/journey/additional_speed_restriction_data.dart';
import 'package:das_client/model/journey/datatype.dart';
import 'package:das_client/model/journey/journey.dart';
import 'package:das_client/model/journey/protection_section.dart';
@@ -47,12 +50,20 @@ class TrainJourney extends StatelessWidget {
case Datatype.servicePoint:
return ServicePointRow(
metadata: journey.metadata,
- servicePoint: rowData as ServicePoint,
- isRouteStart: index == 0,
- isRouteEnd: index == journey.data.length - 1,
+ data: rowData as ServicePoint,
).build(context);
case Datatype.protectionSection:
- return ProtectionSectionRow(metadata: journey.metadata, protectionSection: rowData as ProtectionSection)
+ return ProtectionSectionRow(metadata: journey.metadata, data: rowData as ProtectionSection)
+ .build(context);
+ case Datatype.curvePoint:
+ // TODO:
+ return BaseRowBuilder(metadata: journey.metadata, data: rowData).build(context);
+ case Datatype.signal:
+ // TODO:
+ return BaseRowBuilder(metadata: journey.metadata, data: rowData).build(context);
+ case Datatype.additionalSpeedRestriction:
+ return AdditionalSpeedRestrictionRow(
+ metadata: journey.metadata, data: rowData as AdditionalSpeedRestrictionData)
.build(context);
}
})
diff --git a/das_client/lib/app/widgets/assets.dart b/das_client/lib/app/widgets/assets.dart
index e596d010..f82e233d 100644
--- a/das_client/lib/app/widgets/assets.dart
+++ b/das_client/lib/app/widgets/assets.dart
@@ -10,4 +10,5 @@ class AppAssets {
static const iconHeaderStop = '$_iconsDir/icon_header_stop.svg';
static const iconStopOnRequest = '$_iconsDir/icon_stop_on_request.svg';
static const iconProtectionSection = '$_iconsDir/icon_protection_section.svg';
+ static const iconAdditionalSpeedRestriction = '$_iconsDir/icon_additional_speed_restriction.svg';
}
diff --git a/das_client/lib/model/journey/additional_speed_restriction.dart b/das_client/lib/model/journey/additional_speed_restriction.dart
new file mode 100644
index 00000000..f11d6938
--- /dev/null
+++ b/das_client/lib/model/journey/additional_speed_restriction.dart
@@ -0,0 +1,16 @@
+import 'package:das_client/model/journey/base_data.dart';
+
+class AdditionalSpeedRestriction {
+ AdditionalSpeedRestriction(
+ {required this.kmFrom, required this.kmTo, required this.orderFrom, required this.orderTo, this.speed});
+
+ final double kmFrom;
+ final double kmTo;
+ final int orderFrom;
+ final int orderTo;
+ final int? speed;
+
+ bool needsEndMarker(List journeyData) {
+ return journeyData.where((it) => it.order >= orderFrom && it.order <= orderTo).length > 1;
+ }
+}
diff --git a/das_client/lib/model/journey/additional_speed_restriction_data.dart b/das_client/lib/model/journey/additional_speed_restriction_data.dart
new file mode 100644
index 00000000..3995aa2e
--- /dev/null
+++ b/das_client/lib/model/journey/additional_speed_restriction_data.dart
@@ -0,0 +1,10 @@
+import 'package:das_client/model/journey/additional_speed_restriction.dart';
+import 'package:das_client/model/journey/base_data.dart';
+import 'package:das_client/model/journey/datatype.dart';
+
+class AdditionalSpeedRestrictionData extends BaseData {
+ AdditionalSpeedRestrictionData({required this.restriction, required super.order, required super.kilometre})
+ : super(type: Datatype.additionalSpeedRestriction);
+
+ final AdditionalSpeedRestriction restriction;
+}
diff --git a/das_client/lib/model/journey/base_data.dart b/das_client/lib/model/journey/base_data.dart
index 8035118c..2966c377 100644
--- a/das_client/lib/model/journey/base_data.dart
+++ b/das_client/lib/model/journey/base_data.dart
@@ -1,9 +1,16 @@
import 'package:das_client/model/journey/datatype.dart';
+import 'package:das_client/model/journey/track_equipment.dart';
abstract class BaseData {
- BaseData({required this.type, required this.order, required this.kilometre});
+ BaseData({
+ required this.type,
+ required this.order,
+ required this.kilometre,
+ this.trackEquipment = const [],
+ });
final Datatype type;
final int order;
final List kilometre;
+ final List trackEquipment;
}
diff --git a/das_client/lib/model/journey/bracket_station.dart b/das_client/lib/model/journey/bracket_station.dart
index 3a3ccada..96268be8 100644
--- a/das_client/lib/model/journey/bracket_station.dart
+++ b/das_client/lib/model/journey/bracket_station.dart
@@ -1,5 +1,3 @@
-
-
class BracketStation {
BracketStation({this.mainStationAbbreviation});
diff --git a/das_client/lib/model/journey/curve_point.dart b/das_client/lib/model/journey/curve_point.dart
new file mode 100644
index 00000000..53687e32
--- /dev/null
+++ b/das_client/lib/model/journey/curve_point.dart
@@ -0,0 +1,51 @@
+import 'package:das_client/model/journey/base_data.dart';
+import 'package:das_client/model/journey/datatype.dart';
+
+class CurvePoint extends BaseData {
+ CurvePoint({
+ required super.order,
+ required super.kilometre,
+ super.trackEquipment,
+ this.curvePointType,
+ this.curveType,
+ this.comment,
+ }) : super(type: Datatype.curvePoint);
+
+ final CurvePointType? curvePointType;
+ final CurveType? curveType;
+ final String? comment;
+}
+
+/// marks the beginning and the end of a curve
+enum CurvePointType {
+ begin,
+ end,
+ unknown;
+
+ factory CurvePointType.from(String value) {
+ return values.firstWhere(
+ (e) => e.name.toLowerCase() == value.toLowerCase(),
+ orElse: () => CurvePointType.unknown,
+ );
+ }
+}
+
+/// Type of curve. Is provided only if curvePointType = 'begin'
+enum CurveType {
+ /// begins on the line and ends on the line or a station or a halt
+ curve,
+
+ /// begins in a station
+ stationExitCurve,
+
+ /// begins at an halt
+ curveAfterHalt,
+ unknown;
+
+ factory CurveType.from(String value) {
+ return values.firstWhere(
+ (e) => e.name.toLowerCase() == value.toLowerCase(),
+ orElse: () => CurveType.unknown,
+ );
+ }
+}
diff --git a/das_client/lib/model/journey/datatype.dart b/das_client/lib/model/journey/datatype.dart
index 0f435d7a..46f36983 100644
--- a/das_client/lib/model/journey/datatype.dart
+++ b/das_client/lib/model/journey/datatype.dart
@@ -1,4 +1,7 @@
enum Datatype {
servicePoint,
- protectionSection;
+ protectionSection,
+ signal,
+ curvePoint,
+ additionalSpeedRestriction;
}
diff --git a/das_client/lib/model/journey/metadata.dart b/das_client/lib/model/journey/metadata.dart
index 949a3fb2..7d83f109 100644
--- a/das_client/lib/model/journey/metadata.dart
+++ b/das_client/lib/model/journey/metadata.dart
@@ -1,9 +1,19 @@
+import 'package:das_client/model/journey/additional_speed_restriction.dart';
import 'package:das_client/model/journey/base_data.dart';
import 'package:das_client/model/journey/service_point.dart';
class Metadata {
- const Metadata({this.nextStop, this.currentPosition});
+ Metadata(
+ {this.nextStop,
+ this.currentPosition,
+ this.routeStart,
+ this.routeEnd,
+ List? additionalSpeedRestrictions})
+ : additionalSpeedRestrictions = additionalSpeedRestrictions ?? [];
final ServicePoint? nextStop;
final BaseData? currentPosition;
+ final List additionalSpeedRestrictions;
+ final BaseData? routeStart;
+ final BaseData? routeEnd;
}
diff --git a/das_client/lib/model/journey/service_point.dart b/das_client/lib/model/journey/service_point.dart
index ed829a03..099ee620 100644
--- a/das_client/lib/model/journey/service_point.dart
+++ b/das_client/lib/model/journey/service_point.dart
@@ -4,15 +4,16 @@ 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);
+ ServicePoint({
+ required this.name,
+ required this.mandatoryStop,
+ required this.isStop,
+ required this.isStation,
+ this.bracketStation,
+ required super.order,
+ required super.kilometre,
+ super.trackEquipment,
+ }) : super(type: Datatype.servicePoint);
final LocalizedString name;
final bool mandatoryStop;
diff --git a/das_client/lib/model/journey/signal.dart b/das_client/lib/model/journey/signal.dart
new file mode 100644
index 00000000..cc55d94d
--- /dev/null
+++ b/das_client/lib/model/journey/signal.dart
@@ -0,0 +1,32 @@
+import 'package:das_client/model/journey/base_data.dart';
+import 'package:das_client/model/journey/datatype.dart';
+
+class Signal extends BaseData {
+ Signal({
+ required super.order,
+ required super.kilometre,
+ this.visualIdentifier,
+ this.functions = const [],
+ super.trackEquipment,
+ }) : super(type: Datatype.signal);
+
+ final List functions;
+ final String? visualIdentifier;
+}
+
+enum SignalFunction {
+ entry,
+ exit,
+ intermediate,
+ block,
+ protection,
+ laneChange,
+ unknown;
+
+ factory SignalFunction.from(String value) {
+ return values.firstWhere(
+ (e) => e.name.toLowerCase() == value.toLowerCase(),
+ orElse: () => SignalFunction.unknown,
+ );
+ }
+}
\ No newline at end of file
diff --git a/das_client/lib/model/journey/track_equipment.dart b/das_client/lib/model/journey/track_equipment.dart
new file mode 100644
index 00000000..ebefbcd9
--- /dev/null
+++ b/das_client/lib/model/journey/track_equipment.dart
@@ -0,0 +1,52 @@
+import 'package:collection/collection.dart';
+
+class TrackEquipment {
+ TrackEquipment({
+ required this.type,
+ this.startLocation,
+ this.endLocation,
+ this.appliesToWholeSp = false,
+ });
+
+ final double? startLocation;
+ final double? endLocation;
+ final bool appliesToWholeSp;
+ final TrackEquipmentType type;
+
+ bool isOnLocation(double location) {
+ if (appliesToWholeSp) {
+ return true;
+ } else if (startLocation != null && endLocation != null) {
+ return startLocation! <= location && location <= endLocation!;
+ } else if (startLocation != null) {
+ return startLocation! <= location;
+ } else if (endLocation != null) {
+ return location <= endLocation!;
+ }
+ return false;
+ }
+}
+
+enum TrackEquipmentType {
+ etcsL1ls2TracksWithSingleTrackEquipment('ETCS-L1LS-2TracksWithSingleTrackEquipment'),
+ etcsL2ConvSpeedReversingImpossible('ETCS-L2-convSpeedReversingImpossible'),
+ etcsL2ExtSpeedReversingPossible('ETCS-L2-extSpeedReversingPossible'),
+ etcsL2ExtSpeedReversingImpossible('ETCS-L2-extSpeedReversingImpossible');
+
+ const TrackEquipmentType(this.value);
+
+ final String value;
+
+ static TrackEquipmentType? from(String value) {
+ return values.firstWhereOrNull(
+ (e) => e.value.toLowerCase() == value.toLowerCase()
+ );
+ }
+}
+
+// extensions
+
+extension TrackEquipmentsExtension on Iterable {
+ Iterable whereOnLocation(double location) =>
+ where((trackEquipment) => trackEquipment.isOnLocation(location));
+}
diff --git a/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart b/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart
index 0c67d4e9..44482353 100644
--- a/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart
+++ b/das_client/lib/sfera/src/mapper/sfera_model_mapper.dart
@@ -1,19 +1,27 @@
import 'package:collection/collection.dart';
+import 'package:das_client/model/journey/additional_speed_restriction.dart';
+import 'package:das_client/model/journey/additional_speed_restriction_data.dart';
import 'package:das_client/model/journey/base_data.dart';
import 'package:das_client/model/journey/bracket_station.dart';
+import 'package:das_client/model/journey/curve_point.dart';
import 'package:das_client/model/journey/datatype.dart';
import 'package:das_client/model/journey/journey.dart';
import 'package:das_client/model/journey/metadata.dart';
import 'package:das_client/model/journey/protection_section.dart';
import 'package:das_client/model/journey/service_point.dart';
+import 'package:das_client/model/journey/signal.dart';
+import 'package:das_client/model/journey/track_equipment.dart';
import 'package:das_client/model/localized_string.dart';
import 'package:das_client/sfera/src/model/enums/length_type.dart';
+import 'package:das_client/sfera/src/model/enums/start_end_qualifier.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/enums/xml_enum.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/network_specific_parameter.dart';
import 'package:das_client/sfera/src/model/segment_profile.dart';
+import 'package:das_client/sfera/src/model/segment_profile_list.dart';
import 'package:das_client/sfera/src/model/taf_tap_location.dart';
import 'package:fimber/fimber.dart';
@@ -39,7 +47,6 @@ class SferaModelMapper {
final journeyData = [];
final segmentProfilesLists = journeyProfile.segmentProfilesLists.toList();
-
final tafTapLocations =
segmentProfiles.map((it) => it.areas).whereNotNull().expand((it) => it.tafTapLocations).toList();
@@ -52,7 +59,16 @@ class SferaModelMapper {
it.versionMinor == segmentProfileList.versionMinor)
.first;
+ final trackEquipments = _parseTrackEquipments(segmentProfile);
+
final kilometreMap = _parseKilometre(segmentProfile);
+
+ final curvePoints = _parseCurvePoints(segmentProfile, segmentIndex, kilometreMap, trackEquipments);
+ journeyData.addAll(curvePoints);
+
+ final signals = _parseSignals(segmentProfile, segmentIndex, kilometreMap, trackEquipments);
+ journeyData.addAll(signals);
+
final timingPoints = segmentProfile.points?.timingPoints.toList() ?? [];
for (final tpConstraint in segmentProfileList.timingPointsContraints) {
@@ -65,25 +81,167 @@ class SferaModelMapper {
.first;
journeyData.add(ServicePoint(
- name: _localizedStringFromMultilingualText(tafTapLocation.locationNames),
- order: _calculateOrder(segmentIndex, timingPoint.location),
- mandatoryStop: tpConstraint.stoppingPointInformation?.stopType?.mandatoryStop ?? true,
- isStop: tpConstraint.stopSkipPass == StopSkipPass.stoppingPoint,
- isStation: tafTapLocation.locationType != TafTapLocationType.stoppingLocation,
- bracketStation: _parseBracketStation(tafTapLocations, tafTapLocation),
- kilometre: kilometreMap[timingPoint.location] ?? []));
+ name: _localizedStringFromMultilingualText(tafTapLocation.locationNames),
+ order: _calculateOrder(segmentIndex, timingPoint.location),
+ mandatoryStop: tpConstraint.stoppingPointInformation?.stopType?.mandatoryStop ?? true,
+ isStop: tpConstraint.stopSkipPass == StopSkipPass.stoppingPoint,
+ isStation: tafTapLocation.locationType != TafTapLocationType.stoppingLocation,
+ bracketStation: _parseBracketStation(tafTapLocations, tafTapLocation),
+ kilometre: kilometreMap[timingPoint.location] ?? [],
+ trackEquipment: trackEquipments.whereOnLocation(timingPoint.location).toList(),
+ ));
}
_parseAndAddProtectionSections(journeyData, segmentIndex, segmentProfile, kilometreMap);
}
+ final additionalSpeedRestrictions = _parseAdditionalSpeedRestrictions(journeyProfile, segmentProfiles);
+ for (final restriction in additionalSpeedRestrictions) {
+ journeyData.add(AdditionalSpeedRestrictionData(
+ restriction: restriction, order: restriction.orderFrom, kilometre: [restriction.kmFrom]));
+
+ if (restriction.needsEndMarker(journeyData)) {
+ journeyData.add(AdditionalSpeedRestrictionData(
+ restriction: restriction, order: restriction.orderTo, kilometre: [restriction.kmTo]));
+ }
+ }
+
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);
+ metadata: Metadata(
+ nextStop: servicePoints.length > 1 ? servicePoints[1] as ServicePoint : null,
+ currentPosition: journeyData.first,
+ additionalSpeedRestrictions: additionalSpeedRestrictions,
+ routeStart: journeyData.firstOrNull,
+ routeEnd: journeyData.lastOrNull),
+ data: journeyData,
+ );
+ }
+
+ static List _parseAdditionalSpeedRestrictions(
+ JourneyProfile journeyProfile, List segmentProfiles) {
+ final List result = [];
+ final now = DateTime.now();
+ final segmentProfilesLists = journeyProfile.segmentProfilesLists.toList();
+
+ int? startSegmentIndex;
+ int? endSegmentIndex;
+ double? startLocation;
+ double? endLocation;
+
+ for (int segmentIndex = 0; segmentIndex < segmentProfilesLists.length; segmentIndex++) {
+ final segmentProfileList = segmentProfilesLists[segmentIndex];
+
+ for (final asrTemporaryConstrain in segmentProfileList.asrTemporaryConstrains) {
+ // TODO: Es werden Langsamfahrstellen von 30min vor Start der Fahrt (betriebliche Zeit) bis 30min nach Ende der Fahrt (betriebliche Zeit) angezeigt.
+ if (asrTemporaryConstrain.startTime != null && asrTemporaryConstrain.startTime!.isAfter(now) ||
+ asrTemporaryConstrain.endTime != null && asrTemporaryConstrain.endTime!.isBefore(now)) {
+ continue;
+ }
+
+ switch (asrTemporaryConstrain.startEndQualifier) {
+ case StartEndQualifier.starts:
+ startLocation = asrTemporaryConstrain.startLocation;
+ startSegmentIndex = segmentIndex;
+ break;
+ case StartEndQualifier.startsEnds:
+ startLocation = asrTemporaryConstrain.startLocation;
+ startSegmentIndex = segmentIndex;
+ continue next;
+ next:
+ case StartEndQualifier.ends:
+ endLocation = asrTemporaryConstrain.endLocation;
+ endSegmentIndex = segmentIndex;
+ break;
+ case StartEndQualifier.wholeSp:
+ break;
+ }
+
+ if (startSegmentIndex != null && endSegmentIndex != null && startLocation != null && endLocation != null) {
+ final startSegment = _findSegmentProfile(segmentProfiles, segmentProfilesLists[startSegmentIndex]);
+ final endSegment = _findSegmentProfile(segmentProfiles, segmentProfilesLists[endSegmentIndex]);
+
+ final startKilometreMap = _parseKilometre(startSegment);
+ final endKilometreMap = _parseKilometre(endSegment);
+
+ final startOrder = _calculateOrder(startSegmentIndex, startLocation);
+ final endOrder = _calculateOrder(endSegmentIndex, endLocation);
+
+ result.add(AdditionalSpeedRestriction(
+ kmFrom: startKilometreMap[startLocation]!.first,
+ kmTo: endKilometreMap[endLocation]!.first,
+ orderFrom: startOrder,
+ orderTo: endOrder,
+ speed: asrTemporaryConstrain.additionalSpeedRestriction?.asrSpeed));
+
+ startSegmentIndex = null;
+ endSegmentIndex = null;
+ startLocation = null;
+ endLocation = null;
+ }
+ }
+ }
+
+ if (startSegmentIndex != null || endSegmentIndex != null || startLocation != null || endLocation != null) {
+ Fimber.w('Incomplete additional speed restriction found: '
+ 'startSegmentIndex: $startSegmentIndex, endSegmentIndex: $endSegmentIndex, '
+ 'startLocation: $startLocation, endLocation: $endLocation');
+ }
+
+ return result;
+ }
+
+ static SegmentProfile _findSegmentProfile(
+ List segmentProfiles, SegmentProfileList segmentProfileList) {
+ return segmentProfiles
+ .where((it) =>
+ it.id == segmentProfileList.spId &&
+ it.versionMajor == segmentProfileList.versionMajor &&
+ it.versionMinor == segmentProfileList.versionMinor)
+ .first;
+ }
+
+ static Iterable _parseSignals(SegmentProfile segmentProfile, int segmentIndex,
+ Map> kilometreMap, List trackEquipments) {
+ final signals = segmentProfile.points?.signals ?? [];
+ return signals.map((signal) {
+ return Signal(
+ visualIdentifier: signal.physicalCharacteristics?.visualIdentifier,
+ functions: signal.functions.map((function) => SignalFunction.from(function.value!)).toList(),
+ order: _calculateOrder(segmentIndex, signal.id.location),
+ kilometre: kilometreMap[signal.id.location] ?? [],
+ trackEquipment: trackEquipments.whereOnLocation(signal.id.location).toList(),
+ );
+ });
+ }
+
+ static List _parseTrackEquipments(SegmentProfile segmentProfile) {
+ final nonStandardTrackEquipments = segmentProfile.areas?.nonStandardTrackEquipments ?? [];
+ return nonStandardTrackEquipments
+ .map((element) {
+ final trackEquipmentType = TrackEquipmentType.from(element.trackEquipmentType!.nspValue);
+ if (trackEquipmentType == null) {
+ Fimber.w(
+ 'Encountered nonStandardTrackEquipment without main station NSP declaration: ${element.trackEquipmentType}');
+ return null;
+ } else {
+ final hasStartLocation = element.startEndQualifier == StartEndQualifier.starts ||
+ element.startEndQualifier == StartEndQualifier.startsEnds;
+ final hasEndLocation = element.startEndQualifier == StartEndQualifier.ends ||
+ element.startEndQualifier == StartEndQualifier.startsEnds;
+ return TrackEquipment(
+ type: trackEquipmentType,
+ startLocation: hasStartLocation ? element.startLocation! : null,
+ endLocation: hasEndLocation ? element.endLocation! : null,
+ appliesToWholeSp: element.startEndQualifier == StartEndQualifier.wholeSp,
+ );
+ }
+ })
+ .where((e) => e != null)
+ .cast()
+ .toList();
}
static int _calculateOrder(int segmentIndex, double location) {
@@ -168,4 +326,21 @@ class SferaModelMapper {
return null;
}
+
+ static List _parseCurvePoints(SegmentProfile segmentProfile, int segmentIndex,
+ Map> kilometreMap, List trackEquipments) {
+ final curvePointsNsp = segmentProfile.points?.curvePointsNsp ?? [];
+ return curvePointsNsp.map((curvePointNsp) {
+ final curvePointTypeValue = curvePointNsp.parameters.withName('curvePointType')?.nspValue;
+ final curveTypeValue = curvePointNsp.parameters.withName('curveType')?.nspValue;
+ return CurvePoint(
+ order: _calculateOrder(segmentIndex, curvePointNsp.location),
+ kilometre: kilometreMap[curvePointNsp.location] ?? [],
+ curvePointType: curvePointTypeValue != null ? CurvePointType.from(curvePointTypeValue) : null,
+ curveType: curveTypeValue != null ? CurveType.from(curveTypeValue) : null,
+ comment: curvePointNsp.parameters.withName('comment')?.nspValue,
+ trackEquipment: trackEquipments.whereOnLocation(curvePointNsp.location).toList(),
+ );
+ }).toList();
+ }
}
diff --git a/das_client/lib/sfera/src/model/additional_speed_restriction.dart b/das_client/lib/sfera/src/model/additional_speed_restriction.dart
new file mode 100644
index 00000000..9f4f8e01
--- /dev/null
+++ b/das_client/lib/sfera/src/model/additional_speed_restriction.dart
@@ -0,0 +1,11 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class AdditionalSpeedRestriction extends SferaXmlElement {
+ static const String elementType = 'AdditionalSpeedRestriction';
+
+ AdditionalSpeedRestriction({super.type = elementType, super.attributes, super.children, super.value});
+
+ bool get asrFront => attributes['ASR_Front'] != null ? bool.tryParse(attributes['ASR_Front']!) ?? false : false;
+
+ int? get asrSpeed => attributes['ASR_Speed'] != null ? int.tryParse(attributes['ASR_Speed']!) : null;
+}
diff --git a/das_client/lib/sfera/src/model/enums/start_end_qualifier.dart b/das_client/lib/sfera/src/model/enums/start_end_qualifier.dart
new file mode 100644
index 00000000..80b2fdad
--- /dev/null
+++ b/das_client/lib/sfera/src/model/enums/start_end_qualifier.dart
@@ -0,0 +1,15 @@
+import 'package:das_client/sfera/src/model/enums/xml_enum.dart';
+
+enum StartEndQualifier implements XmlEnum {
+ starts(xmlValue: 'Starts'),
+ ends(xmlValue: 'Ends'),
+ startsEnds(xmlValue: 'StartsEnds'),
+ wholeSp(xmlValue: 'WholeSP');
+
+ const StartEndQualifier({
+ required this.xmlValue,
+ });
+
+ @override
+ final String xmlValue;
+}
diff --git a/das_client/lib/sfera/src/model/enums/temporary_constraint_type.dart b/das_client/lib/sfera/src/model/enums/temporary_constraint_type.dart
new file mode 100644
index 00000000..3379ff03
--- /dev/null
+++ b/das_client/lib/sfera/src/model/enums/temporary_constraint_type.dart
@@ -0,0 +1,21 @@
+import 'package:das_client/sfera/src/model/enums/xml_enum.dart';
+
+enum TemporaryConstraintType implements XmlEnum {
+ asr(xmlValue: 'ASR'),
+ lowAdhesion(xmlValue: 'Low_Adhesion'),
+ tractionTotalCurrent(xmlValue: 'TractionTotalCurrent'),
+ regenerationTotalCurrent(xmlValue: 'RegenerationTotalCurrent'),
+ powerAdvice(xmlValue: 'PowerAdvice'),
+ estimatedVoltage(xmlValue: 'EstimatedVoltage'),
+ wind(xmlValue: 'Wind'),
+ unavailableDasOperatingModes(xmlValue: 'Unavailable_DAS_OperatingModes'),
+ advisedSpeed(xmlValue: 'AdvisedSpeed'),
+ networkSpecificConstraint(xmlValue: 'NetworkSpecificConstraint');
+
+ const TemporaryConstraintType({
+ required this.xmlValue,
+ });
+
+ @override
+ final String xmlValue;
+}
diff --git a/das_client/lib/sfera/src/model/network_specific_area.dart b/das_client/lib/sfera/src/model/network_specific_area.dart
new file mode 100644
index 00000000..62dfd3df
--- /dev/null
+++ b/das_client/lib/sfera/src/model/network_specific_area.dart
@@ -0,0 +1,34 @@
+import 'package:das_client/sfera/src/model/enums/start_end_qualifier.dart';
+import 'package:das_client/sfera/src/model/enums/xml_enum.dart';
+import 'package:das_client/sfera/src/model/network_specific_parameter.dart';
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class NetworkSpecificArea extends SferaXmlElement {
+ static const String elementType = 'NetworkSpecificArea';
+ static const String _trackEquipmentTypeName = 'trackEquipmentType';
+
+ NetworkSpecificArea({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get name => attributes['name']!;
+
+ StartEndQualifier? get startEndQualifier =>
+ XmlEnum.valueOf(StartEndQualifier.values, attributes['startEndQualifier']);
+
+ double? get startLocation => _parseOrNull(attributes['startLocation']);
+
+ double? get endLocation => _parseOrNull(attributes['endLocation']);
+
+ Iterable get networkSpecificParameters => children.whereType();
+
+ NetworkSpecificParameter? get trackEquipmentType =>
+ children.whereType().where((it) => it.name == _trackEquipmentTypeName).firstOrNull;
+
+ double? _parseOrNull(String? source) {
+ return source != null ? double.parse(source) : null;
+ }
+
+ @override
+ bool validate() {
+ return validateHasAttribute('name') && validateHasAttribute('startEndQualifier') && 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
index aa1b3f8d..305f7af1 100644
--- a/das_client/lib/sfera/src/model/network_specific_parameter.dart
+++ b/das_client/lib/sfera/src/model/network_specific_parameter.dart
@@ -14,3 +14,9 @@ class NetworkSpecificParameter extends SferaXmlElement {
return validateHasAttribute('name') && validateHasAttribute('value') && super.validate();
}
}
+
+// extensions
+
+extension NetworkSpecificParametersExtension on Iterable {
+ NetworkSpecificParameter? withName(String name) => where((it) => it.name == name).firstOrNull;
+}
diff --git a/das_client/lib/sfera/src/model/network_specific_point.dart b/das_client/lib/sfera/src/model/network_specific_point.dart
index 9b75892e..c959cf1a 100644
--- a/das_client/lib/sfera/src/model/network_specific_point.dart
+++ b/das_client/lib/sfera/src/model/network_specific_point.dart
@@ -14,4 +14,4 @@ class NetworkSpecificPoint extends SpGenericPoint {
bool validate() {
return validateHasChildOfType() && super.validate();
}
-}
+}
\ No newline at end of file
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 c34ca5dd..67851e12 100644
--- a/das_client/lib/sfera/src/model/segment_profile_list.dart
+++ b/das_client/lib/sfera/src/model/segment_profile_list.dart
@@ -1,5 +1,7 @@
+import 'package:das_client/sfera/src/model/enums/temporary_constraint_type.dart';
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
import 'package:das_client/sfera/src/model/sp_zone.dart';
+import 'package:das_client/sfera/src/model/temporary_constraints.dart';
import 'package:das_client/sfera/src/model/timing_point_constraints.dart';
class SegmentProfileList extends SferaXmlElement {
@@ -17,6 +19,10 @@ class SegmentProfileList extends SferaXmlElement {
Iterable get timingPointsContraints => children.whereType();
+ Iterable get asrTemporaryConstrains => children
+ .whereType()
+ .where((it) => it.temporaryConstraintType == TemporaryConstraintType.asr);
+
@override
bool validate() {
return validateHasAttribute('SP_ID') &&
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 a6c794c3..ec591684 100644
--- a/das_client/lib/sfera/src/model/sfera_xml_element.dart
+++ b/das_client/lib/sfera/src/model/sfera_xml_element.dart
@@ -42,6 +42,21 @@ class SferaXmlElement {
return true;
}
+ bool validateHasAttributeInt(String attribute) {
+ if (!attributes.containsKey(attribute)) {
+ Fimber.w('Validation failed for $type because attribute=$attribute is missing');
+ return false;
+ }
+
+ if (int.tryParse(attributes[attribute]!) == null) {
+ Fimber.w(
+ 'Validation failed for $type because attribute=$attribute with value=${attributes[attribute]} could not be parsed to int');
+ 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');
diff --git a/das_client/lib/sfera/src/model/signal.dart b/das_client/lib/sfera/src/model/signal.dart
index ef335c70..fe8440b0 100644
--- a/das_client/lib/sfera/src/model/signal.dart
+++ b/das_client/lib/sfera/src/model/signal.dart
@@ -1,5 +1,7 @@
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+import 'package:das_client/sfera/src/model/signal_function.dart';
import 'package:das_client/sfera/src/model/signal_id.dart';
+import 'package:das_client/sfera/src/model/signal_physical_characteristics.dart';
class Signal extends SferaXmlElement {
static const String elementType = 'Signal';
@@ -8,6 +10,10 @@ class Signal extends SferaXmlElement {
SignalId get id => children.whereType().first;
+ SignalPhysicalCharacteristics? get physicalCharacteristics => children.whereType().firstOrNull;
+
+ Iterable get functions => children.whereType();
+
@override
bool validate() {
return validateHasChildOfType() && super.validate();
diff --git a/das_client/lib/sfera/src/model/signal_function.dart b/das_client/lib/sfera/src/model/signal_function.dart
new file mode 100644
index 00000000..26fa378c
--- /dev/null
+++ b/das_client/lib/sfera/src/model/signal_function.dart
@@ -0,0 +1,7 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class SignalFunction extends SferaXmlElement {
+ static const String elementType = 'SignalFunction';
+
+ SignalFunction({super.type = elementType, super.attributes, super.children, super.value});
+}
diff --git a/das_client/lib/sfera/src/model/signal_id.dart b/das_client/lib/sfera/src/model/signal_id.dart
index d0279d34..6ba8e497 100644
--- a/das_client/lib/sfera/src/model/signal_id.dart
+++ b/das_client/lib/sfera/src/model/signal_id.dart
@@ -7,10 +7,10 @@ class SignalId extends SferaXmlElement {
String get physicalId => attributes['signal_ID_Physical']!;
- String get location => attributes['location']!;
+ double get location => double.parse(attributes['location']!);
@override
bool validate() {
- return validateHasAttribute('signal_ID_Physical') && validateHasAttribute('location') && super.validate();
+ return validateHasAttribute('signal_ID_Physical') && validateHasAttributeDouble('location') && super.validate();
}
}
diff --git a/das_client/lib/sfera/src/model/signal_physical_characteristics.dart b/das_client/lib/sfera/src/model/signal_physical_characteristics.dart
new file mode 100644
index 00000000..b26d6463
--- /dev/null
+++ b/das_client/lib/sfera/src/model/signal_physical_characteristics.dart
@@ -0,0 +1,14 @@
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class SignalPhysicalCharacteristics extends SferaXmlElement {
+ static const String elementType = 'SignalPhysicalCharacteristics';
+
+ SignalPhysicalCharacteristics({super.type = elementType, super.attributes, super.children, super.value});
+
+ String get visualIdentifier => attributes['visualIdentifier']!;
+
+ @override
+ bool validate() {
+ return validateHasAttribute('visualIdentifier') && super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/model/sp_areas.dart b/das_client/lib/sfera/src/model/sp_areas.dart
index a3b1b89e..431a94d6 100644
--- a/das_client/lib/sfera/src/model/sp_areas.dart
+++ b/das_client/lib/sfera/src/model/sp_areas.dart
@@ -1,10 +1,15 @@
+import 'package:das_client/sfera/src/model/network_specific_area.dart';
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';
+ static const String _nonStandardTrackEquipmentName = 'nonStandardTrackEquipment';
SpAreas({super.type = elementType, super.attributes, super.children, super.value});
Iterable get tafTapLocations => children.whereType();
+
+ Iterable get nonStandardTrackEquipments =>
+ children.whereType().where((it) => it.name == _nonStandardTrackEquipmentName);
}
diff --git a/das_client/lib/sfera/src/model/sp_points.dart b/das_client/lib/sfera/src/model/sp_points.dart
index fbe92ea2..e763ff77 100644
--- a/das_client/lib/sfera/src/model/sp_points.dart
+++ b/das_client/lib/sfera/src/model/sp_points.dart
@@ -7,6 +7,7 @@ import 'package:das_client/sfera/src/model/virtual_balise.dart';
class SpPoints extends SferaXmlElement {
static const String elementType = 'SP_Points';
static const String _protectionSectionNspName = 'protectionSection';
+ static const String _curvePointName = 'curvePoint';
SpPoints({super.type = elementType, super.attributes, super.children, super.value});
@@ -14,6 +15,9 @@ class SpPoints extends SferaXmlElement {
Iterable get signals => children.whereType();
+ Iterable get curvePointsNsp =>
+ children.whereType().where((it) => it.name == _curvePointName);
+
Iterable get balise => children.whereType();
Iterable get protectionSectionNsp =>
diff --git a/das_client/lib/sfera/src/model/temporary_constraint_reason.dart b/das_client/lib/sfera/src/model/temporary_constraint_reason.dart
new file mode 100644
index 00000000..dd28299f
--- /dev/null
+++ b/das_client/lib/sfera/src/model/temporary_constraint_reason.dart
@@ -0,0 +1,7 @@
+import 'package:das_client/sfera/src/model/multilingual_text.dart';
+
+class TemporaryConstraintReason extends MultilingualText {
+ static const String elementType = 'TemporaryConstraintReason';
+
+ TemporaryConstraintReason({super.type = elementType, super.attributes, super.children, super.value});
+}
diff --git a/das_client/lib/sfera/src/model/temporary_constraints.dart b/das_client/lib/sfera/src/model/temporary_constraints.dart
new file mode 100644
index 00000000..be2814fe
--- /dev/null
+++ b/das_client/lib/sfera/src/model/temporary_constraints.dart
@@ -0,0 +1,36 @@
+import 'package:das_client/sfera/src/model/additional_speed_restriction.dart';
+import 'package:das_client/sfera/src/model/enums/start_end_qualifier.dart';
+import 'package:das_client/sfera/src/model/enums/temporary_constraint_type.dart';
+import 'package:das_client/sfera/src/model/enums/xml_enum.dart';
+import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
+
+class TemporaryConstraints extends SferaXmlElement {
+ static const String elementType = 'TemporaryConstraints';
+
+ TemporaryConstraints({super.type = elementType, super.attributes, super.children, super.value});
+
+ StartEndQualifier get startEndQualifier =>
+ XmlEnum.valueOf(StartEndQualifier.values, attributes['startEndQualifier']!)!;
+
+ double? get startLocation =>
+ attributes['startLocation'] != null ? double.tryParse(attributes['startLocation']!) : null;
+
+ double? get endLocation => attributes['endLocation'] != null ? double.tryParse(attributes['endLocation']!) : null;
+
+ DateTime? get startTime => attributes['startTime'] != null ? DateTime.tryParse(attributes['startTime']!) : null;
+
+ DateTime? get endTime => attributes['endTime'] != null ? DateTime.tryParse(attributes['endTime']!) : null;
+
+ TemporaryConstraintType get temporaryConstraintType =>
+ XmlEnum.valueOf(TemporaryConstraintType.values, attributes['temporaryConstraintType']!)!;
+
+ AdditionalSpeedRestriction? get additionalSpeedRestriction =>
+ children.whereType().firstOrNull;
+
+ @override
+ bool validate() {
+ return validateHasAttribute('startEndQualifier') &&
+ validateHasAttribute('temporaryConstraintType') &&
+ super.validate();
+ }
+}
diff --git a/das_client/lib/sfera/src/sfera_reply_parser.dart b/das_client/lib/sfera/src/sfera_reply_parser.dart
index 4973d4c9..8fdbdb00 100644
--- a/das_client/lib/sfera/src/sfera_reply_parser.dart
+++ b/das_client/lib/sfera/src/sfera_reply_parser.dart
@@ -1,3 +1,4 @@
+import 'package:das_client/sfera/src/model/additional_speed_restriction.dart';
import 'package:das_client/sfera/src/model/current_limitation.dart';
import 'package:das_client/sfera/src/model/current_limitation_change.dart';
import 'package:das_client/sfera/src/model/current_limitation_start.dart';
@@ -11,6 +12,7 @@ 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_area.dart';
import 'package:das_client/sfera/src/model/network_specific_parameter.dart';
import 'package:das_client/sfera/src/model/network_specific_point.dart';
import 'package:das_client/sfera/src/model/otn_id.dart';
@@ -19,7 +21,9 @@ import 'package:das_client/sfera/src/model/segment_profile_list.dart';
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_function.dart';
import 'package:das_client/sfera/src/model/signal_id.dart';
+import 'package:das_client/sfera/src/model/signal_physical_characteristics.dart';
import 'package:das_client/sfera/src/model/sp_areas.dart';
import 'package:das_client/sfera/src/model/sp_characteristics.dart';
import 'package:das_client/sfera/src/model/sp_context_information.dart';
@@ -32,6 +36,8 @@ 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/temporary_constraint_reason.dart';
+import 'package:das_client/sfera/src/model/temporary_constraints.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';
@@ -109,6 +115,10 @@ class SferaReplyParser {
return Signal(type: type, attributes: attributes, children: children, value: value);
case SignalId.elementType:
return SignalId(type: type, attributes: attributes, children: children, value: value);
+ case SignalPhysicalCharacteristics.elementType:
+ return SignalPhysicalCharacteristics(type: type, attributes: attributes, children: children, value: value);
+ case SignalFunction.elementType:
+ return SignalFunction(type: type, attributes: attributes, children: children, value: value);
case VirtualBalise.elementType:
return VirtualBalise(type: type, attributes: attributes, children: children, value: value);
case VirtualBalisePosition.elementType:
@@ -155,6 +165,14 @@ class SferaReplyParser {
return SpCharacteristics(type: type, attributes: attributes, children: children, value: value);
case NetworkSpecificPoint.elementType:
return NetworkSpecificPoint(type: type, attributes: attributes, children: children, value: value);
+ case NetworkSpecificArea.elementType:
+ return NetworkSpecificArea(type: type, attributes: attributes, children: children, value: value);
+ case AdditionalSpeedRestriction.elementType:
+ return AdditionalSpeedRestriction(type: type, attributes: attributes, children: children, value: value);
+ case TemporaryConstraints.elementType:
+ return TemporaryConstraints(type: type, attributes: attributes, children: children, value: value);
+ case TemporaryConstraintReason.elementType:
+ return TemporaryConstraintReason(type: type, attributes: attributes, children: children, value: value);
default:
return SferaXmlElement(type: type, attributes: attributes, children: children, value: value);
}
diff --git a/das_client/test/model/journey/signal_test.dart b/das_client/test/model/journey/signal_test.dart
new file mode 100644
index 00000000..1674f1ec
--- /dev/null
+++ b/das_client/test/model/journey/signal_test.dart
@@ -0,0 +1,24 @@
+import 'package:das_client/model/journey/signal.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ test('Test string factory for signal functions', () {
+ // when
+ final randomType = SignalFunction.from('random-type');
+ final block = SignalFunction.from('block');
+ final entry = SignalFunction.from('entry');
+ final exit = SignalFunction.from('exit');
+ final intermediate = SignalFunction.from('intermediate');
+ final laneChange = SignalFunction.from('laneChange');
+ final protection = SignalFunction.from('protection');
+
+ // then
+ expect(randomType, SignalFunction.unknown);
+ expect(block, SignalFunction.block);
+ expect(entry, SignalFunction.entry);
+ expect(exit, SignalFunction.exit);
+ expect(intermediate, SignalFunction.intermediate);
+ expect(laneChange, SignalFunction.laneChange);
+ expect(protection, SignalFunction.protection);
+ });
+}
diff --git a/das_client/test/model/journey/track_equipment_test.dart b/das_client/test/model/journey/track_equipment_test.dart
new file mode 100644
index 00000000..799402ce
--- /dev/null
+++ b/das_client/test/model/journey/track_equipment_test.dart
@@ -0,0 +1,108 @@
+import 'package:das_client/model/journey/track_equipment.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ group('Test track equipment', () {
+ test('Test track equipment on location with start and end location', () {
+ // given
+ final trackEquipment = TrackEquipment(
+ type: TrackEquipmentType.etcsL2ExtSpeedReversingPossible,
+ startLocation: 100.0,
+ endLocation: 300.0,
+ appliesToWholeSp: false,
+ );
+
+ // when
+ final belowStart = trackEquipment.isOnLocation(50.0);
+ final onStart = trackEquipment.isOnLocation(100.0);
+ final between = trackEquipment.isOnLocation(200.0);
+ final onEnd = trackEquipment.isOnLocation(300.0);
+ final aboveEnd = trackEquipment.isOnLocation(400.0);
+
+ // then
+ expect(belowStart, isFalse);
+ expect(onStart, isTrue);
+ expect(between, isTrue);
+ expect(onEnd, isTrue);
+ expect(aboveEnd, isFalse);
+ });
+
+ test('Test track equipment on location that applies whole segment', () async {
+ final trackEquipment = TrackEquipment(
+ type: TrackEquipmentType.etcsL2ExtSpeedReversingPossible,
+ appliesToWholeSp: true,
+ );
+
+ // this combination would normally not happen but should be tested.
+ final trackEquipmentWithStartEnd = TrackEquipment(
+ type: TrackEquipmentType.etcsL2ExtSpeedReversingPossible,
+ startLocation: 100.0,
+ endLocation: 300.0,
+ appliesToWholeSp: true,
+ );
+
+ // when
+ final onLocation1 = trackEquipment.isOnLocation(0);
+ final onLocation2 = trackEquipment.isOnLocation(9999);
+ final belowStart = trackEquipmentWithStartEnd.isOnLocation(50.0);
+ final onStart = trackEquipmentWithStartEnd.isOnLocation(100.0);
+ final between = trackEquipmentWithStartEnd.isOnLocation(200.0);
+ final onEnd = trackEquipmentWithStartEnd.isOnLocation(300.0);
+ final aboveEnd = trackEquipmentWithStartEnd.isOnLocation(400.0);
+
+ // then
+ expect(onLocation1, isTrue);
+ expect(onLocation2, isTrue);
+ expect(belowStart, isTrue);
+ expect(onStart, isTrue);
+ expect(between, isTrue);
+ expect(onEnd, isTrue);
+ expect(aboveEnd, isTrue);
+ });
+
+ test('Test track equipment on location with only start or end location', () async {
+ final trackEquipmentWithStart = TrackEquipment(
+ type: TrackEquipmentType.etcsL2ExtSpeedReversingPossible,
+ startLocation: 300.0,
+ );
+
+ final trackEquipmentWithEnd = TrackEquipment(
+ type: TrackEquipmentType.etcsL2ExtSpeedReversingPossible,
+ endLocation: 400.0,
+ );
+
+ // when
+ final belowStart = trackEquipmentWithStart.isOnLocation(100);
+ final onStart = trackEquipmentWithStart.isOnLocation(300);
+ final aboveStart = trackEquipmentWithStart.isOnLocation(400);
+ final belowEnd = trackEquipmentWithEnd.isOnLocation(100);
+ final onEnd = trackEquipmentWithEnd.isOnLocation(400);
+ final aboveEnd = trackEquipmentWithEnd.isOnLocation(600);
+
+ // then
+ expect(belowStart, isFalse);
+ expect(onStart, isTrue);
+ expect(aboveStart, isTrue);
+ expect(belowEnd, isTrue);
+ expect(onEnd, isTrue);
+ expect(aboveEnd, isFalse);
+ });
+ });
+
+
+ group('Test track equipment type', () {
+ test('Test string factory for track equipment type', () {
+ // when
+ final tracksWithSingleTrackEquipment = TrackEquipmentType.from('ETCS-L1LS-2TracksWithSingleTrackEquipment');
+ final convSpeedReversingImpossible = TrackEquipmentType.from('ETCS-L2-convSpeedReversingImpossible');
+ final extSpeedReversingPossible = TrackEquipmentType.from('ETCS-L2-extSpeedReversingPossible');
+ final extSpeedReversingImpossible = TrackEquipmentType.from('ETCS-L2-extSpeedReversingImpossible');
+
+ // then
+ expect(tracksWithSingleTrackEquipment, TrackEquipmentType.etcsL1ls2TracksWithSingleTrackEquipment);
+ expect(convSpeedReversingImpossible, TrackEquipmentType.etcsL2ConvSpeedReversingImpossible);
+ expect(extSpeedReversingPossible, TrackEquipmentType.etcsL2ExtSpeedReversingPossible);
+ expect(extSpeedReversingImpossible, TrackEquipmentType.etcsL2ExtSpeedReversingImpossible);
+ });
+ });
+}
diff --git a/das_client/test/sfera/mapper/sfera_mapper_test.dart b/das_client/test/sfera/mapper/sfera_mapper_test.dart
index 975936f5..b07aadd4 100644
--- a/das_client/test/sfera/mapper/sfera_mapper_test.dart
+++ b/das_client/test/sfera/mapper/sfera_mapper_test.dart
@@ -1,9 +1,13 @@
import 'dart:io';
+import 'package:das_client/model/journey/additional_speed_restriction_data.dart';
+import 'package:das_client/model/journey/curve_point.dart';
import 'package:das_client/model/journey/datatype.dart';
import 'package:das_client/model/journey/journey.dart';
import 'package:das_client/model/journey/protection_section.dart';
import 'package:das_client/model/journey/service_point.dart';
+import 'package:das_client/model/journey/signal.dart';
+import 'package:das_client/model/journey/track_equipment.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';
@@ -22,13 +26,13 @@ void main() {
return files;
}
- Journey getJourney(String trainNumber, int spCount) {
+ Journey getJourney(String trainNumber, int spCount, {String? spTrainNumber}) {
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)) {
+ for (final File file in getFilesForSp('SFERA_SP_${spTrainNumber ?? trainNumber}', spCount)) {
final segmentProfile = SferaReplyParser.parse(file.readAsStringSync());
expect(segmentProfile.validate(), true);
segmentProfiles.add(segmentProfile);
@@ -57,29 +61,199 @@ void main() {
expect(servicePoints[4].name.de, 'Klammerbahnhof D1');
});
+ test('Test journey data types correctly generated', () async {
+ final journey = getJourney('9999', 5);
+
+ expect(journey.valid, true);
+ expect(journey.data, hasLength(17));
+
+ // segment 1
+ expect(journey.data[0], TypeMatcher());
+ expect(journey.data[1], TypeMatcher());
+ expect(journey.data[2], TypeMatcher());
+ expect(journey.data[3], TypeMatcher());
+ expect(journey.data[4], TypeMatcher());
+ // segment 2
+ expect(journey.data[5], TypeMatcher());
+ expect(journey.data[6], TypeMatcher());
+ expect(journey.data[7], TypeMatcher());
+ expect(journey.data[8], TypeMatcher());
+ // segment 3
+ expect(journey.data[9], TypeMatcher());
+ expect(journey.data[10], TypeMatcher());
+ expect(journey.data[11], TypeMatcher());
+ // segment 4
+ expect(journey.data[12], TypeMatcher());
+ expect(journey.data[13], TypeMatcher());
+ // segment 5
+ expect(journey.data[14], TypeMatcher());
+ expect(journey.data[15], TypeMatcher());
+ expect(journey.data[16], TypeMatcher());
+ });
+
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);
+ expect(journey.data, hasLength(17));
+
+ // segment 1
+ expect(journey.data[0].kilometre[0], 0.2);
+ expect(journey.data[1].kilometre[0], 0.5);
+ expect(journey.data[2].kilometre[0], 0.6);
+ expect(journey.data[3].kilometre[0], 0.7);
+ expect(journey.data[4].kilometre[0], 0.9);
+ // segment 2
+ expect(journey.data[5].kilometre[0], 1.2);
+ expect(journey.data[6].kilometre[0], 1.5);
+ expect(journey.data[7].kilometre[0], 1.7);
+ expect(journey.data[8].kilometre[0], 1.8);
+ // segment 3
+ expect(journey.data[9].kilometre[0], 2.1);
+ expect(journey.data[10].kilometre[0], 2.4);
+ expect(journey.data[11].kilometre[0], 2.6);
+ // segment 4
+ expect(journey.data[12].kilometre[0], 3.7);
+ expect(journey.data[12].kilometre[1], 0);
+ expect(journey.data[13].kilometre[0], 0.2);
+ // segment 5
+ expect(journey.data[14].kilometre[0], 0.4);
+ expect(journey.data[15].kilometre[0], 0.6);
+ expect(journey.data[16].kilometre[0], 0.8);
});
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);
+ expect(journey.data, hasLength(17));
+
+ // segment 1
+ expect(journey.data[0].order, 000200);
+ expect(journey.data[1].order, 000500);
+ expect(journey.data[2].order, 000600);
+ expect(journey.data[3].order, 000700);
+ expect(journey.data[4].order, 000900);
+ // segment 2
+ expect(journey.data[5].order, 100200);
+ expect(journey.data[6].order, 100500);
+ expect(journey.data[7].order, 100700);
+ expect(journey.data[8].order, 100800);
+ // segment 3
+ expect(journey.data[9].order, 200100);
+ expect(journey.data[10].order, 200400);
+ expect(journey.data[11].order, 200600);
+ // segment 4
+ expect(journey.data[12].order, 300700);
+ expect(journey.data[13].order, 300900);
+ // segment 5
+ expect(journey.data[14].order, 400100);
+ expect(journey.data[15].order, 400300);
+ expect(journey.data[16].order, 400500);
+ });
+
+ test('Test track equipment is generated correctly', () async {
+ final journey = getJourney('9999', 5);
+
+ expect(journey.valid, true);
+ expect(journey.data, hasLength(17));
+
+ // segment 1
+ expect(journey.data[0].trackEquipment, isEmpty);
+ expect(journey.data[1].trackEquipment, isEmpty);
+ expect(journey.data[2].trackEquipment, isEmpty);
+ expect(journey.data[4].trackEquipment, isEmpty);
+ // segment 2
+ expect(journey.data[5].trackEquipment, hasLength(1));
+ expect(journey.data[5].trackEquipment[0].appliesToWholeSp, isTrue);
+ expect(journey.data[5].trackEquipment[0].type, TrackEquipmentType.etcsL1ls2TracksWithSingleTrackEquipment);
+ expect(journey.data[6].trackEquipment, hasLength(1));
+ expect(journey.data[6].trackEquipment[0].type, TrackEquipmentType.etcsL1ls2TracksWithSingleTrackEquipment);
+ expect(journey.data[7].trackEquipment, hasLength(1));
+ expect(journey.data[7].trackEquipment[0].type, TrackEquipmentType.etcsL1ls2TracksWithSingleTrackEquipment);
+ expect(journey.data[8].trackEquipment, hasLength(1));
+ expect(journey.data[8].trackEquipment[0].type, TrackEquipmentType.etcsL1ls2TracksWithSingleTrackEquipment);
+ // segment 3
+ expect(journey.data[9].trackEquipment, hasLength(1));
+ expect(journey.data[9].trackEquipment[0].appliesToWholeSp, isFalse);
+ expect(journey.data[9].trackEquipment[0].startLocation, 100.0);
+ expect(journey.data[9].trackEquipment[0].endLocation, 400.0);
+ expect(journey.data[9].trackEquipment[0].type, TrackEquipmentType.etcsL2ConvSpeedReversingImpossible);
+ expect(journey.data[10].trackEquipment, hasLength(1));
+ expect(journey.data[10].trackEquipment[0].type, TrackEquipmentType.etcsL2ConvSpeedReversingImpossible);
+ expect(journey.data[11].trackEquipment, hasLength(1));
+ expect(journey.data[11].trackEquipment[0].type, TrackEquipmentType.etcsL2ExtSpeedReversingPossible);
+ expect(journey.data[11].trackEquipment[0].appliesToWholeSp, isFalse);
+ expect(journey.data[11].trackEquipment[0].startLocation, 500.0);
+ expect(journey.data[11].trackEquipment[0].endLocation, isNull);
+ // segment 4
+ expect(journey.data[12].trackEquipment, hasLength(1));
+ expect(journey.data[12].trackEquipment[0].type, TrackEquipmentType.etcsL2ExtSpeedReversingPossible);
+ expect(journey.data[12].trackEquipment[0].appliesToWholeSp, isFalse);
+ expect(journey.data[12].trackEquipment[0].startLocation, isNull);
+ expect(journey.data[12].trackEquipment[0].endLocation, 800.0);
+ expect(journey.data[13].trackEquipment, isEmpty);
+ // segment 5
+ expect(journey.data[14].trackEquipment, isEmpty);
+ expect(journey.data[15].trackEquipment, isEmpty);
+ expect(journey.data[16].trackEquipment, hasLength(1));
+ expect(journey.data[16].trackEquipment[0].startLocation, 400.0);
+ expect(journey.data[16].trackEquipment[0].endLocation, 800.0);
+ expect(journey.data[16].trackEquipment[0].type, TrackEquipmentType.etcsL2ExtSpeedReversingImpossible);
+ expect(journey.data[16].trackEquipment[0].appliesToWholeSp, isFalse);
+ });
+
+ test('Test signals are generated correctly', () async {
+ final journey = getJourney('9999', 5);
+ final signals = journey.data.where((it) => it.type == Datatype.signal).cast().toList();
+
+ expect(journey.valid, true);
+ expect(signals, hasLength(7));
+ expect(signals[0].visualIdentifier, 'B1');
+ expect(signals[0].functions, hasLength(1));
+ expect(signals[0].functions[0], SignalFunction.block);
+ expect(signals[1].visualIdentifier, 'S1');
+ expect(signals[1].functions, hasLength(1));
+ expect(signals[1].functions[0], SignalFunction.laneChange);
+ expect(signals[2].visualIdentifier, 'E1');
+ expect(signals[2].functions, hasLength(1));
+ expect(signals[2].functions[0], SignalFunction.entry);
+ expect(signals[3].visualIdentifier, 'A1');
+ expect(signals[3].functions, hasLength(1));
+ expect(signals[3].functions[0], SignalFunction.exit);
+ expect(signals[4].visualIdentifier, 'AB1');
+ expect(signals[4].functions, hasLength(1));
+ expect(signals[4].functions[0], SignalFunction.intermediate);
+ expect(signals[5].visualIdentifier, 'D1');
+ expect(signals[5].functions, hasLength(1));
+ expect(signals[5].functions[0], SignalFunction.protection);
+ expect(signals[6].visualIdentifier, 'BAB1');
+ expect(signals[6].functions, hasLength(2));
+ expect(signals[6].functions[0], SignalFunction.block);
+ expect(signals[6].functions[1], SignalFunction.intermediate);
+ });
+
+ test('Test curvePoint are generated correctly', () async {
+ final journey = getJourney('9999', 5);
+ final curvePoints = journey.data.where((it) => it.type == Datatype.curvePoint).cast().toList();
+
+ expect(journey.valid, true);
+ expect(curvePoints, hasLength(5));
+ expect(curvePoints[0].curvePointType, CurvePointType.begin);
+ expect(curvePoints[0].curveType, CurveType.curve);
+ expect(curvePoints[0].comment, 'Kurve 1');
+ expect(curvePoints[1].curvePointType, CurvePointType.end);
+ expect(curvePoints[1].curveType, isNull);
+ expect(curvePoints[1].comment, isNull);
+ expect(curvePoints[2].curvePointType, CurvePointType.begin);
+ expect(curvePoints[2].curveType, CurveType.curve);
+ expect(curvePoints[2].comment, 'Kurve 1');
+ expect(curvePoints[3].curvePointType, CurvePointType.begin);
+ expect(curvePoints[3].curveType, CurveType.stationExitCurve);
+ expect(curvePoints[3].comment, 'Kurve 2');
+ expect(curvePoints[4].curvePointType, CurvePointType.begin);
+ expect(curvePoints[4].curveType, CurveType.curveAfterHalt);
+ expect(curvePoints[4].comment, 'Kurve 3');
});
test('Test stop on demand is parsed correctly', () async {
@@ -155,4 +329,89 @@ void main() {
expect(protectionSections[5].isLong, false);
expect(protectionSections[5].isOptional, false);
});
+
+ test('Test additional speed restriction is parsed correctly no items between', () async {
+ final journey = getJourney('513', 1);
+ final speedRestrictions = journey.data.where((it) => it.type == Datatype.additionalSpeedRestriction).cast().toList();
+
+ expect(journey.valid, true);
+ expect(speedRestrictions, hasLength(1));
+ expect(speedRestrictions[0].restriction.kmFrom, 64.2);
+ expect(speedRestrictions[0].restriction.kmTo, 63.2);
+ expect(speedRestrictions[0].restriction.orderFrom, 700);
+ expect(speedRestrictions[0].restriction.orderTo, 800);
+ expect(speedRestrictions[0].restriction.speed, 60);
+
+ expect(journey.metadata.additionalSpeedRestrictions, hasLength(1));
+ expect(journey.metadata.additionalSpeedRestrictions[0].kmFrom, 64.2);
+ expect(journey.metadata.additionalSpeedRestrictions[0].kmTo, 63.2);
+ expect(journey.metadata.additionalSpeedRestrictions[0].orderFrom, 700);
+ expect(journey.metadata.additionalSpeedRestrictions[0].orderTo, 800);
+ expect(journey.metadata.additionalSpeedRestrictions[0].speed, 60);
+ });
+
+ test('Test additional speed restriction is parsed correctly over multiple segments', () async {
+ final journey = getJourney('500', 3);
+ final speedRestrictions = journey.data.where((it) => it.type == Datatype.additionalSpeedRestriction).cast().toList();
+
+ expect(journey.valid, true);
+ expect(speedRestrictions, hasLength(2));
+ expect(speedRestrictions[0].restriction.kmFrom, 64.2);
+ expect(speedRestrictions[0].restriction.kmTo, 47.2);
+ expect(speedRestrictions[0].restriction.orderFrom, 700);
+ expect(speedRestrictions[0].restriction.orderTo, 206800);
+ expect(speedRestrictions[0].restriction.speed, 60);
+ expect(speedRestrictions[0].order, 700);
+ expect(speedRestrictions[1].restriction.kmFrom, 64.2);
+ expect(speedRestrictions[1].restriction.kmTo, 47.2);
+ expect(speedRestrictions[1].restriction.orderFrom, 700);
+ expect(speedRestrictions[1].restriction.orderTo, 206800);
+ expect(speedRestrictions[1].restriction.speed, 60);
+ expect(speedRestrictions[1].order, 206800);
+
+ expect(journey.metadata.additionalSpeedRestrictions, hasLength(1));
+ expect(journey.metadata.additionalSpeedRestrictions[0].kmFrom, 64.2);
+ expect(journey.metadata.additionalSpeedRestrictions[0].kmTo, 47.2);
+ expect(journey.metadata.additionalSpeedRestrictions[0].orderFrom, 700);
+ expect(journey.metadata.additionalSpeedRestrictions[0].orderTo, 206800);
+ expect(journey.metadata.additionalSpeedRestrictions[0].speed, 60);
+ });
+
+ test('Test additional speed restriction without a date', () async {
+ final journey = getJourney('513_asp_no_date', 1, spTrainNumber: '513');
+ final speedRestrictions = journey.data.where((it) => it.type == Datatype.additionalSpeedRestriction).cast().toList();
+
+ expect(journey.valid, true);
+ expect(speedRestrictions, hasLength(1));
+ expect(speedRestrictions[0].restriction.kmFrom, 64.2);
+ expect(speedRestrictions[0].restriction.kmTo, 63.2);
+ expect(speedRestrictions[0].restriction.orderFrom, 700);
+ expect(speedRestrictions[0].restriction.orderTo, 800);
+ expect(speedRestrictions[0].restriction.speed, 60);
+
+ expect(journey.metadata.additionalSpeedRestrictions, hasLength(1));
+ expect(journey.metadata.additionalSpeedRestrictions[0].kmFrom, 64.2);
+ expect(journey.metadata.additionalSpeedRestrictions[0].kmTo, 63.2);
+ expect(journey.metadata.additionalSpeedRestrictions[0].orderFrom, 700);
+ expect(journey.metadata.additionalSpeedRestrictions[0].orderTo, 800);
+ expect(journey.metadata.additionalSpeedRestrictions[0].speed, 60);
+ });
+
+ test('Test additional speed restriction with date in the past', () async {
+ final journey = getJourney('513_asp_date_before', 1, spTrainNumber: '513');
+ final speedRestrictions = journey.data.where((it) => it.type == Datatype.additionalSpeedRestriction).cast().toList();
+
+ expect(journey.valid, true);
+ expect(speedRestrictions, hasLength(0));
+ expect(journey.metadata.additionalSpeedRestrictions, hasLength(0));
+ });
+
+ test('Test additional speed restriction with date in the future', () async {
+ final journey = getJourney('513_asp_date_after', 1, spTrainNumber: '513');
+ final speedRestrictions = journey.data.where((it) => it.type == Datatype.additionalSpeedRestriction).cast().toList();
+
+ expect(journey.valid, true);
+ expect(speedRestrictions, hasLength(0));
+ expect(journey.metadata.additionalSpeedRestrictions, hasLength(0));
+ });
}
diff --git a/das_client/test/sfera/model/sfera_document_test.dart b/das_client/test/sfera/model/sfera_document_test.dart
index b36bf0a7..30c6272e 100644
--- a/das_client/test/sfera/model/sfera_document_test.dart
+++ b/das_client/test/sfera/model/sfera_document_test.dart
@@ -65,7 +65,7 @@ void main() {
expect(spPoint.signals, hasLength(9));
expect(spPoint.signals.first.id.physicalId, '102346');
- expect(spPoint.signals.first.id.location, '843');
+ expect(spPoint.signals.first.id.location, 843.0);
expect(spPoint.balise, hasLength(3));
expect(spPoint.balise.first.location, '0');
diff --git a/das_client/test_resources/jp/SFERA_JP_500.xml b/das_client/test_resources/jp/SFERA_JP_500.xml
new file mode 100644
index 00000000..6ec92ee5
--- /dev/null
+++ b/das_client/test_resources/jp/SFERA_JP_500.xml
@@ -0,0 +1,141 @@
+
+
+
+
+ 1085
+ 500
+ 2022-01-04
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/jp/SFERA_JP_513.xml b/das_client/test_resources/jp/SFERA_JP_513.xml
index 0a39af76..124c7628 100644
--- a/das_client/test_resources/jp/SFERA_JP_513.xml
+++ b/das_client/test_resources/jp/SFERA_JP_513.xml
@@ -24,12 +24,12 @@
-
+
-
+
@@ -118,5 +118,9 @@
+
+
+
diff --git a/das_client/test_resources/jp/SFERA_JP_513_asp_date_after.xml b/das_client/test_resources/jp/SFERA_JP_513_asp_date_after.xml
new file mode 100644
index 00000000..e46b99f1
--- /dev/null
+++ b/das_client/test_resources/jp/SFERA_JP_513_asp_date_after.xml
@@ -0,0 +1,126 @@
+
+
+
+
+ 1085
+ 513
+ 2022-01-04
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/jp/SFERA_JP_513_asp_date_before.xml b/das_client/test_resources/jp/SFERA_JP_513_asp_date_before.xml
new file mode 100644
index 00000000..226b0bcd
--- /dev/null
+++ b/das_client/test_resources/jp/SFERA_JP_513_asp_date_before.xml
@@ -0,0 +1,126 @@
+
+
+
+
+ 1085
+ 513
+ 2022-01-04
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/jp/SFERA_JP_513_asp_no_date.xml b/das_client/test_resources/jp/SFERA_JP_513_asp_no_date.xml
new file mode 100644
index 00000000..4872fa1d
--- /dev/null
+++ b/das_client/test_resources/jp/SFERA_JP_513_asp_no_date.xml
@@ -0,0 +1,126 @@
+
+
+
+
+ 1085
+ 513
+ 2022-01-04
+
+
+
+
+ 0085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_500_1.xml b/das_client/test_resources/sp/SFERA_SP_500_1.xml
new file mode 100644
index 00000000..646b2a1c
--- /dev/null
+++ b/das_client/test_resources/sp/SFERA_SP_500_1.xml
@@ -0,0 +1,144 @@
+
+
+
+ 0085
+
+
+
+
+
+ CH
+ 3002
+
+
+
+
+
+
+ CH
+ 3003
+
+
+
+
+
+
+ CH
+ 3004
+
+
+
+
+
+
+ CH
+ 3005
+
+
+
+
+
+
+ CH
+ 3006
+
+
+
+
+
+
+
+
+
+
+
+
+ CH
+ 3002
+
+
+
+
+
+
+ CH
+ 3003
+
+
+
+
+
+
+ CH
+ 3004
+
+
+
+
+
+
+ CH
+ 3005
+
+
+
+
+
+
+ CH
+ 3006
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_500_2.xml b/das_client/test_resources/sp/SFERA_SP_500_2.xml
new file mode 100644
index 00000000..7782acb0
--- /dev/null
+++ b/das_client/test_resources/sp/SFERA_SP_500_2.xml
@@ -0,0 +1,190 @@
+
+
+
+ 0085
+
+
+
+
+
+ CH
+ 3007
+
+
+
+
+
+
+ CH
+ 3008
+
+
+
+
+
+
+ CH
+ 3009
+
+
+
+
+
+
+ CH
+ 3010
+
+
+
+
+
+
+ CH
+ 3011
+
+
+
+
+
+
+ CH
+ 3012
+
+
+
+
+
+
+ CH
+ 3013
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CH
+ 3007
+
+
+
+
+
+
+ CH
+ 3008
+
+
+
+
+
+
+ CH
+ 3009
+
+
+
+
+
+
+ CH
+ 3010
+
+
+
+
+
+
+ CH
+ 3011
+
+
+
+
+
+
+ CH
+ 3012
+
+
+
+
+
+
+ CH
+ 3013
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_500_3.xml b/das_client/test_resources/sp/SFERA_SP_500_3.xml
new file mode 100644
index 00000000..0fc094d3
--- /dev/null
+++ b/das_client/test_resources/sp/SFERA_SP_500_3.xml
@@ -0,0 +1,255 @@
+
+
+
+ 0085
+
+
+
+
+
+ CH
+ 3014
+
+
+
+
+
+
+ CH
+ 3015
+
+
+
+
+
+
+ CH
+ 3016
+
+
+
+
+
+
+ CH
+ 3017
+
+
+
+
+
+
+ CH
+ 3018
+
+
+
+
+
+
+ CH
+ 3019
+
+
+
+
+
+
+ CH
+ 3020
+
+
+
+
+
+
+ CH
+ 3021
+
+
+
+
+
+
+ CH
+ 3022
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CH
+ 3014
+
+
+
+
+
+
+ CH
+ 3015
+
+
+
+
+
+
+ CH
+ 3016
+
+
+
+
+
+
+ CH
+ 3017
+
+
+
+
+
+
+ CH
+ 3018
+
+
+
+
+
+
+ CH
+ 3019
+
+
+
+
+
+
+ CH
+ 3020
+
+
+
+
+
+
+ CH
+ 3021
+
+
+
+
+
+
+ CH
+ 3022
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_513_1.xml b/das_client/test_resources/sp/SFERA_SP_513_1.xml
index ea635970..382f2319 100644
--- a/das_client/test_resources/sp/SFERA_SP_513_1.xml
+++ b/das_client/test_resources/sp/SFERA_SP_513_1.xml
@@ -415,6 +415,14 @@
+
+
+
+
+
+
+
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_3.xml b/das_client/test_resources/sp/SFERA_SP_9999_3.xml
index 0266583b..bc50681b 100644
--- a/das_client/test_resources/sp/SFERA_SP_9999_3.xml
+++ b/das_client/test_resources/sp/SFERA_SP_9999_3.xml
@@ -42,7 +42,7 @@
-
+
diff --git a/das_client/test_resources/sp/SFERA_SP_9999_4.xml b/das_client/test_resources/sp/SFERA_SP_9999_4.xml
index 7e08be86..1e127c46 100644
--- a/das_client/test_resources/sp/SFERA_SP_9999_4.xml
+++ b/das_client/test_resources/sp/SFERA_SP_9999_4.xml
@@ -34,7 +34,7 @@
-
+