Version 3.0.375

This commit is contained in:
Anja 2023-11-06 12:23:09 +01:00
parent 2c790239cb
commit ca080c87c0
194 changed files with 10092 additions and 10464 deletions

View file

@ -104,7 +104,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <summary>
/// Converts the instance to text.
/// </summary>
public new string ToString()
public override string ToString()
{
return $"Id={Bike.Id}{(Bike.WheelType != null ? $", wheel(s)={Bike.WheelType}" : string.Empty)}{(Bike.TypeOfBike != null ? $"type={Bike.TypeOfBike}" : "")}, state={State}, location={(!string.IsNullOrEmpty(StationId) ? $"Station {StationId}" : "On the road")}, is demo={IsDemo}.";
}

View file

@ -149,7 +149,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <summary>
/// Converts the instance to text.
/// </summary>
public new string ToString()
public override string ToString()
{
return $"Id={Id}{(WheelType != null ? $", wheel(s)={WheelType}" : string.Empty)}{(TypeOfBike != null ? $", type={TypeOfBike}" : "")}, demo={IsDemo}, state={State.ToString()}, location={(!string.IsNullOrEmpty(StationId) ? $"Station {StationId}" : "On the road")}.";
}

View file

@ -39,7 +39,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// </summary>
Uri OperatorUri { get; }
/// <summary> Holds description about the tarif. </summary>
/// <summary> Holds description about the tariff. </summary>
RentalDescription TariffDescription { get; }
/// <summary>

View file

@ -107,7 +107,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BikeNS
}
/// <summary> Converts the instance to text.</summary>
public new string ToString()
public override string ToString()
{
return WheelType == null || TypeOfBike == null
? $"Id={Id}{(!string.IsNullOrEmpty(Description) ? $", {Description}" : "")}"

View file

@ -110,9 +110,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
LockService,
listener: listener);
public new string ToString()
public override string ToString()
{
return $"Id={Id}{(TypeOfBike != null ? $";type={TypeOfBike}" : "")};state={State.ToString()}";
return $"Id={Id}{(TypeOfBike != null ? $";type={TypeOfBike}" : "")};state={State.ToString()};Lock id={LockInfo.Id}";
}
}
}

View file

