Code updated to 3.0.238

This commit is contained in:
Oliver Hauff 2021-06-26 20:57:55 +02:00
parent 3302d80678
commit 9c6a1fa92b
257 changed files with 7763 additions and 2861 deletions

View file

@ -21,13 +21,13 @@ namespace TINK.Model.Bike.BC
/// <summary> Constructs a bike object.</summary>
protected BikeInfo(
IStateInfo stateInfo,
int id,
string id,
bool? isDemo = DEFAULTVALUEISDEMO,
IEnumerable<string> group = null,
WheelType? wheelType = null,
TypeOfBike? typeOfBike = null,
string description = null,
int? currentStationId = null,
string currentStationId = null,
Uri operatorUri = null,
TariffDescription tariffDescription = null)
{
@ -63,8 +63,8 @@ namespace TINK.Model.Bike.BC
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="wheelType"></param>
public BikeInfo(
int id,
int? currentStationId,
string id,
string currentStationId,
Uri operatorUri = null,
TariffDescription tariffDescription = null,
bool? isDemo = DEFAULTVALUEISDEMO,
@ -99,13 +99,13 @@ namespace TINK.Model.Bike.BC
/// <param name="code">Booking code.</param>
/// <param name="p_oDateTimeNowProvider">Date time provider to calculate reaining time.</param>
public BikeInfo(
int id,
string id,
bool? isDemo,
IEnumerable<string> group,
WheelType? wheelType,
TypeOfBike? typeOfBike,
string description,
int? stationId,
string stationId,
Uri operatorUri,
TariffDescription tariffDescription,
DateTime requestedAt,
@ -142,13 +142,13 @@ namespace TINK.Model.Bike.BC
/// <param name="mailAddress">Mail address of user which booked bike.</param>
/// <param name="code">Booking code.</param>
public BikeInfo(
int id,
string id,
bool? isDemo,
IEnumerable<string> group,
WheelType? wheelType,
TypeOfBike? typeOfBike,
string description,
int? currentStationId,
string currentStationId,
Uri operatorUri,
TariffDescription tariffDescription,
DateTime bookedAt,
@ -179,7 +179,7 @@ namespace TINK.Model.Bike.BC
/// <summary>
/// Station a which bike is located, null otherwise.
/// </summary>
public int? CurrentStation { get; }
public string CurrentStation { get; }
/// <summary> Holds description about the tarif. </summary>
public TariffDescription TariffDescription { get; }
@ -192,7 +192,7 @@ namespace TINK.Model.Bike.BC
get { return m_oStateInfo; }
}
public int Id => Bike.Id;
public string Id => Bike.Id;
public WheelType? WheelType => Bike.WheelType;
@ -210,7 +210,7 @@ namespace TINK.Model.Bike.BC
/// </summary>
public new string ToString()
{
return $"Id={Bike.Id}{(Bike.WheelType != null ? $", wheel(s)={Bike.WheelType}" : string.Empty)}{(Bike.TypeOfBike != null ? $"type={Bike.TypeOfBike}" : "")}, state={State}, location={(CurrentStation.HasValue ? $"Station {CurrentStation}" : "On the road")}, is demo={IsDemo}.";
return $"Id={Bike.Id}{(Bike.WheelType != null ? $", wheel(s)={Bike.WheelType}" : string.Empty)}{(Bike.TypeOfBike != null ? $"type={Bike.TypeOfBike}" : "")}, state={State}, location={(!string.IsNullOrEmpty(CurrentStation)? $"Station {CurrentStation}" : "On the road")}, is demo={IsDemo}.";
}
}
}

View file

@ -29,13 +29,13 @@ namespace TINK.Model.Bike.BC
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="stateInfo">Bike state info.</param>
protected BikeInfoMutable(
int id,
string id,
bool isDemo = BikeInfo.DEFAULTVALUEISDEMO,
IEnumerable<string> group = null,
WheelType? wheelType = null,
TypeOfBike? typeOfBike = null,
string description = null,
int? currentStationId = null,
string currentStationId = null,
Uri operatorUri = null,
TariffDescription tariffDescription = null,
Func<DateTime> dateTimeProvider = null,
@ -71,7 +71,7 @@ namespace TINK.Model.Bike.BC
/// Station a which bike is located, null otherwise.
/// </summary>
[DataMember]
public int? CurrentStation { get; }
public string CurrentStation { get; }
/// <summary> Holds description about the tarif. </summary>
[DataMember]
@ -94,7 +94,7 @@ namespace TINK.Model.Bike.BC
/// <summary> Unused member. </summary>
IStateInfoMutable IBikeInfoMutable.State => m_oStateInfo;
public int Id => m_oBike.Id;
public string Id => m_oBike.Id;
public bool IsDemo { get; }
@ -118,7 +118,7 @@ namespace TINK.Model.Bike.BC
/// <returns></returns>
public new string ToString()
{
return $"Id={Id}{(WheelType != null ? $", wheel(s)={WheelType}" : string.Empty)}{(TypeOfBike != null ? $", type={TypeOfBike}" : "")}, demo={IsDemo}, state={State.ToString()}, location={(CurrentStation.HasValue ? $"Station {CurrentStation}" : "On the road")}.";
return $"Id={Id}{(WheelType != null ? $", wheel(s)={WheelType}" : string.Empty)}{(TypeOfBike != null ? $", type={TypeOfBike}" : "")}, demo={IsDemo}, state={State.ToString()}, location={(!string.IsNullOrEmpty(CurrentStation) ? $"Station {CurrentStation}" : "On the road")}.";
}
}
}

View file

@ -13,7 +13,7 @@ namespace TINK.Model.Bike.BC
/// <summary>
/// Holds the unique id of the bike;
/// </summary>
int Id { get; }
string Id { get; }
/// <summary> True if bike is a demo bike. </summary>
bool IsDemo { get; }
@ -37,7 +37,7 @@ namespace TINK.Model.Bike.BC
/// <summary>
/// Station a which bike is located, null otherwise.
/// </summary>
int? CurrentStation { get; }
string CurrentStation { get; }
/// <summary>
/// Uri of the operator or null, in case of single operator setup.

View file

@ -11,7 +11,7 @@ namespace TINK.Model.Bikes.Bike.BC
/// <summary>
/// Holds the unique id of the bike;
/// </summary>
int Id { get; }
string Id { get; }
/// <summary> True if bike is a demo bike. </summary>
bool IsDemo { get; }
@ -35,7 +35,7 @@ namespace TINK.Model.Bikes.Bike.BC
/// <summary>
/// Station a which bike is located, null otherwise.
/// </summary>
int? CurrentStation { get; }
string CurrentStation { get; }
/// <summary>
/// Holds the rent state of the bike.

View file

@ -29,7 +29,7 @@ namespace TINK.Model.Bike
/// <param name="p_iId">Unique id of bike.</param>
/// <param name="p_strCurrentStationName">Name of station where bike is located, null if bike is on the road.</param>
public Bike(
int p_iId,
string p_iId,
WheelType? wheelType = null,
TypeOfBike? typeOfBike = null,
string description = null)
@ -43,7 +43,7 @@ namespace TINK.Model.Bike
/// <summary>
/// Holds the unique id of the bike;
/// </summary>
public int Id { get; }
public string Id { get; }
/// <summary>
/// Holds the count of wheels.

View file

@ -18,10 +18,10 @@ namespace TINK.Model.Bike.BluetoothLock
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="wheelType">Trike, two wheels, mono, ....</param>
public BikeInfo(
int bikeId,
string bikeId,
int lockId,
Guid lockGuid,
int? currentStationId,
string currentStationId,
Uri operatorUri = null,
TariffDescription tariffDescription = null,
bool? isDemo = DEFAULTVALUEISDEMO,
@ -58,7 +58,7 @@ namespace TINK.Model.Bike.BluetoothLock
/// <param name="p_oDateTimeNowProvider">Date time provider to calculate reaining time.</param>
/// <param name="wheelType"></param>
public BikeInfo(
int id,
string id,
int lockId,
Guid lockGuid,
byte[] userKey,
@ -66,7 +66,7 @@ namespace TINK.Model.Bike.BluetoothLock
byte[] seed,
DateTime requestedAt,
string mailAddress,
int? currentStationId,
string currentStationId,
Uri operatorUri,
TariffDescription tariffDescription,
Func<DateTime> dateTimeProvider,
@ -106,7 +106,7 @@ namespace TINK.Model.Bike.BluetoothLock
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="wheelType"></param>
public BikeInfo(
int id,
string id,
int lockId,
Guid lockGuid,
byte[] userKey,
@ -114,7 +114,7 @@ namespace TINK.Model.Bike.BluetoothLock
byte[] seed,
DateTime bookedAt,
string mailAddress,
int? currentStationId,
string currentStationId,
Uri operatorUri,
TariffDescription tariffDescription = null,
bool? isDemo = DEFAULTVALUEISDEMO,

View file

@ -5,8 +5,9 @@ namespace TINK.Model.Bikes.Bike
/// <summary>
/// Holds tariff info for a single bike.
/// </summary>
#if USCSHARP9
public record TariffDescription
{
{
/// <summary>
/// Name of the tariff.
/// </summary>
@ -37,4 +38,38 @@ namespace TINK.Model.Bikes.Bike
/// </summary>
public double MaxFeeEuroPerDay { get; init; }
}
#else
public class TariffDescription
{
/// <summary>
/// Name of the tariff.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Number of the tariff.
/// </summary>
public int? Number { get; set; }
/// <summary>
/// Costs per hour in euro.
/// </summary>
public double FeeEuroPerHour { get; set; }
/// <summary>
/// Costs of the abo per month.
/// </summary>
public double AboEuroPerMonth { get; set; }
/// <summary>
/// Costs per hour in euro.
/// </summary>
public TimeSpan FreeTimePerSession { get; set; }
/// <summary>
/// Max. costs per day in euro.
/// </summary>
public double MaxFeeEuroPerDay { get; set; }
}
#endif
}

View file

@ -11,37 +11,37 @@ namespace TINK.Model.Bike
public class BikeCollection : IBikeDictionary<BikeInfo>
{
/// <summary> Holds the bike dictionary object.</summary>
private Dictionary<int, BikeInfo> BikeDictionary { get; }
private Dictionary<string, BikeInfo> BikeDictionary { get; }
/// <summary>Constructs an empty bike info dictionary object.</summary>
public BikeCollection()
{
BikeDictionary = new Dictionary<int, BikeInfo>();
BikeDictionary = new Dictionary<string, BikeInfo>();
}
/// <summary> Constructs a bike collection object.</summary>
/// <param name="bikeDictionary"></param>
public BikeCollection(Dictionary<int, BikeInfo> bikeDictionary)
public BikeCollection(Dictionary<string, BikeInfo> bikeDictionary)
{
BikeDictionary = bikeDictionary ??
throw new ArgumentNullException(nameof(bikeDictionary), "Can not construct BikeCollection object.");
}
/// <summary> Gets a bike by its id.</summary>
/// <param name="p_iId">Id of the bike to get.</param>
/// <param name="id">Id of the bike to get.</param>
/// <returns></returns>
public BikeInfo GetById(int p_iId)
public BikeInfo GetById(string id)
{
return BikeDictionary.FirstOrDefault(x => x.Key == p_iId).Value;
return BikeDictionary.FirstOrDefault(x => x.Key == id).Value;
}
/// <summary> Gets the count of bikes. </summary>
public int Count => BikeDictionary.Count;
/// <summary> Gets if a bike with given id exists.</summary>
/// <param name="p_iId">Id of bike.</param>
/// <param name="id">Id of bike.</param>
/// <returns>True if bike is contained, false otherwise.</returns>
public bool ContainsKey(int p_iId) => BikeDictionary.Keys.Contains(p_iId);
public bool ContainsKey(string id) => BikeDictionary.Keys.Contains(id);
/// <summary> Gets the enumerator. </summary>
/// <returns>Enumerator object.</returns>

View file

@ -14,11 +14,11 @@ namespace TINK.Model
/// <returns>BikeCollection holding bikes at given station or empty BikeCollection, if there are no bikes.</returns>
public static BikeCollection GetAtStation(
this BikeCollection bikesAtAnyStation,
int? selectedStation)
string selectedStation)
{
return new BikeCollection(bikesAtAnyStation?
.Where(bike => selectedStation.HasValue && bike.CurrentStation == selectedStation.Value)
.ToDictionary(x => x.Id) ?? new Dictionary<int, BikeInfo>());
.Where(bike => !string.IsNullOrEmpty(selectedStation) && bike.CurrentStation == selectedStation)
.ToDictionary(x => x.Id) ?? new Dictionary<string, BikeInfo>());
}
/// <summary> Filters bikes by bike type. </summary>
@ -28,7 +28,7 @@ namespace TINK.Model
{
return new BikeCollection(bcAndLockItBikes?
.Where(bike => bike is Bike.BluetoothLock.BikeInfo)
.ToDictionary(x => x.Id) ?? new Dictionary<int, BikeInfo>());
.ToDictionary(x => x.Id) ?? new Dictionary<string, BikeInfo>());
}
}
}

View file

@ -44,7 +44,7 @@ namespace TINK.Model.Bike
// Update bike.
GetById(bikeInfo.Id).State.Load(bikeInfo.State);
if (bikesToBeRemoved.Contains<int>(bikeInfo.Id))
if (bikesToBeRemoved.Contains<string>(bikeInfo.Id))
{
// Remove list from obsolete list.
bikesToBeRemoved.Remove(bikeInfo.Id);
@ -86,20 +86,20 @@ namespace TINK.Model.Bike
private set;
}
public void SetSelectedBike(int p_intId)
public void SetSelectedBike(string id)
{
SelectedBike = GetById(p_intId);
SelectedBike = GetById(id);
}
/// <summary>
/// Gets a bike by its id.
/// </summary>
/// <param name="p_iId"></param>
/// <param name="id"></param>
/// <returns></returns>
public BikeInfoMutable GetById(int p_iId)
public BikeInfoMutable GetById(string id)
{
{
return this.FirstOrDefault(bike => bike.Id == p_iId);
return this.FirstOrDefault(bike => bike.Id == id);
}
}
@ -108,18 +108,18 @@ namespace TINK.Model.Bike
/// </summary>
/// <param name="p_strKey">Key to check.</param>
/// <returns>True if bike exists.</returns>
public bool ContainsKey(int p_iId)
public bool ContainsKey(string id)
{
return GetById(p_iId) != null;
return GetById(id) != null;
}
/// <summary>
/// Removes a bike by its id.
/// </summary>
/// <param name="p_iId">Id of bike to be removed.</param>
public void RemoveById(int p_iId)
/// <param name="id">Id of bike to be removed.</param>
public void RemoveById(string id)
{
var l_oBike = GetById(p_iId);
var l_oBike = GetById(id);
if (l_oBike == null)
{
// Nothing to do if bike does not exists.

View file

@ -14,7 +14,7 @@ namespace TINK.Model.Bike
IEnumerable<BluetoothLock.LockInfo> locksInfo)
{
var updatedBikesCollection = new Dictionary<int, BC.BikeInfo>();
var updatedBikesCollection = new Dictionary<string, BC.BikeInfo>();
foreach (var bikeInfo in bikes)
{

View file

@ -7,29 +7,29 @@ namespace TINK.Model.Bike
/// <summary>
/// Gets a bike by its id.
/// </summary>
/// <param name="p_iId"></param>
/// <param name="id"></param>
/// <returns></returns>
T GetById(int p_iId);
T GetById(string id);
/// <summary>
/// Deteermines whether a bike by given key exists.
/// </summary>
/// <param name="p_strKey">Key to check.</param>
/// <returns>True if bike exists.</returns>
bool ContainsKey(int p_iId);
bool ContainsKey(string id);
}
public interface IBikeDictionaryMutable<T> : IBikeDictionary<T>
{
/// <summary>
/// Removes a bike by its id.
/// </summary>
/// <param name="p_iId">Id of bike to be removed.</param>
void RemoveById(int p_iId);
/// <param name="id">Id of bike to be removed.</param>
void RemoveById(string id);
/// <summary>
/// Adds a new element to dictinary.
/// </summary>
/// <param name="p_oNewElement">New element to add.</param>
void Add(T p_oNewElement);
/// <param name="newElement">New element to add.</param>
void Add(T newElement);
}
}

View file

@ -1,10 +1,11 @@
using Serilog;
using System;
using System.Threading.Tasks;
using TINK.Model.Repository;
using TINK.Model.Repository.Request;
using TINK.Model.Repository.Response;
using TINK.Repository;
using TINK.Repository.Request;
using TINK.Repository.Response;
using TINK.Model.User.Account;
using TINK.Model.Device;
namespace TINK.Model.Connector
{
@ -122,7 +123,8 @@ namespace TINK.Model.Connector
public async Task DoReturn(
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
LocationDto location)
LocationDto location,
ISmartDevice smartDevice)
{
Log.ForContext<Command>().Error("Unexpected returning request detected. No user logged in.");
await Task.CompletedTask;
@ -132,7 +134,11 @@ namespace TINK.Model.Connector
/// Submits feedback to copri server.
/// </summary>
/// <param name="userFeedback">Feedback to submit.</param>
#if USCSHARP9
public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri)
#else
public async Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri)
#endif
{
Log.ForContext<Command>().Error("Unexpected submit feedback request detected. No user logged in.");
await Task.CompletedTask;

View file

@ -1,11 +1,12 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Bike.BluetoothLock;
using TINK.Model.Repository;
using TINK.Model.Repository.Exception;
using TINK.Model.Repository.Request;
using TINK.Model.Repository.Response;
using TINK.Repository;
using TINK.Repository.Exception;
using TINK.Repository.Request;
using TINK.Repository.Response;
using TINK.Model.User.Account;
using TINK.Model.Device;
namespace TINK.Model.Connector
{
@ -30,7 +31,7 @@ namespace TINK.Model.Connector
/// Logs user in.
/// If log in succeeds either and session might be updated if it was no more valid (logged in by an different device).
/// If log in fails (password modified) session cookie is set to empty.
/// If communication fails an TINK.Model.Repository.Exception is thrown.
/// If communication fails an TINK.Repository.Exception is thrown.
/// </summary>
/// <param name="p_oAccount">Account to use for login.</param>
public Task<IAccount> DoLogin(string p_strMail, string p_strPassword, string p_strDeviceId)
@ -242,12 +243,13 @@ namespace TINK.Model.Connector
}
/// <summary> Request to return a bike.</summary>
/// <param name="latitude">Latitude of the bike.</param>
/// <param name="longitude">Longitude of the bike.</param>
/// <param name="bike">Bike to return.</param>
/// <param name="locaton">Position of the bike.</param>
/// <param name="smartDevice">Provides info about hard and software.</param>
public async Task DoReturn(
Bikes.Bike.BluetoothLock.IBikeInfoMutable bike,
LocationDto location)
LocationDto location,
ISmartDevice smartDevice)
{
if (bike == null)
{
@ -257,7 +259,7 @@ namespace TINK.Model.Connector
ReservationCancelReturnResponse l_oResponse;
try
{
l_oResponse = (await CopriServer.DoReturn(bike.Id, location, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
l_oResponse = (await CopriServer.DoReturn(bike.Id, location, smartDevice, bike.OperatorUri)).GetIsReturnBikeResponseOk(bike.Id);
}
catch (Exception)
{
@ -272,8 +274,12 @@ namespace TINK.Model.Connector
/// Submits feedback to copri server.
/// </summary>
/// <param name="userFeedback">Feedback to submit.</param>
#if USCSHARP9
public async Task DoSubmitFeedback(ICommand.IUserFeedback userFeedback, Uri opertorUri)
=> await CopriServer.DoSubmitFeedback(userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
#else
public async Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri)
=> await CopriServer.DoSubmitFeedback(userFeedback.BikeId, userFeedback.Message, userFeedback.IsBikeBroken, opertorUri);
#endif
}
}

View file

@ -1,7 +1,8 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Repository.Request;
using TINK.Repository.Request;
using TINK.Model.User.Account;
using TINK.Model.Device;
namespace TINK.Model.Connector
{
@ -44,9 +45,10 @@ namespace TINK.Model.Connector
Task DoBook(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike);
/// <summary> Request to return a bike.</summary>
/// <param name="location">Geolocation of lock when returning bike.</param>
/// <param name="bike">Bike to return.</param>
Task DoReturn(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto geolocation = null);
/// <param name="location">Geolocation of lock when returning bike.</param>
/// <param name="smartDevice">Provides info about hard and software.</param>
Task DoReturn(Bikes.Bike.BluetoothLock.IBikeInfoMutable bike, LocationDto geolocation = null, ISmartDevice smartDevice = null);
/// <summary> True if connector has access to copri server, false if cached values are used. </summary>
bool IsConnected { get; }
@ -55,12 +57,15 @@ namespace TINK.Model.Connector
string SessionCookie { get; }
Task DoSubmitFeedback(IUserFeedback userFeedback, Uri opertorUri);
#if USCSHARP9
/// <summary>
/// Feedback given by user when returning bike.
/// </summary>
public interface IUserFeedback
{
/// <summary> Id of the bike to which the feedback is related to.</summary>
string BikeId { get; }
/// <summary>
/// Holds whether bike is broken or not.
/// </summary>
@ -74,12 +79,37 @@ namespace TINK.Model.Connector
/// </summary>
string Message { get; }
}
#endif
}
/// <summary>Defines delegate to be raised whenever login state changes.</summary>
/// <param name="p_oEventArgs">Holds session cookie and mail address if user logged in successfully.</param>
public delegate void LoginStateChangedEventHandler(object p_oSender, LoginStateChangedEventArgs p_oEventArgs);
#if !USCSHARP9
/// <summary>
/// Feedback given by user when returning bike.
/// </summary>
public interface IUserFeedback
{
/// <summary> Id of the bike to which the feedback is related to.</summary>
string BikeId { get; }
/// <summary>
/// Holds whether bike is broken or not.
/// </summary>
bool IsBikeBroken { get; }
/// <summary>
/// Holds either
/// - general feedback
/// - error description of broken bike
/// or both.
/// </summary>
string Message { get; }
}
#endif
/// <summary> Event arguments to notify about changes of logged in state.</summary>
public class LoginStateChangedEventArgs : EventArgs
{

View file

@ -1,9 +1,25 @@

namespace TINK.Model.Connector
{
#if USCSHARP9
public record UserFeedbackDto : ICommand.IUserFeedback
{
public string BikeId { get; init; }
public bool IsBikeBroken { get; init; }
public string Message { get; init; }
}
#else
#if USCSHARP9
public class UserFeedbackDto : ICommand.IUserFeedback
#else
public class UserFeedbackDto : IUserFeedback
#endif
{
public string BikeId { get; set; }
public bool IsBikeBroken { get; set; }
public string Message { get; set; }
}
#endif
}

View file

@ -1,6 +1,6 @@
using System;
using TINK.Model.Services.CopriApi;
using TINK.Model.Repository;
using TINK.Repository;
namespace TINK.Model.Connector
{

View file

@ -1,6 +1,6 @@
using System;
using TINK.Model.Services.CopriApi;
using TINK.Model.Repository;
using TINK.Repository;
namespace TINK.Model.Connector
{

View file

@ -1,13 +1,26 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace TINK.Model.Connector.Filter
{
public static class GroupFilterFactory
{
/// <summary>
/// Creates filter object.
/// </summary>
/// <param name="group">if value consists
/// - list of strings entries are used to filter (intersect) with or if value is
/// - null or an empty list null filter is applied, i.e. filtering is off.</param>
/// <returns>Filtering object.</returns>
/// <remarks>
/// Tread group values of null and empty lists as marker to turn filtering off to handle COPRI responses maximal flexible.
/// </remarks>
public static IGroupFilter Create(IEnumerable<string> group)
{
return group != null ? (IGroupFilter) new IntersectGroupFilter(group) : new NullGroupFilter();
return group != null && group.Count() > 0
? (IGroupFilter) new IntersectGroupFilter(group) :
new NullGroupFilter();
}
}
}

View file

@ -86,29 +86,33 @@ namespace TINK.Model.Connector
/// <returns></returns>
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
{
var result = await m_oInnerQuery.GetBikesAndStationsAsync();
// Bikes and stations from COPRI or cache
var providerBikesAndStations = await m_oInnerQuery.GetBikesAndStationsAsync();
return new Result<StationsAndBikesContainer>(
result.Source,
new StationsAndBikesContainer(
new StationDictionary(result.Response.StationsAll.CopriVersion, DoFilter(result.Response.StationsAll, Filter)),
new BikeCollection(DoFilter(result.Response.Bikes, Filter))),
result.Exception);
// Do filtering.
var filteredStationsDictionary = new StationDictionary(providerBikesAndStations.Response.StationsAll.CopriVersion, DoFilter(providerBikesAndStations.Response.StationsAll, Filter));
var filteredBikesDictionary = new BikeCollection(DoFilter(providerBikesAndStations.Response.Bikes, Filter));
var filteredBikesAndStations = new Result<StationsAndBikesContainer>(
providerBikesAndStations.Source,
new StationsAndBikesContainer(filteredStationsDictionary, filteredBikesDictionary),
providerBikesAndStations.Exception); ;
return filteredBikesAndStations;
}
/// <summary> Filter bikes by group. </summary>
/// <param name="p_oBikes">Bikes to filter.</param>
/// <param name="bikes">Bikes to filter.</param>
/// <returns>Filtered bikes.</returns>
private static Dictionary<int, BikeInfo> DoFilter(BikeCollection p_oBikes, IGroupFilter filter)
private static Dictionary<string, BikeInfo> DoFilter(BikeCollection bikes, IGroupFilter filter)
{
return p_oBikes.Where(x => filter.DoFilter(x.Group).Count() > 0).ToDictionary(x => x.Id);
return bikes.Where(x => filter.DoFilter(x.Group).Count() > 0).ToDictionary(x => x.Id);
}
/// <summary> Filter stations by broup. </summary>
/// <returns></returns>
private static Dictionary<int, Station.Station> DoFilter(StationDictionary p_oStations, IGroupFilter filter)
private static Dictionary<string, IStation> DoFilter(StationDictionary stations, IGroupFilter filter)
{
return p_oStations.Where(x => filter.DoFilter(x.Group).Count() > 0).ToDictionary((x => x.Id));
return stations.Where(x => filter.DoFilter(x.Group).Count() > 0).ToDictionary(x => x.Id);
}
}
}

View file

@ -90,18 +90,18 @@ namespace TINK.Model.Connector
}
/// <summary> Filter bikes by group. </summary>
/// <param name="p_oBikes">Bikes to filter.</param>
/// <param name="bikes">Bikes to filter.</param>
/// <returns>Filtered bikes.</returns>
public static Dictionary<int, BikeInfo> DoFilter(BikeCollection p_oBikes, IEnumerable<string> p_oFilter)
public static Dictionary<string, BikeInfo> DoFilter(BikeCollection bikes, IEnumerable<string> filter)
{
return p_oBikes.Where(x => x.Group.Intersect(p_oFilter).Count() > 0).ToDictionary(x => x.Id);
return bikes.Where(x => x.Group.Intersect(filter).Count() > 0).ToDictionary(x => x.Id);
}
/// <summary> Filter stations by broup. </summary>
/// <returns></returns>
public static Dictionary<int, Station.Station> DoFilter(StationDictionary p_oStations, IEnumerable<string> p_oFilter)
public static Dictionary<string, IStation> DoFilter(StationDictionary stations, IEnumerable<string> p_oFilter)
{
return p_oStations.Where(x => x.Group.Intersect(p_oFilter).Count() > 0).ToDictionary((x => x.Id));
return stations.Where(x => x.Group.Intersect(p_oFilter).Count() > 0).ToDictionary(x => x.Id);
}
}
}

View file

@ -1,5 +1,5 @@
using System;
using TINK.Model.Repository;
using TINK.Repository;
namespace TINK.Model.Connector
{

View file

@ -1,5 +1,5 @@
using System;
using TINK.Model.Repository;
using TINK.Repository;
namespace TINK.Model.Connector
{

View file

@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using TINK.Model.Bike;
using TINK.Model.Services.CopriApi;
using TINK.Model.Repository;
using TINK.Repository;
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
namespace TINK.Model.Connector
@ -70,7 +70,7 @@ namespace TINK.Model.Connector
Log.ForContext<CachedQuery>().Error("Unexpected call to get be bikes occpied detected. No user is logged in.");
return new Result<BikeCollection>(
typeof(CopriCallsMonkeyStore),
await Task.Run(() => new BikeCollection(new Dictionary<int, BikeInfo>())),
await Task.Run(() => new BikeCollection(new Dictionary<string, BikeInfo>())),
new System.Exception("Abfrage der reservierten/ gebuchten Räder nicht möglich. Kein Benutzer angemeldet."));
}

View file

@ -1,10 +1,9 @@
using MonkeyCache.FileStore;
using System;
using System;
using System.Linq;
using System.Threading.Tasks;
using TINK.Model.Bike;
using TINK.Model.Services.CopriApi;
using TINK.Model.Repository;
using TINK.Repository;
namespace TINK.Model.Connector
{
@ -31,73 +30,74 @@ namespace TINK.Model.Connector
/// <summary> Gets all stations including postions.</summary>
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
{
var resultStations = await server.GetStations();
var stationsResponse = await server.GetStations();
if (resultStations.Source == typeof(CopriCallsMonkeyStore)
|| resultStations.Exception != null)
if (stationsResponse.Source == typeof(CopriCallsMonkeyStore)
|| stationsResponse.Exception != null)
{
// Stations were read from cache ==> get bikes availbalbe and occupied from cache as well to avoid inconsistencies
return new Result<StationsAndBikesContainer>(
resultStations.Source,
stationsResponse.Source,
new StationsAndBikesContainer(
resultStations.Response.GetStationsAllMutable(),
stationsResponse.Response.GetStationsAllMutable(),
UpdaterJSON.GetBikesAll(
(await server.GetBikesAvailable(true)).Response,
(await server.GetBikesOccupied(true)).Response,
Mail,
DateTimeProvider)),
resultStations.Exception);
stationsResponse.Exception);
}
var l_oBikesAvailableResponse = await server.GetBikesAvailable();
if (l_oBikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|| l_oBikesAvailableResponse.Exception != null)
var bikesAvailableResponse = await server.GetBikesAvailable();
if (bikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|| bikesAvailableResponse.Exception != null)
{
// Bikes avilable were read from cache ==> get bikes occupied from cache as well to avoid inconsistencies
return new Result<StationsAndBikesContainer>(
l_oBikesAvailableResponse.Source,
bikesAvailableResponse.Source,
new StationsAndBikesContainer(
(await server.GetStations(true)).Response.GetStationsAllMutable(),
UpdaterJSON.GetBikesAll(l_oBikesAvailableResponse.Response,
UpdaterJSON.GetBikesAll(bikesAvailableResponse.Response,
(await server.GetBikesOccupied(true)).Response,
Mail,
DateTimeProvider)),
l_oBikesAvailableResponse.Exception);
bikesAvailableResponse.Exception);
}
var l_oBikesOccupiedResponse = await server.GetBikesOccupied();
if (l_oBikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|| l_oBikesOccupiedResponse.Exception != null)
var bikesOccupiedResponse = await server.GetBikesOccupied();
if (bikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|| bikesOccupiedResponse.Exception != null)
{
// Bikes occupied were read from cache ==> get bikes available from cache as well to avoid inconsistencies
return new Result<StationsAndBikesContainer>(
l_oBikesOccupiedResponse.Source,
bikesOccupiedResponse.Source,
new StationsAndBikesContainer(
(await server.GetStations(true)).Response.GetStationsAllMutable(),
UpdaterJSON.GetBikesAll(
(await server.GetBikesAvailable(true)).Response,
l_oBikesOccupiedResponse.Response,
bikesOccupiedResponse.Response,
Mail,
DateTimeProvider)),
l_oBikesOccupiedResponse.Exception);
bikesOccupiedResponse.Exception);
}
// Both types bikes could read from copri => update cache
server.AddToCache(resultStations);
server.AddToCache(l_oBikesAvailableResponse);
server.AddToCache(l_oBikesOccupiedResponse);
server.AddToCache(stationsResponse);
server.AddToCache(bikesAvailableResponse);
server.AddToCache(bikesOccupiedResponse);
var exceptions = new[] { resultStations?.Exception, l_oBikesAvailableResponse?.Exception, l_oBikesOccupiedResponse?.Exception }.Where(x => x != null).ToArray();
var exceptions = new[] { stationsResponse?.Exception, bikesAvailableResponse?.Exception, bikesOccupiedResponse?.Exception }.Where(x => x != null).ToArray();
var stationsMutable = stationsResponse.Response.GetStationsAllMutable();
var bikesMutable = UpdaterJSON.GetBikesAll(
bikesAvailableResponse.Response,
bikesOccupiedResponse.Response,
Mail,
DateTimeProvider);
return new Result<StationsAndBikesContainer>(
resultStations.Source,
new StationsAndBikesContainer(
resultStations.Response.GetStationsAllMutable(),
UpdaterJSON.GetBikesAll(
l_oBikesAvailableResponse.Response,
l_oBikesOccupiedResponse.Response,
Mail,
DateTimeProvider)),
stationsResponse.Source,
new StationsAndBikesContainer(stationsMutable, bikesMutable),
exceptions.Length > 0 ? new AggregateException(exceptions) : null);
}

View file

@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using TINK.Model.Bike;
using TINK.Model.Services.CopriApi;
using TINK.Model.Repository;
using TINK.Repository;
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
namespace TINK.Model.Connector
@ -44,7 +44,7 @@ namespace TINK.Model.Connector
Log.ForContext<Query>().Error("Unexpected call to get be bikes occpied detected. No user is logged in.");
return new Result<BikeCollection>(
typeof(CopriCallsMonkeyStore),
await Task.Run(() => new BikeCollection(new Dictionary<int, BikeInfo>())),
await Task.Run(() => new BikeCollection(new Dictionary<string, BikeInfo>())),
new System.Exception("Abfrage der reservierten/ gebuchten Räder fehlgeschlagen. Kein Benutzer angemeldet."));
}

View file

@ -2,7 +2,7 @@
using System.Threading.Tasks;
using TINK.Model.Bike;
using TINK.Model.Services.CopriApi;
using TINK.Model.Repository;
using TINK.Repository;
namespace TINK.Model.Connector
{

View file

@ -5,8 +5,8 @@ using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using TINK.Model.Bike;
using TINK.Model.Repository.Exception;
using TINK.Model.Repository.Response;
using TINK.Repository.Exception;
using TINK.Repository.Response;
using TINK.Model.Services.CopriApi.ServerUris;
using TINK.Model.State;
@ -51,14 +51,16 @@ namespace TINK.Model.Connector
/// <summary> Gets the position from StationInfo object. </summary>
/// <param name="p_oAuthorizationResponse">Object to get information from.</param>
/// <returns>Position information.</returns>
public static IEnumerable<string> GetGroup(this string p_oGroup)
public static IEnumerable<string> GetGroup(this string[] group)
{
if (string.IsNullOrEmpty(p_oGroup))
if (group == null || group.Length == 0)
{
throw new ArgumentException("Can not get goup form string. Group text can not be null.");
// If not logged in stations groups are empty form COPRI version v4.1.
Log.Debug("Can not get goup form string. Group text can not be null.");
return new List<string>();
}
return new HashSet<string>(p_oGroup.Split(',')).ToList();
return new HashSet<string>(group).ToList();
}
/// <summary> Gets the position from StationInfo object. </summary>
@ -78,9 +80,9 @@ namespace TINK.Model.Connector
{
return p_oStationInfo.station_group.GetGroup();
}
catch (System.Exception l_oException)
catch (Exception l_oException)
{
throw new System.Exception($"Can not get group of stations from text \"{p_oStationInfo.station_group}\".", l_oException);
throw new Exception($"Can not get group of stations from text \"{p_oStationInfo.station_group}\".", l_oException);
}
}
@ -310,24 +312,19 @@ namespace TINK.Model.Connector
/// </summary>
/// <param name="p_strGps">Text to extract positon from.</param>
/// <returns>Position object.</returns>
public static Station.Position GetPosition(string p_strGps)
public static Station.Position GetPosition(GpsInfo gps)
{
if (p_strGps == null)
if (gps == null)
{
return null;
}
var l_oPosition = p_strGps.Split(',');
if (l_oPosition.Length != 2)
return null;
double l_oLatitude;
if (!double.TryParse(l_oPosition[0], NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLatitude))
if (!double.TryParse(gps.latitude, NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLatitude))
return null;
double l_oLongitude;
if (!double.TryParse(l_oPosition[1], NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLongitude))
if (!double.TryParse(gps.longitude, NumberStyles.Float, CultureInfo.InvariantCulture, out l_oLongitude))
return null;
return new Station.Position(l_oLatitude, l_oLongitude);
@ -354,5 +351,15 @@ namespace TINK.Model.Connector
? new Uri($"{bikeInfo.uri_operator}/{CopriServerUriList.REST_RESOURCE_ROOT}")
: null;
}
/// <summary> Gets the copriversion from.</summary>
/// <param name="response">Response to get version info from.</param>
/// <returns>COPRI version</returns>
public static Version GetCopriVersion(this CopriVersion response)
=> response!= null
&& !string.IsNullOrEmpty(response.copri_version)
&& Version.TryParse(response.copri_version, out Version copriVersion)
? copriVersion
: throw new InvalidResponseException($"Can not get version info from copri response {response?.copri_version}.");
}
}

View file

