mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2024-11-05 10:36:30 +01:00
182 lines
7 KiB
C#
182 lines
7 KiB
C#
|
using System.Collections.Generic;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using System.Linq;
|
|||
|
using TINK.Services.BluetoothLock.Tdo;
|
|||
|
using TINK.Model.Connector;
|
|||
|
using System;
|
|||
|
using Serilog;
|
|||
|
using TINK.Model.Device;
|
|||
|
using Xamarin.Essentials;
|
|||
|
using TINK.Model.Bike.BluetoothLock;
|
|||
|
|
|||
|
namespace TINK.Services.BluetoothLock.BLE
|
|||
|
{
|
|||
|
public abstract class LockItServiceBase
|
|||
|
{
|
|||
|
/// <summary>Constructs base object.</summary>
|
|||
|
/// <param name="cipher">Encrpyting/ decrypting object.</param> /// <param name="connectTimeout">Timeout to apply when connecting to bluetooth lock.</param>
|
|||
|
public LockItServiceBase(ICipher cipher)
|
|||
|
{
|
|||
|
Cipher = cipher;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary> Holds timeout values for series of connecting attemps to a lock or multiple locks. </summary>
|
|||
|
public ITimeOutProvider TimeOut { get; set; }
|
|||
|
|
|||
|
protected ICipher Cipher { get; }
|
|||
|
|
|||
|
/// <summary> List of available or connected bluetooth devices. </summary>
|
|||
|
protected List<ILockService> DeviceList = new List<ILockService>();
|
|||
|
|
|||
|
|
|||
|
/// <summary> Reconnect locks of interest if required. </summary>
|
|||
|
/// <remarks> Consists of a bluetooth connect plus invocation of an authentication sequence for each lock to be reconnected. </remarks>
|
|||
|
/// <param name="locksInfo">Locks to reconnect.</param>
|
|||
|
/// <param name="connectTimeout">Timeout for connect operation.</param>
|
|||
|
public static async Task CheckReconnect(
|
|||
|
IEnumerable<ILockService> deviceList,
|
|||
|
IEnumerable<LockInfoAuthTdo> locksInfo,
|
|||
|
TimeSpan connectTimeout)
|
|||
|
{
|
|||
|
// Get list of target locks without invalid entries.
|
|||
|
var validLocksInfo = locksInfo
|
|||
|
.Where(x => x.IsIdValid || x.IsGuidValid)
|
|||
|
.Where(x => x.K_seed.Length > 0 && x.K_u.Length > 0)
|
|||
|
.ToList();
|
|||
|
|
|||
|
foreach (var device in deviceList)
|
|||
|
{
|
|||
|
if (device.GetDeviceState() == DeviceState.Connected)
|
|||
|
{
|
|||
|
// No need to reconnect device because device is already connected.
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
var lockInfo = validLocksInfo.FirstOrDefault(x => x.Id == device. Name.GetBluetoothLockId() || x.Guid == device.Guid);
|
|||
|
|
|||
|
if (lockInfo == null)
|
|||
|
{
|
|||
|
// Current lock from deviceList is not of interest detected, no need to reconnect.
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
await device.ReconnectAsync(lockInfo, connectTimeout);
|
|||
|
}
|
|||
|
catch (System.Exception exception)
|
|||
|
{
|
|||
|
// Member is called for background update of missing devices.
|
|||
|
// Do not display any error messages.
|
|||
|
Log.ForContext<LockItServiceBase>().Error($"Reconnect failed. {exception.Message}");
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary> Gets the state for locks of interest.</summary>
|
|||
|
/// <remarks>
|
|||
|
/// Might require a
|
|||
|
/// - connect to lock
|
|||
|
/// - reconnect to lock
|
|||
|
/// </remarks>
|
|||
|
/// <param name="locksInfo">Locks toget info for.</param>
|
|||
|
/// <param name="connectTimeout">Timeout for connect operation of a single lock.</param>
|
|||
|
public async Task<IEnumerable<LockInfoTdo>> GetLocksStateAsync(
|
|||
|
IEnumerable<LockInfoAuthTdo> locksInfo,
|
|||
|
TimeSpan connectTimeout)
|
|||
|
{
|
|||
|
if (locksInfo.Count() == 0)
|
|||
|
{
|
|||
|
// Nothing to do.
|
|||
|
return new List<LockInfoTdo>();
|
|||
|
}
|
|||
|
|
|||
|
// Reconnect locks.
|
|||
|
await CheckReconnect(DeviceList, locksInfo, connectTimeout);
|
|||
|
|
|||
|
// Connect to locks which were not yet discovered.
|
|||
|
DeviceList.AddRange(await CheckConnectMissing(locksInfo, connectTimeout));
|
|||
|
|
|||
|
// Get devices for which to update state.
|
|||
|
var locksInfoState = new List<LockInfoTdo>();
|
|||
|
|
|||
|
foreach (var device in DeviceList)
|
|||
|
{
|
|||
|
var lockInfoAuth = locksInfo.FirstOrDefault(x => x.Guid == device.Guid || x.Id == device.Id);
|
|||
|
if (lockInfoAuth == null)
|
|||
|
{
|
|||
|
// Device is not of interest.
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
var state = await device.GetLockStateAsync();
|
|||
|
locksInfoState.Add(new LockInfoTdo.Builder
|
|||
|
{
|
|||
|
Id = lockInfoAuth.Id,
|
|||
|
Guid = lockInfoAuth.IsGuidValid ? lockInfoAuth.Guid : device.Guid,
|
|||
|
State = state?.State
|
|||
|
}.Build());
|
|||
|
}
|
|||
|
|
|||
|
return locksInfoState;
|
|||
|
}
|
|||
|
|
|||
|
/// <remarks> Consists of a bluetooth connect plus invocation of an authentication sequence. </remarks>
|
|||
|
/// <param name="locksInfo">Locks to reconnect.</param>
|
|||
|
/// <param name="connectTimeout">Timeout for connect operation of a single lock.</param>
|
|||
|
protected abstract Task<IEnumerable<ILockService>> CheckConnectMissing(
|
|||
|
IEnumerable<LockInfoAuthTdo> locksInfo,
|
|||
|
TimeSpan connectTimeout);
|
|||
|
|
|||
|
/// <summary>Gets a lock by bike Id.</summary>
|
|||
|
/// <param name="bikeId"></param>
|
|||
|
/// <returns>Lock object</returns>
|
|||
|
public ILockService this[int bikeId]
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
var device = DeviceList.FirstOrDefault(x => x.Name.GetBluetoothLockId() == bikeId);
|
|||
|
if (device == null)
|
|||
|
{
|
|||
|
return new NullLock(bikeId);
|
|||
|
}
|
|||
|
|
|||
|
return device;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary> Connects to lock.</summary>
|
|||
|
/// <remarks> Consists of a bluetooth connect plus invocation of an authentication sequence. </remarks>
|
|||
|
/// <param name="authInfo"> Info required to connect to lock.</param>
|
|||
|
/// <param name="connectTimeout">Timeout for connect operation.</param>
|
|||
|
public async Task<LockingState> DisconnectAsync(int bikeId, Guid bikeGuid)
|
|||
|
{
|
|||
|
if (DeviceInfo.Platform != DevicePlatform.Unknown && MainThread.IsMainThread == false)
|
|||
|
{
|
|||
|
throw new System.Exception("Can not disconnect from lock. Bluetooth code must be run on main thread");
|
|||
|
}
|
|||
|
|
|||
|
Log.ForContext<LockItByScanServiceBase>().Debug($"Request to disconnect from device {bikeId}/ {bikeGuid}.");
|
|||
|
|
|||
|
var lockIt = DeviceList.FirstOrDefault(x => x.Name.GetBluetoothLockId() == bikeId || x.Guid == bikeGuid);
|
|||
|
|
|||
|
if (lockIt == null)
|
|||
|
{
|
|||
|
// Nothing to do
|
|||
|
return LockingState.Disconnected;
|
|||
|
}
|
|||
|
|
|||
|
DeviceList.Remove(lockIt);
|
|||
|
|
|||
|
if (lockIt.GetDeviceState() == DeviceState.Disconnected)
|
|||
|
{
|
|||
|
// Nothing to do
|
|||
|
return LockingState.Disconnected;
|
|||
|
}
|
|||
|
|
|||
|
await lockIt.Disconnect();
|
|||
|
return LockingState.Disconnected;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|