@ -10,6 +10,7 @@ using TINK.Services.BluetoothLock;
using TINK.Services.BluetoothLock.Exception;
using TINK.Services.BluetoothLock.Tdo;
using TINK.Services.Geolocation;
using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler;
namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
{
@ -91,9 +92,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
{
listener.ReportStep(step);
}
catch (Exception ex)
catch (Exception exception)
{
Log.ForContext<T>().Error("An exception {exception} was thrown invoking step- action for set {step} ", ex, step);
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking step-action for step {step} ", exception, step);
}
}
@ -107,9 +108,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
{
await listener.ReportStateAsync(state, message);
}
catch (Exception ex)
catch (Exception exception)
{
Log.ForContext<T>().Error("An exception {exception} was thrown invoking state- action for set {state} ", ex, state);
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking state-action for state {state} ", exception, state);
}
}
@ -118,21 +119,21 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
{
// Step: Wait until getting geolocation has completed.
InvokeCurrentStep(Step.WaitStopPollingQueryLocation);
Log.ForContext<T>().Debug($"Waiting on steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} to finish...");
Log.ForContext<T>().Information($"Waiting on steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} to finish...");
try
{
await Task.WhenAll(new List<Task> { locationTask, stopPollingTask ?? Task.CompletedTask });
}
catch (Exception ex)
catch (Exception exception)
{
// No location information available.
Log.ForContext<T>().Information("Canceling query location/ wait for polling task to finish failed. {Exception}", ex);
await InvokeCurrentStateAsync(State.WaitGeolocationException, ex.Message);
Log.ForContext<T>().Information("Canceling query location/ wait for polling task to finish failed. {@exception}", exception);
await InvokeCurrentStateAsync(State.WaitGeolocationException, exception.Message);
InvokeCurrentStep(Step.QueryLocationTerminated);
return null;
}
Log.ForContext<T>().Debug($"Steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} finished.");
Log.ForContext<T>().Information($"Steps {Step.StartingQueryingLocation} and {Step.StartStopingPolling} finished.");
InvokeCurrentStep(Step.QueryLocationTerminated);
return locationTask.Result;
}
@ -155,21 +156,23 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
Age = timeStamp.Subtract(location.Timestamp.DateTime),
}.Build()
: null);
Log.ForContext<T>().Information("Backend updated for bike {bikeId} successfully.", bike.Id);
}
catch (Exception exception)
{
Log.ForContext<ReservedOpen>().Information("Updating backend for bike {bikeId} failed.", bike.Id);
//BikesViewModel.RentalProcess.Result = CurrentStepStatus.Failed;
if (exception is WebConnectFailureException)
{
// Copri server is not reachable.
Log.ForContext<T>().Information("User locked bike {bike} in order to pause ride but updating failed (Copri server not reachable).", bike);
Log.ForContext<T>().Debug("Copri server not reachable.");
await InvokeCurrentStateAsync(State.WebConnectFailed, exception.Message);
return;
}
else if (exception is ResponseException copriException)
{
// Copri server is not reachable.
Log.ForContext<T>().Information("User locked bike {bike} in order to pause ride but updating failed. Message: {Message} Details: {Details}", bike, copriException.Message, copriException.Response);
Log.ForContext<T>().Debug("Message: {Message} Details: {Details}", copriException.Message, copriException.Response);
await InvokeCurrentStateAsync(State.ResponseIsInvalid, exception.Message);
return;
}
@ -192,48 +195,49 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
try
{
currentLocationTask = geolocation.GetAsync(ctsLocation.Token, timeStampNow);
Log.ForContext<T>().Information("Starting query location successful.");
}
catch (Exception ex)
catch (Exception exception)
{
// No location information available.
Log.ForContext<T>().Information("Starting query location failed. {Exception}", bike, ex);
await InvokeCurrentStateAsync(State.StartGeolocationException, ex.Message);
Log.ForContext<T>().Information("Starting query location failed. {@exception}", exception);
await InvokeCurrentStateAsync(State.StartGeolocationException, exception.Message);
}
//// Step: Close lock.
IGeolocation currentLocation;
Log.ForContext<T>().Debug($"Starting step {Step.ClosingLock}...");
Log.ForContext<T>().Information($"Starting step {Step.ClosingLock}...");
InvokeCurrentStep(Step.ClosingLock);
LockitLockingState? lockingState;
try
{
lockingState = await lockService[bike.LockInfo.Id].CloseAsync();
Log.ForContext<T>().Information("Lock of bike {bikeId} closed successfully.", bike.Id);
}
catch (Exception exception)
{
Log.ForContext<T>().Information("Lock of bike {bikeId} can not be closed.", bike.Id);
if (exception is OutOfReachException)
{
Log.ForContext<T>().Debug("Lock can not be closed. {Exception}", exception);
Log.ForContext<T>().Debug("Lock is out of reach");
await InvokeCurrentStateAsync(State.OutOfReachError, exception.Message);
}
else if (exception is CouldntCloseMovingException)
{
Log.ForContext<T>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
Log.ForContext<T>().Debug("Lock is moving.");
await InvokeCurrentStateAsync(State.CouldntCloseMovingError, exception.Message);
}
else if (exception is CouldntCloseBoltBlockedException)
{
Log.ForContext<T>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
Log.ForContext<T>().Debug("Bold is blocked.}");
await InvokeCurrentStateAsync(State.CouldntCloseBoltBlockedError, exception.Message);
}
else
{
Log.ForContext<T>().Debug("Lock can not be closed. Lock is out of reach. {Exception}", exception);
Log.ForContext<T>().Debug("{@exception}", exception);
await InvokeCurrentStateAsync(State.GeneralCloseError, exception.Message);
}
Log.ForContext<T>().Error("Lock can not be closed. {Exception}", exception);
// Signal cts to cancel getting geolocation.
ctsLocation.Cancel();

View file

@ -75,9 +75,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
try
{
listener.ReportStep(step);
} catch (Exception ex)
} catch (Exception exception)
{
Log.ForContext<T>().Error("An exception {exception} was thrown invoking step- action for set {step} ", ex, step);
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking step-action for step {step} ", exception, step);
}
}
@ -91,9 +91,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
{
await listener.ReportStateAsync(state, message);
}
catch (Exception ex)
catch (Exception exception)
{
Log.ForContext<T>().Error("An exception {exception} was thrown invoking state- action for set {state} ", ex, state);
Log.ForContext<T>().Error("An exception {@exception} was thrown invoking state-action for state {state} ", exception, state);
}
}
@ -121,8 +121,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
if (deviceState != DeviceState.Connected)
{
// Geolocation can not be queried because bike is not around.
Log.ForContext<T>().Information("User selected booked bike {bike} but returning failed. There is no geolocation information available.", bike);
Log.ForContext<T>().Information("User selected booked bike {bikeId} but returning failed. There is no geolocation information available.", bike.Id);
await InvokeCurrentStateAsync(State.DisconnetedNoLocationError, "");
//// Step: Disconnect lock.
@ -130,11 +129,11 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
try
{
bike.LockInfo.State = await lockService.DisconnectAsync(bike.LockInfo.Id, bike.LockInfo.Guid);
Log.ForContext<T>().Information("Lock from bike {bikeId} disconnected successfully.", bike.Id);
}
catch (Exception exception)
{
Log.ForContext<T>().Error("Lock can not be disconnected. {Exception}", exception);
Log.ForContext<T>().Information("Lock from bike {bikeId} can not be disconnected. {@exception}", bike.Id, exception);
await InvokeCurrentStateAsync(State.DisconnectError, exception.Message);
}
@ -148,20 +147,19 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command
try
{
closingLockLocation = await geolocation.GetAsync(ctsLocation.Token, DateTime.Now);
Log.ForContext<T>().Information("Query location of lock from bike {bikeId} successful.");
}
catch (Exception ex)
catch (Exception exception)
{
// No location information available.
Log.ForContext<T>().Information("Returning closed bike {Bike} is not possible. Geolocation query failed. {Exception}", bike, ex);
await InvokeCurrentStateAsync(State.QueryLocationFailed, ex.Message);
Log.ForContext<T>().Information("Geolocation query failed. {@exception}", exception);
await InvokeCurrentStateAsync(State.QueryLocationFailed, exception.Message);
throw;
}
await InvokeCurrentStateAsync(State.QueryLocationSucceeded, string.Empty);
// Update last lock state time
// save geolocation data for sending to backend
var currentLocationDto = closingLockLocation != null
? new LocationDto.Builder

View file

@ -185,7 +185,7 @@ namespace TINK.Model.Connector
}
/// <summary> Updates COPRI lock state for a booked bike. </summary>
/// <summary> Updates COPRI lock state for a booked or reserved bike. </summary>
/// <param name="bike">Bike to update locking state for.</param>
/// <param name="location">Location of the bike.</param>
/// <returns>Response on updating locking state.</returns>
@ -198,7 +198,7 @@ namespace TINK.Model.Connector
throw new ArgumentNullException("Can not update locking state of bike. No bike object available.");
}
if (bike.State.Value != State.InUseStateEnum.Booked)
if (bike.State.Value != State.InUseStateEnum.Booked && bike.State.Value != State.InUseStateEnum.Reserved)
{
throw new ArgumentNullException($"Can not update locking state of bike. Unexpected booking state {bike.State} detected.");
}

