using System; using System.Collections.Generic; using Serilog; using TINK.Model.Bikes.BikeInfoNS.BC; using TINK.Model.Bikes.BikeInfoNS.BikeNS; using TINK.Model.MiniSurvey; using TINK.Model.State; using TINK.Repository.Response; using BikeExtension = TINK.Model.Bikes.BikeInfoNS.BikeNS.BikeExtension; namespace TINK.Model.Connector.Updater { /// /// Constructs bike info instances/ bike info derived instances. /// public static class BikeInfoFactory { /// Set default lock type to . public static LockModel DEFAULTLOCKMODEL = LockModel.Sigo; /// Creates a bike info object from copri response. /// Copri response for a disposable bike. /// Specifies the data source. public static BikeInfo Create( BikeInfoAvailable bikeInfo, DataSource dataSource) { if (bikeInfo == null) throw new ArgumentNullException(nameof(bikeInfo)); var lockModel = bikeInfo.GetLockModel(); if (lockModel.HasValue && lockModel.Value == LockModel.BordComputer) { // Manual lock bikes are no more supported. Log.Error( $"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. " + "Manual lock bikes are no more supported." + $"Bike number: {bikeInfo.bike}{(bikeInfo.station != null ? $"station number {bikeInfo.station}" : string.Empty)}." ); return null; } switch (bikeInfo.GetState()) { case InUseStateEnum.Disposable: case InUseStateEnum.FeedbackPending: break; default: Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Unexpected state {bikeInfo.GetState()} detected."); return 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."); return null; } var lockType = lockModel.HasValue ? BikeExtension.GetLockType(lockModel.Value) : BikeExtension.GetLockType(DEFAULTLOCKMODEL); // Map bikes without "system"- entry in response to back end- locks. try { switch (lockType) { case LockType.Backend: return new Bikes.BikeInfoNS.CopriLock.BikeInfo( new Bike( bikeInfo.bike, LockModel.Sigo, bikeInfo.GetWheelType(), bikeInfo.GetTypeOfBike(), bikeInfo.GetAaRideType(), bikeInfo.description), DriveFactory.Create(bikeInfo?.bike_type), dataSource, bikeInfo.station, new Bikes.BikeInfoNS.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(), bikeInfo.GetState() == InUseStateEnum.FeedbackPending, bikeInfo.GetOperatorUri(), #if !NOTARIFFDESCRIPTION bikeInfo.rental_description != null ? RentalDescriptionFactory.Create(bikeInfo.rental_description) : TariffDescriptionFactory.Create(bikeInfo.tariff_description), #else Create((TINK.Repository.Response.TariffDescription) null), #endif bikeInfo.GetIsDemo(), bikeInfo.GetGroup(), miniSurvey: bikeInfo.user_miniquery != null ? new MiniSurveyModel(new Dictionary { { "q1", new QuestionModel()} // Add a dummy query. Queries are not yet read from COPRI but compiled into the app. }) : new MiniSurveyModel(), co2Saving: bikeInfo.co2saving); case LockType.Bluethooth: return new Bikes.BikeInfoNS.BluetoothLock.BikeInfo( new Bike( bikeInfo.bike, LockModel.ILockIt, bikeInfo.GetWheelType(), bikeInfo.GetTypeOfBike(), bikeInfo.GetAaRideType(), bikeInfo.description), DriveFactory.Create(bikeInfo?.bike_type), dataSource, bikeInfo.GetBluetoothLockId(), bikeInfo.GetBluetoothLockGuid(), bikeInfo.station, bikeInfo.GetOperatorUri(), #if !NOTARIFFDESCRIPTION bikeInfo.rental_description != null ? RentalDescriptionFactory.Create(bikeInfo.rental_description) : TariffDescriptionFactory.Create(bikeInfo.tariff_description), #else Create((TINK.Repository.Response.TariffDescription)null), #endif bikeInfo.GetIsDemo(), bikeInfo.GetGroup()); default: throw new ArgumentException($"Unsupported lock type {lockType} detected."); } } catch (ArgumentException ex) { // Constructor reported invalid arguments (missing lock id, ....). Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Invalid response detected. Available bike with id {bikeInfo.bike} skipped. {ex.Message}"); return null; } } /// Creates a bike info object from copri response. /// Copri response. /// Mail address of user. /// Date and time provider function. /// Specified the source of the data. public static BikeInfo Create( BikeInfoReservedOrBooked bikeInfo, string mailAddress, Func dateTimeProvider, DataSource dataSource) { if (bikeInfo == null) throw new ArgumentNullException(nameof(bikeInfo)); var lockModel = bikeInfo.GetLockModel(); if (lockModel.HasValue && lockModel.Value == LockModel.BordComputer) { // Manual lock bikes are no more supported. Log.Error( $"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. " + "Manual lock bikes are no more supported." + $"Bike number: {bikeInfo.bike}{(bikeInfo.station != null ? $", station number {bikeInfo.station}" : string.Empty)}." ); return null; } var lockType = lockModel.HasValue ? BikeExtension.GetLockType(lockModel.Value) : BikeExtension.GetLockType(DEFAULTLOCKMODEL); // Map bikes without "system"- entry in response to backend- locks. // Check if bike is a bluetooth lock bike. int lockSerial = bikeInfo.GetBluetoothLockId(); Guid lockGuid = bikeInfo.GetBluetoothLockGuid(); switch (bikeInfo.GetState()) { case InUseStateEnum.Reserved: try { switch (lockType) { case LockType.Bluethooth: return new Bikes.BikeInfoNS.BluetoothLock.BikeInfo( new Bike( bikeInfo.bike, LockModel.ILockIt, bikeInfo.GetWheelType(), bikeInfo.GetTypeOfBike(), bikeInfo.GetAaRideType(), bikeInfo.description), DriveFactory.Create(bikeInfo?.bike_type), dataSource, lockSerial, lockGuid, bikeInfo.GetUserKey(), bikeInfo.GetAdminKey(), bikeInfo.GetSeed(), bikeInfo.GetFrom(), mailAddress, bikeInfo.station, bikeInfo.GetOperatorUri(), #if !NOTARIFFDESCRIPTION bikeInfo.rental_description != null ? RentalDescriptionFactory.Create(bikeInfo.rental_description) : TariffDescriptionFactory.Create(bikeInfo.tariff_description), #else Create((TINK.Repository.Response.TariffDescription)null), #endif dateTimeProvider, bikeInfo.GetIsDemo(), bikeInfo.GetGroup()); case LockType.Backend: return new Bikes.BikeInfoNS.CopriLock.BikeInfo( new Bike( bikeInfo.bike, LockModel.Sigo, bikeInfo.GetWheelType(), bikeInfo.GetTypeOfBike(), bikeInfo.GetAaRideType(), bikeInfo.description), DriveFactory.Create(bikeInfo?.bike_type), dataSource, bikeInfo.GetFrom(), mailAddress, bikeInfo.station, new Bikes.BikeInfoNS.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(), bikeInfo.GetOperatorUri(), #if !NOTARIFFDESCRIPTION bikeInfo.rental_description != null ? RentalDescriptionFactory.Create(bikeInfo.rental_description) : TariffDescriptionFactory.Create(bikeInfo.tariff_description), #else Create((TINK.Repository.Response.TariffDescription)null), #endif dateTimeProvider, bikeInfo.GetIsDemo(), bikeInfo.GetGroup()); default: throw new ArgumentException($"Unsupported lock type {lockType} detected."); } } catch (ArgumentException ex) { // Constructor reported invalid arguments (missing lock id, ....). Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoReservedOrBooked)} argument. Invalid response detected. Reserved bike with id {bikeInfo.bike} skipped. {ex.Message}"); return null; } case InUseStateEnum.Booked: try { switch (lockModel) { case LockModel.ILockIt: return new Bikes.BikeInfoNS.BluetoothLock.BikeInfo( new Bike( bikeInfo.bike, LockModel.ILockIt, bikeInfo.GetWheelType(), bikeInfo.GetTypeOfBike(), bikeInfo.GetAaRideType(), bikeInfo.description), DriveFactory.Create(bikeInfo?.bike_type), dataSource, lockSerial, bikeInfo.GetBluetoothLockGuid(), bikeInfo.GetUserKey(), bikeInfo.GetAdminKey(), bikeInfo.GetSeed(), bikeInfo.GetFrom(), mailAddress, bikeInfo.station, bikeInfo.GetOperatorUri(), #if !NOTARIFFDESCRIPTION bikeInfo.rental_description != null ? RentalDescriptionFactory.Create(bikeInfo.rental_description) : TariffDescriptionFactory.Create(bikeInfo.tariff_description), #else Create((TINK.Repository.Response.TariffDescription)null), #endif bikeInfo.GetIsDemo(), bikeInfo.GetGroup()); case LockModel.BordComputer: throw new NotSupportedException($"Bikes with lock model of type {lockModel} are no more supported."); default: return new Bikes.BikeInfoNS.CopriLock.BikeInfo( new Bike( bikeInfo.bike, LockModel.Sigo, bikeInfo.GetWheelType(), bikeInfo.GetTypeOfBike(), bikeInfo.GetAaRideType(), bikeInfo.description), DriveFactory.Create(bikeInfo?.bike_type), DataSource.Copri, bikeInfo.GetFrom(), mailAddress, bikeInfo.station, new Bikes.BikeInfoNS.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(), bikeInfo.GetOperatorUri(), #if !NOTARIFFDESCRIPTION bikeInfo.rental_description != null ? RentalDescriptionFactory.Create(bikeInfo.rental_description) : TariffDescriptionFactory.Create(bikeInfo.tariff_description), #else Create((TINK.Repository.Response.TariffDescription)null), #endif bikeInfo.GetIsDemo(), bikeInfo.GetGroup()); } } catch (ArgumentException ex) { // Contructor reported invalid arguemts (missing lock id, ....). Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoReservedOrBooked)} argument. Invalid response detected. Booked bike with id {bikeInfo.bike} skipped. {ex.Message}"); return null; } default: Log.Error($"Can not create new {nameof(BikeInfo)}-object from {nameof(BikeInfoAvailable)} argument. Unexpected state {bikeInfo.GetState()} detected."); return null; } } } }