@ -1,11 +1,11 @@
using System;
using TINK.Model.Bike;
using TINK.Model.Station;
using TINK.Model.Repository.Response;
using TINK.Repository.Response;
using TINK.Model.User.Account;
using System.Collections.Generic;
using TINK.Model.State;
using TINK.Model.Repository.Exception;
using TINK.Repository.Exception;
using Serilog;
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
@ -190,8 +190,8 @@ namespace TINK.Model.Connector
string p_strMail,
Func<DateTime> p_oDateTimeProvider)
{
var l_oBikesDictionary = new Dictionary<int, BikeInfo>();
var l_oDuplicates = new Dictionary<int, BikeInfo>();
var l_oBikesDictionary = new Dictionary<string, BikeInfo>();
var l_oDuplicates = new Dictionary<string, BikeInfo>();
// Get bikes from Copri/ file/ memory, ....
if (p_oBikesAvailableResponse != null
@ -295,7 +295,7 @@ namespace TINK.Model.Connector
return null;
}
if (bikeInfo.station == null)
if (string.IsNullOrEmpty(bikeInfo.station))
{
// Bike available must always have a station id because bikes can only be returned at a station.
Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. No station info set.");
@ -485,7 +485,11 @@ namespace TINK.Model.Connector
return new Bikes.Bike.TariffDescription
{
Name = tariffDesciption?.name,
#if USCSHARP9
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : null,
#else
Number = int.TryParse(tariffDesciption?.number, out int number) ? number : (int?) null,
#endif
FreeTimePerSession = double.TryParse(tariffDesciption?.free_hours, NumberStyles.Any, CultureInfo.InvariantCulture, out double freeHours) ? TimeSpan.FromHours(freeHours) : TimeSpan.Zero,
FeeEuroPerHour = double.TryParse(tariffDesciption?.eur_per_hour, NumberStyles.Any, CultureInfo.InvariantCulture, out double euroPerHour) ? euroPerHour : double.NaN,
AboEuroPerMonth = double.TryParse(tariffDesciption?.abo_eur_per_month, NumberStyles.Any, CultureInfo.InvariantCulture, out double aboEuroPerMonth) ? aboEuroPerMonth : double.NaN,

View file

@ -0,0 +1,21 @@
namespace TINK.Model.Device
{
public interface ISmartDevice
{
/// <summary> Gets unitque device identifier. </summary>
/// <returns>Gets the identifies specifying device.</returns>
string Identifier { get; }
/// <summary> Manufacturer (Samsung). </summary>
string Manufacturer { get; }
/// <summary> Device Model (SMG-950U, iPhone10,6). </summary>
string Model { get; }
/// <summary> Platform (Android). </summary>
string PlatformText { get; }
/// <summary> Operating System Version Number (7.0) as text</summary>
string VersionText { get; }
}
}

View file

@ -1,16 +1,16 @@
using Plugin.Permissions.Abstractions;
using Serilog.Events;
using Serilog.Events;
using System;
using System.Threading;
using TINK.Model.Connector;
using TINK.Model.Device;
using TINK.Services.BluetoothLock;
using TINK.Model.Services.CopriApi.ServerUris;
using TINK.Model.Services.Geolocation;
using TINK.Settings;
using TINK.ViewModel.Map;
using TINK.ViewModel.Settings;
using TINK.Services;
using TINK.Model.Station;
using System.Collections.Generic;
namespace TINK.Model
{
@ -35,7 +35,7 @@ namespace TINK.Model
IFilteredConnector GetConnector(bool isConnected);
/// <summary> Name of the station which is selected. </summary>
int? SelectedStation { get; set; }
IStation SelectedStation { get; set; }
/// <summary>Polling periode.</summary>
PollingParameters Polling { get; set; }
@ -67,6 +67,10 @@ namespace TINK.Model
/// <summary> Gets the minimum logging level. </summary>
LogEventLevel MinimumLogEventLevel { get; set; }
/// <summary> Gets a value indicating whether reporting level is verbose or not.</summary>
bool IsReportLevelVerbose { get; set; }
/// <summary> Updates logging level. </summary>
/// <param name="p_oNewLevel">New level to set.</param>
void UpdateLoggingLevel(LogEventLevel p_oNewLevel);
@ -77,22 +81,19 @@ namespace TINK.Model
/// <summary> Holds the different lock service implementations.</summary>
LocksServicesContainerMutable LocksServices { get; }
/// <summary> Holds the different geo location service implementations.</summary>
ServicesContainerMutable<IGeolocation> GeolocationServices { get; }
/// <summary> Holds available app themes.</summary>
ServicesContainerMutable<object> Themes { get; }
/// <summary> Reference of object which provides device information. </summary>
IDevice Device { get; }
/// <summary> Os permission.</summary>
IPermissions Permissions { get; }
ISmartDevice SmartDevice { get; }
/// <summary> Holds the folder where settings files are stored. </summary>
string SettingsFileFolder { get; }
/// <summary> Holds the external path. </summary>
string ExternalFolder { get; }
/// <summary> Holds the stations availalbe. </summary>
IEnumerable<IStation> Stations {get; set;}
}
}

View file

@ -1,6 +1,6 @@
using Serilog;
using System;
using TINK.Model.Repository.Exception;
using TINK.Repository.Exception;
namespace TINK.Model.Logging
{

View file

@ -18,61 +18,61 @@ namespace TINK.Model.Logging
public static class LoggerConfigurationHelper
{
/// <summary> Holds the log file name. </summary>
private static ILoggingDirectoryManager m_oDirectoryManager = new EmptyDirectoryLoggingManger();
private static ILoggingDirectoryManager DirectoryManager { get; set; } = new EmptyDirectoryLoggingManger();
/// <summary> Sets up logging to file.</summary>
/// <param name="p_oLoggerConfiguration">Object to set up logging with.</param>
/// <param name="loggerConfiguration">Object to set up logging with.</param>
/// <param name="p_oDevice">Object to get file informaton from.</param>
/// <param name="p_oRollingInterval">Specifies rolling type.</param>
/// <param name="p_iRetainedFilesCountLimit">Count of file being retained.</param>
/// <param name="rollingInterval">Specifies rolling type.</param>
/// <param name="retainedFilesCountLimit">Count of file being retained.</param>
/// <returns>Logger object.</returns>
public static LoggerConfiguration File(
this LoggerSinkConfiguration p_oLoggerConfiguration,
string p_strLogFileFolder,
RollingInterval p_oRollingInterval = RollingInterval.Session,
int p_iRetainedFilesCountLimit = 10)
this LoggerSinkConfiguration loggerConfiguration,
string logFileFolder,
RollingInterval rollingInterval = RollingInterval.Session,
int retainedFilesCountLimit = 10)
{
if (m_oDirectoryManager is EmptyDirectoryLoggingManger)
if (DirectoryManager is EmptyDirectoryLoggingManger)
{
// Roll file only once per app session.
try
{
m_oDirectoryManager = new LoggingDirectoryManager(
DirectoryManager = new LoggingDirectoryManager(
Directory.GetFiles,
Directory.Exists,
(path) => Directory.CreateDirectory(path),
System.IO.File.Delete,
p_strLogFileFolder,
logFileFolder,
Path.DirectorySeparatorChar,
p_iRetainedFilesCountLimit);
retainedFilesCountLimit);
}
catch (Exception l_oException)
{
Log.Error("Log directory manager could not be instanciated successfully. {@l_oException}", l_oException);
m_oDirectoryManager = new EmptyDirectoryLoggingManger();
DirectoryManager = new EmptyDirectoryLoggingManger();
}
}
try
{
m_oDirectoryManager.DeleteObsoleteLogs();
DirectoryManager.DeleteObsoleteLogs();
}
catch (Exception l_oException)
{
Log.Error("Not all obsolte log files could be deleted successfully. {@l_oException}", l_oException);
}
if (p_oLoggerConfiguration == null)
if (loggerConfiguration == null)
{
return null;
}
return p_oLoggerConfiguration.File(
return loggerConfiguration.File(
new JsonFormatter(),
m_oDirectoryManager.LogFileName,
DirectoryManager.LogFileName,
/*shared: true, // Leads to exception if activated.*/
rollingInterval: Serilog.RollingInterval.Infinite,
retainedFileCountLimit: p_iRetainedFilesCountLimit);
retainedFileCountLimit: retainedFilesCountLimit);
}
/// <summary> Gets all log files in logging directory. </summary>
@ -82,7 +82,7 @@ namespace TINK.Model.Logging
{
try
{
return m_oDirectoryManager.GetLogFiles();
return DirectoryManager.GetLogFiles();
}
catch (Exception l_oException)
{
@ -97,7 +97,7 @@ namespace TINK.Model.Logging
public static string GetLogFilePath(
this ILogger p_oLogger)
{
return m_oDirectoryManager.LogFilePath;
return DirectoryManager.LogFilePath;
}
}
}

View file

@ -59,7 +59,7 @@ namespace TINK.Model.Logging
if (string.IsNullOrEmpty(LogFileTitle))
{
LogFileTitle = $"{ DateTime.Now:yyyy_MM_dd_HH_mm_ss}.jsnl";
LogFileTitle = $"{DateTime.Now:yyyy_MM_dd_HH_mm_ss}.jsnl";
}
// Create directory if direcotry does not exist.
@ -74,7 +74,6 @@ namespace TINK.Model.Logging
throw new FileOperationException($"Logging directory {LogFilePath} could not be created successfully.", l_oException);
}
}
}

View file

@ -34,6 +34,9 @@ namespace TINK.Model.Settings
/// <summary> Key of the logging level entry. </summary>
public const string MINLOGGINGLEVELKEY = "MinimumLoggingLevel";
/// <summary> Key of the logging level entry. </summary>
public const string ISREPORTLEVELVERBOSEKEY = "IsReportLevelVerbose";
/// <summary> Key of the center to ... entry. </summary>
public const string CENTERMAPTOCURRENTLOCATION = "CenterMapToCurrentLocation";
@ -273,6 +276,16 @@ namespace TINK.Model.Settings
return (LogEventLevel)int.Parse(JsonConvert.DeserializeObject<string>(l_strLevel));
}
/// <summary> Gets a value indicating whether report level is verbose or not.</summary>
/// <param name="settingsJSON">Dictionary to get value from.</param>
public static bool? GetIsReportLevelVerbose(Dictionary<string, string> settingsJSON) => GetNullableEntry<bool>(ISREPORTLEVELVERBOSEKEY, settingsJSON);
/// <summary> Sets a value indicating whether report level is verbose or not.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get value from.</param>
public static Dictionary<string, string> SetIsReportLevelVerbose(this IDictionary<string, string> targetDictionary, bool isReportLevelVerbose)
=> SetEntry(isReportLevelVerbose, ISREPORTLEVELVERBOSEKEY, targetDictionary);
/// <summary> Sets the logging level.</summary>
/// <param name="p_oSettingsJSON">Dictionary to get logging level from.</param>
public static Dictionary<string, string> SetMinimumLoggingLevel(this IDictionary<string, string> p_oTargetDictionary, LogEventLevel p_oLevel)

View file