View file

@ -11,12 +11,12 @@ namespace TINK.Model.Connector
public class ConnectorCache : IConnector
{
/// <summary>Constructs a copri connector object to connect to cache.</summary>
/// <remarks>Used for offline szenario to ensure responsiveness of app by preventing hopeless tries to communicate with COPRI. </remarks>
/// <remarks>Used for offline scenario to ensure responsiveness of app by preventing hopeless tries to communicate with COPRI. </remarks>
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
/// <param name="sessionCookie"> Holds the session cookie.</param>
/// <param name="mail">Mail of user.</param>
/// <param name="smartDevice">Holds info about smart device.</param>
/// <param name="server"> Is null in production and migh be a mock in testing context.</param>
/// <param name="server"> Is null in production and might be a mock in testing context.</param>
public ConnectorCache(
AppContextInfo appContextInfo,
string uiIsoLangugageName,
@ -36,10 +36,10 @@ namespace TINK.Model.Connector
mail);
}
/// <summary> Object for queriying stations and bikes.</summary>
/// <summary> Object for querying stations and bikes.</summary>
public ICommand Command { get; private set; }
/// <summary> Object for queriying stations and bikes.</summary>
/// <summary> Object for querying stations and bikes.</summary>
public IQuery Query { get; private set; }
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>

View file

@ -64,9 +64,10 @@ namespace TINK.Model.Connector
}
/// <summary> Gets bikes either bikes available if no user is logged in or bikes available and bikes occupied if a user is logged in. </summary>
public async Task<Result<BikeCollection>> GetBikesAsync()
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
{
var result = await m_oInnerQuery.GetBikesAsync();
var result = await m_oInnerQuery.GetBikesAsync(operatorUri);
return new Result<BikeCollection>(
result.Source,
new BikeCollection(DoFilter(result.Response, Filter)),
@ -129,6 +130,7 @@ namespace TINK.Model.Connector
station.Group,
station.Position,
station.StationName,
station.OperatorUri,
station.OperatorData,
new BikeGroupCol(station.BikeGroups
.Where(group => filter.DoFilter(new List<string> { group.Group }).Count() > 0))) as IStation)

View file

@ -1,11 +1,11 @@
namespace TINK.Model.Connector
namespace TINK.Model.Connector
{
public interface IConnector
{
/// <summary> Object for queriying stations and bikes.</summary>
/// <summary> Object for querying stations and bikes.</summary>
ICommand Command { get; }
/// <summary> Object for queriying stations and bikes.</summary>
/// <summary> Object for querying stations and bikes.</summary>
IQuery Query { get; }
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>

View file

@ -56,9 +56,10 @@ namespace TINK.Model.Connector
}
/// <summary> Gets bikes either bikes available if no user is logged in or bikes available and bikes occupied if a user is logged in. </summary>
public async Task<Result<BikeCollection>> GetBikesAsync()
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
{
var result = await m_oInnerQuery.GetBikesAsync();
var result = await m_oInnerQuery.GetBikesAsync(operatorUri);
return new Result<BikeCollection>(
result.Source,
new BikeCollection(result.Response.ToDictionary(x => x.Id)),

View file

@ -71,14 +71,15 @@ namespace TINK.Model.Connector
}
/// <summary> Gets bikes available. </summary>
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
/// <returns>Collection of bikes.</returns>
public async Task<Result<BikeCollection>> GetBikesAsync()
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
{
var result = await server.GetBikesAvailable();
var result = await server.GetBikesAvailable(operatorUri: operatorUri);
if (result.Source != typeof(CopriCallsMonkeyStore))
{
server.AddToCache(result);
server.AddToCache(result, operatorUri);
}

View file

@ -127,21 +127,24 @@ namespace TINK.Model.Connector
}
/// <summary> Gets bikes available and bikes occupied. </summary>
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
/// <returns>Collection of bikes.</returns>
public async Task<Result<BikeCollection>> GetBikesAsync()
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
{
var bikesAvailableResponse = await Server.GetBikesAvailable();
var bikesAvailableResponse = await Server.GetBikesAvailable(operatorUri: operatorUri);
if (bikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|| bikesAvailableResponse.Exception != null)
{
// Bikes available were read from cache ==> get bikes occupied from cache as well to avoid inconsistencies.
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available read from cache. Reading bikes occupied from cache as well.");
// Bikes were read from cache.
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available and bikes occupied from cache invoking one single call.");
return new Result<BikeCollection>(
bikesAvailableResponse.Source,
BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse.Response?.bikes?.Values,
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
operatorUri?.AbsoluteUri == null ?
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values // Get bikes occupied from cache as well to avoid inconsistencies.
: bikesAvailableResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache),
@ -149,7 +152,27 @@ namespace TINK.Model.Connector
bikesAvailableResponse.Exception);
}
var bikesOccupiedResponse = await Server.GetBikesOccupied();
if (operatorUri?.AbsoluteUri != null)
{
// Both types bikes could read from copri successfully => update cache
Server.AddToCache(bikesAvailableResponse, operatorUri);
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available and occupied read successfully from server invoking one single request.");
return new Result<BikeCollection>(
bikesAvailableResponse.Source,
BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse.Response?.bikes?.Values,
bikesAvailableResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Copri),
bikesAvailableResponse.GeneralData,
bikesAvailableResponse.Exception != null ? new AggregateException(new[] { bikesAvailableResponse.Exception }) : null);
}
/// Legacy implementation: GetBikesOccupied are not returned in <see cref="ICachedCopriServer.GetBikesAvailable"/> call.
/// A separate call <see cref="ICachedCopriServer.GetBikesOccupied"/> is required to retrieve all bikes.
var bikesOccupiedResponse = await Server.GetBikesOccupied(); /* Only query bikes occupied if operator uri is unknown. */
if (bikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|| bikesOccupiedResponse.Exception != null)
{
@ -158,7 +181,7 @@ namespace TINK.Model.Connector
return new Result<BikeCollection>(
bikesOccupiedResponse.Source,
BikeCollectionFactory.GetBikesAll(
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
(await Server.GetBikesAvailable(true, operatorUri)).Response?.bikes?.Values,
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider,
@ -168,7 +191,7 @@ namespace TINK.Model.Connector
}
// Both types bikes could read from copri => update cache
Server.AddToCache(bikesAvailableResponse);
Server.AddToCache(bikesAvailableResponse, operatorUri);
Server.AddToCache(bikesOccupiedResponse);
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available and occupied read successfully from server.");

View file

@ -1,3 +1,4 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Bikes;
using TINK.Model.Services.CopriApi;
@ -14,7 +15,8 @@ namespace TINK.Model.Connector
Task<Result<BikeCollection>> GetBikesOccupiedAsync();
/// <summary> Gets bikes either bikes available if no user is logged in or bikes available and bikes occupied if a user is logged in. </summary>
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
/// <returns>Collection of bikes.</returns>
Task<Result<BikeCollection>> GetBikesAsync();
Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null);
}
}

