using System; using System.Threading.Tasks; using TINK.Model.Bike.BluetoothLock; using TINK.Repository; using TINK.Repository.Exception; using TINK.Repository.Request; using TINK.Repository.Response; using TINK.Model.User.Account; using TINK.Model.Device; namespace TINK.Model.Connector { public class CommandLoggedIn : BaseLoggedIn, ICommand { /// True if connector has access to copri server, false if cached values are used. public bool IsConnected => CopriServer.IsConnected; /// Is raised whenever login state has changed. public event LoginStateChangedEventHandler LoginStateChanged; /// Constructs a copri query object. /// Server which implements communication. public CommandLoggedIn(ICopriServerBase p_oCopriServer, string p_strSessionCookie, string p_strMail, Func p_oDateTimeProvider) : base(p_oCopriServer, p_strSessionCookie, p_strMail, p_oDateTimeProvider) { } /// /// 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. /// /// Account to use for login. public Task 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."); } /// Logs user out. 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()); } /// /// Request to reserve a bike. /// /// Bike to book. 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); } /// Request to cancel a reservation. /// Bike to cancel reservation. 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); } /// Get authentication keys. /// Bike to get new keys for. 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); } /// Updates COPRI lock state for a booked bike. /// Bike to update locking state for. /// Response on updating locking state. 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; } } /// Request to book a bike. /// Bike to book. 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); } /// Request to return a bike. /// Bike to return. /// Position of the bike. /// Provides info about hard and software. public async Task DoReturn( Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto location, ISmartDevice smartDevice) { 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, smartDevice, 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); } /// /// Submits feedback to copri server. /// /// Feedback to submit. #if USCSHARP9 public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri) => await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri); #else public async Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri) => await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri); #endif } }