Skip to content

Commit

Permalink
feat: added SFERA mapping for graduated station speed (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
rawi-coding committed Jan 9, 2025
1 parent f4f9013 commit 62fe845
Show file tree
Hide file tree
Showing 20 changed files with 621 additions and 22 deletions.
2 changes: 2 additions & 0 deletions das_client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
Expand Down
4 changes: 2 additions & 2 deletions das_client/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_appauth: aef998cfbcc307dff7f2fbe1f59a50323748dc25
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46

PODFILE CHECKSUM: d9dad56c0cd0b4fd8b4fe3034a53fd42a0b990f6
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import 'package:das_client/app/pages/journey/train_journey/widgets/header/adl_notification.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/header/header.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/train_journey.dart';
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';

// TODO: handle extraLarge font sizes (diff to figma) globally.
// TODO: Add testing
class TrainJourneyOverview extends StatelessWidget {
const TrainJourneyOverview({super.key});

Expand All @@ -14,10 +11,6 @@ class TrainJourneyOverview extends StatelessWidget {
return const Column(
children: [
Header(),
ADLNotification(
message: 'VMax fahren bis Wettingen',
margin: EdgeInsets.fromLTRB(sbbDefaultSpacing * 0.5, 0, sbbDefaultSpacing * 0.5, sbbDefaultSpacing),
),
Expanded(child: TrainJourney()),
],
);
Expand Down
2 changes: 1 addition & 1 deletion das_client/lib/model/journey/curve_point.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ enum CurvePointType {
}
}

/// Type of curve. Is provided only if curvePointType = 'begin'
/// Type of curve. Is provided only if type is [CurvePointType.begin]
enum CurveType {
/// begins on the line and ends on the line or a station or a halt
curve,
Expand Down
41 changes: 41 additions & 0 deletions das_client/lib/model/journey/graduated_station_speeds.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:das_client/model/journey/speed.dart';
import 'package:das_client/model/journey/train_series.dart';

class GraduatedStationSpeeds {
GraduatedStationSpeeds({
required this.trainSeries,
this.text,
this.incomingSpeeds = const [],
this.outgoingSpeeds = const [],
}) : assert(trainSeries.isNotEmpty);

final List<TrainSeries> trainSeries;
final String? text;
final List<Speed> incomingSpeeds;
final List<Speed> outgoingSpeeds;

factory GraduatedStationSpeeds.from(List<TrainSeries> trainSeries, String speedString, {String? text}) {
final parts = speedString.split('/');
final incomingSpeeds = parts[0].split('-');
final outgoingSpeeds = parts.length > 1 ? parts[1].split('-') : [];

// validate station speed format
final speedRegex = RegExp(Speed.speedRegex);
final formatSatisfied = [...incomingSpeeds, ...outgoingSpeeds].every((speed) => speedRegex.hasMatch(speed));
if (incomingSpeeds.isNotEmpty && !formatSatisfied) {
throw ArgumentError('Invalid graduated station speed format: $speedString');
}

return GraduatedStationSpeeds(
trainSeries: trainSeries,
text: text,
incomingSpeeds: incomingSpeeds.map((speed) => Speed.from(speed)).toList(),
outgoingSpeeds: outgoingSpeeds.map((speed) => Speed.from(speed)).toList(),
);
}

@override
String toString() {
return 'GraduatedStationSpeeds(trainSeries: $trainSeries, incomingSpeeds: $incomingSpeeds, outgoingSpeeds: $outgoingSpeeds)';
}
}
5 changes: 4 additions & 1 deletion das_client/lib/model/journey/service_point.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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/station_speed_data.dart';
import 'package:das_client/model/localized_string.dart';

class ServicePoint extends BaseData {
Expand All @@ -13,16 +14,18 @@ class ServicePoint extends BaseData {
this.isStop = false,
this.isStation = false,
this.bracketStation,
this.stationSpeedData,
}) : super(type: Datatype.servicePoint);

final LocalizedString name;
final bool mandatoryStop;
final bool isStop;
final bool isStation;
final BracketStation? bracketStation;
final StationSpeedData? stationSpeedData;

@override
String toString() {
return 'ServicePoint(order: $order, kilometre: $kilometre, name: $name, mandatoryStop: $mandatoryStop, isStop: $isStop, isStation: $isStation, bracketStation: $bracketStation, speedData: $speedData)';
return 'ServicePoint(order: $order, kilometre: $kilometre, name: $name, mandatoryStop: $mandatoryStop, isStop: $isStop, isStation: $isStation, bracketStation: $bracketStation, speedData: $speedData, stationSpeedData: $stationSpeedData)';
}
}
35 changes: 35 additions & 0 deletions das_client/lib/model/journey/speed.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class Speed {
const Speed({
required this.speed,
this.isSquared = false,
this.isCircled = false,
});

factory Speed.from(String value) {
if (RegExp(speedRegex).hasMatch(value)) {
final digit = RegExp(r'\d+').stringMatch(value);
return Speed(
speed: int.parse(digit!),
isCircled: value.contains('{'),
isSquared: value.contains('['),
);
}

throw ArgumentError('Invalid speed format: $value');
}

static const speedRegex = r'^\d+$|^\[\d+\]$|^\{\d+\}$'; // exp. 50, {60} or [70]

final int speed;

/// Normally means that speed is higher than given by signaling. Can have other meanings so this variable is named generically.
final bool isSquared;

/// Normally means that speed is lower than given by signaling. Can have other meanings so this variable is named generically.
final bool isCircled;

@override
String toString() {
return 'Speed(speed: $speed, isSquared: $isSquared, isCircled: $isCircled)';
}
}
15 changes: 15 additions & 0 deletions das_client/lib/model/journey/station_speed_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:collection/collection.dart';
import 'package:das_client/model/journey/graduated_station_speeds.dart';
import 'package:das_client/model/journey/train_series.dart';

class StationSpeedData {
StationSpeedData({this.graduatedStationSpeeds = const []});

final List<GraduatedStationSpeeds> graduatedStationSpeeds;

GraduatedStationSpeeds? graduatedSpeedsFor(TrainSeries? trainSeries) {
if (trainSeries == null) return null;

return graduatedStationSpeeds.firstWhereOrNull((it) => it.trainSeries.contains(trainSeries));
}
}
52 changes: 42 additions & 10 deletions das_client/lib/sfera/src/mapper/sfera_model_mapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ import 'package:das_client/model/journey/cab_signaling.dart';
import 'package:das_client/model/journey/connection_track.dart';
import 'package:das_client/model/journey/curve_point.dart';
import 'package:das_client/model/journey/datatype.dart';
import 'package:das_client/model/journey/graduated_station_speeds.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/speed_change.dart';
import 'package:das_client/model/journey/speed_data.dart';
import 'package:das_client/model/journey/station_speed_data.dart';
import 'package:das_client/model/journey/track_equipment.dart';
import 'package:das_client/model/journey/train_series.dart';
import 'package:das_client/model/journey/velocity.dart';
import 'package:das_client/model/localized_string.dart';
import 'package:das_client/sfera/src/mapper/track_equipment_mapper.dart';
Expand All @@ -24,6 +27,7 @@ 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/graduated_speed_info.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';
Expand Down Expand Up @@ -96,14 +100,17 @@ 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] ?? [],
speedData: _speedDataFromSpeeds(tafTapLocation.stationSpeed?.xmlStationSpeed.element)));
name: _localizedStringFromMultilingualText(tafTapLocation.locationNames),
order: calculateOrder(segmentIndex, timingPoint.location),
mandatoryStop: tpConstraint.stoppingPointInformation?.stopType?.mandatoryStop ?? true,
isStop: tpConstraint.stopSkipPass == StopSkipPass.stoppingPoint,
isStation: tafTapLocation.locationType != TafTapLocationType.stoppingLocation,
bracketStation: _parseBracketStation(tafTapLocations, tafTapLocation),
kilometre: kilometreMap[timingPoint.location] ?? [],
speedData: _speedDataFromSpeeds(tafTapLocation.stationSpeed?.xmlStationSpeed.element),
stationSpeedData:
_stationSpeedDataFromGraduatedSpeedInfo(tafTapLocation.stationSpeed?.xmlGraduatedSpeedInfo?.element),
));
}

_parseAndAddProtectionSections(journeyData, segmentIndex, segmentProfile, kilometreMap);
Expand Down Expand Up @@ -381,11 +388,36 @@ class SferaModelMapper {
.toList());
}

static StationSpeedData? _stationSpeedDataFromGraduatedSpeedInfo(GraduatedSpeedInfo? graduatedSpeedInfo) {
if (graduatedSpeedInfo == null) return null;

final graduatedStationSpeeds = <GraduatedStationSpeeds>[];
for (final entity in graduatedSpeedInfo.entities) {
if (entity.adSpeed != null) {
final speeds = GraduatedStationSpeeds.from([TrainSeries.A, TrainSeries.D], entity.adSpeed!, text: entity.text);
graduatedStationSpeeds.add(speeds);
}
if (entity.nSpeed != null) {
final speeds = GraduatedStationSpeeds.from([TrainSeries.N], entity.nSpeed!, text: entity.text);
graduatedStationSpeeds.add(speeds);
}
if (entity.sSpeed != null) {
final speeds = GraduatedStationSpeeds.from([TrainSeries.S], entity.sSpeed!, text: entity.text);
graduatedStationSpeeds.add(speeds);
}
if (entity.roSpeed != null) {
final speeds = GraduatedStationSpeeds.from([TrainSeries.R, TrainSeries.O], entity.roSpeed!, text: entity.text);
graduatedStationSpeeds.add(speeds);
}
}

return StationSpeedData(graduatedStationSpeeds: graduatedStationSpeeds);
}

static Set<BreakSeries> _parseAvailableBreakSeries(List<BaseData> journeyData) {
return journeyData
.where((it) => it.speedData != null)
.map((it) => it.speedData!.velocities)
.expand((it) => it)
.expand((it) => it.speedData!.velocities)
.where((it) => it.breakSeries != null)
.map((it) => BreakSeries(trainSeries: it.trainSeries, breakSeries: it.breakSeries!))
.toSet();
Expand Down
10 changes: 10 additions & 0 deletions das_client/lib/sfera/src/model/graduated_speed_info.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:das_client/sfera/src/model/graduated_speed_info_entity.dart';
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';

class GraduatedSpeedInfo extends SferaXmlElement {
static const String elementType = 'entries';

GraduatedSpeedInfo({super.type = elementType, super.attributes, super.children, super.value});

Iterable<GraduatedSpeedInfoEntity> get entities => children.whereType<GraduatedSpeedInfoEntity>();
}
17 changes: 17 additions & 0 deletions das_client/lib/sfera/src/model/graduated_speed_info_entity.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';

class GraduatedSpeedInfoEntity extends SferaXmlElement {
static const String elementType = 'entry';

GraduatedSpeedInfoEntity({super.type = elementType, super.attributes, super.children, super.value});

String? get nSpeed => attributes['nSpeed'];

String? get roSpeed => attributes['roSpeed'];

String? get adSpeed => attributes['adSpeed'];

String? get sSpeed => attributes['sSpeed'];

String? get text => attributes['text'];
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:das_client/sfera/src/model/sfera_xml_element.dart';
import 'package:das_client/sfera/src/model/track_equipment_type_wrapper.dart';
import 'package:das_client/sfera/src/model/xml_curve_speed.dart';
import 'package:das_client/sfera/src/model/xml_graduated_speed_info.dart';
import 'package:das_client/sfera/src/model/xml_new_line_speed.dart';
import 'package:das_client/sfera/src/model/xml_station_speed.dart';

Expand All @@ -19,6 +20,8 @@ class NetworkSpecificParameter extends SferaXmlElement {
return XmlCurveSpeed(attributes: attributes, children: children, value: value);
} else if (attributes?['name'] == XmlStationSpeed.elementName) {
return XmlStationSpeed(attributes: attributes, children: children, value: value);
} else if (attributes?['name'] == XmlGraduatedSpeedInfo.elementName) {
return XmlGraduatedSpeedInfo(attributes: attributes, children: children, value: value);
}
return NetworkSpecificParameter(attributes: attributes, children: children, value: value);
}
Expand Down
3 changes: 3 additions & 0 deletions das_client/lib/sfera/src/model/station_speed_nsp.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:das_client/sfera/src/model/taf_tap_location_nsp.dart';
import 'package:das_client/sfera/src/model/xml_graduated_speed_info.dart';
import 'package:das_client/sfera/src/model/xml_station_speed.dart';

class StationSpeedNsp extends TafTapLocationNsp {
Expand All @@ -8,6 +9,8 @@ class StationSpeedNsp extends TafTapLocationNsp {

XmlStationSpeed get xmlStationSpeed => children.whereType<XmlStationSpeed>().first;

XmlGraduatedSpeedInfo? get xmlGraduatedSpeedInfo => children.whereType<XmlGraduatedSpeedInfo>().firstOrNull;

@override
bool validate() {
return validateHasChildOfType<XmlStationSpeed>() && super.validate();
Expand Down
9 changes: 9 additions & 0 deletions das_client/lib/sfera/src/model/xml_graduated_speed_info.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:das_client/sfera/src/model/graduated_speed_info.dart';
import 'package:das_client/sfera/src/model/network_specific_parameter.dart';
import 'package:das_client/sfera/src/model/nsp_xml_element.dart';

class XmlGraduatedSpeedInfo extends NetworkSpecificParameter with NspXmlElement<GraduatedSpeedInfo> {
static const String elementName = 'xmlGraduatedSpeedInfo';

XmlGraduatedSpeedInfo({super.attributes, super.children, super.value});
}
6 changes: 6 additions & 0 deletions das_client/lib/sfera/src/sfera_reply_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'package:das_client/sfera/src/model/current_limitation_start.dart';
import 'package:das_client/sfera/src/model/curve_speed.dart';
import 'package:das_client/sfera/src/model/das_operating_modes_selected.dart';
import 'package:das_client/sfera/src/model/g2b_reply_payload.dart';
import 'package:das_client/sfera/src/model/graduated_speed_info.dart';
import 'package:das_client/sfera/src/model/graduated_speed_info_entity.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';
Expand Down Expand Up @@ -200,6 +202,10 @@ class SferaReplyParser {
return TrainCharacteristics(type: type, attributes: attributes, children: children, value: value);
case TcFeatures.elementType:
return TcFeatures(type: type, attributes: attributes, children: children, value: value);
case GraduatedSpeedInfoEntity.elementType:
return GraduatedSpeedInfoEntity(type: type, attributes: attributes, children: children, value: value);
case GraduatedSpeedInfo.elementType:
return GraduatedSpeedInfo(type: type, attributes: attributes, children: children, value: value);
default:
return SferaXmlElement(type: type, attributes: attributes, children: children, value: value);
}
Expand Down
Loading

0 comments on commit 62fe845

Please sign in to comment.