View file

@ -54,10 +54,11 @@ namespace TINK.Model.Connector
}
/// <summary> Gets bikes occupied. </summary>
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
/// <returns> Collection of bikes. </returns>
public async Task<Result<BikeCollection>> GetBikesAsync()
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
{
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
var bikesAvailableResponse = await server.GetBikesAvailableAsync(operatorUri);
return new Result<BikeCollection>(
typeof(CopriCallsMonkeyStore),
bikesAvailableResponse.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Cache),

View file

@ -69,10 +69,11 @@ namespace TINK.Model.Connector
}
/// <summary> Gets bikes available and bikes occupied. </summary>
/// <param name="operatorUri">Uri of the operator host to get bikes from or null if bikes have to be gotten form primary host.</param>
/// <returns>Collection of bikes.</returns>
public async Task<Result<BikeCollection>> GetBikesAsync()
public async Task<Result<BikeCollection>> GetBikesAsync(Uri operatorUri = null)
{
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
var bikesAvailableResponse = await server.GetBikesAvailableAsync(operatorUri);
var bikesOccupiedResponse = await server.GetBikesOccupiedAsync();
return new Result<BikeCollection>(

View file

@ -361,9 +361,35 @@ namespace TINK.Model.Connector
/// <returns>Operator Uri</returns>
public static Uri GetOperatorUri(this BikeInfoBase bikeInfo)
{
return bikeInfo?.uri_operator != null && !string.IsNullOrEmpty(bikeInfo?.uri_operator)
? new Uri($"{bikeInfo.uri_operator}/{CopriServerUriList.REST_RESOURCE_ROOT}")
: null;
if (Uri.TryCreate(bikeInfo?.uri_operator, UriKind.Absolute, out var operatorUri))
{
// Valid uri detected.
return new Uri($"{operatorUri.AbsoluteUri}/{CopriServerUriList.REST_RESOURCE_ROOT}");
}
Log.Error(!string.IsNullOrEmpty(bikeInfo?.uri_operator)
? $"Operator uri can not be extracted from bike info base object {bikeInfo.uri_operator}. Uri is not valid."
: "Operator uri can not be extracted from bike info base object. Entry is null or empty.");
return null;
}
/// <summary>
/// Gets the operator Uri from response.
/// </summary>
/// <param name="stationInfo"> Response to get uri from.</param>
/// <returns>Operator Uri</returns>
public static Uri GetOperatorUri(this StationInfo stationInfo)
{
if (Uri.TryCreate(stationInfo?.uri_operator, UriKind.Absolute, out var operatorUri))
{
// Valid uri detected.
return new Uri($"{operatorUri.AbsoluteUri}/{CopriServerUriList.REST_RESOURCE_ROOT}");
}
Log.Error(!string.IsNullOrEmpty(stationInfo?.uri_operator)
? $"Operator uri can not be extracted from station object {stationInfo.uri_operator}. Uri is not valid."
: "Operator uri can not be extracted from station object. Entry is null or empty.");
return null;
}
/// <summary> Tries to get the copri version from response.</summary>
@ -415,6 +441,7 @@ namespace TINK.Model.Connector
station.GetGroup(),
station.GetPosition(),
station.description,
station.GetOperatorUri(),
new Data(station.operator_data?.operator_name,
station.operator_data?.operator_phone,
station.operator_data?.operator_hours,

View file

@ -19,7 +19,7 @@ namespace TINK.Model.Connector.Updater
DataSource dataSource)
=> GetBikesAll(
bikesAvailableResponse?.bikes?.Values,
new BikesReservedOccupiedResponse()?.bikes_occupied?.Values, // There are no occupied bikes.
bikesAvailableResponse?.bikes_occupied?.Values,
string.Empty,
() => DateTime.Now,
dataSource);

View file

@ -106,7 +106,7 @@ namespace TINK.Model.State
/// Transforms object to string.
/// </summary>
/// <returns></returns>
public new string ToString()
public override string ToString()
{
return _InUseState.Value.ToString("g");
}

View file

@ -149,7 +149,7 @@ namespace TINK.Model.State
/// Transforms object to string.
/// </summary>
/// <returns></returns>
public new string ToString()
public override string ToString()
{
return _StateInfo.ToString();
}

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using TINK.Model.Stations.StationNS.Operator;
@ -17,6 +18,9 @@ namespace TINK.Model.Stations.StationNS
/// <summary> Gets the name of the station.</summary>
string StationName { get; }
/// <summary> Uri of the operator or null. </summary>
Uri OperatorUri { get; }
/// <summary> Holds the gps- position of the station.</summary>
IPosition Position { get; }
@ -29,5 +33,6 @@ namespace TINK.Model.Stations.StationNS
/// <summary> Gets bike <see cref="BikeGroupCol.Entry"/> objects. </summary>
/// <remarks> Each entry has a name, holds the count of available bikes at station and the group value. /// </remarks>
IBikeGroupCol BikeGroups { get; }
}
}

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using TINK.Model.Stations.StationNS.Operator;
@ -15,6 +16,9 @@ namespace TINK.Model.Stations.StationNS
/// <summary> Gets the name of the station.</summary>
public string StationName => string.Empty;
/// <summary> Uri of the operator or null. </summary>
public Uri OperatorUri => null;
/// <summary> Holds the gps- position of the station.</summary>
public IPosition Position => PositionFactory.Create();

