private async void InitModelTree(EkCarTypeEnum?carType, string modelId) { Assure.CheckFlowState(carType != null || modelId != null, $"Either {nameof(carType)} or {nameof(modelId)} should not be null."); // free UI thread await ThreadHelper.RunInBackgroundThreadAsync(() => { // build model tree var carModelTree = EkSettingsHelper.GetCarModelTree(); if (carModelTree == null || carModelTree.Length == 0) { return(Task.CompletedTask); } foreach (var ekCarGroup in carModelTree) { var groupCategory = new Category(ekCarGroup.CarType); _allGroups[groupCategory.Id] = groupCategory; foreach (var manufacturer in ekCarGroup.Manufacturers) { var manufacturerCategory = new Category(ekCarGroup.CarType, manufacturer) { ParentCategoryId = groupCategory.Id, }; _allManufacturers[manufacturerCategory.Id] = manufacturerCategory; foreach (var carModel in manufacturer.CarModels) { var modelCategory = new Category(carModel) { ParentCategoryId = manufacturerCategory.Id, }; _allModels[modelCategory.Id] = modelCategory; } } } Category selectedCategory = null; if (modelId != null) { selectedCategory = _allModels.GetValueOrDefault(modelId); } else if (carType != null) { selectedCategory = _allGroups.GetValueOrDefault(carType.ToString()); } if (selectedCategory == null) { selectedCategory = _allGroups.Values.First(); } SelectCategory(selectedCategory); return(Task.CompletedTask); }); }
private UserControl GetErrorPageView(string errorMessage) { var errorPageView = _applicationViewProvider.GetErrorPageView(errorMessage); Assure.CheckFlowState(errorPageView != null, $"'{nameof(IKioskApplicationViewProvider)}.{nameof(IKioskApplicationViewProvider.GetErrorPageView)}' returned null."); return(errorPageView); }
private UserControl GetInitializationView(ComponentInitializationLog initializationLog) { var initizalitionView = _applicationViewProvider.GetInitializationView(initializationLog); Assure.CheckFlowState(initizalitionView != null, $"'{nameof(IKioskApplicationViewProvider)}.{nameof(IKioskApplicationViewProvider.GetInitializationView)}' returned null."); return(initizalitionView); }
public async Task ShowModalAsync(ModalArgs modalArgs) { Assure.ArgumentNotNull(modalArgs, nameof(modalArgs)); CheckIfInitialized(); try { await ThreadHelper.RunInUiThreadAsync(() => { var modalContext = new ModalContext(); var modalContent = modalArgs.ModalContentProvider(modalContext); Assure.CheckFlowState(modalContent != null, $"'{nameof(ModalArgs)}.{nameof(ModalArgs.ModalContentProvider)}' has returned null."); var modalContentContainer = _modalViewProvider.GetModalContentContainer(modalContent, () => modalContext.OnCloseModalRequest(), modalArgs.ModalTitle, modalArgs.CenterTitleHorizontally, modalArgs.ShowCancelButton); Assure.CheckFlowState(modalContentContainer != null, $"'{nameof(IModalViewProvider)}.{nameof(IModalViewProvider.GetModalContentContainer)}' has returned null."); _modalLayer.Children.Add(modalContentContainer); modalContext.OnCloseModalRequest = () => { _modalLayer.Children.Remove(modalContentContainer); (modalContent as IDisposable)?.Dispose(); }; // check is closed on construction phase if (modalContext.IsEarlyClosingRequested) { modalContext.OnCloseModalRequest(); } }); } catch (Exception ex) { Log.Error(LogContextEnum.Application, $"'{nameof(ShowModalAsync)}' has failed.", ex); } }
private UserControl GetMainPageView() { var mainPageView = _applicationViewProvider.GetMainPageView(); Assure.CheckFlowState(mainPageView != null, $"'{nameof(IKioskApplicationViewProvider)}.{nameof(IKioskApplicationViewProvider.GetMainPageView)}' returned null."); return(mainPageView); }
public async Task WriteAsync(byte[] messageBytes, CancellationToken?externalCancellationToken) { EnsureOpened(); Assure.ArgumentNotNull(messageBytes, nameof(messageBytes)); Assure.CheckFlowState(messageBytes.Length > 0, $"Passed '{nameof(messageBytes)}' is empty."); await _deviceIoPort.WriteAsync(messageBytes, externalCancellationToken); }
private KioskApplicationViews GetApplicationViews() { var applicationViews = _applicationViewProvider.GetApplicationViews(); Assure.CheckFlowState(applicationViews != null, $"'{nameof(IKioskApplicationViewProvider)}.{nameof(IKioskApplicationViewProvider.GetApplicationViews)}' returned null."); Assure.CheckFlowState(applicationViews.ApplicationView != null, $"'{nameof(KioskApplicationViews)}.{nameof(KioskApplicationViews.ApplicationView)}' is null."); Assure.CheckFlowState(applicationViews.ModalLayer != null, $"'{nameof(KioskApplicationViews)}.{nameof(KioskApplicationViews.ModalLayer)}' is null."); return(applicationViews); }
private void CheckIfInitialized() { // design-mode support if (DesignMode.DesignModeEnabled && _kioskLanguages == null) { Initialize(new[] { "uk", "ru", "en" }); } Assure.CheckFlowState(_kioskLanguages != null, $"'{nameof(LanguageManager)}' is not initialized. Run '{nameof(Initialize)}' first."); }
/// <summary> /// Special method to register contract directly. /// Should be used only by KioskApplicationBase. /// </summary> internal void RegisterContractInternal <TComponentContract>(string componentRole, ComponentContractInfo componentContractInfo) { Assure.ArgumentNotNull(componentRole, nameof(componentRole)); Assure.ArgumentNotNull(componentContractInfo, nameof(componentContractInfo)); var contactType = typeof(TComponentContract); Assure.CheckFlowState(contactType.IsInterface, $"{contactType.FullName} is not interface."); _componentsByRole[componentRole] = componentContractInfo.Component; AddContractInfo(contactType, componentContractInfo); }
private async Task <SerialDevice> GetSerialDeviceAsync(string serialPortName) { Assure.CheckFlowState(!string.IsNullOrEmpty(serialPortName), $"Empty '{nameof(serialPortName)}'."); // ReSharper disable AssignNullToNotNullAttribute var aqsFilter = SerialDevice.GetDeviceSelector(serialPortName); // ReSharper restore AssignNullToNotNullAttribute var deviceInformation = (await DeviceInformation.FindAllAsync(aqsFilter)).FirstOrDefault(); if (deviceInformation == null) { throw new DeviceIoPortInitializationException($"Serial device on '{serialPortName}' port was not found."); } var portOpenningTry = 0; GetSerialDeviceTry: portOpenningTry++; var serialDevice = await SerialDevice.FromIdAsync(deviceInformation.Id); if (serialDevice != null) { return(serialDevice); } var deviceAccessStatus = DeviceAccessInformation.CreateFromId(deviceInformation.Id).CurrentStatus; switch (deviceAccessStatus) { case DeviceAccessStatus.DeniedByUser: throw new DeviceIoPortInitializationException($"Access to device on '{serialPortName}' port was blocked by the user."); case DeviceAccessStatus.DeniedBySystem: // This status is most likely caused by app permissions (did not declare the device in the app's package.appxmanifest) // This status does not cover the case where the device is already opened by another app. throw new DeviceIoPortInitializationException($"Access to device on '{serialPortName}' was blocked by the system."); default: // Sometimes serial port is not freed fast after previous Dispose. Retry. var message = $"Access to device on '{serialPortName}' was blocked with unknown error (try {portOpenningTry}). Possibly opened by another app."; if (portOpenningTry < MaxPortOpenningTries) { Log.Error(LogContextEnum.Device, message); await Task.Delay(PortOpenningTryDelay); goto GetSerialDeviceTry; } throw new DeviceIoPortInitializationException(message); } }
// todo: request configuration periodically? (considering that kiosk will not be restarted long period of time) private async Task <(KioskConfiguration Configuration, bool IsLoadedFromCache)> GetKioskConfigurationAsync(ComponentOperationContext context) { var tryNumber = 0; GetConfigurationTry: try { tryNumber++; var response = await ServerApiProxy.Current.GetKioskConfigurationAsync(new EmptyRequest()); Assure.CheckFlowState(response.KioskConfiguration != null, $"Received '{nameof(KioskConfiguration)}' is null."); var kioskConfiguration = response.KioskConfiguration; // cache configuration for case of Internet absence await _persistentCacheManager.SaveAsync(kioskConfiguration, context); return(kioskConfiguration, false); } catch (Exception ex) { if (tryNumber == 1) { context.Log.Error(LogContextEnum.Communication, $"Kiosk configuration request failed (try {tryNumber}).", ex); } if (tryNumber < GetInitialConfigurationMaxTries) { await Task.Delay(GetInitialConfigurationNextTryDelay); goto GetConfigurationTry; } // try to get cached configuration var cachedKioskConfiguration = await _persistentCacheManager.LoadAsync(context); if (cachedKioskConfiguration == null) { throw; } context.Log.Warning(LogContextEnum.Component, "Kiosk configuration was loaded from cache."); return(cachedKioskConfiguration, true); } }
/// <summary> /// Not thread-safe. /// </summary> public void AddStatePropertyListener <TProperty>(Func <TProperty, Task> propertyChangeHandler, bool runHandlerWithCurrentValue = true) { Assure.ArgumentNotNull(propertyChangeHandler, nameof(propertyChangeHandler)); // not thread safe since await-s are used below (thread safe behavior would make the implementation more complicated) Assure.CheckFlowState(_listenerKey == null, "Listener was already added."); var componentState = ComponentManager.Current.GetContractState(this); var componentStateType = componentState.GetType(); var propertyInfo = componentStateType.GetProperty(PropertyName); if (propertyInfo == null || !propertyInfo.CanRead || propertyInfo.PropertyType != typeof(TProperty)) { throw new ComponentConfigurationException($"'{componentStateType.FullName}' doesn't contain public readable property '{PropertyName}' of type '{typeof(TProperty).FullName}'."); } // TBD: probably it's faster to read the value via compiled expressions TProperty ValueReader() => (TProperty)propertyInfo.GetValue(componentState); async Task StateChangeHandler(string propertyName) { if (propertyName == PropertyName) { try { var value = ValueReader(); await propertyChangeHandler(value); } catch (Exception ex) { Log.Error(LogContextEnum.Component, $"Property change listener of link '{this}' failed.", ex); } } } _listenerKey = componentState.AddStateListener(StateChangeHandler); if (runHandlerWithCurrentValue) { ThreadHelper.RunInNewThreadAsync(() => StateChangeHandler(PropertyName)); } }
internal StatePersistenceManager(string stateRecordName, Func <TState, TState> statePostProcessor, string kioskAppDataFolderName) { Assure.ArgumentNotNull(stateRecordName, nameof(stateRecordName)); Assure.ArgumentNotNull(statePostProcessor, nameof(statePostProcessor)); Assure.ArgumentNotNull(kioskAppDataFolderName, nameof(kioskAppDataFolderName)); var recordNameBuilder = new StringBuilder(stateRecordName); foreach (var prohibitedRecordNameCharacter in ProhibitedRecordNameCharacters) { recordNameBuilder.Replace(prohibitedRecordNameCharacter, ""); } _stateRecordName = recordNameBuilder.ToString(); Assure.CheckFlowState(_stateRecordName.Length > 0, $"{nameof(stateRecordName)} '{stateRecordName}' is empty or consists only of prohibited symbols."); _statePostProcessor = statePostProcessor; _kioskAppDataFolderName = kioskAppDataFolderName; }
public async Task <TMessage[]> GetNextFromQueueAsync <TMessage>( string queueName, int messageCount, CancellationToken cancellationToken) where TMessage : class, new() { Assure.ArgumentNotNull(queueName, nameof(queueName)); Assure.CheckFlowState(messageCount > 0 && messageCount <= 32, $"{nameof(messageCount)} should belong to interval (0..32]."); var queueClient = CreateQueueClient(); var queue = queueClient.GetQueueReference(queueName); var queueMessages = await queue.GetMessagesAsync( messageCount, null, null, null, cancellationToken); var messageList = new List <TMessage>(); foreach (var queueMessage in queueMessages) { await queue.DeleteMessageAsync( queueMessage.Id, queueMessage.PopReceipt, null, null, cancellationToken); try { var message = JsonConvert.DeserializeObject <TMessage>(queueMessage.AsString); messageList.Add(message); } catch (Exception ex) { _logger.LogError(LoggingEvents.NotificationError, ex, $"Bad format of queue message '{queueMessage.Id}'."); } } return(messageList.ToArray()); }
private Task ShowInactivityConfirmationModal() { return(ModalManager.Current.ShowModalAsync(new ModalArgs( "InactivityConfirmationModal_Question", modalContext => { lock (_inactivityProcessingLock) { // check if tracking has been stopped in parallel if (_inactivityBehavior == null) { throw new OperationCanceledException($"Inactivity modal has been interrupted by '{nameof(StopTracking)}'."); } _currentInactivityModalModel = new InactivityConfirmationModalModel(modalContext, _inactivityBehavior.ModalDurationSecs, OnConfirmationModalCountdownRunOut); var modal = _inactivityViewProvider.GetInactivityConfirmationModalWithStartedCountdown(_currentInactivityModalModel); Assure.CheckFlowState(modal != null, $"'{nameof(IInactivityViewProvider)}.{nameof(IInactivityViewProvider.GetInactivityConfirmationModalWithStartedCountdown)}' has returned null."); return modal; } }, centerTitleHorizontally: true, showCancelButton: false))); }
private static string ReplaceSeparatorsWithSpaceAndSurroundWithSpace(string text) { Assure.CheckFlowState(!string.IsNullOrEmpty(text), $"{nameof(text)} is empty."); var resultBuilder = new StringBuilder(" "); // ReSharper disable PossibleNullReferenceException foreach (var textSymbol in text) // ReSharper restore PossibleNullReferenceException { if (char.IsLetterOrDigit(textSymbol)) { resultBuilder.Append(textSymbol); } else { resultBuilder.Append(" "); } } resultBuilder.Append(" "); return(resultBuilder.ToString()); }
private ComponentBase CreateComponentInstance(ComponentConfiguration componentConfiguration) { try { Assure.ArgumentNotNull(componentConfiguration, nameof(componentConfiguration)); Assure.ArgumentNotNull(componentConfiguration.ComponentName, nameof(componentConfiguration.ComponentName)); Assure.ArgumentNotNull(componentConfiguration.ComponentRole, nameof(componentConfiguration.ComponentRole)); Assure.CheckFlowState(_supportedComponentTypes != null, $"{nameof(ComponentManager)} is not initialized."); // ReSharper disable PossibleNullReferenceException var componentTypeNames = _supportedComponentTypes.Keys // ReSharper restore PossibleNullReferenceException .Where(x => x.EndsWith(componentConfiguration.ComponentName)) .ToArray(); if (componentTypeNames.Length > 1) { throw new ComponentConfigurationException($"More than 1 app component type matches to component name '{componentConfiguration.ComponentName}': {string.Join(", ", componentTypeNames)}."); } if (componentTypeNames.Length == 0) { throw new ComponentConfigurationException($"There is no app component type that matches to component name '{componentConfiguration.ComponentName}'."); } var componentType = _supportedComponentTypes[componentTypeNames[0]]; object componentInstance; try { componentInstance = Activator.CreateInstance(componentType); } catch (Exception ex) { throw new ComponentConfigurationException($"Instantiation of '{componentType.FullName}' failed with '{ex.Message}'."); } if (!(componentInstance is ComponentBase typedComponentInstance)) { throw new ComponentConfigurationException($"Component type '{componentType.FullName}' doesn't inherit {nameof(ComponentBase)}."); } var contractStateType = typeof(ComponentState); // get all properties including private and explicit interface implementations // it's done on component level since it's much simpler than to check an hierarchy of contact interfaces // TBD: move all checks to contract registration - check entire hierarchy of contact interfaces var componentProperties = componentType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (var componentProperty in componentProperties) { var isOperationOrState = false; // check if operation property if (componentProperty.PropertyType.IsConstructedGenericType && componentProperty.PropertyType.GetGenericTypeDefinition() == typeof(ComponentOperation <,>)) { isOperationOrState = true; var operationValue = componentProperty.GetValue(typedComponentInstance); if (operationValue == null) { throw new ComponentConfigurationException($"Component operation '{componentType.FullName}'.{componentProperty.Name} is null after instantiation."); } } // check if state property if (contractStateType.IsAssignableFrom(componentProperty.PropertyType)) { isOperationOrState = true; var contractStateValue = componentProperty.GetValue(typedComponentInstance); if (contractStateValue == null) { throw new ComponentConfigurationException($"Component contract state '{componentType.FullName}'.{componentProperty.Name} is null after instantiation."); } var statePropertyName = componentProperty.Name; if (statePropertyName != ContractStatePropertyName // for explicit contract implementations && !statePropertyName.EndsWith("." + ContractStatePropertyName)) { throw new ComponentConfigurationException($"Component contract state '{componentType.FullName}'.{componentProperty.Name} is not named '{ContractStatePropertyName}' (required by convention)."); } } if (isOperationOrState) { if (componentProperty.CanWrite) { throw new ComponentConfigurationException($"Component operation/state '{componentType.FullName}'.{componentProperty.Name} must be readonly."); } } } typedComponentInstance.ComponentRole = componentConfiguration.ComponentRole; typedComponentInstance.ComponentName = componentConfiguration.ComponentName; return(typedComponentInstance); } catch (Exception ex) { Log.Error(LogContextEnum.Configuration, $"Component '{componentConfiguration?.ComponentRole} ({componentConfiguration?.ComponentName})' instantiation failed.", ex); return(null); } }
protected override async Task <ComponentInitializeResponse> InitializeAsync(ComponentInitializeRequest request, ComponentOperationContext context) { State.KioskAppVersion = VersionHelper.AppVersionString; // app configuration var _appConfiguration = await AppDataStorage.Current.LoadRecordAsync <KioskAppConfiguration>( KioskFolderNames.AppData_Configuration, KioskFileNames.KioskAppConfigurationWithoutExtension, true, CancellationToken.None); Assure.CheckFlowState(_appConfiguration.ServerUri != null, $"'{nameof(KioskFileNames.KioskAppConfigurationWithoutExtension)}.{nameof(KioskAppConfiguration.ServerUri)}' is null."); State.ServerUri = _appConfiguration.ServerUri; // serial key string serialKey; try { var kioskPublicFolder = await StorageHelper.GetKioskRootFolderAsync(); var storageFile = await kioskPublicFolder.GetFileAsync(KioskFileNames.Key); serialKey = await FileIO.ReadTextAsync(storageFile, UnicodeEncoding.Utf8); } catch (FileNotFoundException) { Status.SetSelfStatus(ComponentStatusCodeEnum.Error, $"'{KioskFileNames.Key}' was not found."); return(ComponentInitializeResponse.GetError()); } // init server API proxy ServerApiProxy.Current.Initialize(State.ServerUri, serialKey); // get kiosk configuration var loadedConfiguration = await GetKioskConfigurationAsync(context); var kioskConfiguration = loadedConfiguration.Configuration; Assure.CheckFlowState(kioskConfiguration != null, "Loaded kiosk configuration is null."); // state properties State.KioskConfiguration = kioskConfiguration; // ReSharper disable PossibleNullReferenceException State.KioskId = kioskConfiguration.KioskId; // ReSharper restore PossibleNullReferenceException State.SupportPhone = kioskConfiguration.SupportPhone; State.KioskAddress = kioskConfiguration.KioskAddress; // init languages LanguageManager.Current.Initialize(kioskConfiguration.LanguageCodes); if (loadedConfiguration.IsLoadedFromCache) { Status.SetSelfStatus(ComponentStatusCodeEnum.Warning, "Loaded from cache."); } else { Status.SetSelfStatus(ComponentStatusCodeEnum.Ok, null); } return(ComponentInitializeResponse.GetSuccess()); }
private async Task StartKioskApplicationAsync() { try { // block updates during initialization await KioskAppNotReadyForUpdateSign.SetAsync(); State.ApplicationState = KioskApplicationStateEnum.Initializing; _applicationViewProvider = GetApplicationViewProvider(); Assure.CheckFlowState(_applicationViewProvider != null, $"'{nameof(GetApplicationViewProvider)}' returned null."); // set initialization view SetWindowView(GetInitializationView(ComponentManager.Current.InitializationLog)); #if !DEBUG await Task.Delay(ApplicationInitializationDelay); #endif // register itself as component ComponentManager.Current.RegisterContractInternal <IKioskApplicationContract>( "KioskApplication", new ComponentContractInfo(this, Status, State)); // register supported components var supportedComponentTypes = new List <Type>() { // default components typeof(KioskAppSettings), typeof(ServerSyncService), }; var supportedAppComponentTypes = GetSupportedAppComponents(); if (supportedAppComponentTypes != null) { supportedComponentTypes.AddRange(supportedAppComponentTypes); } ComponentManager.Current.Initialize(supportedComponentTypes); // init app settings component await ComponentManager.Current.RegisterAndInitializeAsync( new ComponentConfiguration() { ComponentRole = CoreComponentRoles.KioskAppSettings, ComponentName = nameof(KioskAppSettings), Settings = null, }); var kioskAppSettingsComponent = ComponentManager.Current.GetComponent <IKioskAppSettingsContract>(CoreComponentRoles.KioskAppSettings); if (!kioskAppSettingsComponent.Status.IsOperational()) { throw new KioskAppInitializationException($"{kioskAppSettingsComponent.FullName}: {kioskAppSettingsComponent.Status.Message}"); } // init core components await ComponentManager.Current.RegisterAndInitializeAsync( new ComponentConfiguration() { ComponentRole = CoreComponentRoles.ServerSyncService, ComponentName = nameof(ServerSyncService), Settings = null, }); // register app specific components await ComponentManager.Current.RegisterAndInitializeAsync(kioskAppSettingsComponent.State.KioskConfiguration.AppComponents); // component initialization is completed - prepare and run app UI // application view var applicationViews = GetApplicationViews(); SetWindowView(applicationViews.ApplicationView); // initialize common UI managers ModalManager.Current.Initialize(applicationViews.ModalLayer, GetModalViewProvider()); // initialize inactivity behavior InactivityManager.Current.Initialize(applicationViews.ApplicationView, GetInactivityViewProvider()); await GotoMainPageAsync(); await OnLaunchedAsync(); } catch (Exception ex) { Log.Error(LogContextEnum.Application, "Initialization.", ex); // allow updates if initialization fails await KioskAppNotReadyForUpdateSign.UnsetAsync(); State.ApplicationState = KioskApplicationStateEnum.Error; // todo: doesn't show ex.Message (should be shown in admin/service panel - KioskOperability 'Operability' component) SetWindowView(GetErrorPageView(ex.Message)); } }
private void CheckIfInitialized() { Assure.CheckFlowState(_inactivityViewProvider != null, $"'{nameof(InactivityManager)}' is not initialized. Run '{nameof(Initialize)}' first."); }