mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-01-05 13:06:27 +01:00
183 lines
5.3 KiB
C#
183 lines
5.3 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
using Serilog;
|
|
using ShareeBike.MultilingualResources;
|
|
using ShareeBike.Services.BluetoothLock;
|
|
using ShareeBike.Services.BluetoothLock.Exception;
|
|
using ShareeBike.Services.BluetoothLock.Tdo;
|
|
using ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler;
|
|
using ShareeBike.ViewModel.Bikes;
|
|
|
|
namespace ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command
|
|
{
|
|
public static class ConnectAndGetStateCommand
|
|
{
|
|
/// <summary>
|
|
/// Possible steps of connecting or disconnecting a lock.
|
|
/// </summary>
|
|
public enum Step
|
|
{
|
|
ConnectLock,
|
|
GetLockingState,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Possible steps of connecting or disconnecting a lock.
|
|
/// </summary>
|
|
public enum State
|
|
{
|
|
BluetoothOff,
|
|
NoLocationPermission,
|
|
LocationServicesOff,
|
|
OutOfReachError,
|
|
GeneralConnectLockError,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Interface to notify view model about steps/ state changes of connecting or disconnecting lock process.
|
|
/// </summary>
|
|
public interface IConnectAndGetStateCommandListener
|
|
{
|
|
/// <summary>
|
|
/// Reports current step.
|
|
/// </summary>
|
|
/// <param name="currentStep">Current step to report.</param>
|
|
void ReportStep(Step currentStep);
|
|
|
|
/// <summary>
|
|
/// Reports current state.
|
|
/// </summary>
|
|
/// <param name="currentState">Current state to report.</param>
|
|
/// <param name="message">Message describing the current state.</param>
|
|
/// <returns></returns>
|
|
Task ReportStateAsync(State currentState, string message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connect or disconnect lock.
|
|
/// </summary>
|
|
/// <param name="listener"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="Exception"></exception>
|
|
public static async Task InvokeAsync<T>(
|
|
IBikeInfoMutable bike,
|
|
ILocksService lockService,
|
|
IConnectAndGetStateCommandListener listener = null)
|
|
{
|
|
// Invokes member to notify about step being started.
|
|
void InvokeCurrentStep(Step step)
|
|
{
|
|
if (listener == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
listener.ReportStep(step);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking step-action for step {step} ", exception, step);
|
|
}
|
|
}
|
|
|
|
// Invokes member to notify about state change.
|
|
async Task InvokeCurrentStateAsync(State state, string message)
|
|
{
|
|
if (listener == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
await listener.ReportStateAsync(state, message);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking state-action for state {state} ", exception, state);
|
|
}
|
|
}
|
|
|
|
// Connect lock
|
|
InvokeCurrentStep(Step.ConnectLock);
|
|
|
|
LockInfoTdo result = null;
|
|
var continueConnect = true;
|
|
var retryCount = 1;
|
|
while (continueConnect && result == null)
|
|
{
|
|
try
|
|
{
|
|
result = await lockService.ConnectAsync(
|
|
new LockInfoAuthTdo.Builder { Id = bike.LockInfo.Id, Guid = bike.LockInfo.Guid, K_seed = bike.LockInfo.Seed, K_u = bike.LockInfo.UserKey }.Build(),
|
|
lockService.TimeOut.GetSingleConnect(retryCount));
|
|
Log.ForContext<T>().Information("Connected to lock of bike {bikeId} successfully. Value is {lockState}.", bike.Id, bike.LockInfo.State);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Log.ForContext<T>().Information("Connection to lock of bike {bikeId} failed. ", bike.Id);
|
|
if (exception is ConnectBluetoothNotOnException)
|
|
{
|
|
continueConnect = false;
|
|
Log.ForContext<T>().Error("Bluetooth not on.");
|
|
await InvokeCurrentStateAsync(State.BluetoothOff, exception.Message);
|
|
}
|
|
else if (exception is ConnectLocationPermissionMissingException)
|
|
{
|
|
continueConnect = false;
|
|
Log.ForContext<T>().Error("Location permission missing.");
|
|
await InvokeCurrentStateAsync(State.NoLocationPermission, exception.Message);
|
|
}
|
|
else if (exception is ConnectLocationOffException)
|
|
{
|
|
continueConnect = false;
|
|
Log.ForContext<T>().Error("Location services not on.");
|
|
await InvokeCurrentStateAsync(State.LocationServicesOff, exception.Message);
|
|
}
|
|
else if (exception is OutOfReachException)
|
|
{
|
|
continueConnect = false;
|
|
Log.ForContext<T>().Error("Lock is out of reach.");
|
|
await InvokeCurrentStateAsync(State.OutOfReachError, exception.Message);
|
|
}
|
|
else
|
|
{
|
|
Log.ForContext<T>().Error("{@exception}", exception);
|
|
|
|
string message;
|
|
if (retryCount < 2)
|
|
{
|
|
message = AppResources.ErrorConnectLock;
|
|
}
|
|
else if (retryCount < 3)
|
|
{
|
|
message = AppResources.ErrorConnectLockEscalationLevel1;
|
|
}
|
|
else
|
|
{
|
|
message = AppResources.ErrorConnectLockEscalationLevel2;
|
|
continueConnect = false;
|
|
}
|
|
await InvokeCurrentStateAsync(State.GeneralConnectLockError, message);
|
|
}
|
|
|
|
if (continueConnect)
|
|
{
|
|
retryCount++;
|
|
continue;
|
|
}
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// Get locking state
|
|
InvokeCurrentStep(Step.GetLockingState);
|
|
bike.LockInfo.State = result?.State?.GetLockingState() ?? LockingState.UnknownDisconnected;
|
|
if (bike.LockInfo.State == LockingState.UnknownDisconnected)
|
|
{
|
|
// Do not display any messages here, because search is implicit.
|
|
Log.ForContext<T>().Error("Lock is still not connected.");
|
|
}
|
|
bike.LockInfo.Guid = result?.Guid ?? new Guid();
|
|
}
|
|
}
|
|
}
|