View file

@ -12,11 +12,13 @@ namespace TINK.Model.Stations.StationNS
/// <param name="group">Group (TINK, Konrad) to which station is related.</param>
/// <param name="position">GPS- position of the station.</param>
/// <param name="stationName">Name of the station.</param>
/// <param name="operatorUri">Uri of the operator or null.</param>
public Station(
string id,
IEnumerable<string> group = null,
IPosition position = null,
string stationName = "",
Uri operatorUri = null,
IData operatorData = null,
IBikeGroupCol bikeGropCol = null)
{
@ -26,6 +28,7 @@ namespace TINK.Model.Stations.StationNS
StationName = stationName ?? string.Empty;
OperatorData = operatorData ?? new Data();
BikeGroups = bikeGropCol ?? new BikeGroupCol();
OperatorUri = operatorUri;
}
/// <summary> Holds the unique id of the station.c</summary>
@ -49,5 +52,8 @@ namespace TINK.Model.Stations.StationNS
/// <summary> Gets bike <see cref="BikeGroupCol.Entry"/> objects. </summary>
/// <remarks> Each entry has a name, holds the count of available bikes at station and the group value. /// </remarks>
public IBikeGroupCol BikeGroups { get; }
/// <summary> Uri of the operator or null. </summary>
public Uri OperatorUri { get; }
}
}

