mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-06-22 05:47:28 +02:00
Version 3.0.337
This commit is contained in:
parent
fd0e63cf10
commit
573fe77e12
2336 changed files with 33688 additions and 86082 deletions
|
@ -1,12 +1,13 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using TINK.Model.Connector.Updater;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Repository;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Repository.Response;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Model.Device;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -84,7 +85,7 @@ namespace TINK.Model.Connector
|
|||
/// </summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
public async Task DoReserve(
|
||||
Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
Bikes.BikeInfoNS.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected booking request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
|
@ -92,7 +93,7 @@ namespace TINK.Model.Connector
|
|||
|
||||
/// <summary> Request to cancel a reservation.</summary>
|
||||
/// <param name="p_oBike">Bike to book.</param>
|
||||
public async Task DoCancelReservation(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
public async Task DoCancelReservation(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected cancel reservation request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
|
@ -100,7 +101,7 @@ namespace TINK.Model.Connector
|
|||
|
||||
/// <summary> Get authentication keys.</summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
public async Task CalculateAuthKeys(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
|
||||
public async Task CalculateAuthKeys(Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected request to get authenticatin keys detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
|
@ -110,7 +111,7 @@ namespace TINK.Model.Connector
|
|||
/// <param name="bike">Bike to update locking state for.</param>
|
||||
/// <param name="location">Location where lock was opened/ changed.</param>
|
||||
/// <returns>Response on updating locking state.</returns>
|
||||
public async Task StartReturningBike(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
public async Task StartReturningBike(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected request to notify about start of returning bike. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
|
@ -120,26 +121,26 @@ namespace TINK.Model.Connector
|
|||
/// <remarks> Operator specific call.</remarks>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <returns>Response on notification about start of returning sequence.</returns>
|
||||
public async Task UpdateLockingStateAsync(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto location)
|
||||
public async Task UpdateLockingStateAsync(Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable bike, LocationDto location)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected request to update locking state detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task DoBook(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
public async Task DoBook(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected booking request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task BookAndOpenAync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
public async Task BookAndOpenAync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected request to book and open bike detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<BookingFinishedModel> DoReturn(
|
||||
Bikes.Bike.BC.IBikeInfoMutable bike,
|
||||
Bikes.BikeInfoNS.BC.IBikeInfoMutable bike,
|
||||
LocationDto location,
|
||||
ISmartDevice smartDevice)
|
||||
{
|
||||
|
@ -148,7 +149,7 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
public async Task<BookingFinishedModel> ReturnAndCloseAsync(
|
||||
Bikes.Bike.CopriLock.IBikeInfoMutable bike,
|
||||
Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike,
|
||||
ISmartDevice smartDevice)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected close lock and return request detected. No user logged in.");
|
||||
|
@ -177,10 +178,10 @@ namespace TINK.Model.Connector
|
|||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OpenLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
public Task OpenLockAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public Task CloseLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
public Task CloseLockAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike)
|
||||
=> throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using TINK.Model.Connector.Updater;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Repository;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Repository.Response;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Model.Device;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Services.CopriApi;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
|
@ -25,7 +26,7 @@ namespace TINK.Model.Connector
|
|||
public CommandLoggedIn(ICopriServerBase p_oCopriServer,
|
||||
string sessionCookie,
|
||||
string mail,
|
||||
Func<DateTime> dateTimeProvider) : base(p_oCopriServer, sessionCookie, mail, dateTimeProvider)
|
||||
Func<DateTime> dateTimeProvider) : base(p_oCopriServer, sessionCookie, mail, dateTimeProvider)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -75,7 +76,7 @@ namespace TINK.Model.Connector
|
|||
/// Request to reserve a bike.
|
||||
/// </summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
public async Task DoReserve(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
public async Task DoReserve(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
|
@ -93,13 +94,13 @@ namespace TINK.Model.Connector
|
|||
throw;
|
||||
}
|
||||
|
||||
bike.Load(response, Mail, Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
bike.Load(response, Mail, Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
/// <summary> Request to cancel a reservation.</summary>
|
||||
/// <param name="bike">Bike to cancel reservation.</param>
|
||||
public async Task DoCancelReservation(
|
||||
Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
Bikes.BikeInfoNS.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
|
@ -117,12 +118,12 @@ namespace TINK.Model.Connector
|
|||
throw;
|
||||
}
|
||||
|
||||
bike.Load(Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
bike.Load(Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
/// <summary> Get authentication keys.</summary>
|
||||
/// <param name="bike">Bike to get new keys for.</param>
|
||||
public async Task CalculateAuthKeys(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
|
||||
public async Task CalculateAuthKeys(Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable bike)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
|
@ -152,17 +153,17 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
UpdaterJSON.Load(
|
||||
bike,
|
||||
response,
|
||||
Mail,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
bike,
|
||||
response,
|
||||
Mail,
|
||||
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
/// <summary> Notifies COPRI about start of returning sequence. </summary>
|
||||
/// <remarks> Operator specific call.</remarks>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <returns>Response on notification about start of returning sequence.</returns>
|
||||
public async Task StartReturningBike(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
public async Task StartReturningBike(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
|
@ -188,7 +189,7 @@ namespace TINK.Model.Connector
|
|||
/// <param name="location">Location of the bike.</param>
|
||||
/// <returns>Response on updating locking state.</returns>
|
||||
public async Task UpdateLockingStateAsync(
|
||||
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
|
||||
IBikeInfoMutable bike,
|
||||
LocationDto location)
|
||||
{
|
||||
if (bike == null)
|
||||
|
@ -221,10 +222,10 @@ namespace TINK.Model.Connector
|
|||
try
|
||||
{
|
||||
(await CopriServer.UpdateLockingStateAsync(
|
||||
bike.Id,
|
||||
bike.Id,
|
||||
state.Value,
|
||||
bike.OperatorUri,
|
||||
location,
|
||||
location,
|
||||
bike.LockInfo.BatteryPercentage)).GetIsBookingResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
|
@ -236,7 +237,7 @@ namespace TINK.Model.Connector
|
|||
|
||||
/// <summary> Request to book a bike. </summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
public async Task DoBook(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
public async Task DoBook(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
|
@ -249,22 +250,22 @@ namespace TINK.Model.Connector
|
|||
double batteryPercentage = btBike != null ? btBike.LockInfo.BatteryPercentage : double.NaN;
|
||||
|
||||
response = (await CopriServer.DoBookAsync(
|
||||
bike.Id,
|
||||
guid,
|
||||
bike.Id,
|
||||
guid,
|
||||
batteryPercentage,
|
||||
bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
||||
|
||||
bike.Load(
|
||||
response,
|
||||
response,
|
||||
Mail,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Books a bike and opens the lock.
|
||||
/// </summary>
|
||||
/// <param name="bike">Bike to book and open.</param>
|
||||
public async Task BookAndOpenAync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
public async Task BookAndOpenAync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike)
|
||||
=> await Polling.BookAndOpenAync(CopriServer, bike, Mail);
|
||||
|
||||
/// <summary> Request to return a bike.</summary>
|
||||
|
@ -272,7 +273,7 @@ namespace TINK.Model.Connector
|
|||
/// <param name="locaton">Position of the bike for bluetooth locks.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
public async Task<BookingFinishedModel> DoReturn(
|
||||
Bikes.Bike.BC.IBikeInfoMutable bike,
|
||||
Bikes.BikeInfoNS.BC.IBikeInfoMutable bike,
|
||||
LocationDto location = null,
|
||||
ISmartDevice smartDevice = null)
|
||||
{
|
||||
|
@ -282,16 +283,16 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
DoReturnResponse response
|
||||
= (await CopriServer.DoReturn(bike.Id, location, smartDevice, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
|
||||
= (await CopriServer.DoReturn(bike.Id, location, smartDevice, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
|
||||
|
||||
bike.Load(Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
bike.Load(Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.None);
|
||||
return response?.Create() ?? new BookingFinishedModel();
|
||||
}
|
||||
|
||||
/// <summary> Request to return bike and close the lock.</summary>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
public async Task<BookingFinishedModel> ReturnAndCloseAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike, ISmartDevice smartDevice = null)
|
||||
public async Task<BookingFinishedModel> ReturnAndCloseAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike, ISmartDevice smartDevice = null)
|
||||
=> await Polling.ReturnAndCloseAync(CopriServer, smartDevice, bike);
|
||||
|
||||
/// <summary>
|
||||
|
@ -302,8 +303,11 @@ namespace TINK.Model.Connector
|
|||
public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri)
|
||||
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
|
||||
#else
|
||||
public async Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri)
|
||||
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
|
||||
/// <summary> Submits feedback for a renting operation.</summary>
|
||||
public async Task DoSubmitFeedback(
|
||||
IUserFeedback userFeedback,
|
||||
Uri opertorUri)
|
||||
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.CurrentChargeBars, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
|
||||
#endif
|
||||
|
||||
/// <summary> Submits mini survey to copri server. </summary>
|
||||
|
@ -311,9 +315,9 @@ namespace TINK.Model.Connector
|
|||
public async Task DoSubmitMiniSurvey(IDictionary<string, string> answers)
|
||||
=> await CopriServer.DoSubmitMiniSurvey(answers);
|
||||
|
||||
public async Task OpenLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
public async Task OpenLockAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike)
|
||||
=> await CopriServer.OpenAync(bike);
|
||||
public async Task CloseLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike)
|
||||
public async Task CloseLockAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike)
|
||||
=> await CopriServer.CloseAync(bike);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Repository.Request;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Model.Device;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Device;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Repository.Request;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -25,54 +25,54 @@ namespace TINK.Model.Connector
|
|||
|
||||
/// <summary> Request to reserve a bike.</summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
Task DoReserve(Bikes.Bike.BC.IBikeInfoMutable bike);
|
||||
Task DoReserve(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to cancel a reservation.</summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
Task DoCancelReservation(Bikes.Bike.BC.IBikeInfoMutable bike);
|
||||
Task DoCancelReservation(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Get authentication keys to connect to lock.</summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
Task CalculateAuthKeys(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike);
|
||||
Task CalculateAuthKeys(Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Notifies COPRI about start of returning sequence. </summary>
|
||||
/// <remarks> Operator specific call.</remarks>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <returns>Response on notification about start of returning sequence.</returns>
|
||||
Task StartReturningBike(Bikes.Bike.BC.IBikeInfoMutable bike);
|
||||
Task StartReturningBike(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Updates COPRI lock state for a booked bike. </summary>
|
||||
/// <param name="bike">Bike to update locking state for.</param>
|
||||
/// <param name="location">Geolocation of lock when returning bike.</param>
|
||||
/// <returns>Response on updating locking state.</returns>
|
||||
Task UpdateLockingStateAsync(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto location = null);
|
||||
Task UpdateLockingStateAsync(Bikes.BikeInfoNS.BluetoothLock.IBikeInfoMutable bike, LocationDto location = null);
|
||||
|
||||
/// <summary> Request to book a bike.</summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
Task DoBook(Bikes.Bike.BC.IBikeInfoMutable bike);
|
||||
Task DoBook(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to book a bike and open its lock.</summary>
|
||||
/// <param name="bike">Bike to book and to open lock for.</param>
|
||||
Task BookAndOpenAync(Bikes.Bike.CopriLock.IBikeInfoMutable bike);
|
||||
Task BookAndOpenAync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to open lock.</summary>
|
||||
/// <param name="bike">Bike for which lock has to be opened.</param>
|
||||
Task OpenLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike);
|
||||
Task OpenLockAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to close lock.</summary>
|
||||
/// <param name="bike">Bike for which lock has to be closed.</param>
|
||||
Task CloseLockAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike);
|
||||
Task CloseLockAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to return a bike.</summary>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <param name="location">Geolocation of lock when returning bike.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
Task<BookingFinishedModel> DoReturn(Bikes.Bike.BC.IBikeInfoMutable bike, LocationDto geolocation = null, ISmartDevice smartDevice = null);
|
||||
Task<BookingFinishedModel> DoReturn(Bikes.BikeInfoNS.BC.IBikeInfoMutable bike, LocationDto geolocation = null, ISmartDevice smartDevice = null);
|
||||
|
||||
/// <summary> Request to return bike and close the lock.</summary>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
/// <param name="smartDevice">Provides info about hard and software.</param>
|
||||
Task<BookingFinishedModel> ReturnAndCloseAsync(Bikes.Bike.CopriLock.IBikeInfoMutable bike, ISmartDevice smartDevice = null);
|
||||
Task<BookingFinishedModel> ReturnAndCloseAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike, ISmartDevice smartDevice = null);
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
bool IsConnected { get; }
|
||||
|
@ -80,7 +80,10 @@ namespace TINK.Model.Connector
|
|||
/// <summary> True if user is logged in false if not. </summary>
|
||||
string SessionCookie { get; }
|
||||
|
||||
Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri);
|
||||
/// <summary> Submits feedback for a renting operation.</summary>
|
||||
Task DoSubmitFeedback(
|
||||
IUserFeedback userFeedback,
|
||||
Uri opertorUri);
|
||||
|
||||
/// <summary> Submits mini survey to copri server. </summary>
|
||||
/// <param name="answers">Collection of answers.</param>
|
||||
|
@ -124,6 +127,11 @@ namespace TINK.Model.Connector
|
|||
/// <summary> Id of the bike to which the feedback is related to.</summary>
|
||||
string BikeId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the current chargeing level of the battery in bars, null if unkonwn.
|
||||
/// </summary>
|
||||
int? CurrentChargeBars { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds whether bike is broken or not.
|
||||
/// </summary>
|
||||
|
|
|
@ -17,9 +17,16 @@ namespace TINK.Model.Connector
|
|||
{
|
||||
public string BikeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the current chargeing level of the battery in bars, null if unkonwn.
|
||||
/// </summary>
|
||||
public int? CurrentChargeBars { get; set; }
|
||||
|
||||
public bool IsBikeBroken { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -5,33 +5,46 @@ using TINK.Repository;
|
|||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary>
|
||||
/// Connects tink app to copri by getting data from copri and updating tink app model (i.e. bikes, user, ...)
|
||||
/// Connects app to copri data by getting data from copri.
|
||||
/// </summary>
|
||||
public class Connector : IConnector
|
||||
{
|
||||
/// <summary>Constructs a copri connector object.</summary>
|
||||
/// <param name="activeUri"> Uri to connect to.</param>
|
||||
/// <param name="appContextInfo">Provides app related info (app name and version, merchantid) to pass to COPRI.</param>
|
||||
/// /// <param name="sessionCookie"> Holds the session cookie.</param>
|
||||
/// <param name="p_strMail">Mail of user.</param>
|
||||
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
|
||||
/// <param name="sessionCookie"> Holds the session cookie.</param>
|
||||
/// <param name="mail">Mail of user.</param>
|
||||
/// <param name="expiresAfter">Timespan which holds value after which cache expires.</param>
|
||||
/// <param name="server"> Provides cached addess to copri.</param>
|
||||
/// <param name="server"> Is null in production and migh be a mock in testing context.</param>
|
||||
public Connector(
|
||||
Uri activeUri,
|
||||
AppContextInfo appContextInfo,
|
||||
string uiIsoLangugageName,
|
||||
string sessionCookie,
|
||||
string mail,
|
||||
TimeSpan? expiresAfter = null,
|
||||
ICachedCopriServer server = null )
|
||||
ICachedCopriServer server = null)
|
||||
{
|
||||
Command = GetCommand(
|
||||
server ?? new CopriProviderHttps(activeUri, appContextInfo.MerchantId, appContextInfo, sessionCookie),
|
||||
sessionCookie,
|
||||
server ?? new CopriProviderHttps(
|
||||
activeUri,
|
||||
appContextInfo.MerchantId,
|
||||
appContextInfo,
|
||||
uiIsoLangugageName,
|
||||
sessionCookie),
|
||||
sessionCookie,
|
||||
mail);
|
||||
|
||||
Query = GetQuery(
|
||||
server ?? new CopriProviderHttps(activeUri, appContextInfo.MerchantId, appContextInfo, sessionCookie, expiresAfter),
|
||||
sessionCookie,
|
||||
server ?? new CopriProviderHttps(
|
||||
activeUri,
|
||||
appContextInfo.MerchantId,
|
||||
appContextInfo,
|
||||
uiIsoLangugageName,
|
||||
sessionCookie,
|
||||
expiresAfter),
|
||||
sessionCookie,
|
||||
mail);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,28 +5,30 @@ using TINK.Repository;
|
|||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary>
|
||||
/// Connects tink app to copri by getting data from copri and updating tink app model (i.e. bikes, user, ...)
|
||||
/// Connects app to copri data by getting data from cache.
|
||||
/// </summary>
|
||||
public class ConnectorCache : IConnector
|
||||
{
|
||||
/// <summary>Constructs a copri connector object.</summary>
|
||||
/// <param name="p_strSessionCookie"> Holds the session cookie.</param>
|
||||
/// <param name="p_strMail">Mail of user.</param>
|
||||
/// <param name="server"> Provides addess to copri.</param>
|
||||
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
|
||||
/// <param name="sessionCookie"> Holds the session cookie.</param>
|
||||
/// <param name="mail">Mail of user.</param>
|
||||
/// <param name="server"> Is null in production and migh be a mock in testing context.</param>
|
||||
public ConnectorCache(
|
||||
AppContextInfo appContextInfo,
|
||||
string uiIsoLangugageName,
|
||||
string sessionCookie,
|
||||
string mail,
|
||||
ICopriServer server = null)
|
||||
{
|
||||
|
||||
Command = Connector.GetCommand(
|
||||
server ?? new CopriProviderMonkeyStore(appContextInfo.MerchantId, sessionCookie),
|
||||
server ?? new CopriProviderMonkeyStore(appContextInfo.MerchantId, uiIsoLangugageName, sessionCookie),
|
||||
sessionCookie,
|
||||
mail);
|
||||
|
||||
Query = GetQuery(
|
||||
server ?? new CopriProviderMonkeyStore(appContextInfo.MerchantId, sessionCookie),
|
||||
server ?? new CopriProviderMonkeyStore(appContextInfo.MerchantId, uiIsoLangugageName, sessionCookie),
|
||||
sessionCookie,
|
||||
mail);
|
||||
}
|
||||
|
|
|
@ -10,18 +10,20 @@ namespace TINK.Model.Connector
|
|||
/// </summary>
|
||||
/// <param name="isConnected">True if online, false if offline. If offline cache connector is returned.</param>
|
||||
/// <param name="appContextInfo">Provides app related info (app name and version, merchantid) to pass to COPRI.</param>
|
||||
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
|
||||
/// <returns></returns>
|
||||
public static IConnector Create(
|
||||
bool isConnected,
|
||||
bool isConnected,
|
||||
Uri activeUri,
|
||||
AppContextInfo appContextInfo,
|
||||
string sessionCookie,
|
||||
string mail,
|
||||
AppContextInfo appContextInfo,
|
||||
string uiIsoLangugageName,
|
||||
string sessionCookie,
|
||||
string mail,
|
||||
TimeSpan? expiresAfter = null)
|
||||
{
|
||||
return isConnected
|
||||
? new Connector(activeUri, appContextInfo, sessionCookie, mail, expiresAfter: expiresAfter) as IConnector
|
||||
: new ConnectorCache(appContextInfo, sessionCookie, mail);
|
||||
? new Connector(activeUri, appContextInfo, uiIsoLangugageName, sessionCookie, mail, expiresAfter: expiresAfter) as IConnector
|
||||
: new ConnectorCache(appContextInfo, uiIsoLangugageName, sessionCookie, mail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TINK.Model.Connector.Filter
|
||||
|
@ -18,8 +17,8 @@ namespace TINK.Model.Connector.Filter
|
|||
/// </remarks>
|
||||
public static IGroupFilter Create(IEnumerable<string> group)
|
||||
{
|
||||
return group != null && group.Count() > 0
|
||||
? (IGroupFilter) new IntersectGroupFilter(group) :
|
||||
return group != null && group.Count() > 0
|
||||
? (IGroupFilter)new IntersectGroupFilter(group) :
|
||||
new NullGroupFilter();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace TINK.Model.Connector.Filter
|
|||
/// <param name="group">Group to transform.</param>
|
||||
/// <returns>Enumeration of numeric bike categories.</returns>
|
||||
public static IEnumerable<string> ToBikeCategory(this IEnumerable<string> group)
|
||||
=> group?.Select(x => x.GetBikeCategory())?.Where(x => !string.IsNullOrEmpty(x))
|
||||
=> group?.Select(x => x.GetBikeCategory())?.Where(x => !string.IsNullOrEmpty(x))
|
||||
?? new List<string>();
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Connector.Filter;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.Station;
|
||||
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -48,7 +48,7 @@ namespace TINK.Model.Connector
|
|||
private class QueryProvider : IQuery
|
||||
{
|
||||
/// <summary> Holds the filter. </summary>
|
||||
private IGroupFilter Filter { get; }
|
||||
private IGroupFilter Filter { get; }
|
||||
|
||||
/// <summary> Holds the reference to object which performs copry queries.</summary>
|
||||
private IQuery m_oInnerQuery;
|
||||
|
@ -67,7 +67,7 @@ namespace TINK.Model.Connector
|
|||
{
|
||||
var result = await m_oInnerQuery.GetBikesAsync();
|
||||
return new Result<BikeCollection>(
|
||||
result.Source,
|
||||
result.Source,
|
||||
new BikeCollection(DoFilter(result.Response, Filter)),
|
||||
result.GeneralData,
|
||||
result.Exception);
|
||||
|
@ -78,7 +78,7 @@ namespace TINK.Model.Connector
|
|||
{
|
||||
var result = await m_oInnerQuery.GetBikesOccupiedAsync();
|
||||
return new Result<BikeCollection>(
|
||||
result.Source,
|
||||
result.Source,
|
||||
new BikeCollection(result.Response.ToDictionary(x => x.Id)),
|
||||
result.GeneralData,
|
||||
result.Exception);
|
||||
|
@ -98,7 +98,7 @@ namespace TINK.Model.Connector
|
|||
providerBikesAndStations.Source,
|
||||
new StationsAndBikesContainer(filteredStationsDictionary, filteredBikesDictionary),
|
||||
providerBikesAndStations.GeneralData,
|
||||
providerBikesAndStations.Exception);
|
||||
providerBikesAndStations.Exception);
|
||||
|
||||
return filteredBikesAndStations;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace TINK.Model.Connector
|
|||
public static IFilteredConnector Create(IEnumerable<string> group, IConnector connector)
|
||||
{
|
||||
return group != null
|
||||
? (IFilteredConnector) new FilteredConnector(group, connector)
|
||||
? (IFilteredConnector)new FilteredConnector(group, connector)
|
||||
: new NullFilterConnector(connector); // Do not apply filtering.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.Station;
|
||||
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ namespace TINK.Model.Connector
|
|||
/// <summary> Constructs a query object.</summary>
|
||||
/// <param name="p_oInnerQuery"></param>
|
||||
/// <param name="p_oFilter"></param>
|
||||
public QueryProvider(IQuery p_oInnerQuery)
|
||||
public QueryProvider(IQuery p_oInnerQuery)
|
||||
{
|
||||
m_oInnerQuery = p_oInnerQuery;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace TINK.Model.Connector
|
|||
|
||||
/// <summary> Gets the merchant id.</summary>
|
||||
protected string MerchantId => CopriServer.MerchantId;
|
||||
|
||||
|
||||
/// <summary> Constructs a query base object.</summary>
|
||||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
/// <param name="p_oErrorStack">Object which hold communication objects.</param>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Connector.Updater;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Repository;
|
||||
using TINK.Services.CopriApi;
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -16,14 +17,14 @@ namespace TINK.Model.Connector
|
|||
private readonly ICachedCopriServer server;
|
||||
|
||||
/// <summary>Constructs a copri query object.</summary>
|
||||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
/// <param name="copriServer">Server which implements communication.</param>
|
||||
public CachedQuery(
|
||||
ICopriServerBase p_oCopriServer) : base(p_oCopriServer)
|
||||
ICopriServerBase copriServer) : base(copriServer)
|
||||
{
|
||||
server = p_oCopriServer as ICachedCopriServer;
|
||||
server = copriServer as ICachedCopriServer;
|
||||
if (server == null)
|
||||
{
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {p_oCopriServer.GetType()}.");
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {copriServer.GetType()}.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Connector.Updater;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Repository;
|
||||
|
||||
|
@ -11,26 +13,26 @@ namespace TINK.Model.Connector
|
|||
public class CachedQueryLoggedIn : BaseLoggedIn, IQuery
|
||||
{
|
||||
/// <summary> Cached copri server. </summary>
|
||||
private readonly ICachedCopriServer server;
|
||||
private ICachedCopriServer Server { get; }
|
||||
|
||||
/// <summary>Constructs a copri query object.</summary>
|
||||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
public CachedQueryLoggedIn(ICopriServerBase p_oCopriServer,
|
||||
string p_strSessionCookie,
|
||||
string p_strMail,
|
||||
Func<DateTime> p_oDateTimeProvider) : base(p_oCopriServer, p_strSessionCookie, p_strMail, p_oDateTimeProvider)
|
||||
/// <param name="copriServer">Server which implements communication.</param>
|
||||
public CachedQueryLoggedIn(ICopriServerBase copriServer,
|
||||
string sessionCookie,
|
||||
string mail,
|
||||
Func<DateTime> dateTimeProvider) : base(copriServer, sessionCookie, mail, dateTimeProvider)
|
||||
{
|
||||
server = p_oCopriServer as ICachedCopriServer;
|
||||
if (server == null)
|
||||
Server = copriServer as ICachedCopriServer;
|
||||
if (Server == null)
|
||||
{
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {p_oCopriServer.GetType()}.");
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {copriServer.GetType()}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets all stations including postions.</summary>
|
||||
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
|
||||
{
|
||||
var stationsResponse = await server.GetStations();
|
||||
var stationsResponse = await Server.GetStations();
|
||||
|
||||
if (stationsResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| stationsResponse.Exception != null)
|
||||
|
@ -41,15 +43,15 @@ namespace TINK.Model.Connector
|
|||
new StationsAndBikesContainer(
|
||||
stationsResponse.Response.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(
|
||||
(await server.GetBikesAvailable(true)).Response,
|
||||
(await server.GetBikesOccupied(true)).Response,
|
||||
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
|
||||
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider)),
|
||||
stationsResponse.GeneralData,
|
||||
stationsResponse.Exception);
|
||||
}
|
||||
|
||||
var bikesAvailableResponse = await server.GetBikesAvailable();
|
||||
var bikesAvailableResponse = await Server.GetBikesAvailable();
|
||||
if (bikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| bikesAvailableResponse.Exception != null)
|
||||
{
|
||||
|
@ -57,16 +59,16 @@ namespace TINK.Model.Connector
|
|||
return new Result<StationsAndBikesContainer>(
|
||||
bikesAvailableResponse.Source,
|
||||
new StationsAndBikesContainer(
|
||||
(await server.GetStations(true)).Response.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(bikesAvailableResponse.Response,
|
||||
(await server.GetBikesOccupied(true)).Response,
|
||||
(await Server.GetStations(true)).Response.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(bikesAvailableResponse.Response?.bikes?.Values,
|
||||
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider)),
|
||||
bikesAvailableResponse.GeneralData,
|
||||
bikesAvailableResponse.Exception);
|
||||
}
|
||||
|
||||
var bikesOccupiedResponse = await server.GetBikesOccupied();
|
||||
var bikesOccupiedResponse = await Server.GetBikesOccupied();
|
||||
if (bikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| bikesOccupiedResponse.Exception != null)
|
||||
{
|
||||
|
@ -74,10 +76,10 @@ namespace TINK.Model.Connector
|
|||
return new Result<StationsAndBikesContainer>(
|
||||
bikesOccupiedResponse.Source,
|
||||
new StationsAndBikesContainer(
|
||||
(await server.GetStations(true)).Response.GetStationsAllMutable(),
|
||||
(await Server.GetStations(true)).Response.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(
|
||||
(await server.GetBikesAvailable(true)).Response,
|
||||
bikesOccupiedResponse.Response,
|
||||
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
|
||||
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider)),
|
||||
bikesOccupiedResponse.GeneralData,
|
||||
|
@ -85,16 +87,16 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
// Both types bikes could read from copri => update cache
|
||||
server.AddToCache(stationsResponse);
|
||||
server.AddToCache(bikesAvailableResponse);
|
||||
server.AddToCache(bikesOccupiedResponse);
|
||||
Server.AddToCache(stationsResponse);
|
||||
Server.AddToCache(bikesAvailableResponse);
|
||||
Server.AddToCache(bikesOccupiedResponse);
|
||||
|
||||
var exceptions = new[] { stationsResponse?.Exception, bikesAvailableResponse?.Exception, bikesOccupiedResponse?.Exception }.Where(x => x != null).ToArray();
|
||||
|
||||
var stationsMutable = stationsResponse.Response.GetStationsAllMutable();
|
||||
var bikesMutable = UpdaterJSON.GetBikesAll(
|
||||
bikesAvailableResponse.Response,
|
||||
bikesOccupiedResponse.Response,
|
||||
bikesAvailableResponse.Response?.bikes?.Values,
|
||||
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider);
|
||||
|
||||
|
@ -109,58 +111,108 @@ namespace TINK.Model.Connector
|
|||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesOccupiedAsync()
|
||||
{
|
||||
var result = await server.GetBikesOccupied();
|
||||
server.AddToCache(result);
|
||||
return new Result<BikeCollection>(result.Source, result.Response.GetBikesOccupied(Mail, DateTimeProvider), result.GeneralData, result.Exception);
|
||||
var bikesAvailableResponse = await Server.GetBikesAvailable(false);
|
||||
if (bikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| bikesAvailableResponse.Exception != null)
|
||||
{
|
||||
// Bikes available were read from cache ==> get bikes occupied from cache as well to avoid inconsistencies.
|
||||
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available read from cache. Reading bikes occupied from cache as well.");
|
||||
return new Result<BikeCollection>(
|
||||
bikesAvailableResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(
|
||||
bikesAvailableResponse.Response?.bikes?.Values?.Where(bike => bike.GetState() == State.InUseStateEnum.FeedbackPending),
|
||||
(await Server.GetBikesOccupied(true))?.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
bikesAvailableResponse.GeneralData,
|
||||
bikesAvailableResponse.Exception);
|
||||
}
|
||||
|
||||
var bikesOccupiedResponse = await Server.GetBikesOccupied(false);
|
||||
if (bikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| bikesOccupiedResponse.Exception != null)
|
||||
{
|
||||
// Bikes occupied were read from cache ==> get bikes available from cache as well to avoid inconsistencies
|
||||
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes occupied read from cache. Reread bikes available from cache as well.");
|
||||
return new Result<BikeCollection>(
|
||||
bikesOccupiedResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(
|
||||
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values?.Where(bike => bike.GetState() == State.InUseStateEnum.FeedbackPending),
|
||||
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
bikesOccupiedResponse.GeneralData,
|
||||
bikesOccupiedResponse.Exception);
|
||||
}
|
||||
|
||||
// Both types bikes could read from copri => update bikes occupied cache.
|
||||
// // Do not add bikes available to cache because this might lead to conflicts calls GetBikesAsync() and bikes with FeedbackPending state are of no use offline.
|
||||
Server.AddToCache(bikesOccupiedResponse);
|
||||
|
||||
return new Result<BikeCollection>(
|
||||
bikesOccupiedResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(
|
||||
bikesAvailableResponse?.Response.bikes?.Values?.Select(bike => bike)?.Where(bike => bike.GetState() == State.InUseStateEnum.FeedbackPending),
|
||||
bikesOccupiedResponse?.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
bikesOccupiedResponse.GeneralData,
|
||||
bikesOccupiedResponse.Exception);
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes available and bikes occupied. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
{
|
||||
var l_oBikesAvailableResponse = await server.GetBikesAvailable();
|
||||
var bikesAvailableResponse = await Server.GetBikesAvailable();
|
||||
|
||||
if (l_oBikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| l_oBikesAvailableResponse.Exception != null)
|
||||
if (bikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| bikesAvailableResponse.Exception != null)
|
||||
{
|
||||
// Bikes avilable were read from cache ==> get bikes occupied from cache as well to avoid inconsistencies
|
||||
// Bikes available were read from cache ==> get bikes occupied from cache as well to avoid inconsistencies.
|
||||
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available read from cache. Reading bikes occupied from cache as well.");
|
||||
return new Result<BikeCollection>(
|
||||
l_oBikesAvailableResponse.Source,
|
||||
bikesAvailableResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(
|
||||
l_oBikesAvailableResponse.Response,
|
||||
(await server.GetBikesOccupied(true)).Response,
|
||||
bikesAvailableResponse.Response?.bikes?.Values,
|
||||
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
l_oBikesAvailableResponse.GeneralData,
|
||||
l_oBikesAvailableResponse.Exception);
|
||||
bikesAvailableResponse.GeneralData,
|
||||
bikesAvailableResponse.Exception);
|
||||
}
|
||||
|
||||
var l_oBikesOccupiedResponse = await server.GetBikesOccupied();
|
||||
if (l_oBikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| l_oBikesOccupiedResponse.Exception != null)
|
||||
var bikesOccupiedResponse = await Server.GetBikesOccupied();
|
||||
if (bikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| bikesOccupiedResponse.Exception != null)
|
||||
{
|
||||
// Bikes occupied were read from cache ==> get bikes available from cache as well to avoid inconsistencies
|
||||
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes occupied read from cache. Reread bikes available from cache as well.");
|
||||
return new Result<BikeCollection>(
|
||||
l_oBikesOccupiedResponse.Source,
|
||||
bikesOccupiedResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(
|
||||
(await server.GetBikesAvailable(true)).Response,
|
||||
l_oBikesOccupiedResponse.Response,
|
||||
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
|
||||
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
l_oBikesOccupiedResponse.GeneralData,
|
||||
l_oBikesOccupiedResponse.Exception);
|
||||
|
||||
bikesOccupiedResponse.GeneralData,
|
||||
bikesOccupiedResponse.Exception);
|
||||
}
|
||||
|
||||
// Both types bikes could read from copri => update cache
|
||||
server.AddToCache(l_oBikesAvailableResponse);
|
||||
server.AddToCache(l_oBikesOccupiedResponse);
|
||||
Server.AddToCache(bikesAvailableResponse);
|
||||
Server.AddToCache(bikesOccupiedResponse);
|
||||
|
||||
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available and occupied read successfully from server.");
|
||||
return new Result<BikeCollection>(
|
||||
l_oBikesAvailableResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(l_oBikesAvailableResponse.Response, l_oBikesOccupiedResponse.Response, Mail, DateTimeProvider),
|
||||
l_oBikesAvailableResponse.GeneralData,
|
||||
l_oBikesAvailableResponse.Exception != null || l_oBikesOccupiedResponse.Exception != null ? new AggregateException(new[] { l_oBikesAvailableResponse.Exception, l_oBikesOccupiedResponse.Exception }) : null);
|
||||
}
|
||||
bikesAvailableResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(
|
||||
bikesAvailableResponse.Response?.bikes?.Values,
|
||||
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
bikesAvailableResponse.GeneralData,
|
||||
bikesAvailableResponse.Exception != null || bikesOccupiedResponse.Exception != null ? new AggregateException(new[] { bikesAvailableResponse.Exception, bikesOccupiedResponse.Exception }) : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Connector.Updater;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Repository;
|
||||
using TINK.Services.CopriApi;
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
|
@ -20,7 +21,7 @@ namespace TINK.Model.Connector
|
|||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
public Query(ICopriServerBase p_oCopriServer) : base(p_oCopriServer)
|
||||
{
|
||||
server = p_oCopriServer as ICopriServer;
|
||||
server = p_oCopriServer as ICopriServer;
|
||||
if (server == null)
|
||||
{
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {p_oCopriServer.GetType()}.");
|
||||
|
@ -30,11 +31,11 @@ namespace TINK.Model.Connector
|
|||
/// <summary> Gets all stations including postions.</summary>
|
||||
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
|
||||
{
|
||||
var stationsAllResponse = await server.GetStationsAsync();
|
||||
var stationsAllResponse = await server.GetStationsAsync();
|
||||
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
|
||||
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
new StationsAndBikesContainer(stationsAllResponse.GetStationsAllMutable(), bikesAvailableResponse.GetBikesAvailable()),
|
||||
stationsAllResponse.GetGeneralData());
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.Connector.Updater;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Repository;
|
||||
|
||||
|
@ -9,23 +11,23 @@ namespace TINK.Model.Connector
|
|||
/// <summary> Provides query functionality for a logged in user. </summary>
|
||||
public class QueryLoggedIn : BaseLoggedIn, IQuery
|
||||
{
|
||||
/// <summary> Cached copri server. </summary>
|
||||
/// <summary> Copri server. </summary>
|
||||
private readonly ICopriServer server;
|
||||
|
||||
/// <summary>Constructs a copri query object.</summary>
|
||||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
public QueryLoggedIn(ICopriServerBase p_oCopriServer,
|
||||
string p_strSessionCookie,
|
||||
string p_strMail,
|
||||
Func<DateTime> p_oDateTimeProvider) : base(p_oCopriServer, p_strSessionCookie, p_strMail, p_oDateTimeProvider)
|
||||
/// <param name="copriServer">Server which implements communication.</param>
|
||||
public QueryLoggedIn(ICopriServerBase copriServer,
|
||||
string sessionCookie,
|
||||
string mail,
|
||||
Func<DateTime> dateTimeProvider) : base(copriServer, sessionCookie, mail, dateTimeProvider)
|
||||
{
|
||||
server = p_oCopriServer as ICopriServer;
|
||||
server = copriServer as ICopriServer;
|
||||
if (server == null)
|
||||
{
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {p_oCopriServer.GetType()}.");
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {copriServer.GetType()}.");
|
||||
}
|
||||
|
||||
server = p_oCopriServer as ICopriServer;
|
||||
server = copriServer as ICopriServer;
|
||||
}
|
||||
|
||||
/// <summary> Gets all stations including postions.</summary>
|
||||
|
@ -39,30 +41,45 @@ namespace TINK.Model.Connector
|
|||
typeof(CopriCallsMonkeyStore),
|
||||
new StationsAndBikesContainer(
|
||||
stationResponse.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(bikesAvailableResponse, bikesOccupiedResponse, Mail, DateTimeProvider)),
|
||||
UpdaterJSON.GetBikesAll(
|
||||
bikesAvailableResponse?.bikes?.Values,
|
||||
bikesOccupiedResponse?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider)),
|
||||
stationResponse.GetGeneralData());
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied. </summary>
|
||||
/// <summary> Gets bikes occupied and bikes for which feedback is required. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesOccupiedAsync()
|
||||
{
|
||||
var bikesOccupiedResponse = (await server.GetBikesOccupiedAsync());
|
||||
var bikesFeedbackRequired = await server.GetBikesAvailableAsync();
|
||||
var bikesOccupiedResponse = await server.GetBikesOccupiedAsync();
|
||||
|
||||
return new Result<BikeCollection>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
bikesOccupiedResponse.GetBikesOccupied(Mail, DateTimeProvider),
|
||||
UpdaterJSON.GetBikesAll(
|
||||
bikesFeedbackRequired.bikes?.Values?.Select(bike => bike)?.Where(bike => bike.GetState() == State.InUseStateEnum.FeedbackPending),
|
||||
bikesOccupiedResponse?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
bikesOccupiedResponse.GetGeneralData());
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes available and bikes occupied. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
{
|
||||
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
|
||||
var bikesOccupiedResponse = await server.GetBikesOccupiedAsync();
|
||||
|
||||
|
||||
return new Result<BikeCollection>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
UpdaterJSON.GetBikesAll(bikesAvailableResponse, bikesOccupiedResponse, Mail, DateTimeProvider),
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
UpdaterJSON.GetBikesAll(
|
||||
bikesAvailableResponse?.bikes?.Values,
|
||||
bikesOccupiedResponse?.bikes_occupied?.Values,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
bikesAvailableResponse.GetGeneralData());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,26 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Response;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
|
||||
using TINK.Model.Services.CopriApi.ServerUris;
|
||||
using TINK.Model.State;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Response;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary>
|
||||
/// Conversion helper functionality.
|
||||
/// Converts weak typed JSON data (mostly string) to strong typed c# data (base types, enumerations, objects, ...).
|
||||
/// JSON is received from COPRI and deserialized using Json.NET.
|
||||
/// </summary>
|
||||
public static class TextToTypeHelper
|
||||
{
|
||||
/// <summary> Holds the text for demo bikes. </summary>
|
||||
private const string DEMOBIKEMARKER = "DEMO";
|
||||
|
||||
/// <summary> Part text denoting two wheel cargo bike.. </summary>
|
||||
private const string TWOWHEELCARGOMARKERFRAGMENT = "LONG";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position from StationInfo object.
|
||||
/// </summary>
|
||||
|
@ -30,7 +28,7 @@ namespace TINK.Model.Connector
|
|||
/// <returns>Position information.</returns>
|
||||
public static IPosition GetPosition(this StationsAvailableResponse.StationInfo stationInfo)
|
||||
=> GetPosition(stationInfo.gps);
|
||||
|
||||
|
||||
/// <summary> Gets the position from StationInfo object. </summary>
|
||||
/// <param name="authorizationResponse">Object to get information from.</param>
|
||||
/// <returns>Position information.</returns>
|
||||
|
@ -83,7 +81,7 @@ namespace TINK.Model.Connector
|
|||
{
|
||||
try
|
||||
{
|
||||
return stationInfo.station_group.GetGroup();
|
||||
return stationInfo.station_group.GetGroup();
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
{
|
||||
|
@ -92,48 +90,67 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position from StationInfo object.
|
||||
/// Gets the position from BikeInfoBase object.
|
||||
/// </summary>
|
||||
/// <param name="bikeInfo">Object to get information from.</param>
|
||||
/// <returns>Position information.</returns>
|
||||
/// <returns>Rental state.</returns>
|
||||
public static InUseStateEnum GetState(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
var l_oState = bikeInfo.state;
|
||||
var stateText = bikeInfo.state?.ToLower()?.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(l_oState))
|
||||
if (string.IsNullOrEmpty(stateText))
|
||||
{
|
||||
throw new InvalidResponseException<BikeInfoBase>(
|
||||
string.Format("Unknown reservation state detected. Member {0}.{1}.", typeof(BikeInfoBase), nameof(BikeInfoBase.state)),
|
||||
"Bike state must not be empty.",
|
||||
bikeInfo);
|
||||
}
|
||||
|
||||
if (l_oState == "available")
|
||||
if (Enum.TryParse(stateText, out InUseStateEnum state))
|
||||
return state;
|
||||
|
||||
if (stateText == "available")
|
||||
{
|
||||
return InUseStateEnum.Disposable;
|
||||
}
|
||||
else if (l_oState == "reserved" ||
|
||||
l_oState == "requested")
|
||||
else if (stateText == "reserved" ||
|
||||
stateText == "requested")
|
||||
{
|
||||
return InUseStateEnum.Reserved;
|
||||
}
|
||||
else if (l_oState == "booked" ||
|
||||
l_oState == "occupied")
|
||||
else if (stateText == "booked" ||
|
||||
stateText == "occupied")
|
||||
{
|
||||
return InUseStateEnum.Booked;
|
||||
}
|
||||
|
||||
throw new CommunicationException(string.Format("Unknown bike state detected. State is {0}.", l_oState));
|
||||
throw new CommunicationException(string.Format("Unknown bike state detected. State is {0}.", stateText));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position from BikeInfoAvailable object.
|
||||
/// </summary>
|
||||
/// <param name="bikeInfo">Object to get information from.</param>
|
||||
/// <returns>Rental state.</returns>
|
||||
public static InUseStateEnum GetState(this BikeInfoAvailable bikeInfo)
|
||||
{
|
||||
var state = GetState((BikeInfoBase)bikeInfo);
|
||||
|
||||
if (state != InUseStateEnum.Disposable)
|
||||
return state;
|
||||
|
||||
return bikeInfo.GetIsFeedbackPending()
|
||||
? InUseStateEnum.FeedbackPending
|
||||
: InUseStateEnum.Disposable;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the from date information from JSON.
|
||||
/// </summary>
|
||||
/// <param name="bikeInfo">JSON to get information from..</param>
|
||||
/// <returns>From information.</returns>
|
||||
/// <returns>From information if bike hold this information 0001-01-01 (DateTime.MinValue) otherwise.</returns>
|
||||
public static DateTime GetFrom(this BikeInfoReservedOrBooked bikeInfo)
|
||||
{
|
||||
return DateTime.Parse(bikeInfo.start_time);
|
||||
}
|
||||
=> DateTime.TryParse(bikeInfo?.start_time, out DateTime dateFrom) ? dateFrom : DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the bike is a trike or not.
|
||||
|
@ -144,7 +161,7 @@ namespace TINK.Model.Connector
|
|||
{
|
||||
return bikeInfo?.description != null
|
||||
? bikeInfo.description.ToUpper().Contains(DEMOBIKEMARKER)
|
||||
: (bool?) null;
|
||||
: (bool?)null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -178,7 +195,7 @@ namespace TINK.Model.Connector
|
|||
/// <returns>From information.</returns>
|
||||
public static bool GetIsBluetoothLockBike(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
return !string.IsNullOrEmpty(bikeInfo.system)
|
||||
return !string.IsNullOrEmpty(bikeInfo.system)
|
||||
&& bikeInfo.system.ToUpper().StartsWith("ILOCKIT");
|
||||
}
|
||||
|
||||
|
@ -208,7 +225,7 @@ namespace TINK.Model.Connector
|
|||
/// <summary> Gets whether the bike has a bord computer or not. </summary>
|
||||
/// <param name="bikeInfo">JSON to get information from..</param>
|
||||
/// <returns>From information.</returns>
|
||||
public static int GetBluetoothLockId(this BikeInfoAvailable bikeInfo)
|
||||
public static int GetBluetoothLockId(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
return TextToLockItTypeHelper.GetBluetoothLockId(bikeInfo?.Ilockit_ID);
|
||||
}
|
||||
|
@ -216,7 +233,7 @@ namespace TINK.Model.Connector
|
|||
/// <summary> Gets whether the bike has a bord computer or not. </summary>
|
||||
/// <param name="bikeInfo">JSON to get information from..</param>
|
||||
/// <returns>From information.</returns>
|
||||
public static Guid GetBluetoothLockGuid(this BikeInfoAvailable bikeInfo)
|
||||
public static Guid GetBluetoothLockGuid(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
// return new Guid("00000000-0000-0000-0000-e57e6b9aee16");
|
||||
return Guid.TryParse(bikeInfo?.Ilockit_GUID, out Guid lockGuid)
|
||||
|
@ -266,38 +283,9 @@ namespace TINK.Model.Connector
|
|||
/// <param name="bikeInfo">JSON to get information from..</param>
|
||||
/// <returns>From information.</returns>
|
||||
public static WheelType? GetWheelType(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
var l_oDescription = bikeInfo.description;
|
||||
|
||||
if (l_oDescription == null)
|
||||
{
|
||||
// Can not get type of wheel if description text is empty.
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (WheelType l_oWheelType in Enum.GetValues(typeof(WheelType)))
|
||||
{
|
||||
|
||||
if (l_oDescription.ToUpper().Contains(l_oWheelType.ToString().ToUpper()))
|
||||
{
|
||||
return l_oWheelType;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for custom value "Long".
|
||||
if (l_oDescription.ToUpper().Contains(TWOWHEELCARGOMARKERFRAGMENT))
|
||||
{
|
||||
return WheelType.Two;
|
||||
}
|
||||
|
||||
// Check for Stadrad.
|
||||
if (GetTypeOfBike(bikeInfo) == TypeOfBike.Citybike)
|
||||
{
|
||||
return WheelType.Two;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
=> Enum.TryParse(bikeInfo?.bike_type?.wheels, true, out WheelType wheelType)
|
||||
? wheelType
|
||||
: (WheelType?)null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of bike.
|
||||
|
@ -305,25 +293,9 @@ namespace TINK.Model.Connector
|
|||
/// <param name="bikeInfo">Object to get bike type from.</param>
|
||||
/// <returns>Type of bike.</returns>
|
||||
public static TypeOfBike? GetTypeOfBike(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
var l_oDescription = bikeInfo?.description;
|
||||
|
||||
if (l_oDescription == null)
|
||||
{
|
||||
// Can not get type of wheel if description text is empty.
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (TypeOfBike l_oTypeOfBike in Enum.GetValues(typeof(TypeOfBike)))
|
||||
{
|
||||
if (l_oDescription.ToUpper().Contains(l_oTypeOfBike.GetCopriText().ToUpper()))
|
||||
{
|
||||
return l_oTypeOfBike;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
=> Enum.TryParse(bikeInfo?.bike_type?.category, true, out TypeOfBike typeOfBike)
|
||||
? typeOfBike
|
||||
: (TypeOfBike?)null;
|
||||
|
||||
/// <summary> Get position from a ,- separated string. </summary>
|
||||
/// <param name="gps">Text to extract positon from.</param>
|
||||
|
@ -338,48 +310,33 @@ namespace TINK.Model.Connector
|
|||
/// <returns>Position object.</returns>
|
||||
public static Map.IMapSpan GetMapSpan(this MapSpan mapSpan)
|
||||
=> Map.MapSpanFactory.Create(
|
||||
GetPosition(mapSpan?.center),
|
||||
double.TryParse(mapSpan?.radius, NumberStyles.Float, CultureInfo.InvariantCulture, out double radius) ? radius: double.NaN);
|
||||
GetPosition(mapSpan?.center),
|
||||
double.TryParse(mapSpan?.radius, NumberStyles.Float, CultureInfo.InvariantCulture, out double radius) ? radius : double.NaN);
|
||||
|
||||
|
||||
/// <summary> Gets text of bike from. </summary>
|
||||
/// <param name="p_eType">Type to get text for.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetCopriText(this TypeOfBike p_eType)
|
||||
{
|
||||
switch (p_eType)
|
||||
{
|
||||
case TypeOfBike.Citybike:
|
||||
return "Stadtrad";
|
||||
|
||||
default:
|
||||
return p_eType.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the locking state from response.
|
||||
/// </summary>
|
||||
/// <param name="bikeInfo"> Response locking state from.</param>
|
||||
/// <returns>Locking state</returns>
|
||||
public static Bikes.Bike.CopriLock.LockingState GetCopriLockingState(this BikeInfoBase bikeInfo)
|
||||
public static Bikes.BikeInfoNS.CopriLock.LockingState GetCopriLockingState(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(bikeInfo?.lock_state))
|
||||
return Bikes.Bike.CopriLock.LockingState.UnknownDisconnected;
|
||||
return Bikes.BikeInfoNS.CopriLock.LockingState.UnknownDisconnected;
|
||||
|
||||
if (bikeInfo.lock_state.ToUpper().Trim() == "locked".ToUpper())
|
||||
return Bikes.Bike.CopriLock.LockingState.Closed;
|
||||
return Bikes.BikeInfoNS.CopriLock.LockingState.Closed;
|
||||
|
||||
if (bikeInfo.lock_state.ToUpper().Trim() == "locking".ToUpper())
|
||||
return Bikes.Bike.CopriLock.LockingState.Closing;
|
||||
return Bikes.BikeInfoNS.CopriLock.LockingState.Closing;
|
||||
|
||||
if (bikeInfo.lock_state.ToUpper().Trim() == "unlocked".ToUpper())
|
||||
return Bikes.Bike.CopriLock.LockingState.Open;
|
||||
return Bikes.BikeInfoNS.CopriLock.LockingState.Open;
|
||||
|
||||
if (bikeInfo.lock_state.ToUpper().Trim() == "unlocking".ToUpper())
|
||||
return Bikes.Bike.CopriLock.LockingState.Opening;
|
||||
return Bikes.BikeInfoNS.CopriLock.LockingState.Opening;
|
||||
|
||||
return Bikes.Bike.CopriLock.LockingState.UnknownDisconnected;
|
||||
return Bikes.BikeInfoNS.CopriLock.LockingState.UnknownDisconnected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -409,8 +366,15 @@ namespace TINK.Model.Connector
|
|||
/// <param name="response">Response to get version info from.</param>
|
||||
/// <returns>COPRI version</returns>
|
||||
public static Version GetCopriVersion(this CopriVersion response)
|
||||
=> response.TryGetCopriVersion(out Version copriVersion)
|
||||
=> response.TryGetCopriVersion(out Version copriVersion)
|
||||
? copriVersion
|
||||
: throw new InvalidResponseException($"Can not get version info from copri response {response?.copri_version}.");
|
||||
|
||||
/// <summary>
|
||||
/// Gets bike advanced bike state. If entry Co2Saving exists feedback is required.
|
||||
/// </summary>
|
||||
/// <param name="bike">Bike get to state from.</param>
|
||||
public static bool GetIsFeedbackPending(this BikeInfoAvailable bike)
|
||||
=> bike.co2saving != null;
|
||||
}
|
||||
}
|
||||
|
|
330
TINKLib/Model/Connector/Updater/BikeInfoFactory.cs
Normal file
330
TINKLib/Model/Connector/Updater/BikeInfoFactory.cs
Normal file
|
@ -0,0 +1,330 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Serilog;
|
||||
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using TINK.Model.State;
|
||||
using TINK.Repository.Response;
|
||||
using BikeExtension = TINK.Model.Bikes.BikeInfoNS.BikeNS.BikeExtension;
|
||||
using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
|
||||
|
||||
namespace TINK.Model.Connector.Updater
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs bike info instances/ bike info derived instances.
|
||||
/// </summary>
|
||||
public static class BikeInfoFactory
|
||||
{
|
||||
/// <summary> Set default lock type to . </summary>
|
||||
public static LockModel DEFAULTLOCKMODEL = LockModel.Sigo;
|
||||
|
||||
/// <summary> Creates a bike info object from copri response. </summary>
|
||||
/// <param name="bikeInfo">Copri response for a disposable bike. </param>
|
||||
public static BikeInfo Create(BikeInfoAvailable bikeInfo)
|
||||
{
|
||||
if (bikeInfo == null) throw new ArgumentNullException(nameof(bikeInfo));
|
||||
|
||||
var lockModel = bikeInfo.GetLockModel();
|
||||
|
||||
if (lockModel.HasValue
|
||||
&& lockModel.Value == LockModel.BordComputer)
|
||||
{
|
||||
// Manual lock bikes are no more supported.
|
||||
Log.Error(
|
||||
$"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. " +
|
||||
"Manual lock bikes are no more supported." +
|
||||
$"Bike number: {bikeInfo.bike}{(bikeInfo.station != null ? $"station number {bikeInfo.station}" : string.Empty)}."
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (bikeInfo.GetState())
|
||||
{
|
||||
case InUseStateEnum.Disposable:
|
||||
case InUseStateEnum.FeedbackPending:
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Unexpected state {bikeInfo.GetState()} detected.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(bikeInfo.station))
|
||||
{
|
||||
// Bike available must always have a station id because bikes can only be returned at a station.
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. No station info set.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var lockType = lockModel.HasValue
|
||||
? BikeExtension.GetLockType(lockModel.Value)
|
||||
: BikeExtension.GetLockType(DEFAULTLOCKMODEL); // Map bikes without "system"- entry in response to backend- locks.
|
||||
|
||||
try
|
||||
{
|
||||
switch (lockType)
|
||||
{
|
||||
case LockType.Backend:
|
||||
return new Bikes.BikeInfoNS.CopriLock.BikeInfo(
|
||||
new Bike(
|
||||
bikeInfo.bike,
|
||||
LockModel.Sigo,
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description),
|
||||
DriveFactory.Create(bikeInfo?.bike_type),
|
||||
bikeInfo.station,
|
||||
new Bikes.BikeInfoNS.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
|
||||
bikeInfo.GetState() == InUseStateEnum.FeedbackPending,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null
|
||||
? RentalDescriptionFactory.Create(bikeInfo.rental_description)
|
||||
: TariffDescriptionFactory.Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription) null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
miniSurvey: bikeInfo.user_miniquery != null
|
||||
? new MiniSurveyModel(new Dictionary<string, IQuestionModel> {
|
||||
{ "q1", new QuestionModel()} // Add a dummy querry. Querries are not yet read from COPRI but compiled into the app.
|
||||
})
|
||||
: new MiniSurveyModel(),
|
||||
co2Saving: bikeInfo.co2saving);
|
||||
|
||||
case LockType.Bluethooth:
|
||||
return new Bikes.BikeInfoNS.BluetoothLock.BikeInfo(
|
||||
new Bike(
|
||||
bikeInfo.bike,
|
||||
LockModel.ILockIt,
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description),
|
||||
DriveFactory.Create(bikeInfo?.bike_type),
|
||||
bikeInfo.GetBluetoothLockId(),
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null
|
||||
? RentalDescriptionFactory.Create(bikeInfo.rental_description)
|
||||
: TariffDescriptionFactory.Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup());
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported lock type {lockType} detected.");
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// Contructor reported invalid arguemts (missing lock id, ....).
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Invalid response detected. Available bike with id {bikeInfo.bike} skipped. {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Creates a bike info object from copri response. </summary>
|
||||
/// <param name="bikeInfo">Copri response. </param>
|
||||
/// <param name="mailAddress">Mail address of user.</param>
|
||||
/// <param name="dateTimeProvider">Date and time provider function.</param>
|
||||
public static BikeInfo Create(
|
||||
BikeInfoReservedOrBooked bikeInfo,
|
||||
string mailAddress,
|
||||
Func<DateTime> dateTimeProvider)
|
||||
{
|
||||
if (bikeInfo == null) throw new ArgumentNullException(nameof(bikeInfo));
|
||||
|
||||
var lockModel = bikeInfo.GetLockModel();
|
||||
|
||||
if (lockModel.HasValue
|
||||
&& lockModel.Value == LockModel.BordComputer)
|
||||
{
|
||||
// Manual lock bikes are no more supported.
|
||||
Log.Error(
|
||||
$"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. " +
|
||||
"Manual lock bikes are no more supported." +
|
||||
$"Bike number: {bikeInfo.bike}{(bikeInfo.station != null ? $", station number {bikeInfo.station}" : string.Empty)}."
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
var lockType = lockModel.HasValue
|
||||
? BikeExtension.GetLockType(lockModel.Value)
|
||||
: BikeExtension.GetLockType(DEFAULTLOCKMODEL); // Map bikes without "system"- entry in response to backend- locks.
|
||||
|
||||
// Check if bike is a bluetooth lock bike.
|
||||
int lockSerial = bikeInfo.GetBluetoothLockId();
|
||||
Guid lockGuid = bikeInfo.GetBluetoothLockGuid();
|
||||
|
||||
switch (bikeInfo.GetState())
|
||||
{
|
||||
case InUseStateEnum.Reserved:
|
||||
try
|
||||
{
|
||||
switch (lockType)
|
||||
{
|
||||
case LockType.Bluethooth:
|
||||
return new Bikes.BikeInfoNS.BluetoothLock.BikeInfo(
|
||||
new Bike(
|
||||
bikeInfo.bike,
|
||||
LockModel.ILockIt,
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description),
|
||||
DriveFactory.Create(bikeInfo?.bike_type),
|
||||
lockSerial,
|
||||
lockGuid,
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null
|
||||
? RentalDescriptionFactory.Create(bikeInfo.rental_description)
|
||||
: TariffDescriptionFactory.Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup());
|
||||
|
||||
case LockType.Backend:
|
||||
return new Bikes.BikeInfoNS.CopriLock.BikeInfo(
|
||||
new Bike(
|
||||
bikeInfo.bike,
|
||||
LockModel.Sigo,
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description),
|
||||
DriveFactory.Create(bikeInfo?.bike_type),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
new Bikes.BikeInfoNS.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null
|
||||
? RentalDescriptionFactory.Create(bikeInfo.rental_description)
|
||||
: TariffDescriptionFactory.Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup());
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported lock type {lockType} detected.");
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// Contructor reported invalid arguemts (missing lock id, ....).
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoReservedOrBooked)} argument. Invalid response detected. Reserved bike with id {bikeInfo.bike} skipped. {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
case InUseStateEnum.Booked:
|
||||
try
|
||||
{
|
||||
switch (lockModel)
|
||||
{
|
||||
case LockModel.ILockIt:
|
||||
return new Bikes.BikeInfoNS.BluetoothLock.BikeInfo(
|
||||
new Bike(
|
||||
bikeInfo.bike,
|
||||
LockModel.ILockIt,
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description),
|
||||
DriveFactory.Create(bikeInfo?.bike_type),
|
||||
lockSerial,
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null
|
||||
? RentalDescriptionFactory.Create(bikeInfo.rental_description)
|
||||
: TariffDescriptionFactory.Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup());
|
||||
|
||||
case LockModel.BordComputer:
|
||||
return new BikeInfo(
|
||||
new Bike(
|
||||
bikeInfo.bike,
|
||||
LockModel.BordComputer,
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description),
|
||||
DriveFactory.Create(bikeInfo?.bike_type),
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null
|
||||
? RentalDescriptionFactory.Create(bikeInfo.rental_description)
|
||||
: TariffDescriptionFactory.Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode);
|
||||
default:
|
||||
return new Bikes.BikeInfoNS.CopriLock.BikeInfo(
|
||||
new Bike(
|
||||
bikeInfo.bike,
|
||||
LockModel.Sigo,
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description),
|
||||
DriveFactory.Create(bikeInfo?.bike_type),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
new Bikes.BikeInfoNS.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null
|
||||
? RentalDescriptionFactory.Create(bikeInfo.rental_description)
|
||||
: TariffDescriptionFactory.Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup());
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// Contructor reported invalid arguemts (missing lock id, ....).
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoReservedOrBooked)} argument. Invalid response detected. Booked bike with id {bikeInfo.bike} skipped. {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
default:
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Unexpected state {bikeInfo.GetState()} detected.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using TINK.Repository.Response;
|
||||
|
||||
namespace TINK.Model.Connector.Updater
|
||||
{
|
||||
public static class BookingFinishedModelFactory
|
||||
{
|
||||
/// <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)
|
||||
{
|
||||
var bookingFinished = new BookingFinishedModel
|
||||
{
|
||||
Co2Saving = response?.co2saving
|
||||
};
|
||||
|
||||
if (response?.user_miniquery == null)
|
||||
|
||||
{
|
||||
return bookingFinished;
|
||||
}
|
||||
|
||||
var miniquery = response.user_miniquery;
|
||||
bookingFinished.MiniSurvey = new MiniSurveyModel
|
||||
{
|
||||
Title = miniquery.title,
|
||||
Subtitle = miniquery.subtitle,
|
||||
Footer = miniquery.footer
|
||||
};
|
||||
|
||||
foreach (var question in miniquery?.questions?.OrderBy(x => x.Key) ?? new Dictionary<string, MiniSurveyResponse.Question>().OrderBy(x => x.Key))
|
||||
{
|
||||
if (string.IsNullOrEmpty(question.Key.Trim())
|
||||
|| question.Value.query == null)
|
||||
{
|
||||
// Skip invalid entries.
|
||||
continue;
|
||||
}
|
||||
|
||||
bookingFinished.MiniSurvey.Questions.Add(
|
||||
question.Key,
|
||||
new QuestionModel());
|
||||
}
|
||||
|
||||
return bookingFinished;
|
||||
}
|
||||
}
|
||||
}
|
45
TINKLib/Model/Connector/Updater/DriveFactory.cs
Normal file
45
TINKLib/Model/Connector/Updater/DriveFactory.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using TINK.Model.Bikes.BikeInfoNS.DriveNS;
|
||||
using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS;
|
||||
using TINK.Model.Bikes.BikeInfoNS.DriveNS.EngineNS;
|
||||
using TINK.Repository.Response;
|
||||
|
||||
namespace TINK.Model.Connector.Updater
|
||||
{
|
||||
public static class DriveFactory
|
||||
{
|
||||
public static Drive Create(this BikeType bikeType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(bikeType?.engine?.manufacturer))
|
||||
{
|
||||
// Bike is has no engine
|
||||
return new Drive();
|
||||
}
|
||||
|
||||
// Bike is a pedelec.
|
||||
return new Drive(
|
||||
new Engine(bikeType?.engine?.manufacturer),
|
||||
new Battery.Builder
|
||||
{
|
||||
CurrentChargePercent = double.TryParse(bikeType?.battery?.charge_current_percent, out double currentChargePercent)
|
||||
? currentChargePercent
|
||||
: double.NaN,
|
||||
|
||||
CurrentChargeBars = int.TryParse(bikeType?.battery?.charge_current_bars, out int currentChargeBars)
|
||||
? (int?)currentChargeBars
|
||||
: null,
|
||||
|
||||
MaxChargeBars = int.TryParse(bikeType?.battery?.charge_max_bars, out int maxChargeBars)
|
||||
? (int?)maxChargeBars
|
||||
: null,
|
||||
|
||||
IsBackendAccessible = bikeType?.battery?.backend_accessible != null && int.TryParse(bikeType.battery.backend_accessible, out int accessible)
|
||||
? (bool?)(accessible > 0)
|
||||
: null,
|
||||
|
||||
IsHidden = bikeType?.battery?.hidden != null && int.TryParse(bikeType.battery.hidden, out int hidden)
|
||||
? (bool?)(hidden > 0)
|
||||
: null
|
||||
}.Build());
|
||||
}
|
||||
}
|
||||
}
|
63
TINKLib/Model/Connector/Updater/RentalDescriptionFactory.cs
Normal file
63
TINKLib/Model/Connector/Updater/RentalDescriptionFactory.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TINK.Model.Connector.Updater
|
||||
{
|
||||
public static class RentalDescriptionFactory
|
||||
{
|
||||
/// <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.BikeInfoNS.RentalDescription Create(this Repository.Response.RentalDescription rentalDesciption)
|
||||
{
|
||||
Bikes.BikeInfoNS.RentalDescription.TariffElement CreateTarifEntry(string[] elementValue)
|
||||
{
|
||||
return new Bikes.BikeInfoNS.RentalDescription.TariffElement
|
||||
{
|
||||
Description = elementValue != null && elementValue.Length > 0 ? elementValue[0] : string.Empty,
|
||||
Value = elementValue != null && elementValue.Length > 1 ? elementValue[1] : string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
Bikes.BikeInfoNS.RentalDescription.InfoElement CreateInfoElement(string[] elementValue)
|
||||
{
|
||||
return new Bikes.BikeInfoNS.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.BikeInfoNS.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.BikeInfoNS.RentalDescription.InfoElement>();
|
||||
|
||||
var bike = new Bikes.BikeInfoNS.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;
|
||||
}
|
||||
}
|
||||
}
|
80
TINKLib/Model/Connector/Updater/TariffDescriptionFactory.cs
Normal file
80
TINKLib/Model/Connector/Updater/TariffDescriptionFactory.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using System.Globalization;
|
||||
using TINK.MultilingualResources;
|
||||
using TINK.Repository.Response;
|
||||
|
||||
namespace TINK.Model.Connector.Updater
|
||||
{
|
||||
public static class TariffDescriptionFactory
|
||||
{
|
||||
/// <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.BikeInfoNS.RentalDescription Create(this TariffDescription tariffDesciption)
|
||||
{
|
||||
var bike = new Bikes.BikeInfoNS.RentalDescription
|
||||
{
|
||||
Name = tariffDesciption?.name,
|
||||
#if USCSHARP9
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : null,
|
||||
#else
|
||||
Id = int.TryParse(tariffDesciption?.number, out int number) ? number : (int?)null,
|
||||
#endif
|
||||
};
|
||||
|
||||
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.BikeInfoNS.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.BikeInfoNS.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.BikeInfoNS.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.BikeInfoNS.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.BikeInfoNS.RentalDescription.InfoElement { Key = "AGB", Value = tariffDesciption.operator_agb });
|
||||
}
|
||||
|
||||
return bike;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,26 +1,19 @@
|
|||
using System;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Station;
|
||||
using TINK.Repository.Response;
|
||||
using TINK.Model.User.Account;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Model.State;
|
||||
using TINK.Repository.Exception;
|
||||
using Serilog;
|
||||
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
using IBikeInfoMutable = TINK.Model.Bikes.Bike.BC.IBikeInfoMutable;
|
||||
using BikeExtension = TINK.Model.Bikes.Bike.BikeExtension;
|
||||
|
||||
using System.Globalization;
|
||||
using TINK.Model.Bikes;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.Station;
|
||||
using TINK.Model.Station.Operator;
|
||||
using Xamarin.Forms;
|
||||
using System.Linq;
|
||||
using TINK.Model.MiniSurvey;
|
||||
using TINK.Model.User.Account;
|
||||
using TINK.Repository.Exception;
|
||||
using TINK.Repository.Response;
|
||||
using TINK.Services.CopriApi;
|
||||
using TINK.MultilingualResources;
|
||||
using Xamarin.Forms;
|
||||
using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
|
||||
using IBikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.IBikeInfoMutable;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
namespace TINK.Model.Connector.Updater
|
||||
{
|
||||
/// <summary>
|
||||
/// Connects TINK app to copri using JSON as input data format.
|
||||
|
@ -33,11 +26,12 @@ namespace TINK.Model.Connector
|
|||
/// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
|
||||
public static void Load(
|
||||
this IBikeInfoMutable bike,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel notifyLevel)
|
||||
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel)
|
||||
{
|
||||
|
||||
bike.State.Load(InUseStateEnum.Disposable, notifyLevel: notifyLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all statsion for station provider and add them into station list.
|
||||
/// </summary>
|
||||
|
@ -132,9 +126,9 @@ namespace TINK.Model.Connector
|
|||
this IBikeInfoMutable bike,
|
||||
BikeInfoReservedOrBooked bikeInfo,
|
||||
string mailAddress,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.Bike.BC.NotifyPropertyChangedLevel.All)
|
||||
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.All)
|
||||
{
|
||||
if (bike is Bike.BluetoothLock.BikeInfoMutable btBikeInfo)
|
||||
if (bike is Bikes.BikeInfoNS.BluetoothLock.BikeInfoMutable btBikeInfo)
|
||||
{
|
||||
btBikeInfo.LockInfo.Load(
|
||||
bikeInfo.GetBluetoothLockId(),
|
||||
|
@ -181,13 +175,11 @@ namespace TINK.Model.Connector
|
|||
/// <returns>New collection of available bikes.</returns>
|
||||
public static BikeCollection GetBikesAvailable(
|
||||
this BikesAvailableResponse bikesAvailableResponse)
|
||||
{
|
||||
return GetBikesAll(
|
||||
bikesAvailableResponse,
|
||||
new BikesReservedOccupiedResponse(), // There are no occupied bikes.
|
||||
=> GetBikesAll(
|
||||
bikesAvailableResponse?.bikes?.Values,
|
||||
new BikesReservedOccupiedResponse()?.bikes_occupied?.Values, // There are no occupied bikes.
|
||||
string.Empty,
|
||||
() => DateTime.Now);
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied from copri server response. </summary>
|
||||
/// <param name="p_oBikesAvailable">Response to create bikes from.</param>
|
||||
|
@ -198,18 +190,19 @@ namespace TINK.Model.Connector
|
|||
Func<DateTime> dateTimeProvider)
|
||||
{
|
||||
return GetBikesAll(
|
||||
new BikesAvailableResponse(),
|
||||
bikesOccupiedResponse,
|
||||
new BikesAvailableResponse()?.bikes?.Values,
|
||||
bikesOccupiedResponse?.bikes_occupied?.Values,
|
||||
mail,
|
||||
dateTimeProvider);
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied from copri server response. </summary>
|
||||
/// <param name="p_oBikesAvailable">Response to create bikes from.</param>
|
||||
/// <param name="bikesAvailable">Response to create bikes available from.</param>
|
||||
/// <param name="bikesOccupied">Response to create bikes occupied from.</param>
|
||||
/// <returns>New collection of occupied bikes.</returns>
|
||||
public static BikeCollection GetBikesAll(
|
||||
BikesAvailableResponse bikesAvailableResponse,
|
||||
BikesReservedOccupiedResponse bikesOccupiedResponse,
|
||||
IEnumerable<BikeInfoAvailable> bikesAvailable,
|
||||
IEnumerable<BikeInfoReservedOrBooked> bikesOccupied,
|
||||
string mail,
|
||||
Func<DateTime> dateTimeProvider)
|
||||
{
|
||||
|
@ -217,10 +210,9 @@ namespace TINK.Model.Connector
|
|||
var duplicates = new Dictionary<string, BikeInfo>();
|
||||
|
||||
// Get bikes from Copri/ file/ memory, ....
|
||||
if (bikesAvailableResponse != null
|
||||
&& bikesAvailableResponse.bikes != null)
|
||||
if (bikesAvailable != null)
|
||||
{
|
||||
foreach (var bikeInfoResponse in bikesAvailableResponse.bikes.Values)
|
||||
foreach (var bikeInfoResponse in bikesAvailable)
|
||||
{
|
||||
var bikeInfo = BikeInfoFactory.Create(bikeInfoResponse);
|
||||
if (bikeInfo == null)
|
||||
|
@ -247,10 +239,9 @@ namespace TINK.Model.Connector
|
|||
}
|
||||
|
||||
// Get bikes from Copri/ file/ memory, ....
|
||||
if (bikesOccupiedResponse != null
|
||||
&& bikesOccupiedResponse.bikes_occupied != null)
|
||||
if (bikesOccupied != null)
|
||||
{
|
||||
foreach (var bikeInfoResponse in bikesOccupiedResponse.bikes_occupied.Values)
|
||||
foreach (var bikeInfoResponse in bikesOccupied)
|
||||
{
|
||||
BikeInfo bikeInfo = BikeInfoFactory.Create(
|
||||
bikeInfoResponse,
|
||||
|
@ -288,437 +279,4 @@ namespace TINK.Model.Connector
|
|||
return new BikeCollection(bikesDictionary);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs bike info instances/ bike info derived instances.
|
||||
/// </summary>
|
||||
public static class BikeInfoFactory
|
||||
{
|
||||
/// <summary> Set default lock type to . </summary>
|
||||
public static LockModel DEFAULTLOCKMODEL = LockModel.Sigo;
|
||||
|
||||
/// <summary> Creates a bike info object from copri response. </summary>
|
||||
/// <param name="bikeInfo">Copri response for a disposable bike. </param>
|
||||
public static BikeInfo Create(BikeInfoAvailable bikeInfo)
|
||||
{
|
||||
var lockModel = bikeInfo.GetLockModel();
|
||||
|
||||
if (lockModel.HasValue
|
||||
&& lockModel.Value == LockModel.BordComputer)
|
||||
{
|
||||
// Manual lock bikes are no more supported.
|
||||
Log.Error(
|
||||
$"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. " +
|
||||
"Manual lock bikes are no more supported." +
|
||||
$"Bike number: {bikeInfo.bike}{(bikeInfo.station != null ? $"station number {bikeInfo.station}" : string.Empty)}."
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (bikeInfo.GetState())
|
||||
{
|
||||
case InUseStateEnum.Disposable:
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Unexpected state {bikeInfo.GetState()} detected.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(bikeInfo.station))
|
||||
{
|
||||
// Bike available must always have a station id because bikes can only be returned at a station.
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. No station info set.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var lockType = lockModel.HasValue
|
||||
? BikeExtension.GetLockType(lockModel.Value)
|
||||
: BikeExtension.GetLockType(DEFAULTLOCKMODEL); // Map bikes without "system"- entry in response to backend- locks.
|
||||
|
||||
try
|
||||
{
|
||||
switch (lockType)
|
||||
{
|
||||
case LockType.Backend:
|
||||
return new Bike.CopriLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.station,
|
||||
new Bikes.Bike.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState()}.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription) null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
|
||||
case LockType.Bluethooth:
|
||||
return new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetBluetoothLockId(),
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported lock type {lockType} detected.");
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// Contructor reported invalid arguemts (missing lock id, ....).
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Invalid response detected. Available bike with id {bikeInfo.bike} skipped. {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Creates a bike info object from copri response. </summary>
|
||||
/// <param name="bikeInfo">Copri response. </param>
|
||||
/// <param name="mailAddress">Mail address of user.</param>
|
||||
/// <param name="dateTimeProvider">Date and time provider function.</param>
|
||||
public static BikeInfo Create(
|
||||
BikeInfoReservedOrBooked bikeInfo,
|
||||
string mailAddress,
|
||||
Func<DateTime> dateTimeProvider)
|
||||
{
|
||||
var lockModel = bikeInfo.GetLockModel();
|
||||
|
||||
if (lockModel.HasValue
|
||||
&& lockModel.Value == LockModel.BordComputer)
|
||||
{
|
||||
// Manual lock bikes are no more supported.
|
||||
Log.Error(
|
||||
$"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. " +
|
||||
"Manual lock bikes are no more supported." +
|
||||
$"Bike number: {bikeInfo.bike}{(bikeInfo.station != null ? $", station number {bikeInfo.station}" : string.Empty)}."
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
var lockType = lockModel.HasValue
|
||||
? BikeExtension.GetLockType(lockModel.Value)
|
||||
: BikeExtension.GetLockType(DEFAULTLOCKMODEL); // Map bikes without "system"- entry in response to backend- locks.
|
||||
|
||||
// Check if bike is a bluetooth lock bike.
|
||||
int lockSerial = bikeInfo.GetBluetoothLockId();
|
||||
Guid lockGuid = bikeInfo.GetBluetoothLockGuid();
|
||||
|
||||
switch (bikeInfo.GetState())
|
||||
{
|
||||
case InUseStateEnum.Reserved:
|
||||
try
|
||||
{
|
||||
switch (lockType)
|
||||
{
|
||||
case LockType.Bluethooth:
|
||||
return new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
lockGuid,
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
|
||||
case LockType.Backend:
|
||||
return new Bike.CopriLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
new Bikes.Bike.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported lock type {lockType} detected.");
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// Contructor reported invalid arguemts (missing lock id, ....).
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoReservedOrBooked)} argument. Invalid response detected. Reserved bike with id {bikeInfo.bike} skipped. {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
case InUseStateEnum.Booked:
|
||||
try
|
||||
{
|
||||
switch (lockModel)
|
||||
{
|
||||
case LockModel.ILockIt:
|
||||
return new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
|
||||
case LockModel.BordComputer:
|
||||
return new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
LockModel.BordComputer,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode);
|
||||
default:
|
||||
return new Bike.CopriLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
new Bikes.Bike.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
bikeInfo.rental_description != null ? Create(bikeInfo.rental_description) : Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description);
|
||||
}
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// Contructor reported invalid arguemts (missing lock id, ....).
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoReservedOrBooked)} argument. Invalid response detected. Booked bike with id {bikeInfo.bike} skipped. {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
default:
|
||||
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Unexpected state {bikeInfo.GetState()} detected.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
var bike = new Bikes.Bike.RentalDescription
|
||||
{
|
||||
Name = tariffDesciption?.name,
|
||||
#if USCSHARP9
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : null,
|
||||
#else
|
||||
Id = int.TryParse(tariffDesciption?.number, out int number) ? number : (int?)null,
|
||||
#endif
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
var bookingFinished = new BookingFinishedModel
|
||||
{
|
||||
Co2Saving = response?.co2saving
|
||||
};
|
||||
|
||||
if (response?.user_miniquery == null)
|
||||
|
||||
{
|
||||
return bookingFinished;
|
||||
}
|
||||
|
||||
var miniquery = response.user_miniquery;
|
||||
bookingFinished.MiniSurvey = new MiniSurveyModel
|
||||
{
|
||||
Title = miniquery.title,
|
||||
Subtitle = miniquery.subtitle,
|
||||
Footer = miniquery.footer
|
||||
};
|
||||
|
||||
foreach (var question in miniquery?.questions?.OrderBy(x => x.Key) ?? new Dictionary<string, MiniSurveyResponse.Question>().OrderBy(x => x.Key))
|
||||
{
|
||||
if (string.IsNullOrEmpty(question.Key.Trim())
|
||||
|| question.Value.query == null)
|
||||
{
|
||||
// Skip invalid entries.
|
||||
continue;
|
||||
}
|
||||
|
||||
bookingFinished.MiniSurvey.Questions.Add(
|
||||
question.Key,
|
||||
new MiniSurveyModel.QuestionModel());
|
||||
}
|
||||
|
||||
return bookingFinished;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue