mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2024-11-12 13:36:26 +01:00
256 lines
8.5 KiB
C#
256 lines
8.5 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Serilog;
|
|
using TINK.Model;
|
|
using TINK.Model.Bikes.BikeInfoNS.CopriLock;
|
|
using TINK.Model.Connector;
|
|
using TINK.Model.Connector.Updater;
|
|
using TINK.Model.Device;
|
|
using TINK.Model.Services.CopriApi;
|
|
using TINK.Repository;
|
|
using TINK.Repository.Response;
|
|
using TINK.Services.CopriApi.Exception;
|
|
|
|
namespace TINK.Services.CopriApi
|
|
{
|
|
public static class Polling
|
|
{
|
|
/// <summary> Timeout for open/ close operations.</summary>
|
|
private const int OPEN_CLOSE_TIMEOUT_MS = 50000;
|
|
|
|
/// <summary> Opens lock.</summary>
|
|
/// <param name="copriServer"> Instance to communicate with backend.</param>
|
|
/// <param name="bike">Bike object holding id of bike to open. Lock state of object is updated after open request.</param>
|
|
public static async Task OpenAync(
|
|
this ICopriServerBase copriServer,
|
|
IBikeInfoMutable bike)
|
|
{
|
|
if (!(copriServer is ICachedCopriServer cachedServer))
|
|
throw new ArgumentNullException(nameof(copriServer));
|
|
|
|
await cachedServer.OpenAync(bike);
|
|
}
|
|
|
|
/// <summary> Opens lock.</summary>
|
|
/// <param name="cachedServer"> Instance to communicate with backend.</param>
|
|
/// <param name="bike">Bike object holding id of bike to open. Lock state of object is updated after open request.</param>
|
|
public static async Task OpenAync(
|
|
this ICachedCopriServer cachedServer,
|
|
IBikeInfoMutable bike)
|
|
{
|
|
// Send command to close lock
|
|
await cachedServer.UpdateLockingStateAsync(
|
|
bike.Id,
|
|
Repository.Request.lock_state.unlocking,
|
|
bike.OperatorUri);
|
|
|
|
var lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
|
|
|
var watch = new Stopwatch();
|
|
watch.Start();
|
|
|
|
while (lockingState != LockingState.Open
|
|
&& lockingState != LockingState.UnknownDisconnected
|
|
&& watch.Elapsed < TimeSpan.FromMilliseconds(OPEN_CLOSE_TIMEOUT_MS))
|
|
{
|
|
// Delay a litte to reduce load on backend.
|
|
await Task.Delay(3000);
|
|
|
|
lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
|
Log.Information($"Current lock state is {lockingState}.");
|
|
}
|
|
|
|
// Update locking state.
|
|
bike.LockInfo.State = lockingState;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Books a bike and opens the lock.
|
|
/// </summary>
|
|
/// <param name="copriServer"> Instance to communicate with backend.</param>
|
|
/// <param name="bike">Bike to book and open.</param>
|
|
/// <param name="mailAddress">Mail address of user which books bike.</param>
|
|
public static async Task BookAndOpenAync(
|
|
this ICopriServerBase copriServer,
|
|
IBikeInfoMutable bike,
|
|
string mailAddress)
|
|
{
|
|
if (bike == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(bike), "Can not book bike and open lock. No bike object available.");
|
|
}
|
|
|
|
if (!(copriServer is ICachedCopriServer cachedServer))
|
|
throw new ArgumentNullException(nameof(copriServer));
|
|
|
|
// Send command to open lock
|
|
var response = bike.State.Value == Model.State.InUseStateEnum.Disposable
|
|
? (await copriServer.BookAvailableAndStartOpeningAsync(bike.Id, bike.OperatorUri)).GetIsBookingResponseOk(bike.Id)
|
|
: (await copriServer.BookReservedAndStartOpeningAsync(bike.Id, bike.OperatorUri)).GetIsBookingResponseOk(bike.Id);
|
|
|
|
// Updated locking state.
|
|
var lockingState = await cachedServer.GetOccupiedBikeLockStateAsync(bike.Id);
|
|
|
|
var watch = new Stopwatch();
|
|
watch.Start();
|
|
|
|
while (lockingState.HasValue /* if null bike is no more occupied*/ &&
|
|
lockingState.Value != LockingState.Open
|
|
&& watch.Elapsed < TimeSpan.FromMilliseconds(OPEN_CLOSE_TIMEOUT_MS))
|
|
{
|
|
// Delay a litte to reduce load on backend.
|
|
await Task.Delay(3000);
|
|
|
|
lockingState = await cachedServer.GetOccupiedBikeLockStateAsync(bike.Id);
|
|
Log.Debug($"Current lock state of bike {bike.Id} is {(lockingState.HasValue ? lockingState.Value.ToString() : "-")}.");
|
|
}
|
|
|
|
// Check if bike is still occupied.
|
|
if (lockingState == null)
|
|
{
|
|
// User did not take bike out of the station
|
|
throw new BikeStillInStationException("Booking was canceled because bike is still in station.");
|
|
}
|
|
|
|
// Update booking state.
|
|
bike.Load(
|
|
response,
|
|
mailAddress,
|
|
Model.Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.None);
|
|
|
|
// Update locking state.
|
|
bike.LockInfo.State = lockingState.Value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a bike and closes the lock.
|
|
/// </summary>
|
|
/// <param name="copriServer"> Instance to communicate with backend.</param>
|
|
/// <param name="bike">Bike to close.</param>
|
|
public static async Task CloseAync(
|
|
this ICopriServerBase copriServer,
|
|
IBikeInfoMutable bike)
|
|
{
|
|
if (!(copriServer is ICachedCopriServer cachedServer))
|
|
throw new ArgumentNullException(nameof(copriServer));
|
|
|
|
// Send command to close lock
|
|
await copriServer.UpdateLockingStateAsync(
|
|
bike.Id,
|
|
Repository.Request.lock_state.locking,
|
|
bike.OperatorUri);
|
|
|
|
var lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
|
|
|
var watch = new Stopwatch();
|
|
watch.Start();
|
|
|
|
while (lockingState != LockingState.Closed
|
|
&& lockingState != LockingState.UnknownDisconnected
|
|
&& watch.Elapsed < TimeSpan.FromMilliseconds(OPEN_CLOSE_TIMEOUT_MS))
|
|
{
|
|
// Delay a litte to reduce load on backend.
|
|
await Task.Delay(3000);
|
|
|
|
lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
|
Log.Information($"Current lock state is {lockingState}.");
|
|
}
|
|
|
|
// Update locking state.
|
|
bike.LockInfo.State = lockingState;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a bike and closes the lock.
|
|
/// </summary>
|
|
/// <param name="copriServer"> Instance to communicate with backend.</param>
|
|
/// <param name="smartDevice">Smart device on which app runs on.</param>
|
|
/// <param name="mailAddress">Mail address of user which books bike.</param>
|
|
public static async Task<BookingFinishedModel> ReturnAndCloseAync(
|
|
this ICopriServerBase copriServer,
|
|
ISmartDevice smartDevice,
|
|
IBikeInfoMutable bike)
|
|
{
|
|
if (!(copriServer is ICachedCopriServer cachedServer))
|
|
throw new ArgumentNullException(nameof(copriServer));
|
|
|
|
// Send command to open lock
|
|
DoReturnResponse response =
|
|
await copriServer.ReturnAndStartClosingAsync(bike.Id, bike.OperatorUri);
|
|
|
|
// Update booking state
|
|
bike.Load(
|
|
Model.Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.None,
|
|
response.bike_returned.station ?? string.Empty);
|
|
|
|
var lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
|
|
|
var watch = new Stopwatch();
|
|
watch.Start();
|
|
|
|
while (lockingState != LockingState.Closed
|
|
&& lockingState != LockingState.UnknownDisconnected
|
|
&& watch.Elapsed < TimeSpan.FromMilliseconds(OPEN_CLOSE_TIMEOUT_MS))
|
|
{
|
|
// Delay a litte to reduce load on backend.
|
|
await Task.Delay(3000);
|
|
|
|
lockingState = await cachedServer.GetLockStateAsync(bike.Id);
|
|
Log.Information($"Current lock state is {lockingState}.");
|
|
}
|
|
|
|
// Update locking state.
|
|
bike.LockInfo.State = lockingState;
|
|
|
|
return response?.Create() ?? new BookingFinishedModel();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Queries the locking state from copri.
|
|
/// </summary>
|
|
/// <param name="copriServer">Service to use.</param>
|
|
/// <param name="bikeId">Bike id to query lock state for.</param>
|
|
/// <returns>Locking state</returns>
|
|
private static async Task<LockingState> GetLockStateAsync(
|
|
this ICachedCopriServer copriServer,
|
|
string bikeId)
|
|
{
|
|
// Querry reserved or booked bikes first for performance reasons.
|
|
var bikeReservedOrBooked = (await copriServer.GetBikesOccupied(false))?.Response.bikes_occupied?.Values?.FirstOrDefault(x => x.bike == bikeId);
|
|
if (bikeReservedOrBooked != null)
|
|
{
|
|
return bikeReservedOrBooked.GetCopriLockingState();
|
|
}
|
|
|
|
var bikeAvailable = (await copriServer.GetBikesAvailable(false))?.Response.bikes?.Values?.FirstOrDefault(x => x.bike == bikeId);
|
|
if (bikeAvailable != null)
|
|
{
|
|
return bikeAvailable.GetCopriLockingState();
|
|
}
|
|
|
|
return LockingState.UnknownDisconnected;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Queries the locking state of a occupied bike from copri.
|
|
/// </summary>
|
|
/// <param name="copriServer">Service to use.</param>
|
|
/// <param name="bikeId">Bike id to query lock state for.</param>
|
|
/// <returns>Locking state if bike is still occupied, null otherwise.</returns>
|
|
private static async Task<LockingState?> GetOccupiedBikeLockStateAsync(
|
|
this ICachedCopriServer copriServer,
|
|
string bikeId)
|
|
{
|
|
var bikeReservedOrBooked = (await copriServer.GetBikesOccupied(false))?.Response.bikes_occupied?.Values?.FirstOrDefault(x => x.bike == bikeId);
|
|
if (bikeReservedOrBooked != null)
|
|
{
|
|
return bikeReservedOrBooked.GetCopriLockingState();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|