View file

@ -182,7 +182,6 @@ namespace TINK.Model
ISpecialFolder specialFolder,
ICipher cipher,
ITheme theme,
object arendiCentral = null,
Action<SendOrPostCallback, object> postAction = null,
Version currentVersion = null,
Version lastVersion = null,
@ -225,11 +224,7 @@ namespace TINK.Model
#if BLUETOOTHLE // Requires LockItBluetoothle library.
new Bluetoothle.LockItByGuidService(Cipher),
#endif
#if ARENDI // Requires LockItArendi library.
new Arendi.LockItByGuidService(Cipher, arendiCentral),
new Arendi.LockItByScanService(Cipher, arendiCentral),
#endif
new LocksServiceInReach(),
new LocksServiceInReach(),
new LocksServiceOutOfReach(),
};

View file

@ -19,6 +19,7 @@ namespace TINK.Model.User.Account
SwitchNoSiteCaching = 1024, // Allows to turn off/ on caching of sites displayed in app hosted by COPRI
ReportLevel = 2048, // Allows extent to show error messages.
SwitchTheme = 4096, // Allows user to switch theme (sharee.bike, Mein konrad, LastenRad Bayern)
ManageAlarmAndSounds = 8192, // Allows user to manage sounds and alarm settings (lock open and connected)
All = PickCopriServer +
ManageCopriCacheExpiration +
ManagePolling +
@ -28,7 +29,8 @@ namespace TINK.Model.User.Account
ShowDiagnostics +
SwitchNoSiteCaching +
ReportLevel +
SwitchTheme,
SwitchTheme +
ManageAlarmAndSounds,
}
/// <summary>

View file

@ -732,6 +732,11 @@ namespace TINK.Model
AppResources.ChangeLog_3_0_374_MK_SB,
new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike }
},
{
new Version(3, 0, 375),
string.Format("{0} <br /> {1} <br /> {2}", AppResources.ChangeLog_MinorImprovements, AppResources.ChangeLog_PackageUpdates, AppResources.ChangeLog_MinorBugFixes),
new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike }
},
};
/// <summary> Manges the whats new information.</summary>