mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-04-19 03:27:29 +02:00
Initial version.
This commit is contained in:
parent
193aaa1a56
commit
b72c67a53e
228 changed files with 25924 additions and 0 deletions
141
TINKLib/Model/Connector/Command/Command.cs
Normal file
141
TINKLib/Model/Connector/Command/Command.cs
Normal file
|
@ -0,0 +1,141 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Repository;
|
||||
using TINK.Model.Repository.Request;
|
||||
using TINK.Model.Repository.Response;
|
||||
using TINK.Model.User.Account;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
public class Command : Base, ICommand
|
||||
{
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
public bool IsConnected => CopriServer.IsConnected;
|
||||
|
||||
/// <summary> No user is logged in.</summary>
|
||||
public string SessionCookie => null;
|
||||
|
||||
/// <summary> Is raised whenever login state has changed.</summary>
|
||||
public event LoginStateChangedEventHandler LoginStateChanged;
|
||||
|
||||
/// <summary>Constructs a copri query object.</summary>
|
||||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
public Command(
|
||||
ICopriServerBase p_oCopriServer) : base(p_oCopriServer)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs user in.
|
||||
/// If log in succeeds either and session might be updated if it was no more valid (logged in by an different device).
|
||||
/// If log in fails (password modified) session cookie is set to empty.
|
||||
/// If communication fails an exception is thrown.
|
||||
/// </summary>
|
||||
public async Task<IAccount> DoLogin(
|
||||
string p_strMail,
|
||||
string p_strPassword,
|
||||
string p_strDeviceId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(p_strMail))
|
||||
{
|
||||
throw new ArgumentNullException("Can not loging user. Mail address must not be null or empty.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(p_strPassword))
|
||||
{
|
||||
throw new ArgumentNullException("Can not loging user. Password must not be null or empty.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(p_strDeviceId))
|
||||
{
|
||||
throw new ArgumentNullException("Can not loging user. Device not be null or empty.");
|
||||
}
|
||||
|
||||
AuthorizationResponse l_oResponse;
|
||||
try
|
||||
{
|
||||
l_oResponse = (await CopriServer.DoAuthorizationAsync(p_strMail, p_strPassword, p_strDeviceId)).GetIsResponseOk(p_strMail);
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
var l_oAccount = l_oResponse.GetAccount(MerchantId, p_strMail, p_strPassword);
|
||||
|
||||
// Log in state changes. Notify parent object to update.
|
||||
LoginStateChanged?.Invoke(this, new LoginStateChangedEventArgs(l_oAccount.SessionCookie, l_oAccount.Mail));
|
||||
|
||||
return l_oAccount;
|
||||
}
|
||||
|
||||
/// <summary> Logs user out. </summary>
|
||||
public async Task DoLogout()
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected log out request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to reserve a bike.
|
||||
/// </summary>
|
||||
/// <param name="p_oBike">Bike to book.</param>
|
||||
public async Task DoReserve(
|
||||
Bikes.Bike.BC.IBikeInfoMutable p_oBike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected booking request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary> Request to cancel a reservation.</summary>
|
||||
/// <param name="p_oBike">Bike to book.</param>
|
||||
public async Task DoCancelReservation(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected cancel reservation request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary> Get authentication keys.</summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
public async Task CalculateAuthKeys(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected request to get authenticatin keys detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary> Updates COPRI lock state for a booked bike. </summary>
|
||||
/// <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 UpdateLockingStateAsync(Bikes.Bike.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.BluetoothLock.IBikeInfoMutable bike)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected booking request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task DoReturn(
|
||||
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
|
||||
LocationDto location)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected returning request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits feedback to copri server.
|
||||
/// </summary>
|
||||
/// <param name="userFeedback">Feedback to submit.</param>
|
||||
public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri)
|
||||
{
|
||||
Log.ForContext<Command>().Error("Unexpected submit feedback request detected. No user logged in.");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
279
TINKLib/Model/Connector/Command/CommandLoggedIn.cs
Normal file
279
TINKLib/Model/Connector/Command/CommandLoggedIn.cs
Normal file
|
@ -0,0 +1,279 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike.BluetoothLock;
|
||||
using TINK.Model.Repository;
|
||||
using TINK.Model.Repository.Exception;
|
||||
using TINK.Model.Repository.Request;
|
||||
using TINK.Model.Repository.Response;
|
||||
using TINK.Model.User.Account;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
public class CommandLoggedIn : BaseLoggedIn, ICommand
|
||||
{
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
public bool IsConnected => CopriServer.IsConnected;
|
||||
|
||||
/// <summary> Is raised whenever login state has changed.</summary>
|
||||
public event LoginStateChangedEventHandler LoginStateChanged;
|
||||
|
||||
/// <summary>Constructs a copri query object.</summary>
|
||||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
public CommandLoggedIn(ICopriServerBase p_oCopriServer,
|
||||
string p_strSessionCookie,
|
||||
string p_strMail,
|
||||
Func<DateTime> p_oDateTimeProvider) : base(p_oCopriServer, p_strSessionCookie, p_strMail, p_oDateTimeProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs user in.
|
||||
/// If log in succeeds either and session might be updated if it was no more valid (logged in by an different device).
|
||||
/// If log in fails (password modified) session cookie is set to empty.
|
||||
/// If communication fails an TINK.Model.Repository.Exception is thrown.
|
||||
/// </summary>
|
||||
/// <param name="p_oAccount">Account to use for login.</param>
|
||||
public Task<IAccount> DoLogin(string p_strMail, string p_strPassword, string p_strDeviceId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(p_strMail))
|
||||
{
|
||||
throw new ArgumentNullException("Can not loging user. Mail address must not be null or empty.");
|
||||
}
|
||||
|
||||
throw new Exception($"Fehler beim Anmelden von unter {p_strMail}. Benutzer {Mail} ist bereits angemeldet.");
|
||||
}
|
||||
|
||||
/// <summary> Logs user out. </summary>
|
||||
public async Task DoLogout()
|
||||
{
|
||||
AuthorizationoutResponse l_oResponse = null;
|
||||
try
|
||||
{
|
||||
l_oResponse = (await CopriServer.DoAuthoutAsync()).GetIsResponseOk();
|
||||
}
|
||||
catch (AuthcookieNotDefinedException)
|
||||
{
|
||||
// Cookie is no more defined, i.e. no need to logout user at copri because user is already logged out.
|
||||
// Just ignore this error.
|
||||
// User logged out, log in state changed. Notify parent object to update.
|
||||
LoginStateChanged?.Invoke(this, new LoginStateChangedEventArgs());
|
||||
return;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
// User logged out, log in state changed. Notify parent object to update.
|
||||
LoginStateChanged?.Invoke(this, new LoginStateChangedEventArgs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to reserve a bike.
|
||||
/// </summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
public async Task DoReserve(Bikes.Bike.BC.IBikeInfoMutable bike)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
throw new ArgumentNullException("Can not reserve bike. No bike object available.");
|
||||
}
|
||||
|
||||
BikeInfoReservedOrBooked response;
|
||||
try
|
||||
{
|
||||
response = (await CopriServer.DoReserveAsync(bike.Id, bike.OperatorUri)).GetIsReserveResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Exception was not expected or too many subsequent excepitons detected.
|
||||
throw;
|
||||
}
|
||||
|
||||
bike.Load(response, Mail, DateTimeProvider, Bikes.Bike.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)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
throw new ArgumentNullException("Can not cancel reservation of bike. No bike object available.");
|
||||
}
|
||||
|
||||
ReservationCancelReturnResponse response;
|
||||
try
|
||||
{
|
||||
response = (await CopriServer.DoCancelReservationAsync(bike.Id, bike.OperatorUri)).GetIsCancelReservationResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Exception was not expected or too many subsequent excepitons detected.
|
||||
throw;
|
||||
}
|
||||
|
||||
bike.Load(Bikes.Bike.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)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
throw new ArgumentNullException("Can not calculate auth keys. No bike object available.");
|
||||
}
|
||||
|
||||
switch (bike.State.Value)
|
||||
{
|
||||
case State.InUseStateEnum.Reserved:
|
||||
case State.InUseStateEnum.Booked:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentNullException($"Can not calculate auth keys. Unexpected bike state {bike.State.Value} detected.");
|
||||
}
|
||||
|
||||
BikeInfoReservedOrBooked response;
|
||||
Guid guid = (bike is BikeInfoMutable btBike) ? btBike.LockInfo.Guid : new Guid();
|
||||
try
|
||||
{
|
||||
response = (await CopriServer.CalculateAuthKeysAsync(bike.Id, bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Exception was not expected or too many subsequent excepitons detected.
|
||||
throw;
|
||||
}
|
||||
|
||||
UpdaterJSON.Load(
|
||||
bike,
|
||||
response,
|
||||
Mail,
|
||||
DateTimeProvider,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
/// <summary> Updates COPRI lock state for a booked bike. </summary>
|
||||
/// <param name="bike">Bike to update locking state for.</param>
|
||||
/// <returns>Response on updating locking state.</returns>
|
||||
public async Task UpdateLockingStateAsync(
|
||||
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto location)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
throw new ArgumentNullException("Can not book bike. No bike object available.");
|
||||
}
|
||||
|
||||
if (bike.State.Value != State.InUseStateEnum.Booked)
|
||||
{
|
||||
throw new ArgumentNullException($"Can not update locking state of bike. Unexpected booking state {bike.State} detected.");
|
||||
}
|
||||
|
||||
lock_state? state = null;
|
||||
switch (bike.LockInfo.State)
|
||||
{
|
||||
case LockingState.Open:
|
||||
state = lock_state.unlocked;
|
||||
break;
|
||||
|
||||
case LockingState.Closed:
|
||||
state = lock_state.locked;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!state.HasValue)
|
||||
{
|
||||
throw new ArgumentNullException($"Can not update locking state of bike. Unexpected locking state {bike.LockInfo.State} detected.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
(await CopriServer.UpdateLockingStateAsync(
|
||||
bike.Id,
|
||||
location,
|
||||
state.Value,
|
||||
bike.LockInfo.BatteryPercentage,
|
||||
bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Exception was not expected or too many subsequent excepitons detected.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Request to book a bike. </summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
public async Task DoBook(
|
||||
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bike), "Can not book bike. No bike object available.");
|
||||
}
|
||||
|
||||
BikeInfoReservedOrBooked response;
|
||||
var btBike = bike as BikeInfoMutable;
|
||||
Guid guid = btBike != null ? btBike.LockInfo.Guid : new Guid();
|
||||
double batteryPercentage = btBike != null ? btBike.LockInfo.BatteryPercentage : double.NaN;
|
||||
try
|
||||
{
|
||||
response = (await CopriServer.DoBookAsync(
|
||||
bike.Id,
|
||||
guid,
|
||||
batteryPercentage,
|
||||
bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Exception was not expected or too many subsequent excepitons detected.
|
||||
throw;
|
||||
}
|
||||
|
||||
bike.Load(
|
||||
response,
|
||||
Mail,
|
||||
DateTimeProvider,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
/// <summary> Request to return a bike.</summary>
|
||||
/// <param name="latitude">Latitude of the bike.</param>
|
||||
/// <param name="longitude">Longitude of the bike.</param>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
public async Task DoReturn(
|
||||
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
|
||||
LocationDto location)
|
||||
{
|
||||
if (bike == null)
|
||||
{
|
||||
throw new ArgumentNullException("Can not return bike. No bike object available.");
|
||||
}
|
||||
|
||||
ReservationCancelReturnResponse l_oResponse;
|
||||
try
|
||||
{
|
||||
l_oResponse = (await CopriServer.DoReturn(bike.Id, location, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Exception was not expected or too many subsequent exceptions detected.
|
||||
throw;
|
||||
}
|
||||
|
||||
bike.Load(Bikes.Bike.BC.NotifyPropertyChangedLevel.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits feedback to copri server.
|
||||
/// </summary>
|
||||
/// <param name="userFeedback">Feedback to submit.</param>
|
||||
public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri)
|
||||
=> await CopriServer.DoSubmitFeedback(userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
|
||||
|
||||
}
|
||||
}
|
99
TINKLib/Model/Connector/Command/ICommand.cs
Normal file
99
TINKLib/Model/Connector/Command/ICommand.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Repository.Request;
|
||||
using TINK.Model.User.Account;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
public interface ICommand
|
||||
{
|
||||
/// <summary> Is raised whenever login state has changed.</summary>
|
||||
event LoginStateChangedEventHandler LoginStateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Logs user in.
|
||||
/// If log in succeeds either and session might be updated if it was no more valid (logged in by an different device).
|
||||
/// If log in fails (password modified) session cookie is set to empty.
|
||||
/// If communication fails an exception is thrown.
|
||||
/// </summary>
|
||||
Task<IAccount> DoLogin(string p_strMail, string p_strPassword, string p_strDeviceId);
|
||||
|
||||
/// <summary> Logs user out. </summary>
|
||||
Task DoLogout();
|
||||
|
||||
/// <summary> Request to reserve a bike.</summary>
|
||||
/// <param name="p_oBike">Bike to book.</param>
|
||||
Task DoReserve(Bikes.Bike.BC.IBikeInfoMutable p_oBike);
|
||||
|
||||
/// <summary> Request to cancel a reservation.</summary>
|
||||
/// <param name="p_oBike">Bike to book.</param>
|
||||
Task DoCancelReservation(Bikes.Bike.BC.IBikeInfoMutable p_oBike);
|
||||
|
||||
/// <summary> Get authentication keys to connect to lock.</summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
Task CalculateAuthKeys(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Updates COPRI lock state for a booked bike. </summary>
|
||||
/// <param name="bikeId">Id of the 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);
|
||||
|
||||
/// <summary> Request to book a bike.</summary>
|
||||
/// <param name="bike">Bike to book.</param>
|
||||
Task DoBook(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike);
|
||||
|
||||
/// <summary> Request to return a bike.</summary>
|
||||
/// <param name="location">Geolocation of lock when returning bike.</param>
|
||||
/// <param name="bike">Bike to return.</param>
|
||||
Task DoReturn(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto geolocation = null);
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
bool IsConnected { get; }
|
||||
|
||||
/// <summary> True if user is logged in false if not. </summary>
|
||||
string SessionCookie { get; }
|
||||
|
||||
Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri);
|
||||
|
||||
/// <summary>
|
||||
/// Feedback given by user when returning bike.
|
||||
/// </summary>
|
||||
public interface IUserFeedback
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds whether bike is broken or not.
|
||||
/// </summary>
|
||||
bool IsBikeBroken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds either
|
||||
/// - general feedback
|
||||
/// - error description of broken bike
|
||||
/// or both.
|
||||
/// </summary>
|
||||
string Message { get; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Defines delegate to be raised whenever login state changes.</summary>
|
||||
/// <param name="p_oEventArgs">Holds session cookie and mail address if user logged in successfully.</param>
|
||||
public delegate void LoginStateChangedEventHandler(object p_oSender, LoginStateChangedEventArgs p_oEventArgs);
|
||||
|
||||
/// <summary> Event arguments to notify about changes of logged in state.</summary>
|
||||
public class LoginStateChangedEventArgs : EventArgs
|
||||
{
|
||||
public LoginStateChangedEventArgs() : this(string.Empty, string.Empty)
|
||||
{ }
|
||||
|
||||
public LoginStateChangedEventArgs(string p_strSessionCookie, string p_strMail)
|
||||
{
|
||||
SessionCookie = p_strSessionCookie;
|
||||
Mail = p_strMail;
|
||||
}
|
||||
|
||||
public string SessionCookie { get; }
|
||||
|
||||
public string Mail { get; }
|
||||
}
|
||||
}
|
9
TINKLib/Model/Connector/Command/UserFeedbackDto.cs
Normal file
9
TINKLib/Model/Connector/Command/UserFeedbackDto.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
public record UserFeedbackDto : ICommand.IUserFeedback
|
||||
{
|
||||
public bool IsBikeBroken { get; init; }
|
||||
public string Message { get; init; }
|
||||
}
|
||||
}
|
57
TINKLib/Model/Connector/Connector.cs
Normal file
57
TINKLib/Model/Connector/Connector.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.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, ...)
|
||||
/// </summary>
|
||||
public class Connector : IConnector
|
||||
{
|
||||
/// <summary>Constructs a copri connector object.</summary>
|
||||
/// <param name="activeUri"> Uri to connect to.</param>
|
||||
/// <param name="userAgent">Holds the name and version of the TINKApp.</param>
|
||||
/// /// <param name="sessionCookie"> Holds the session cookie.</param>
|
||||
/// <param name="p_strMail">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>
|
||||
public Connector(
|
||||
Uri activeUri,
|
||||
string userAgent,
|
||||
string sessionCookie,
|
||||
string mail,
|
||||
TimeSpan? expiresAfter = null,
|
||||
ICachedCopriServer server = null )
|
||||
{
|
||||
Command = GetCommand(
|
||||
server ?? new CopriProviderHttps(activeUri, TinkApp.MerchantId, userAgent, sessionCookie),
|
||||
sessionCookie,
|
||||
mail);
|
||||
|
||||
Query = GetQuery(
|
||||
server ?? new CopriProviderHttps(activeUri, TinkApp.MerchantId, userAgent, sessionCookie, expiresAfter),
|
||||
sessionCookie,
|
||||
mail);
|
||||
}
|
||||
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
public ICommand Command { get; private set; }
|
||||
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
public IQuery Query { get; private set; }
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
public bool IsConnected => Command.IsConnected;
|
||||
|
||||
/// <summary> Gets a command object to perform copri commands. </summary>
|
||||
public static ICommand GetCommand(ICopriServerBase copri, string sessioncookie, string mail) => string.IsNullOrEmpty(sessioncookie)
|
||||
? new Command(copri)
|
||||
: new CommandLoggedIn(copri, sessioncookie, mail, () => DateTime.Now) as ICommand;
|
||||
|
||||
/// <summary> Gets a command object to perform copri queries. </summary>
|
||||
private static IQuery GetQuery(ICachedCopriServer copri, string sessioncookie, string mail) => string.IsNullOrEmpty(sessioncookie)
|
||||
? new CachedQuery(copri) as IQuery
|
||||
: new CachedQueryLoggedIn(copri, sessioncookie, mail, () => DateTime.Now);
|
||||
}
|
||||
}
|
47
TINKLib/Model/Connector/ConnectorCache.cs
Normal file
47
TINKLib/Model/Connector/ConnectorCache.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.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, ...)
|
||||
/// </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>
|
||||
public ConnectorCache(
|
||||
string sessionCookie,
|
||||
string mail,
|
||||
ICopriServer server = null)
|
||||
{
|
||||
|
||||
Command = Connector.GetCommand(
|
||||
server ?? new CopriProviderMonkeyStore(TinkApp.MerchantId, sessionCookie),
|
||||
sessionCookie,
|
||||
mail);
|
||||
|
||||
Query = GetQuery(
|
||||
server ?? new CopriProviderMonkeyStore(TinkApp.MerchantId, sessionCookie),
|
||||
sessionCookie,
|
||||
mail);
|
||||
}
|
||||
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
public ICommand Command { get; private set; }
|
||||
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
public IQuery Query { get; private set; }
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
public bool IsConnected => Command.IsConnected;
|
||||
|
||||
/// <summary> Gets a command object to perform copri queries. </summary>
|
||||
private static IQuery GetQuery(ICopriServer copri, string sessioncookie, string mail) => string.IsNullOrEmpty(sessioncookie)
|
||||
? new Query(copri) as IQuery
|
||||
: new QueryLoggedIn(copri, sessioncookie, mail, () => DateTime.Now);
|
||||
}
|
||||
}
|
19
TINKLib/Model/Connector/ConnectorFactory.cs
Normal file
19
TINKLib/Model/Connector/ConnectorFactory.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
public class ConnectorFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a connector object depending on whether beein onlin or offline.
|
||||
/// </summary>
|
||||
/// <param name="isConnected">True if online, false if offline</param>
|
||||
/// <returns></returns>
|
||||
public static IConnector Create(bool isConnected, Uri activeUri, string userAgent, string sessionCookie, string mail, TimeSpan? expiresAfter = null)
|
||||
{
|
||||
return isConnected
|
||||
? new Connector(activeUri, userAgent, sessionCookie, mail, expiresAfter: expiresAfter) as IConnector
|
||||
: new ConnectorCache(sessionCookie, mail);
|
||||
}
|
||||
}
|
||||
}
|
13
TINKLib/Model/Connector/Filter/GroupFilterFactory.cs
Normal file
13
TINKLib/Model/Connector/Filter/GroupFilterFactory.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.Model.Connector.Filter
|
||||
{
|
||||
public static class GroupFilterFactory
|
||||
{
|
||||
public static IGroupFilter Create(IEnumerable<string> group)
|
||||
{
|
||||
return group != null ? (IGroupFilter) new IntersectGroupFilter(group) : new NullGroupFilter();
|
||||
}
|
||||
}
|
||||
}
|
9
TINKLib/Model/Connector/Filter/IGroupFilter.cs
Normal file
9
TINKLib/Model/Connector/Filter/IGroupFilter.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.Model.Connector.Filter
|
||||
{
|
||||
public interface IGroupFilter
|
||||
{
|
||||
IEnumerable<string> DoFilter(IEnumerable<string> filter);
|
||||
}
|
||||
}
|
20
TINKLib/Model/Connector/Filter/IntersectGroupFilter.cs
Normal file
20
TINKLib/Model/Connector/Filter/IntersectGroupFilter.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TINK.Model.Connector.Filter
|
||||
{
|
||||
/// <summary> Filters to enumerations of string by intersecting.</summary>
|
||||
public class IntersectGroupFilter : IGroupFilter
|
||||
{
|
||||
private IEnumerable<string> Group { get; set; }
|
||||
|
||||
public IntersectGroupFilter(IEnumerable<string> group) => Group = group ?? new List<string>();
|
||||
|
||||
/// <summary> Applies filtering. </summary>
|
||||
/// <param name="filter">Enumeration of filter values to filter with or null if no filtering has to be applied.</param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<string> DoFilter(IEnumerable<string> filter) => filter != null
|
||||
? Group.Intersect(filter)
|
||||
: Group;
|
||||
}
|
||||
}
|
9
TINKLib/Model/Connector/Filter/NullGroupFilter.cs
Normal file
9
TINKLib/Model/Connector/Filter/NullGroupFilter.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.Model.Connector.Filter
|
||||
{
|
||||
public class NullGroupFilter : IGroupFilter
|
||||
{
|
||||
public IEnumerable<string> DoFilter(IEnumerable<string> filter) => filter;
|
||||
}
|
||||
}
|
11
TINKLib/Model/Connector/FilterHelper.cs
Normal file
11
TINKLib/Model/Connector/FilterHelper.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace TINK.Model.Connector
|
||||
{
|
||||
public static class FilterHelper
|
||||
{
|
||||
/// <summary> Holds the Konrad group (city bikes).</summary>
|
||||
public const string FILTERKONRAD = "Konrad";
|
||||
|
||||
/// <summary> Holds the tink group (Lastenräder).</summary>
|
||||
public const string FILTERTINKGENERAL = "TINK";
|
||||
}
|
||||
}
|
115
TINKLib/Model/Connector/FilteredConnector.cs
Normal file
115
TINKLib/Model/Connector/FilteredConnector.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Connector.Filter;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.Station;
|
||||
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary> Filters connector respones.</summary>
|
||||
/// <remarks>Former name: Filter</remarks>
|
||||
public class FilteredConnector : IFilteredConnector
|
||||
{
|
||||
/// <summary> Constructs a filter object. </summary>
|
||||
/// <param name="group">Filter group.</param>
|
||||
/// <param name="connector">Connector object.</param>
|
||||
public FilteredConnector(
|
||||
IEnumerable<string> group,
|
||||
IConnector connector)
|
||||
{
|
||||
Connector = connector;
|
||||
|
||||
if (Connector == null)
|
||||
{
|
||||
throw new ArgumentException("Can not construct filter object. Connector- and command objects must not be null.");
|
||||
}
|
||||
|
||||
Query = new QueryProvider(Connector.Query, GroupFilterFactory.Create(group));
|
||||
}
|
||||
|
||||
/// <summary> Inner connector object.</summary>
|
||||
public IConnector Connector { get; }
|
||||
|
||||
/// <summary> Command object. </summary>
|
||||
public ICommand Command => Connector.Command;
|
||||
|
||||
/// <summary> Object to query information. </summary>
|
||||
public IQuery Query { get; }
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
public bool IsConnected => Connector.IsConnected;
|
||||
|
||||
/// <summary> Object to perform filtered queries.</summary>
|
||||
private class QueryProvider : IQuery
|
||||
{
|
||||
/// <summary> Holds the filter. </summary>
|
||||
private IGroupFilter Filter { get; }
|
||||
|
||||
/// <summary> Holds the reference to object which performs copry queries.</summary>
|
||||
private IQuery m_oInnerQuery;
|
||||
|
||||
/// <summary> Constructs a query object.</summary>
|
||||
/// <param name="innerQuerry"></param>
|
||||
/// <param name="filter"></param>
|
||||
public QueryProvider(IQuery innerQuerry, IGroupFilter filter)
|
||||
{
|
||||
m_oInnerQuery = innerQuerry;
|
||||
Filter = filter;
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes either bikes available if no user is logged in or bikes available and bikes occupied if a user is logged in. </summary>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
{
|
||||
var result = await m_oInnerQuery.GetBikesAsync();
|
||||
return new Result<BikeCollection>(
|
||||
result.Source,
|
||||
new BikeCollection(DoFilter(result.Response, Filter)),
|
||||
result.Exception);
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied if a user is logged in. </summary>
|
||||
public async Task<Result<BikeCollection>> GetBikesOccupiedAsync()
|
||||
{
|
||||
var result = await m_oInnerQuery.GetBikesOccupiedAsync();
|
||||
return new Result<BikeCollection>(
|
||||
result.Source,
|
||||
new BikeCollection(result.Response.ToDictionary(x => x.Id)),
|
||||
result.Exception);
|
||||
}
|
||||
|
||||
/// <summary> Gets all station applying filter rules. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
|
||||
{
|
||||
var result = await m_oInnerQuery.GetBikesAndStationsAsync();
|
||||
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
result.Source,
|
||||
new StationsAndBikesContainer(
|
||||
new StationDictionary(result.Response.StationsAll.CopriVersion, DoFilter(result.Response.StationsAll, Filter)),
|
||||
new BikeCollection(DoFilter(result.Response.Bikes, Filter))),
|
||||
result.Exception);
|
||||
}
|
||||
|
||||
/// <summary> Filter bikes by group. </summary>
|
||||
/// <param name="p_oBikes">Bikes to filter.</param>
|
||||
/// <returns>Filtered bikes.</returns>
|
||||
private static Dictionary<int, BikeInfo> DoFilter(BikeCollection p_oBikes, IGroupFilter filter)
|
||||
{
|
||||
return p_oBikes.Where(x => filter.DoFilter(x.Group).Count() > 0).ToDictionary(x => x.Id);
|
||||
}
|
||||
|
||||
/// <summary> Filter stations by broup. </summary>
|
||||
/// <returns></returns>
|
||||
private static Dictionary<int, Station.Station> DoFilter(StationDictionary p_oStations, IGroupFilter filter)
|
||||
{
|
||||
return p_oStations.Where(x => filter.DoFilter(x.Group).Count() > 0).ToDictionary((x => x.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
TINKLib/Model/Connector/FilteredConnectorFactory.cs
Normal file
16
TINKLib/Model/Connector/FilteredConnectorFactory.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
public static class FilteredConnectorFactory
|
||||
{
|
||||
/// <summary> Creates a filter object. </summary>
|
||||
/// <param name="group"></param>
|
||||
public static IFilteredConnector Create(IEnumerable<string> group, IConnector connector)
|
||||
{
|
||||
return group != null
|
||||
? (IFilteredConnector) new FilteredConnector(group, connector)
|
||||
: new NullFilterConnector(connector);
|
||||
}
|
||||
}
|
||||
}
|
14
TINKLib/Model/Connector/IConnector.cs
Normal file
14
TINKLib/Model/Connector/IConnector.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace TINK.Model.Connector
|
||||
{
|
||||
public interface IConnector
|
||||
{
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
ICommand Command { get; }
|
||||
|
||||
/// <summary> Object for queriying stations and bikes.</summary>
|
||||
IQuery Query { get; }
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
bool IsConnected { get; }
|
||||
}
|
||||
}
|
7
TINKLib/Model/Connector/IFilteredConnector.cs
Normal file
7
TINKLib/Model/Connector/IFilteredConnector.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace TINK.Model.Connector
|
||||
{
|
||||
public interface IFilteredConnector : IConnector
|
||||
{
|
||||
IConnector Connector { get; }
|
||||
}
|
||||
}
|
108
TINKLib/Model/Connector/NullFilterConnector.cs
Normal file
108
TINKLib/Model/Connector/NullFilterConnector.cs
Normal file
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.Station;
|
||||
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary> Filters connector respones.</summary>
|
||||
public class NullFilterConnector : IFilteredConnector
|
||||
{
|
||||
/// <summary> Constructs a filter object. </summary>
|
||||
/// <param name="p_oGroup">Filter group.</param>
|
||||
/// <param name="connector">Connector object.</param>
|
||||
public NullFilterConnector(
|
||||
IConnector connector)
|
||||
{
|
||||
Connector = connector;
|
||||
|
||||
if (Connector == null)
|
||||
{
|
||||
throw new ArgumentException("Can not construct filter object. Connector- and command objects must not be null.");
|
||||
}
|
||||
|
||||
Query = new QueryProvider(Connector.Query);
|
||||
}
|
||||
|
||||
/// <summary> Inner connector object.</summary>
|
||||
public IConnector Connector { get; }
|
||||
|
||||
/// <summary> Command object. </summary>
|
||||
public ICommand Command => Connector.Command;
|
||||
|
||||
/// <summary> Object to query information. </summary>
|
||||
public IQuery Query { get; }
|
||||
|
||||
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
|
||||
public bool IsConnected => Connector.IsConnected;
|
||||
|
||||
/// <summary> Object to perform filtered queries.</summary>
|
||||
private class QueryProvider : IQuery
|
||||
{
|
||||
/// <summary> Holds the reference to object which performs copry queries.</summary>
|
||||
private IQuery m_oInnerQuery;
|
||||
|
||||
/// <summary> Constructs a query object.</summary>
|
||||
/// <param name="p_oInnerQuery"></param>
|
||||
/// <param name="p_oFilter"></param>
|
||||
public QueryProvider(IQuery p_oInnerQuery)
|
||||
{
|
||||
m_oInnerQuery = p_oInnerQuery;
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes either bikes available if no user is logged in or bikes available and bikes occupied if a user is logged in. </summary>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
{
|
||||
var result = await m_oInnerQuery.GetBikesAsync();
|
||||
return new Result<BikeCollection>(
|
||||
result.Source,
|
||||
new BikeCollection(result.Response.ToDictionary(x => x.Id)),
|
||||
result.Exception);
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied if a user is logged in. </summary>
|
||||
public async Task<Result<BikeCollection>> GetBikesOccupiedAsync()
|
||||
{
|
||||
var result = await m_oInnerQuery.GetBikesOccupiedAsync();
|
||||
return new Result<BikeCollection>(
|
||||
result.Source,
|
||||
new BikeCollection(result.Response.ToDictionary(x => x.Id)),
|
||||
result.Exception);
|
||||
}
|
||||
|
||||
/// <summary> Gets all station applying filter rules. </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
|
||||
{
|
||||
var result = await m_oInnerQuery.GetBikesAndStationsAsync();
|
||||
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
result.Source,
|
||||
new StationsAndBikesContainer(
|
||||
new StationDictionary(result.Response.StationsAll.CopriVersion, result.Response.StationsAll.ToDictionary(x => x.Id)),
|
||||
new BikeCollection(result.Response.Bikes.ToDictionary(x => x.Id))),
|
||||
result.Exception);
|
||||
}
|
||||
|
||||
/// <summary> Filter bikes by group. </summary>
|
||||
/// <param name="p_oBikes">Bikes to filter.</param>
|
||||
/// <returns>Filtered bikes.</returns>
|
||||
public static Dictionary<int, BikeInfo> DoFilter(BikeCollection p_oBikes, IEnumerable<string> p_oFilter)
|
||||
{
|
||||
return p_oBikes.Where(x => x.Group.Intersect(p_oFilter).Count() > 0).ToDictionary(x => x.Id);
|
||||
}
|
||||
|
||||
/// <summary> Filter stations by broup. </summary>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<int, Station.Station> DoFilter(StationDictionary p_oStations, IEnumerable<string> p_oFilter)
|
||||
{
|
||||
return p_oStations.Where(x => x.Group.Intersect(p_oFilter).Count() > 0).ToDictionary((x => x.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
TINKLib/Model/Connector/Query/Base.cs
Normal file
27
TINKLib/Model/Connector/Query/Base.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using TINK.Model.Repository;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides information required for copri commands/ query operations.
|
||||
/// </summary>
|
||||
public class Base
|
||||
{
|
||||
/// <summary> Reference to object which provides access to copri server. </summary>
|
||||
protected ICopriServerBase CopriServer { get; }
|
||||
|
||||
/// <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>
|
||||
protected Base(
|
||||
ICopriServerBase p_oCopriServer)
|
||||
{
|
||||
CopriServer = p_oCopriServer
|
||||
?? throw new ArgumentException("Can not instantiate command/ query base- object. Copri server object must never be null or emtpy.");
|
||||
}
|
||||
}
|
||||
}
|
39
TINKLib/Model/Connector/Query/BaseLoggedIn.cs
Normal file
39
TINKLib/Model/Connector/Query/BaseLoggedIn.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using TINK.Model.Repository;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary>Holds user infromation required for copri related commands/ query operations. </summary>
|
||||
public class BaseLoggedIn : Base
|
||||
{
|
||||
/// <summary>Session cookie used to sign in to copri.</summary>
|
||||
public string SessionCookie { get; }
|
||||
|
||||
/// <summary> Mail address of the user. </summary>
|
||||
protected string Mail { get; }
|
||||
|
||||
/// <summary> Object which provides date time info. </summary>
|
||||
protected readonly Func<DateTime> DateTimeProvider;
|
||||
|
||||
/// <summary>Constructs a copri query object.</summary>
|
||||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
public BaseLoggedIn(ICopriServerBase p_oCopriServer,
|
||||
string p_strSessionCookie,
|
||||
string p_strMail,
|
||||
Func<DateTime> p_oDateTimeProvider) : base(p_oCopriServer)
|
||||
{
|
||||
if (string.IsNullOrEmpty(p_strSessionCookie))
|
||||
throw new ArgumentException("Can not instantiate query object- object. Session cookie must never be null or emtpy.");
|
||||
|
||||
if (string.IsNullOrEmpty(p_strMail))
|
||||
throw new ArgumentException("Can not instantiate query object- object. Mail address must never be null or emtpy.");
|
||||
|
||||
DateTimeProvider = p_oDateTimeProvider
|
||||
?? throw new ArgumentException("Can not instantiate connector- object. No date time provider object available.");
|
||||
|
||||
SessionCookie = p_strSessionCookie;
|
||||
|
||||
Mail = p_strMail;
|
||||
}
|
||||
}
|
||||
}
|
86
TINKLib/Model/Connector/Query/CachedQuery.cs
Normal file
86
TINKLib/Model/Connector/Query/CachedQuery.cs
Normal file
|
@ -0,0 +1,86 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.Repository;
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
public class CachedQuery : Base, IQuery
|
||||
{
|
||||
/// <summary> Cached copri server. </summary>
|
||||
private readonly ICachedCopriServer server;
|
||||
|
||||
/// <summary>Constructs a copri query object.</summary>
|
||||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
public CachedQuery(
|
||||
ICopriServerBase p_oCopriServer) : base(p_oCopriServer)
|
||||
{
|
||||
server = p_oCopriServer as ICachedCopriServer;
|
||||
if (server == null)
|
||||
{
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {p_oCopriServer.GetType()}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets all stations including postions and bikes.</summary>
|
||||
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
|
||||
{
|
||||
var resultStations = await server.GetStations();
|
||||
|
||||
if (resultStations.Source == typeof(CopriCallsMonkeyStore))
|
||||
{
|
||||
// Communication with copri in order to get stations failed.
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
resultStations.Source,
|
||||
new StationsAndBikesContainer(
|
||||
resultStations.Response.GetStationsAllMutable(),
|
||||
(await server.GetBikesAvailable(true)).Response.GetBikesAvailable()),
|
||||
resultStations.Exception);
|
||||
}
|
||||
|
||||
var resultBikes = await server.GetBikesAvailable();
|
||||
if (resultBikes.Source == typeof(CopriCallsMonkeyStore))
|
||||
{
|
||||
// Communication with copri in order to get bikes failed.
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
resultBikes.Source,
|
||||
new StationsAndBikesContainer(
|
||||
(await server.GetStations(true)).Response.GetStationsAllMutable(),
|
||||
resultBikes.Response.GetBikesAvailable()),
|
||||
resultBikes.Exception);
|
||||
}
|
||||
|
||||
// Communicatin with copri succeeded.
|
||||
server.AddToCache(resultStations);
|
||||
server.AddToCache(resultBikes);
|
||||
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
resultStations.Source,
|
||||
new StationsAndBikesContainer(resultStations.Response.GetStationsAllMutable(), resultBikes.Response.GetBikesAvailable()));
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesOccupiedAsync()
|
||||
{
|
||||
Log.ForContext<CachedQuery>().Error("Unexpected call to get be bikes occpied detected. No user is logged in.");
|
||||
return new Result<BikeCollection>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
await Task.Run(() => new BikeCollection(new Dictionary<int, BikeInfo>())),
|
||||
new System.Exception("Abfrage der reservierten/ gebuchten Räder nicht möglich. Kein Benutzer angemeldet."));
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes available. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
{
|
||||
var result = await server.GetBikesAvailable();
|
||||
server.AddToCache(result);
|
||||
return new Result<BikeCollection>(result.Source, result.Response.GetBikesAvailable(), result.Exception);
|
||||
}
|
||||
}
|
||||
}
|
159
TINKLib/Model/Connector/Query/CachedQueryLoggedIn.cs
Normal file
159
TINKLib/Model/Connector/Query/CachedQueryLoggedIn.cs
Normal file
|
@ -0,0 +1,159 @@
|
|||
using MonkeyCache.FileStore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.Repository;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary> Provides query functionality for a logged in user. </summary>
|
||||
public class CachedQueryLoggedIn : BaseLoggedIn, IQuery
|
||||
{
|
||||
/// <summary> Cached copri server. </summary>
|
||||
private readonly ICachedCopriServer server;
|
||||
|
||||
/// <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)
|
||||
{
|
||||
server = p_oCopriServer as ICachedCopriServer;
|
||||
if (server == null)
|
||||
{
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {p_oCopriServer.GetType()}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets all stations including postions.</summary>
|
||||
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
|
||||
{
|
||||
var resultStations = await server.GetStations();
|
||||
|
||||
if (resultStations.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| resultStations.Exception != null)
|
||||
{
|
||||
// Stations were read from cache ==> get bikes availbalbe and occupied from cache as well to avoid inconsistencies
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
resultStations.Source,
|
||||
new StationsAndBikesContainer(
|
||||
resultStations.Response.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(
|
||||
(await server.GetBikesAvailable(true)).Response,
|
||||
(await server.GetBikesOccupied(true)).Response,
|
||||
Mail,
|
||||
DateTimeProvider)),
|
||||
resultStations.Exception);
|
||||
}
|
||||
|
||||
var l_oBikesAvailableResponse = await server.GetBikesAvailable();
|
||||
if (l_oBikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| l_oBikesAvailableResponse.Exception != null)
|
||||
{
|
||||
// Bikes avilable were read from cache ==> get bikes occupied from cache as well to avoid inconsistencies
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
l_oBikesAvailableResponse.Source,
|
||||
new StationsAndBikesContainer(
|
||||
(await server.GetStations(true)).Response.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(l_oBikesAvailableResponse.Response,
|
||||
(await server.GetBikesOccupied(true)).Response,
|
||||
Mail,
|
||||
DateTimeProvider)),
|
||||
l_oBikesAvailableResponse.Exception);
|
||||
}
|
||||
|
||||
var l_oBikesOccupiedResponse = await server.GetBikesOccupied();
|
||||
if (l_oBikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| l_oBikesOccupiedResponse.Exception != null)
|
||||
{
|
||||
// Bikes occupied were read from cache ==> get bikes available from cache as well to avoid inconsistencies
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
l_oBikesOccupiedResponse.Source,
|
||||
new StationsAndBikesContainer(
|
||||
(await server.GetStations(true)).Response.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(
|
||||
(await server.GetBikesAvailable(true)).Response,
|
||||
l_oBikesOccupiedResponse.Response,
|
||||
Mail,
|
||||
DateTimeProvider)),
|
||||
l_oBikesOccupiedResponse.Exception);
|
||||
}
|
||||
|
||||
// Both types bikes could read from copri => update cache
|
||||
server.AddToCache(resultStations);
|
||||
server.AddToCache(l_oBikesAvailableResponse);
|
||||
server.AddToCache(l_oBikesOccupiedResponse);
|
||||
|
||||
var exceptions = new[] { resultStations?.Exception, l_oBikesAvailableResponse?.Exception, l_oBikesOccupiedResponse?.Exception }.Where(x => x != null).ToArray();
|
||||
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
resultStations.Source,
|
||||
new StationsAndBikesContainer(
|
||||
resultStations.Response.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(
|
||||
l_oBikesAvailableResponse.Response,
|
||||
l_oBikesOccupiedResponse.Response,
|
||||
Mail,
|
||||
DateTimeProvider)),
|
||||
exceptions.Length > 0 ? new AggregateException(exceptions) : null);
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied. </summary>
|
||||
/// <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.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();
|
||||
|
||||
if (l_oBikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| l_oBikesAvailableResponse.Exception != null)
|
||||
{
|
||||
// Bikes avilable were read from cache ==> get bikes occupied from cache as well to avoid inconsistencies
|
||||
return new Result<BikeCollection>(
|
||||
l_oBikesAvailableResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(
|
||||
l_oBikesAvailableResponse.Response,
|
||||
(await server.GetBikesOccupied(true)).Response,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
l_oBikesAvailableResponse.Exception);
|
||||
}
|
||||
|
||||
var l_oBikesOccupiedResponse = await server.GetBikesOccupied();
|
||||
if (l_oBikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|
||||
|| l_oBikesOccupiedResponse.Exception != null)
|
||||
{
|
||||
// Bikes occupied were read from cache ==> get bikes available from cache as well to avoid inconsistencies
|
||||
return new Result<BikeCollection>(
|
||||
l_oBikesOccupiedResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(
|
||||
(await server.GetBikesAvailable(true)).Response,
|
||||
l_oBikesOccupiedResponse.Response,
|
||||
Mail,
|
||||
DateTimeProvider),
|
||||
l_oBikesOccupiedResponse.Exception);
|
||||
|
||||
}
|
||||
|
||||
// Both types bikes could read from copri => update cache
|
||||
server.AddToCache(l_oBikesAvailableResponse);
|
||||
server.AddToCache(l_oBikesOccupiedResponse);
|
||||
|
||||
return new Result<BikeCollection>(
|
||||
l_oBikesAvailableResponse.Source,
|
||||
UpdaterJSON.GetBikesAll(l_oBikesAvailableResponse.Response, l_oBikesOccupiedResponse.Response, Mail, DateTimeProvider),
|
||||
l_oBikesAvailableResponse.Exception != null || l_oBikesOccupiedResponse.Exception != null ? new AggregateException(new[] { l_oBikesAvailableResponse.Exception, l_oBikesOccupiedResponse.Exception }) : null);
|
||||
}
|
||||
}
|
||||
}
|
20
TINKLib/Model/Connector/Query/IQuery.cs
Normal file
20
TINKLib/Model/Connector/Query/IQuery.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
public interface IQuery
|
||||
{
|
||||
/// <summary> Gets all stations including postions.</summary>
|
||||
Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync();
|
||||
|
||||
/// <summary> Gets bikes occupied is a user is logged in. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
Task<Result<BikeCollection>> GetBikesOccupiedAsync();
|
||||
|
||||
/// <summary> Gets bikes either bikes available if no user is logged in or bikes available and bikes occupied if a user is logged in. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
Task<Result<BikeCollection>> GetBikesAsync();
|
||||
}
|
||||
}
|
60
TINKLib/Model/Connector/Query/Query.cs
Normal file
60
TINKLib/Model/Connector/Query/Query.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.Repository;
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary> Provides query functionality without login. </summary>
|
||||
public class Query : Base, IQuery
|
||||
{
|
||||
/// <summary> Cached copri server. </summary>
|
||||
private readonly ICopriServer server;
|
||||
|
||||
/// <summary>Constructs a copri query object.</summary>
|
||||
/// <param name="p_oCopriServer">Server which implements communication.</param>
|
||||
public Query(ICopriServerBase p_oCopriServer) : base(p_oCopriServer)
|
||||
{
|
||||
server = p_oCopriServer as ICopriServer;
|
||||
if (server == null)
|
||||
{
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {p_oCopriServer.GetType()}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets all stations including postions.</summary>
|
||||
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
|
||||
{
|
||||
var stationsAllResponse = await server.GetStationsAsync();
|
||||
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
|
||||
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
new StationsAndBikesContainer( stationsAllResponse.GetStationsAllMutable(), bikesAvailableResponse.GetBikesAvailable()));
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesOccupiedAsync()
|
||||
{
|
||||
Log.ForContext<Query>().Error("Unexpected call to get be bikes occpied detected. No user is logged in.");
|
||||
return new Result<BikeCollection>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
await Task.Run(() => new BikeCollection(new Dictionary<int, BikeInfo>())),
|
||||
new System.Exception("Abfrage der reservierten/ gebuchten Räder fehlgeschlagen. Kein Benutzer angemeldet."));
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied. </summary>
|
||||
/// <returns> Collection of bikes. </returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
{
|
||||
return new Result<BikeCollection>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
(await server.GetBikesAvailableAsync()).GetBikesAvailable());
|
||||
}
|
||||
}
|
||||
}
|
65
TINKLib/Model/Connector/Query/QueryLoggedIn.cs
Normal file
65
TINKLib/Model/Connector/Query/QueryLoggedIn.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Services.CopriApi;
|
||||
using TINK.Model.Repository;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary> Provides query functionality for a logged in user. </summary>
|
||||
public class QueryLoggedIn : BaseLoggedIn, IQuery
|
||||
{
|
||||
/// <summary> Cached 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)
|
||||
{
|
||||
server = p_oCopriServer as ICopriServer;
|
||||
if (server == null)
|
||||
{
|
||||
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {p_oCopriServer.GetType()}.");
|
||||
}
|
||||
|
||||
server = p_oCopriServer as ICopriServer;
|
||||
}
|
||||
|
||||
/// <summary> Gets all stations including postions.</summary>
|
||||
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
|
||||
{
|
||||
var stationResponse = await server.GetStationsAsync();
|
||||
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
|
||||
var bikesOccupiedResponse = await server.GetBikesOccupiedAsync();
|
||||
|
||||
return new Result<StationsAndBikesContainer>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
new StationsAndBikesContainer(
|
||||
stationResponse.GetStationsAllMutable(),
|
||||
UpdaterJSON.GetBikesAll(bikesAvailableResponse, bikesOccupiedResponse, Mail, DateTimeProvider)));
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesOccupiedAsync()
|
||||
{
|
||||
return new Result<BikeCollection>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
(await server.GetBikesOccupiedAsync()).GetBikesOccupied(Mail, DateTimeProvider));
|
||||
}
|
||||
/// <summary> Gets bikes available and bikes occupied. </summary>
|
||||
/// <returns>Collection of bikes.</returns>
|
||||
public async Task<Result<BikeCollection>> GetBikesAsync()
|
||||
{
|
||||
var l_oBikesAvailableResponse = await server.GetBikesAvailableAsync();
|
||||
var l_oBikesOccupiedResponse = await server.GetBikesOccupiedAsync();
|
||||
|
||||
return new Result<BikeCollection>(
|
||||
typeof(CopriCallsMonkeyStore),
|
||||
UpdaterJSON.GetBikesAll(l_oBikesAvailableResponse, l_oBikesOccupiedResponse, Mail, DateTimeProvider));
|
||||
}
|
||||
}
|
||||
}
|
358
TINKLib/Model/Connector/TextToTypeHelper.cs
Normal file
358
TINKLib/Model/Connector/TextToTypeHelper.cs
Normal file
|
@ -0,0 +1,358 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Repository.Exception;
|
||||
using TINK.Model.Repository.Response;
|
||||
using TINK.Model.Services.CopriApi.ServerUris;
|
||||
using TINK.Model.State;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary>
|
||||
/// Conversion helper functionality.
|
||||
/// </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>
|
||||
/// <param name="p_oStationInfo">Object to get information from.</param>
|
||||
/// <returns>Position information.</returns>
|
||||
public static Station.Position GetPosition(this StationsAllResponse.StationInfo p_oStationInfo)
|
||||
{
|
||||
return GetPosition(p_oStationInfo.gps);
|
||||
}
|
||||
|
||||
/// <summary> Gets the position from StationInfo object. </summary>
|
||||
/// <param name="p_oAuthorizationResponse">Object to get information from.</param>
|
||||
/// <returns>Position information.</returns>
|
||||
public static IEnumerable<string> GetGroup(this AuthorizationResponse p_oAuthorizationResponse)
|
||||
{
|
||||
try
|
||||
{
|
||||
return p_oAuthorizationResponse.user_group.GetGroup();
|
||||
}
|
||||
catch (Exception l_oException)
|
||||
{
|
||||
throw new Exception($"Can not get group of user from text \"{p_oAuthorizationResponse.user_group}\".", l_oException);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets the position from StationInfo object. </summary>
|
||||
/// <param name="p_oAuthorizationResponse">Object to get information from.</param>
|
||||
/// <returns>Position information.</returns>
|
||||
public static IEnumerable<string> GetGroup(this string p_oGroup)
|
||||
{
|
||||
if (string.IsNullOrEmpty(p_oGroup))
|
||||
{
|
||||
throw new ArgumentException("Can not get goup form string. Group text can not be null.");
|
||||
}
|
||||
|
||||
return new HashSet<string>(p_oGroup.Split(',')).ToList();
|
||||
}
|
||||
|
||||
/// <summary> Gets the position from StationInfo object. </summary>
|
||||
/// <param name="p_oAuthorizationResponse">Object to get information from.</param>
|
||||
/// <returns>Position information.</returns>
|
||||
public static string GetGroup(this IEnumerable<string> p_oGroup)
|
||||
{
|
||||
return string.Join(",", p_oGroup);
|
||||
}
|
||||
|
||||
/// <summary> Gets the position from StationInfo object. </summary>
|
||||
/// <param name="p_oStationInfo">Object to get information from.</param>
|
||||
/// <returns>Position information.</returns>
|
||||
public static IEnumerable<string> GetGroup(this StationsAllResponse.StationInfo p_oStationInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
return p_oStationInfo.station_group.GetGroup();
|
||||
}
|
||||
catch (System.Exception l_oException)
|
||||
{
|
||||
throw new System.Exception($"Can not get group of stations from text \"{p_oStationInfo.station_group}\".", l_oException);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position from StationInfo object.
|
||||
/// </summary>
|
||||
/// <param name="p_oBikeInfo">Object to get information from.</param>
|
||||
/// <returns>Position information.</returns>
|
||||
public static Station.Position GetPosition(this BikeInfoAvailable p_oBikeInfo)
|
||||
{
|
||||
return GetPosition(p_oBikeInfo.gps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position from StationInfo object.
|
||||
/// </summary>
|
||||
/// <param name="p_oBikeInfo">Object to get information from.</param>
|
||||
/// <returns>Position information.</returns>
|
||||
public static InUseStateEnum GetState(this BikeInfoBase p_oBikeInfo)
|
||||
{
|
||||
var l_oState = p_oBikeInfo.state;
|
||||
|
||||
if (string.IsNullOrEmpty(l_oState))
|
||||
{
|
||||
throw new InvalidResponseException<BikeInfoBase>(
|
||||
string.Format("Unknown reservation state detected. Member {0}.{1}.", typeof(BikeInfoBase), nameof(BikeInfoBase.state)),
|
||||
p_oBikeInfo);
|
||||
}
|
||||
|
||||
if (l_oState == "available")
|
||||
{
|
||||
return InUseStateEnum.Disposable;
|
||||
}
|
||||
else if (l_oState == "reserved" ||
|
||||
l_oState == "requested")
|
||||
{
|
||||
return InUseStateEnum.Reserved;
|
||||
}
|
||||
else if (l_oState == "booked" ||
|
||||
l_oState == "occupied")
|
||||
{
|
||||
return InUseStateEnum.Booked;
|
||||
}
|
||||
|
||||
throw new CommunicationException(string.Format("Unknown bike state detected. State is {0}.", l_oState));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the from date information from JSON.
|
||||
/// </summary>
|
||||
/// <param name="p_oBikeInfo">JSON to get information from..</param>
|
||||
/// <returns>From information.</returns>
|
||||
public static DateTime GetFrom(this BikeInfoReservedOrBooked p_oBikeInfo)
|
||||
{
|
||||
return DateTime.Parse(p_oBikeInfo.start_time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the bike is a trike or not.
|
||||
/// </summary>
|
||||
/// <param name="p_oBikeInfo">JSON to get information from..</param>
|
||||
/// <returns>From information.</returns>
|
||||
public static bool? GetIsDemo(this BikeInfoBase p_oBikeInfo)
|
||||
{
|
||||
return p_oBikeInfo?.description != null
|
||||
? p_oBikeInfo.description.ToUpper().Contains(DEMOBIKEMARKER)
|
||||
: (bool?) null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the bike is a trike or not.
|
||||
/// </summary>
|
||||
/// <param name="p_oBikeInfo">JSON to get information from..</param>
|
||||
/// <returns>From information.</returns>
|
||||
public static IEnumerable<string> GetGroup(this BikeInfoBase p_oBikeInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
return p_oBikeInfo?.bike_group?.GetGroup()?.ToList() ?? new List<string>();
|
||||
}
|
||||
catch (System.Exception l_oException)
|
||||
{
|
||||
throw new System.Exception($"Can not get group of user from text \"{p_oBikeInfo.bike_group}\".", l_oException);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets whether the bike has a bord computer or not. </summary>
|
||||
/// <param name="p_oBikeInfo">JSON to get information from.</param>
|
||||
/// <returns>From information.</returns>
|
||||
public static bool GetIsManualLockBike(this BikeInfoBase p_oBikeInfo)
|
||||
{
|
||||
return !string.IsNullOrEmpty(p_oBikeInfo.system)
|
||||
&& p_oBikeInfo.system.ToUpper().StartsWith("LOCK");
|
||||
}
|
||||
|
||||
/// <summary> Gets whether the bike has a bord computer or not. </summary>
|
||||
/// <param name="p_oBikeInfo">JSON to get information from..</param>
|
||||
/// <returns>From information.</returns>
|
||||
public static bool GetIsBluetoothLockBike(this BikeInfoBase p_oBikeInfo)
|
||||
{
|
||||
return !string.IsNullOrEmpty(p_oBikeInfo.system)
|
||||
&& p_oBikeInfo.system.ToUpper().StartsWith("ILOCKIT");
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return TextToLockItTypeHelper.GetBluetoothLockId(bikeInfo?.Ilockit_ID);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
// return new Guid("00000000-0000-0000-0000-e57e6b9aee16");
|
||||
return Guid.TryParse(bikeInfo?.Ilockit_GUID, out Guid lockGuid)
|
||||
? lockGuid
|
||||
: TextToLockItTypeHelper.INVALIDLOCKGUID;
|
||||
}
|
||||
|
||||
public static byte[] GetUserKey(this BikeInfoReservedOrBooked bikeInfo)
|
||||
{
|
||||
return GetKey(bikeInfo.K_u);
|
||||
}
|
||||
|
||||
public static byte[] GetAdminKey(this BikeInfoReservedOrBooked bikeInfo)
|
||||
{
|
||||
return GetKey(bikeInfo.K_a);
|
||||
}
|
||||
|
||||
public static byte[] GetSeed(this BikeInfoReservedOrBooked bikeInfo)
|
||||
{
|
||||
return GetKey(bikeInfo.K_seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get array of keys from string of format "[12, -9, 5]"
|
||||
/// </summary>
|
||||
/// <param name="keyArrayText"></param>
|
||||
/// <returns></returns>
|
||||
private static byte[] GetKey(string keyArrayText)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(keyArrayText))
|
||||
return new byte[0];
|
||||
|
||||
return Regex.Replace(keyArrayText, @"\[(.*)\]", "$1").Split(',').Select(x => (byte)sbyte.Parse(x)).ToArray();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error("Can not extract K_u/ K_a/ or K_seed. Key {ArrayText} does not is not of expected format. {Exception}", keyArrayText, exception);
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the bike is a trike or not.
|
||||
/// </summary>
|
||||
/// <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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of bike.
|
||||
/// </summary>
|
||||
/// <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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get position from a ,- separated string.
|
||||
/// </summary>
|
||||
/// <param name="p_strGps">Text to extract positon from.</param>
|
||||
/// <returns>Position object.</returns>
|
||||
public static Station.Position GetPosition(string p_strGps)
|
||||
{
|
||||
if (p_strGps == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var l_oPosition = p_strGps.Split(',');
|
||||
|
||||
if (l_oPosition.Length != 2)
|
||||
return null;
|
||||
|
||||
double l_oLatitude;
|
||||
if (!double.TryParse(l_oPosition[0], NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLatitude))
|
||||
return null;
|
||||
|
||||
double l_oLongitude;
|
||||
if (!double.TryParse(l_oPosition[1], NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLongitude))
|
||||
return null;
|
||||
|
||||
return new Station.Position(l_oLatitude, l_oLongitude);
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
}
|
||||
|
||||
public static Uri GetOperatorUri(this BikeInfoBase bikeInfo)
|
||||
{
|
||||
return bikeInfo?.uri_operator != null && !string.IsNullOrEmpty(bikeInfo?.uri_operator)
|
||||
? new Uri($"{bikeInfo.uri_operator}/{CopriServerUriList.REST_RESOURCE_ROOT}")
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
496
TINKLib/Model/Connector/Updater/UpdaterJSON.cs
Normal file
496
TINKLib/Model/Connector/Updater/UpdaterJSON.cs
Normal file
|
@ -0,0 +1,496 @@
|
|||
using System;
|
||||
using TINK.Model.Bike;
|
||||
using TINK.Model.Station;
|
||||
using TINK.Model.Repository.Response;
|
||||
using TINK.Model.User.Account;
|
||||
using System.Collections.Generic;
|
||||
using TINK.Model.State;
|
||||
using TINK.Model.Repository.Exception;
|
||||
using Serilog;
|
||||
|
||||
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
|
||||
using IBikeInfoMutable = TINK.Model.Bikes.Bike.BC.IBikeInfoMutable;
|
||||
using System.Globalization;
|
||||
|
||||
namespace TINK.Model.Connector
|
||||
{
|
||||
/// <summary>
|
||||
/// Connects TINK app to copri using JSON as input data format.
|
||||
/// </summary>
|
||||
/// <todo>Rename to UpdateFromCopri.</todo>
|
||||
public static class UpdaterJSON
|
||||
{
|
||||
/// <summary> Loads a bike object from copri server cancel reservation/ booking update request.</summary>
|
||||
/// <param name="bike">Bike object to load response into.</param>
|
||||
/// <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)
|
||||
{
|
||||
|
||||
bike.State.Load(InUseStateEnum.Disposable, notifyLevel: notifyLevel);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets all statsion for station provider and add them into station list.
|
||||
/// </summary>
|
||||
/// <param name="p_oStationList">List of stations to update.</param>
|
||||
public static StationDictionary GetStationsAllMutable(this StationsAllResponse p_oStationsAllResponse)
|
||||
{
|
||||
// Get stations from Copri/ file/ memory, ....
|
||||
if (p_oStationsAllResponse == null
|
||||
|| p_oStationsAllResponse.stations == null)
|
||||
{
|
||||
// Latest list of stations could not be retrieved from provider.
|
||||
return new StationDictionary();
|
||||
}
|
||||
|
||||
Version.TryParse(p_oStationsAllResponse.copri_version, out Version l_oCopriVersion);
|
||||
|
||||
var l_oStations = new StationDictionary(p_oVersion: l_oCopriVersion);
|
||||
|
||||
foreach (var l_oStation in p_oStationsAllResponse.stations)
|
||||
{
|
||||
if (l_oStations.GetById(l_oStation.Value.station) != null)
|
||||
{
|
||||
// Can not add station to list of station. Id is not unique.
|
||||
throw new InvalidResponseException<StationsAllResponse>(
|
||||
string.Format("Station id {0} is not unique.", l_oStation.Value.station), p_oStationsAllResponse);
|
||||
}
|
||||
|
||||
l_oStations.Add(new Station.Station(
|
||||
l_oStation.Value.station,
|
||||
l_oStation.Value.GetGroup(),
|
||||
l_oStation.Value.GetPosition(),
|
||||
l_oStation.Value.description));
|
||||
}
|
||||
|
||||
return l_oStations;
|
||||
}
|
||||
|
||||
/// <summary> Gets account object from login response.</summary>
|
||||
/// <param name="merchantId">Needed to extract cookie from autorization response.</param>
|
||||
/// <param name="loginResponse">Response to get session cookie and debug level from.</param>
|
||||
/// <param name="mail">Mail address needed to construct a complete account object (is not part of response).</param>
|
||||
/// <param name="password">Password needed to construct a complete account object (is not part of response).</param>
|
||||
public static IAccount GetAccount(
|
||||
this AuthorizationResponse loginResponse,
|
||||
string merchantId,
|
||||
string mail,
|
||||
string password)
|
||||
{
|
||||
if (loginResponse == null)
|
||||
{
|
||||
throw new ArgumentNullException("p_oLoginResponse");
|
||||
}
|
||||
|
||||
return new Account(
|
||||
mail,
|
||||
password,
|
||||
loginResponse.authcookie?.Replace(merchantId, ""),
|
||||
loginResponse.GetGroup(),
|
||||
loginResponse.debuglevel == 1
|
||||
? Permissions.All :
|
||||
(Permissions)loginResponse.debuglevel) ;
|
||||
}
|
||||
|
||||
/// <summary> Load bike object from booking response. </summary>
|
||||
/// <param name="bike">Bike object to load from response.</param>
|
||||
/// <param name="bikeInfo">Booking response.</param>
|
||||
/// <param name="mailAddress">Mail address of user which books bike.</param>
|
||||
/// <param name="p_strSessionCookie">Session cookie of user which books bike.</param>
|
||||
/// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
|
||||
public static void Load(
|
||||
this IBikeInfoMutable bike,
|
||||
BikeInfoReservedOrBooked bikeInfo,
|
||||
string mailAddress,
|
||||
Func<DateTime> dateTimeProvider,
|
||||
Bikes.Bike.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.Bike.BC.NotifyPropertyChangedLevel.All)
|
||||
{
|
||||
|
||||
var l_oDateTimeProvider = dateTimeProvider != null
|
||||
? dateTimeProvider
|
||||
: () => DateTime.Now;
|
||||
|
||||
if (bike is Bike.BluetoothLock.BikeInfoMutable btBikeInfo)
|
||||
{
|
||||
btBikeInfo.LockInfo.Load(
|
||||
bikeInfo.GetBluetoothLockId(),
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey());
|
||||
}
|
||||
|
||||
var l_oState = bikeInfo.GetState();
|
||||
switch (l_oState)
|
||||
{
|
||||
case InUseStateEnum.Disposable:
|
||||
bike.State.Load(
|
||||
InUseStateEnum.Disposable,
|
||||
notifyLevel: notifyLevel);
|
||||
break;
|
||||
|
||||
case InUseStateEnum.Reserved:
|
||||
bike.State.Load(
|
||||
InUseStateEnum.Reserved,
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode,
|
||||
notifyLevel);
|
||||
break;
|
||||
|
||||
case InUseStateEnum.Booked:
|
||||
bike.State.Load(
|
||||
InUseStateEnum.Booked,
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode,
|
||||
notifyLevel);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception(string.Format("Unexpected bike state detected. state is {0}.", l_oState));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes available from copri server response.</summary>
|
||||
/// <param name="p_oBikesAvailableResponse">Response to create collection from.</param>
|
||||
/// <returns>New collection of available bikes.</returns>
|
||||
public static BikeCollection GetBikesAvailable(
|
||||
this BikesAvailableResponse p_oBikesAvailableResponse)
|
||||
{
|
||||
return GetBikesAll(
|
||||
p_oBikesAvailableResponse,
|
||||
new BikesReservedOccupiedResponse(), // 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>
|
||||
/// <returns>New collection of occupied bikes.</returns>
|
||||
public static BikeCollection GetBikesOccupied(
|
||||
this BikesReservedOccupiedResponse p_oBikesOccupiedResponse,
|
||||
string p_strMail,
|
||||
Func<DateTime> p_oDateTimeProvider)
|
||||
{
|
||||
return GetBikesAll(
|
||||
new BikesAvailableResponse(),
|
||||
p_oBikesOccupiedResponse,
|
||||
p_strMail,
|
||||
p_oDateTimeProvider);
|
||||
}
|
||||
|
||||
/// <summary> Gets bikes occupied from copri server response. </summary>
|
||||
/// <param name="p_oBikesAvailable">Response to create bikes from.</param>
|
||||
/// <returns>New collection of occupied bikes.</returns>
|
||||
public static BikeCollection GetBikesAll(
|
||||
BikesAvailableResponse p_oBikesAvailableResponse,
|
||||
BikesReservedOccupiedResponse p_oBikesOccupiedResponse,
|
||||
string p_strMail,
|
||||
Func<DateTime> p_oDateTimeProvider)
|
||||
{
|
||||
var l_oBikesDictionary = new Dictionary<int, BikeInfo>();
|
||||
var l_oDuplicates = new Dictionary<int, BikeInfo>();
|
||||
|
||||
// Get bikes from Copri/ file/ memory, ....
|
||||
if (p_oBikesAvailableResponse != null
|
||||
&& p_oBikesAvailableResponse.bikes != null)
|
||||
{
|
||||
foreach (var bikeInfoResponse in p_oBikesAvailableResponse.bikes.Values)
|
||||
{
|
||||
var l_oBikeInfo = BikeInfoFactory.Create(bikeInfoResponse);
|
||||
if (l_oBikeInfo == null)
|
||||
{
|
||||
// Response is not valid.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (l_oBikesDictionary.ContainsKey(l_oBikeInfo.Id))
|
||||
{
|
||||
// Duplicates are not allowed.
|
||||
Log.Error($"Duplicate bike with id {l_oBikeInfo.Id} detected evaluating bikes available. Bike status is {l_oBikeInfo.State.Value}.");
|
||||
|
||||
if (!l_oDuplicates.ContainsKey(l_oBikeInfo.Id))
|
||||
{
|
||||
l_oDuplicates.Add(l_oBikeInfo.Id, l_oBikeInfo);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
l_oBikesDictionary.Add(l_oBikeInfo.Id, l_oBikeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Get bikes from Copri/ file/ memory, ....
|
||||
if (p_oBikesOccupiedResponse != null
|
||||
&& p_oBikesOccupiedResponse.bikes_occupied != null)
|
||||
{
|
||||
foreach (var l_oBikeInfoResponse in p_oBikesOccupiedResponse.bikes_occupied.Values)
|
||||
{
|
||||
BikeInfo l_oBikeInfo = BikeInfoFactory.Create(
|
||||
l_oBikeInfoResponse,
|
||||
p_strMail,
|
||||
p_oDateTimeProvider);
|
||||
|
||||
if (l_oBikeInfo == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (l_oBikesDictionary.ContainsKey(l_oBikeInfo.Id))
|
||||
{
|
||||
// Duplicates are not allowed.
|
||||
Log.Error($"Duplicate bike with id {l_oBikeInfo.Id} detected evaluating bikes occupied. Bike status is {l_oBikeInfo.State.Value}.");
|
||||
if (!l_oDuplicates.ContainsKey(l_oBikeInfo.Id))
|
||||
{
|
||||
l_oDuplicates.Add(l_oBikeInfo.Id, l_oBikeInfo);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
l_oBikesDictionary.Add(l_oBikeInfo.Id, l_oBikeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove entries which are not unique.
|
||||
foreach (var l_oDuplicate in l_oDuplicates)
|
||||
{
|
||||
l_oBikesDictionary.Remove(l_oDuplicate.Key);
|
||||
}
|
||||
|
||||
return new BikeCollection(l_oBikesDictionary);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs bike info instances/ bike info derived instances.
|
||||
/// </summary>
|
||||
public static class BikeInfoFactory
|
||||
{
|
||||
public static BikeInfo Create(BikeInfoAvailable bikeInfo)
|
||||
{
|
||||
if (bikeInfo.GetIsManualLockBike())
|
||||
{
|
||||
// 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 (bikeInfo.station == null)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return !bikeInfo.GetIsBluetoothLockBike()
|
||||
? new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription) null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description)
|
||||
: new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetBluetoothLockId(),
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
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(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>
|
||||
/// <returns></returns>
|
||||
public static BikeInfo Create(
|
||||
BikeInfoReservedOrBooked bikeInfo,
|
||||
string mailAddress,
|
||||
Func<DateTime> dateTimeProvider)
|
||||
{
|
||||
if (bikeInfo.GetIsManualLockBike())
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Check if bike is a bluetooth lock bike.
|
||||
var isBluetoothBike = bikeInfo.GetIsBluetoothLockBike();
|
||||
int lockSerial = bikeInfo.GetBluetoothLockId();
|
||||
Guid lockGuid = bikeInfo.GetBluetoothLockGuid();
|
||||
|
||||
switch (bikeInfo.GetState())
|
||||
{
|
||||
case InUseStateEnum.Reserved:
|
||||
try
|
||||
{
|
||||
return !isBluetoothBike
|
||||
? new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode,
|
||||
dateTimeProvider)
|
||||
: new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
lockGuid,
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
dateTimeProvider,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike());
|
||||
}
|
||||
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
|
||||
{
|
||||
return !isBluetoothBike
|
||||
? new BikeInfo(
|
||||
bikeInfo.bike,
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike(),
|
||||
bikeInfo.description,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.timeCode)
|
||||
: new Bike.BluetoothLock.BikeInfo(
|
||||
bikeInfo.bike,
|
||||
lockSerial,
|
||||
bikeInfo.GetBluetoothLockGuid(),
|
||||
bikeInfo.GetUserKey(),
|
||||
bikeInfo.GetAdminKey(),
|
||||
bikeInfo.GetSeed(),
|
||||
bikeInfo.GetFrom(),
|
||||
mailAddress,
|
||||
bikeInfo.station,
|
||||
bikeInfo.GetOperatorUri(),
|
||||
#if !NOTARIFFDESCRIPTION
|
||||
Create(bikeInfo.tariff_description),
|
||||
#else
|
||||
Create((TINK.Repository.Response.TariffDescription)null),
|
||||
#endif
|
||||
bikeInfo.GetIsDemo(),
|
||||
bikeInfo.GetGroup(),
|
||||
bikeInfo.GetWheelType(),
|
||||
bikeInfo.GetTypeOfBike());
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public static Bikes.Bike.TariffDescription Create(this TINK.Repository.Response.TariffDescription tariffDesciption)
|
||||
{
|
||||
return new Bikes.Bike.TariffDescription
|
||||
{
|
||||
Name = tariffDesciption?.name,
|
||||
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : null,
|
||||
FreeTimePerSession = double.TryParse(tariffDesciption?.free_hours, NumberStyles.Any, CultureInfo.InvariantCulture, out double freeHours) ? TimeSpan.FromHours(freeHours) : TimeSpan.Zero,
|
||||
FeeEuroPerHour = double.TryParse(tariffDesciption?.eur_per_hour, NumberStyles.Any, CultureInfo.InvariantCulture, out double euroPerHour) ? euroPerHour : double.NaN,
|
||||
AboEuroPerMonth = double.TryParse(tariffDesciption?.abo_eur_per_month, NumberStyles.Any, CultureInfo.InvariantCulture, out double aboEuroPerMonth) ? aboEuroPerMonth : double.NaN,
|
||||
MaxFeeEuroPerDay = double.TryParse(tariffDesciption?.max_eur_per_day, NumberStyles.Any, CultureInfo.InvariantCulture, out double maxEuroPerDay) ? maxEuroPerDay : double.NaN,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue