mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-06-21 21:46:27 +02:00
Version 3.0.312.
This commit is contained in:
parent
310ea37085
commit
fd0e63cf10
94 changed files with 3189 additions and 6352 deletions
|
@ -30,7 +30,7 @@ namespace TINK.Model.Bike.BC
|
|||
string description = null,
|
||||
string stationId = null,
|
||||
Uri operatorUri = null,
|
||||
TariffDescription tariffDescription = null)
|
||||
RentalDescription tariffDescription = null)
|
||||
{
|
||||
Bike = new Bike(id, lockModel, wheelType, typeOfBike, description);
|
||||
|
||||
|
@ -69,7 +69,7 @@ namespace TINK.Model.Bike.BC
|
|||
LockModel lockModel,
|
||||
string stationId,
|
||||
Uri operatorUri = null,
|
||||
TariffDescription tariffDescription = null,
|
||||
RentalDescription tariffDescription = null,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
|
@ -111,7 +111,7 @@ namespace TINK.Model.Bike.BC
|
|||
string description,
|
||||
string stationId,
|
||||
Uri operatorUri,
|
||||
TariffDescription tariffDescription,
|
||||
RentalDescription tariffDescription,
|
||||
DateTime requestedAt,
|
||||
string mailAddress,
|
||||
string code,
|
||||
|
@ -157,7 +157,7 @@ namespace TINK.Model.Bike.BC
|
|||
string description,
|
||||
string currentStationId,
|
||||
Uri operatorUri,
|
||||
TariffDescription tariffDescription,
|
||||
RentalDescription tariffDescription,
|
||||
DateTime bookedAt,
|
||||
string mailAddress,
|
||||
string code) : this(
|
||||
|
@ -190,7 +190,7 @@ namespace TINK.Model.Bike.BC
|
|||
public string StationId { get; }
|
||||
|
||||
/// <summary> Holds description about the tarif. </summary>
|
||||
public TariffDescription TariffDescription { get; }
|
||||
public RentalDescription TariffDescription { get; }
|
||||
|
||||
|
||||
/// Holds the rent state of the bike.
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace TINK.Model.Bike.BC
|
|||
string stationId = null,
|
||||
string stationName = null,
|
||||
Uri operatorUri = null,
|
||||
TariffDescription tariffDescription = null,
|
||||
RentalDescription tariffDescription = null,
|
||||
Func<DateTime> dateTimeProvider = null,
|
||||
IStateInfo stateInfo = null)
|
||||
{
|
||||
|
@ -82,7 +82,7 @@ namespace TINK.Model.Bike.BC
|
|||
|
||||
/// <summary> Holds description about the tarif. </summary>
|
||||
[DataMember]
|
||||
public TariffDescription TariffDescription { get; private set; }
|
||||
public RentalDescription TariffDescription { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the rent state of the bike.
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace TINK.Model.Bike.BC
|
|||
Uri OperatorUri { get; }
|
||||
|
||||
/// <summary> Holds description about the tarif. </summary>
|
||||
TariffDescription TariffDescription { get; }
|
||||
RentalDescription TariffDescription { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the rent state of the bike.
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace TINK.Model.Bike.BluetoothLock
|
|||
Guid lockGuid,
|
||||
string currentStationId,
|
||||
Uri operatorUri = null,
|
||||
TariffDescription tariffDescription = null,
|
||||
RentalDescription tariffDescription = null,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
|
@ -69,7 +69,7 @@ namespace TINK.Model.Bike.BluetoothLock
|
|||
string mailAddress,
|
||||
string currentStationId,
|
||||
Uri operatorUri,
|
||||
TariffDescription tariffDescription,
|
||||
RentalDescription tariffDescription,
|
||||
Func<DateTime> dateTimeProvider,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
|
@ -118,7 +118,7 @@ namespace TINK.Model.Bike.BluetoothLock
|
|||
string mailAddress,
|
||||
string currentStationId,
|
||||
Uri operatorUri,
|
||||
TariffDescription tariffDescription = null,
|
||||
RentalDescription tariffDescription = null,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace TINK.Model.Bike.CopriLock
|
|||
string currentStationId,
|
||||
LockInfo lockInfo,
|
||||
Uri operatorUri = null,
|
||||
TariffDescription tariffDescription = null,
|
||||
RentalDescription tariffDescription = null,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
|
@ -62,7 +62,7 @@ namespace TINK.Model.Bike.CopriLock
|
|||
string currentStationId,
|
||||
LockInfo lockInfo,
|
||||
Uri operatorUri,
|
||||
TariffDescription tariffDescription,
|
||||
RentalDescription tariffDescription,
|
||||
Func<DateTime> dateTimeProvider,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
|
@ -105,7 +105,7 @@ namespace TINK.Model.Bike.CopriLock
|
|||
string currentStationId,
|
||||
LockInfo lockInfo,
|
||||
Uri operatorUri,
|
||||
TariffDescription tariffDescription = null,
|
||||
RentalDescription tariffDescription = null,
|
||||
bool? isDemo = DEFAULTVALUEISDEMO,
|
||||
IEnumerable<string> group = null,
|
||||
WheelType? wheelType = null,
|
||||
|
|
47
TINKLib/Model/Bikes/Bike/RentalDescription.cs
Normal file
47
TINKLib/Model/Bikes/Bike/RentalDescription.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.Model.Bikes.Bike
|
||||
{
|
||||
/// <summary>
|
||||
/// Successor of TarifDescription- object.
|
||||
/// Manages tariff- and rental info.
|
||||
/// </summary>
|
||||
public class RentalDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// The different elements of a tariff (example: "Max Gebühr", )
|
||||
/// </summary>
|
||||
public class TariffElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the tariff element. To be displayed to user (example of elements: "Gratis Mietzeit", "Mietgebühr", "Max Gebühr").
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Holds the tariff element value. To be displayed to user (example: "9.00 € / Tag").
|
||||
/// </summary>
|
||||
public string Value { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class InfoElement
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the tariff.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Number of the tariff.
|
||||
/// </summary>
|
||||
public int? Id { get; set; }
|
||||
|
||||
public Dictionary<string, TariffElement> TariffEntries { get; set; } = new Dictionary<string, TariffElement>();
|
||||
|
||||
public Dictionary<string, InfoElement> InfoEntries { get; set; } = new Dictionary<string, InfoElement>();
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ using Xamarin.Forms;
|
|||
using System.Linq;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using TINK.Services.CopriApi;
|
||||
using TINK.MultilingualResources;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -347,7 +348,7 @@ namespace TINK.Model.Connector
|
|||
new Bikes.Bike.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState()}.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription) null),
|
||||
#endif
|
||||
|
@ -365,7 +366,7 @@ namespace TINK.Model.Connector
|
|||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
|
@ -438,9 +439,9 @@ namespace TINK.Model.Connector
|
|||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
|
@ -458,7 +459,7 @@ namespace TINK.Model.Connector
|
|||
new Bikes.Bike.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
|
@ -497,11 +498,11 @@ namespace TINK.Model.Connector
|
|||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
|
@ -519,11 +520,11 @@ namespace TINK.Model.Connector
|
|||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode);
|
||||
default:
|
||||
|
@ -535,9 +536,9 @@ namespace TINK.Model.Connector
|
|||
new Bikes.Bike.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
|
@ -559,28 +560,130 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
}
|
||||
|
||||
public static Bikes.Bike.TariffDescription Create(this TariffDescription tariffDesciption)
|
||||
/// <summary>
|
||||
/// Creates rental description object from JSON- tarif description object.
|
||||
/// </summary>
|
||||
/// <param name="tariffDesciption">Source JSON object.</param>
|
||||
/// <returns>Tariff description object.</returns>
|
||||
public static Bikes.Bike.RentalDescription Create(this TariffDescription tariffDesciption)
|
||||
{
|
||||
return new Bikes.Bike.TariffDescription
|
||||
var bike = new Bikes.Bike.RentalDescription
|
||||
{
|
||||
Name = tariffDesciption?.name,
|
||||
#if USCSHARP9
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : null,
|
||||
#else
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : (int?)null,
|
||||
Id = int.TryParse(tariffDesciption?.number, out int number) ? number : (int?)null,
|
||||
#endif
|
||||
FreeTimePerSession = double.TryParse(tariffDesciption?.free_hours, NumberStyles.Any, CultureInfo.InvariantCulture, out double freeHours) ? TimeSpan.FromHours(freeHours) : TimeSpan.Zero,
|
||||
FeeEuroPerHour = double.TryParse(tariffDesciption?.eur_per_hour, NumberStyles.Any, CultureInfo.InvariantCulture, out double euroPerHour) ? euroPerHour : double.NaN,
|
||||
AboEuroPerMonth = double.TryParse(tariffDesciption?.abo_eur_per_month, NumberStyles.Any, CultureInfo.InvariantCulture, out double aboEuroPerMonth) ? aboEuroPerMonth : double.NaN,
|
||||
MaxFeeEuroPerDay = double.TryParse(tariffDesciption?.max_eur_per_day, NumberStyles.Any, CultureInfo.InvariantCulture, out double maxEuroPerDay) ? maxEuroPerDay : double.NaN,
|
||||
OperatorAgb = tariffDesciption?.operator_agb,
|
||||
TrackingInfo = tariffDesciption?.track_info
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(tariffDesciption?.free_hours)
|
||||
&& double.TryParse(tariffDesciption?.free_hours, NumberStyles.Any, CultureInfo.InvariantCulture, out double freeHours))
|
||||
{
|
||||
// Free time. Unit hours,format floating point number.
|
||||
bike.TariffEntries.Add("1", new Bikes.Bike.RentalDescription.TariffElement {
|
||||
Description = AppResources.MessageBikesManagementTariffDescriptionFreeTimePerSession,
|
||||
Value =string.Format("{0} {1}", freeHours.ToString("0.00"), AppResources.MessageBikesManagementTariffDescriptionHour)
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tariffDesciption?.eur_per_hour)
|
||||
&& double.TryParse(tariffDesciption?.eur_per_hour, NumberStyles.Any, CultureInfo.InvariantCulture, out double euroPerHour))
|
||||
{
|
||||
// Euro per hour. Format floating point.
|
||||
bike.TariffEntries.Add("2", new Bikes.Bike.RentalDescription.TariffElement {
|
||||
Description = AppResources.MessageBikesManagementTariffDescriptionFeeEuroPerHour,
|
||||
Value = string.Format("{0} {1}", euroPerHour.ToString("0.00"), AppResources.MessageBikesManagementTariffDescriptionEuroPerHour)
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tariffDesciption?.max_eur_per_day)
|
||||
&& double.TryParse(tariffDesciption.max_eur_per_day, NumberStyles.Any, CultureInfo.InvariantCulture, out double maxEuroPerDay))
|
||||
{
|
||||
// Max euro per day. Format floating point.
|
||||
bike.TariffEntries.Add("3", new Bikes.Bike.RentalDescription.TariffElement {
|
||||
Description = AppResources.MessageBikesManagementTariffDescriptionMaxFeeEuroPerDay,
|
||||
Value = string.Format("{0} {1}", maxEuroPerDay.ToString("0.00"), AppResources.MessageBikesManagementMaxFeeEuroPerDay)
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tariffDesciption?.abo_eur_per_month)
|
||||
&& double.TryParse(tariffDesciption.abo_eur_per_month, NumberStyles.Any, CultureInfo.InvariantCulture, out double aboEuroPerMonth))
|
||||
{
|
||||
// Abo per month
|
||||
bike.TariffEntries.Add("4", new Bikes.Bike.RentalDescription.TariffElement {
|
||||
Description = AppResources.MessageBikesManagementTariffDescriptionAboEuroPerMonth,
|
||||
Value = string.Format("{0} {1}", aboEuroPerMonth.ToString("0.00"), AppResources.MessageBikesManagementTariffDescriptionEuroPerMonth)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(tariffDesciption?.operator_agb ?? string.Empty))
|
||||
{
|
||||
bike.InfoEntries.Add("1", new Bikes.Bike.RentalDescription.InfoElement { Key = "AGB", Value = tariffDesciption.operator_agb });
|
||||
}
|
||||
|
||||
return bike;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates rental description object from JSON- tarif description object.
|
||||
/// </summary>
|
||||
/// <param name="rentalDesciption">Source JSON object.</param>
|
||||
/// <returns>Tariff description object.</returns>
|
||||
|
||||
public static Bikes.Bike.RentalDescription Create(this RentalDescription rentalDesciption)
|
||||
{
|
||||
Bikes.Bike.RentalDescription.TariffElement CreateTarifEntry(string[] elementValue)
|
||||
{
|
||||
return new Bikes.Bike.RentalDescription.TariffElement
|
||||
{
|
||||
Description = elementValue != null && elementValue.Length > 0 ? elementValue[0] : string.Empty,
|
||||
Value = elementValue != null && elementValue.Length > 1 ? elementValue[1] : string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
Bikes.Bike.RentalDescription.InfoElement CreateInfoElement(string[] elementValue)
|
||||
{
|
||||
return new Bikes.Bike.RentalDescription.InfoElement
|
||||
{
|
||||
Key = elementValue != null && elementValue.Length > 0 ? elementValue[0] : string.Empty,
|
||||
Value = elementValue != null && elementValue.Length > 1 ? elementValue[1] : string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
// Read tariff elements.
|
||||
var tarifEntries = rentalDesciption?.tarif_elements != null
|
||||
? rentalDesciption.tarif_elements.Select(x => new
|
||||
{
|
||||
Key = x.Key,
|
||||
Value = CreateTarifEntry(x.Value)
|
||||
}).ToLookup(x => x.Key, x => x.Value).ToDictionary(x => x.Key, x => x.First())
|
||||
: new Dictionary<string, Bikes.Bike.RentalDescription.TariffElement>();
|
||||
|
||||
// Read info elements.
|
||||
var InfoEntries = rentalDesciption?.rental_info != null
|
||||
? rentalDesciption.rental_info.Select(x => new
|
||||
{
|
||||
Key = x.Key,
|
||||
Value = CreateInfoElement(x.Value)
|
||||
}).ToLookup(x => x.Key, x => x.Value).ToDictionary(x => x.Key, x => x.First())
|
||||
: new Dictionary<string, Bikes.Bike.RentalDescription.InfoElement>();
|
||||
|
||||
var bike = new Bikes.Bike.RentalDescription
|
||||
{
|
||||
Name = rentalDesciption?.name ?? string.Empty,
|
||||
Id = int.TryParse(rentalDesciption?.id ?? string.Empty, out int number) ? number : (int?)null,
|
||||
TariffEntries = tarifEntries,
|
||||
InfoEntries = InfoEntries
|
||||
};
|
||||
|
||||
return bike;
|
||||
}
|
||||
|
||||
/// <summary> Creates a booking finished object from response.</summary>
|
||||
/// <param name="response">Response to create survey object from.</param>
|
||||
public static BookingFinishedModel Create(this DoReturnResponse response)
|
||||
public static BookingFinishedModel Create(this DoReturnResponse response)
|
||||
{
|
||||
var bookingFinished = new BookingFinishedModel
|
||||
{
|
||||
|
|
|
@ -526,7 +526,19 @@ namespace TINK.Model
|
|||
{
|
||||
new Version(3, 0, 299),
|
||||
AppResources.ChangeLog3_0_299
|
||||
}
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 300),
|
||||
AppResources.ChangeLog3_0_231 // Minor improvements.
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 311),
|
||||
AppResources.ChangeLog3_0_301
|
||||
},
|
||||
{
|
||||
new Version(3, 0, 312),
|
||||
AppResources.ChangeLog3_0_312
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary> Manges the whats new information.</summary>
|
||||
|
|
|
@ -151,7 +151,7 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open lock & continue renting.
|
||||
/// Looks up a localized string similar to Open lock.
|
||||
/// </summary>
|
||||
public static string ActionOpenAndPause {
|
||||
get {
|
||||
|
@ -1063,6 +1063,28 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Extended tariffs supported..
|
||||
/// </summary>
|
||||
public static string ChangeLog3_0_301 {
|
||||
get {
|
||||
return ResourceManager.GetString("ChangeLog3_0_301", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to For Lastenrad Bayern users
|
||||
///- missing titles added to several pages
|
||||
///- new station markers availabe
|
||||
///- layout of Wort-Bild-Marke amended
|
||||
///.
|
||||
/// </summary>
|
||||
public static string ChangeLog3_0_312 {
|
||||
get {
|
||||
return ResourceManager.GetString("ChangeLog3_0_312", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Lock of rented bike cannot be be connected right now..
|
||||
/// </summary>
|
||||
|
@ -1587,7 +1609,7 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please open a bike station page to to contact the bike sharing operator..
|
||||
/// Looks up a localized string similar to Contact operator?.
|
||||
/// </summary>
|
||||
public static string MarkingContactNoStationInfoAvailableNoButton {
|
||||
get {
|
||||
|
@ -1993,6 +2015,15 @@ namespace TINK.MultilingualResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to €/month.
|
||||
/// </summary>
|
||||
public static string MessageBikesManagementTariffDescriptionEuroPerMonth {
|
||||
get {
|
||||
return ResourceManager.GetString("MessageBikesManagementTariffDescriptionEuroPerMonth", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Rental fees.
|
||||
/// </summary>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<value>Schloss öffnen & Rad mieten</value>
|
||||
</data>
|
||||
<data name="ActionOpenAndPause" xml:space="preserve">
|
||||
<value>Schloss öffnen & Miete fortsetzen</value>
|
||||
<value>Schloss öffnen</value>
|
||||
</data>
|
||||
<data name="ActionRequest" xml:space="preserve">
|
||||
<value>Rad reservieren</value>
|
||||
|
@ -612,7 +612,7 @@ Layout Anzeige Radnamen und nummern verbessert.</value>
|
|||
<value>Bitte Anmelden um Fahrräder zu reservieren! <font color="blue"><u>Hier</u></font> tippen, um auf Anmeldeseite zu wechseln.</value>
|
||||
</data>
|
||||
<data name="MarkingContactNoStationInfoAvailableNoButton" xml:space="preserve">
|
||||
<value>Bitte eine Fahrradstationsseite öffen, um den Betreiber zu kontaktieren.</value>
|
||||
<value>Betreiber kontaktieren?</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_241" xml:space="preserve">
|
||||
<value>Auf der Kontaktseite werden Kontaktinformationen betreiberspezifisch angezeigt.</value>
|
||||
|
@ -901,6 +901,16 @@ Kleinere Fehlerbehebungen.
|
|||
<value>Neue Wort-Bild-Marke in das Menü der Lastenrad Bayern App integriert.</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_299" xml:space="preserve">
|
||||
<value>Fehlerbehebung: Bluetooth-Schlossanstereuerung funktioniert wieder unter Android 12.</value>
|
||||
<value>Fehlerbehebung: Bluetooth-Schlossansteuerung funktioniert wieder unter Android 12.</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_301" xml:space="preserve">
|
||||
<value>Unterstützung erweiterte Tarife.</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_312" xml:space="preserve">
|
||||
<value>Für Lastenrad Bayern
|
||||
- fehlende Titeltexte werden angezeigt
|
||||
- neue Stationsmarker verfügbar
|
||||
- layout der Wort-Bild-Marke verbessert
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
|
@ -136,7 +136,7 @@
|
|||
<value>Open lock & rent bike</value>
|
||||
</data>
|
||||
<data name="ActionOpenAndPause" xml:space="preserve">
|
||||
<value>Open lock & continue renting</value>
|
||||
<value>Open lock</value>
|
||||
</data>
|
||||
<data name="ActionRequest" xml:space="preserve">
|
||||
<value>Reserve bike</value>
|
||||
|
@ -708,7 +708,7 @@ Layout of bike names and id display improved.</value>
|
|||
<value>Find bike by id functionality added.</value>
|
||||
</data>
|
||||
<data name="MarkingContactNoStationInfoAvailableNoButton" xml:space="preserve">
|
||||
<value>Please open a bike station page to to contact the bike sharing operator.</value>
|
||||
<value>Contact operator?</value>
|
||||
</data>
|
||||
<data name="MarkingContactSupport" xml:space="preserve">
|
||||
<value><font color="blue"><u>Contact</u></font> {0}.</value>
|
||||
|
@ -995,4 +995,17 @@ Minor bugfixes.</value>
|
|||
<data name="ChangeLog3_0_299" xml:space="preserve">
|
||||
<value>Bugfix: Bluetooth lock control works again on Android 12.</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_301" xml:space="preserve">
|
||||
<value>Extended tariffs supported.</value>
|
||||
</data>
|
||||
<data name="MessageBikesManagementTariffDescriptionEuroPerMonth" xml:space="preserve">
|
||||
<value>€/month</value>
|
||||
</data>
|
||||
<data name="ChangeLog3_0_312" xml:space="preserve">
|
||||
<value>For Lastenrad Bayern users
|
||||
- missing titles added to several pages
|
||||
- new station markers availabe
|
||||
- layout of Wort-Bild-Marke amended
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
|
@ -31,8 +31,8 @@
|
|||
<target state="final">Schloss öffnen & Rad mieten</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionOpenAndPause" translate="yes" xml:space="preserve">
|
||||
<source>Open lock & continue renting</source>
|
||||
<target state="final">Schloss öffnen & Miete fortsetzen</target>
|
||||
<source>Open lock</source>
|
||||
<target state="final">Schloss öffnen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ActionRequest" translate="yes" xml:space="preserve">
|
||||
<source>Reserve bike</source>
|
||||
|
@ -818,8 +818,8 @@ Layout Anzeige Radnamen und nummern verbessert.</target>
|
|||
<target state="translated">Bitte Anmelden um Fahrräder zu reservieren! <bpt id="1"><font color="blue"></bpt><bpt id="2"><u></bpt>Hier<ept id="2"></u></ept><ept id="1"></font></ept> tippen, um auf Anmeldeseite zu wechseln.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="MarkingContactNoStationInfoAvailableNoButton" translate="yes" xml:space="preserve">
|
||||
<source>Please open a bike station page to to contact the bike sharing operator.</source>
|
||||
<target state="translated">Bitte eine Fahrradstationsseite öffen, um den Betreiber zu kontaktieren.</target>
|
||||
<source>Contact operator?</source>
|
||||
<target state="translated">Betreiber kontaktieren?</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ChangeLog3_0_241" translate="yes" xml:space="preserve">
|
||||
<source>Bike sharing system operator specific support info displayed on contact page.</source>
|
||||
|
@ -1227,7 +1227,27 @@ Kleinere Fehlerbehebungen.
|
|||
</trans-unit>
|
||||
<trans-unit id="ChangeLog3_0_299" translate="yes" xml:space="preserve">
|
||||
<source>Bugfix: Bluetooth lock control works again on Android 12.</source>
|
||||
<target state="translated">Fehlerbehebung: Bluetooth-Schlossanstereuerung funktioniert wieder unter Android 12.</target>
|
||||
<target state="translated">Fehlerbehebung: Bluetooth-Schlossansteuerung funktioniert wieder unter Android 12.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ChangeLog3_0_301" translate="yes" xml:space="preserve">
|
||||
<source>Extended tariffs supported.</source>
|
||||
<target state="translated">Unterstützung erweiterte Tarife.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="MessageBikesManagementTariffDescriptionEuroPerMonth" translate="yes" xml:space="preserve">
|
||||
<source>€/month</source>
|
||||
<target state="new">€/month</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ChangeLog3_0_312" translate="yes" xml:space="preserve">
|
||||
<source>For Lastenrad Bayern users
|
||||
- missing titles added to several pages
|
||||
- new station markers availabe
|
||||
- layout of Wort-Bild-Marke amended
|
||||
</source>
|
||||
<target state="translated">Für Lastenrad Bayern
|
||||
- fehlende Titeltexte werden angezeigt
|
||||
- neue Stationsmarker verfügbar
|
||||
- layout der Wort-Bild-Marke verbessert
|
||||
</target>
|
||||
</trans-unit>
|
||||
</group>
|
||||
</body>
|
||||
|
|
|
@ -60,8 +60,13 @@ namespace TINK.Repository.Response
|
|||
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
/// <summary> Holds the tariff information for a bike. </summary>
|
||||
/// <remarks> This member is obsolete. Use <cref="rental_description"> instead.</cref></remarks>
|
||||
[DataMember]
|
||||
public TariffDescription tariff_description { get; private set; }
|
||||
|
||||
/// <summary> Holds the rental information for a bike. </summary>
|
||||
[DataMember]
|
||||
public RentalDescription rental_description { get; private set; }
|
||||
#endif
|
||||
/// <summary> Loading state of motor battery in % ]0..100[. </summary>
|
||||
[DataMember]
|
||||
|
|
39
TINKLib/Repository/Response/RentalDescription.cs
Normal file
39
TINKLib/Repository/Response/RentalDescription.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace TINK.Repository.Response
|
||||
{
|
||||
/// <summary>
|
||||
/// Successor of TarifDescription- object.
|
||||
/// Manages tariff- and rental info.
|
||||
/// </summary>
|
||||
[DataContract]
|
||||
public class RentalDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the tariff.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id of the tariff.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string id { get; private set; }
|
||||
|
||||
/// <summary> Holds tariff entires to show to user.</summary>
|
||||
[DataMember]
|
||||
public Dictionary<
|
||||
string /* Key of tariff object for sorting purposes*/,
|
||||
string[] /* Holds two Elements: first element is the description of the element (example: "Max Gebühr"), second is the value (example: "9.00 € / Tag")*/>
|
||||
tarif_elements { get; private set; }
|
||||
|
||||
/// <summary> Holds tariff entires to show to user.</summary>
|
||||
[DataMember]
|
||||
public Dictionary<
|
||||
string /* Key of info object for sorting purposes*/,
|
||||
string[] /* Holds two Elements: first element is the key of the element (example: "Tracking"), second is the value (example: "Ich stimme der Speicherung (Tracking) meiner Fahrstrecke ....")*/>
|
||||
rental_info { get; private set; }
|
||||
}
|
||||
}
|
|
@ -349,7 +349,6 @@ namespace TINK.ViewModel.Account
|
|||
|
||||
|
||||
m_oViewUpdateManager = new PollingUpdateTaskManager(
|
||||
() => GetType().Name,
|
||||
() =>
|
||||
{
|
||||
TinkApp.PostAction(
|
||||
|
|
|
@ -14,7 +14,6 @@ using TINK.MultilingualResources;
|
|||
using TINK.View;
|
||||
using TINK.Model.User;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User.Account;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
|
|
|
@ -1,684 +0,0 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using Serilog;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using Xamarin.Essentials;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Model.User;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using TINK.Model;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
public class BookedOpen : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public BookedOpen(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
ILocksService lockService,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionCloseAndReturn, // Copri button text: "Schloss schließen & Miete beenden"
|
||||
true, // Show button to allow user to return bike.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
lockService,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionClose; // BT button text "Schließen".
|
||||
IsLockitButtonVisible = true; // Show button to allow user to lock bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Disposable;
|
||||
|
||||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await CloseLockAndReturnBike();
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
|
||||
/// <summary> Close lock and return bike.</summary>
|
||||
public async Task<IRequestHandler> CloseLockAndReturnBike()
|
||||
{
|
||||
// Prevent concurrent interaction
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<Location> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.MessageErrorQueryLocationStartTitle,
|
||||
$"{AppResources.MessageErrorQueryLocationMessage}\r\n{ex.Message}",
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Ask whether to really return bike?
|
||||
var l_oResult = await ViewService.DisplayAlert(
|
||||
string.Empty,
|
||||
string.Format(AppResources.QuestionCloseLockAndReturnBike, SelectedBike.GetFullDisplayName()),
|
||||
AppResources.MessageAnswerYes,
|
||||
AppResources.MessageAnswerNo);
|
||||
|
||||
if (l_oResult == false)
|
||||
{
|
||||
// User aborted closing and returning bike process
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {l_oId} in order to close and return but action was canceled.", SelectedBike.Id);
|
||||
|
||||
// Cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on abort returning opened bike failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.IsIdle = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Unlock bike.
|
||||
Log.ForContext<BookedOpen>().Information("Request to return bike {bike} detected.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Notify COPRI about start reaturning bike
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartReturningBike;
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.StartReturningBike(
|
||||
SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
Task updateLockingStateTask = Task.CompletedTask;
|
||||
try
|
||||
{
|
||||
updateLockingStateTask = ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike);
|
||||
}
|
||||
catch (Exception innerExceptionStartUpdateLockingState)
|
||||
{
|
||||
// No location information available/ updating state failed.
|
||||
Log.ForContext<BookedOpen>().Information("Start update locking state failed on lock operating error. {Exception}", SelectedBike, innerExceptionStartUpdateLockingState);
|
||||
}
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(new List<Task> { currentLocationTask ?? Task.CompletedTask, updateLockingStateTask });
|
||||
}
|
||||
catch (Exception innerExWhenAll)
|
||||
{
|
||||
// No location information available/ updating state failed.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location/ updating lock state failed on closing lock error. {Exception}", SelectedBike, innerExWhenAll);
|
||||
}
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
if (SelectedBike.LockInfo.State != LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error($"Lock can not be closed. Invalid locking state state {SelectedBike.LockInfo.State} detected.");
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
Task updateLockingStateTask = Task.CompletedTask;
|
||||
try
|
||||
{
|
||||
updateLockingStateTask = ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike);
|
||||
}
|
||||
catch (Exception innerExceptionStartUpdateLockingState)
|
||||
{
|
||||
// No location information available/ updating state failed.
|
||||
Log.ForContext<BookedOpen>().Information("Start update locking state failed on unexpected state. {Exception}", SelectedBike, innerExceptionStartUpdateLockingState);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
SelectedBike.LockInfo.State == LockingState.Open
|
||||
? AppResources.ErrorCloseLockStillOpenMessage
|
||||
: string.Format(AppResources.ErrorCloseLockUnexpectedStateMessage, SelectedBike.LockInfo.State),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(new List<Task> { currentLocationTask ?? Task.CompletedTask, updateLockingStateTask});
|
||||
}
|
||||
catch (Exception innerExWhenAll)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location/ updating lock state failed failed on unexpected lock state failed. {Exception}", SelectedBike, innerExWhenAll);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
Location currentLocation = null;
|
||||
try
|
||||
{
|
||||
var task = await Task.WhenAny(new List<Task> { currentLocationTask });
|
||||
currentLocation = currentLocationTask.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Returning bike {Bike} is not possible. Query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.MessageErrorQueryLocationTitle,
|
||||
AppResources.MessageErrorQueryLocationMessage,
|
||||
ex.GetErrorMessage(),
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically(); // Restart polling again.
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true; // Unlock GUI
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReturningBike;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
var feedBackUri = SelectedBike?.OperatorUri;
|
||||
BookingFinishedModel bookingFinished;
|
||||
try
|
||||
{
|
||||
bookingFinished = await ConnectorFactory(IsConnected).Command.DoReturn(
|
||||
SelectedBike,
|
||||
currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null,
|
||||
SmartDevice);
|
||||
// If canceling bike succedes remove bike because it is not ready to be booked again
|
||||
IsRemoveBikeRequired = true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
AppResources.ErrorReturnBikeNoWebTitle,
|
||||
string.Format("{0}\r\n{1}", AppResources.ErrorReturnBikeNoWebMessage, WebConnectFailureException.GetHintToPossibleExceptionsReasons),
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NotAtStationException notAtStationException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeNotAtStationMessage, notAtStationException.StationNr, notAtStationException.Distance),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is NoGPSDataException)
|
||||
{
|
||||
// COPRI returned an error.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an no GPS- data error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
string.Format(AppResources.ErrorReturnBikeLockOpenNoGPSMessage),
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User selected booked bike {bike} but returing failed. COPRI returned an error.", SelectedBike);
|
||||
|
||||
await ViewService.DisplayAdvancedAlert(
|
||||
"Statusfehler beim Zurückgeben des Rads!",
|
||||
copriException.Message,
|
||||
copriException.Response,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("User selected booked bike {bike} but returning failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnBikeTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
Log.ForContext<BookedOpen>().Information("User returned bike {bike} successfully.", SelectedBike);
|
||||
|
||||
// Disconnect lock.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextDisconnectingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = await LockService.DisconnectAsync(SelectedBike.LockInfo.Id, SelectedBike.LockInfo.Guid);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be disconnected. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorDisconnect;
|
||||
}
|
||||
|
||||
#if !USERFEEDBACKDLG_OFF
|
||||
// Do get Feedback
|
||||
var feedback = await ViewService.DisplayUserFeedbackPopup(bookingFinished?.Co2Saving);
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.DoSubmitFeedback(
|
||||
new UserFeedbackDto { BikeId = SelectedBike.Id, IsBikeBroken = feedback.IsBikeBroken, Message = feedback.Message },
|
||||
feedBackUri);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("Submitting feedback for bike {bike} failed. COPRI returned an error.", SelectedBike);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Submitting feedback for bike {bike} failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
}
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorReturnSubmitFeedbackTitle,
|
||||
AppResources.ErrorReturnSubmitFeedbackMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bookingFinished != null && bookingFinished.MiniSurvey.Questions.Count > 0)
|
||||
{
|
||||
await ViewService.PushModalAsync(ViewTypes.MiniSurvey);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
Log.ForContext<BookedOpen>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<Location> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Closing lock of bike {Bike} is not possible. Starting query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
|
||||
}
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geoposition.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
Location currentLocation = null;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask });
|
||||
currentLocation = currentLocationTask.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedOpen>().Information("Getting geolocation when closing lock of bike {Bike} failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike,
|
||||
currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedOpen>().Information("User locked bike {bike} in order to pause ride but updating failed. Message: {Message} Details: {Details}", SelectedBike, copriException.Message, copriException.Response);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedOpen>().Error("User locked bike {bike} in order to pause ride but updating failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<BookedOpen>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,386 +0,0 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Model.User;
|
||||
using Xamarin.Essentials;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
public class BookedUnknown : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...).</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public BookedUnknown(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
ILocksService lockService,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndPause, // Schloss öffnen und Miete fortsetzen.
|
||||
true, // Show button to enabled returning of bike.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
lockService,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionClose; // BT button text "Schließen".;
|
||||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Booked;
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await OpenLock();
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<BookedUnknown>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldWasBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillOpenTitle,
|
||||
AppResources.ErrorOpenLockBoldWasBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// When bold is blocked lock is still closed even if exception occurres.
|
||||
// In all other cases state is supposed to be unknown. Example: Lock is out of reach and no more bluetooth connected.
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Akkustate can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("Akkustate can not be read. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<Location> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
|
||||
}
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Canceling query location failed on unexpected lock state failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Canceling query location failed on unexpected lock state failed. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geoposition.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
Location currentLocation = null;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
currentLocation = currentLocationTask?.Result ?? null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<BookedUnknown>().Information("Get geolocation failed when closing lock of bike {Bike} with unknown state. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike,
|
||||
currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<BookedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed. Message: {Message} Details: {Details}", SelectedBike, copriException.Message, copriException.Response);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<BookedUnknown>().Error("User locked bike {bike} in order to pause ride but updating failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<BookedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,386 +0,0 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.Connector;
|
||||
using TINK.Model.State;
|
||||
using TINK.View;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Services.Geolocation;
|
||||
using TINK.Services.BluetoothLock;
|
||||
using TINK.Services.BluetoothLock.Exception;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Model.Bikes.Bike.BluetoothLock;
|
||||
using TINK.Model.User;
|
||||
using Xamarin.Essentials;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.Device;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
||||
{
|
||||
public class ReservedUnknown : Base, IRequestHandler
|
||||
{
|
||||
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...)</param>
|
||||
/// <param name="bikesViewModel">View model to be used for progress report and unlocking/ locking view.</param>
|
||||
public ReservedUnknown(
|
||||
IBikeInfoMutable selectedBike,
|
||||
Func<bool> isConnectedDelegate,
|
||||
Func<bool, IConnector> connectorFactory,
|
||||
IGeolocation geolocation,
|
||||
ILocksService lockService,
|
||||
Func<IPollingUpdateTaskManager> viewUpdateManager,
|
||||
ISmartDevice smartDevice,
|
||||
IViewService viewService,
|
||||
IBikesViewModel bikesViewModel,
|
||||
IUser activeUser) : base(
|
||||
selectedBike,
|
||||
AppResources.ActionOpenAndBook, // BT button text "Schloss öffnen und Rad mieten."
|
||||
false, // Show button to enabled returning of bike.
|
||||
isConnectedDelegate,
|
||||
connectorFactory,
|
||||
geolocation,
|
||||
lockService,
|
||||
viewUpdateManager,
|
||||
smartDevice,
|
||||
viewService,
|
||||
bikesViewModel,
|
||||
activeUser)
|
||||
{
|
||||
LockitButtonText = AppResources.ActionClose; // BT button text "Schließen"
|
||||
IsLockitButtonVisible = true; // Show button to enable opening lock in case user took a pause and does not want to return the bike.
|
||||
}
|
||||
|
||||
/// <summary> Gets the bike state. </summary>
|
||||
public override InUseStateEnum State => InUseStateEnum.Reserved;
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption1() => await OpenLock();
|
||||
|
||||
/// <summary> Open bike and update COPRI lock state. </summary>
|
||||
public async Task<IRequestHandler> OpenLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
Log.ForContext<ReservedUnknown>().Information("User request to unlock bike {bike}.", SelectedBike);
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOpeningLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].OpenAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldIsBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. Bold is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenBoldWasBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. Bold was or is blocked. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockStillOpenTitle,
|
||||
AppResources.ErrorOpenLockBoldWasBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntOpenInconsistentStateExecption inconsistentState
|
||||
&& inconsistentState.State == LockingState.Closed)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be opened. lock reports state closed. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
AppResources.ErrorOpenLockStillClosedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("Lock can not be opened. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorOpenLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
// When bold is blocked lock is still closed even if exception occurres.
|
||||
// In all other cases state is supposed to be unknown. Example: Lock is out of reach and no more bluetooth connected.
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextReadingChargingLevel;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.BatteryPercentage = await LockService[SelectedBike.LockInfo.Id].GetBatteryPercentageAsync();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Akkustate can not be read, bike out of range. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelOutOfReach;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("Akkustate can not be read. {Exception}", exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorReadingChargingLevelGeneral;
|
||||
}
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(SelectedBike);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed. {response}.", SelectedBike, copriException.Response);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("User locked bike {bike} in order to pause ride but updating failed . {@l_oException}", SelectedBike.Id, exception);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> HandleRequestOption2() => await CloseLock();
|
||||
|
||||
|
||||
/// <summary> Close lock in order to pause ride and update COPRI lock state.</summary>
|
||||
public async Task<IRequestHandler> CloseLock()
|
||||
{
|
||||
// Unlock bike.
|
||||
BikesViewModel.IsIdle = false;
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("User request to lock bike {bike} in order to pause ride.", SelectedBike);
|
||||
|
||||
// Start getting geolocation.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationStart;
|
||||
var ctsLocation = new CancellationTokenSource();
|
||||
Task<Location> currentLocationTask = null;
|
||||
var timeStamp = DateTime.Now;
|
||||
try
|
||||
{
|
||||
currentLocationTask = Geolocation.GetAsync(ctsLocation.Token, timeStamp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Returning bike {Bike} is not possible. Start query location failed. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationQuery;
|
||||
}
|
||||
|
||||
// Stop polling before returning bike.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextOneMomentPlease;
|
||||
await ViewUpdateManager().StopUpdatePeridically();
|
||||
|
||||
// Close lock
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextClosingLock;
|
||||
try
|
||||
{
|
||||
SelectedBike.LockInfo.State = (await LockService[SelectedBike.LockInfo.Id].CloseAsync())?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
|
||||
// Signal cts to cancel getting geolocation.
|
||||
ctsLocation.Cancel();
|
||||
|
||||
if (exception is OutOfReachException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockOutOfReachMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CounldntCloseMovingException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockMovingMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else if (exception is CouldntCloseBoldBlockedException)
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
|
||||
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
AppResources.ErrorCloseLockBoldBlockedMessage,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("Lock can not be closed. {Exception}", exception);
|
||||
await ViewService.DisplayAlert(
|
||||
AppResources.ErrorCloseLockTitle,
|
||||
exception.Message,
|
||||
AppResources.MessageAnswerOk);
|
||||
}
|
||||
|
||||
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
|
||||
? stateAwareException.State
|
||||
: LockingState.UnknownDisconnected;
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
// Wait until cancel getting geolocation has completed.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocationCancelWait;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Canceling query location failed on closing lock error. {Exception}", SelectedBike, ex);
|
||||
}
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
|
||||
// Get geoposition.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextQueryLocation;
|
||||
Location currentLocation = null;
|
||||
try
|
||||
{
|
||||
await Task.WhenAny(new List<Task> { currentLocationTask ?? Task.CompletedTask });
|
||||
currentLocation = currentLocationTask?.Result ?? null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// No location information available.
|
||||
Log.ForContext<ReservedUnknown>().Information("Returning bike {Bike} is not possible. {Exception}", SelectedBike, ex);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorQueryLocationWhenAny;
|
||||
}
|
||||
|
||||
// Lock list to avoid multiple taps while copri action is pending.
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdatingLockingState;
|
||||
|
||||
IsConnected = IsConnectedDelegate();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectorFactory(IsConnected).Command.UpdateLockingStateAsync(
|
||||
SelectedBike,
|
||||
currentLocation != null
|
||||
? new LocationDto.Builder
|
||||
{
|
||||
Latitude = currentLocation.Latitude,
|
||||
Longitude = currentLocation.Longitude,
|
||||
Accuracy = currentLocation.Accuracy ?? double.NaN,
|
||||
Age = timeStamp.Subtract(currentLocation.Timestamp.DateTime),
|
||||
}.Build()
|
||||
: null);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is WebConnectFailureException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", SelectedBike);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorNoWebUpdateingLockstate;
|
||||
}
|
||||
else if (exception is ResponseException copriException)
|
||||
{
|
||||
// Copri server is not reachable.
|
||||
Log.ForContext<ReservedUnknown>().Information("User locked bike {bike} in order to pause ride but updating failed. Message: {Message} Details: {Details}", SelectedBike, copriException.Message, copriException.Response);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorStatusUpdateingLockstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ForContext<ReservedUnknown>().Error("User locked bike {bike} in order to pause ride but updating failed. {@l_oException}", SelectedBike.Id, exception);
|
||||
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextErrorConnectionUpdateingLockstate;
|
||||
}
|
||||
}
|
||||
|
||||
Log.ForContext<ReservedUnknown>().Information("User paused ride using {bike} successfully.", SelectedBike);
|
||||
BikesViewModel.ActionText = AppResources.ActivityTextStartingUpdater;
|
||||
await ViewUpdateManager().StartUpdateAyncPeridically();
|
||||
BikesViewModel.ActionText = string.Empty;
|
||||
BikesViewModel.IsIdle = true;
|
||||
return RequestHandlerFactory.Create(SelectedBike, IsConnectedDelegate, ConnectorFactory, Geolocation, LockService, ViewUpdateManager, SmartDevice, ViewService, BikesViewModel, ActiveUser);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using TINK.Model.Bikes.Bike;
|
||||
using TINK.MultilingualResources;
|
||||
|
||||
namespace TINK.ViewModel.Bikes.Bike
|
||||
{
|
||||
|
@ -9,64 +9,59 @@ namespace TINK.ViewModel.Bikes.Bike
|
|||
/// </summary>
|
||||
public class TariffDescriptionViewModel
|
||||
{
|
||||
private TariffDescription Tariff { get; }
|
||||
private const string AGBKEY = "AGB";
|
||||
|
||||
public TariffDescriptionViewModel(TariffDescription tariff)
|
||||
public TariffDescriptionViewModel(RentalDescription tariff)
|
||||
{
|
||||
Tariff = tariff;
|
||||
}
|
||||
Name = tariff?.Name ?? string.Empty;
|
||||
|
||||
public string Header
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(FeeEuroPerHour)
|
||||
&& string.IsNullOrEmpty(AboEuroPerMonth)
|
||||
&& string.IsNullOrEmpty(FreeTimePerSession)
|
||||
&& string.IsNullOrEmpty(MaxFeeEuroPerDay))
|
||||
// No tariff description details available.
|
||||
return string.Empty;
|
||||
TariffEntries = tariff != null && tariff?.TariffEntries != null
|
||||
? new ObservableCollection<RentalDescription.TariffElement>(tariff.TariffEntries.OrderBy(x => x.Key).Select(x => x.Value))
|
||||
: new ObservableCollection<RentalDescription.TariffElement>();
|
||||
|
||||
// Up to version MessageBikesManagementTariffDescriptionTariffHeaderNameId
|
||||
return Tariff?.Name ?? "-";
|
||||
}
|
||||
InfoEntries = tariff != null && tariff?.InfoEntries != null
|
||||
? new ObservableCollection<string>(tariff.InfoEntries.OrderBy(x => x.Key).Where(x => x.Value.Key != AGBKEY).Select(x => x.Value.Value))
|
||||
: new ObservableCollection<string>();
|
||||
|
||||
OperatorAgb = tariff?.InfoEntries != null
|
||||
&& tariff.InfoEntries.Select(x => x.Value.Key).Contains(AGBKEY)
|
||||
? tariff?.InfoEntries.FirstOrDefault(x => x.Value.Key == AGBKEY).Value.Value
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Costs per hour in euro.
|
||||
/// Holds the name of the tariff.
|
||||
/// </summary>
|
||||
public string FeeEuroPerHour
|
||||
=> !double.IsNaN(Tariff.FeeEuroPerHour)
|
||||
? string.Format("{0} {1}", Tariff.FeeEuroPerHour.ToString("0.00"), AppResources.MessageBikesManagementTariffDescriptionEuroPerHour)
|
||||
: string.Empty;
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Costs of the abo per month.
|
||||
/// Holds the tariff entries to display.
|
||||
/// </summary>
|
||||
public string AboEuroPerMonth
|
||||
=> !double.IsNaN(Tariff.AboEuroPerMonth)
|
||||
? string.Format("{0} {1}", Tariff.AboEuroPerMonth.ToString("0.00"), AppResources.MessageBikesManagementTariffDescriptionEuroPerHour)
|
||||
: string.Empty;
|
||||
public ObservableCollection<RentalDescription.TariffElement> TariffEntries { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Free use time.
|
||||
/// Holds the info entries to display.
|
||||
/// </summary>
|
||||
public string FreeTimePerSession
|
||||
=> Tariff.FreeTimePerSession != TimeSpan.Zero
|
||||
? string.Format("{0} {1}", Tariff.FreeTimePerSession.TotalHours, AppResources.MessageBikesManagementTariffDescriptionHour)
|
||||
: string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Max costs per day in euro.
|
||||
/// </summary>
|
||||
public string MaxFeeEuroPerDay
|
||||
=> !double.IsNaN(Tariff.FeeEuroPerHour)
|
||||
? string.Format("{0} {1}", Tariff.MaxFeeEuroPerDay.ToString("0.00"), AppResources.MessageBikesManagementMaxFeeEuroPerDay)
|
||||
: string.Empty;
|
||||
public ObservableCollection<string> InfoEntries { get; private set; }
|
||||
|
||||
/// <summary> Info about operator agb as HTML (i.g. text and hyperlink). </summary>
|
||||
public string OperatorAgb => !string.IsNullOrEmpty(Tariff?.OperatorAgb)
|
||||
? Tariff.OperatorAgb
|
||||
: string.Empty;
|
||||
public string OperatorAgb { get; set; }
|
||||
|
||||
public RentalDescription.TariffElement TarifEntry1 => TariffEntries.Count > 0 ? TariffEntries[0] : new RentalDescription.TariffElement();
|
||||
public RentalDescription.TariffElement TarifEntry2 => TariffEntries.Count > 1 ? TariffEntries[1] : new RentalDescription.TariffElement();
|
||||
public RentalDescription.TariffElement TarifEntry3 => TariffEntries.Count > 2 ? TariffEntries[2] : new RentalDescription.TariffElement();
|
||||
public RentalDescription.TariffElement TarifEntry4 => TariffEntries.Count > 3 ? TariffEntries[3] : new RentalDescription.TariffElement();
|
||||
|
||||
public RentalDescription.TariffElement TarifEntry5 => TariffEntries.Count > 4 ? TariffEntries[4] : new RentalDescription.TariffElement();
|
||||
public RentalDescription.TariffElement TarifEntry6 => TariffEntries.Count > 5 ? TariffEntries[5] : new RentalDescription.TariffElement();
|
||||
public RentalDescription.TariffElement TarifEntry7 => TariffEntries.Count > 6 ? TariffEntries[6] : new RentalDescription.TariffElement();
|
||||
public RentalDescription.TariffElement TarifEntry8 => TariffEntries.Count > 7 ? TariffEntries[7] : new RentalDescription.TariffElement();
|
||||
public RentalDescription.TariffElement TarifEntry9 => TariffEntries.Count > 8 ? TariffEntries[8] : new RentalDescription.TariffElement();
|
||||
|
||||
public string InfoEntry1 => InfoEntries.Count > 0 ? InfoEntries[0] : string.Empty;
|
||||
public string InfoEntry2 => InfoEntries.Count > 1 ? InfoEntries[1] : string.Empty;
|
||||
public string InfoEntry3 => InfoEntries.Count > 2 ? InfoEntries[2] : string.Empty;
|
||||
public string InfoEntry4 => InfoEntries.Count > 3 ? InfoEntries[3] : string.Empty;
|
||||
public string InfoEntry5 => InfoEntries.Count > 4 ? InfoEntries[4] : string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -449,7 +449,7 @@ namespace TINK.ViewModel.Bikes
|
|||
/// <param name="updateAction"> Update fuction passed as argument by child class.</param>
|
||||
protected async Task OnAppearing(Action updateAction)
|
||||
{
|
||||
m_oViewUpdateManager = new PollingUpdateTaskManager(() => GetType().Name, updateAction);
|
||||
m_oViewUpdateManager = new PollingUpdateTaskManager(updateAction);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
@ -556,7 +556,6 @@ namespace TINK.ViewModel.Map
|
|||
{
|
||||
// Start task which periodically updates pins.
|
||||
return new PollingUpdateTaskManager(
|
||||
() => GetType().Name,
|
||||
() =>
|
||||
{
|
||||
try
|
||||
|
|
|
@ -7,64 +7,59 @@ using TINK.Repository.Exception;
|
|||
|
||||
namespace TINK.ViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Object to periodically actuate an action.
|
||||
/// </summary>
|
||||
public class PollingUpdateTask
|
||||
{
|
||||
/// <summary>Name of child class provider. </summary>
|
||||
private readonly Func<String> m_oGetChildName;
|
||||
|
||||
/// <summary> Object to control canelling. </summary>
|
||||
private readonly CancellationTokenSource m_oCanellationTokenSource;
|
||||
private CancellationTokenSource CanellationTokenSource { get; }
|
||||
|
||||
/// <summary> Task to perform update. </summary>
|
||||
private readonly Task m_oUpdateTask;
|
||||
private Task UpdateTask { get; }
|
||||
|
||||
/// <summary> Action which performs an update.</summary>
|
||||
private readonly Action m_oUpdateAction;
|
||||
private Action UpdateAction { get; }
|
||||
|
||||
/// <summary> Obects which does a periodic update. </summary>
|
||||
/// <param name="p_oGetChildName">Name of the child class. For logging purposes.</param>
|
||||
/// <param name="p_oUpdateAction">Update action to perform.</param>
|
||||
/// <param name="p_oPolling"> Holds whether to poll or not and the periode leght is polling is on. </param>
|
||||
/// <param name="updateAction">Update action to perform.</param>
|
||||
/// <param name="polling"> Holds whether to poll or not and the periode leght is polling is on. </param>
|
||||
public PollingUpdateTask(
|
||||
Func<String> p_oGetChildName,
|
||||
Action p_oUpdateAction,
|
||||
TimeSpan? p_oPolling)
|
||||
Action updateAction,
|
||||
TimeSpan? polling)
|
||||
{
|
||||
m_oGetChildName = p_oGetChildName
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}- obect. Argument {nameof(p_oGetChildName)} must not be null.");
|
||||
UpdateAction = updateAction
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}- obect. Argument {nameof(updateAction)} must not be null.");
|
||||
|
||||
m_oUpdateAction = p_oUpdateAction
|
||||
?? throw new ArgumentException($"Can not construct {GetType().Name}- obect. Argument {nameof(p_oUpdateAction)} must not be null.");
|
||||
|
||||
if (!p_oPolling.HasValue)
|
||||
if (!polling.HasValue)
|
||||
{
|
||||
// Automatic update is switched off.
|
||||
Log.ForContext<PollingUpdateTask>().Debug($"Automatic update is off, context {GetType().Name} at {DateTime.Now}.");
|
||||
return;
|
||||
}
|
||||
|
||||
var l_iUpdatePeriodeSet = p_oPolling.Value;
|
||||
var updatePeriodeSet = polling.Value;
|
||||
|
||||
m_oCanellationTokenSource = new CancellationTokenSource();
|
||||
CanellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
int l_iCycleIndex = 2;
|
||||
m_oUpdateTask = Task.Run(
|
||||
int cycleIndex = 2;
|
||||
UpdateTask = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
while (!m_oCanellationTokenSource.IsCancellationRequested)
|
||||
while (!CanellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(l_iUpdatePeriodeSet, m_oCanellationTokenSource.Token);
|
||||
await Task.Delay(updatePeriodeSet, CanellationTokenSource.Token);
|
||||
{
|
||||
// N. update cycle
|
||||
Log.ForContext<PollingUpdateTask>().Information($"Actuating {l_iCycleIndex} update cycle, context {GetType().Name} at {DateTime.Now}.");
|
||||
Log.ForContext<PollingUpdateTask>().Information($"Actuating {cycleIndex} update cycle, context {GetType().Name} at {DateTime.Now}.");
|
||||
|
||||
m_oUpdateAction();
|
||||
UpdateAction();
|
||||
|
||||
l_iCycleIndex = l_iCycleIndex < int.MaxValue ? ++l_iCycleIndex : 0;
|
||||
cycleIndex = cycleIndex < int.MaxValue ? ++cycleIndex : 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
m_oCanellationTokenSource.Token);
|
||||
CanellationTokenSource.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -73,51 +68,57 @@ namespace TINK.ViewModel
|
|||
/// </summary>
|
||||
public async Task Terminate()
|
||||
{
|
||||
if (UpdateTask == null)
|
||||
{
|
||||
// Nothing to do if no task was created.
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel update task;
|
||||
if (m_oCanellationTokenSource == null)
|
||||
if (CanellationTokenSource == null)
|
||||
{
|
||||
throw new Exception($"Can not terminate periodical update task, context {GetType().Name} at {DateTime.Now}. No task running.");
|
||||
}
|
||||
|
||||
Log.Information($"Request to terminate update cycle, context {GetType().Name} at {DateTime.Now}.");
|
||||
m_oCanellationTokenSource.Cancel();
|
||||
CanellationTokenSource.Cancel();
|
||||
|
||||
try
|
||||
{
|
||||
await m_oUpdateTask;
|
||||
await UpdateTask;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// Polling update was canceled.
|
||||
// Nothing to notice/ worry about.
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
catch (Exception exception)
|
||||
{
|
||||
// An error occurred updating pin.
|
||||
var l_oAggregateException = l_oException as AggregateException;
|
||||
if (l_oAggregateException == null)
|
||||
var aggregateException = exception as AggregateException;
|
||||
if (aggregateException == null)
|
||||
{
|
||||
// Unexpected exception detected. Exception should alyways be of type AggregateException
|
||||
Log.Error("An/ several errors occurred on update task. {@Exceptions}.", l_oException);
|
||||
Log.Error("An/ several errors occurred on update task. {@Exceptions}.", exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (l_oAggregateException.InnerExceptions.Count == 1
|
||||
&& l_oAggregateException.InnerExceptions[0].GetType() == typeof(TaskCanceledException))
|
||||
if (aggregateException.InnerExceptions.Count == 1
|
||||
&& aggregateException.InnerExceptions[0].GetType() == typeof(TaskCanceledException))
|
||||
{
|
||||
// Polling update was canceled.
|
||||
// Nothing to notice/ worry about.
|
||||
}
|
||||
else if (l_oAggregateException.InnerExceptions.Count() > 0 &&
|
||||
l_oAggregateException.InnerExceptions.First(x => !x.GetIsConnectFailureException()) == null) // There is no exception which is not of type connect failure.
|
||||
else if (aggregateException.InnerExceptions.Count() > 0 &&
|
||||
aggregateException.InnerExceptions.First(x => !x.GetIsConnectFailureException()) == null) // There is no exception which is not of type connect failure.
|
||||
{
|
||||
// All exceptions were caused by communication error
|
||||
Log.Information("An/ several web related connect failure exceptions occurred on update task. {@Exceptions}.", l_oException);
|
||||
Log.Information("An/ several web related connect failure exceptions occurred on update task. {@Exceptions}.", exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
// All exceptions were caused by communication errors.
|
||||
Log.Error("An/ several exceptions occurred on update task. {@Exception}.", l_oException);
|
||||
Log.Error("An/ several exceptions occurred on update task. {@Exception}.", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,14 +10,11 @@ namespace TINK.ViewModel
|
|||
/// </summary>
|
||||
public class PollingUpdateTaskManager : IPollingUpdateTaskManager
|
||||
{
|
||||
/// <summary>Name of child class provider. </summary>
|
||||
private readonly Func<string> m_oGetChildName;
|
||||
|
||||
/// <summary> Action which performs an update.</summary>
|
||||
private Action m_oUpdateAction;
|
||||
private Action UpdateAction { get; }
|
||||
|
||||
/// <summary> Reference on the current polling task</summary>
|
||||
private PollingUpdateTask m_oPollingUpdateTask;
|
||||
private PollingUpdateTask UpdateTask { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets polling parameters.
|
||||
|
@ -30,16 +27,12 @@ namespace TINK.ViewModel
|
|||
|
||||
/// <summary>Constructs a update manager object. </summary>
|
||||
/// <param name="p_oGetChildName">Name of the child class. For logging purposes.</param>
|
||||
/// <param name="p_oUpdateAction"></param>
|
||||
/// <param name="updateAction"></param>
|
||||
public PollingUpdateTaskManager(
|
||||
Func<String> p_oGetChildName,
|
||||
Action p_oUpdateAction)
|
||||
Action updateAction)
|
||||
{
|
||||
m_oGetChildName = p_oGetChildName ?? throw new ArgumentException(
|
||||
$"Can not construct {GetType().Name}- obect. Argument {nameof(p_oGetChildName)} must not be null.");
|
||||
|
||||
m_oUpdateAction = p_oUpdateAction ?? throw new ArgumentException(
|
||||
$"Can not construct {GetType().Name}- obect. Argument {nameof(p_oUpdateAction)} must not be null.");
|
||||
UpdateAction = updateAction ?? throw new ArgumentException(
|
||||
$"Can not construct {GetType().Name}- obect. Argument {nameof(updateAction)} must not be null.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -49,13 +42,13 @@ namespace TINK.ViewModel
|
|||
/// <param name="pollingParameters">Parametes holding polling periode, if null last parameters are used if available.</param>
|
||||
public async Task StartUpdateAyncPeridically(PollingParameters pollingParameters = null)
|
||||
{
|
||||
if (m_oPollingUpdateTask != null)
|
||||
if (UpdateTask != null)
|
||||
{
|
||||
Log.Error($"Call to stop periodic update task missed in context {GetType().Name} at {DateTime.Now}.");
|
||||
|
||||
// Call of StopUpdatePeriodically missed.
|
||||
// Terminate running polling update task before starting a new one
|
||||
await m_oPollingUpdateTask.Terminate();
|
||||
await UpdateTask.Terminate();
|
||||
}
|
||||
|
||||
if (pollingParameters != null)
|
||||
|
@ -79,9 +72,8 @@ namespace TINK.ViewModel
|
|||
return;
|
||||
}
|
||||
|
||||
m_oPollingUpdateTask = new PollingUpdateTask(
|
||||
m_oGetChildName,
|
||||
m_oUpdateAction,
|
||||
UpdateTask = new PollingUpdateTask(
|
||||
UpdateAction,
|
||||
PollingParameters.Periode);
|
||||
}
|
||||
|
||||
|
@ -91,15 +83,15 @@ namespace TINK.ViewModel
|
|||
/// </summary>
|
||||
public async Task StopUpdatePeridically()
|
||||
{
|
||||
if (m_oPollingUpdateTask == null)
|
||||
if (UpdateTask == null)
|
||||
{
|
||||
// Nothing to do if there is no update task pending.
|
||||
return;
|
||||
}
|
||||
|
||||
await m_oPollingUpdateTask.Terminate();
|
||||
await UpdateTask.Terminate();
|
||||
|
||||
m_oPollingUpdateTask = null;
|
||||
UpdateTask = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue