\
\
\
\
Scheduler successfully prepared for loading [282xy5] | -| Info| 06/04/2020, 10:31:19.820| web.solutions| 080A0400 | Loading configuration started [282xy5]| +| Info | 06/04/2020, 10:33:11.964 | web.solutions | 080A0401 | Loading configuration successfully finished [282xy5] | +| ... | ... | ... | ... |... | +| ... | ... | ... | \
\
\
\
\
Scheduler successfully prepared for loading [282xy5] | +| Info| 06/04/2020, 10:31:19.820| web.solutions| 080A0400 | Loading configuration started [282xy5]| ->**Note:** ->The id of a save or load operation can also be found on and copied from the "Manage app data" page (Save or Load report) in order to search for corresponding messages in the Logbook. The diagnostic numbers may be used to find the beginning and end of the respective operation in the Logbook, e.g. "080A0400" denoting "Loading configuration started". +> **Note:** +> The id of a save or load operation can also be found on and copied from the "Manage app data" page (Save or Load report) in order to search for corresponding messages in the Logbook. The diagnostic numbers may be used to find the beginning and end of the respective operation in the Logbook, e.g. "080A0400" denoting "Loading configuration started". #### Robustness @@ -280,9 +304,9 @@ The coordinator (Solutions app) ensures the following properties: ### From copying to smart loading -Apps must update their appdata sub-directory contents during the load operation to reflect the data which has been loaded and is now active in the apps. +Apps must update their appdata subdirectory contents during the load operation to reflect the data which has been loaded and is now active in the apps. -A first basic implementation may be to just copy the directory contents from the "configuration to load" to the corresponding appdata sub-directory. The Solutions app provides a default implementation of this file copy functionality. To activate this functionality, use the copyOnLoad option in your app directory declarations (cf. section "Specify your app directories"). +A first basic implementation may be to just copy the directory contents from the "configuration to load" to the corresponding appdata subdirectory. The Solutions app provides a default implementation of this file copy functionality. To activate this functionality, use the copyOnLoad option in your app directory declarations (cf. section "Specify your app directories"). The following example shows how a set of copyOnLoad declarations would be applied for a given configuration: @@ -312,14 +336,12 @@ The following example shows how a set of copyOnLoad declarations would be applie f: false (inherited from e) g: true (inherited from a) - However, an app might improve the load functionality by evaluating the changes to be applied during load. The app may determine that state changes (e.g., stopping the PLC for loading) are actually not required depending on the kind of data to load. Some participants may even be able to skip a load operation completely if their part of the configuration has not changed. If you want to take control and be able to evaluate differences between the configuration files and the appdata files, -- declare your base directory or specific sub-directories you would like to handle yourself in the package manifest -- omit the copyOnLoad option (or set copyOnLoad to "false") - +- declare your base directory or specific subdirectories you would like to handle yourself in the package manifest +- omit the copyOnLoad option (or set copyOnLoad to *false*) >**Note:** For compatibility reasons with previous ctrlX CORE releases, copyOnLoad is executed before the "prepare" phase. If you do not use copyOnLoad, you should persist your app data in the "load" phase as specified; see the "Annotated load process" in the Appendix for details @@ -344,7 +366,6 @@ For compatibility reasons with previous ctrlX CORE releases, copyOnLoad is execu ![image](images/How-To-Persist-app-data-Fig06_Loading(FAILED).png) - ### Problem schema definition Failure responses to command requests must adhere to the following schema: @@ -471,7 +492,7 @@ Problem: Apps may need to store files in their appdata directories which are only needed at runtime. These files (or directories) have to be ignored on loading and when saving the appdata to an archived configuration. -An app can specify such files using the "appPrivateFiles" element in the configuration section of its manifest. The element value is an array of strings where each string represents a regular expression following the RE2 syntax (https://golang.org/s/re2syntax). The expressions describe paths and names of files to be considered as private and are checked case-insensitively. +An app can specify such files using the "appPrivateFiles" element in the configuration section of its manifest. The element value is an array of strings where each string represents a regular expression following the RE2 syntax (
+When I use the Angular DecimalPipe I get "123,456,789" as a result regardless of the language settings (DE or EN) in the ctrlX app.
+ +I tried to supply a locale to the DecimalPipe like this: + +
+It seems that at the moment there is no general guideline on how to handle locale aware formatting within our application. +### Expected behavior +When the user changes between languages in the ctrlX app, the formatting of numbers and dates is influcenced by that change.
+As a developer I can use the standard Angular pipes like DecimalPipe and the formatting of the output respects the current language selection.
+This must be managed globally. This is not a responsibility of a single feature module.
+As there are multiple locales per language (e.g. en-US, en-GB) , we must either decide on a fixed locale per language or we must provide the user with the ability to select the locale in addition to the language. +### Solution +After some research here is a possible solution. It involves the following steps. + ++ Register a provider that will a provide a value of the LOCALE_ID that is based on the currently selected language. ++ Register the localeData for the German language. + +Here are the changes necessary to make this work. Everything is located in the app.module file. + + import { APP_INITIALIZER, LOCALE_ID, NgModule} from '@angular/core'; + import { registerLocaleData } from "@angular/common"; + import localeDe from "@angular/common/locales/de"; + + @NgModule({ + declarations: [ + // existing code omitted + ], + imports: [ + // existing code omitted + ], + providers: [ + { + // existing code omitted + }, { + provide: LOCALE_ID, + deps: [TranslateService], + useFactory: (translateService: TranslateService) => translateService.currentLang + } + ], + bootstrap: [AppComponent] + }) + + export class AppModule { + constructor() { + registerLocaleData(localeDe); + } + } + + + + +## Gratulations - We're finished - Let's start coding! + +## Support +### Developer Community + +Please join the [Developer Community](https://developer.community.boschrexroth.com/) + +### SDK Forum + +Please visit the [SDK Forum](https://developer.community.boschrexroth.com/t5/ctrlX-AUTOMATION/ct-p/dcdev_community-bunit-dcae/) + +### Issues + +If you've found an error in these sample, please [file an issue](https://github.com/boschrexroth) + +## License + +MIT License + +Copyright (c) 2021 Bosch Rexroth AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/samples-angular/hello.multilanguage/angular.json b/samples-angular/hello.multilanguage/angular.json new file mode 100644 index 00000000..a88c9038 --- /dev/null +++ b/samples-angular/hello.multilanguage/angular.json @@ -0,0 +1,118 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "multilanguage": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/app", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [], + "baseHref": "/ctrlx-hello-multilanguage/" + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "512mb", + "maximumError": "512mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "512mb", + "maximumError": "512mb" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "multilanguage:build:production" + }, + "development": { + "browserTarget": "multilanguage:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "multilanguage:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [] + } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "src/**/*.ts", + "src/**/*.html" + ] + } + } + } + } + }, + "cli": { + "defaultCollection": "@angular-eslint/schematics" + } +} diff --git a/samples-angular/hello.multilanguage/build-snap-amd64.sh b/samples-angular/hello.multilanguage/build-snap-amd64.sh new file mode 100644 index 00000000..d090a57a --- /dev/null +++ b/samples-angular/hello.multilanguage/build-snap-amd64.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +snapcraft clean --destructive-mode +snapcraft --destructive-mode --target-arch=amd64 --enable-experimental-target-arch \ No newline at end of file diff --git a/samples-angular/hello.multilanguage/build-snap-arm64.sh b/samples-angular/hello.multilanguage/build-snap-arm64.sh new file mode 100644 index 00000000..b41b7fd2 --- /dev/null +++ b/samples-angular/hello.multilanguage/build-snap-arm64.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +snapcraft clean --destructive-mode +snapcraft --destructive-mode --target-arch=arm64 --enable-experimental-target-arch \ No newline at end of file diff --git a/samples-angular/hello.multilanguage/configs/package-assets/ctrlx-hello-multilanguage.package-manifest.json b/samples-angular/hello.multilanguage/configs/package-assets/ctrlx-hello-multilanguage.package-manifest.json new file mode 100644 index 00000000..3e6cb9c2 --- /dev/null +++ b/samples-angular/hello.multilanguage/configs/package-assets/ctrlx-hello-multilanguage.package-manifest.json @@ -0,0 +1,24 @@ +{ + "version": "1.0.0", + "id": "ctrlx-hello-multilanguage", + "menus": { + "sidebar": [ + { + "id": "ctrlx-hello-multilanguage", + "title": "Hello Multilanguage", + "icon": "bosch-ic-translate", + "link": "/ctrlx-hello-multilanguage", + "target": "_blank" + } + ] + }, + "services": { + "proxyMapping": [ + { + "binding": "unix://{$PACKAGE_WWW_SOCKET}", + "name": "ctrlx-hello-multilanguage", + "url": "/ctrlx-hello-multilanguage" + } + ] + } +} \ No newline at end of file diff --git a/samples-angular/hello.multilanguage/karma.conf.js b/samples-angular/hello.multilanguage/karma.conf.js new file mode 100644 index 00000000..57d5d8d6 --- /dev/null +++ b/samples-angular/hello.multilanguage/karma.conf.js @@ -0,0 +1,59 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageReporter: { + dir: require('path').join(__dirname, './coverage'), + reporters: [ + {type: 'html', subdir: '.'}, + {type: 'lcovonly', subdir: '.'} + ], + fixWebpackSourcePaths: true, + thresholds: { + statements: 50, + lines: 50, + branches: 10, + functions: 40 + } + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome', 'Chrome_NoSandbox', 'ChromeHeadless', 'ChromeHeadless_NoSandbox'], + singleRun: false, + restartOnFileChange: true, + browserDisconnectTolerance: 2, + browserDisconnectTimeout: 50000, + browserNoActivityTimeout: 20000, + customLaunchers: { + Chrome_NoSandbox: { + base: 'Chrome', + flags: ['--no-sandbox'] + }, + ChromeHeadless_NoSandbox: { + base: 'ChromeHeadless', + flags: ['--no-sandbox'] + }, + Chrome_with_debugging: { + base: 'Chrome', + flags: ['--remote-debugging-port=9222'], + debug: true + } + } + }); +}; diff --git a/samples-angular/hello.multilanguage/package.json b/samples-angular/hello.multilanguage/package.json new file mode 100644 index 00000000..ae31f45e --- /dev/null +++ b/samples-angular/hello.multilanguage/package.json @@ -0,0 +1,37 @@ +{ + "name": "multilanguage", + "version": "0.0.1", + "scripts": { + "ng": "ng", + "start": "ng serve -o", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/animations": "~13.3.11", + "@angular/cdk": "^13.3.9", + "@angular/common": "~13.3.11", + "@angular/compiler": "~13.3.11", + "@angular/core": "~13.3.11", + "@angular/forms": "~13.3.11", + "@angular/material": "^13.3.9", + "@angular/platform-browser": "~13.3.11", + "@angular/platform-browser-dynamic": "~13.3.11", + "@ngx-translate/core": "^14.0.0", + "@ngx-translate/http-loader": "^7.0.0", + "ngx-translate-multi-http-loader": "^7.0.5", + "snap": "^1.1.0", + "tslib": "^2.0.0", + "zone.js": "^0.12.0" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^13.3.6", + "@angular/cli": "~13.3.8", + "@angular/compiler-cli": "~13.3.9", + "@types/node": "^18.11.9", + "ngx-translate-extract": "^1.0.0", + "typescript": "~4.6.2" + } +} diff --git a/samples-angular/hello.multilanguage/snap/snapcraft.yaml b/samples-angular/hello.multilanguage/snap/snapcraft.yaml new file mode 100644 index 00000000..c80394c8 --- /dev/null +++ b/samples-angular/hello.multilanguage/snap/snapcraft.yaml @@ -0,0 +1,55 @@ +# See https://snapcraft.io/docs/getting-started +# See https://snapcraft.io/docs/snapcraft-yaml-reference +name: ctrlx-hello-multilanguage +version: '1.0.0' +base: core20 +title: Hello Multilanguage +summary: Multilanguage sample in Angular for ctrlX +description: | + This programm shows how to set differen languages in Third-party apps. +grade: stable # must be 'stable' to release into candidate/stable channels +confinement: strict # reduced file access to selected areas, reduced + +parts: + configs: + source: ./configs + plugin: dump + organize: + 'package-assets/*': package-assets/${SNAPCRAFT_PROJECT_NAME}/ + + www: + source: . + plugin: npm + npm-node-version: '14.15.0' + override-build: | + # install node + snapcraftctl build + + # setup npm + npm config set unsafe-perm true + + # install dependencies + npm install + + # run angular build script + npm run build + + # remove node + rm -rf ${SNAPCRAFT_PART_INSTALL} + organize: + '${SNAPCRAFT_PART_BUILD}/dist/app': package-www/${SNAPCRAFT_PROJECT_NAME}/ + +slots: + package-assets: + interface: content + content: package-assets + source: + read: + - $SNAP/package-assets/${SNAPCRAFT_PROJECT_NAME} + + package-www: + interface: content + content: package-www + source: + read: + - $SNAP/package-www/${SNAPCRAFT_PROJECT_NAME} diff --git a/samples-angular/hello.multilanguage/src/app/app.component.html b/samples-angular/hello.multilanguage/src/app/app.component.html new file mode 100644 index 00000000..445fe5a2 --- /dev/null +++ b/samples-angular/hello.multilanguage/src/app/app.component.html @@ -0,0 +1,3 @@ +
{{'doc.pageTitle' | translate}}
+{{'doc.title' | translate}}
++ +
{{'doc.table.id' | translate}} | +{{book.id}} | +{{'doc.table.author' | translate}} | +{{book.author}} | +{{'doc.table.title' | translate}} | +{{book.title | translate}} | +{{'doc.table.category' | translate}} | +{{book.category | translate}} | +{{'doc.table.price' | translate}} | +{{book.price | currency:'EUR':'symbol':'1.2-2' }} | +
---|
+ | + |