@ -15,6 +15,8 @@ namespace TINK.Model.Settings
{
public const LogEventLevel DEFAULTLOGGINLEVEL = LogEventLevel.Error;
public const bool DEFAULTREPOTLEVEL = false;
// Default value of the expires after entry. Controls the expiration time of the cache values.
private TimeSpan DEFAULTEXPIRESAFTER = TimeSpan.FromSeconds(1);
@ -24,6 +26,7 @@ namespace TINK.Model.Settings
/// <param name="activeUri"></param>
/// <param name="pollingParameters"></param>
/// <param name="minimumLogEventLevel">Minimum logging level to be applied.</param>
/// <param name="isReportLevelVerbose">True if logging level is verbose.</param>
/// <param name="expiresAfter">Holds the expires after value.</param>
/// <param name="activeLockService">Gets the name of the lock service to use.</param>
/// <param name="connectTimeout">Timeout to apply when connecting to bluetooth lock</param>
@ -34,6 +37,7 @@ namespace TINK.Model.Settings
Uri activeUri = null,
PollingParameters pollingParameters = null,
LogEventLevel? minimumLogEventLevel = null,
bool? isReportLevelVerbose = null,
TimeSpan? expiresAfter = null,
string activeLockService = null,
TimeSpan? connectTimeout = null,
@ -48,6 +52,7 @@ namespace TINK.Model.Settings
ActiveUri = GetActiveUri(activeUri);
PollingParameters = pollingParameters ?? PollingParameters.Default;
MinimumLogEventLevel = minimumLogEventLevel ?? DEFAULTLOGGINLEVEL;
IsReportLevelVerbose = isReportLevelVerbose ?? DEFAULTREPOTLEVEL;
ExpiresAfter = expiresAfter ?? DEFAULTEXPIRESAFTER;
ActiveLockService = activeLockService ?? LocksServicesContainerMutable.DefaultLocksservice;
ConnectTimeout = connectTimeout ?? new TimeSpan(0, 0, TimeOutProvider.DEFAULT_BLUETOOTHCONNECT_TIMEOUTSECONDS); // Try one sec. to connect.
@ -93,6 +98,9 @@ namespace TINK.Model.Settings
public string ActiveTheme { get; }
/// <summary> Gets a value indicating whether reporting level is verbose or not.</summary>
public bool IsReportLevelVerbose { get; }
public static Uri GetActiveUri(Uri activeUri) => activeUri ?? Services.CopriApi.ServerUris.CopriServerUriList.DefaultActiveUri;
public static bool GetCenterMapToCurrentLocation(Uri activeUri)

View file

@ -5,7 +5,7 @@ namespace TINK.Model.Station
public interface IStation
{
/// <summary> Holds the unique id of the station.c</summary>
int Id { get; }
string Id { get; }
/// <summary> Holds the group to which the station belongs.</summary>
IEnumerable<string> Group { get; }

View file

@ -6,7 +6,7 @@ namespace TINK.Model.Station
public class NullStation : IStation
{
/// <summary> Holds the unique id of the station.c</summary>
public int Id => -1;
public string Id => null;
/// <summary> Holds the group to which the station belongs.</summary>
public IEnumerable<string> Group => new List<string>();

View file

@ -12,7 +12,7 @@ namespace TINK.Model.Station
/// <param name="p_oPosition">GPS- position of the station.</param>
/// <param name="p_strStationName">Name of the station.</param>
public Station(
int p_iId,
string p_iId,
IEnumerable<string> p_oGroup,
Position p_oPosition,
string p_strStationName = "")
@ -24,7 +24,7 @@ namespace TINK.Model.Station
}
/// <summary> Holds the unique id of the station.c</summary>
public int Id { get; }
public string Id { get; }
/// <summary> Holds the group to which the station belongs.</summary>
public IEnumerable<string> Group { get; }

View file

@ -4,10 +4,10 @@ using System.Collections.Generic;
namespace TINK.Model.Station
{
public class StationDictionary : IEnumerable<Station>
public class StationDictionary : IEnumerable<IStation>
{
/// <summary> Holds the list of stations. </summary>
private readonly IDictionary<int, Station> m_oStationDictionary;
private readonly IDictionary<string, IStation> m_oStationDictionary;
/// <summary> Count of stations. </summary>
public int Count { get { return m_oStationDictionary.Count; } }
@ -16,16 +16,16 @@ namespace TINK.Model.Station
/// <summary> Constructs a station dictionary object. </summary>
/// <param name="p_oVersion">Version of copri- service.</param>
public StationDictionary(Version p_oVersion = null, IDictionary<int, Station> p_oStations = null)
public StationDictionary(Version p_oVersion = null, IDictionary<string, IStation> p_oStations = null)
{
m_oStationDictionary = p_oStations ?? new Dictionary<int, Station>();
m_oStationDictionary = p_oStations ?? new Dictionary<string, IStation>();
CopriVersion = p_oVersion != null
? new Version(p_oVersion.Major, p_oVersion.Minor, p_oVersion.Revision, p_oVersion.Build)
: new Version(0, 0, 0, 0);
}
public IEnumerator<Station> GetEnumerator()
public IEnumerator<IStation> GetEnumerator()
{
return m_oStationDictionary.Values.GetEnumerator();
}
@ -33,41 +33,41 @@ namespace TINK.Model.Station
/// <summary>
/// Deteermines whether a station by given key exists.
/// </summary>
/// <param name="p_strKey">Key to check.</param>
/// <param name="key">Key to check.</param>
/// <returns>True if station exists.</returns>
public bool ContainsKey(int p_strKey)
public bool ContainsKey(string key)
{
return m_oStationDictionary.ContainsKey(p_strKey);
return m_oStationDictionary.ContainsKey(key);
}
/// <summary>
/// Remove a station by station id.
/// </summary>
/// <param name="p_iId"></param>
public void RemoveById(int p_iId)
/// <param name="id"></param>
public void RemoveById(string id)
{
if (!m_oStationDictionary.ContainsKey(p_iId))
if (!m_oStationDictionary.ContainsKey(id))
{
// Nothing to do if there is no station with given name.
return;
}
m_oStationDictionary.Remove(p_iId);
m_oStationDictionary.Remove(id);
}
/// <summary>
/// Remove a station by station name.
/// </summary>
/// <param name="p_iId"></param>
public Station GetById(int p_iId)
/// <param name="id"></param>
public IStation GetById(string id)
{
if (!m_oStationDictionary.ContainsKey(p_iId))
if (!m_oStationDictionary.ContainsKey(id))
{
// Nothing to do if there is no station with given name.
return null;
}
return m_oStationDictionary[p_iId];
return m_oStationDictionary[id];
}
/// <summary>

View file

@ -22,6 +22,7 @@ using TINK.ViewModel.Settings;
using TINK.Services;
using TINK.Services.BluetoothLock.BLE;
using Xamarin.Forms;
using TINK.Model.Station;
namespace TINK.Model
{
@ -34,7 +35,7 @@ namespace TINK.Model
/// <returns>True if setting credentials succeeded.</returns>
public delegate bool SetCredentialsDelegate(string p_strMailAddress, string p_strPassword);
/// <summary>Returns the id of the app to be identified by copri.</summary>
/// <summary>Returns the id of the app (sharee.bike) to be identified by copri.</summary>
public static string MerchantId => "oiF2kahH";
/// <summary>
@ -67,6 +68,9 @@ namespace TINK.Model
/// <summary> Gets the minimum logging level. </summary>
public LogEventLevel MinimumLogEventLevel { get; set; }
/// <summary> Gets a value indicating whether reporting level is verbose or not.</summary>
public bool IsReportLevelVerbose { get; set; }
/// <summary> Holds the uri which is applied after restart. </summary>
public Uri NextActiveUri { get; set; }
@ -81,6 +85,7 @@ namespace TINK.Model
.SetGroupFilterSettings(FilterGroupSetting)
.SetAppVersion(AppVersion)
.SetMinimumLoggingLevel(MinimumLogEventLevel)
.SetIsReportLevelVerbose(IsReportLevelVerbose)
.SetExpiresAfter(ExpiresAfter)
.SetWhatsNew(AppVersion)
.SetActiveLockService(LocksServices.Active.GetType().FullName)
@ -115,7 +120,11 @@ namespace TINK.Model
/// <summary>
/// Holds the default polling value.
/// </summary>
#if USCSHARP9
public TimeSpan DefaultPolling => new (0, 0, 10);
#else
public TimeSpan DefaultPolling => new TimeSpan(0, 0, 10);
#endif
/// <summary> Constructs TinkApp object. </summary>
/// <param name="settings"></param>
@ -139,10 +148,9 @@ namespace TINK.Model
Settings.Settings settings,
IStore accountStore,
Func<bool, Uri, string, string, TimeSpan, IConnector> connectorFactory,
IGeolocation geolocationService,
IGeolodationDependent geolodationServiceDependent,
IServicesContainer<IGeolocation> geolocationServicesContainer,
ILocksService locksService,
IDevice device,
ISmartDevice device,
ISpecialFolder specialFolder,
ICipher cipher,
IPermissions permissions = null,
@ -188,16 +196,8 @@ namespace TINK.Model
new HashSet<object> { new Themes.Konrad() , new Themes.ShareeBike() },
settings.ActiveTheme);
GeolocationServices = new ServicesContainerMutable<IGeolocation>(
geolocationService == null
? new HashSet<IGeolocation> { new LastKnownGeolocationService(geolodationServiceDependent), new SimulatedGeolocationService(geolodationServiceDependent), new GeolocationService(geolodationServiceDependent) }
: new HashSet<IGeolocation> { geolocationService },
geolocationService == null
? (lastVersion >= new Version(3, 0, 173) ? settings.ActiveGeolocationService : typeof(LastKnownGeolocationService).FullName)
: geolocationService.GetType().FullName);
// Load filters from settings or apply defaults if no settings are available
var l_oAccount = accountStore.Load();
GeolocationServices = geolocationServicesContainer
?? throw new ArgumentException($"Can not instantiate {nameof(TinkApp)}- object. No geolocation services container object available.");
if (settings.ActiveUri == new Uri(CopriServerUriList.TINK_LIVE) ||
settings.ActiveUri == new Uri(CopriServerUriList.TINK_DEVEL))
@ -217,7 +217,7 @@ namespace TINK.Model
CenterMapToCurrentLocation = settings.CenterMapToCurrentLocation;
Device = device
SmartDevice = device
?? throw new ArgumentException("Can not instantiate TinkApp- object. No device information provider available.");
if (specialFolder == null)
@ -236,12 +236,10 @@ namespace TINK.Model
SettingsFileFolder = specialFolder.GetInternalPersonalDir();
SelectedStation = null;
ActiveUser = new User.User(
accountStore,
l_oAccount,
device.GetIdentifier());
accountStore.GetType().Name == "StoreLegacy" ? new Store() : accountStore,
accountStore.Load().Result,
device.Identifier);
this.isConnectedFunc = isConnectedFunc ?? (() => CrossConnectivity.Current.IsConnected);
@ -249,7 +247,7 @@ namespace TINK.Model
// Create filtered connector for offline mode.
m_oConnector = FilteredConnectorFactory.Create(
FilterGroupSetting.DoFilter(l_oAccount.DoFilter(GroupFilterMapPage.DoFilter())),
FilterGroupSetting.DoFilter(GroupFilterMapPage.DoFilter()),
ConnectorFactory(GetIsConnected(), settings.ActiveUri, ActiveUser.SessionCookie, ActiveUser.Mail, ExpiresAfter));
// Get uris from file.
@ -266,8 +264,7 @@ namespace TINK.Model
MinimumLogEventLevel = settings.MinimumLogEventLevel;
Permissions = permissions ??
throw new ArgumentException("Can not instantiate TinkApp- object. Permissions object must never be null.");
IsReportLevelVerbose = settings.IsReportLevelVerbose;
WhatsNew = new WhatsNew(AppVersion, lastVersion, whatsNewShownInVersion);
@ -299,13 +296,10 @@ namespace TINK.Model
public User.User ActiveUser { get; }
/// <summary> Reference of object which provides device information. </summary>
public IDevice Device { get; }
/// <summary> Os permission.</summary>
public IPermissions Permissions { get; }
public ISmartDevice SmartDevice { get; }
/// <summary> Holds delegate to determine whether device is connected or not.</summary>
private Func<bool> isConnectedFunc;
private readonly Func<bool> isConnectedFunc;
/// <summary> Gets whether device is connected to internet or not. </summary>
public bool GetIsConnected() => isConnectedFunc();
@ -328,13 +322,16 @@ namespace TINK.Model
public ICipher Cipher { get; }
/// <summary> Name of the station which is selected. </summary>
public int? SelectedStation { get; set; }
public IStation SelectedStation { get; set; } = new Station.Station(null, new List<string>(), null);
/// <summary> Holds the stations availalbe. </summary>
public IEnumerable<IStation> Stations { get; set; } = new List<Station.Station>();
/// <summary> Action to post to GUI thread.</summary>
public Action<SendOrPostCallback, object> PostAction { get; }
/// <summary> Function which creates a connector depending on connected status.</summary>
private Func<bool, Uri, string, string, TimeSpan, IConnector> ConnectorFactory { get; }
private Func<bool, Uri, string /*userAgent*/, string /*sessionCookie*/, TimeSpan, IConnector> ConnectorFactory { get; }
/// <summary> Holds the object which provides offline data.</summary>
private IFilteredConnector m_oConnector;
@ -362,14 +359,11 @@ namespace TINK.Model
return m_oConnector;
}
/// <summary> Query geolocation. </summary>
public IGeolocation Geolocation => GeolocationServices.Active;
/// <summary> Manages the different types of LocksService objects.</summary>
public LocksServicesContainerMutable LocksServices { get; set; }
/// <summary> Holds available app themes.</summary>
public ServicesContainerMutable<IGeolocation> GeolocationServices { get; }
public IServicesContainer<IGeolocation> GeolocationServices { get; }
/// <summary> Manages the different types of LocksService objects.</summary>
public ServicesContainerMutable<object> Themes { get; }

View file

@ -17,6 +17,7 @@ namespace TINK.Model.User.Account
PickLoggingLevel = 64, // Allows to select the logging level.
ShowDiagnostics = 128, // Turns on display of diagnostics.
SwitchNoSiteCaching = 1024, // Allows to turn off/ on caching of sites displayed in app hosted by COPRI
ReportLevel = 2048, // Allows extent to show error messages.
All = PickCopriServer +
ManageCopriCacheExpiration +
ManagePolling +
@ -24,7 +25,8 @@ namespace TINK.Model.User.Account
PickLocationServiceImplementation +
PickLoggingLevel +
ShowDiagnostics +
SwitchNoSiteCaching,
SwitchNoSiteCaching +
ReportLevel,
}
/// <summary>
@ -48,24 +50,24 @@ namespace TINK.Model.User.Account
public class Account : IAccount
{
/// <summary> Constructs an account object.</summary>
/// <param name="p_oMail">Mail addresss.</param>
/// <param name="p_Pwd">Password.</param>
/// <param name="p_oSessionCookie">Session cookie from copri.</param>
/// <param name="p_strGroup">Group holdig info about Group (TINK, Konrad, ...)</param>
/// <param name="mail">Mail addresss.</param>
/// <param name="password">Password.</param>
/// <param name="sessionCookie">Session cookie from copri.</param>
/// <param name="group">Group holdig info about Group (TINK, Konrad, ...)</param>
/// <param name="p_iDebugLevel">Flag which controls display of debug settings.</param>
public Account(
string p_oMail,
string p_Pwd,
string p_oSessionCookie,
IEnumerable<string> p_strGroup,
string mail,
string password,
string sessionCookie,
IEnumerable<string> group,
Permissions debugLevel = Permissions.None)
{
Mail = p_oMail;
Pwd = p_Pwd;
SessionCookie = p_oSessionCookie;
Mail = mail;
Pwd = password;
SessionCookie = sessionCookie;
DebugLevel = debugLevel;
Group = p_strGroup != null
? new HashSet<string>(p_strGroup).ToList()
Group = group != null
? new HashSet<string>(group).ToList()
: throw new ArgumentException("Can not instantiate account object. Reference to group list must not be empty.");
}

View file

@ -1,26 +1,26 @@
namespace TINK.Model.User.Account
using System.Threading.Tasks;
namespace TINK.Model.User.Account
{
/// <summary>
/// Interface to manage an account store.
/// </summary>
/// <summary>Interface to manage an account store.</summary>
public interface IStore
{
/// <summary>
/// Reads mail address and password from account store.
/// </summary>
/// <returns></returns>
IAccount Load();
Task<IAccount> Load();
/// <summary>
/// Writes mail address and password to account store.
/// </summary>
/// <param name="p_oMailAndPwd"></param>
void Save(IAccount p_oMailAndPwd);
/// <param name="mailAndPwd"></param>
Task Save(IAccount mailAndPwd);
/// <summary>
/// Deletes mail address and password from account store.
/// </summary>
/// <returns> Empty account instance if deleting succeeded.</returns>
IAccount Delete(IAccount p_oMailAndPwd);
IAccount Delete(IAccount mailAndPwd);
}
}

View file

@ -0,0 +1,67 @@
using Serilog;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Essentials;
namespace TINK.Model.User.Account
{
public class Store : IStore
{
/// <summary> Holds id of the debug level key. </summary>
private const string KEY_DEBUGLEVEL = "DebugLevel";
/// <summary> Holds the id of the session. </summary>
private const string KEY_SESSIONCOOKIE = "SessionCookie";
/// <summary> Holds id of the mail address key. </summary>
private const string KEY_MAILADDRESS = "MailAddress";
public IAccount Delete(IAccount account)
{
SecureStorage.RemoveAll();
return new EmptyAccount();
}
public async Task<IAccount> Load()
{
var mail = string.Empty;
var sessionCookie = string.Empty;
var debugLevel = Permissions.None;
try
{
mail = await SecureStorage.GetAsync(KEY_MAILADDRESS);
sessionCookie = await SecureStorage.GetAsync(KEY_SESSIONCOOKIE);
Enum.TryParse(await SecureStorage.GetAsync(KEY_DEBUGLEVEL), out debugLevel);
}
catch (Exception exception)
{
Log.ForContext<Store>().Error("Loading account from store failed. {Exception}", exception);
}
return new Account(
mail,
string.Empty,
sessionCookie,
new List<string>(),
debugLevel);
}
public async Task Save(IAccount mailAndPwd)
{
try
{
await SecureStorage.SetAsync(KEY_MAILADDRESS, mailAndPwd.Mail);
await SecureStorage.SetAsync(KEY_SESSIONCOOKIE, mailAndPwd.SessionCookie);
await SecureStorage.SetAsync(KEY_DEBUGLEVEL, mailAndPwd.DebugLevel.ToString());
}
catch (Exception exception)
{
Log.ForContext<Store>().Error("Saving account from store failed. {Exception}", exception);
}
}
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using TINK.Model.User.Account;
namespace TINK.Model.User
@ -29,14 +30,14 @@ namespace TINK.Model.User
/// </summary>
/// <param name="p_oAccountStore"> Object to use for loading and saving user data.</param>
public User(
IStore p_oAccountStore,
IAccount p_oAccount,
string p_strDeviceId)
IStore accountStore,
IAccount account,
string deviceId)
{
m_oStore = p_oAccountStore
m_oStore = accountStore
?? throw new ArgumentException("Can not instantiate user- object. No store functionality available.");
DeviceId = p_strDeviceId;
m_oAccount = new AccountMutable(p_oAccount);
DeviceId = deviceId;
m_oAccount = new AccountMutable(account);
}
/// <summary> Is fired wheneverlogin state changes. </summary>
@ -109,13 +110,13 @@ namespace TINK.Model.User
/// <param name="p_oAccount">Account to use for login.</param>
/// <param name="p_str_DeviceId">Holds the Id to identify the device.</param>
/// <param name="isConnected">True if connector has access to copri server, false if cached values are used.</param>
public void Login(IAccount account)
public async Task Login(IAccount account)
{
// Update account instance from copri data.
m_oAccount.Copy(account);
// Save data to store.
m_oStore.Save(m_oAccount);
await m_oStore.Save(m_oAccount);
// Nothing to do because state did not change.
StateChanged?.Invoke(this, new EventArgs());

View file

@ -316,7 +316,7 @@ namespace TINK.Model
},
{
new Version(3, 0, 208),
AppResources.ChangeLog3_0_208
AppResources.ChangeLog3_0_208 // Minor fixes.
},
{
new Version(3, 0, 209),
@ -340,7 +340,7 @@ namespace TINK.Model
},
{
new Version(3, 0, 218),
AppResources.ChangeLog3_0_218
AppResources.ChangeLog3_0_208
},
{
new Version(3, 0, 219),
@ -353,8 +353,61 @@ namespace TINK.Model
{
new Version(3, 0, 222),
AppResources.ChangeLog3_0_222
},
{
new Version(3, 0, 223),
AppResources.ChangeLog3_0_208
},
{
new Version(3, 0, 224),
AppResources.ChangeLog3_0_224
},
{
new Version(3, 0, 225),
AppResources.ChangeLog3_0_208
},
{
new Version(3, 0, 226),
AppResources.ChangeLog3_0_226
},
{
new Version(3, 0, 227),
AppResources.ChangeLog3_0_227
},
{
new Version(3, 0, 228),
AppResources.ChangeLog3_0_208
},
{
new Version(3, 0, 231),
AppResources.ChangeLog3_0_231
},
{
new Version(3, 0, 232),
AppResources.ChangeLog3_0_232
},
{
new Version(3, 0, 234),
AppResources.ChangeLog3_0_234
},
{
new Version(3, 0, 235),
AppResources.ChangeLog3_0_235
},
{
new Version(3, 0, 236),
AppResources.ChangeLog3_0_236
},
{
new Version(3, 0, 237),
AppResources.ChangeLog3_0_237
},
{
new Version(3, 0, 238),
AppResources.ChangeLog3_0_231
}
};
/// <summary> Manges the whats new information.</summary>

View file

@ -222,6 +222,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Offline..
/// </summary>
public static string ActivityTextConnectionStateOffline {
get {
return ResourceManager.GetString("ActivityTextConnectionStateOffline", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Disconnecting lock....
/// </summary>
@ -322,11 +331,20 @@ namespace TINK.MultilingualResources {
}
/// <summary>
/// Looks up a localized string similar to Connection error. Code: {0}..
/// Looks up a localized string similar to Connection error. Status code: {0}..
/// </summary>
public static string ActivityTextErrorWebException {
public static string ActivityTextErrorWebExceptionGeneralError {
get {
return ResourceManager.GetString("ActivityTextErrorWebException", resourceCulture);
return ResourceManager.GetString("ActivityTextErrorWebExceptionGeneralError", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Connection error. Status description: {0}..
/// </summary>
public static string ActivityTextErrorWebExceptionProtocolError {
get {
return ResourceManager.GetString("ActivityTextErrorWebExceptionProtocolError", resourceCulture);
}
}
@ -339,6 +357,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Cache used..
/// </summary>
public static string ActivityTextException {
get {
return ResourceManager.GetString("ActivityTextException", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Loading Stations and Bikes....
/// </summary>
@ -456,6 +483,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Updateing.....
/// </summary>
public static string ActivityTextUpdating {
get {
return ResourceManager.GetString("ActivityTextUpdating", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Updated to latest lock firmware..
/// </summary>
@ -557,15 +593,6 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Minor fixes..
/// </summary>
public static string ChangeLog3_0_218 {
get {
return ResourceManager.GetString("ChangeLog3_0_218", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Icons added to flyout menu..
/// </summary>
@ -602,6 +629,92 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Updated for latest server version..
/// </summary>
public static string ChangeLog3_0_224 {
get {
return ResourceManager.GetString("ChangeLog3_0_224", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Fixes
///- station markers are updated correctly after login/ logout if required
///- routing from &quot;bikes at station&quot; to &quot;login&quot; page and other routings corrected.
/// </summary>
public static string ChangeLog3_0_226 {
get {
return ResourceManager.GetString("ChangeLog3_0_226", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Feedback dialog added which is shown after returning bike..
/// </summary>
public static string ChangeLog3_0_227 {
get {
return ResourceManager.GetString("ChangeLog3_0_227", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Minor improvements..
/// </summary>
public static string ChangeLog3_0_231 {
get {
return ResourceManager.GetString("ChangeLog3_0_231", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Menu modified..
/// </summary>
public static string ChangeLog3_0_232 {
get {
return ResourceManager.GetString("ChangeLog3_0_232", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Tariff info page is displayed correctly again.
///Error handling improved..
/// </summary>
public static string ChangeLog3_0_234 {
get {
return ResourceManager.GetString("ChangeLog3_0_234", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Obsolete object replaced with up-to-date one..
/// </summary>
public static string ChangeLog3_0_235 {
get {
return ResourceManager.GetString("ChangeLog3_0_235", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Stations names instead descriptions shown on page title.
///Error message when opening lock fails improved.
///Layout of bike names and id display improved..
/// </summary>
public static string ChangeLog3_0_236 {
get {
return ResourceManager.GetString("ChangeLog3_0_236", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Hard- and software information send to backend when returning bike..
/// </summary>
public static string ChangeLog3_0_237 {
get {
return ResourceManager.GetString("ChangeLog3_0_237", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Lock of rented bike can not be found..
/// </summary>
@ -688,9 +801,18 @@ namespace TINK.MultilingualResources {
/// <summary>
/// Looks up a localized string similar to Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again..
/// </summary>
public static string ErrorOpenLockMessage {
public static string ErrorOpenLockBoldBlockedMessage {
get {
return ResourceManager.GetString("ErrorOpenLockMessage", resourceCulture);
return ResourceManager.GetString("ErrorOpenLockBoldBlockedMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Lock was blocked and might still be. Please ensure that no obstacle prevents lock from opening and try again..
/// </summary>
public static string ErrorOpenLockBoldWasBlockedMessage {
get {
return ResourceManager.GetString("ErrorOpenLockBoldWasBlockedMessage", resourceCulture);
}
}
@ -715,6 +837,15 @@ namespace TINK.MultilingualResources {
/// <summary>
/// Looks up a localized string similar to Lock can not be opened!.
/// </summary>
public static string ErrorOpenLockStillOpenTitle {
get {
return ResourceManager.GetString("ErrorOpenLockStillOpenTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error while opening lock!.
/// </summary>
public static string ErrorOpenLockTitle {
get {
return ResourceManager.GetString("ErrorOpenLockTitle", resourceCulture);
@ -770,6 +901,24 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Your feedback could not be send to server successfully..
/// </summary>
public static string ErrorReturnSubmitFeedbackMessage {
get {
return ResourceManager.GetString("ErrorReturnSubmitFeedbackMessage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Submitting feedback failed!.
/// </summary>
public static string ErrorReturnSubmitFeedbackTitle {
get {
return ResourceManager.GetString("ErrorReturnSubmitFeedbackTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Attachment could not be created..
/// </summary>
@ -871,15 +1020,6 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Bike Location {0}.
/// </summary>
public static string MarkingBikesAtStationTitle {
get {
return ResourceManager.GetString("MarkingBikesAtStationTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Contact.
/// </summary>
@ -1207,6 +1347,16 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to This version of the {0} App is not compatible with server detected.
///Please contact the support for help..
/// </summary>
public static string MessageCopriVersionIsOutdated {
get {
return ResourceManager.GetString("MessageCopriVersionIsOutdated", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Attention: Lock is closed!
///{0}
@ -1247,7 +1397,7 @@ namespace TINK.MultilingualResources {
}
/// <summary>
/// Looks up a localized string similar to Error during login!.
/// Looks up a localized string similar to Error while logging in!.
/// </summary>
public static string MessageLoginErrorTitle {
get {
@ -1300,6 +1450,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Error while logging out!.
/// </summary>
public static string MessageLogoutErrorTitle {
get {
return ResourceManager.GetString("MessageLogoutErrorTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Session has expired.
///Either there are more than 8 devices in use or the user&apos;s account is no longer valid.

View file

@ -61,7 +61,7 @@
<value>Kein Benutzer angemeldet.</value>
</data>
<data name="MessageAppVersionIsOutdated" xml:space="preserve">
<value>Diese version der {0} App ist veraltet. Bitte auf aktuelle Version aktualisieren.</value>
<value>Diese Version der {0} App ist veraltet. Bitte auf aktuelle Version aktualisieren.</value>
</data>
<data name="QuestionSupportmailSubject" xml:space="preserve">
<value>Betrifft die Anfrage/ Anmerkung die {0}-App oder ein allgemeines Thema?</value>
@ -99,9 +99,6 @@
<data name="MarkingTabFees" xml:space="preserve">
<value>Tarife</value>
</data>
<data name="ErrorOpenLockMessage" xml:space="preserve">
<value>Schloss ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen.</value>
</data>
<data name="ErrorCloseLockBoldBlockedMessage" xml:space="preserve">
<value>Schloss ist blockiert. Bitte sicherstellen, dass keine Speiche oder ein anderer Gegenstand das Schloss blockiert und Vorgang wiederholen.</value>
</data>
@ -190,9 +187,6 @@ Entweder es sind mehr als 8 Geräte in Benutzung oder das Konto ist nicht mehr g
Die Nutzung der App ist auf maximal 8 Geräten pro Konto möglich.
Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen, ob das Konto noch gültig ist.</value>
</data>
<data name="MarkingBikesAtStationTitle" xml:space="preserve">
<value>Fahrradstandort {0}</value>
</data>
<data name="StatusTextReservationExpiredCodeMaxReservationTime" xml:space="preserve">
<value>Code ist {0}, max. Reservierungszeit von {1} Min. abgelaufen.
</value>
@ -296,9 +290,6 @@ Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen
<data name="ErrorOpenLockOutOfReadMessage" xml:space="preserve">
<value>Schloss kann erst geöffnet werden, wenn Rad in der Nähe ist.</value>
</data>
<data name="ErrorOpenLockTitle" xml:space="preserve">
<value>Schloss kann nicht geöffnet werden!</value>
</data>
<data name="ErrorCloseLockOutOfReachMessage" xml:space="preserve">
<value>Schloss kann erst geschlossen werden, wenn das Rad in der Nähe ist. </value>
</data>
@ -480,9 +471,6 @@ Bitte App neu starten um Rad Infos zu bekommen.</value>
<data name="ActivityTextErrorWebConnectFailureException" xml:space="preserve">
<value>Verbindung unterbrochen, Server nicht erreichbar.</value>
</data>
<data name="ActivityTextErrorWebException" xml:space="preserve">
<value>Verbindungsfehler. Code: {0}.</value>
</data>
<data name="ActivityTextErrorWebForbiddenException" xml:space="preserve">
<value>Verbindung unterbrochen, Server beschäftigt.</value>
</data>
@ -528,9 +516,6 @@ Bitte App neu starten um Rad Infos zu bekommen.</value>
<data name="ChangeLog3_0_217" xml:space="preserve">
<value>Pakete aktualisiert.</value>
</data>
<data name="ChangeLog3_0_218" xml:space="preserve">
<value>Kleine Verbesserungen.</value>
</data>
<data name="ChangeLog3_0_219" xml:space="preserve">
<value>Icons zum Flyout-Menü hinzugefügt.</value>
</data>
@ -543,4 +528,77 @@ Bitte App neu starten um Rad Infos zu bekommen.</value>
<data name="ChangeLog3_0_222" xml:space="preserve">
<value>Kleine Fehlerbehebungen und Verbesserungen.</value>
</data>
<data name="MessageLogoutErrorTitle" xml:space="preserve">
<value>Fehler beim Abmelden!</value>
</data>
<data name="MessageCopriVersionIsOutdated" xml:space="preserve">
<value>Diese Version der {0} App ist nicht kompatibel mit dem erkannten Server.
Bitte kontaktieren sie den Support!</value>
</data>
<data name="ChangeLog3_0_224" xml:space="preserve">
<value>Aktualisiert auf aktuelle Serverversion.</value>
</data>
<data name="ChangeLog3_0_226" xml:space="preserve">
<value>Fehlerbehebungen
- Stationssymbole werden nach dem An-/ Abmelden aktualisiert
- Navigation von "Fahrradstandort X" auf "Anmelen"- Seite und andere Verknüpfungen korrigiert
</value>
</data>
<data name="ActivityTextErrorWebExceptionProtocolError" xml:space="preserve">
<value>Verbindungsfehler. Statusbeschreibung: {0}.</value>
</data>
<data name="ActivityTextErrorWebExceptionGeneralError" xml:space="preserve">
<value>Verbindungsfehler. Status statucode: {0}.</value>
</data>
<data name="ChangeLog3_0_227" xml:space="preserve">
<value>Seite zur Eingabe von Rückmeldungen hinzugefügt, der nach Rückgabe eines Rads angezeigt wird.</value>
</data>
<data name="ActivityTextConnectionStateOffline" xml:space="preserve">
<value>Offline.</value>
</data>
<data name="ActivityTextException" xml:space="preserve">
<value>Cache Daten.</value>
</data>
<data name="ErrorReturnSubmitFeedbackMessage" xml:space="preserve">
<value>Ihre Rückmeldung konnte nicht an den Server übermittelt werden.</value>
</data>
<data name="ErrorReturnSubmitFeedbackTitle" xml:space="preserve">
<value>Übermittlung der Rückmeldung fehlgeschlagen!</value>
</data>
<data name="ActivityTextUpdating" xml:space="preserve">
<value>Aktualisiere....</value>
</data>
<data name="ChangeLog3_0_231" xml:space="preserve">
<value>Kleine Verbesserungen.</value>
</data>
<data name="ChangeLog3_0_232" xml:space="preserve">
<value>Menu geändert.</value>
</data>
<data name="ChangeLog3_0_234" xml:space="preserve">
<value>Tarifinfo-Seite wird wieder korrekt angezeigt.
Fehlerhandling verbessert.</value>
</data>
<data name="ChangeLog3_0_235" xml:space="preserve">
<value>Veraltetes Objekt durch aktuelles ersetzt.</value>
</data>
<data name="ErrorOpenLockBoldBlockedMessage" xml:space="preserve">
<value>Schloss ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen.</value>
</data>
<data name="ErrorOpenLockBoldWasBlockedMessage" xml:space="preserve">
<value>Schloss war oder ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen.</value>
</data>
<data name="ErrorOpenLockTitle" xml:space="preserve">
<value>Fehler beim Schlossöffnen!</value>
</data>
<data name="ErrorOpenLockStillOpenTitle" xml:space="preserve">
<value>Schloss kann nicht geöffnet werden!</value>
</data>
<data name="ChangeLog3_0_236" xml:space="preserve">
<value>Anzeige von Stationsnamen statt Nummern.
Fehlermeldung beim Öffnen von Schloss verbessert.
Layout Anzeige Radnamen und nummern verbessert.</value>
</data>
<data name="ChangeLog3_0_237" xml:space="preserve">
<value>Hard- und software information werden an Backend übermittelt bei Radrückgabe.</value>
</data>
</root>

View file

@ -156,9 +156,6 @@
<data name="ErrorCloseLockMovingMessage" xml:space="preserve">
<value>Lock can only be closed if bike is not moving. Please park bike and try again.</value>
</data>
<data name="ErrorOpenLockMessage" xml:space="preserve">
<value>Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.</value>
</data>
<data name="ErrorReservedSearchMessage" xml:space="preserve">
<value>Lock of reserved bike can not be found.</value>
</data>
@ -193,9 +190,6 @@ Bike can only be returned if bike is in reach and location information is avail
<data name="MarkingAccount" xml:space="preserve">
<value>Account</value>
</data>
<data name="MarkingBikesAtStationTitle" xml:space="preserve">
<value>Bike Location {0}</value>
</data>
<data name="MarkingFeedbackAndContact" xml:space="preserve">
<value>Contact</value>
</data>
@ -378,7 +372,7 @@ Please login to app once again. In case this fails please check on website if th
<value>Connection error during registration!</value>
</data>
<data name="MessageLoginErrorTitle" xml:space="preserve">
<value>Error during login!</value>
<value>Error while logging in!</value>
</data>
<data name="MessageLoginRecoverPassword" xml:space="preserve">
<value>Please connect to Internet to recover the password.</value>
@ -396,7 +390,7 @@ Please login to app once again. In case this fails please check on website if th
<value>Lock cannot be opened until bike is near.</value>
</data>
<data name="ErrorOpenLockTitle" xml:space="preserve">
<value>Lock can not be opened!</value>
<value>Error while opening lock!</value>
</data>
<data name="ErrorCloseLockOutOfReachMessage" xml:space="preserve">
<value>Lock cannot be closed until bike is near.</value>
@ -577,8 +571,8 @@ Please restart app in order to get bike info.</value>
<data name="ActivityTextErrorWebConnectFailureException" xml:space="preserve">
<value>Connection interrupted, server unreachable.</value>
</data>
<data name="ActivityTextErrorWebException" xml:space="preserve">
<value>Connection error. Code: {0}.</value>
<data name="ActivityTextErrorWebExceptionGeneralError" xml:space="preserve">
<value>Connection error. Status code: {0}.</value>
</data>
<data name="ActivityTextErrorWebForbiddenException" xml:space="preserve">
<value>Connection interrupted, server busy.</value>
@ -625,9 +619,6 @@ Please restart app in order to get bike info.</value>
<data name="ChangeLog3_0_217" xml:space="preserve">
<value>Packages updated.</value>
</data>
<data name="ChangeLog3_0_218" xml:space="preserve">
<value>Minor fixes.</value>
</data>
<data name="ChangeLog3_0_219" xml:space="preserve">
<value>Icons added to flyout menu.</value>
</data>
@ -640,4 +631,70 @@ Please restart app in order to get bike info.</value>
<data name="ChangeLog3_0_222" xml:space="preserve">
<value>Minor bugfix and improvements.</value>
</data>
<data name="MessageLogoutErrorTitle" xml:space="preserve">
<value>Error while logging out!</value>
</data>
<data name="MessageCopriVersionIsOutdated" xml:space="preserve">
<value>This version of the {0} App is not compatible with server detected.
Please contact the support for help.</value>
</data>
<data name="ChangeLog3_0_224" xml:space="preserve">
<value>Updated for latest server version.</value>
</data>
<data name="ChangeLog3_0_226" xml:space="preserve">
<value>Fixes
- station markers are updated correctly after login/ logout if required
- routing from "bikes at station" to "login" page and other routings corrected</value>
</data>
<data name="ActivityTextErrorWebExceptionProtocolError" xml:space="preserve">
<value>Connection error. Status description: {0}.</value>
</data>
<data name="ChangeLog3_0_227" xml:space="preserve">
<value>Feedback dialog added which is shown after returning bike.</value>
</data>
<data name="ActivityTextConnectionStateOffline" xml:space="preserve">
<value>Offline.</value>
</data>
<data name="ActivityTextException" xml:space="preserve">
<value>Cache used.</value>
</data>
<data name="ErrorReturnSubmitFeedbackMessage" xml:space="preserve">
<value>Your feedback could not be send to server successfully.</value>
</data>
<data name="ErrorReturnSubmitFeedbackTitle" xml:space="preserve">
<value>Submitting feedback failed!</value>
</data>
<data name="ActivityTextUpdating" xml:space="preserve">
<value>Updateing....</value>
</data>
<data name="ChangeLog3_0_231" xml:space="preserve">
<value>Minor improvements.</value>
</data>
<data name="ChangeLog3_0_232" xml:space="preserve">
<value>Menu modified.</value>
</data>
<data name="ChangeLog3_0_234" xml:space="preserve">
<value>Tariff info page is displayed correctly again.
Error handling improved.</value>
</data>
<data name="ChangeLog3_0_235" xml:space="preserve">
<value>Obsolete object replaced with up-to-date one.</value>
</data>
<data name="ErrorOpenLockBoldBlockedMessage" xml:space="preserve">
<value>Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.</value>
</data>
<data name="ErrorOpenLockBoldWasBlockedMessage" xml:space="preserve">
<value>Lock was blocked and might still be. Please ensure that no obstacle prevents lock from opening and try again.</value>
</data>
<data name="ErrorOpenLockStillOpenTitle" xml:space="preserve">
<value>Lock can not be opened!</value>
</data>
<data name="ChangeLog3_0_236" xml:space="preserve">
<value>Stations names instead descriptions shown on page title.
Error message when opening lock fails improved.
Layout of bike names and id display improved.</value>
</data>
<data name="ChangeLog3_0_237" xml:space="preserve">
<value>Hard- and software information send to backend when returning bike.</value>
</data>
</root>

View file

@ -72,7 +72,7 @@
</trans-unit>
<trans-unit id="MessageAppVersionIsOutdated" translate="yes" xml:space="preserve">
<source>This version of the {0} App is outdated. Please update to the latest version.</source>
<target state="translated">Diese version der {0} App ist veraltet. Bitte auf aktuelle Version aktualisieren.</target>
<target state="translated">Diese Version der {0} App ist veraltet. Bitte auf aktuelle Version aktualisieren.</target>
</trans-unit>
<trans-unit id="QuestionSupportmailSubject" translate="yes" xml:space="preserve">
<source>Does your request/ comment relate to the {0}-app or to a more general subject?</source>
@ -122,10 +122,6 @@
<source>Pricing</source>
<target state="translated">Tarife</target>
</trans-unit>
<trans-unit id="ErrorOpenLockMessage" translate="yes" xml:space="preserve">
<source>Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.</source>
<target state="translated">Schloss ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen.</target>
</trans-unit>
<trans-unit id="ErrorCloseLockBoldBlockedMessage" translate="yes" xml:space="preserve">
<source>Lock is blocked. Please ensure that no spoke or any other obstacle prevents the lock from closing and try again.</source>
<target state="translated">Schloss ist blockiert. Bitte sicherstellen, dass keine Speiche oder ein anderer Gegenstand das Schloss blockiert und Vorgang wiederholen.</target>
@ -248,10 +244,6 @@ Entweder es sind mehr als 8 Geräte in Benutzung oder das Konto ist nicht mehr g
Die Nutzung der App ist auf maximal 8 Geräten pro Konto möglich.
Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen, ob das Konto noch gültig ist.</target>
</trans-unit>
<trans-unit id="MarkingBikesAtStationTitle" translate="yes" xml:space="preserve">
<source>Bike Location {0}</source>
<target state="translated">Fahrradstandort {0}</target>
</trans-unit>
<trans-unit id="StatusTextReservationExpiredCodeMaxReservationTime" translate="yes" xml:space="preserve">
<source>Code {0}, max. reservation time of {1} minutes expired.</source>
<target state="translated">Code ist {0}, max. Reservierungszeit von {1} Min. abgelaufen.
@ -365,7 +357,7 @@ Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen
<target state="translated">Verbindungsfehler beim Registrieren!</target>
</trans-unit>
<trans-unit id="MessageLoginErrorTitle" translate="yes" xml:space="preserve">
<source>Error during login!</source>
<source>Error while logging in!</source>
<target state="translated">Fehler bei der Anmeldung!</target>
</trans-unit>
<trans-unit id="MessageLoginRecoverPassword" translate="yes" xml:space="preserve">
@ -388,10 +380,6 @@ Bitte erneut in App anmelden. Sollte dies fehlschlagen bitte auf Website prüfen
<source>Lock cannot be opened until bike is near.</source>
<target state="translated">Schloss kann erst geöffnet werden, wenn Rad in der Nähe ist.</target>
</trans-unit>
<trans-unit id="ErrorOpenLockTitle" translate="yes" xml:space="preserve">
<source>Lock can not be opened!</source>
<target state="translated">Schloss kann nicht geöffnet werden!</target>
</trans-unit>
<trans-unit id="ErrorCloseLockOutOfReachMessage" translate="yes" xml:space="preserve">
<source>Lock cannot be closed until bike is near.</source>
<target state="translated">Schloss kann erst geschlossen werden, wenn das Rad in der Nähe ist. </target>
@ -640,10 +628,6 @@ Bitte App neu starten um Rad Infos zu bekommen.</target>
<source>Connection interrupted, server unreachable.</source>
<target state="translated">Verbindung unterbrochen, Server nicht erreichbar.</target>
</trans-unit>
<trans-unit id="ActivityTextErrorWebException" translate="yes" xml:space="preserve">
<source>Connection error. Code: {0}.</source>
<target state="translated">Verbindungsfehler. Code: {0}.</target>
</trans-unit>
<trans-unit id="ActivityTextErrorWebForbiddenException" translate="yes" xml:space="preserve">
<source>Connection interrupted, server busy.</source>
<target state="translated">Verbindung unterbrochen, Server beschäftigt.</target>
@ -704,10 +688,6 @@ Bitte App neu starten um Rad Infos zu bekommen.</target>
<source>Packages updated.</source>
<target state="translated">Pakete aktualisiert.</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_218" translate="yes" xml:space="preserve">
<source>Minor fixes.</source>
<target state="translated">Kleine Verbesserungen.</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_219" translate="yes" xml:space="preserve">
<source>Icons added to flyout menu.</source>
<target state="translated">Icons zum Flyout-Menü hinzugefügt.</target>
@ -724,6 +704,107 @@ Bitte App neu starten um Rad Infos zu bekommen.</target>
<source>Minor bugfix and improvements.</source>
<target state="translated">Kleine Fehlerbehebungen und Verbesserungen.</target>
</trans-unit>
<trans-unit id="MessageLogoutErrorTitle" translate="yes" xml:space="preserve">
<source>Error while logging out!</source>
<target state="translated">Fehler beim Abmelden!</target>
</trans-unit>
<trans-unit id="MessageCopriVersionIsOutdated" translate="yes" xml:space="preserve">
<source>This version of the {0} App is not compatible with server detected.
Please contact the support for help.</source>
<target state="translated">Diese Version der {0} App ist nicht kompatibel mit dem erkannten Server.
Bitte kontaktieren sie den Support!</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_224" translate="yes" xml:space="preserve">
<source>Updated for latest server version.</source>
<target state="translated">Aktualisiert auf aktuelle Serverversion.</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_226" translate="yes" xml:space="preserve">
<source>Fixes
- station markers are updated correctly after login/ logout if required
- routing from "bikes at station" to "login" page and other routings corrected</source>
<target state="translated">Fehlerbehebungen
- Stationssymbole werden nach dem An-/ Abmelden aktualisiert
- Navigation von "Fahrradstandort X" auf "Anmelen"- Seite und andere Verknüpfungen korrigiert
</target>
</trans-unit>
<trans-unit id="ActivityTextErrorWebExceptionProtocolError" translate="yes" xml:space="preserve">
<source>Connection error. Status description: {0}.</source>
<target state="translated">Verbindungsfehler. Statusbeschreibung: {0}.</target>
</trans-unit>
<trans-unit id="ActivityTextErrorWebExceptionGeneralError" translate="yes" xml:space="preserve">
<source>Connection error. Status code: {0}.</source>
<target state="translated">Verbindungsfehler. Status statucode: {0}.</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_227" translate="yes" xml:space="preserve">
<source>Feedback dialog added which is shown after returning bike.</source>
<target state="translated">Seite zur Eingabe von Rückmeldungen hinzugefügt, der nach Rückgabe eines Rads angezeigt wird.</target>
</trans-unit>
<trans-unit id="ActivityTextConnectionStateOffline" translate="yes" xml:space="preserve">
<source>Offline.</source>
<target state="translated">Offline.</target>
</trans-unit>
<trans-unit id="ActivityTextException" translate="yes" xml:space="preserve">
<source>Cache used.</source>
<target state="translated">Cache Daten.</target>
</trans-unit>
<trans-unit id="ErrorReturnSubmitFeedbackMessage" translate="yes" xml:space="preserve">
<source>Your feedback could not be send to server successfully.</source>
<target state="translated">Ihre Rückmeldung konnte nicht an den Server übermittelt werden.</target>
</trans-unit>
<trans-unit id="ErrorReturnSubmitFeedbackTitle" translate="yes" xml:space="preserve">
<source>Submitting feedback failed!</source>
<target state="translated">Übermittlung der Rückmeldung fehlgeschlagen!</target>
</trans-unit>
<trans-unit id="ActivityTextUpdating" translate="yes" xml:space="preserve">
<source>Updateing....</source>
<target state="translated">Aktualisiere....</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_231" translate="yes" xml:space="preserve">
<source>Minor improvements.</source>
<target state="translated">Kleine Verbesserungen.</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_232" translate="yes" xml:space="preserve">
<source>Menu modified.</source>
<target state="translated">Menu geändert.</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_234" translate="yes" xml:space="preserve">
<source>Tariff info page is displayed correctly again.
Error handling improved.</source>
<target state="translated">Tarifinfo-Seite wird wieder korrekt angezeigt.
Fehlerhandling verbessert.</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_235" translate="yes" xml:space="preserve">
<source>Obsolete object replaced with up-to-date one.</source>
<target state="translated">Veraltetes Objekt durch aktuelles ersetzt.</target>
</trans-unit>
<trans-unit id="ErrorOpenLockBoldBlockedMessage" translate="yes" xml:space="preserve">
<source>Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.</source>
<target state="translated">Schloss ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen.</target>
</trans-unit>
<trans-unit id="ErrorOpenLockBoldWasBlockedMessage" translate="yes" xml:space="preserve">
<source>Lock was blocked and might still be. Please ensure that no obstacle prevents lock from opening and try again.</source>
<target state="translated">Schloss war oder ist blockiert. Bitte Ursache von Blockierung beheben und Vorgang wiederholen.</target>
</trans-unit>
<trans-unit id="ErrorOpenLockTitle" translate="yes" xml:space="preserve">
<source>Error while opening lock!</source>
<target state="translated">Fehler beim Schlossöffnen!</target>
</trans-unit>
<trans-unit id="ErrorOpenLockStillOpenTitle" translate="yes" xml:space="preserve">
<source>Lock can not be opened!</source>
<target state="translated">Schloss kann nicht geöffnet werden!</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_236" translate="yes" xml:space="preserve">
<source>Stations names instead descriptions shown on page title.
Error message when opening lock fails improved.
Layout of bike names and id display improved.</source>
<target state="translated">Anzeige von Stationsnamen statt Nummern.
Fehlermeldung beim Öffnen von Schloss verbessert.
Layout Anzeige Radnamen und nummern verbessert.</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_237" translate="yes" xml:space="preserve">
<source>Hard- and software information send to backend when returning bike.</source>
<target state="translated">Hard- und software information werden an Backend übermittelt bei Radrückgabe.</target>
</trans-unit>
</group>
</body>
</file>

View file

@ -5,13 +5,13 @@ using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using TINK.Model.Repository.Exception;
using TINK.Model.Repository.Request;
using TINK.Model.Repository.Response;
using TINK.Model.Logging;
using TINK.Repository.Exception;
using TINK.Repository.Request;
using TINK.Repository.Response;
using TINK.Model.Logging;
using TINK.Model.Device;
namespace TINK.Model.Repository
namespace TINK.Repository
{
/// <summary> Object which manages calls to copri. </summary>
public class CopriCallsHttps : ICopriServer
@ -20,17 +20,17 @@ namespace TINK.Model.Repository
private IRequestBuilder requestBuilder;
/// <summary> Initializes a instance of the copri calls https object. </summary>
/// <param name="p_oCopriHost">Host to connect to. </param>
/// <param name="p_strMerchantId">Id of the merchant.</param>
/// <param name="copriHost">Host to connect to. </param>
/// <param name="merchantId">Id of the merchant.</param>
/// <param name="userAgent">Holds the name and version of the TINKApp.</param>
/// <param name="sessionCookie">Session cookie if user is logged in, null otherwise.</param>
public CopriCallsHttps(
Uri p_oCopriHost,
string p_strMerchantId,
Uri copriHost,
string merchantId,
string userAgent,
string sessionCookie = null)
{
m_oCopriHost = p_oCopriHost
m_oCopriHost = copriHost
?? throw new System.Exception($"Can not construct {GetType().ToString()}- object. Uri of copri host must not be null.");
UserAgent = !string.IsNullOrEmpty(userAgent)
@ -38,8 +38,8 @@ namespace TINK.Model.Repository
: throw new System.Exception($"Can not construct {GetType().ToString()}- object. User agent must not be null or empty.");
requestBuilder = string.IsNullOrEmpty(sessionCookie)
? new RequestBuilder(p_strMerchantId) as IRequestBuilder
: new RequestBuilderLoggedIn(p_strMerchantId, sessionCookie);
? new RequestBuilder(merchantId) as IRequestBuilder
: new RequestBuilderLoggedIn(merchantId, sessionCookie);
}
/// <summary> Holds the URL for rest calls.</summary>
@ -108,14 +108,15 @@ namespace TINK.Model.Repository
/// <returns>List of files.</returns>
public async Task<StationsAllResponse> GetStationsAsync()
{
return await GetStationsAsync(m_oCopriHost.AbsoluteUri, requestBuilder.GetStations(), UserAgent);
var stations = await GetStationsAsync(m_oCopriHost.AbsoluteUri, requestBuilder.GetStations(), UserAgent);
return stations;
}
/// <summary> Get authentication keys. </summary>
/// <param name="bikeId">Id of the bike to get keys for.</param>
/// <returns>Response holding authentication keys.</returns>
public async Task<ReservationBookingResponse> GetAuthKeys(int bikeId)
=> await GetAuthKeysAsync(m_oCopriHost.AbsoluteUri, requestBuilder.CalculateAuthKeys(bikeId), UserAgent);
public async Task<ReservationBookingResponse> GetAuthKeys(string bikeId)
=> await GetAuthKeysAsync(m_oCopriHost.AbsoluteUri, requestBuilder.CalculateAuthParameters(bikeId), UserAgent);
/// <summary> Gets booking request response.</summary>
@ -123,7 +124,7 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Booking response.</returns>
public async Task<ReservationBookingResponse> DoReserveAsync(
int bikeId,
string bikeId,
Uri operatorUri)
{
return await DoReserveAsync(
@ -137,7 +138,7 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response on cancel booking request.</returns>
public async Task<ReservationCancelReturnResponse> DoCancelReservationAsync(
int bikeId,
string bikeId,
Uri operatorUri)
{
return await DoCancelReservationAsync(
@ -151,11 +152,11 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response holding authentication keys.</returns>
public async Task<ReservationBookingResponse> CalculateAuthKeysAsync(
int bikeId,
string bikeId,
Uri operatorUri)
=> await GetAuthKeysAsync(
operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri,
requestBuilder.CalculateAuthKeys(bikeId),
requestBuilder.CalculateAuthParameters(bikeId),
UserAgent);
/// <summary> Updates lock state for a booked bike. </summary>
@ -166,7 +167,7 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response on updating locking state.</returns>
public async Task<ReservationBookingResponse> UpdateLockingStateAsync(
int bikeId,
string bikeId,
LocationDto location,
lock_state state,
double batteryLevel,
@ -185,7 +186,7 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Requst on booking request.</returns>
public async Task<ReservationBookingResponse> DoBookAsync(
int bikeId,
string bikeId,
Guid guid,
double batteryPercentage,
Uri operatorUri)
@ -199,31 +200,35 @@ namespace TINK.Model.Repository
/// <summary> Returns a bike. </summary>
/// <param name="bikeId">Id of the bike to return.</param>
/// <param name="location">Geolocation of lock.</param>
/// <param name="smartDevice">Provides info about hard and software.</param>
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response on returning request.</returns>
public async Task<ReservationCancelReturnResponse> DoReturn(
int bikeId,
string bikeId,
LocationDto location,
ISmartDevice smartDevice,
Uri operatorUri)
{
return await DoReturn(
operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri,
requestBuilder.DoReturn(bikeId, location),
requestBuilder.DoReturn(bikeId, location, smartDevice),
UserAgent);
}
/// <summary> Submits feedback to copri server. </summary>
/// <param name="bikeId">Id of the bike to which the feedback is related to.</param>
/// <param name="isBikeBroken">True if bike is broken.</param>
/// <param name="message">General purpose message or error description.</param>
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response on submitting feedback request.</returns>
public async Task<SubmitFeedbackResponse> DoSubmitFeedback(
string bikeId,
string message,
bool isBikeBroken,
Uri operatorUri) =>
await DoSubmitFeedback(
operatorUri?.AbsoluteUri ?? m_oCopriHost.AbsoluteUri,
requestBuilder.DoSubmitFeedback(message, isBikeBroken),
requestBuilder.DoSubmitFeedback(bikeId, message, isBikeBroken),
UserAgent);
/// <summary> Logs user in. </summary>
@ -238,10 +243,10 @@ namespace TINK.Model.Repository
{
#if !WINDOWS_UWP
/// Extract session cookie from response.
string l_oResponseText = string.Empty;
string response = string.Empty;
try
{
l_oResponseText = await PostAsync(
response = await PostAsync(
copriHost,
command,
userAgent,
@ -262,7 +267,7 @@ namespace TINK.Model.Repository
throw;
}
return JsonConvert.DeserializeObject<ResponseContainer<AuthorizationResponse>>(l_oResponseText)?.tinkjson;
return CopriCallsStatic.DeserializeResponse<AuthorizationResponse>(response, (version) => new UnsupportedCopriVersionDetectedException());
#else
return null;
#endif
@ -299,7 +304,7 @@ namespace TINK.Model.Repository
}
/// Extract session cookie from response.
return JsonConvert.DeserializeObject<ResponseContainer<AuthorizationoutResponse>>(l_oLogoutResponse)?.tinkjson;
return CopriCallsStatic.DeserializeResponse<AuthorizationoutResponse>(l_oLogoutResponse, (version) => new UnsupportedCopriVersionDetectedException());
#else
return null;
#endif
@ -317,10 +322,10 @@ namespace TINK.Model.Repository
string userAgent = null)
{
#if !WINDOWS_UWP
string l_oStationsAllResponse;
string response;
try
{
l_oStationsAllResponse = await PostAsync(p_strCopriHost, p_oCommand, userAgent);
response = await PostAsync(p_strCopriHost, p_oCommand, userAgent);
}
catch (System.Exception l_oException)
{
@ -338,7 +343,9 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return JsonConvert.DeserializeObject<ResponseContainer<StationsAllResponse>>(l_oStationsAllResponse)?.tinkjson;
return CopriCallsStatic.DeserializeResponse(
response,
(version) => CopriCallsMonkeyStore.GetEmptyStationsAllResponse(version));
#else
return null;
#endif
@ -354,10 +361,10 @@ namespace TINK.Model.Repository
string userAgent = null)
{
#if !WINDOWS_UWP
string l_oBikesAvaialbeResponse;
string response;
try
{
l_oBikesAvaialbeResponse = await PostAsync(p_strCopriHost, l_oCommand, userAgent);
response = await PostAsync(p_strCopriHost, l_oCommand, userAgent);
}
catch (System.Exception l_oException)
{
@ -375,7 +382,10 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return CopriCallsStatic.DeserializeBikesAvailableResponse(l_oBikesAvaialbeResponse);
return CopriCallsStatic.DeserializeResponse(
response,
(version) => CopriCallsMonkeyStore.GetEmptyBikesAvailableResponse(version));
#else
return null;
#endif
@ -391,10 +401,10 @@ namespace TINK.Model.Repository
string userAgent = null)
{
#if !WINDOWS_UWP
string l_oBikesOccupiedResponse;
string response;
try
{
l_oBikesOccupiedResponse = await PostAsync(p_strCopriHost, p_oCommand, userAgent);
response = await PostAsync(p_strCopriHost, p_oCommand, userAgent);
}
catch (System.Exception l_oException)
{
@ -412,7 +422,9 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return CopriCallsStatic.DeserializeBikesOccupiedResponse(l_oBikesOccupiedResponse);
return CopriCallsStatic.DeserializeResponse(
response,
(version) => CopriCallsMonkeyStore.GetEmptyBikesReservedOccupiedResponse(version));
#else
return null;
#endif
@ -449,7 +461,7 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return JsonConvert.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.tinkjson;
return JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.shareejson;
#else
return null;
#endif
@ -485,7 +497,7 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return JsonConvert.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.tinkjson;
return JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.shareejson;
#else
return null;
#endif
@ -522,7 +534,7 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return JsonConvert.DeserializeObject<ResponseContainer<ReservationCancelReturnResponse>>(l_oBikesAvaialbeResponse)?.tinkjson;
return JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationCancelReturnResponse>>(l_oBikesAvaialbeResponse)?.shareejson;
#else
return null;
#endif
@ -555,7 +567,7 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return JsonConvert.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.tinkjson;
return JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.shareejson;
#else
return null;
#endif
@ -588,7 +600,7 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return JsonConvert.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.tinkjson;
return JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(l_oBikesAvaialbeResponse)?.shareejson;
#else
return null;
#endif
@ -621,7 +633,7 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return JsonConvert.DeserializeObject<ResponseContainer<ReservationCancelReturnResponse>>(l_oBikesAvaialbeResponse)?.tinkjson;
return JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationCancelReturnResponse>>(l_oBikesAvaialbeResponse)?.shareejson;
#else
return null;
#endif
@ -654,7 +666,7 @@ namespace TINK.Model.Repository
}
// Extract bikes from response.
return JsonConvert.DeserializeObject<ResponseContainer<SubmitFeedbackResponse>>(userFeedbackResponse)?.tinkjson;
return JsonConvertRethrow.DeserializeObject<ResponseContainer<SubmitFeedbackResponse>>(userFeedbackResponse)?.shareejson;
#else
return null;
#endif
@ -775,9 +787,9 @@ namespace TINK.Model.Repository
return null;
#endif
}
catch (System.Exception l_oException)
catch (System.Exception exception)
{
Log.ForContext<CopriCallsHttps>().InformationOrError("Posting command {DisplayCommand} to host {URL} failed. {Exception}.", displayCommandFunc(), uRL, l_oException);
Log.ForContext<CopriCallsHttps>().InformationOrError("Posting command {DisplayCommand} to host {URL} failed. {Exception}.", displayCommandFunc(), uRL, exception);
throw;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,12 @@
using MonkeyCache.FileStore;
using System;
using System.Threading.Tasks;
using TINK.Model.Connector;
using TINK.Model.Repository.Request;
using TINK.Model.Repository.Response;
using TINK.Model.Services.CopriApi;
using TINK.Repository.Request;
using TINK.Repository.Response;
using TINK.Model.Services.CopriApi;
using TINK.Model.Device;
namespace TINK.Model.Repository
namespace TINK.Repository
{
public class CopriCallsMonkeyStore : ICopriCache
{
@ -18,7 +17,7 @@ namespace TINK.Model.Repository
private IRequestBuilder requestBuilder;
public const string BIKESAVAILABLE = @"{
""copri_version"" : ""3.0.0.0"",
""copri_version"" : ""4.1.0.0"",
""bikes"" : {},
""response_state"" : ""OK"",
""apiserver"" : ""https://app.tink-konstanz.de"",
@ -26,26 +25,78 @@ namespace TINK.Model.Repository
""response"" : ""bikes_available""
}";
/// <summary> Gets an empty response. </summary>
/// <param name="copriVersion">Version of empty response.</param>
/// <returns>Response.</returns>
public static BikesAvailableResponse GetEmptyBikesAvailableResponse(string copriVersion)
=> JsonConvertRethrow.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE.Replace("4.1.0.0", copriVersion));
#if !COPRIVERSION41
public const string BIKESOCCUPIED = @"{
""debuglevel"" : ""1"",
""user_id"" : """",
""response"" : ""user_bikes_occupied"",
""user_group"" : ""Konrad,TINK"",
""user_group"" : [ ""Konrad"", ""TINK"" ],
""authcookie"" : """",
""response_state"" : ""OK"",
""bikes_occupied"" : {},
""copri_version"" : ""3.0.0.0"",
""copri_version"" : ""4.1.0.0"",
""apiserver"" : ""https://app.tink-konstanz.de""
}";
#endif
public const string STATIONS = @"{
""apiserver"" : ""https://app.tink-konstanz.de"",
/// <summary> Gets an empty response. </summary>
/// <param name="copriVersion">Version of empty response.</param>
/// <returns>Response.</returns>
public static BikesReservedOccupiedResponse GetEmptyBikesReservedOccupiedResponse(string copriVersion)
=> JsonConvertRethrow.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED.Replace("4.1.0.0", copriVersion));
#if !COPRIVERSION41
/// <summary> Version COPRI 4.0. or earlier</summary>
public const string STATIONSALL = @"{
""apiserver"" : """",
""authcookie"" : """",
""response"" : ""stations_all"",
""copri_version"" : ""3.0.0.0"",
""copri_version"" : ""4.1.0.0"",
""stations"" : {},
""response_state"" : ""OK""
}";
#else
/// <summary> Version COPRI 4.1 or later. </summary>
public const string STATIONSALL = @"{
""uri_primary"": """"
""uri_operator_array"": [ ]
""authcookie"" : """",
""response"" : ""stations_all"",
""copri_version"" : ""4.1.0.0"",
""stations"" : {},
""response_state"" : ""OK""
}";
#endif
#if !COPRIVERSION41
/// <summary> Version COPRI 4.0. or earlier</summary>
public const string AUTHORIZATION = @"{
""user_group"" : [ """" ],
""response"" : ""authorization"",
""response_state"" : ""OK"",
""copri_version"" : ""4.1.0.0""
}";
#else
/// <summary> Version COPRI 4.0. or earlier</summary>
public const string AUTHORIZATION = @"{
""user_group"" : [],
""response"" : ""authorization"",
""response_state"" : ""OK"",
""copri_version"" : ""4.1.0.0""
}";
#endif
/// <summary> Gets an empty response. </summary>
/// <param name="copriVersion">Version of empty response.</param>
/// <returns>Response.</returns>
public static StationsAllResponse GetEmptyStationsAllResponse(string copriVersion)
=> JsonConvertRethrow.DeserializeObject<StationsAllResponse>(STATIONSALL.Replace("4.1.0.0", copriVersion));
/// <summary>
/// Holds the seconds after which station and bikes info is considered to be invalid.
@ -79,53 +130,57 @@ namespace TINK.Model.Repository
// Ensure that store holds valid entries.
if (!Barrel.Current.Exists(requestBuilder.GetBikesAvailable()))
{
AddToCache(JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE), new TimeSpan(0));
AddToCache(JsonConvertRethrow.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE), new TimeSpan(0));
}
// Do not query bikes occupied if no user is logged in (leads to not implemented exception)
if (!string.IsNullOrEmpty(sessionCookie) && !Barrel.Current.Exists(requestBuilder.GetBikesOccupied()))
{
AddToCache(JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED), new TimeSpan(0));
AddToCache(JsonConvertRethrow.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED), new TimeSpan(0));
}
if (!Barrel.Current.Exists(requestBuilder.GetStations()))
{
AddToCache(JsonConvert.DeserializeObject<StationsAllResponse>(STATIONS), new TimeSpan(0));
AddToCache(JsonConvertRethrow.DeserializeObject<StationsAllResponse>(STATIONSALL), new TimeSpan(0));
}
}
public Task<ReservationBookingResponse> DoReserveAsync(int bikeId, Uri operatorUri)
public Task<ReservationBookingResponse> DoReserveAsync(string bikeId, Uri operatorUri)
{
throw new System.Exception("Reservierung im Offlinemodus nicht möglich!");
}
public Task<ReservationCancelReturnResponse> DoCancelReservationAsync(int p_iBikeId, Uri operatorUri)
public Task<ReservationCancelReturnResponse> DoCancelReservationAsync(string bikeId, Uri operatorUri)
{
throw new System.Exception("Abbrechen einer Reservierung im Offlinemodus nicht möglich!");
}
public Task<ReservationBookingResponse> CalculateAuthKeysAsync(int bikeId, Uri operatorUri)
public Task<ReservationBookingResponse> CalculateAuthKeysAsync(string bikeId, Uri operatorUri)
=> throw new System.Exception("Schlosssuche im Offlinemodus nicht möglich!");
public Task<ReservationBookingResponse> UpdateLockingStateAsync(
int bikeId,
string bikeId,
LocationDto geolocation,
lock_state state,
double batteryLevel,
Uri operatorUri)
=> throw new System.Exception("Aktualisierung des Schlossstatuses im Offlinemodus nicht möglich!");
public Task<ReservationBookingResponse> DoBookAsync(int bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
public Task<ReservationBookingResponse> DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
{
throw new System.Exception("Buchung im Offlinemodus nicht möglich!");
}
public Task<ReservationCancelReturnResponse> DoReturn(int bikeId, LocationDto geolocation, Uri operatorUri)
public Task<ReservationCancelReturnResponse> DoReturn(
string bikeId,
LocationDto geolocation,
ISmartDevice smartDevice,
Uri operatorUri)
{
throw new System.Exception("Rückgabe im Offlinemodus nicht möglich!");
}
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string message, bool isBikeBroken, Uri operatorUri) =>
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string bikeId, string message, bool isBikeBroken, Uri operatorUri) =>
throw new System.Exception("Übermittlung von Feedback im Offlinemodus nicht möglich!");
public Task<AuthorizationResponse> DoAuthorizationAsync(string p_strMailAddress, string p_strPassword, string p_strDeviceId)
@ -207,7 +262,7 @@ namespace TINK.Model.Repository
{
Barrel.Current.Add(
requestBuilder.GetStations(),
JsonConvert.SerializeObject(stations),
JsonConvertRethrow.SerializeObject(stations),
expiresAfter);
}
}
@ -239,8 +294,8 @@ namespace TINK.Model.Repository
lock (monkeyLock)
{
Barrel.Current.Add(
requestBuilder.GetBikesAvailable(),
JsonConvert.SerializeObject(bikes),
requestBuilder.GetBikesAvailable(),
JsonConvertRethrow.SerializeObject(bikes),
expiresAfter);
}
}
@ -273,7 +328,7 @@ namespace TINK.Model.Repository
{
Barrel.Current.Add(
requestBuilder.GetBikesOccupied(),
JsonConvert.SerializeObject(bikes),
JsonConvertRethrow.SerializeObject(bikes),
expiresAfter);
}
}

View file

@ -1,28 +1,65 @@
using TINK.Model.Repository.Response;
using Serilog;
using System;
using TINK.Model.Connector;
using TINK.Repository.Response;
namespace TINK.Model.Repository
namespace TINK.Repository
{
public static class CopriCallsStatic
{
/// <summary>
/// Deserializes JSON from response string.
/// </summary>
/// <param name="p_strResponse">Response to deserialize.</param>
/// <returns></returns>
public static BikesAvailableResponse DeserializeBikesAvailableResponse(string p_strResponse)
#if !USCSHARP9
private static Version UNSUPPORTEDFUTURECOPRIVERSIONLOWER = new Version(4, 1);
#else
private static Version UNSUPPORTEDFUTURECOPRIVERSIONLOWER = new(4, 1);
#endif
#if !USCSHARP9
private static Version UNSUPPORTEDFUTURECOPRIVERSIONUPPER = new Version(4, 2);
#else
private static Version UNSUPPORTEDFUTURECOPRIVERSIONUPPER = new(4, 2);
#endif
public static Version UnsupportedVersionLower => UNSUPPORTEDFUTURECOPRIVERSIONLOWER;
public static Version UnsupportedVersionUpper => UNSUPPORTEDFUTURECOPRIVERSIONUPPER;
/// <summary> Deserializes reponse JSON if response is of supported version or provides default response otherwise. </summary>
/// <typeparam name="T">Type of response object.</typeparam>
/// <param name="response">Response JSON.</param>
/// <param name="emptyResponseFactory">Factory providing default delegate.</param>
/// <returns>Response object.</returns>
public static T DeserializeResponse<T>(this string response, Func<string, T> emptyResponseFactory) where T: class
{
return JsonConvert.DeserializeObject<ResponseContainer<BikesAvailableResponse>>(p_strResponse)?.tinkjson;
// Get COPRI version from respone.
var bikeInfoBase = JsonConvertRethrow.DeserializeObject<VersionindependentResponse>(response)?.shareejson;
if (bikeInfoBase.GetCopriVersion() < UNSUPPORTEDFUTURECOPRIVERSIONLOWER
|| bikeInfoBase.GetCopriVersion() >= UNSUPPORTEDFUTURECOPRIVERSIONUPPER)
{
return emptyResponseFactory?.Invoke(bikeInfoBase.copri_version) ?? null;
}
return JsonConvertRethrow.DeserializeObject<ResponseContainer<T>>(response)?.shareejson;
}
/// <summary>
/// Deserializes JSON from response string.
/// </summary>
/// <param name="p_strResponse">Response to deserialize.</param>
/// <returns></returns>
public static BikesReservedOccupiedResponse DeserializeBikesOccupiedResponse(string p_strResponse)
/// <summary> Deserializes reponse JSON if response is of supported version or throws an exception. </summary>
/// <typeparam name="T">Type of response object.</typeparam>
/// <param name="response">Response JSON.</param>
/// <param name="unsupportedVersionExectpion">Exception to fire.</param>
/// <returns>Response object.</returns>
public static T DeserializeResponse<T>(this string response, Func<string, System.Exception> unsupportedVersionExectpion = null) where T : class
{
return JsonConvert.DeserializeObject<ResponseContainer<BikesReservedOccupiedResponse>>(p_strResponse)?.tinkjson;
// Get COPRI version from respone.
var bikeInfoBase = JsonConvertRethrow.DeserializeObject<VersionindependentResponse>(response)?.shareejson;
if (bikeInfoBase.GetCopriVersion() < UNSUPPORTEDFUTURECOPRIVERSIONLOWER
|| bikeInfoBase.GetCopriVersion() >= UNSUPPORTEDFUTURECOPRIVERSIONUPPER)
{
Log.Error($"Unsupported copri version {bikeInfoBase.copri_version} detected on attempt to log in.");
throw unsupportedVersionExectpion?.Invoke(bikeInfoBase.copri_version) ?? new System.Exception($"Unsupported COPRI version {bikeInfoBase.copri_version} detected.");
}
return JsonConvertRethrow.DeserializeObject<ResponseContainer<T>>(response)?.shareejson;
}
}
}

View file

@ -1,4 +1,4 @@
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
/// <summary>
/// Is fired with reqest used a cookie which is not defined.
@ -15,11 +15,25 @@
{
}
/// <summary>
/// Gets whether authcookie is defined or not.
/// </summary>
/// <param name="reponse">Response to check</param>
/// <param name="actionText">Text holding contectin in which authcookie is checked.</param>
/// <param name="exception">Exception thrown if cookie is not defined.</param>
/// <returns></returns>
public static bool IsAuthcookieNotDefined(
Response.ResponseBase reponse,
string actionText,
out AuthcookieNotDefinedException exception)
{
if (reponse == null || reponse.response_state == null)
{
// Empty response or response withoud response state is no authcookie not defined exeception.
exception = null;
return false;
}
if (!reponse.response_state.ToUpper().Contains(AUTH_FAILURE_QUERY_AUTHCOOKIENOTDEFIED.ToUpper())
&& !reponse.response_state.ToUpper().Contains(AUTH_FAILURE_BOOK_AUTICOOKIENOTDEFIED.ToUpper())
&& !reponse.response_state.ToUpper().Contains(AUTH_FAILURE_BIKESOCCUPIED_AUTICOOKIENOTDEFIED.ToUpper())

View file

@ -1,4 +1,4 @@
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
public class InvalidAuthorizationResponseException : InvalidResponseException<Response.ResponseBase>
{

View file

@ -1,6 +1,6 @@
using System.Text.RegularExpressions;
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
/// <summary> Handles booking request which fail due to too many bikes requested/ booked.</summary>
public class BookingDeclinedException : InvalidResponseException

View file

@ -1,4 +1,4 @@
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
public class CallNotRequiredException : System.Exception
{

View file

@ -1,4 +1,4 @@
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
public class CommunicationException : System.Exception
{

View file

@ -1,6 +1,4 @@
using TINK.Model.Repository.Exception;
namespace TINK.Repository.Exception
namespace TINK.Repository.Exception
{
public class DeserializationException : CommunicationException
{

View file

@ -1,4 +1,4 @@
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
public class InvalidResponseException<T> : InvalidResponseException
{

View file

@ -1,5 +1,4 @@
using TINK.Model.Repository.Exception;

namespace TINK.Repository.Exception
{
public class NoGPSDataException : InvalidResponseException

View file

@ -1,6 +1,6 @@
using System.Text.RegularExpressions;
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
public class NotAtStationException : InvalidResponseException
{

View file

@ -1,4 +1,4 @@
using TINK.Model.Repository.Response;
using TINK.Repository.Response;
namespace TINK.Repository.Exception
{

View file

@ -1,4 +1,4 @@
using TINK.Model.Repository.Response;
using TINK.Repository.Response;
namespace TINK.Repository.Exception
{

View file

@ -0,0 +1,8 @@
namespace TINK.Repository.Exception
{
public class UnsupportedCopriVersionDetectedException : System.Exception
{
public UnsupportedCopriVersionDetectedException() : base("Unsupported app version detected.")
{}
}
}

View file

@ -1,4 +1,4 @@
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
public class WebConnectFailureException : CommunicationException
{

View file

@ -1,42 +1,53 @@
using System.Net;
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
public static class WebExceptionHelper
{
/// <summary> Gets if a exception is caused by an error connecting to copri (LAN or mobile data off/ not reachable, proxy, ...).</summary>
/// <param name="p_oException">Expection to check.</param>
/// <param name="exception">Expection to check.</param>
/// <returns>True if exception if caused by an connection error. </returns>
public static bool GetIsConnectFailureException(this System.Exception p_oException)
public static bool GetIsConnectFailureException(this System.Exception exception)
{
var l_oException = p_oException as WebException;
if (l_oException == null)
#if !USCSHARP9
if (!(exception is WebException webException))
#else
if (exception is not WebException webException)
#endif
{
return false;
}
return l_oException.Status == WebExceptionStatus.ConnectFailure // Happens if WLAN and mobile data is off/ Router denies internet access/ ...
|| l_oException.Status == WebExceptionStatus.NameResolutionFailure // Happens sometimes when not WLAN and no mobil connection are available (bad connection in lift).
|| l_oException.Status == WebExceptionStatus.ReceiveFailure; // Happened when modile was connected to WLAN
return webException.Status == WebExceptionStatus.ConnectFailure // Happens if WLAN and mobile data is off/ Router denies internet access/ ...
|| webException.Status == WebExceptionStatus.NameResolutionFailure // Happens sometimes when not WLAN and no mobil connection are available (bad connection in lift).
|| webException.Status == WebExceptionStatus.ReceiveFailure; // Happened when modile was connected to WLAN
}
/// <summary> Gets if a exception is caused by clicking too fast.</summary>
/// <param name="p_oException">Expection to check.</param>
/// <param name="exception">Expection to check.</param>
/// <returns>True if exception if caused by a fast click sequence. </returns>
public static bool GetIsForbiddenException(this System.Exception p_oException)
public static bool GetIsForbiddenException(this System.Exception exception)
{
if (!(p_oException is WebException l_oException))
#if !USCSHARP9
if (!(exception is WebException webException))
#else
if (exception is not WebException webException)
#endif
{
return false;
}
if (!(l_oException?.Response is HttpWebResponse l_oResponse))
#if !USCSHARP9
if (!(webException?.Response is HttpWebResponse response))
#else
if (webException?.Response is not HttpWebResponse response)
#endif
{
return false;
}
return l_oException.Status == WebExceptionStatus.ProtocolError
&& l_oResponse.StatusCode == HttpStatusCode.Forbidden;
return webException.Status == WebExceptionStatus.ProtocolError
&& response.StatusCode == HttpStatusCode.Forbidden;
}
}
}

View file

@ -1,4 +1,4 @@
namespace TINK.Model.Repository.Exception
namespace TINK.Repository.Exception
{
public class WebForbiddenException : CommunicationException
{

View file

@ -1,10 +1,10 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Repository.Request;
using TINK.Model.Repository.Response;
using TINK.Model.Device;
using TINK.Repository.Request;
using TINK.Repository.Response;
namespace TINK.Model.Repository
namespace TINK.Repository
{
/// <summary> Interface to communicate with copri server.</summary>
public interface ICopriServerBase
@ -28,7 +28,7 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response on reserving request.</returns>
Task<ReservationBookingResponse> DoReserveAsync(
int bikeId,
string bikeId,
Uri operatorUri);
/// <summary> Cancels reservation of bik. </summary>
@ -36,7 +36,7 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response on cancel reservation request.</returns>
Task<ReservationCancelReturnResponse> DoCancelReservationAsync(
int bikeId,
string bikeId,
Uri operatorUri);
/// <summary> Get authentication keys. </summary>
@ -44,7 +44,7 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response holding authentication keys.</returns>
Task<ReservationBookingResponse> CalculateAuthKeysAsync(
int bikeId,
string bikeId,
Uri operatorUri);
/// <summary> Updates COPRI lock state for a booked bike. </summary>
@ -55,7 +55,7 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response on updating locking state.</returns>
Task<ReservationBookingResponse> UpdateLockingStateAsync(
int bikeId,
string bikeId,
LocationDto location,
lock_state state,
double batteryPercentage,
@ -68,7 +68,7 @@ namespace TINK.Model.Repository
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response on booking request.</returns>
Task<ReservationBookingResponse> DoBookAsync(
int bikeId,
string bikeId,
Guid guid,
double batteryPercentage,
Uri operatorUri);
@ -76,19 +76,23 @@ namespace TINK.Model.Repository
/// <summary> Returns a bike. </summary>
/// <param name="bikeId">Id of the bike to return.</param>
/// <param name="location">Geolocation of lock.</param>
/// <param name="smartDevice">Provides info about hard and software.</param>
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <returns>Response on returning request.</returns>
Task<ReservationCancelReturnResponse> DoReturn(
int bikeId,
string bikeId,
LocationDto location,
ISmartDevice smartDevice,
Uri operatorUri);
/// <summary>
/// Submits feedback to copri server.
/// </summary>
/// <param name="bikeId">Id of the bike to submit feedback for.</param>
/// <param name="isBikeBroken">True if bike is broken.</param>
/// <param name="message">General purpose message or error description.</param>
Task<SubmitFeedbackResponse> DoSubmitFeedback(
string bikeId,
string message,
bool isBikeBroken,
Uri operatorUri);

View file

@ -1,6 +1,7 @@
using System;
using TINK.Model.Device;
namespace TINK.Model.Repository.Request
namespace TINK.Repository.Request
{
/// <summary> Defines members to create requests.</summary>
public interface IRequestBuilder
@ -41,17 +42,17 @@ namespace TINK.Model.Repository.Request
/// <summary> Gets reservation request (synonym: reservation == request == reservieren). </summary>
/// <param name="bikeId">Id of the bike to reserve.</param>
/// <returns>Requst to reserve bike.</returns>
string DoReserve(int bikeId);
string DoReserve(string bikeId);
/// <summary> Gets request to cancel reservation. </summary>
/// <param name="bikeId">Id of the bike to cancel reservation for.</param>
/// <returns>Requst on cancel booking request.</returns>
string DoCancelReservation(int bikeId);
string DoCancelReservation(string bikeId);
/// <summary> Request to get keys. </summary>
/// <param name="bikeId">Id of the bike to get keys for.</param>
/// <returns>Request to get keys.</returns>
string CalculateAuthKeys(int bikeId);
string CalculateAuthParameters(string bikeId);
/// <summary> Gets the request for updating lock state for a booked bike. </summary>
/// <param name="bikeId">Id of the bike to update locking state for.</param>
@ -59,7 +60,7 @@ namespace TINK.Model.Repository.Request
/// <param name="state">New locking state.</param>
/// <returns>Request to update locking state.</returns>
string UpateLockingState(
int bikeId,
string bikeId,
LocationDto location,
lock_state state,
double batteryPercentage);
@ -69,20 +70,21 @@ namespace TINK.Model.Repository.Request
/// <param name="guid">Used to publish GUID from app to copri. Used for initial setup of bike in copri.</param>
/// <param name="batteryPercentage">Holds the filling level percentage of the battery.</param>
/// <returns>Request to booking bike.</returns>
string DoBook(int bikeId, Guid guid, double batteryPercentage);
string DoBook(string bikeId, Guid guid, double batteryPercentage);
/// <summary> Gets request for returning the bike. </summary>
/// <param name="bikeId">Id of the bike to return.</param>
/// <param name="location">Geolocation of lock when returning bike.</param>
/// <returns>Requst on returning request.</returns>
string DoReturn(int bikeId, LocationDto location);
string DoReturn(string bikeId, LocationDto location, ISmartDevice smartDevice);
/// <summary>
/// Gets request for submiting feedback to copri server.
/// </summary>
/// <param name="bikeId">Id of the bike to which the feedback is related to.</param>
/// <param name="message">General purpose message or error description.</param>
/// <param name="isBikeBroken">True if bike is broken.</param>
string DoSubmitFeedback(string message = null, bool isBikeBroken = false);
string DoSubmitFeedback(string bikeId, string message = null, bool isBikeBroken = false);
}
/// <summary> Copri locking states</summary>
@ -92,6 +94,7 @@ namespace TINK.Model.Repository.Request
unlocked
}
/// <summary> Holds lockation info.</summary>
public class LocationDto
{
public double Latitude { get; private set; }

View file

@ -1,8 +1,9 @@
using System;
using System.Net;
using TINK.Model.Repository.Exception;
using TINK.Model.Device;
using TINK.Repository.Exception;
namespace TINK.Model.Repository.Request
namespace TINK.Repository.Request
{
/// <summary> Creates requests if no user is logged in.</summary>
public class RequestBuilder : IRequestBuilder
@ -80,32 +81,34 @@ namespace TINK.Model.Repository.Request
public string GetBikesOccupied() => throw new NotSupportedException();
/// <summary> Gets booking request response. </summary>
/// <param name="p_iBikeId">Id of the bike to book.</param>
/// <param name="bikeId">Id of the bike to book.</param>
/// <returns>Response on booking request.</returns>
public string DoReserve(int p_iBikeId) => throw new NotSupportedException();
public string DoReserve(string bikeId) => throw new NotSupportedException();
/// <summary> Gets cancel booking request response. </summary>
/// <param name="p_iBikeId">Id of the bike to book.</param>
/// <returns>Response on cancel booking request.</returns>
public string DoCancelReservation(int p_iBikeId) => throw new NotSupportedException();
public string DoCancelReservation(string p_iBikeId) => throw new NotSupportedException();
/// <summary> Request to calculate authentication keys. </summary>
/// <param name="bikeId">Id of the bike to get keys for.</param>
/// <returns>Response on request.</returns>
public string CalculateAuthKeys(int bikeId) => throw new NotSupportedException();
public string CalculateAuthParameters(string bikeId) => throw new NotSupportedException();
public string UpateLockingState(int bikeId, LocationDto geolocation, lock_state state, double batteryPercentage)
public string UpateLockingState(string bikeId, LocationDto geolocation, lock_state state, double batteryPercentage)
=> throw new NotSupportedException();
public string DoBook(int bikeId, Guid guid, double batteryPercentage) => throw new NotSupportedException();
public string DoBook(string bikeId, Guid guid, double batteryPercentage) => throw new NotSupportedException();
public string DoReturn(int bikeId, LocationDto geolocation) => throw new NotSupportedException();
public string DoReturn(string bikeId, LocationDto geolocation, ISmartDevice smartDevice) => throw new NotSupportedException();
/// <summary> Gets submit feedback request. </summary>
/// <param name="bikeId">Id of the bike to which the feedback is related to.</param>
/// <param name="message">General purpose message or error description.</param>
/// <param name="isBikeBroken">True if bike is broken.</param>
/// <returns>Submit feedback request.</returns>
public string DoSubmitFeedback(
string bikeId,
string message = null,
bool isBikeBroken = false) => throw new NotSupportedException();
}

View file

@ -1,9 +1,10 @@
using System;
using System.Globalization;
using System.Net;
using TINK.Model.Repository.Exception;
using TINK.Model.Device;
using TINK.Repository.Exception;
namespace TINK.Model.Repository.Request
namespace TINK.Repository.Request
{
/// <summary> Creates requests if a user is logged in.</summary>
public class RequestBuilderLoggedIn : IRequestBuilder
@ -75,21 +76,21 @@ namespace TINK.Model.Repository.Request
/// <remarks> Operator specific call.</remarks>
/// <param name="bikeId">Id of the bike to reserve.</param>
/// <returns>Requst to reserve bike.</returns>
public string DoReserve(int bikeId)
public string DoReserve(string bikeId)
=> $"request=booking_request&bike={bikeId}&authcookie={SessionCookie}{MerchantId}";
/// <summary> Gets request to cancel reservation. </summary>
/// <remarks> Operator specific call.</remarks>
/// <param name="bikeId">Id of the bike to cancel reservation for.</param>
/// <returns>Requst on cancel booking request.</returns>
public string DoCancelReservation(int p_iBikeId)
public string DoCancelReservation(string p_iBikeId)
=> $"request=booking_cancel&bike={p_iBikeId}&authcookie={SessionCookie}{MerchantId}";
/// <summary> Request to get keys. </summary>
/// <remarks> Operator specific call.</remarks>
/// <param name="bikeId">Id of the bike to get keys for.</param>
/// <returns>Request to get keys.</returns>
public string CalculateAuthKeys(int bikeId)
public string CalculateAuthParameters(string bikeId)
=> $"request=booking_update&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&genkey=1";
/// <summary> Gets the request for updating lock state for a booked bike. </summary>
@ -97,9 +98,9 @@ namespace TINK.Model.Repository.Request
/// <param name="bikeId">Id of the bike to update locking state for.</param>
/// <param name="state">New locking state.</param>
/// <returns>Request to update locking state.</returns>
public string UpateLockingState(int bikeId, LocationDto geolocation, lock_state state, double batteryPercentage)
public string UpateLockingState(string bikeId, LocationDto geolocation, lock_state state, double batteryPercentage)
{
return $"request=booking_update&bike={bikeId}{GetLocationKey(geolocation)}&lock_state={state}{GetBatteryPercentageKey(batteryPercentage)}&authcookie={SessionCookie}{MerchantId}";
return $"request=booking_update&bike={bikeId}{GetLocationParameters(geolocation)}&lock_state={state}{GetBatteryPercentageParameters(batteryPercentage)}&authcookie={SessionCookie}{MerchantId}";
}
/// <summary> Gets booking request request (synonym: booking == renting == mieten). </summary>
@ -108,55 +109,65 @@ namespace TINK.Model.Repository.Request
/// <param name="guid">Used to publish GUID from app to copri. Used for initial setup of bike in copri.</param>
/// <param name="batteryPercentage">Holds the filling level percentage of the battery.</param>
/// <returns>Request to booking bike.</returns>
public string DoBook(int bikeId, Guid guid, double batteryPercentage)
=> $"request=booking_update&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&Ilockit_GUID={guid}&state=occupied&lock_state=unlocked{GetBatteryPercentageKey(batteryPercentage)}";
public string DoBook(string bikeId, Guid guid, double batteryPercentage)
=> $"request=booking_update&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&Ilockit_GUID={guid}&state=occupied&lock_state=unlocked{GetBatteryPercentageParameters(batteryPercentage)}";
/// <summary> Gets request for returning the bike. </summary>
/// <remarks> Operator specific call.</remarks>
/// <param name="bikeId">Id of bike to return.</param>
/// <param name="geolocation">Geolocation of lock when returning bike.</param>
/// <returns>Requst on returning request.</returns>
public string DoReturn(int bikeId, LocationDto geolocation)
public string DoReturn(string bikeId, LocationDto geolocation, ISmartDevice smartDevice)
{
return $"request=booking_update&bike={bikeId}&authcookie={SessionCookie}{MerchantId}&state=available{GetLocationKey(geolocation)}&lock_state=locked";
return $"request=booking_update" +
$"&bike={bikeId}" +
$"&authcookie={SessionCookie}{MerchantId}" +
$"&state=available" +
$"{GetLocationParameters(geolocation)}" +
$"&lock_state=locked" +
$"{GetSmartDeviceParameters(smartDevice)}";
}
/// <summary> Gets submit feedback request. </summary>
/// <param name="bikeId">Id of the bike to return.</param>
/// <param name="message">General purpose message or error description.</param>
/// <param name="isBikeBroken">True if bike is broken.</param>
/// <returns>Submit feedback request.</returns>
public string DoSubmitFeedback(
string bikeId,
string message = null,
bool isBikeBroken = false)
{
if (string.IsNullOrEmpty(message) && !isBikeBroken)
{
// User just acknoledged biked returned message.
return "request=user_feedback";
return $"request=user_feedback&bike={bikeId}&authcookie={SessionCookie}{MerchantId}";
}
if (isBikeBroken == false)
{
// Bike is ok and user entered a feedback message.
return $"request=user_feedback&message={WebUtility.UrlEncode(message)}";
return $"request=user_feedback&bike={bikeId}&message={WebUtility.UrlEncode(message)}&authcookie={SessionCookie}{MerchantId}";
}
if (string.IsNullOrEmpty(message))
{
// User just marked bike as broken without comment.
return $"request=user_feedback&bike_broken=1";
return $"request=user_feedback&bike={bikeId}&bike_broken=1&authcookie={SessionCookie}{MerchantId}";
}
// Bike is marked as broken and user added a comment.
return $"request=user_feedback&bike_broken=1&message={WebUtility.UrlEncode(message)}";
return $"request=user_feedback&bike={bikeId}&bike_broken=1&message={WebUtility.UrlEncode(message)}&authcookie={SessionCookie}{MerchantId}";
}
private string GetBatteryPercentageKey(double batteryPercentage) => !double.IsNaN(batteryPercentage)
private string GetBatteryPercentageParameters(double batteryPercentage) => !double.IsNaN(batteryPercentage)
? $"&voltage={batteryPercentage.ToString(CultureInfo.InvariantCulture)}"
: string.Empty;
private string GetLocationKey(LocationDto geolocation)
/// <summary> Gets the geolocation parameter. </summary>
/// <param name="geolocation">Geolocation or null.</param>
private string GetLocationParameters(LocationDto geolocation)
{
if (geolocation == null)
return string.Empty;
@ -166,5 +177,16 @@ namespace TINK.Model.Repository.Request
return $"&gps={geolocation.Latitude.ToString(CultureInfo.InvariantCulture)},{geolocation.Longitude.ToString(CultureInfo.InvariantCulture)}&gps_accuracy={geolocation.Accuracy.Value.ToString(CultureInfo.InvariantCulture)}&gps_age={geolocation.Age.TotalSeconds}";
}
/// <summary> Gets the geolocation parameter. </summary>
/// <param name="geolocation">Geolocation or null.</param>
private string GetSmartDeviceParameters(ISmartDevice smartDevice)
=> smartDevice != null
? $"{(!string.IsNullOrEmpty(smartDevice.Manufacturer) ? $"&user_device_manufaturer={smartDevice.Manufacturer})" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.Model) ? $"&user_device_model={smartDevice.Model}" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.PlatformText) ? $"&user_device_platform={smartDevice.PlatformText}" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.VersionText) ? $"&user_device_version={smartDevice.VersionText}" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.Identifier) ? $"&user_device_id={smartDevice.Identifier}" : string.Empty)}"
: string.Empty;
}
}

View file

@ -1,6 +1,6 @@
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
[DataContract]
public class AuthorizationResponse : ResponseBase
@ -10,6 +10,6 @@ namespace TINK.Model.Repository.Response
/// <summary> Holds the group of the bike (TINK, Konrad, ...).</summary>
[DataMember]
public string user_group { get; private set; }
public string[] user_group { get; private set; }
}
}

View file

@ -1,6 +1,6 @@
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
[DataContract]
public class AuthorizationoutResponse : ResponseBase

View file

@ -1,6 +1,6 @@
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
[DataContract]
public class BikeInfoAvailable : BikeInfoBase
@ -9,7 +9,7 @@ namespace TINK.Model.Repository.Response
/// Position of the bike.
/// </summary>
[DataMember]
public string gps { get; private set; }
public GpsInfo gps { get; private set; }
[DataMember]
/// <summary> Full advertisement name.</summary>

View file

@ -1,7 +1,6 @@
using System.Runtime.Serialization;
using TINK.Repository.Response;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
/// <summary>
/// Holds info about a single bike.
@ -13,13 +12,13 @@ namespace TINK.Model.Repository.Response
/// Id of the bike.
/// </summary>
[DataMember]
public int bike { get; private set; }
public string bike { get; private set; }
/// <summary>
/// Id of the station.
/// </summary>
[DataMember]
public int? station { get; private set; }
public string station { get; private set; }
/// <summary>
/// Holds the localized (german) description of the bike.
@ -32,7 +31,7 @@ namespace TINK.Model.Repository.Response
/// Copri returns values "TINK", "Konrad".
/// </remarks>
[DataMember]
public string bike_group { get; private set; }
public string[] bike_group { get; private set; }
/// <summary>
/// Rental state.

View file

@ -1,6 +1,6 @@
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
[DataContract]
public class BikeInfoReservedOrBooked : BikeInfoAvailable

View file

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
/// <summary>
/// Holds the information about all bikes and is used for deserialization of copri answer.
@ -13,6 +13,6 @@ namespace TINK.Model.Repository.Response
/// Dictionary of bikes.
/// </summary>
[DataMember]
public Dictionary<int, BikeInfoAvailable> bikes { get; private set; }
public Dictionary<string, BikeInfoAvailable> bikes { get; private set; }
}
}

View file

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
public class BikesReservedOccupiedResponse : ResponseBase
{
@ -9,6 +9,6 @@ namespace TINK.Model.Repository.Response
/// Dictionary of bikes.
/// </summary>
[DataMember]
public Dictionary<int, BikeInfoReservedOrBooked> bikes_occupied { get; private set; }
public Dictionary<string, BikeInfoReservedOrBooked> bikes_occupied { get; private set; }
}
}

View file

@ -0,0 +1,11 @@
using System.Runtime.Serialization;
namespace TINK.Repository.Response
{
[DataContract]
public class CopriVersion
{
[DataMember]
public string copri_version { get; private set; }
}
}

View file

@ -0,0 +1,23 @@
using System.Runtime.Serialization;
namespace TINK.Repository.Response
{
/// <summary>
/// Holds info about a single bike.
/// </summary>
[DataContract]
public class GpsInfo
{
/// <summary>
/// Latitude position of the bike.
/// </summary>
[DataMember]
public string latitude { get; private set; }
/// <summary>
/// Longitude position of the bike.
/// </summary>
[DataMember]
public string longitude { get; private set; }
}
}

View file

@ -2,7 +2,7 @@
namespace TINK.Repository.Response
{
public static class JsonConvert
public static class JsonConvertRethrow
{
/// <summary>
/// Deserializes COPRI responses in a consitent way for entire app.

View file

@ -1,6 +1,6 @@
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
/// <summary>
/// Holds the information about a booking request and is used for deserialization of copri answer.

View file

@ -1,5 +1,5 @@

namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
/// <summary>
/// Holds the information about a cancel booking request and is used for deserialization of copri answer.

View file

@ -1,9 +1,9 @@
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
[DataContract]
public class ResponseBase
public class ResponseBase : CopriVersion
{
[DataMember]
public string response_state { get; private set; }
@ -17,9 +17,6 @@ namespace TINK.Model.Repository.Response
[DataMember]
public string authcookie { get; private set; }
[DataMember]
public string copri_version { get; private set; }
/// <summary> Textual description of response. </summary>
public new string ToString()
{

View file

@ -1,12 +1,12 @@
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
[DataContract]
public class ResponseContainer<T>
{
[DataMember]
public T tinkjson { get; private set; }
public T shareejson { get; private set; }
/// <summary>
/// Serializes object to string.
@ -14,12 +14,12 @@ namespace TINK.Model.Repository.Response
/// <returns></returns>
public override string ToString()
{
if (tinkjson == null)
if (shareejson == null)
{
return "Response container does not hold no entry.";
}
return tinkjson.ToString();
return shareejson.ToString();
}
}
}

View file

@ -1,9 +1,8 @@
using System.Linq;
using TINK.Model.Repository.Exception;
using TINK.MultilingualResources;
using TINK.Repository.Exception;
using TINK.MultilingualResources;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
public static class ResponseHelper
{
@ -83,7 +82,7 @@ namespace TINK.Model.Repository.Response
/// <returns></returns>
public static BikeInfoReservedOrBooked GetIsReserveResponseOk(
this ReservationBookingResponse bookingResponse,
int bikeId)
string bikeId)
{
GetIsResponseOk(bookingResponse, string.Format(AppResources.ExceptionTextReservationBikeFailedGeneral, bikeId));
@ -111,7 +110,7 @@ namespace TINK.Model.Repository.Response
/// <returns></returns>
public static BikeInfoReservedOrBooked GetIsBookingResponseOk(
this ReservationBookingResponse bookingResponse,
int bikeId)
string bikeId)
{
GetIsResponseOk(bookingResponse, string.Format(AppResources.ExceptionTextRentingBikeFailedGeneral, bikeId));
@ -134,7 +133,7 @@ namespace TINK.Model.Repository.Response
/// <returns>Verified response.</returns>
public static T GetIsResponseOk<T>(this T response, string textOfAction) where T : ResponseBase
{
if (response == null)
if (response == null || response.response_state == null)
{
throw new InvalidResponseException<T>(textOfAction, null);
}
@ -161,7 +160,7 @@ namespace TINK.Model.Repository.Response
/// <returns>Verified response.</returns>
public static ReservationCancelReturnResponse GetIsCancelReservationResponseOk(
this ReservationCancelReturnResponse cancelBookingResponse,
int bikeId)
string bikeId)
{
GetIsResponseOk<ResponseBase>(cancelBookingResponse, BIKES_CANCELREQUEST_ACTIONTEXT);
@ -184,7 +183,7 @@ namespace TINK.Model.Repository.Response
/// <returns>Verified response.</returns>
public static ReservationCancelReturnResponse GetIsReturnBikeResponseOk(
this ReservationCancelReturnResponse returnBikeResponse,
int bikeId)
string bikeId)
{
// Check if bike is at station.
if (NotAtStationException.IsNotAtStation(returnBikeResponse.response_state.ToUpper(), out NotAtStationException notAtStationException))
@ -219,22 +218,8 @@ namespace TINK.Model.Repository.Response
/// <returns></returns>
public static BikesReservedOccupiedResponse GetBikesOccupiedNone(string p_strSesstionCookie = null)
{
var l_oJson = BIKES_OCCUPIED_REQUEST_NONE_FILE.Replace(@"""authcookie"": """"", @"""authcookie"": """ + (p_strSesstionCookie ?? string.Empty) + @"""");
return CopriCallsStatic.DeserializeBikesOccupiedResponse(l_oJson);
var l_oJson = CopriCallsMonkeyStore.BIKESOCCUPIED.Replace(@"""authcookie"": """"", @"""authcookie"": """ + (p_strSesstionCookie ?? string.Empty) + @"""");
return CopriCallsStatic.DeserializeResponse(@"{ ""shareejson"" : " + l_oJson + "}", (version) => new BikesReservedOccupiedResponse());
}
/// <summary>
/// Holds an empty bikes occupied response.
/// </summary>
private const string BIKES_OCCUPIED_REQUEST_NONE_FILE = @"
{
""tinkjson"": {
""response_state"": ""OK"",
""bikes_occupied"": { },
""authcookie"": """",
""response"": ""user_bikes_occupied"",
""apiserver"": ""https://tinkwwp.copri-bike.de""
}
}";
}
}

View file

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace TINK.Model.Repository.Response
namespace TINK.Repository.Response
{
/// <summary>
/// Holds the information about all stations and is used for deserialization of copri answer.
@ -19,10 +19,10 @@ namespace TINK.Model.Repository.Response
/// Unique id of the station.
/// </summary>
[DataMember]
public int station { get; private set; }
public string station { get; private set; }
[DataMember]
public string station_group { get; private set; }
public string[] station_group { get; private set; }
[DataMember]
public string description { get; private set; }
@ -31,13 +31,13 @@ namespace TINK.Model.Repository.Response
/// Position of the station.
/// </summary>
[DataMember]
public string gps { get; private set; }
public GpsInfo gps { get; private set; }
}
/// <summary>
/// Dictionary of bikes.
/// </summary>
[DataMember]
public Dictionary<int, StationInfo> stations { get; private set; }
public Dictionary<string, StationInfo> stations { get; private set; }
}
}

View file

@ -1,6 +1,4 @@
using TINK.Model.Repository.Response;
namespace TINK.Repository.Response
namespace TINK.Repository.Response
{
public class SubmitFeedbackResponse : ResponseBase
{

View file

@ -6,7 +6,11 @@ namespace TINK.Repository.Response
/// Holds tariff info for a single bike.
/// </summary>
[DataContract]
#if USCSHARP9
public record TariffDescription
#else
public class TariffDescription
#endif
{
/// <summary>
/// Name of the tariff.

View file

@ -0,0 +1,22 @@
using System.Runtime.Serialization;
namespace TINK.Repository.Response
{
[DataContract]
public class VersionindependentResponse
{
private CopriVersion _shareejson;
/// <summary> Root element for versions 4.0 and older. </summary>
[DataMember]
public CopriVersion tinkjson { get; private set; }
/// <summary> Root element from 4.1 and later. </summary>
[DataMember]
public CopriVersion shareejson
{
get => _shareejson ?? tinkjson;
private set { _shareejson = value; }
}
}
}

View file

@ -1,9 +1,9 @@
using Serilog;
using System;
using System.Threading.Tasks;
using TINK.Model.Repository;
using TINK.Model.Repository.Request;
using TINK.Model.Repository.Response;
using TINK.Model.Device;
using TINK.Repository;
using TINK.Repository.Request;
using TINK.Repository.Response;
namespace TINK.Model.Services.CopriApi
@ -123,9 +123,12 @@ namespace TINK.Model.Services.CopriApi
try
{
Log.ForContext<CopriProviderHttps>().Debug($"Querrying stations from copri.");
var stations = await HttpsServer.GetStationsAsync();
return new Result<StationsAllResponse>(
typeof(CopriCallsHttps),
(await HttpsServer.GetStationsAsync()).GetIsResponseOk("Abfrage der Stationen fehlsgeschlagen."));
stations.GetIsResponseOk("Abfrage der Stationen fehlsgeschlagen."));
}
catch (Exception exception)
{
@ -197,23 +200,23 @@ namespace TINK.Model.Services.CopriApi
return await HttpsServer.DoAuthoutAsync();
}
public async Task<ReservationBookingResponse> DoReserveAsync(int p_iBikeId, Uri operatorUri)
public async Task<ReservationBookingResponse> DoReserveAsync(string bikeId, Uri operatorUri)
{
return await HttpsServer.DoReserveAsync(p_iBikeId, operatorUri);
return await HttpsServer.DoReserveAsync(bikeId, operatorUri);
}
public async Task<ReservationCancelReturnResponse> DoCancelReservationAsync(int p_iBikeId, Uri operatorUri)
public async Task<ReservationCancelReturnResponse> DoCancelReservationAsync(string bikeId, Uri operatorUri)
{
return await HttpsServer.DoCancelReservationAsync(p_iBikeId, operatorUri);
return await HttpsServer.DoCancelReservationAsync(bikeId, operatorUri);
}
public async Task<ReservationBookingResponse> CalculateAuthKeysAsync(int bikeId, Uri operatorUri)
public async Task<ReservationBookingResponse> CalculateAuthKeysAsync(string bikeId, Uri operatorUri)
{
return await HttpsServer.CalculateAuthKeysAsync(bikeId, operatorUri);
}
public async Task<ReservationBookingResponse> UpdateLockingStateAsync(
int bikeId,
string bikeId,
LocationDto location,
lock_state state,
double batteryLevel,
@ -225,22 +228,27 @@ namespace TINK.Model.Services.CopriApi
/// <param name="guid">Used to publish GUID from app to copri. Used for initial setup of bike in copri.</param>
/// <param name="batteryPercentage">Holds the filling level percentage of the battery.</param>
/// <returns>Response on booking request.</returns>
public async Task<ReservationBookingResponse> DoBookAsync(int bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
public async Task<ReservationBookingResponse> DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
{
return await HttpsServer.DoBookAsync(bikeId, guid, batteryPercentage, operatorUri);
}
public async Task<ReservationCancelReturnResponse> DoReturn(int bikeId, LocationDto location, Uri operatorUri)
public async Task<ReservationCancelReturnResponse> DoReturn(
string bikeId,
LocationDto location,
ISmartDevice smartDevice,
Uri operatorUri)
{
return await HttpsServer.DoReturn(bikeId, location, operatorUri);
return await HttpsServer.DoReturn(bikeId, location, smartDevice, operatorUri);
}
/// <summary>
/// Submits feedback to copri server.
/// </summary>
/// <param name="bikeId">Id of the bike to submit feedback for.</param>
/// <param name="message">General purpose message or error description.</param>
/// <param name="isBikeBroken">True if bike is broken.</param>
public async Task<SubmitFeedbackResponse> DoSubmitFeedback(string message, bool isBikeBroken, Uri opertorUri) =>
await HttpsServer.DoSubmitFeedback(message, isBikeBroken, opertorUri);
public async Task<SubmitFeedbackResponse> DoSubmitFeedback(string bikeId, string message, bool isBikeBroken, Uri opertorUri) =>
await HttpsServer.DoSubmitFeedback(bikeId, message, isBikeBroken, opertorUri);
}
}

View file

@ -1,8 +1,8 @@
using System;
using System.Threading.Tasks;
using TINK.Model.Repository;
using TINK.Model.Repository.Request;
using TINK.Model.Repository.Response;
using TINK.Model.Device;
using TINK.Repository;
using TINK.Repository.Request;
using TINK.Repository.Response;
namespace TINK.Model.Services.CopriApi
@ -30,34 +30,38 @@ namespace TINK.Model.Services.CopriApi
/// <summary> Gets the merchant id.</summary>
public string MerchantId => monkeyStore.MerchantId;
public Task<ReservationBookingResponse> DoReserveAsync(int p_iBikeId, Uri operatorUri)
public Task<ReservationBookingResponse> DoReserveAsync(string bikeId, Uri operatorUri)
=> throw new NotSupportedException($"{nameof(DoReserveAsync)} is not cachable.");
public Task<ReservationCancelReturnResponse> DoCancelReservationAsync(int p_iBikeId, Uri operatorUri)
public Task<ReservationCancelReturnResponse> DoCancelReservationAsync(string bikeId, Uri operatorUri)
=> throw new NotSupportedException($"{nameof(DoCancelReservationAsync)} is not cachable.");
public Task<ReservationBookingResponse> CalculateAuthKeysAsync(int bikeId, Uri operatorUri)
public Task<ReservationBookingResponse> CalculateAuthKeysAsync(string bikeId, Uri operatorUri)
=> throw new NotSupportedException($"{nameof(CalculateAuthKeysAsync)} is not cachable.");
public async Task<ReservationBookingResponse> UpdateLockingStateAsync(
int bikeId,
string bikeId,
LocationDto geolocation,
lock_state state,
double batteryLevel,
Uri operatorUri)
=> await monkeyStore.UpdateLockingStateAsync(bikeId, geolocation, state, batteryLevel, operatorUri);
public async Task<ReservationBookingResponse> DoBookAsync(int bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
public async Task<ReservationBookingResponse> DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
{
return await monkeyStore.DoBookAsync(bikeId, guid, batteryPercentage, operatorUri);
}
public async Task<ReservationCancelReturnResponse> DoReturn(int bikeId, LocationDto geolocation, Uri operatorUri)
public async Task<ReservationCancelReturnResponse> DoReturn(
string bikeId,
LocationDto geolocation,
ISmartDevice smartDevice,
Uri operatorUri)
{
return await monkeyStore.DoReturn(bikeId, geolocation, operatorUri);
return await monkeyStore.DoReturn(bikeId, geolocation, smartDevice, operatorUri);
}
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string messge, bool bIsBikeBroke, Uri operatorUri) => throw new NotImplementedException();
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string bikeId, string messge, bool bIsBikeBroke, Uri operatorUri) => throw new NotImplementedException();
public async Task<AuthorizationResponse> DoAuthorizationAsync(string p_strMailAddress, string p_strPassword, string p_strDeviceId)
{

View file

@ -1,8 +1,7 @@

using System;
using System.Threading.Tasks;
using TINK.Model.Repository;
using TINK.Model.Repository.Response;
using TINK.Repository;
using TINK.Repository.Response;
namespace TINK.Model.Services.CopriApi
{

View file

@ -1,5 +1,5 @@
using TINK.Model.Repository;
using TINK.Model.Repository.Response;
using TINK.Repository;
using TINK.Repository.Response;
namespace TINK.Model.Services.CopriApi
{

View file

@ -32,13 +32,13 @@ namespace TINK.Services.CopriApi.ServerUris
/// <summary> Get the agb resource name name depending on host name. </summary>
/// <param name="hostName">Host name.</param>
/// <returns>AGB resource..</returns>
/// <returns>AGB resource.</returns>
public static string GetAGBResource(this string hostName)
=> $"{hostName.GetSiteFolderName()}/{(hostName.GetIsCopri()? "konrad-TINK-AGB" : "agb.html")}";
/// <summary> Get the agb resource name name depending on host name. </summary>
/// <summary> Get the privacy resource name name depending on host name. </summary>
/// <param name="hostName">Host name.</param>
/// <returns>AGB resource..</returns>
/// <returns>Privacy resource.</returns>
public static string GetPrivacyResource(this string hostName)
=> $"{hostName.GetSiteFolderName()}/{(hostName.GetIsCopri() ? "Datenschutz" : "privacy.html")}";
}

View file

@ -73,7 +73,11 @@ namespace TINK.Model.Services.CopriApi.ServerUris
public Uri ActiveUri { get; }
/// <summary> Gets the active uri. </summary>
public static Uri DevelopUri => new(TINK_DEVEL);
#if USCSHARP9
public static Uri DevelopUri => new (TINK_DEVEL);
#else
public static Uri DevelopUri => new Uri(TINK_DEVEL);
#endif
/// <summary> Gets the known uris. </summary>
[JsonProperty]

View file

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace TINK.Services
{
public interface IServicesContainer<T> : IEnumerable<T>
{
/// <summary> Get the active service.</summary>
T Active { get; }
/// <summary> Sets service as active service by name. </summary>
/// <param name="active">Name of the new service obecs.</param>
void SetActive(string active);
}
}

View file

@ -8,7 +8,7 @@ namespace TINK.Services
{
/// <summary> Container of service objects (locks , geolocation, ...) where one service is active. </summary>
/// <remarks> All service objects must be of different type. </remarks>
public class ServicesContainerMutable<T>: IEnumerable<T>, INotifyPropertyChanged
public class ServicesContainerMutable<T>: IEnumerable<T>, INotifyPropertyChanged, IServicesContainer<T>
{
private readonly Dictionary<string, T> serviceDict;

Some files were not shown because too many files have changed in this diff Show more