2021-05-13 20:03:07 +02:00
|
|
|
|
using System;
|
2022-08-30 15:42:25 +02:00
|
|
|
|
using System.Collections.Generic;
|
2021-05-13 20:03:07 +02:00
|
|
|
|
using System.Threading.Tasks;
|
2022-08-30 15:42:25 +02:00
|
|
|
|
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
|
|
|
|
|
using TINK.Model.Connector.Updater;
|
|
|
|
|
using TINK.Model.Device;
|
|
|
|
|
using TINK.Model.User.Account;
|
2021-06-26 20:57:55 +02:00
|
|
|
|
using TINK.Repository;
|
|
|
|
|
using TINK.Repository.Exception;
|
|
|
|
|
using TINK.Repository.Request;
|
|
|
|
|
using TINK.Repository.Response;
|
2022-04-25 22:15:15 +02:00
|
|
|
|
using TINK.Services.CopriApi;
|
2021-05-13 20:03:07 +02:00
|
|
|
|
|
|
|
|
|
namespace TINK.Model.Connector
|
|
|
|
|
{
|
2022-09-06 16:08:19 +02:00
|
|
|
|
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 sessionCookie,
|
|
|
|
|
string mail,
|
|
|
|
|
Func<DateTime> dateTimeProvider) : base(p_oCopriServer, sessionCookie, mail, dateTimeProvider)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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.Repository.Exception is thrown.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="p_oAccount">Account to use for login.</param>
|
|
|
|
|
public Task<IAccount> DoLogin(string mail, string password, string deviceId)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(mail))
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("Can not loging user. Mail address must not be null or empty.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Exception($"Fehler beim Anmelden von unter {mail}. 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.BikeInfoNS.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, 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.BikeInfoNS.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.BikeInfoNS.BC.NotifyPropertyChangedLevel.None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Get authentication keys.</summary>
|
|
|
|
|
/// <param name="bike">Bike to get new keys for.</param>
|
|
|
|
|
public async Task CalculateAuthKeys(Bikes.BikeInfoNS.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,
|
|
|
|
|
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.BikeInfoNS.BC.IBikeInfoMutable bike)
|
|
|
|
|
{
|
|
|
|
|
if (bike == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("Can not notify about start returning bike. No bike object available.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
(await CopriServer.StartReturningBike(
|
|
|
|
|
bike.Id,
|
|
|
|
|
bike.OperatorUri)).GetIsResponseOk("Start returning bike");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
// Exception was not expected or too many subsequent excepitons detected.
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Updates COPRI lock state for a booked bike. </summary>
|
|
|
|
|
/// <param name="bike">Bike to update locking state for.</param>
|
|
|
|
|
/// <param name="location">Location of the bike.</param>
|
|
|
|
|
/// <returns>Response on updating locking state.</returns>
|
|
|
|
|
public async Task UpdateLockingStateAsync(
|
|
|
|
|
IBikeInfoMutable bike,
|
|
|
|
|
LocationDto location)
|
|
|
|
|
{
|
|
|
|
|
if (bike == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("Can not update locking state of 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,
|
|
|
|
|
state.Value,
|
|
|
|
|
bike.OperatorUri,
|
|
|
|
|
location,
|
|
|
|
|
bike.LockInfo.BatteryPercentage)).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.BikeInfoNS.BC.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;
|
|
|
|
|
|
|
|
|
|
response = (await CopriServer.DoBookAsync(
|
|
|
|
|
bike.Id,
|
|
|
|
|
guid,
|
|
|
|
|
batteryPercentage,
|
|
|
|
|
bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
|
|
|
|
|
|
|
|
|
bike.Load(
|
|
|
|
|
response,
|
|
|
|
|
Mail,
|
|
|
|
|
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.BikeInfoNS.CopriLock.IBikeInfoMutable bike)
|
|
|
|
|
=> await Polling.BookAndOpenAync(CopriServer, bike, Mail);
|
|
|
|
|
|
|
|
|
|
/// <summary> Request to return a bike.</summary>
|
|
|
|
|
/// <param name="bike">Bike to return.</param>
|
|
|
|
|
/// <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.BikeInfoNS.BC.IBikeInfoMutable bike,
|
|
|
|
|
LocationDto location = null,
|
|
|
|
|
ISmartDevice smartDevice = null)
|
|
|
|
|
{
|
|
|
|
|
if (bike == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("Can not return bike. No bike object available.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DoReturnResponse response
|
|
|
|
|
= (await CopriServer.DoReturn(bike.Id, location, smartDevice, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
|
|
|
|
|
|
|
|
|
|
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.BikeInfoNS.CopriLock.IBikeInfoMutable bike, ISmartDevice smartDevice = null)
|
|
|
|
|
=> await Polling.ReturnAndCloseAync(CopriServer, smartDevice, bike);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Submits feedback to copri server.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="userFeedback">Feedback to submit.</param>
|
2021-06-26 20:57:55 +02:00
|
|
|
|
#if USCSHARP9
|
2021-05-13 20:03:07 +02:00
|
|
|
|
public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri)
|
2021-06-26 20:57:55 +02:00
|
|
|
|
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
|
|
|
|
|
#else
|
2022-09-06 16:08:19 +02:00
|
|
|
|
/// <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);
|
2021-06-26 20:57:55 +02:00
|
|
|
|
#endif
|
2021-08-01 17:24:15 +02:00
|
|
|
|
|
2022-09-06 16:08:19 +02:00
|
|
|
|
/// <summary> Submits mini survey to copri server. </summary>
|
|
|
|
|
/// <param name="answers">Collection of answers.</param>
|
|
|
|
|
public async Task DoSubmitMiniSurvey(IDictionary<string, string> answers)
|
|
|
|
|
=> await CopriServer.DoSubmitMiniSurvey(answers);
|
2022-04-25 22:15:15 +02:00
|
|
|
|
|
2022-09-06 16:08:19 +02:00
|
|
|
|
public async Task OpenLockAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike)
|
|
|
|
|
=> await CopriServer.OpenAync(bike);
|
|
|
|
|
public async Task CloseLockAsync(Bikes.BikeInfoNS.CopriLock.IBikeInfoMutable bike)
|
|
|
|
|
=> await CopriServer.CloseAync(bike);
|
|
|
|
|
}
|
2021-05-13 20:03:07 +02:00
|
|
|
|
}
|