2021-05-13 20:03:07 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using Serilog;
|
2022-08-30 15:42:25 +02:00
|
|
|
|
using TINK.Model.Bikes;
|
|
|
|
|
using TINK.Model.State;
|
|
|
|
|
using TINK.Model.Station;
|
2021-07-20 23:06:09 +02:00
|
|
|
|
using TINK.Model.Station.Operator;
|
2022-08-30 15:42:25 +02:00
|
|
|
|
using TINK.Model.User.Account;
|
|
|
|
|
using TINK.Repository.Exception;
|
|
|
|
|
using TINK.Repository.Response;
|
2022-01-04 18:54:03 +01:00
|
|
|
|
using TINK.Services.CopriApi;
|
2022-08-30 15:42:25 +02:00
|
|
|
|
using Xamarin.Forms;
|
|
|
|
|
using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
|
|
|
|
|
using IBikeInfoMutable = TINK.Model.Bikes.BikeInfoNS.BC.IBikeInfoMutable;
|
2021-05-13 20:03:07 +02:00
|
|
|
|
|
2022-08-30 15:42:25 +02:00
|
|
|
|
namespace TINK.Model.Connector.Updater
|
2021-05-13 20:03:07 +02:00
|
|
|
|
{
|
2022-09-06 16:08:19 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Connects TINK app to copri using JSON as input data format.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <todo>Rename to UpdateFromCopri.</todo>
|
|
|
|
|
public static class UpdaterJSON
|
|
|
|
|
{
|
|
|
|
|
/// <summary> Loads a bike object from copri server cancel reservation/ booking update request.</summary>
|
|
|
|
|
/// <param name="bike">Bike object to load response into.</param>
|
|
|
|
|
/// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
|
|
|
|
|
public static void Load(
|
|
|
|
|
this IBikeInfoMutable bike,
|
|
|
|
|
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
bike.State.Load(InUseStateEnum.Disposable, notifyLevel: notifyLevel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets all statsion for station provider and add them into station list.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="p_oStationList">List of stations to update.</param>
|
|
|
|
|
public static StationDictionary GetStationsAllMutable(this StationsAvailableResponse stationsAllResponse)
|
|
|
|
|
{
|
|
|
|
|
// Get stations from Copri/ file/ memory, ....
|
|
|
|
|
if (stationsAllResponse == null
|
|
|
|
|
|| stationsAllResponse.stations == null)
|
|
|
|
|
{
|
|
|
|
|
// Latest list of stations could not be retrieved from provider.
|
|
|
|
|
return new StationDictionary();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Version.TryParse(stationsAllResponse.copri_version, out Version copriVersion);
|
|
|
|
|
|
|
|
|
|
var stations = new StationDictionary(p_oVersion: copriVersion);
|
|
|
|
|
|
|
|
|
|
foreach (var station in stationsAllResponse.stations)
|
|
|
|
|
{
|
|
|
|
|
if (stations.GetById(station.Value.station) != null)
|
|
|
|
|
{
|
|
|
|
|
// Can not add station to list of station. Id is not unique.
|
|
|
|
|
throw new InvalidResponseException<StationsAvailableResponse>(
|
|
|
|
|
string.Format("Station id {0} is not unique.", station.Value.station), stationsAllResponse);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stations.Add(new Station.Station(
|
|
|
|
|
station.Value.station,
|
|
|
|
|
station.Value.GetGroup(),
|
|
|
|
|
station.Value.GetPosition(),
|
|
|
|
|
station.Value.description,
|
|
|
|
|
new Data(station.Value.operator_data?.operator_name,
|
|
|
|
|
station.Value.operator_data?.operator_phone,
|
|
|
|
|
station.Value.operator_data?.operator_hours,
|
|
|
|
|
station.Value.operator_data?.operator_email,
|
|
|
|
|
!string.IsNullOrEmpty(station.Value.operator_data?.operator_color)
|
|
|
|
|
? Color.FromHex(station.Value.operator_data?.operator_color)
|
|
|
|
|
: (Color?)null)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return stations;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets general data from COPRI response.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="response">Response to get data from.</param>
|
|
|
|
|
/// <returns>General data object initialized form COPRI response.</returns>
|
|
|
|
|
public static GeneralData GetGeneralData(this ResponseBase response)
|
|
|
|
|
=> new GeneralData(
|
|
|
|
|
response.init_map.GetMapSpan(),
|
|
|
|
|
response.merchant_message,
|
|
|
|
|
response.TryGetCopriVersion(out Version copriVersion)
|
|
|
|
|
? new Version(0, 0)
|
|
|
|
|
: copriVersion,
|
|
|
|
|
new ResourceUrls(response.tariff_info_html, response.bike_info_html, response.agb_html, response.privacy_html, response.impress_html));
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets account object from login response.</summary>
|
|
|
|
|
/// <param name="merchantId">Needed to extract cookie from autorization response.</param>
|
|
|
|
|
/// <param name="loginResponse">Response to get session cookie and debug level from.</param>
|
|
|
|
|
/// <param name="mail">Mail address needed to construct a complete account object (is not part of response).</param>
|
|
|
|
|
/// <param name="password">Password needed to construct a complete account object (is not part of response).</param>
|
|
|
|
|
public static IAccount GetAccount(
|
|
|
|
|
this AuthorizationResponse loginResponse,
|
|
|
|
|
string merchantId,
|
|
|
|
|
string mail,
|
|
|
|
|
string password)
|
|
|
|
|
{
|
|
|
|
|
if (loginResponse == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(loginResponse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Account(
|
|
|
|
|
mail,
|
|
|
|
|
password,
|
|
|
|
|
loginResponse.GetIsAgbAcknowledged(),
|
|
|
|
|
loginResponse.authcookie?.Replace(merchantId, ""),
|
|
|
|
|
loginResponse.GetGroup(),
|
|
|
|
|
loginResponse.debuglevel == 1
|
|
|
|
|
? Permissions.All :
|
|
|
|
|
(Permissions)loginResponse.debuglevel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Load bike object from booking response. </summary>
|
|
|
|
|
/// <param name="bike">Bike object to load from response.</param>
|
|
|
|
|
/// <param name="bikeInfo">Booking response.</param>
|
|
|
|
|
/// <param name="mailAddress">Mail address of user which books bike.</param>
|
|
|
|
|
/// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
|
|
|
|
|
public static void Load(
|
|
|
|
|
this IBikeInfoMutable bike,
|
|
|
|
|
BikeInfoReservedOrBooked bikeInfo,
|
|
|
|
|
string mailAddress,
|
|
|
|
|
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.All)
|
|
|
|
|
{
|
|
|
|
|
if (bike is Bikes.BikeInfoNS.BluetoothLock.BikeInfoMutable btBikeInfo)
|
|
|
|
|
{
|
|
|
|
|
btBikeInfo.LockInfo.Load(
|
|
|
|
|
bikeInfo.GetBluetoothLockId(),
|
|
|
|
|
bikeInfo.GetBluetoothLockGuid(),
|
|
|
|
|
bikeInfo.GetSeed(),
|
|
|
|
|
bikeInfo.GetUserKey(),
|
|
|
|
|
bikeInfo.GetAdminKey());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var l_oState = bikeInfo.GetState();
|
|
|
|
|
switch (l_oState)
|
|
|
|
|
{
|
|
|
|
|
case InUseStateEnum.Disposable:
|
|
|
|
|
bike.State.Load(
|
|
|
|
|
InUseStateEnum.Disposable,
|
|
|
|
|
notifyLevel: notifyLevel);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case InUseStateEnum.Reserved:
|
|
|
|
|
bike.State.Load(
|
|
|
|
|
InUseStateEnum.Reserved,
|
|
|
|
|
bikeInfo.GetFrom(),
|
|
|
|
|
mailAddress,
|
|
|
|
|
bikeInfo.timeCode,
|
|
|
|
|
notifyLevel);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case InUseStateEnum.Booked:
|
|
|
|
|
bike.State.Load(
|
|
|
|
|
InUseStateEnum.Booked,
|
|
|
|
|
bikeInfo.GetFrom(),
|
|
|
|
|
mailAddress,
|
|
|
|
|
bikeInfo.timeCode,
|
|
|
|
|
notifyLevel);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new Exception(string.Format("Unexpected bike state detected. state is {0}.", l_oState));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets bikes available from copri server response.</summary>
|
|
|
|
|
/// <param name="bikesAvailableResponse">Response to create collection from.</param>
|
|
|
|
|
/// <returns>New collection of available bikes.</returns>
|
|
|
|
|
public static BikeCollection GetBikesAvailable(
|
|
|
|
|
this BikesAvailableResponse bikesAvailableResponse)
|
|
|
|
|
=> GetBikesAll(
|
|
|
|
|
bikesAvailableResponse?.bikes?.Values,
|
|
|
|
|
new BikesReservedOccupiedResponse()?.bikes_occupied?.Values, // There are no occupied bikes.
|
|
|
|
|
string.Empty,
|
|
|
|
|
() => DateTime.Now);
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets bikes occupied from copri server response. </summary>
|
|
|
|
|
/// <param name="p_oBikesAvailable">Response to create bikes from.</param>
|
|
|
|
|
/// <returns>New collection of occupied bikes.</returns>
|
|
|
|
|
public static BikeCollection GetBikesOccupied(
|
|
|
|
|
this BikesReservedOccupiedResponse bikesOccupiedResponse,
|
|
|
|
|
string mail,
|
|
|
|
|
Func<DateTime> dateTimeProvider)
|
|
|
|
|
{
|
|
|
|
|
return GetBikesAll(
|
|
|
|
|
new BikesAvailableResponse()?.bikes?.Values,
|
|
|
|
|
bikesOccupiedResponse?.bikes_occupied?.Values,
|
|
|
|
|
mail,
|
|
|
|
|
dateTimeProvider);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets bikes occupied from copri server response. </summary>
|
|
|
|
|
/// <param name="bikesAvailable">Response to create bikes available from.</param>
|
|
|
|
|
/// <param name="bikesOccupied">Response to create bikes occupied from.</param>
|
|
|
|
|
/// <returns>New collection of occupied bikes.</returns>
|
|
|
|
|
public static BikeCollection GetBikesAll(
|
|
|
|
|
IEnumerable<BikeInfoAvailable> bikesAvailable,
|
|
|
|
|
IEnumerable<BikeInfoReservedOrBooked> bikesOccupied,
|
|
|
|
|
string mail,
|
|
|
|
|
Func<DateTime> dateTimeProvider)
|
|
|
|
|
{
|
|
|
|
|
var bikesDictionary = new Dictionary<string, BikeInfo>();
|
|
|
|
|
var duplicates = new Dictionary<string, BikeInfo>();
|
|
|
|
|
|
|
|
|
|
// Get bikes from Copri/ file/ memory, ....
|
|
|
|
|
if (bikesAvailable != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var bikeInfoResponse in bikesAvailable)
|
|
|
|
|
{
|
|
|
|
|
var bikeInfo = BikeInfoFactory.Create(bikeInfoResponse);
|
|
|
|
|
if (bikeInfo == null)
|
|
|
|
|
{
|
|
|
|
|
// Response is not valid.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bikesDictionary.ContainsKey(bikeInfo.Id))
|
|
|
|
|
{
|
|
|
|
|
// Duplicates are not allowed.
|
|
|
|
|
Log.Error($"Duplicate bike with id {bikeInfo.Id} detected evaluating bikes available. Bike status is {bikeInfo.State.Value}.");
|
|
|
|
|
|
|
|
|
|
if (!duplicates.ContainsKey(bikeInfo.Id))
|
|
|
|
|
{
|
|
|
|
|
duplicates.Add(bikeInfo.Id, bikeInfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bikesDictionary.Add(bikeInfo.Id, bikeInfo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get bikes from Copri/ file/ memory, ....
|
|
|
|
|
if (bikesOccupied != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var bikeInfoResponse in bikesOccupied)
|
|
|
|
|
{
|
|
|
|
|
BikeInfo bikeInfo = BikeInfoFactory.Create(
|
|
|
|
|
bikeInfoResponse,
|
|
|
|
|
mail,
|
|
|
|
|
dateTimeProvider);
|
|
|
|
|
|
|
|
|
|
if (bikeInfo == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bikesDictionary.ContainsKey(bikeInfo.Id))
|
|
|
|
|
{
|
|
|
|
|
// Duplicates are not allowed.
|
|
|
|
|
Log.Error($"Duplicate bike with id {bikeInfo.Id} detected evaluating bikes occupied. Bike status is {bikeInfo.State.Value}.");
|
|
|
|
|
if (!duplicates.ContainsKey(bikeInfo.Id))
|
|
|
|
|
{
|
|
|
|
|
duplicates.Add(bikeInfo.Id, bikeInfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bikesDictionary.Add(bikeInfo.Id, bikeInfo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove entries which are not unique.
|
|
|
|
|
foreach (var l_oDuplicate in duplicates)
|
|
|
|
|
{
|
|
|
|
|
bikesDictionary.Remove(l_oDuplicate.Key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new BikeCollection(bikesDictionary);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-13 20:03:07 +02:00
|
|
|
|
}
|