├── .gitignore
├── LICENSE
├── README.md
├── clients
└── binsa
│ ├── .babelrc
│ ├── .eslintrc
│ ├── .gitignore
│ ├── README.md
│ ├── conf
│ ├── build-nw.js
│ ├── fixes
│ │ └── argsToArray.js
│ ├── webpack-license-plugin.js
│ ├── webpack.dev.js
│ └── webpack.prod.js
│ ├── cypress.json
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── sandbox.html
│ ├── src
│ ├── app.vue
│ ├── background.js
│ ├── components
│ │ ├── collapsibleCard.vue
│ │ ├── dataConcent.vue
│ │ ├── login.vue
│ │ ├── messageForm.vue
│ │ ├── navbar.vue
│ │ ├── qrField.vue
│ │ ├── selectLang.vue
│ │ └── url.vue
│ ├── i18n
│ │ ├── de.json
│ │ ├── en.json
│ │ ├── es.json
│ │ └── index.js
│ ├── img
│ │ ├── icons
│ │ │ ├── android-chrome-144x144.png
│ │ │ ├── android-chrome-192x192.png
│ │ │ ├── android-chrome-256x256.png
│ │ │ ├── android-chrome-36x36.png
│ │ │ ├── android-chrome-384x384.png
│ │ │ ├── android-chrome-48x48.png
│ │ │ ├── android-chrome-512x512.png
│ │ │ ├── android-chrome-72x72.png
│ │ │ ├── android-chrome-96x96.png
│ │ │ ├── apple-touch-icon-114x114.png
│ │ │ ├── apple-touch-icon-120x120.png
│ │ │ ├── apple-touch-icon-144x144.png
│ │ │ ├── apple-touch-icon-152x152.png
│ │ │ ├── apple-touch-icon-167x167.png
│ │ │ ├── apple-touch-icon-180x180.png
│ │ │ ├── apple-touch-icon-57x57.png
│ │ │ ├── apple-touch-icon-60x60.png
│ │ │ ├── apple-touch-icon-72x72.png
│ │ │ ├── apple-touch-icon-76x76.png
│ │ │ ├── apple-touch-icon-precomposed.png
│ │ │ ├── apple-touch-icon.png
│ │ │ ├── favicon-16x16.png
│ │ │ ├── favicon-230x230.png
│ │ │ ├── favicon-32x32.png
│ │ │ ├── favicon-96x96.png
│ │ │ ├── favicon.ico
│ │ │ ├── firefox_app_128x128.png
│ │ │ ├── firefox_app_512x512.png
│ │ │ ├── firefox_app_60x60.png
│ │ │ ├── mstile-144x144.png
│ │ │ ├── mstile-150x150.png
│ │ │ ├── mstile-310x150.png
│ │ │ ├── mstile-310x310.png
│ │ │ ├── mstile-70x70.png
│ │ │ ├── unide.png
│ │ │ └── unide.svg
│ │ └── logo.svg
│ ├── index.js
│ ├── manifests
│ │ ├── browserconfig.xml
│ │ ├── manifest.json
│ │ ├── manifest.webapp
│ │ └── web-manifest.json
│ ├── pages
│ │ ├── about.vue
│ │ ├── ciHelp.vue
│ │ ├── configuration
│ │ │ ├── index.vue
│ │ │ ├── ppm.vue
│ │ │ ├── ppmp.vue
│ │ │ └── preferences.vue
│ │ ├── home.vue
│ │ ├── notFound.vue
│ │ ├── ppm
│ │ │ ├── index.vue
│ │ │ └── measurements.vue
│ │ └── ppmp
│ │ │ ├── index.vue
│ │ │ ├── measurements.vue
│ │ │ ├── messages.vue
│ │ │ ├── processes.vue
│ │ │ └── sendingDaemon.js
│ ├── router.js
│ ├── sandbox.js
│ ├── sensorBus.js
│ ├── store
│ │ ├── configuration
│ │ │ ├── index.js
│ │ │ └── ppmp.js
│ │ └── index.js
│ ├── styles
│ │ └── variables.scss
│ └── sw.js
│ └── tests
│ ├── integration
│ └── main_spec.js
│ └── support
│ ├── commands.js
│ └── index.js
├── pom.xml
├── ppmp
└── ppmp-schema
│ ├── .gitignore
│ ├── pom.xml
│ └── src
│ └── main
│ └── resources
│ └── org
│ └── eclipse
│ └── iot
│ └── unide
│ └── ppmp
│ ├── v2
│ ├── measurement_schema.json
│ ├── message_schema.json
│ └── process_schema.json
│ └── v3
│ ├── definitions.json
│ ├── measurement_schema.json
│ ├── message_schema.json
│ └── process_schema.json
├── servers
├── README.md
├── pom.xml
└── rest
│ ├── .gitignore
│ ├── README.adoc
│ ├── assets
│ ├── grafana
│ │ └── grafana_influxdb_dashboard.json
│ └── postman
│ │ ├── unide.postman_collection.json
│ │ └── unide.postman_environment.json
│ ├── manifest.yml
│ ├── pom.xml
│ └── src
│ ├── main
│ ├── docs
│ │ └── asciidoc
│ │ │ ├── diagramms
│ │ │ └── ppmp_message_proccessing.puml
│ │ │ ├── documentation.adoc
│ │ │ ├── images
│ │ │ └── logo.svg
│ │ │ └── styles.css
│ ├── java
│ │ └── org
│ │ │ └── eclipse
│ │ │ └── iot
│ │ │ └── unide
│ │ │ └── server
│ │ │ ├── DependencyProvider.java
│ │ │ ├── MainVerticle.java
│ │ │ ├── ppmp
│ │ │ ├── PpmpEvent.java
│ │ │ ├── PpmpEventCodec.java
│ │ │ ├── PpmpSpecDictionary.java
│ │ │ ├── PpmpType.java
│ │ │ └── PpmpValidator.java
│ │ │ ├── receiver
│ │ │ ├── PpmpEventReceiver.java
│ │ │ ├── Receiver.java
│ │ │ ├── ReceiverException.java
│ │ │ ├── ReceiverFactory.java
│ │ │ ├── ReceiverVerticle.java
│ │ │ ├── influxdb
│ │ │ │ ├── AbstractInfluxDbConsumer.java
│ │ │ │ ├── InfluxDbProperties.java
│ │ │ │ ├── InfluxDbReceiver.java
│ │ │ │ ├── MachineMessageConsumer.java
│ │ │ │ ├── MeasurementConsumer.java
│ │ │ │ └── ProcessConsumer.java
│ │ │ ├── sql
│ │ │ │ ├── AbstractSqlConsumer.java
│ │ │ │ ├── JdbcExecutorService.java
│ │ │ │ ├── MachineMessageConsumer.java
│ │ │ │ ├── MeasurementConsumer.java
│ │ │ │ ├── ProcessConsumer.java
│ │ │ │ └── SqlDbReceiver.java
│ │ │ └── util
│ │ │ │ └── PpmpHelper.java
│ │ │ └── web
│ │ │ ├── Addresses.java
│ │ │ └── RestVerticle.java
│ └── resources
│ │ ├── application_conf.json
│ │ ├── log4j.properties
│ │ └── org
│ │ └── eclipse
│ │ └── iot
│ │ └── unide
│ │ └── server
│ │ └── receiver
│ │ └── sql
│ │ └── migrations
│ │ ├── h2
│ │ └── V1__initial_setup.sql
│ │ └── postgres
│ │ └── V1__initial_setup.sql
│ └── test
│ ├── java
│ └── org
│ │ └── eclipse
│ │ └── iot
│ │ └── unide
│ │ └── server
│ │ ├── PpmpSimulator.java
│ │ ├── jdbc
│ │ ├── JdbcMockFactory.java
│ │ └── JdbcMockFactoryTest.java
│ │ └── web
│ │ ├── FileUtils.java
│ │ ├── RestEndpointInfluxTest.java
│ │ ├── RestEndpointPostgresTest.java
│ │ └── RestEndpointValidationTest.java
│ └── resources
│ ├── log4j.properties
│ └── server
│ └── messages
│ ├── machine_message_invalid.json
│ ├── machine_message_valid.json
│ ├── measurement_message_invalid.json
│ ├── measurement_message_valid.json
│ ├── process_message_invalid.json
│ └── process_message_valid.json
└── website
├── .babelrc
├── .eslintrc
├── .gitignore
├── README.md
├── assets
├── schema.js
├── schemas
│ ├── v2
│ │ ├── measurement_schema.json
│ │ ├── message_schema.json
│ │ └── process_schema.json
│ └── v3
│ │ ├── definitions.json
│ │ ├── measurement_schema.json
│ │ ├── message_schema.json
│ │ └── process_schema.json
├── styles.scss
└── variables.scss
├── blog
├── DZone-article-published.md
├── New-process-message-spec.md
├── New-website.md
├── News-coverage.md
├── Official-launch.md
├── Project-approved.md
├── Release-0.2.0.md
├── Template-for-PPMP-transformation.md
├── Transform-PPMP-with-camel.md
├── Unide-at-BCX.md
├── Unide-proposal-online.md
├── Validator-Online.md
├── Version-3-final.md
├── Version-3.md
├── grinding-machine-scenario.md
└── i40-testbed-started.md
├── components
├── blogArchives.vue
├── collapsibleCard.vue
├── cookieCheck.vue
├── eclipseFooter.vue
├── navbar.vue
├── recentPosts.vue
├── schemaDetail.vue
├── schemaLink.vue
└── schemaToc.vue
├── layouts
├── default.vue
└── sidebar.vue
├── modules
└── postsIdxPlugin.js
├── nuxt.config.js
├── package.json
├── pages
├── article.vue
├── blog.vue
├── faq.vue
├── index.vue
├── proposal.vue
├── specification.vue
└── specification
│ ├── index.vue
│ ├── v2
│ ├── machine-message.vue
│ ├── measurement-message.vue
│ └── process-message.vue
│ └── v3
│ ├── machine-message.vue
│ ├── measurement-message.vue
│ └── process-message.vue
├── plugins
└── prismjs.js
└── static
├── favicon.ico
├── images
├── analysis.jpg
├── blog
│ ├── Transform-PPMP-with-camel-1.svg
│ ├── Transform-PPMP-with-camel-1.uml
│ ├── Transform-PPMP-with-camel-2-eclipse.png
│ ├── Transform-PPMP-with-camel-2-logfile.png
│ ├── Transform-PPMP-with-camel-camel.png
│ ├── Transform-PPMP-with-camel-welding.jpg
│ ├── grinding-machine-ce4iot-dashboard.png
│ └── grinding-machine-grafana-dashboard.png
├── collaborators_v2.svg
├── collaborators_v2.uml
├── eclipse-426x100.png
├── languages.png
├── logo.png
├── machines.jpg
└── specification
│ ├── v2
│ ├── measurementPayload.svg
│ ├── messagePayload.svg
│ └── processPayload.svg
│ └── v3
│ ├── definitions.iuml
│ ├── legend.svg
│ ├── measurementPayload.svg
│ ├── measurementPayload.uml
│ ├── messagePayload.svg
│ ├── messagePayload.uml
│ ├── processPayload.svg
│ └── processPayload.uml
└── logo.svg
/.gitignore:
--------------------------------------------------------------------------------
1 | ### JetBrains template
2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
4 |
5 | # User-specific stuff:
6 | .idea/workspace.xml
7 | .idea/tasks.xml
8 | .idea/dictionaries
9 | .idea/vcs.xml
10 | .idea/jsLibraryMappings.xml
11 |
12 | # Sensitive or high-churn files:
13 | .idea/dataSources.ids
14 | .idea/dataSources.xml
15 | .idea/dataSources.local.xml
16 | .idea/sqlDataSources.xml
17 | .idea/dynamic.xml
18 | .idea/uiDesigner.xml
19 |
20 | # Gradle:
21 | .idea/gradle.xml
22 | .idea/libraries
23 |
24 | # Mongo Explorer plugin:
25 | .idea/mongoSettings.xml
26 |
27 | ## File-based project format:
28 | *.iws
29 |
30 | ## Plugin-specific files:
31 |
32 | # IntelliJ
33 | /out/
34 |
35 | # mpeltonen/sbt-idea plugin
36 | .idea_modules/
37 |
38 | # JIRA plugin
39 | atlassian-ide-plugin.xml
40 |
41 | # Crashlytics plugin (for Android Studio and IntelliJ)
42 | com_crashlytics_export_strings.xml
43 | crashlytics.properties
44 | crashlytics-build.properties
45 | fabric.properties
46 |
47 | # Created by .ignore support plugin (hsz.mobi)
48 |
49 | *.classpath
50 | *.project
51 | .settings/
52 | *.log
53 |
54 | .sts4-cache/
55 | .vscode/
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [Eclipse Unide](https://www.eclipse.org/unide) publishes the current version of Production Performance Management Protocol (PPMP) and develops simple server/client implementations. The implementations store the payloads in a database and displays them through a simple user interface. Everyone can then use these samples for their custom condition monitoring applications.
4 |
5 | # Getting started
6 |
7 | For installing the Unide server and binding implementations use Maven.
8 |
9 | ## Installation
10 |
11 | Run `mvn clean install`
12 |
--------------------------------------------------------------------------------
/clients/binsa/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [["env", {
3 | "targets": {
4 | "browsers": [
5 | "> 1%",
6 | "not ie < 11"
7 | ]
8 | }
9 | }]],
10 | "plugins": [
11 | ["transform-runtime"],
12 | "lodash",
13 | "syntax-dynamic-import"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/clients/binsa/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "parserOptions": {
4 | "ecmaVersion": 6
5 | },
6 | "plugins": [
7 | "standard", "promise", "html", "babel"
8 | ],
9 | "extends": ["standard", "vue"],
10 | "env": {
11 | "browser": true,
12 | "jquery": false
13 | },
14 | "globals": {
15 | "chrome": true,
16 | "browser": true,
17 | "nw": true
18 | },
19 | "rules": {
20 | "key-spacing": ["error", {
21 | "align": {
22 | "beforeColon": false,
23 | "afterColon": true,
24 | "on": "value"
25 | },
26 | "multiLine": {
27 | "beforeColon": false,
28 | "afterColon": true
29 | }
30 | }],
31 | "brace-style": ["warn", "1tbs", {
32 | "allowSingleLine": false
33 | }],
34 | "camelcase": ["error", {
35 | "properties": "always"
36 | }],
37 | "comma-style": ["warn", "last"],
38 | "consistent-return": ["off"],
39 | "default-case": ["error"],
40 | "indent": ["error", 2, {
41 | "SwitchCase": 1,
42 | "MemberExpression": 1,
43 | "VariableDeclarator": {
44 | "var": 2,
45 | "let": 2,
46 | "const": 3
47 | },
48 | "FunctionDeclaration": {
49 | "body": 1,
50 | "parameters": "first"
51 | },
52 | "FunctionExpression": {
53 | "body": 1,
54 | "parameters": "first"
55 | },
56 | "CallExpression": {
57 | "arguments": "first"
58 | },
59 | "ArrayExpression": "first",
60 | "ObjectExpression": 1
61 | }],
62 | "keyword-spacing": ["error", {
63 | "before": true,
64 | "after": true,
65 | "overrides": {
66 | "if": {
67 | "after": false
68 | },
69 | "for": {
70 | "after": false
71 | },
72 | "while": {
73 | "after": false
74 | },
75 | "catch": {
76 | "after": false
77 | }
78 | }
79 | }],
80 | "no-multi-spaces": "off",
81 | "no-shadow": ["warn"],
82 | "no-unused-vars": ["warn"],
83 | "no-unused-expressions": "off",
84 | "no-use-before-define": ["error", {
85 | "functions": true,
86 | "classes": true,
87 | "variables": true
88 | }],
89 | "one-var": ["error", "always"],
90 | "one-var-declaration-per-line": ["error", "always"],
91 | "quote-props": ["warn", "as-needed"],
92 | "semi": ["error", "always"],
93 | "space-before-function-paren": ["error", "never"],
94 | "vars-on-top": "error",
95 |
96 | "promise/param-names": "warn",
97 | "promise/always-return": "warn",
98 | "promise/catch-or-return": "warn"
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/clients/binsa/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist*
3 | npm-debug.log*
4 | *.vis
5 | tests/integration/eclipsecq_spec.js
6 | tests/videos
7 | tests/screenshots
8 | conf/cache
9 | jenkins.pipeline
10 |
11 | .vscode/settings.json
12 | .vscode/team-essentials/state.json
13 |
--------------------------------------------------------------------------------
/clients/binsa/conf/build-nw.js:
--------------------------------------------------------------------------------
1 | /*
2 | * example file on how to build executable from /dist folder
3 | * install nw-builder first, than
4 | * node conf\build-nw.js
5 | */
6 |
7 | const NwBuilder = require('nw-builder'),
8 | pkgJson = require('../package.json'),
9 | nw = new NwBuilder({
10 | files: [`${__dirname}/../dist/**`, `!../dist/${pkgJson.name}`],
11 | platforms: ['win64', 'linux64'], // ['win', 'win32', 'win64', 'osx', 'osx32', 'osx64', 'linux', 'linux32', 'linux64']
12 | version: '0.26.6',
13 | flavor: 'normal',
14 | appName: pkgJson.name,
15 | appVersion: pkgJson.version,
16 | buildDir: `${__dirname}/../dist`,
17 | cacheDir: `${__dirname}/cache`,
18 | winVersionString: {
19 | CompanyName: "Bosch Software Innovations",
20 | FileDescription: pkgJson.name,
21 | ProductName: pkgJson.name,
22 | LegalCopyright: "Copyright 2017"
23 | },
24 | winIco: `${__dirname}/../src/img/icons/favicon.ico`
25 | });
26 |
27 | nw.on('log', console.log);
28 |
29 | nw.build().then(function () {
30 | console.log('build');
31 | }).catch(function (error) {
32 | console.error(error);
33 | });
34 |
--------------------------------------------------------------------------------
/clients/binsa/conf/fixes/argsToArray.js:
--------------------------------------------------------------------------------
1 | // dropin es6 replacement for argsarray module
2 |
3 | export default fn => function(...args) {
4 | return fn.call(this, args);
5 | };
6 |
--------------------------------------------------------------------------------
/clients/binsa/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "port": 8081,
3 | "fixturesFolder": "tests/fixtures",
4 | "integrationFolder": "tests/integration",
5 | "screenshotsFolder": "tests/screenshots",
6 | "supportFile": "tests/support",
7 | "videosFolder": "tests/videos"
8 | }
9 |
--------------------------------------------------------------------------------
/clients/binsa/sandbox.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/clients/binsa/src/background.js:
--------------------------------------------------------------------------------
1 | if(chrome && chrome.app) {
2 | chrome.app.runtime.onLaunched.addListener(function() {
3 | chrome.app.window.create('index.html', {
4 | outerBounds: {
5 | width: 680,
6 | height: 720
7 | }
8 | });
9 | });
10 | } else if(browser) {
11 | browser.browserAction.onClicked.addListener(() => {
12 | browser.windows.create({
13 | url: 'index.html',
14 | width: 680,
15 | height: 820,
16 | type: 'panel'
17 | });
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/clients/binsa/src/components/collapsibleCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
33 |
90 |
91 |
116 |
117 |
--------------------------------------------------------------------------------
/clients/binsa/src/components/dataConcent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ $t(`dataConcent.title`) }}
4 | {{ $t(`dataConcent.titleAccepted`) }}
5 |
6 |
{{ $t(`dataConcent.information`) }}
7 |
{{ $t(`dataConcent.requirement`) }}
8 |
9 |
10 |
11 |
12 |
31 |
32 |
--------------------------------------------------------------------------------
/clients/binsa/src/components/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 | {{ $t('login.user.label') }}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
{{ errors.first('user') }}
22 |
23 |
24 |
25 | {{ $t('login.password.label') }}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
{{ errors.first('password') }}
34 |
35 |
36 |
37 |
38 |
39 |
40 | {{ $t('login.remember') }}
41 |
42 |
43 |
44 |
52 |
53 |
54 |
55 |
56 |
97 |
--------------------------------------------------------------------------------
/clients/binsa/src/components/messageForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
64 |
65 |
66 |
111 |
112 |
126 |
--------------------------------------------------------------------------------
/clients/binsa/src/components/selectLang.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
17 |
18 |
19 |
20 |
21 |
56 |
57 |
60 |
--------------------------------------------------------------------------------
/clients/binsa/src/components/url.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | http
7 | https
8 |
9 |
10 |
11 |
12 |
13 | ://
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | :
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
101 |
102 |
115 |
--------------------------------------------------------------------------------
/clients/binsa/src/i18n/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import store from '../store';
3 | import VueI18n from 'vue-i18n';
4 | import { Validator } from 'vee-validate';
5 | import get from 'lodash/get';
6 | import {
7 | mapState
8 | } from 'vuex';
9 |
10 | Vue.use(VueI18n);
11 |
12 | VueI18n.prototype._initVM = function(data) {
13 | const i18n = this;
14 | data.loading = null;
15 | this._vm = new Vue({
16 | data,
17 | store,
18 | watch: {
19 | 'configuration.preferences.lang': function() {
20 | const lang = this.configuration ? this.configuration.preferences.lang : null,
21 | me = this;
22 | if(!lang) {
23 | return;
24 | }
25 | if(process.env.LANGS.indexOf(lang) === -1) {
26 | throw Error("unknown language '" + lang + "'.");
27 | }
28 | if(!(this.messages[lang] && Validator.dictionary[lang])) {
29 | const p = Promise.all([
30 | import(/* webpackChunkName:"i18n-" */ './' + lang + '.json'),
31 | import(/* webpackChunkName:"vee-validate-i18n-" */ 'vee-validate/dist/locale/' + lang + '.js')
32 | ])
33 | .then(([custom, locale]) => {
34 | // if I'm the last update
35 | if(p === me.loading) {
36 | // i18n.setLocaleMessage(lang, custom);
37 | i18n._vm.$set(i18n._vm.messages, lang, custom);
38 | i18n.locale = lang;
39 | Validator.addLocale(locale.default || locale);
40 | Validator.setLocale(lang);
41 | // = me.$set(me, 'locale', lang);
42 | me.$set(me, 'loading', null);
43 | }
44 | return me.loading || p;
45 | })
46 | .catch(err => {
47 | console.error(err);
48 | });
49 | this.$set(this, 'loading', p);
50 | } else {
51 | i18n.locale = lang;
52 | Validator.setLocale(lang);
53 | this.$set(me, 'loading', null);
54 | }
55 | }
56 | },
57 | computed: mapState({
58 | configuration: state => state.configuration.configuration
59 | })
60 | });
61 | };
62 |
63 | export default new VueI18n({
64 | locale: get(store, 'state.configuration.preferences.lang', ((navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage).toLowerCase().split(/[_-]+/)[0]),
65 | fallbackLocale: 'en',
66 | messages: {}
67 | });
68 |
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/android-chrome-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/android-chrome-144x144.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/android-chrome-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/android-chrome-256x256.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/android-chrome-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/android-chrome-36x36.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/android-chrome-384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/android-chrome-384x384.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/android-chrome-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/android-chrome-48x48.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/android-chrome-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/android-chrome-72x72.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/android-chrome-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/android-chrome-96x96.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-114x114.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-144x144.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-167x167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-167x167.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-57x57.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-72x72.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/favicon-230x230.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/favicon-230x230.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/favicon-96x96.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/favicon.ico
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/firefox_app_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/firefox_app_128x128.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/firefox_app_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/firefox_app_512x512.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/firefox_app_60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/firefox_app_60x60.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/mstile-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/mstile-144x144.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/mstile-310x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/mstile-310x150.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/mstile-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/mstile-310x310.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/mstile-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/mstile-70x70.png
--------------------------------------------------------------------------------
/clients/binsa/src/img/icons/unide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/clients/binsa/src/img/icons/unide.png
--------------------------------------------------------------------------------
/clients/binsa/src/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import App from './app';
3 | import 'es6-promise/auto'; // needed for webpack dynamic module loading
4 | import Sw from 'offline-plugin/runtime'; // mapped to null-loader in development
5 |
6 | if(process.env.NODE_ENV !== 'development') {
7 | const idle = function() {};
8 | Sw.install({
9 | // the new sw should take control immediately
10 | onUpdateReady: () => {
11 | Sw.applyUpdate();
12 | },
13 | onUpdating: idle,
14 | // reload the page to make use of new sw version
15 | // window.location.reload();
16 | onUpdated: idle,
17 | onUpdateFailed: idle,
18 | onInstalled: idle,
19 | onError: idle
20 | });
21 | }
22 |
23 | new Vue(App).$mount('#main');
24 |
25 | // if started in nw.js, allow/fake 'multiple instances'
26 | if(window.nw) {
27 | nw.App.on('open', function(cmdline) {
28 | const win = nw.Window.get();
29 | nw.Window.open(location.href, {
30 | /* eslint camelcase: 0 */
31 | new_instance: true,
32 | width: win.width,
33 | height: win.height
34 | }, newWin => {
35 |
36 | });
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/clients/binsa/src/manifests/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | #2d89ef
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/clients/binsa/src/manifests/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= htmlWebpackPlugin.options.packageJson.title || 'Webpack App' %>",
3 | "short_name": "<%= htmlWebpackPlugin.options.packageJson.name || 'webpack-app' %>",
4 | "description": "<%= htmlWebpackPlugin.options.packageJson.description || 'A Webpack App' %>",
5 | "version": "<%= htmlWebpackPlugin.options.packageJson.version || '1.0.0' %>",
6 | "manifest_version": 2,
7 | "app": {
8 | "background": {
9 | "scripts": ["<%= process.env.BASEPATH %>background.js"]
10 | }
11 | },
12 | "icons": {
13 | "36": "<%= process.env.BASEPATH %>icons/android-chrome-36x36.png",
14 | "48": "<%= process.env.BASEPATH %>icons/android-chrome-48x48.png",
15 | "60": "<%= process.env.BASEPATH %>icons/firefox_app_60x60.png",
16 | "72": "<%= process.env.BASEPATH %>icons/android-chrome-72x72.png",
17 | "96": "<%= process.env.BASEPATH %>icons/android-chrome-96x96.png",
18 | "128": "<%= process.env.BASEPATH %>icons/firefox_app_128x128.png",
19 | "144": "<%= process.env.BASEPATH %>icons/android-chrome-144x144.png",
20 | "192": "<%= process.env.BASEPATH %>icons/android-chrome-192x192.png",
21 | "256": "<%= process.env.BASEPATH %>icons/android-chrome-256x256.png",
22 | "384": "<%= process.env.BASEPATH %>icons/android-chrome-384x384.png",
23 | "512": "<%= process.env.BASEPATH %>icons/android-chrome-512x512.png"
24 | },
25 | "sockets": {
26 | "tcp": {
27 | "connect": ""
28 | }
29 | },
30 | "permissions": ["system.cpu", "system.memory", "notifications", "storage", "geolocation", "*://*/*"],
31 | "sandbox": {
32 | "pages": ["sandbox.html"]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/clients/binsa/src/manifests/manifest.webapp:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= htmlWebpackPlugin.options.packageJson.name || 'webpack-app' %>",
3 | "description": "<%= htmlWebpackPlugin.options.packageJson.description || 'A Webpack App' %>",
4 | "version": "<%= htmlWebpackPlugin.options.packageJson.version || '1.0.0' %>",
5 | "icons": {
6 | "36": "<%= process.env.BASEPATH %>icons/android-chrome-36x36.png",
7 | "48": "<%= process.env.BASEPATH %>icons/android-chrome-48x48.png",
8 | "60": "<%= process.env.BASEPATH %>icons/firefox_app_60x60.png",
9 | "72": "<%= process.env.BASEPATH %>icons/android-chrome-72x72.png",
10 | "96": "<%= process.env.BASEPATH %>icons/android-chrome-96x96.png",
11 | "128": "<%= process.env.BASEPATH %>icons/firefox_app_128x128.png",
12 | "144": "<%= process.env.BASEPATH %>icons/android-chrome-144x144.png",
13 | "192": "<%= process.env.BASEPATH %>icons/android-chrome-192x192.png",
14 | "256": "<%= process.env.BASEPATH %>icons/android-chrome-256x256.png",
15 | "384": "<%= process.env.BASEPATH %>icons/android-chrome-384x384.png",
16 | "512": "<%= process.env.BASEPATH %>icons/firefox_app_512x512.png"
17 | },
18 | "developer": {
19 | "name": "<%= htmlWebpackPlugin.options.packageJson.author %>",
20 | "url": null
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/clients/binsa/src/manifests/web-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= htmlWebpackPlugin.options.packageJson.title || 'Webpack App' %>",
3 | "short_name": "<%= htmlWebpackPlugin.options.packageJson.name || 'webpack-app' %>",
4 | "description": "<%= htmlWebpackPlugin.options.packageJson.description || 'A Webpack App' %>",
5 | "version": "<%= htmlWebpackPlugin.options.packageJson.version || '1.0.0' %>",
6 | "manifest_version": 2,
7 | "dir": "auto",
8 | "lang": "en-US",
9 | "display": "fullscreen",
10 | "orientation": "any",
11 | "background_color": "#f9f9f9",
12 | "theme_color": "#3d5165",
13 | "start_url": "<%= process.env.BASEPATH %>",
14 | "icons": [
15 | {
16 | "src": "<%= process.env.BASEPATH %>icons/android-chrome-36x36.png",
17 | "sizes": "36x36",
18 | "type": "image/png"
19 | },
20 | {
21 | "src": "<%= process.env.BASEPATH %>icons/android-chrome-48x48.png",
22 | "sizes": "48x48",
23 | "type": "image/png"
24 | },
25 | {
26 | "src": "<%= process.env.BASEPATH %>icons/android-chrome-72x72.png",
27 | "sizes": "72x72",
28 | "type": "image/png"
29 | },
30 | {
31 | "src": "<%= process.env.BASEPATH %>icons/android-chrome-96x96.png",
32 | "sizes": "96x96",
33 | "type": "image/png"
34 | },
35 | {
36 | "src": "<%= process.env.BASEPATH %>icons/android-chrome-144x144.png",
37 | "sizes": "144x144",
38 | "type": "image/png"
39 | },
40 | {
41 | "src": "<%= process.env.BASEPATH %>icons/android-chrome-192x192.png",
42 | "sizes": "192x192",
43 | "type": "image/png"
44 | },
45 | {
46 | "src": "<%= process.env.BASEPATH %>icons/android-chrome-256x256.png",
47 | "sizes": "256x256",
48 | "type": "image/png"
49 | },
50 | {
51 | "src": "<%= process.env.BASEPATH %>icons/android-chrome-384x384.png",
52 | "sizes": "384x384",
53 | "type": "image/png"
54 | },
55 | {
56 | "src": "<%= process.env.BASEPATH %>icons/android-chrome-512x512.png",
57 | "sizes": "512x512",
58 | "type": "image/png"
59 | }
60 | ]
61 | }
62 |
--------------------------------------------------------------------------------
/clients/binsa/src/pages/ciHelp.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
16 |
17 |
44 |
--------------------------------------------------------------------------------
/clients/binsa/src/pages/configuration/preferences.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ $t('configuration.language') }}:
6 |
7 |
8 |
9 |
10 |
14 | {{ option }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
65 |
66 |
75 |
--------------------------------------------------------------------------------
/clients/binsa/src/pages/home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ $t('home.title') }}
8 |
9 |
10 | {{ $t('home.subtitle') }}
11 |
12 |
13 |
14 |
15 |
16 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/clients/binsa/src/pages/notFound.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ $t('notFound', {path: $route.path} ) }}
5 |
6 |
7 |
8 |
32 |
--------------------------------------------------------------------------------
/clients/binsa/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Router from 'vue-router';
3 | import Home from 'pages/home';
4 | /*
5 | import PPMP from 'pages/ppmp';
6 | import Configuration from 'pages/configuration';
7 | import About from 'pages/about';
8 | */
9 |
10 | Vue.use(Router);
11 |
12 | const routes = [{
13 | path: '/',
14 | component: Home
15 | }, {
16 | path: '/index.html',
17 | component: Home
18 | }, {
19 | path: '/ppmp/:configId?/:deviceId?',
20 | component: () => import(/* webpackChunkName:"ppmp" */ 'pages/ppmp'),
21 | children: [{
22 | path: '/ppmp/:configId/:deviceId/measurements',
23 | component: () => import(/* webpackChunkName:"ppmp" */ 'pages/ppmp/measurements'),
24 | props: true
25 | }, {
26 | path: '/ppmp/:configId/:deviceId/processes',
27 | component: () => import(/* webpackChunkName:"ppmp" */ 'pages/ppmp/processes'),
28 | props: true
29 | }, {
30 | path: '/ppmp/:configId/:deviceId/messages',
31 | component: () => import(/* webpackChunkName:"ppmp" */ 'pages/ppmp/messages'),
32 | props: true
33 | }, {
34 | path: '*',
35 | component: function() {
36 | return import(/* webpackChunkName:"notFound" */'./pages/notFound.vue');
37 | }
38 | }]
39 | }, {
40 | path: '/about',
41 | component: () => import(/* webpackChunkName:"about" */ 'pages/about')
42 | }, {
43 | path: '/configuration',
44 | component: () => import(/* webpackChunkName:"configuration" */ 'pages/configuration')
45 | }];
46 |
47 | if(process.env.NODE_ENV === 'development') {
48 | routes.push({
49 | path: '/ciHelp',
50 | component: () => import(/* webpackChunkName:"ciHelper" */ 'pages/ciHelp')
51 | });
52 | }
53 |
54 | routes.push({
55 | path: '*',
56 | component: function() {
57 | return import(/* webpackChunkName:"notFound" */'./pages/notFound.vue');
58 | }
59 | });
60 |
61 | export default new Router({
62 | // mode: 'hash',
63 | mode: 'history',
64 | base: process.env.BASEPATH || '/',
65 | routes
66 | });
67 |
--------------------------------------------------------------------------------
/clients/binsa/src/sandbox.js:
--------------------------------------------------------------------------------
1 | window.addEventListener('message', function(ev) {
2 | const id = ev.data.id;
3 | try {
4 | const testdata = event.data || {},
5 | tasks = [],
6 | render = function(tpl, data) {
7 | return Promise.resolve(data);
8 | };
9 | if(typeof testdata.data === 'string') {
10 | /* eslint no-eval: 0 */
11 | eval("'use scrict;'; testdata.data=" + testdata.data);
12 | }
13 | testdata.times = testdata.times || 1;
14 |
15 | for(let i = 0; i < testdata.times; i++) {
16 | tasks.push(render(testdata.tpl, testdata.data));
17 | }
18 | Promise.all(tasks)
19 | .then(function(result) {
20 | ev.source.postMessage({ id: id, result: result }, '*');
21 | return result;
22 | })
23 | .catch(function(err) {
24 | ev.source.postMessage({ id: id, error: err.toString() }, '*');
25 | });
26 | } catch(err) {
27 | ev.source.postMessage({ id: id, error: err.toString() }, '*');
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/clients/binsa/src/store/configuration/ppmp.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | export default (store, prefix = []) => {
4 | if(!(prefix instanceof Array)) {
5 | prefix = [prefix];
6 | }
7 |
8 | store.registerModule(prefix.concat('ppmp'), {
9 | namespaced: true,
10 | state: {
11 | registry: {},
12 | translationCounts: {}
13 | },
14 | mutations: {
15 | setTranslationCountsForConfigId(state, { id, counts }) {
16 | if(state.translationCounts[id]) {
17 | Vue.set(state.translationCounts, id, {});
18 | }
19 | Vue.set(state.translationCounts, id, counts);
20 | },
21 | register(state, { key, configId, deviceId, daemon }) {
22 | if(daemon) {
23 | if(!state.registry[key]) {
24 | Vue.set(state.registry, key, {});
25 | }
26 | if(!state.registry[key][configId]) {
27 | Vue.set(state.registry[key], configId, {});
28 | }
29 | Vue.set(state.registry[key][configId], deviceId, () => daemon);
30 | } else {
31 | if(state.registry[key] && state.registry[key][configId] && state.registry[key][configId][deviceId]) {
32 | Vue.delete(state.registry[key][configId], deviceId);
33 | if(!Object.keys(state.registry[key][configId] || {}).length) {
34 | Vue.delete(state.registry[key], configId);
35 | }
36 | }
37 | }
38 | }
39 | },
40 | getters: {
41 | getRegistry: (state) =>
42 | (key, configId, deviceId) => {
43 | let s = state.registry[key];
44 | s = s ? s[configId] : null;
45 | return s && s[deviceId] ? s[deviceId]() : null;
46 | },
47 | getTranslationsFor: (state, getters, rootState) =>
48 | (configId, deviceId) => {
49 | return (state.translationCounts[configId] || {})[deviceId] || {};
50 | },
51 | getTotalTranslationsFor: (state, getters) =>
52 | (configId, deviceId) =>
53 | Object.values(getters.getTranslationsFor(configId, deviceId)).reduce((l, v) => l + v, 0)
54 | }
55 | });
56 |
57 | // sync local translation count cache with every commit
58 | store.subscribe(mutation => {
59 | if(mutation.type === 'configuration/saveConfiguration') {
60 | let configs = mutation.payload;
61 | configs = (configs instanceof Array) ? configs : [configs];
62 | configs.filter(config => config.type === 'ppmp' || config.type === 'ppm')
63 | .forEach(config => {
64 | var counts = Object.entries(config.translate || {}).reduce((o, [deviceId, t]) => {
65 | o[deviceId] = Object.keys(t || {}).reduce((l, v) => {
66 | l[v] = Object.keys(t[v]).length;
67 | return l;
68 | }, {});
69 | return o;
70 | }, {});
71 | store.commit(prefix.concat('ppmp', 'setTranslationCountsForConfigId').join('/'), {
72 | id: config._id,
73 | counts
74 | });
75 | });
76 | }
77 | });
78 | };
79 |
--------------------------------------------------------------------------------
/clients/binsa/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 | import configuration from './configuration';
4 |
5 | Vue.use(Vuex);
6 |
7 | export default new Vuex.Store({
8 | strict: (process.env.NODE_ENV !== 'production'),
9 | plugins: [configuration],
10 | state: {},
11 | mutations: {},
12 | modules: {
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/clients/binsa/src/sw.js:
--------------------------------------------------------------------------------
1 | // console.log('own sw.js', self);
2 |
3 | const API_CACHE_NAME = 'ppm-api-cache';
4 |
5 | /*
6 | function useFallback() {
7 | return Promise.resolve(new Response('errortext', {
8 | headers: {
9 | 'Content-Type': 'image/svg+xml'
10 | }
11 | }));
12 | }
13 | */
14 |
15 | self.addEventListener('fetch', event => {
16 | // cache PPM REST API
17 | if(event.request.method === 'GET' && event.request.url.search(/INL_CY/) !== -1) {
18 | event.respondWith(caches.open(API_CACHE_NAME).then(function(cache) {
19 | return fetch(event.request)
20 | .then(function(response) {
21 | if(!response.ok) {
22 | return Promise.reject(response);
23 | }
24 | return cache.put(event.request, response.clone())
25 | .then(function() {
26 | return response;
27 | });
28 | })
29 | .catch(function(err) {
30 | return cache.match(event.request)
31 | .then(function(matching) {
32 | return matching || Promise.reject(err);
33 | });
34 | });
35 | }));
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/clients/binsa/tests/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This is will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/clients/binsa/tests/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 | 4.0.0
15 | org.eclipse.iot.unide.ppmp
16 | unide-parent
17 | 0.3.0-SNAPSHOT
18 | pom
19 |
20 | Unide
21 | Unide provides simple and easy to use server, client and binding implementations for using the I4.0 standard Production Performance Management Protocol (PPMP).
22 | https://www.eclipse.org/unide/
23 | 2016
24 |
25 |
26 |
27 | Eclipse Public License - Version 1.0
28 | http://www.eclipse.org/org/documents/epl-v10.php
29 |
30 |
31 |
32 |
33 | Eclipse Foundation
34 | http://www.eclipse.org/
35 |
36 |
37 |
38 | ppmp/ppmp-schema
39 | servers
40 |
41 |
42 |
43 |
44 |
45 | maven-compiler-plugin
46 | 3.5.1
47 |
48 | 1.8
49 | 1.8
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/ppmp/ppmp-schema/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/ppmp/ppmp-schema/pom.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 | 4.0.0
8 |
9 | org.eclipse.iot.unide.ppmp
10 | ppmp-schema
11 | 3.0.0-SNAPSHOT
12 |
13 |
14 |
15 | repo.eclipse.org
16 | Unide Repository - Releases
17 | https://repo.eclipse.org/content/repositories/unide-releases/
18 |
19 |
20 | repo.eclipse.org
21 | Unide Repository - Snapshots
22 | https://repo.eclipse.org/content/repositories/unide-snapshots/
23 |
24 |
25 |
26 |
27 |
28 |
29 | eclipse_jar_signing
30 |
31 | false
32 |
33 | enableEclipseJarSigner
34 |
35 |
36 |
37 |
38 | eclipse-repo
39 | Eclipse CBI Repository
40 |
41 | true
42 |
43 |
44 | false
45 |
46 | https://repo.eclipse.org/content/repositories/cbi-releases/
47 |
48 |
49 |
50 |
51 |
52 | org.eclipse.cbi.maven.plugins
53 | eclipse-jarsigner-plugin
54 | 1.1.4
55 |
56 | true
57 |
58 |
59 |
60 | sign-jars
61 | verify
62 |
63 | sign
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/ppmp/ppmp-schema/src/main/resources/org/eclipse/iot/unide/ppmp/v3/measurement_schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "content-spec": {
5 | "type": "string",
6 | "default": "urn:spec://eclipse.org/unide/machine-message#v3",
7 | "description": "Defines what the format version is"
8 | },
9 | "device": {
10 | "$ref": "definitions.json#/definitions/device"
11 | },
12 | "part": {
13 | "$ref": "definitions.json#/definitions/part"
14 | },
15 | "measurements": {
16 | "allOf": [
17 | {
18 | "$ref": "definitions.json#/definitions/measurements"
19 | },
20 | {
21 | "items": {
22 | "properties": {
23 | "series": {
24 | "required": ["time"]
25 | }
26 | }
27 | }
28 | }
29 | ]
30 | }
31 | },
32 | "required": [
33 | "content-spec",
34 | "device",
35 | "measurements"
36 | ]
37 | }
--------------------------------------------------------------------------------
/ppmp/ppmp-schema/src/main/resources/org/eclipse/iot/unide/ppmp/v3/message_schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "content-spec": {
5 | "type": "string",
6 | "default": "urn:spec://eclipse.org/unide/machine-message#v3",
7 | "description": "Defines what the format version is"
8 | },
9 | "device": {
10 | "$ref": "definitions.json#/definitions/device"
11 | },
12 | "messages": {
13 | "minItems": 1,
14 | "type": "array",
15 | "items": {
16 | "type": "object",
17 | "description": "Collection of messages",
18 | "properties": {
19 | "code": {
20 | "$ref": "definitions.json#/definitions/code"
21 | },
22 | "description": {
23 | "type": "string",
24 | "description": "The description is used to describe the purpose of the message, e.g. the problem",
25 | "maxLength": 2000
26 | },
27 | "hint": {
28 | "type": "string",
29 | "description": "In case a problem is reported, the hint can be used to point out a possible solution",
30 | "maxLength": 2000
31 | },
32 | "origin": {
33 | "type": "string",
34 | "description": "The origin of the message if not the device identified by deviceID in the header element. Could be used to identify a subsystem or a particular sensor/part of the device where the message actually relates to."
35 | },
36 | "severity": {
37 | "type": "string",
38 | "description": "Severity of the message. It complements type for describing urgency and relevance of a message",
39 | "enum": [
40 | "HIGH",
41 | "MEDIUM",
42 | "LOW",
43 | "UNKNOWN"
44 | ],
45 | "default": "UNKNOWN"
46 | },
47 | "source": {
48 | "type": "string",
49 | "description": "The type of message. Default is DEVICE but can be set to TECHNICAL_INFO indicating a problem with the integration of the actual device. Allowed values: DEVICE, TECHNICAL_INFO",
50 | "enum": [
51 | "DEVICE",
52 | "TECHNICAL_INFO"
53 | ],
54 | "default": "DEVICE"
55 | },
56 | "state": {
57 | "type": "string",
58 | "enum": [
59 | "NEW",
60 | "ENDED"
61 | ],
62 | "description": "Determines the lifecycle state of the message"
63 | },
64 | "title": {
65 | "type": "string",
66 | "description": "Title of the message. If title not set the code will be stored as fallback",
67 | "maxLength": 1000
68 | },
69 | "ts": {
70 | "format": "date-time",
71 | "type": "string",
72 | "description": "Start time of the the data measurment in ISO 8601 format"
73 | },
74 | "type": {
75 | "type": "string",
76 | "description": "Describes the debug level of the message. It complements severity for describing urgency and relevance of a message",
77 | "enum": [
78 | "INFO",
79 | "WARNING",
80 | "ERROR",
81 | "UNKNOWN"
82 | ],
83 | "default": "UNKNOWN"
84 | },
85 | "additionalData": {
86 | "type": "object"
87 | }
88 | },
89 | "required": [
90 | "ts",
91 | "code"
92 | ]
93 | }
94 | }
95 | },
96 | "required": [
97 | "content-spec",
98 | "device",
99 | "messages"
100 | ]
101 | }
--------------------------------------------------------------------------------
/servers/README.md:
--------------------------------------------------------------------------------
1 | Parent for PPMP Protocol compatible servers.
2 |
3 | See server README for purpose and setup instructions.
4 |
--------------------------------------------------------------------------------
/servers/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 | 4.0.0
13 | unide-servers
14 | pom
15 | unide-servers
16 | This projects provides several server implementations for different protocols.
17 |
18 | org.eclipse.iot.unide.ppmp
19 | unide-parent
20 | 0.3.0-SNAPSHOT
21 | ../
22 |
23 |
24 | rest
25 |
26 |
27 |
--------------------------------------------------------------------------------
/servers/rest/.gitignore:
--------------------------------------------------------------------------------
1 | .settings
2 | target/
3 | db
4 |
5 | *.classpath
6 | *.project
7 | *.log.*
8 | .vertx
9 | db
10 |
--------------------------------------------------------------------------------
/servers/rest/README.adoc:
--------------------------------------------------------------------------------
1 | include::./src/main/docs/asciidoc/documentation.adoc[]
2 |
--------------------------------------------------------------------------------
/servers/rest/assets/postman/unide.postman_environment.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "e50cdb37-0599-3b44-7626-199a7461c9b1",
3 | "name": "Unide DEV",
4 | "values": [
5 | {
6 | "enabled": true,
7 | "key": "url",
8 | "value": "http://localhost:8090",
9 | "type": "text"
10 | },
11 | {
12 | "enabled": true,
13 | "key": "influxurl",
14 | "value": "http://localhost:8086",
15 | "type": "text"
16 | }
17 | ],
18 | "timestamp": 1513678751733,
19 | "_postman_variable_scope": "environment",
20 | "_postman_exported_at": "2018-01-05T13:50:26.508Z",
21 | "_postman_exported_using": "Postman/5.5.0"
22 | }
--------------------------------------------------------------------------------
/servers/rest/manifest.yml:
--------------------------------------------------------------------------------
1 | ---
2 | applications:
3 | - name: unide-ppmp-server
4 | instances: 1
5 | host: unide-ppmp-server-${random-word}
6 | path: target/ppmp-server-0.3.0-SNAPSHOT.jar
7 | buildpack: https://github.com/cloudfoundry/java-buildpack.git
8 | memory: 1GB
--------------------------------------------------------------------------------
/servers/rest/src/main/docs/asciidoc/diagramms/ppmp_message_proccessing.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | skinparam classFontColor red
3 | skinparam classFontSize 10
4 | skinparam classFontName Aapex
5 |
6 | participant "Client" as Client
7 | participant "RestVerticle" as RestVerticle
8 | participant "PpmpValidator" as PpmpValidator
9 | participant "EventBus" as EventBus
10 | participant "ReceiverVerticle" as ReceiverVerticle
11 | participant "Receiver" as Receiver
12 | participant "Database" as Database
13 |
14 | Client -> RestVerticle : POST: /rest/v2/
15 | RestVerticle -> PpmpValidator: ppmp json
16 | note left: if vallidation error then throw Exception
17 |
18 | RestVerticle <- PpmpValidator : PpmpEvent
19 | RestVerticle -> EventBus :PpmpEvent
20 |
21 | EventBus -> ReceiverVerticle : PpmpEvent
22 | ReceiverVerticle -> Receiver : PpmpEvent
23 | Receiver -> Database : write/...
24 | Receiver <- Database : success / failed
25 | ReceiverVerticle <- Receiver : success / failed
26 | EventBus <- ReceiverVerticle : success / failed
27 | RestVerticle <- EventBus : success / failed
28 |
29 | Client <- RestVerticle : success / failed
30 |
31 | @enduml
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/DependencyProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server;
6 |
7 | import java.sql.Connection;
8 | import java.sql.SQLException;
9 | import java.util.function.Supplier;
10 |
11 | import javax.sql.DataSource;
12 | import org.apache.commons.dbcp2.BasicDataSource;
13 | import org.eclipse.iot.unide.server.receiver.Receiver;
14 | import org.eclipse.iot.unide.server.receiver.ReceiverFactory;
15 |
16 | import io.vertx.core.json.JsonObject;
17 |
18 | /**
19 | * Provides methods to create needed dependencies.
20 | */
21 | public class DependencyProvider {
22 |
23 | private JsonObject config;
24 |
25 | public DependencyProvider(JsonObject config) {
26 | this.config = config;
27 | }
28 |
29 | public Receiver getReceiver() {
30 | return createReceiver(config);
31 | }
32 |
33 | /**
34 | * Creates a {@link Receiver} based on the property "persistence.system".
35 | *
36 | * If the property is sql - a sql receiver will returned. Default receiver
37 | * is the influxDb receiver.
38 | *
39 | * @param config
40 | * - the config object
41 | * @return a receiver instance
42 | */
43 | private Receiver createReceiver(JsonObject config) {
44 | String persistence = config.getString("persistence.system", "influxdb");
45 | if (persistence.equals("sql")) {
46 | return ReceiverFactory.createSqlReceiver(getDataSource());
47 | }
48 | return ReceiverFactory.createInfluxBbReceiver(config);
49 | }
50 |
51 | /**
52 | * Returns a data source. The data source returns a Connection on every
53 | * {@link DataSource#getConnection()}
54 | *
55 | * @return DataSource which produces a connection when
56 | * {@link DataSource#getConnection()} is called.
57 | */
58 | public Supplier getConnection() {
59 | DataSource ds = getDataSource();
60 | return () -> {
61 | // https://docs.oracle.com/javase/tutorial/jdbc/basics/sqldatasources.html
62 | // DataSource is the preferred way to get connections over
63 | // DriverManager
64 | try {
65 | return ds.getConnection();
66 | } catch (SQLException e) {
67 | throw new RuntimeException("Failed to get connection", e);
68 | }
69 | };
70 | }
71 |
72 | public DataSource getDataSource() {
73 | String user = config.getString("sqlDb.user");
74 | String url = config.getString("sqlDb.url");
75 | String driver = config.getString("sqlDb.driver");
76 | String password = config.getString("sqlDb.password");
77 | BasicDataSource dataSource = new BasicDataSource();
78 | if (driver == null) {
79 | driver = "org.postgresql.Driver";
80 | }
81 | try {
82 | Class.forName(driver);
83 | } catch (ClassNotFoundException e) {
84 | throw new RuntimeException("No driver for database found", e);
85 | }
86 | dataSource.setDriverClassName(driver);
87 | dataSource.setUrl(url);
88 | dataSource.setUsername(user);
89 | dataSource.setPassword(password);
90 | dataSource.setDefaultAutoCommit(false);
91 | return dataSource;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/MainVerticle.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server;
6 |
7 | import org.eclipse.iot.unide.server.receiver.Receiver;
8 | import org.eclipse.iot.unide.server.receiver.ReceiverVerticle;
9 | import org.eclipse.iot.unide.server.web.RestVerticle;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import io.vertx.core.AbstractVerticle;
14 | import io.vertx.core.AsyncResult;
15 | import io.vertx.core.CompositeFuture;
16 | import io.vertx.core.DeploymentOptions;
17 | import io.vertx.core.Future;
18 | import io.vertx.core.Handler;
19 |
20 | public class MainVerticle extends AbstractVerticle {
21 |
22 | private static final Logger LOG = LoggerFactory.getLogger( MainVerticle.class );
23 |
24 | private DependencyProvider dependencyProvider;
25 |
26 | public MainVerticle() {
27 | }
28 |
29 | public MainVerticle( DependencyProvider dependencyProvider ) {
30 | this.dependencyProvider = dependencyProvider;
31 | }
32 |
33 | @Override
34 | public void start( Future startFuture ) throws Exception {
35 | if ( dependencyProvider == null ) {
36 | dependencyProvider = new DependencyProvider( config() );
37 | }
38 |
39 | boolean enablePersistence = config().getBoolean( "persistence.enable", true );
40 | if ( enablePersistence ) {
41 | CompositeFuture deployFuture = CompositeFuture.all( deployRestVerticle(), deployReceiverVerticle() );
42 | deployFuture.setHandler( deploymentHandler( startFuture ) );
43 | } else {
44 | Future future = deployRestVerticle();
45 | future.setHandler( deploymentHandler( startFuture ) );
46 | }
47 | }
48 |
49 | private Handler> deploymentHandler( Future startFuture ) {
50 | return handler -> {
51 | if ( handler.failed() ) {
52 | LOG.error( "Failed to start application.", handler.cause() );
53 | startFuture.fail( handler.cause() );
54 | }
55 | LOG.info( "Application started successful." );
56 | startFuture.complete();
57 | };
58 | }
59 |
60 | private DeploymentOptions getDeploymentOptions() {
61 | return new DeploymentOptions().setConfig( config() );
62 | }
63 |
64 | private Future deployRestVerticle() {
65 | Future future = Future.future();
66 | vertx.deployVerticle( new RestVerticle(), getDeploymentOptions(), future.completer() );
67 | return future;
68 | }
69 |
70 | private Future deployReceiverVerticle() {
71 | Future future = Future.future();
72 | Receiver receiver = dependencyProvider.getReceiver();
73 | vertx.deployVerticle( new ReceiverVerticle( receiver ),
74 | getDeploymentOptions().setWorker( true ), future.completer() );
75 | return future;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/ppmp/PpmpEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.ppmp;
6 |
7 | /**
8 | * Contains the payload of a PPMP message and the type of the message.
9 | *
10 | * An object of this class can be send over the vert.x eventbus when the codec with {@link PpmpEventCodec} is registered.
11 | */
12 | public class PpmpEvent {
13 | private final PpmpType ppmpType;
14 | private final String payload;
15 |
16 | public PpmpEvent( PpmpType ppmpType, String payload ) {
17 | this.ppmpType = ppmpType;
18 | this.payload = payload;
19 | }
20 |
21 | public PpmpType getPpmpType() {
22 | return ppmpType;
23 | }
24 |
25 | public String getPayload() {
26 | return payload;
27 | }
28 |
29 | @Override
30 | public String toString() {
31 | return "PpmpEvent{" +
32 | "ppmpType=" + ppmpType +
33 | ", payload='" + payload + '\'' +
34 | '}';
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/ppmp/PpmpEventCodec.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.ppmp;
6 |
7 | import io.vertx.core.buffer.Buffer;
8 | import io.vertx.core.eventbus.MessageCodec;
9 | import io.vertx.core.json.JsonObject;
10 |
11 | /**
12 | * Codec for {@code PpmpEvent}.
13 | *
14 | * This codec have to be registered on the vert.x eventBus to enable {@link PpmpEvent} as message types.
15 | */
16 | public class PpmpEventCodec implements MessageCodec {
17 |
18 | private static final String PPMP_TYPE = "ppmpType";
19 | private static final String PPMP_PAYLOAD = "ppmpPayload";
20 |
21 | @Override
22 | public void encodeToWire( Buffer buffer, PpmpEvent ppmpEvent ) {
23 | // Easiest ways is using JSON object
24 | JsonObject jsonToEncode = new JsonObject();
25 | jsonToEncode.put( PPMP_TYPE, ppmpEvent.getPpmpType().name() );
26 | jsonToEncode.put( PPMP_PAYLOAD, ppmpEvent.getPayload() );
27 |
28 | // Encode object to string
29 | String jsonToStr = jsonToEncode.encode();
30 |
31 | // Length of JSON: is NOT characters count
32 | int length = jsonToStr.getBytes().length;
33 |
34 | // Write data into given buffer
35 | buffer.appendInt( length );
36 | buffer.appendString( jsonToStr );
37 | }
38 |
39 | @Override
40 | public PpmpEvent decodeFromWire( int position, Buffer buffer ) {
41 | int pos = position;
42 | int length = buffer.getInt( pos );
43 | String jsonStr = buffer.getString( pos += 4, pos += length );
44 | JsonObject contentJson = new JsonObject( jsonStr );
45 | PpmpType ppmpType = PpmpType.valueOf( contentJson.getString( PPMP_TYPE ) );
46 | String ppmpPayload = contentJson.getString( PPMP_PAYLOAD );
47 | return new PpmpEvent( ppmpType, ppmpPayload );
48 | }
49 |
50 | @Override
51 | public PpmpEvent transform( PpmpEvent ppmpEvent ) {
52 | return ppmpEvent;
53 | }
54 |
55 | @Override
56 | public String name() {
57 | return this.getClass().getSimpleName();
58 | }
59 |
60 | @Override
61 | public byte systemCodecID() {
62 | return -1;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/ppmp/PpmpSpecDictionary.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.ppmp;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | import javax.xml.bind.ValidationException;
11 |
12 | /**
13 | * Ppmp Protocol types have to contain the type information of the message. The field content-spec contains this
14 | * information.
15 | *
16 | * This class contains mappings to map the content-spec attribute in a ppmp - mesasge to internal {@link PpmpType}.
17 | */
18 | class PpmpSpecDictionary {
19 |
20 | /**
21 | * Schemas are located in the ppmp-schema dependency.
22 | */
23 | private static final String ROOT_SCHEMA_LOCATION = "org/eclipse/iot/unide/ppmp/v2/";
24 | private static final String MESSAGES_SCHEMA_LOCATION = ROOT_SCHEMA_LOCATION + "message_schema.json";
25 | private static final String MEASUREMENTS_SCHEMA_LOCATION = ROOT_SCHEMA_LOCATION + "measurement_schema.json";
26 | private static final String PROCESSES_SCHEMA_LOCATION = ROOT_SCHEMA_LOCATION + "process_schema.json";
27 |
28 | private PpmpSpecDictionary() {
29 | }
30 |
31 | /**
32 | * Map for content-spec and the JSON-schema to use
33 | */
34 | private static Map CONTENT_SPEC_MAPPING;
35 |
36 | static {
37 | Map tmp = new HashMap();
38 | tmp.put( "urn:spec://eclipse.org/unide/machine-message#v2", PpmpType.MESSAGE );
39 | tmp.put( "urn:spec://bosch.com/ppm/machine-message#v2", PpmpType.MESSAGE );
40 | tmp.put( "urn:spec://eclipse.org/unide/measurement-message#v2", PpmpType.MEASUREMENT );
41 | tmp.put( "urn:spec://bosch.com/ppm/measurement-message#v2", PpmpType.MEASUREMENT );
42 | tmp.put( "urn:spec://eclipse.org/unide/process-message#v2", PpmpType.PROCESS );
43 | tmp.put( "urn:spec://bosch.com/ppm/process-message#v2", PpmpType.PROCESS );
44 | CONTENT_SPEC_MAPPING = tmp;
45 | }
46 |
47 | /**
48 | * Returns the resource name for the given type
49 | *
50 | * @param ppmpType - to get the resource for
51 | * @return the resource name
52 | */
53 | static String getResourceName( PpmpType ppmpType ) {
54 | switch ( ppmpType ) {
55 | case MESSAGE:
56 | return MESSAGES_SCHEMA_LOCATION;
57 | case MEASUREMENT:
58 | return MEASUREMENTS_SCHEMA_LOCATION;
59 | case PROCESS:
60 | return PROCESSES_SCHEMA_LOCATION;
61 | default:
62 | throw new IllegalArgumentException( "Unknown type '" + ppmpType + "'" );
63 | }
64 | }
65 |
66 | /**
67 | * @param contentSpec - the content-spec value
68 | * @return the corresponding type for given contentSpec
69 | */
70 | static PpmpType getPpmpType( String contentSpec ) throws ValidationException {
71 | PpmpType ppmpType = CONTENT_SPEC_MAPPING.get( contentSpec );
72 | if ( ppmpType == null ) {
73 | throw new ValidationException( "For contentSpec '" + contentSpec + "' no type mapping exists" );
74 | }
75 | return ppmpType;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/ppmp/PpmpType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.ppmp;
6 |
7 | /**
8 | * Represents PPMP - Protocol types
9 | */
10 | public enum PpmpType {
11 | /**
12 | * PPMP-Message type
13 | */
14 | MESSAGE,
15 | /**
16 | * PPMP-Measurement type
17 | */
18 | MEASUREMENT,
19 | /**
20 | * PPMP-Process type
21 | */
22 | PROCESS
23 | }
24 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/PpmpEventReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver;
6 |
7 | import java.util.function.Consumer;
8 |
9 | import org.eclipse.iot.unide.ppmp.PPMPPackager;
10 | import org.eclipse.iot.unide.ppmp.measurements.MeasurementsWrapper;
11 | import org.eclipse.iot.unide.ppmp.messages.MessagesWrapper;
12 | import org.eclipse.iot.unide.ppmp.process.ProcessWrapper;
13 | import org.eclipse.iot.unide.server.ppmp.PpmpEvent;
14 | import org.eclipse.iot.unide.server.ppmp.PpmpType;
15 |
16 | /**
17 | * Base class for {@link Receiver} implementations.
18 | * {@link PpmpEvent} payloads are converted and delegated to the corresponding consumers.
19 | * The consumer implementations have to be provided by the implementation classes.
20 | */
21 | public abstract class PpmpEventReceiver implements Receiver{
22 |
23 | private PPMPPackager ppmpMapper;
24 |
25 | public PpmpEventReceiver(PPMPPackager ppmpMapper){
26 | this.ppmpMapper = ppmpMapper;
27 | }
28 |
29 | @Override
30 | public void handle( PpmpEvent ppmpEvent ) throws ReceiverException {
31 | try {
32 | PpmpType ppmpType = ppmpEvent.getPpmpType();
33 | String ppmpPayload = ppmpEvent.getPayload();
34 | switch ( ppmpEvent.getPpmpType() ) {
35 | case MESSAGE:
36 | MessagesWrapper messagesWrapper = ppmpMapper.getMessagesBean( ppmpPayload );
37 | getMessagesConsumer().accept( messagesWrapper );
38 | break;
39 | case MEASUREMENT:
40 | MeasurementsWrapper measurementsWrapper = ppmpMapper.getMeasurementsBean( ppmpPayload );
41 | getMeasurementsConsumer().accept( measurementsWrapper );
42 | break;
43 | case PROCESS:
44 | ProcessWrapper processWrapper = ppmpMapper.getProcessesBean( ppmpPayload );
45 | getProcessesConsumer().accept( processWrapper );
46 | break;
47 | default:
48 | throw new IllegalArgumentException( "Unknown type '" + ppmpType + "'" );
49 | }
50 | } catch ( Exception e ) {
51 | throw new ReceiverException( "Failed to handle message", e );
52 | }
53 | }
54 |
55 | protected abstract Consumer getMessagesConsumer();
56 | protected abstract Consumer getMeasurementsConsumer();
57 | protected abstract Consumer getProcessesConsumer();
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/Receiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver;
6 |
7 | import org.eclipse.iot.unide.server.ppmp.PpmpEvent;
8 |
9 | /**
10 | * Handles {@link PpmpEvent}
11 | */
12 | public interface Receiver {
13 | /**
14 | * Called on application startup to do init tasks.
15 | *
16 | * @throws ReceiverException - When something went wrong during the init
17 | */
18 | void init() throws ReceiverException;
19 |
20 | /**
21 | * Handles the given event
22 | *
23 | * @param event - The event to handle
24 | * @throws ReceiverException - When something went wrong during the processing
25 | */
26 | void handle( PpmpEvent event ) throws ReceiverException;
27 | }
28 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/ReceiverException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver;
6 |
7 | /**
8 | * Custom exception for {@link Receiver}.
9 | */
10 | public class ReceiverException extends Exception {
11 | public ReceiverException( String message, Throwable cause ) {
12 | super( message, cause );
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/ReceiverFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver;
6 |
7 | import javax.sql.DataSource;
8 |
9 | import org.eclipse.iot.unide.server.receiver.influxdb.InfluxDbReceiver;
10 | import org.eclipse.iot.unide.server.receiver.sql.SqlDbReceiver;
11 |
12 | import io.vertx.core.json.JsonObject;
13 |
14 | /**
15 | * Factory class for creation of {@link Receiver}
16 | */
17 | public final class ReceiverFactory {
18 |
19 | /**
20 | * Creates an {@link Receiver} backed by InfluxDB as persistence.
21 | *
22 | * @param config the InfluxDB configs
23 | * @return a {@link Receiver} backed by InfluxDB as persistence
24 | */
25 | public static Receiver createInfluxBbReceiver( JsonObject config ) {
26 | return new InfluxDbReceiver( config );
27 | }
28 |
29 | /**
30 | * Creates an {@link Receiver} backed by SQL DB as persistence.
31 | *
32 | * @param dataSourceSupplier a supplier for retrieving the db parameter
33 | * @return a {@link Receiver} backed by SQL DB as persistence
34 | */
35 | public static Receiver createSqlReceiver( DataSource dataSource ) {
36 | return new SqlDbReceiver( dataSource );
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/ReceiverVerticle.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver;
6 |
7 | import org.apache.log4j.Logger;
8 | import org.eclipse.iot.unide.server.ppmp.PpmpEvent;
9 | import org.eclipse.iot.unide.server.web.Addresses;
10 | import org.eclipse.iot.unide.server.web.RestVerticle;
11 |
12 | import io.vertx.core.AbstractVerticle;
13 | import io.vertx.core.Future;
14 | import io.vertx.core.eventbus.Message;
15 |
16 | /**
17 | * Verticle which subscribes to {@link Addresses#SEND_PPMP_EVENT} and processes {@link PpmpEvent}
18 | */
19 | public class ReceiverVerticle extends AbstractVerticle {
20 | private static final Logger LOG = Logger.getLogger( RestVerticle.class );
21 |
22 | private final Receiver receiver;
23 |
24 |
25 | public ReceiverVerticle( Receiver receiver ) {
26 | this.receiver = receiver;
27 | }
28 |
29 | @Override
30 | public void start( Future startFuture ) throws Exception {
31 | try {
32 | receiver.init();
33 | registerEventPpmpMessageConsumer();
34 | startFuture.complete();
35 | } catch ( Exception e ) {
36 | startFuture.fail( e );
37 | throw new RuntimeException( "Failed to start " + ReceiverVerticle.class.getName(), e );
38 | }
39 | }
40 |
41 | private void registerEventPpmpMessageConsumer() {
42 | vertx.eventBus().consumer( Addresses.SEND_PPMP_EVENT, ( Message message ) -> {
43 | PpmpEvent ppmpEvent = message.body();
44 | try {
45 | receiver.handle( ppmpEvent );
46 | message.reply( "Received" );
47 | } catch ( ReceiverException e ) {
48 | LOG.error( "Failed to handle message with type '" + ppmpEvent.getPpmpType() + "'", e);
49 | message.fail( 500, e.getMessage() );
50 | }
51 | } );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/influxdb/AbstractInfluxDbConsumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.influxdb;
6 |
7 | import java.util.function.Consumer;
8 |
9 | import org.influxdb.InfluxDB;
10 |
11 | /**
12 | * Base class for influxDb {@link Consumer}
13 | *
14 | * @param - Type of Object to consume
15 | */
16 | abstract class AbstractInfluxDbConsumer implements Consumer {
17 | private InfluxDB influxDB;
18 | private String databaseName;
19 |
20 | /**
21 | * @param influxDB - the influxDb instance
22 | * @param databaseName - the name of the database to use for this {@link Consumer}
23 | */
24 | AbstractInfluxDbConsumer( InfluxDB influxDB, String databaseName ) {
25 | this.influxDB = influxDB;
26 | this.databaseName = databaseName;
27 | }
28 |
29 | /**
30 | * @return the database name
31 | */
32 | String getDatabaseName() {
33 | return databaseName;
34 | }
35 |
36 | /**
37 | * @return the influxDb instanz
38 | */
39 | InfluxDB getInfluxDb() {
40 | return influxDB;
41 | }
42 |
43 | /**
44 | * @param object - object to check
45 | * @return true when given object is not null
46 | */
47 | static boolean isNotNull( Object object ) {
48 | return null != object;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/influxdb/InfluxDbProperties.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.influxdb;
6 |
7 | import io.vertx.core.json.JsonObject;
8 |
9 | /**
10 | * Represents influxDb properties
11 | */
12 | class InfluxDbProperties {
13 | static final String MESSAGES_DB_NAME = "Messages";
14 | static final String MEASUREMENTS_DB_NAME = "Measurements";
15 | static final String PROCESSES_DB_NAME = "Processes";
16 |
17 | final String url;
18 | final String user;
19 | final String password;
20 |
21 | InfluxDbProperties( JsonObject configuration ) {
22 | this.url = configuration.getString( "influxDb.url" );
23 | this.user = configuration.getString( "influxDb.user" );
24 | this.password = configuration.getString( "influxDb.password" );
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/influxdb/InfluxDbReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.influxdb;
6 |
7 | import java.io.IOException;
8 | import java.util.function.Consumer;
9 |
10 | import org.apache.log4j.Logger;
11 | import org.eclipse.iot.unide.ppmp.PPMPPackager;
12 | import org.eclipse.iot.unide.ppmp.measurements.MeasurementsWrapper;
13 | import org.eclipse.iot.unide.ppmp.messages.MessagesWrapper;
14 | import org.eclipse.iot.unide.ppmp.process.ProcessWrapper;
15 | import org.eclipse.iot.unide.server.receiver.PpmpEventReceiver;
16 | import org.eclipse.iot.unide.server.receiver.ReceiverException;
17 | import org.influxdb.InfluxDB;
18 | import org.influxdb.InfluxDBFactory;
19 |
20 | import io.vertx.core.json.JsonObject;
21 | import okhttp3.Interceptor;
22 | import okhttp3.OkHttpClient;
23 | import okhttp3.Request;
24 | import okio.Buffer;
25 |
26 | /**
27 | * Manages connections to InfluxDB
28 | */
29 | public final class InfluxDbReceiver extends PpmpEventReceiver {
30 |
31 | private InfluxDB influxDb;
32 |
33 | public InfluxDbReceiver( JsonObject jsonObject ) {
34 | super( new PPMPPackager() );
35 | InfluxDbProperties influxDbProperties = new InfluxDbProperties( jsonObject );
36 | this.influxDb = InfluxDBFactory
37 | .connect( influxDbProperties.url, influxDbProperties.user, influxDbProperties.password,
38 | new OkHttpClientFactory().createClient() );
39 | }
40 |
41 | @Override
42 | public void init() throws ReceiverException {
43 | influxDb.createDatabase( InfluxDbProperties.MESSAGES_DB_NAME );
44 | influxDb.createDatabase( InfluxDbProperties.MEASUREMENTS_DB_NAME );
45 | influxDb.createDatabase( InfluxDbProperties.PROCESSES_DB_NAME );
46 | }
47 |
48 | @Override
49 | protected Consumer getMessagesConsumer() {
50 | return new MachineMessageConsumer( influxDb, InfluxDbProperties.MESSAGES_DB_NAME );
51 | }
52 |
53 | @Override
54 | protected Consumer getMeasurementsConsumer() {
55 | return new MeasurementConsumer( influxDb, InfluxDbProperties.MEASUREMENTS_DB_NAME );
56 | }
57 |
58 | @Override
59 | protected Consumer getProcessesConsumer() {
60 | return new ProcessConsumer( influxDb, InfluxDbProperties.PROCESSES_DB_NAME );
61 | }
62 |
63 | private static class OkHttpClientFactory {
64 | private static final Logger LOG = Logger.getLogger( InfluxDbReceiver.class );
65 |
66 | OkHttpClient.Builder createClient() {
67 | return new OkHttpClient.Builder().addInterceptor( getRequestLoggingInterceptor() );
68 | }
69 |
70 | private Interceptor getRequestLoggingInterceptor() {
71 | return chain -> {
72 | if ( LOG.isDebugEnabled() ) {
73 | Request request = chain.request();
74 | String body = bodyToString( request );
75 | LOG.debug( "Url: '" + request.url() + "' Headers: '" + request.headers() + "' Body: '" + body + "'" );
76 | }
77 | return chain.proceed( chain.request() );
78 | };
79 | }
80 |
81 | private static String bodyToString( final Request request ) {
82 | if ( request.body() == null ) {
83 | return "";
84 | }
85 | try {
86 | final Request copy = request.newBuilder().build();
87 | final Buffer buffer = new Buffer();
88 | copy.body().writeTo( buffer );
89 | return buffer.readUtf8();
90 | } catch ( final IOException e ) {
91 | return "Failed to extract body from request";
92 | }
93 | }
94 | }
95 |
96 | }
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/influxdb/MachineMessageConsumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.influxdb;
6 |
7 | import java.util.concurrent.TimeUnit;
8 |
9 | import org.eclipse.iot.unide.ppmp.commons.Device;
10 | import org.eclipse.iot.unide.ppmp.messages.Message;
11 | import org.eclipse.iot.unide.ppmp.messages.MessagesWrapper;
12 | import org.influxdb.InfluxDB;
13 | import org.influxdb.dto.BatchPoints;
14 | import org.influxdb.dto.Point;
15 | import org.influxdb.dto.Point.Builder;
16 |
17 | /**
18 | * Consumer class for Message
19 | */
20 | class MachineMessageConsumer extends AbstractInfluxDbConsumer {
21 |
22 | private static final String MEASUREMENT_NAME = "ppmp_messages";
23 |
24 | MachineMessageConsumer(InfluxDB connection, String databaseName) {
25 | super(connection, databaseName);
26 | }
27 |
28 | /**
29 | * Inserts a single Message-Array with device information
30 | *
31 | * @param message
32 | * Message-Array as PPMP java binding object
33 | * @param device
34 | * Device as PPMP java binding object
35 | */
36 | private void insert(Message message, Device device) {
37 | BatchPoints batchPoints = BatchPoints.database(getDatabaseName()).consistency(InfluxDB.ConsistencyLevel.ALL)
38 | .build();
39 | Builder builder = Point.measurement(MEASUREMENT_NAME).time(message.getTimestamp().toInstant().toEpochMilli(),
40 | TimeUnit.MILLISECONDS);
41 |
42 | setTags(builder, message, device);
43 | setFields(builder, message);
44 |
45 | Point point = builder.build();
46 | batchPoints.point(point);
47 | getInfluxDb().write(batchPoints);
48 | }
49 |
50 | /**
51 | * Sets the tag / index information in the db
52 | *
53 | * @param builder
54 | * - a pointer to the database
55 | * @param message
56 | * the data of a single message
57 | * @param device
58 | * the device object of the payload
59 | */
60 | private void setTags(Builder builder, Message message, Device device) {
61 | builder.tag("deviceId", device.getDeviceID());
62 | builder.tag("code", message.getCode());
63 | }
64 |
65 | /**
66 | * Sets other, non-indexed information in the db
67 | *
68 | * @param builder
69 | * - a pointer to the database
70 | * @param message
71 | * the data of a single message
72 | */
73 | private void setFields(Builder builder, Message message) {
74 | if (isNotNull(message.getOrigin())) {
75 | builder.addField("origin", message.getOrigin());
76 | }
77 |
78 | if (isNotNull(message.getSeverity())) {
79 | builder.addField("severity", message.getSeverity().name());
80 | }
81 |
82 | if (isNotNull(message.getTitle())) {
83 | builder.addField("title", message.getTitle());
84 | }
85 |
86 | if (isNotNull(message.getDescription())) {
87 | builder.addField("description", message.getDescription());
88 | }
89 |
90 | if (isNotNull(message.getHint())) {
91 | builder.addField("hint", message.getHint());
92 | }
93 |
94 | if (isNotNull(message.getType())) {
95 | builder.addField("type", message.getType().name());
96 | }
97 | }
98 |
99 | @Override
100 | public void accept(MessagesWrapper data) {
101 | Device device = data.getDevice();
102 | data.getMessages().forEach(message -> insert(message, device));
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/influxdb/MeasurementConsumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.influxdb;
6 |
7 | import java.util.List;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | import org.eclipse.iot.unide.ppmp.commons.Device;
11 | import org.eclipse.iot.unide.ppmp.measurements.Measurements;
12 | import org.eclipse.iot.unide.ppmp.measurements.MeasurementsWrapper;
13 | import org.eclipse.iot.unide.server.receiver.util.PpmpHelper;
14 | import org.influxdb.InfluxDB;
15 | import org.influxdb.dto.BatchPoints;
16 | import org.influxdb.dto.Point;
17 | import org.influxdb.dto.Point.Builder;
18 |
19 | /**
20 | * Consumer class for Measurement objects
21 | */
22 | class MeasurementConsumer extends AbstractInfluxDbConsumer {
23 |
24 | private static final String MEASUREMENT_NAME = "ppmp_measurements";
25 |
26 | private static final String DEVICE_ID = "deviceId";
27 | private static final String MEASUREMENT_POINT = "measurementPoint";
28 | private static final String MEASUREMENT_VALUE = "value";
29 |
30 | MeasurementConsumer(InfluxDB influxDB, String databaseName) {
31 | super(influxDB, databaseName);
32 | }
33 |
34 | private void insertMeasurements(Measurements measurements, Device device) {
35 | BatchPoints batchPoints = BatchPoints.database(getDatabaseName()).consistency(InfluxDB.ConsistencyLevel.ALL)
36 | .build();
37 | List measurementValues = PpmpHelper.getMeasurementValues(measurements);
38 | measurementValues.forEach(measurement -> {
39 | Builder pointBuilder = Point.measurement(MEASUREMENT_NAME).time(measurement.getTime(),
40 | TimeUnit.MILLISECONDS);
41 | setTags(pointBuilder, measurement, device);
42 | setFields(pointBuilder, measurement);
43 | batchPoints.point(pointBuilder.build());
44 | });
45 | getInfluxDb().write(batchPoints);
46 | }
47 |
48 | /**
49 | * Sets the tag / index information in the db
50 | *
51 | * @param builder
52 | * a pointer to the database
53 | * @param measurement
54 | * a single measurement
55 | * @param device
56 | * the device object of the payload
57 | */
58 | private void setTags(Builder builder, PpmpHelper.MeasurementValue measurement, Device device) {
59 | builder.tag(MEASUREMENT_POINT, measurement.getName());
60 | builder.tag(DEVICE_ID, device.getDeviceID());
61 | }
62 |
63 | /**
64 | * Sets other, non-indexed information in the db
65 | *
66 | * @param builder
67 | * a pointer to the database
68 | * @param measurement
69 | * a single measurement
70 | */
71 | private void setFields(Builder builder, PpmpHelper.MeasurementValue measurement) {
72 | builder.addField(MEASUREMENT_VALUE, measurement.getValue());
73 | }
74 |
75 | @Override
76 | public void accept(MeasurementsWrapper data) {
77 | Device device = data.getDevice();
78 | data.getMeasurements().forEach(measurement -> insertMeasurements(measurement, device));
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/influxdb/ProcessConsumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.influxdb;
6 |
7 | import java.util.concurrent.TimeUnit;
8 |
9 | import org.eclipse.iot.unide.ppmp.commons.Device;
10 | import org.eclipse.iot.unide.ppmp.process.Process;
11 | import org.eclipse.iot.unide.ppmp.process.ProcessWrapper;
12 | import org.eclipse.iot.unide.server.receiver.util.PpmpHelper;
13 | import org.influxdb.InfluxDB;
14 | import org.influxdb.dto.BatchPoints;
15 | import org.influxdb.dto.Point;
16 | import org.influxdb.dto.Point.Builder;
17 |
18 | /**
19 | * Consumer class for Process
20 | */
21 | class ProcessConsumer extends AbstractInfluxDbConsumer {
22 |
23 | private static final String DEVICE_ID = "deviceId";
24 | private static final String MEASUREMENT_NAME = "ppmp_processes";
25 | private static final String PROGRAM_ID = "program";
26 | private static final String PAYLOAD = "payload";
27 |
28 | ProcessConsumer(InfluxDB influxDB, String databaseName) {
29 | super(influxDB, databaseName);
30 | }
31 |
32 | /**
33 | * Inserts the process data
34 | *
35 | * @param processWrapper
36 | * The whole process message
37 | */
38 | private void insert(ProcessWrapper processWrapper) {
39 |
40 | BatchPoints batchPointsProcess = BatchPoints.database(getDatabaseName())
41 | .consistency(InfluxDB.ConsistencyLevel.ALL).build();
42 |
43 | long startTime = processWrapper.getProcess().getTimestamp().toInstant().toEpochMilli();
44 | Builder pointBuilder = Point.measurement(MEASUREMENT_NAME).time(startTime, TimeUnit.MILLISECONDS);
45 |
46 | setTags(pointBuilder, processWrapper.getProcess(), processWrapper.getDevice());
47 | setFields(pointBuilder, processWrapper);
48 |
49 | batchPointsProcess.point(pointBuilder.build());
50 | getInfluxDb().write(batchPointsProcess);
51 | }
52 |
53 | /**
54 | * Sets the tag / index information in the db
55 | *
56 | * @param builder
57 | * a pointer to the database
58 | * @param process
59 | * the process data
60 | * @param device
61 | * the device object of the payload
62 | */
63 | private void setTags(Builder builder, Process process, Device device) {
64 | String programId = null;
65 | if (isNotNull(process) && isNotNull(process.getProgram())) {
66 | programId = process.getProgram().getId();
67 | if (isNotNull(programId)) {
68 | builder.tag(PROGRAM_ID, programId);
69 | }
70 | }
71 | builder.tag(DEVICE_ID, device.getDeviceID());
72 | }
73 |
74 | /**
75 | * Sets other, non-indexed information in the db
76 | *
77 | * @param builder
78 | * a pointer to the database
79 | * @param processWrapper
80 | * the complete payload of the process Message
81 | */
82 | private void setFields(Builder builder, ProcessWrapper processWrapper) {
83 | builder.addField(PAYLOAD, PpmpHelper.toJson(processWrapper));
84 | }
85 |
86 | @Override
87 | public void accept(ProcessWrapper data) {
88 | insert(data);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/sql/AbstractSqlConsumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.sql;
6 |
7 | import java.util.function.Consumer;
8 |
9 | /**
10 | * Base class for sql {@link Consumer}
11 | *
12 | * @param - Type of Object to consume
13 | */
14 | abstract class AbstractSqlConsumer implements Consumer {
15 |
16 | private final JdbcExecutorService executorService;
17 |
18 | /**
19 | * @param executorService - the executorService
20 | */
21 | AbstractSqlConsumer( JdbcExecutorService executorService ) {
22 | this.executorService = executorService;
23 | }
24 |
25 | JdbcExecutorService getExecutorService() {
26 | return executorService;
27 | }
28 |
29 | /**
30 | * @param object - object to check
31 | * @return true when given object is not null
32 | */
33 | static boolean isNotNull( Object object ) {
34 | return null != object;
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/sql/JdbcExecutorService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.sql;
6 |
7 | import java.sql.Connection;
8 | import java.sql.SQLException;
9 |
10 | import javax.sql.DataSource;
11 |
12 | import org.flywaydb.core.Flyway;
13 |
14 | /**
15 | * Service to execute JDBC statements. Exception handling and commits are done
16 | * by this service.
17 | *
18 | * Provides method for execution of flyway changelog.
19 | */
20 | public class JdbcExecutorService {
21 |
22 | private static final String FLYWAY_BASE = "org/eclipse/iot/unide/server/receiver/sql/migrations";
23 | private static final String FLYWAY_TABLE = "SCHEMA_VERSION";
24 |
25 | private DataSource dataSource;
26 |
27 | public JdbcExecutorService(DataSource dataSource) {
28 | this.dataSource = dataSource;
29 | }
30 |
31 | /**
32 | * The given runner will be executed by this method by passing a valid jdbc
33 | * connection. The caller does not have handle connection related actions.
34 | * Commit, close and exception handling is done by this method.
35 | *
36 | * @param runner
37 | * executes the given runner
38 | */
39 | public void execute(ExecutorCommand runner) {
40 | Connection connection = null;
41 | try {
42 | connection = getConnection();
43 | connection.setAutoCommit(false);
44 | runner.accept(connection);
45 | connection.commit();
46 | } catch (Exception ex) {
47 | throw new RuntimeException("Data access failed.", ex);
48 | } finally {
49 | try {
50 | if (connection != null) {
51 | connection.close();
52 | }
53 | } catch (SQLException e) {
54 | // ignore
55 | }
56 | }
57 | }
58 |
59 | private Connection getConnection() throws SQLException {
60 | return dataSource.getConnection();
61 | }
62 |
63 | @FunctionalInterface
64 | public interface ExecutorCommand {
65 | void accept(T t) throws Exception;
66 | }
67 |
68 | /**
69 | * Executes the database changelog using flyway.
70 | */
71 | void executeChangeLog() {
72 | Flyway flyway = new Flyway();
73 | flyway.setTable(FLYWAY_TABLE);
74 | flyway.setDataSource(dataSource);
75 | flyway.setLocations(FLYWAY_BASE + "/" + getDbType());
76 | flyway.setBaselineOnMigrate(true);
77 | flyway.migrate();
78 | }
79 |
80 | /**
81 | * Returns the database type, if known.
82 | *
83 | * @return String which is database type
84 | */
85 | public String getDbType() {
86 | String dbName = "unknown";
87 | String dbType;
88 | try {
89 | dbName = dataSource.getConnection().getMetaData().getDatabaseProductName();
90 | } catch (SQLException e) {
91 | throw new RuntimeException( "Failed to get connection", e );
92 | }
93 | switch (dbName) {
94 | case "PostgreSQL":
95 | case "MockDatabase":
96 | dbType = "postgres";
97 | break;
98 | case "H2":
99 | dbType = "h2";
100 | break;
101 | // case "Microsoft SQL Server":
102 | // case "Oracle":
103 | // case "MySQL":
104 | // case "HSQL Database Engine":
105 | default:
106 | throw new RuntimeException("Unknown database '" + dbName + "'");
107 | }
108 | return dbType;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/sql/MachineMessageConsumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.sql;
6 |
7 | import java.sql.PreparedStatement;
8 | import java.sql.Timestamp;
9 |
10 | import org.eclipse.iot.unide.ppmp.commons.Device;
11 | import org.eclipse.iot.unide.ppmp.messages.Message;
12 | import org.eclipse.iot.unide.ppmp.messages.MessagesWrapper;
13 |
14 | /**
15 | * Consumer class for Message
16 | */
17 | class MachineMessageConsumer extends AbstractSqlConsumer {
18 |
19 | private static final String INSERT_MESSAGE =
20 | "INSERT INTO ppmp_messages(time, deviceid, code, severity, title, description, hint, type) VALUES(?,?,?,?,?,?,?,?)";
21 |
22 | /**
23 | * @param executorService - the connection
24 | */
25 | MachineMessageConsumer( JdbcExecutorService executorService ) {
26 | super( executorService );
27 | }
28 |
29 | @Override
30 | public void accept( MessagesWrapper data ) {
31 | Device device = data.getDevice();
32 | data.getMessages().forEach( message -> insert( message, device ) );
33 | }
34 |
35 | /**
36 | * Inserts a single Message-Array with device information
37 | *
38 | * @param message Message-Array as PPMP java binding object
39 | * @param device Device as PPMP java binding object
40 | */
41 | private void insert( Message message, Device device ) {
42 | getExecutorService().execute( connection -> {
43 | PreparedStatement insertMessage = connection.prepareStatement( INSERT_MESSAGE );
44 | long time = message.getTimestamp().toInstant().toEpochMilli();
45 | insertMessage.setTimestamp( 1, new Timestamp( time ) );
46 | insertMessage.setString( 2, device.getDeviceID() );
47 | insertMessage.setString( 3, message.getCode() );
48 | insertMessage.setString( 4, getSeverityName( message.getSeverity() ) );
49 | insertMessage.setString( 5, message.getTitle() );
50 | insertMessage.setString( 6, message.getDescription() );
51 | insertMessage.setString( 7, message.getHint() );
52 | insertMessage.setString( 8, getType( message.getType() ) );
53 | insertMessage.executeUpdate();
54 | } );
55 | }
56 |
57 | private String getSeverityName( Message.MessageSeverity messageSeverity ) {
58 | return isNotNull( messageSeverity ) ? messageSeverity.name() : null;
59 | }
60 |
61 | private String getType( Message.MessageType messageType ) {
62 | return isNotNull( messageType ) ? messageType.name() : null;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/sql/MeasurementConsumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.sql;
6 |
7 | import java.sql.PreparedStatement;
8 | import java.sql.SQLException;
9 | import java.sql.Timestamp;
10 | import java.util.List;
11 |
12 | import org.eclipse.iot.unide.ppmp.commons.Device;
13 | import org.eclipse.iot.unide.ppmp.measurements.Measurements;
14 | import org.eclipse.iot.unide.ppmp.measurements.MeasurementsWrapper;
15 | import org.eclipse.iot.unide.server.receiver.util.PpmpHelper;
16 |
17 | /**
18 | * Consumer class for Measurement objects
19 | */
20 | class MeasurementConsumer extends AbstractSqlConsumer {
21 |
22 | private static final String INSERT_MEASUREMENT =
23 | "INSERT INTO ppmp_measurements(time, deviceid, measurementpoint, value) VALUES(?,?,?,?)";
24 |
25 | MeasurementConsumer( JdbcExecutorService executorService ) {
26 | super( executorService );
27 | }
28 |
29 | @Override
30 | public void accept( MeasurementsWrapper data ) {
31 | Device device = data.getDevice();
32 | data.getMeasurements().forEach( measurement -> insertSingleMeasurement( measurement, device ) );
33 | }
34 |
35 | private void insertSingleMeasurement( Measurements measurements, Device device ) {
36 | List measurementValues = PpmpHelper.getMeasurementValues( measurements );
37 | final String deviceID = device.getDeviceID();
38 | getExecutorService().execute( connection ->
39 | measurementValues.forEach( measurement -> {
40 | try {
41 | PreparedStatement statement = connection.prepareStatement( INSERT_MEASUREMENT );
42 | statement.setTimestamp( 1, toTimeStamp( measurement.getTime() ) );
43 | statement.setString( 2, deviceID );
44 | statement.setString( 3, measurement.getName() );
45 | statement.setDouble( 4, measurement.getValue() );
46 | statement.executeUpdate();
47 | } catch ( SQLException e ) {
48 | throw new RuntimeException( e );
49 | }
50 | }
51 | )
52 | );
53 | }
54 |
55 | private static Timestamp toTimeStamp( long timestamp ) {
56 | return new Timestamp( timestamp );
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/sql/ProcessConsumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.sql;
6 |
7 | import java.sql.PreparedStatement;
8 | import java.sql.Timestamp;
9 |
10 | import org.eclipse.iot.unide.ppmp.commons.Device;
11 | import org.eclipse.iot.unide.ppmp.process.Process;
12 | import org.eclipse.iot.unide.ppmp.process.ProcessWrapper;
13 | import org.eclipse.iot.unide.server.receiver.util.PpmpHelper;
14 |
15 | /**
16 | * Consumer class for Process
17 | */
18 | class ProcessConsumer extends AbstractSqlConsumer {
19 |
20 | private static final String INSERT_PROCESS = "INSERT INTO ppmp_processes(time, deviceid, programid, payload) VALUES(?,?,?,?)";
21 | private static final String INSERT_PROCESS_PG = "INSERT INTO ppmp_processes(time, deviceid, programid, payload) VALUES(?,?,?,to_json(?::json))";
22 |
23 | /**
24 | * @param executorService
25 | * - the connection
26 | */
27 | ProcessConsumer(JdbcExecutorService executorService) {
28 | super(executorService);
29 | }
30 |
31 | @Override
32 | public void accept(ProcessWrapper data) {
33 | insertProcessData(data);
34 | }
35 |
36 | /**
37 | * Inserts the process data
38 | *
39 | * @param processWrapper
40 | * The whole process message
41 | */
42 | private void insertProcessData(ProcessWrapper processWrapper) {
43 | getExecutorService().execute(connection -> {
44 | Device device = processWrapper.getDevice();
45 | String programId = getProgramId(processWrapper.getProcess());
46 | long time = processWrapper.getProcess().getTimestamp().toInstant().toEpochMilli();
47 |
48 | String sql = INSERT_PROCESS;
49 | String dbType = getExecutorService().getDbType();
50 | if (dbType == "postgres") {
51 | sql = INSERT_PROCESS_PG;
52 | }
53 | PreparedStatement insertProcess = connection.prepareStatement(sql);
54 | insertProcess.setTimestamp(1, new Timestamp(time));
55 | insertProcess.setString(2, device.getDeviceID());
56 | insertProcess.setString(3, programId);
57 | insertProcess.setString(4, PpmpHelper.toJson(processWrapper));
58 | insertProcess.executeUpdate();
59 | });
60 | }
61 |
62 | private static String getProgramId(Process process) {
63 | if (process == null || process.getProgram() == null) {
64 | return null;
65 | }
66 | return process.getProgram().getId();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/sql/SqlDbReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.sql;
6 |
7 | import java.util.function.Consumer;
8 |
9 | import javax.sql.DataSource;
10 |
11 | import org.eclipse.iot.unide.ppmp.PPMPPackager;
12 | import org.eclipse.iot.unide.ppmp.measurements.MeasurementsWrapper;
13 | import org.eclipse.iot.unide.ppmp.messages.MessagesWrapper;
14 | import org.eclipse.iot.unide.ppmp.process.ProcessWrapper;
15 | import org.eclipse.iot.unide.server.receiver.PpmpEventReceiver;
16 | import org.eclipse.iot.unide.server.receiver.ReceiverException;
17 |
18 | /**
19 | * This receiver is backed by a sql Database.
20 | * Received {@link org.eclipse.iot.unide.server.ppmp.PpmpEvent}'s are persisted to sql.
21 | */
22 | public class SqlDbReceiver extends PpmpEventReceiver {
23 |
24 | private JdbcExecutorService executorService;
25 |
26 | public SqlDbReceiver( DataSource dataSource ) {
27 | super( new PPMPPackager() );
28 | this.executorService = new JdbcExecutorService( dataSource );
29 | }
30 |
31 | @Override
32 | public void init() throws ReceiverException {
33 | try {
34 | executorService.executeChangeLog();
35 | } catch ( Exception e ) {
36 | throw new ReceiverException( "Failed to execute changeset", e );
37 | }
38 | }
39 |
40 | @Override
41 | protected Consumer getMessagesConsumer() {
42 | return new MachineMessageConsumer( executorService );
43 | }
44 |
45 | @Override
46 | protected Consumer getMeasurementsConsumer() {
47 | return new MeasurementConsumer( executorService );
48 | }
49 |
50 | @Override
51 | protected Consumer getProcessesConsumer() {
52 | return new ProcessConsumer( executorService );
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/receiver/util/PpmpHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.receiver.util;
6 |
7 | import java.util.List;
8 | import java.util.Map;
9 | import java.util.stream.Collectors;
10 | import java.util.stream.IntStream;
11 | import java.util.stream.Stream;
12 |
13 | import org.eclipse.iot.unide.ppmp.PPMPPackager;
14 | import org.eclipse.iot.unide.ppmp.measurements.Measurements;
15 |
16 | /**
17 | * Helper class for Ppmp related logic.
18 | */
19 | public class PpmpHelper {
20 |
21 | private static final String TIME_FIELD = "$_time";
22 |
23 | /**
24 | * Converts the given object to Json.
25 | *
26 | * For the convert {@link PPMPPackager} is used.
27 | *
28 | * @param object - the object to convert
29 | * @return Json representation of given object
30 | */
31 | public static String toJson( Object object ) {
32 | try {
33 | PPMPPackager ppmpPackager = new PPMPPackager();
34 | return ppmpPackager.getMessage( object );
35 | } catch ( Exception ex ) {
36 | throw new RuntimeException( "Failed to decode process to json", ex );
37 | }
38 | }
39 |
40 | /**
41 | * A single Measurement message can consist of multiple measurement-value entries.
42 | * For instance a single Measurement could have 10 temperature measurements.
43 | * This method extract's all measurement-value entries from the given measurement.
44 | *
45 | * @param measurement - the measurement to get measurement-value entries from
46 | * @return list of all measurement-value entries from given measurement
47 | */
48 | public static List getMeasurementValues( Measurements measurement ) {
49 | Stream>> seriesIterator = measurement.getSeriesMap().getSeries().entrySet()
50 | .stream();
51 | long startTime = measurement.getTimestamp().toInstant().toEpochMilli();
52 | Map> values = seriesIterator.filter( entry -> !TIME_FIELD.equals( entry.getKey() ) )
53 | .collect( Collectors
54 | .toMap( Map.Entry::getKey, Map.Entry::getValue ) );
55 | List timeLists = measurement.getSeriesMap().getSeries().get( TIME_FIELD );
56 | return values.entrySet().stream().map( entry ->
57 | IntStream.range( 0, timeLists.size() ).mapToObj( i -> {
58 | Number measurementValue = entry.getValue().get( i );
59 | Number timeOffset = timeLists.get( i );
60 | long time = startTime + timeOffset.longValue();
61 | return new MeasurementValue( entry.getKey(), time, measurementValue.doubleValue() );
62 | } ) ).flatMap( stream -> stream )
63 | .collect( Collectors.toList() );
64 | }
65 |
66 | /**
67 | * Represents a single measurement-value entry.
68 | */
69 | public static class MeasurementValue {
70 | private String name;
71 | private long time;
72 | private double value;
73 |
74 | MeasurementValue( String name, long time, double value ) {
75 | this.name = name;
76 | this.time = time;
77 | this.value = value;
78 | }
79 |
80 | public String getName() {
81 | return name;
82 | }
83 |
84 | public long getTime() {
85 | return time;
86 | }
87 |
88 | public double getValue() {
89 | return value;
90 | }
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/servers/rest/src/main/java/org/eclipse/iot/unide/server/web/Addresses.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.web;
6 |
7 | /**
8 | * Constants for vert.x eventbus message addresses used in the application.
9 | */
10 | public class Addresses {
11 | /**
12 | * Send's an ppmp - event.
13 | */
14 | public static final String SEND_PPMP_EVENT = "send_ppmp_event";
15 | }
16 |
--------------------------------------------------------------------------------
/servers/rest/src/main/resources/application_conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "http.port": 8090,
3 | "persistence.enable" : true,
4 | "persistence.system" : "sql",
5 | "influxDb.url": "http://localhost:8086",
6 | "influxDb.user": "root",
7 | "influxDb.password": "root",
8 | "sqlDb.url" : "jdbc:h2:tcp://localhost:9040/./db/test",
9 | "sqlDb.user" : "sa",
10 | "sqlDb.driver": "org.h2.Driver",
11 | "sqlDb.password" : ""
12 | }
--------------------------------------------------------------------------------
/servers/rest/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # Root logger option
2 | log4j.rootLogger=INFO, stdout, file
3 |
4 | # Redirect log messages to console
5 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
6 | log4j.appender.stdout.Target=System.out
7 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
8 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
9 |
10 | # Redirect log messages to a log file, support file rolling.
11 | log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
12 | log4j.appender.file.datePattern='.'yyyy-MM-dd_HH-mm
13 | log4j.appender.file.File=unide.log
14 | log4j.appender.file.MaxFileSize=5MB
15 | log4j.appender.file.layout=org.apache.log4j.PatternLayout
16 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
17 |
--------------------------------------------------------------------------------
/servers/rest/src/main/resources/org/eclipse/iot/unide/server/receiver/sql/migrations/h2/V1__initial_setup.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE ppmp_messages (
2 | time TIMESTAMP NOT NULL,
3 | deviceId TEXT NOT NULL,
4 | code TEXT NOT NULL,
5 | severity TEXT NULL,
6 | title TEXT NULL,
7 | description TEXT NULL,
8 | hint TEXT NULL,
9 | type TEXT NULL
10 | );
11 |
12 | CREATE TABLE ppmp_measurements (
13 | time TIMESTAMP NOT NULL,
14 | deviceid TEXT NOT NULL,
15 | measurementpoint TEXT NOT NULL,
16 | value DECIMAL NOT NULL
17 | );
18 |
19 | CREATE TABLE ppmp_processes (
20 | time TIMESTAMP NOT NULL,
21 | deviceid TEXT NOT NULL,
22 | programid TEXT NULL,
23 | payload CLOB NOT NULL
24 | );
25 |
--------------------------------------------------------------------------------
/servers/rest/src/main/resources/org/eclipse/iot/unide/server/receiver/sql/migrations/postgres/V1__initial_setup.sql:
--------------------------------------------------------------------------------
1 | CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;
2 |
3 | CREATE TABLE ppmp_messages (
4 | time TIMESTAMP NOT NULL,
5 | deviceId TEXT NOT NULL,
6 | code TEXT NOT NULL,
7 | severity TEXT NULL,
8 | title TEXT NULL,
9 | description TEXT NULL,
10 | hint TEXT NULL,
11 | type TEXT NULL
12 | );
13 |
14 | SELECT create_hypertable('ppmp_messages', 'time');
15 |
16 | CREATE TABLE ppmp_measurements (
17 | time TIMESTAMP NOT NULL,
18 | deviceid TEXT NOT NULL,
19 | measurementpoint TEXT NOT NULL,
20 | value DECIMAL NOT NULL
21 | );
22 |
23 | SELECT create_hypertable('ppmp_measurements', 'time');
24 |
25 | CREATE TABLE ppmp_processes (
26 | time TIMESTAMP NOT NULL,
27 | deviceid TEXT NOT NULL,
28 | programid TEXT NULL,
29 | payload JSON NOT NULL
30 | );
31 |
32 | SELECT create_hypertable('ppmp_processes', 'time');
33 |
--------------------------------------------------------------------------------
/servers/rest/src/test/java/org/eclipse/iot/unide/server/PpmpSimulator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server;
6 |
7 | import java.io.IOException;
8 | import java.net.URL;
9 | import java.time.OffsetDateTime;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 | import java.util.concurrent.ThreadLocalRandom;
13 | import java.util.stream.Collectors;
14 | import java.util.stream.IntStream;
15 |
16 | import org.eclipse.iot.unide.ppmp.commons.Device;
17 | import org.eclipse.iot.unide.ppmp.measurements.Measurements;
18 | import org.eclipse.iot.unide.ppmp.measurements.MeasurementsWrapper;
19 | import org.eclipse.iot.unide.ppmp.measurements.SeriesMap;
20 |
21 | import com.fasterxml.jackson.annotation.JsonInclude;
22 | import com.fasterxml.jackson.databind.DeserializationFeature;
23 | import com.fasterxml.jackson.databind.ObjectMapper;
24 | import com.fasterxml.jackson.databind.SerializationFeature;
25 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
26 |
27 | import okhttp3.Call;
28 | import okhttp3.MediaType;
29 | import okhttp3.OkHttpClient;
30 | import okhttp3.Request;
31 | import okhttp3.RequestBody;
32 | import okhttp3.Response;
33 |
34 | public class PpmpSimulator {
35 |
36 | public static void main( String[] args ) throws IOException, InterruptedException {
37 |
38 | ObjectMapper objectMapper = new ObjectMapper();
39 | objectMapper.enable( DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY );
40 | objectMapper.registerModule( new JavaTimeModule() );
41 |
42 | objectMapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
43 | objectMapper.setSerializationInclusion( JsonInclude.Include.NON_NULL );
44 |
45 | OkHttpClient okHttpClient = new OkHttpClient();
46 | int count = 0;
47 | while ( true ) {
48 | count = count + 1;
49 | System.out.println( count + " .Going to send request" );
50 | MeasurementsWrapper measurements = new MeasurementsWrapper();
51 | Device device = new Device();
52 | device.setDeviceID( "33a58b-8350-4592-23121-755194497d" );
53 | measurements.setDevice( device );
54 | List measurementsList = new ArrayList<>();
55 | measurementsList.add( generateMeasurements() );
56 | measurements.setMeasurements( measurementsList );
57 |
58 | String body = objectMapper.writeValueAsString( measurements );
59 |
60 | Call call = okHttpClient.newCall(
61 | new Request.Builder().url( new URL( "http://localhost:8090/rest/v2/" ) )
62 | .post( RequestBody.create( MediaType.parse( "application/json" ), body ) )
63 | .build() );
64 | Response response = call.execute();
65 | int responseCode = response.code();
66 | System.out.println( count + " Response send. Code: " + responseCode );
67 | Thread.sleep( 3000 );
68 | }
69 |
70 | }
71 |
72 | private static Measurements generateMeasurements() {
73 | Measurements measurements = new Measurements();
74 |
75 | measurements.setCode( "TEST" );
76 | measurements.setTimestamp( OffsetDateTime.now() );
77 |
78 | SeriesMap seriesMap = new SeriesMap();
79 | seriesMap.setSeriesValue( "$_time", randomValues( 0, 1, 10, 90 ) );
80 | seriesMap.setSeriesValue( "temprature", randomValues( 0, 1, 30,50 ) );
81 | seriesMap.setSeriesValue( "pressure", randomValues( 0, 1, 40,80 ) );
82 | measurements.setSeriesMap( seriesMap );
83 |
84 | return measurements;
85 | }
86 |
87 | private static List randomValues( int begin, int end, int minValue, int maxValue ) {
88 | return IntStream.range( begin, end )
89 | .map( val -> ThreadLocalRandom.current().nextInt( minValue, maxValue + 1 ) )
90 | .boxed().collect( Collectors.toList() );
91 |
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/servers/rest/src/test/java/org/eclipse/iot/unide/server/jdbc/JdbcMockFactoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.jdbc;
6 |
7 | import java.sql.Connection;
8 | import java.sql.PreparedStatement;
9 | import java.sql.SQLException;
10 |
11 | import org.junit.Assert;
12 | import org.junit.Rule;
13 | import org.junit.Test;
14 |
15 | import com.github.tomakehurst.wiremock.client.WireMock;
16 | import com.github.tomakehurst.wiremock.junit.WireMockRule;
17 |
18 | public class JdbcMockFactoryTest {
19 |
20 | @Rule
21 | public WireMockRule wireMockRule = new WireMockRule( 8080 );
22 |
23 | @Test
24 | public void execute_update_statement_should_return_valid_response() throws SQLException {
25 | int expectedUpdateResult = 21;
26 | wireMockRule.stubFor( WireMock.post( "/sqlStub" )
27 | .withRequestBody( WireMock
28 | .equalTo( "INSERT INTO TEST(col1, col2, col3, col4) VALUES(?,?,?,?)" ) )
29 | .withHeader( "1", WireMock.equalTo( "UpdateValue1" ) )
30 | .withHeader( "2", WireMock.equalTo( "UpdateValue2" ) )
31 | .withHeader( "3", WireMock.equalTo( "UpdateValue3" ) )
32 | .withHeader( "4", WireMock.equalTo( "10.0" ) )
33 | .willReturn( WireMock.aResponse().withBody( String.valueOf( expectedUpdateResult ) )
34 | .withStatus( 200 ) )
35 | );
36 |
37 | Connection mockConnection = JdbcMockFactory.getMockConnection( "http://localhost:8080/sqlStub" );
38 | PreparedStatement preparedStatement = mockConnection
39 | .prepareStatement( "INSERT INTO TEST(col1, col2, col3, col4) VALUES(?,?,?,?)" );
40 | preparedStatement.setString( 1, "UpdateValue1" );
41 | preparedStatement.setString( 2, "UpdateValue2" );
42 | preparedStatement.setString( 3, "UpdateValue3" );
43 | preparedStatement.setDouble( 4, 10 );
44 | int updateResult = preparedStatement.executeUpdate();
45 | Assert.assertEquals( expectedUpdateResult, updateResult );
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/servers/rest/src/test/java/org/eclipse/iot/unide/server/web/FileUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.web;
6 |
7 | import java.io.IOException;
8 | import java.nio.charset.Charset;
9 |
10 | import org.apache.commons.io.IOUtils;
11 |
12 | class FileUtils {
13 | static String readFile( String filename ) {
14 | ClassLoader classLoader = FileUtils.class.getClassLoader();
15 | try {
16 | return IOUtils.toString( classLoader.getResourceAsStream( filename ), Charset.defaultCharset() );
17 | } catch ( IOException e ) {
18 | throw new RuntimeException( e );
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/servers/rest/src/test/java/org/eclipse/iot/unide/server/web/RestEndpointValidationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018 Bosch Software Innovations GmbH. All rights reserved.
3 | */
4 |
5 | package org.eclipse.iot.unide.server.web;
6 |
7 | import static com.jayway.restassured.RestAssured.given;
8 | import static org.hamcrest.CoreMatchers.equalTo;
9 | import static org.hamcrest.MatcherAssert.assertThat;
10 | import static org.hamcrest.collection.IsEmptyCollection.empty;
11 |
12 | import java.io.IOException;
13 |
14 | import org.eclipse.iot.unide.server.MainVerticle;
15 | import org.junit.After;
16 | import org.junit.Before;
17 | import org.junit.Rule;
18 | import org.junit.Test;
19 | import org.junit.runner.RunWith;
20 |
21 | import com.github.tomakehurst.wiremock.junit.WireMockRule;
22 | import com.jayway.restassured.RestAssured;
23 |
24 | import io.vertx.core.DeploymentOptions;
25 | import io.vertx.core.Vertx;
26 | import io.vertx.core.json.JsonObject;
27 | import io.vertx.ext.unit.TestContext;
28 | import io.vertx.ext.unit.junit.VertxUnitRunner;
29 |
30 | /**
31 | * Unit test for validation Endpoint.
32 | *
33 | * This test ensures that the validation endpoint is still working when persistence is disabled.
34 | */
35 | @RunWith( VertxUnitRunner.class )
36 | public class RestEndpointValidationTest {
37 |
38 | private static final String PROCESS_MESSAGE_VALID = "server/messages/process_message_valid.json";
39 | private static final String PROCESS_MESSAGE_INVALID = "server/messages/process_message_invalid.json";
40 |
41 | private static final int PORT = 8080;
42 | private static final String APPLICATION_URL = "http://localhost" + ":" + PORT;
43 | private static final int WIREMOCK_PORT = 9090;
44 |
45 | private static final String PPMP_REST_VALIDATE_VALIDATE = "/rest/v2/validate";
46 | private Vertx vertx;
47 |
48 | @Rule
49 | public final WireMockRule wireMockRule = new WireMockRule( WIREMOCK_PORT );
50 |
51 | @Before
52 | public void setUp( TestContext context ) throws IOException {
53 | RestAssured.baseURI = APPLICATION_URL;
54 | vertx = Vertx.vertx();
55 | DeploymentOptions options = new DeploymentOptions()
56 | .setConfig( new JsonObject().put( "http.port", PORT )
57 | .put( "persistence.enable", false ) );
58 | vertx.deployVerticle( MainVerticle.class.getName(), options, context.asyncAssertSuccess() );
59 | }
60 |
61 | @After
62 | public void after() {
63 | vertx.close();
64 | }
65 |
66 | @Test
67 | public void test_valid_message_should_return_200() {
68 | String invalidMessage = FileUtils.readFile( PROCESS_MESSAGE_VALID );
69 | given().body( invalidMessage ).post( PPMP_REST_VALIDATE_VALIDATE )
70 | .then().statusCode( 200 )
71 | .body( equalTo( "Ppmp Message of type 'PROCESS' is valid" ) );
72 | assertThat( wireMockRule.getServeEvents().getRequests(), empty() );
73 | }
74 |
75 | @Test
76 | public void test_valid_message_should_return_400() {
77 | String invalidMessage = FileUtils.readFile( PROCESS_MESSAGE_INVALID );
78 | given().body( invalidMessage ).post( PPMP_REST_VALIDATE_VALIDATE )
79 | .then().statusCode( 400 )
80 | .body( equalTo(
81 | "{\"measurements\":\"is missing but it is required\"}" ) );
82 | assertThat( wireMockRule.getServeEvents().getRequests(), empty() );
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/servers/rest/src/test/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2017 Bosch Software Innovations GmbH. All rights reserved.
3 | #
4 |
5 | # Root logger option
6 | log4j.rootLogger=INFO, stdout, file
7 |
8 | # Redirect log messages to console
9 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender
10 | log4j.appender.stdout.Target=System.out
11 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
12 | log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
13 |
14 | # Redirect log messages to a log file, support file rolling.
15 | log4j.appender.file=org.apache.log4j.RollingFileAppender
16 | log4j.appender.file.File=unide.log
17 | log4j.appender.file.MaxFileSize=5MB
18 | log4j.appender.file.MaxBackupIndex=10
19 | log4j.appender.file.layout=org.apache.log4j.PatternLayout
20 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
--------------------------------------------------------------------------------
/servers/rest/src/test/resources/server/messages/machine_message_invalid.json:
--------------------------------------------------------------------------------
1 | {
2 | "content-spec": "urn:spec://eclipse.org/unide/machine-message#v2",
3 | "device":
4 | {
5 | "deviceID": "2ca5158b-8350-4592-bff9-755194497d4e",
6 | "operationalStatus": "normal",
7 | "metaData":
8 | {
9 | "swVersion": "2.0.3.13",
10 | "swBuildID": "41535"
11 | }
12 | },
13 |
14 | "messages":
15 | [
16 | {
17 | "ts": "2002-05-30T09:30:10.125+02:00",
18 | "type": "TECHNICAL_INFO",
19 | "severity": "HIGH",
20 | "code": "33-02",
21 | "title": "Disk size limit reached",
22 | "description": "Disk size has reached limit. Unable to write log files."
23 | },
24 | {
25 | "type": "TECHNICAL_INFO",
26 | "severity": "HIGH",
27 | "code": "33-02",
28 | "title": "Disk size limit reached",
29 | "description": "Disk size has reached limit. Unable to write log files."
30 | }
31 | ]
32 | }
--------------------------------------------------------------------------------
/servers/rest/src/test/resources/server/messages/machine_message_valid.json:
--------------------------------------------------------------------------------
1 | {
2 | "content-spec": "urn:spec://eclipse.org/unide/machine-message#v2",
3 | "device":
4 | {
5 | "deviceID": "2ca5158b-8350-4592-bff9-755194497d4e",
6 | "operationalStatus": "normal",
7 | "metaData":
8 | {
9 | "swVersion": "2.0.3.13",
10 | "swBuildID": "41535"
11 | }
12 | },
13 |
14 | "messages":
15 | [
16 | {
17 | "ts": "2002-05-30T09:30:10.125+02:00",
18 | "type": "TECHNICAL_INFO",
19 | "severity": "HIGH",
20 | "code": "33-02",
21 | "title": "Disk size limit reached",
22 | "description": "Disk size has reached limit. Unable to write log files."
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/servers/rest/src/test/resources/server/messages/measurement_message_invalid.json:
--------------------------------------------------------------------------------
1 | {
2 | "content-spec": "urn:spec://eclipse.org/unide/measurement-message#v2",
3 | "device":
4 | {
5 | "deviceID": "a4927dad-58d4-4580-b460-79cefd56775b",
6 | "operationalStatus": "normal",
7 | "metaData":
8 | {
9 | "swVersion": "2.0.3.13",
10 | "swBuildID": "41535"
11 | }
12 | },
13 |
14 | "part":
15 | {
16 | "partTypeID": "F00VH07328",
17 | "partID": "420003844",
18 | "result": "NOK",
19 | "code": "HUH289",
20 | "metaData":
21 | {
22 | "chargeID": "845849",
23 | "toolID": "32324-432143"
24 | }
25 | },
26 |
27 | "measurements":
28 | [
29 | {
30 | "ts": "2002-05-30T09:30:10.123+02:00",
31 | "result": "NOK",
32 | "code": "0000 EE01",
33 | "limits":
34 | {
35 | "temperature":
36 | {
37 | "upperError": 4444,
38 | "lowerError": 44,
39 | "upperWarn": 2222,
40 | "lowerWarn": 46
41 | }
42 | },
43 |
44 | "series":
45 | {
46 | "temperature":
47 | [
48 | 45.4243,
49 | 46.42342,
50 | 44.2432
51 | ]
52 | }
53 | },
54 |
55 | {
56 | "ts": "2002-05-30T09:30:10.123+02:00",
57 | "result": "OK",
58 | "limits":
59 | {
60 | "force":
61 | {
62 | "upperError": 25,
63 | "lowerError": 20
64 | },
65 |
66 | "pressure":
67 | {
68 | "upperError": 60,
69 | "lowerError": 40.4
70 | }
71 | },
72 |
73 | "series":
74 | {
75 | "$_time":
76 | [
77 | 0,
78 | 23,
79 | 24
80 | ],
81 | "force":
82 | [
83 | 26,
84 | 23,
85 | 24
86 | ],
87 |
88 | "pressure":
89 | [
90 | 52.4,
91 | 46.32,
92 | 44.2432
93 | ]
94 | }
95 | }
96 | ]
97 | }
--------------------------------------------------------------------------------
/servers/rest/src/test/resources/server/messages/measurement_message_valid.json:
--------------------------------------------------------------------------------
1 | {
2 | "content-spec": "urn:spec://eclipse.org/unide/measurement-message#v2",
3 | "device":
4 | {
5 | "deviceID": "a4927dad-58d4-4580-b460-79cefd56775b",
6 | "operationalStatus": "normal",
7 | "metaData":
8 | {
9 | "swVersion": "2.0.3.13",
10 | "swBuildID": "41535"
11 | }
12 | },
13 |
14 | "part":
15 | {
16 | "partTypeID": "F00VH07328",
17 | "partID": "420003844",
18 | "result": "NOK",
19 | "code": "HUH289",
20 | "metaData":
21 | {
22 | "chargeID": "845849",
23 | "toolID": "32324-432143"
24 | }
25 | },
26 |
27 | "measurements":
28 | [
29 | {
30 | "ts": "2002-05-30T09:30:10.123+02:00",
31 | "result": "OK",
32 | "limits":
33 | {
34 | "force":
35 | {
36 | "upperError": 25,
37 | "lowerError": 20
38 | },
39 |
40 | "pressure":
41 | {
42 | "upperError": 60,
43 | "lowerError": 40.4
44 | }
45 | },
46 | "series":
47 | {
48 | "$_time":
49 | [
50 | 0,
51 | 23
52 | ],
53 | "force":
54 | [
55 | 26,
56 | 23
57 | ],
58 |
59 | "pressure":
60 | [
61 | 52.4,
62 | 46.32
63 | ]
64 | }
65 | }
66 | ]
67 | }
--------------------------------------------------------------------------------
/servers/rest/src/test/resources/server/messages/process_message_invalid.json:
--------------------------------------------------------------------------------
1 | {
2 | "content-spec" : "urn:spec://eclipse.org/unide/process-message#v2",
3 | "device" : {
4 | "deviceID" : "a4927dad-58d4-4580-b460-79cefd56775b",
5 | "operationalStatus" : "normal",
6 | "metaData" : {
7 | "swVersion" : "2.0.3.13",
8 | "swBuildId" : "41535"
9 | }
10 | },
11 | "process" : {
12 | "externalProcessId" : "b4927dad-58d4-4580-b460-79cefd56775b",
13 | "ts" : "2002-05-30T09:30:10.123+02:00",
14 | "result" : "NOK",
15 | "shutoffPhase" : "phase 1",
16 | "program" : {
17 | "id" : "1",
18 | "lastChangedDate" : "2002-05-30T09:30:10.123+02:00"
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/servers/rest/src/test/resources/server/messages/process_message_valid.json:
--------------------------------------------------------------------------------
1 | {
2 | "content-spec" : "urn:spec://eclipse.org/unide/process-message#v2",
3 | "device" : {
4 | "deviceID" : "a4927dad-58d4-4580-b460-79cefd56775b",
5 | "operationalStatus" : "normal",
6 | "metaData" : {
7 | "swVersion" : "2.0.3.13",
8 | "swBuildId" : "41535"
9 | }
10 | },
11 | "process" : {
12 | "externalProcessId" : "b4927dad-58d4-4580-b460-79cefd56775b",
13 | "ts" : "2002-05-30T09:30:10.123+02:00",
14 | "result" : "NOK",
15 | "shutoffPhase" : "phase 1",
16 | "program" : {
17 | "id" : "1",
18 | "name" : "Program 1",
19 | "lastChangedDate" : "2002-05-30T09:30:10.123+02:00"
20 | }
21 | },
22 | "measurements" : [
23 | {
24 | "ts" : "2002-05-30T09:30:10.123+02:00",
25 | "phase" : "phasen name 2",
26 | "name" : "500 Grad links drehen",
27 | "result" : "NOK",
28 | "code" : "0000 EE01",
29 | "series" : {
30 | "time" : [0, 23, 24],
31 | "force" : [26, 23, 24],
32 | "pressure" : [52.4, 46.32, 44.2432],
33 | "temperature" : [45.4243, 46.42342, 44.2432]
34 | }
35 | }
36 | ]
37 | }
--------------------------------------------------------------------------------
/website/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [["env", {
3 | "targets": {
4 | "browsers": [
5 | "> 1%",
6 | "not ie < 11"
7 | ]
8 | }
9 | }]],
10 | "plugins": [
11 | "@babel/transform-runtime"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/website/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "parser": "babel-eslint",
4 | "ecmaVersion": 6
5 | },
6 | "plugins": [
7 | "standard", "promise", "html", "babel"
8 | ],
9 | "extends": ["plugin:vue/essential", "@vue/standard"],
10 | "env": {
11 | "browser": true,
12 | "jquery": false
13 | },
14 | "globals": {
15 | "chrome": true,
16 | "browser": true,
17 | "nw": true
18 | },
19 | "rules": {
20 | "key-spacing": ["error", {
21 | "align": {
22 | "beforeColon": false,
23 | "afterColon": true,
24 | "on": "value"
25 | },
26 | "multiLine": {
27 | "beforeColon": false,
28 | "afterColon": true
29 | }
30 | }],
31 | "brace-style": ["warn", "1tbs", {
32 | "allowSingleLine": false
33 | }],
34 | "camelcase": ["error", {
35 | "properties": "always"
36 | }],
37 | "comma-style": ["warn", "last"],
38 | "consistent-return": ["off"],
39 | "default-case": ["error"],
40 | "indent": ["error", 2, {
41 | "SwitchCase": 1,
42 | "MemberExpression": 1,
43 | "VariableDeclarator": {
44 | "var": 2,
45 | "let": 2,
46 | "const": 3
47 | },
48 | "FunctionDeclaration": {
49 | "body": 1,
50 | "parameters": "first"
51 | },
52 | "FunctionExpression": {
53 | "body": 1,
54 | "parameters": "first"
55 | },
56 | "CallExpression": {
57 | "arguments": "first"
58 | },
59 | "ArrayExpression": "first",
60 | "ObjectExpression": 1
61 | }],
62 | "keyword-spacing": ["error", {
63 | "before": true,
64 | "after": true,
65 | "overrides": {
66 | "if": {
67 | "after": false
68 | },
69 | "for": {
70 | "after": false
71 | },
72 | "while": {
73 | "after": false
74 | },
75 | "catch": {
76 | "after": false
77 | }
78 | }
79 | }],
80 | "no-multi-spaces": "off",
81 | "no-shadow": ["warn"],
82 | "no-unused-vars": ["warn"],
83 | "no-unused-expressions": "off",
84 | "no-use-before-define": ["error", {
85 | "functions": true,
86 | "classes": true,
87 | "variables": true
88 | }],
89 | "one-var": ["error", "always"],
90 | "one-var-declaration-per-line": ["error", "always"],
91 | "quote-props": ["warn", "as-needed"],
92 | "semi": ["error", "always"],
93 | "space-before-function-paren": ["error", "never"],
94 | "vars-on-top": "error",
95 |
96 | "promise/param-names": "warn",
97 | "promise/always-return": "warn",
98 | "promise/catch-or-return": "warn"
99 | }
100 | }
101 |
102 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 |
4 | # logs
5 | npm-debug.log
6 |
7 | # Nuxt build
8 | .nuxt
9 |
10 | # Nuxt generate
11 | dist
12 |
13 | plantuml.jar
14 |
15 | # https://git.eclipse.org/r/www.eclipse.org/unide
16 | unide
17 | .npmrc
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | # unide
2 |
3 | > Eclipse Unide: Understand Industry devices
4 |
5 | ## Build Setup
6 |
7 | ``` bash
8 | # install dependencies
9 | $ npm install # Or yarn install
10 |
11 | # serve with hot reload at localhost:3000
12 | $ npm run dev
13 |
14 | # build for production and launch server
15 | $ npm run build
16 | $ npm start
17 |
18 | # generate static project
19 | $ npm run generate
20 | ```
21 |
22 | For detailed explanation on how things work, checkout the [Nuxt.js docs](https://github.com/nuxt/nuxt.js).
23 |
--------------------------------------------------------------------------------
/website/assets/schemas/v3/measurement_schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "content-spec": {
5 | "type": "string",
6 | "default": "urn:spec://eclipse.org/unide/machine-message#v3",
7 | "description": "Defines what the format version is"
8 | },
9 | "device": {
10 | "$ref": "definitions.json#/definitions/device"
11 | },
12 | "part": {
13 | "$ref": "definitions.json#/definitions/part"
14 | },
15 | "measurements": {
16 | "allOf": [
17 | {
18 | "$ref": "definitions.json#/definitions/measurements"
19 | },
20 | {
21 | "items": {
22 | "properties": {
23 | "series": {
24 | "required": ["time"]
25 | }
26 | }
27 | }
28 | }
29 | ]
30 | }
31 | },
32 | "required": [
33 | "content-spec",
34 | "device",
35 | "measurements"
36 | ]
37 | }
--------------------------------------------------------------------------------
/website/assets/schemas/v3/message_schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "content-spec": {
5 | "type": "string",
6 | "default": "urn:spec://eclipse.org/unide/machine-message#v3",
7 | "description": "Defines what the format version is"
8 | },
9 | "device": {
10 | "$ref": "definitions.json#/definitions/device"
11 | },
12 | "messages": {
13 | "minItems": 1,
14 | "type": "array",
15 | "items": {
16 | "type": "object",
17 | "description": "Collection of messages",
18 | "properties": {
19 | "code": {
20 | "$ref": "definitions.json#/definitions/code"
21 | },
22 | "description": {
23 | "type": "string",
24 | "description": "The description is used to describe the purpose of the message, e.g. the problem",
25 | "maxLength": 2000
26 | },
27 | "hint": {
28 | "type": "string",
29 | "description": "In case a problem is reported, the hint can be used to point out a possible solution",
30 | "maxLength": 2000
31 | },
32 | "origin": {
33 | "type": "string",
34 | "description": "The origin of the message if not the device identified by deviceID in the header element. Could be used to identify a subsystem or a particular sensor/part of the device where the message actually relates to."
35 | },
36 | "severity": {
37 | "type": "string",
38 | "description": "Severity of the message. It complements type for describing urgency and relevance of a message",
39 | "enum": [
40 | "HIGH",
41 | "MEDIUM",
42 | "LOW",
43 | "UNKNOWN"
44 | ],
45 | "default": "UNKNOWN"
46 | },
47 | "source": {
48 | "type": "string",
49 | "description": "The type of message. Default is DEVICE but can be set to TECHNICAL_INFO indicating a problem with the integration of the actual device. Allowed values: DEVICE, TECHNICAL_INFO",
50 | "enum": [
51 | "DEVICE",
52 | "TECHNICAL_INFO"
53 | ],
54 | "default": "DEVICE"
55 | },
56 | "state": {
57 | "type": "string",
58 | "enum": [
59 | "NEW",
60 | "ENDED"
61 | ],
62 | "description": "Determines the lifecycle state of the message"
63 | },
64 | "title": {
65 | "type": "string",
66 | "description": "Title of the message. If title not set the code will be stored as fallback",
67 | "maxLength": 1000
68 | },
69 | "ts": {
70 | "format": "date-time",
71 | "type": "string",
72 | "description": "Start time of the the data measurment in ISO 8601 format"
73 | },
74 | "type": {
75 | "type": "string",
76 | "description": "Describes the debug level of the message. It complements severity for describing urgency and relevance of a message",
77 | "enum": [
78 | "INFO",
79 | "WARNING",
80 | "ERROR",
81 | "UNKNOWN"
82 | ],
83 | "default": "UNKNOWN"
84 | },
85 | "additionalData": {
86 | "type": "object"
87 | }
88 | },
89 | "required": [
90 | "ts",
91 | "code"
92 | ]
93 | }
94 | }
95 | },
96 | "required": [
97 | "content-spec",
98 | "device",
99 | "messages"
100 | ]
101 | }
--------------------------------------------------------------------------------
/website/assets/styles.scss:
--------------------------------------------------------------------------------
1 | @import "assets/variables.scss";
2 | @import "~bulma/bulma";
3 | @import "~font-awesome/scss/font-awesome.scss";
4 | @import "~prismjs/themes/prism";
5 |
6 | body {
7 | overflow: visible;
8 | }
9 | @include touch {
10 | #__layout > div > .container {
11 | margin-left: $gap;
12 | margin-right: $gap;
13 | }
14 | }
15 |
16 | a {
17 | text-decoration: underline;
18 | &:hover {
19 | text-decoration: underline;
20 | }
21 | }
22 | .footer, .navbar, .sidebar {
23 | a {
24 | text-decoration: none;
25 | }
26 | }
27 | .title {
28 | font-weight: normal;
29 | }
30 |
31 | .router-link-active {
32 | font-weight: bold;
33 | }
34 | .is-loading {
35 | position: relative;
36 | * {
37 | pointer-events: none;
38 | opacity: 0.5;
39 | }
40 | &:after {
41 | @include loader;
42 | position: relative;
43 | width: 1em;
44 | position: absolute;
45 | top: 4em;
46 | left: calc(50% - 2.5em);
47 | width: 5em;
48 | height: 5em;
49 | border-width: 0.25em;
50 | }
51 | }
52 | .__nuxt-error-page {
53 | position: relative;
54 | }
55 | .accordion {
56 | margin-top: 2em;
57 | > :first-child {
58 | &, & > header {
59 | border-top-left-radius: $radius-large;
60 | border-top-right-radius: $radius-large;
61 | }
62 | }
63 | :last-child {
64 | & {
65 | border-bottom-left-radius: $radius-large;
66 | border-bottom-right-radius: $radius-large;
67 | }
68 | &.collapsed {
69 | & > header {
70 | border-bottom-left-radius: $radius-large;
71 | border-bottom-right-radius: $radius-large;
72 | }
73 | }
74 | }
75 | }
76 |
77 | /* prism */
78 | pre[class*="language-"] {
79 | background: $light;
80 | border-radius: $radius-large;
81 | border: 1px solid $grey-lighter;
82 | .number {
83 | align-items: inherit;
84 | background-color: inherit;
85 | border-radius: inherit;
86 | display: inherit;
87 | font-size: inherit;
88 | height: inherit;
89 | justify-content: inherit;
90 | margin-right: inherit;
91 | min-width: inherit;
92 | padding: inherit;
93 | text-align: inherit;
94 | vertical-align: inherit;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/website/blog/DZone-article-published.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: DZone article published
3 | date: 2017-10-06 00:00:00
4 | tags: "media"
5 | ---
6 | A new article about the Production Performance Management Protocol and the Eclipse Unide project was published at [DZone](https://dzone.com/articles/eclipse-unide-a-way-to-establish-an-open-industry)!
7 | It explains how to use the [the Production Performance Management Protocol validator](https://www.eclipse.org/unide/2017/06/29/Validator-Online/), gives an overview of the InfluxDB / Grafana visualisation and how this setup can be used in an Eclipse Testbed.
8 |
--------------------------------------------------------------------------------
/website/blog/New-process-message-spec.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: New Production Performance Management Protocol-Specification for processes
3 | date: 2017-06-01 00:00:00
4 | tags: "process message"
5 | ---
6 | We’re happy to announce, that the process message specification is online! You can find it at [http://www.eclipse.org/unide/specification](http://www.eclipse.org/unide/specification). With this type of messages you have a standard format for sending data out of discrete and possibly complex processes. 
7 |
--------------------------------------------------------------------------------
/website/blog/New-website.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Updated Website
3 | date: 2018-01-26 00:00:00
4 | tags: "media"
5 | ---
6 | It turned out that even with multiple reviews, it is possible to have small mistakes in the Production Performance Management Protocol documentation. In order to avoid such contradictions between diagrams, specification, json-schema and eventual implementation, we decided to rework the project structure and fixed [the JSON Schemas](https://github.com/eclipse/unide/issues/21). The corresponding uml diagrams are generated with [plantuml](http://plantuml.com/). For even better understandibility, the attributes are sorted lexicographically.
7 |
8 | Although the website has the same look, it is completely reworked as a [single-page application](https://en.wikipedia.org/wiki/Single-page_application) to generate the specification directly from the json-schema. Instead of using [hexo](https://hexo.io/) it is now based on [nuxt.js](https://nuxtjs.org/). [Vue.js](https://vuejs.org/) as a basis for nuxt is also used in the [new Production Performance Management Protocol client application called binsa](https://github.com/eclipse/unide/tree/master/clients/binsa).
9 |
--------------------------------------------------------------------------------
/website/blog/News-coverage.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: News Coverage
3 | date: 2016-09-27 09:14:27
4 | tags: "media"
5 | ---
6 | After the [Official launch](/unide/2016/09/21/Official%20launch/) of unide and the Production Performance Management Protocol, there was some media coverage about the project.
7 |
8 | Triggered by a statement by [Bosch CEO Volkmar Denner](http://www.bosch.com/en/com/bosch_group/board_management/dr_rer_nat_volkmar_denner/volkmar-denner.html) on *["initiating new machine language for Industry 4.0"](http://www.bosch-presse.de/pressportal/en/bosch-initiates-new-machine-language-for-industry-4-0-65216.html)*, twitter, blogs and magazines mention Production Performance Management Protocol, e.g.:
9 | * [electormagazine](https://www.elektormagazine.com/news/free-ppmp-from-bosch-makes-industry-4-0-open-for-all)
10 | * [computer-automation](http://www.computer-automation.de/feldebene/vernetzung/artikel/134233/) (de)
11 |
12 | Looking forward to spreading the word further.
13 |
--------------------------------------------------------------------------------
/website/blog/Official-launch.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Official launch
3 | date: 2016-09-21 09:47:43
4 | tags: "project start"
5 | ---
6 | Today, we officially launch Unide. In the last days, we have redesigned the logo, published Blog posts and started work on the initial (code) contributions.
7 | It even seems like good timing, since there was a [survey in Germany about open platforms & standards in industry 4.0](http://m.heise.de/newsticker/meldung/Umfrage-Unternehmen-fordern-offene-Plattform-fuer-die-Industrie-4-0-3327135.html), basically asking for something like Unide/the Production Performance Management Protocol.
8 |
9 | ### the logo
10 | Unide and the Production Performance Management Protocol's goal is to enable you to connect machines and receive their measurements / alerts. The logo represents that. It has you ("*U*") highlighted and connects to rings. As a side note, we found that the words almost look like *you-nice*.
11 |
12 | ### blog entries
13 | If you are interested in further readings, check out the post at [blog.bosch-si.com](http://blog.bosch-si.com).
14 |
15 | ### initial contributions
16 | We are working on creating the initial contributions for Unide. Since there are a few legal aspects we have to consider, stay tuned. The code will be published to our [github repository](https://github.com/eclipse/unide).
17 |
18 |
--------------------------------------------------------------------------------
/website/blog/Project-approved.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Eclipse Project approved
3 | date: 2016-09-16 12:16:37
4 | tags: "project start"
5 | ---
6 | Unide proposal has been approved to be an Eclipse Project! You can now find all relevant information at [https://projects.eclipse.org/projects/iot.unide](https://projects.eclipse.org/projects/iot.unide).
7 |
8 | There's still some paperwork pending, but it looks promising! Next steps are
9 | * creating a proper homepage with relevant information and the Production Performance Management Protocol specification
10 | * license checking of initial commitment code.
11 | * getting in touch with Eclipse community
12 | * making plans for the upcoming months
13 |
14 | If you are interested in industry 4.0, IoT stack in manufacturing or you are building industry machines and want to find out more about the possiblities with the Production Performance Management Protocol, let us know: [unide-inbox@eclipse.org](mailto:unide-inbox@eclipse.org?subject=Interested%20in%20contributing)
15 |
16 |
--------------------------------------------------------------------------------
/website/blog/Unide-at-BCX.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Unide at Bosch Hackaton
3 | date: 2017-03-23 17:00:00
4 | tags: "media"
5 | ---
6 | Unide and the Production Performance Management Protocol were used in Bosch Connected Experience Hackaton by students and developers to connect sensors to backend solutions in just two days!
7 | They even took a professional video:
8 |
9 | VIDEO
10 |
11 |
12 | Also in the same conference, Bosch CEO Dr. Volkmar Denner refered to the Production Performance Management Protocol again :
13 |
14 | On a more practical level, the Product Performance Management Protocol, or PPMP, is being tried out at the Bosch plant in Homburg, Germany, as part of a joint Industrie 4.0 and IIC testbed. Bosch itself developed the Production Performance Management Protocol, which is available to all at no cost. It allows small and medium-sized enterprises to transmit data from their sensors quickly, simply, and securely to the production systems of large companies. This helps remove some of the obstacles preventing entry into connected manufacturing. This first practical experience will be used to further refine the new protocol – work which will be done by Eclipse, an open-source community. Once again, we see that openness is our life blood.
15 | Dr. Volkmar Denner, Bosch CEO
16 |
17 |
18 |
--------------------------------------------------------------------------------
/website/blog/Unide-proposal-online.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Unide proposal online
3 | date: 2016-09-12 09:26:37
4 | tags: "project start"
5 | ---
6 | We're happy to announce, that the proposal for our new Eclipse Project is online! You can find it at [https://projects.eclipse.org/proposals/unide](https://projects.eclipse.org/proposals/unide). For further discussions, there's the [forum post](https://www.eclipse.org/forums/index.php/t/1080186/) as well.
7 | Now, it's time to study all details about Eclipse processes and [license regulations](https://eclipse.org/legal/eplfaq.php#3RDPARTY).
8 |
9 |
12 |
13 |
14 | The projects name is derived from un derstand i ndustry de vices. Other ideas like co nnect i ndustry ma chines turned out to have meanings in foreign languages or being trademarked already.
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/website/blog/Validator-Online.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: First Production Performance Management Protocol-Validator Online
3 | date: 2017-06-29 00:00:00
4 | tags: "validator"
5 | ---
6 | Our first Production Performance Management Protocol-Validator as a HTTP-server is online!
7 |
8 | What we already contribtuted as sourcecode to the Unide-project we've also installed on an eclipse sandbox server. Now you are able to validate your Production Performance Management Protocol-messages through the internet without running the server on your local machine. The intention is to make the latest specification validator accessible to everyone at anytime.
9 |
10 | You can reach the server by sending POST-requests to the following endpoints:
11 |
12 |
13 | http://unide.eclipse.org/rest/v2/message?validate=true
14 | http://unide.eclipse.org/rest/v2/measurement?validate=true
15 | http://unide.eclipse.org/rest/v2/process?validate=true
16 |
17 |
18 | Further functions of the sandbox server will be also visualization and storaging of incoming Production Performance Management Protocol-messages.
19 |
--------------------------------------------------------------------------------
/website/blog/Version-3.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Protocol Version 3 release candidate
3 | date: 2019-03-01 00:00:00
4 | tags: "v3"
5 | ---
6 |
7 | After a long discussion phase, the Production Performance Management Protocol version 3 is finally linked on the specification page. Special thanks to [bgusach](https://github.com/bgusach), [bf-bryants](https://github.com/bf-bryants), [muelsen](https://github.com/muelsen) and [alaendle](https://github.com/alaendle) for their contributions [via github issues](https://github.com/eclipse/unide/issues)!
8 | # The most important changes
9 | * `context` section in `measurements`\
10 | The optional context section ccontains information on how to interpret the measurements. This includes
11 | * the field '`type`' (Number, String or Boolean value) indicates which format the `` of a `series` have.
12 | * The `unit` key can be used to describe the unit of measurement. There are different understandings and standarizations for units ('C' stands for Coulomb, not Celsius), like [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units), [SenML](https://tools.ietf.org/html/draft-ietf-core-senml-14#section-12.1), [ISO 80000-1:2009](https://www.iso.org/obp/ui/#iso:std:iso:80000:-1:ed-1:v1:en), etc.. The `unit` key here is a string without further restriction. If that is needed, it can be specified via an URI in `namespace`.
13 | * `namespace` may contain an URI to identify further detail on the values in this measurement section. This can be a description of units, lengths or naming conventions for measurement points, but also a reference to a semantic model.
14 | * `series` can contain other than numeric measurements\
15 | if `context.type` contains any of 'BASE64', 'BOOLEAN', 'NUMBER', 'STRING', 'REF' or 'OTHER', the corresponding measurement in `series` are represented in this type ('REF', 'OTHER' and 'BASE64' are Strings). This way for example, even small base64 encoded binary data, as send from iolink devices, can be included. 'REF' could be used in multipart messages to reference to other sections or even external content.
16 | * `mode` and `state` are introduced in `device` and replaces v2's `operationalMode`, which was found to not clearly diffentiate the functional mode and technical state.
17 | * A common `id` was used and replaces different spellings in `deviceID`, `partID`, `program.id`.
18 | * `additionalData` in the various sections is used for any kind of not-specified data, that should be included in the telegram. Where v2's `metaData` had to be key/String pairs, `additionalData` can also be complex JSON objects.
19 | * `time` field replaces the v2 `$_time` field, which is difficult to represent in some programming languages. Note that `time` is not an Integer anymore but a JSON number, to allow sub-millisecond values.
20 | * no more `shutoffValues` and `shutoffPhase`\
21 | `shutoffValues` can be expressed as specialValues with a meaningful `name`, `shutoffPhase` is the measurement with the latest timestamp `ts`.
22 | * a common definition.json schema is used for sections that are used in multiple telegrams. With the help of JSON schema 'anyOf', sections inherit and can extend from these base definitions.
23 |
24 | # Next steps
25 | The most recent updates and this post should finalize the v3 schema. Vetos with suggestions that find a consensus fairly soon after posting are stil accepted [via github issues](https://github.com/eclipse/unide/issues). Major changes and suggestions should rather be addressed to a v4.
26 |
27 | The bindings and server should be updated to accept and validate v3 as well. After updating further dependencies, creating eclipse CQs and undergoing the release process, everything should be wrapped up in the unide release 0.3.0.
--------------------------------------------------------------------------------
/website/blog/i40-testbed-started.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Unide is part of Industry 4.0 Testbed
3 | date: 2017-10-19 00:00:00
4 | tags: "testbed"
5 | ---
6 | The Eclipse IoT Working group has started another testbed around Production Performance Management. Together with other Eclipse projects but also manufacturing companies, we aim to showcase and test the software in real life scenarios.
7 | [Contact Software](https://www.contact-software.com), [Eurotech](https://www.eurotech.com) and [Eclipse 4diac](https://www.eclipse.org/4diac/) provide bindings to actual machines, Unide and the Production Performance Management Protocol will be used for structuring and normalizing the information, [Ecipse Hono](https://projects.eclipse.org/projects/iot.hono) and other provide the advanced infrastructure and [influx data](https://www.influxdata.com/) persists the data.
8 | You can find more information in the [Press Release](https://eclipse.org/org/press-release/20171019_industry40_testbed.php) or the [homepage of the testbed](https://iot.eclipse.org/testbeds/production-performance-management/#).
9 |
--------------------------------------------------------------------------------
/website/components/blogArchives.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Blog Archives
5 |
6 |
7 |
8 |
9 | {{ post | timeStamp }}
10 |
11 | ({{ post.count }})
12 |
13 |
14 |
15 |
16 |
17 |
37 |
38 |
45 |
--------------------------------------------------------------------------------
/website/components/collapsibleCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
33 |
91 |
92 |
124 |
--------------------------------------------------------------------------------
/website/components/cookieCheck.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
This website uses cookies for reasons of functionality, convenience, and statistics. For information on deleting the cookies, please consult your browser’s help function.
8 |
Some Eclipse Foundation pages use cookies to better serve you when you return to the site. You can set your browser to notify you before you receive a cookie or turn off cookies. If you do so, however, some areas of some sites may not function properly. To read Eclipse Foundation Privacy Policy click here.
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
59 |
60 |
103 |
--------------------------------------------------------------------------------
/website/components/eclipseFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
28 |
39 |
--------------------------------------------------------------------------------
/website/components/navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
29 |
30 |
31 |
40 |
41 |
111 |
--------------------------------------------------------------------------------
/website/components/recentPosts.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Recent Posts
5 |
6 |
7 |
8 |
9 | {{ post.title }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
27 |
28 |
35 |
--------------------------------------------------------------------------------
/website/components/schemaLink.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | yes
6 |
7 |
8 | no
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {{ key }}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
46 |
--------------------------------------------------------------------------------
/website/components/schemaToc.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
35 |
36 |
42 |
--------------------------------------------------------------------------------
/website/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Home
6 |
7 |
8 | Proposal
9 |
10 |
11 | Specification
12 |
13 |
14 | Blog Archives
15 |
16 |
17 | FAQ
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
--------------------------------------------------------------------------------
/website/layouts/sidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Home
6 |
7 |
8 | Proposal
9 |
10 |
11 | Specification
12 |
13 |
14 | Blog Archives
15 |
16 |
17 | FAQ
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
50 |
51 |
62 |
--------------------------------------------------------------------------------
/website/nuxt.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | head: {
3 | title: 'Welcome',
4 | titleTemplate: 'Eclipse unide - %s',
5 | meta: [{
6 | charset: 'utf-8'
7 | }, {
8 | name: 'viewport',
9 | content: 'width=device-width, initial-scale=1'
10 | }, {
11 | hid: 'description',
12 | name: 'description',
13 | content: 'Eclipse Unide: Understand Industry devices'
14 | }],
15 | link: [
16 | { rel: 'icon', type: 'image/x-icon', href: '/unide/favicon.ico' }
17 | ]
18 | },
19 | css: [{
20 | src: '~assets/styles.scss',
21 | lang: 'scss'
22 | }],
23 | render: {
24 | resourceHints: false
25 | },
26 | loading: { color: '#50237f' },
27 | build: {
28 | extend(config, ctx) {
29 | if(ctx.isClient && ctx.isDev) {
30 | // only fails in client.js in dev mode, so no real need for:
31 | /* if(!(config.entry.app instanceof Array)) {
32 | config.entry.app = [ config.entry.app ];
33 | }
34 | config.entry.app.unshift('core-js/fn/array/filter'); */
35 | config.module.rules.push({
36 | enforce: 'pre',
37 | test: /\.(js|vue)$/,
38 | loader: 'eslint-loader',
39 | exclude: /(node_modules)/
40 | });
41 | }
42 | config.module.rules.unshift({
43 | type: 'javascript/auto',
44 | test: /schema\.json$/,
45 | exclude: /node_modules/,
46 | loader: 'json-schema-loader',
47 | options: {}
48 | });
49 | },
50 | postcss: {
51 | plugins: {
52 | 'postcss-custom-properties': false
53 | }
54 | },
55 | extractCSS: true,
56 | publicPath: '/files/'
57 | },
58 |
59 | plugins: [{
60 | src: '~/plugins/prismjs.js'
61 | }],
62 |
63 | generate: {
64 | routes: ['machine', 'measurement', 'process'].map((name) => `/specification/${name}-message`)
65 | },
66 | router: {
67 | base: '/unide/',
68 | scrollBehavior({ hash:selector }, from, savedPosition) {
69 | if(savedPosition) {
70 | return savedPosition;
71 | } else {
72 | return selector ? { selector } : {};
73 | }
74 | },
75 | extendRoutes(routes, resolve) {
76 | // alias for default PPMP version
77 | ['machine', 'measurement', 'process'].forEach((name) => routes.push({
78 | path: `/specification/${name}-message`,
79 | redirect: `/specification/v3/${name}-message`
80 | }));
81 | }
82 | //, fallback: true
83 | },
84 |
85 | modules: [['~modules/postsIdxPlugin', {}]]
86 | };
87 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "unide",
3 | "version": "1.0.0",
4 | "description": "Eclipse Unide: Understand Industry devices",
5 | "author": "Axel Meinhardt ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "nuxt",
9 | "build": "nuxt build",
10 | "postinstall": "npm run generate",
11 | "start": "nuxt start",
12 | "generate": "nuxt generate",
13 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
14 | "precommit": "npm run lint",
15 | "uml": "java -Djava.awt.headless=true -jar plantuml.jar -charset utf8 -o . -tsvg static/images/specification/*/*.uml"
16 | },
17 | "dependencies": {
18 | "@vue/eslint-config-standard": "^4.0.0",
19 | "axios": "^0.18.0",
20 | "babel-runtime": "^6.26.0",
21 | "bulma": "^0.6.1",
22 | "font-awesome": "^4.7.0",
23 | "front-matter": "^2.3.0",
24 | "json-schema-loader": "git://github.com/joshheyse/json-schema-loader#649e1d4",
25 | "lodash": "^4.17.11",
26 | "markdown-it": "^8.4.2",
27 | "markdown-it-decorate": "^1.2.2",
28 | "nuxt": "^2.4.5",
29 | "prismjs": "^1.15.0",
30 | "tiny-cookie": "^2.1.2",
31 | "vue-prism-component": "^1.1.1",
32 | "webpack-virtual-modules": "^0.1.10"
33 | },
34 | "devDependencies": {
35 | "babel-eslint": "^10.0.1",
36 | "eslint": "^5.14.1",
37 | "eslint-config-standard": "^12.0.0",
38 | "eslint-config-vue": "^2.0.2",
39 | "eslint-loader": "^2.1.2",
40 | "eslint-plugin-babel": "^5.3.0",
41 | "eslint-plugin-html": "^5.0.3",
42 | "eslint-plugin-import": "^2.16.0",
43 | "eslint-plugin-node": "^8.0.1",
44 | "eslint-plugin-promise": "^4.0.1",
45 | "eslint-plugin-standard": "^4.0.0",
46 | "eslint-plugin-vue": "^5.2.2",
47 | "node-sass": "^4.14.1",
48 | "sass-loader": "^7.1.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/website/pages/article.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ post.title }}
5 |
6 |
7 |
8 | {{ post.date | timeStamp }}
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{ post.prev.title }}
20 |
21 |
22 |
23 |
24 | {{ post.next.title }}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
67 |
68 |
128 |
--------------------------------------------------------------------------------
/website/pages/blog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ dateHeader }}
5 |
6 |
7 |
8 |
9 |
10 | {{ item.title }}
11 |
12 |
13 |
14 |
15 |
16 | {{ item.date | timeStamp($route.params) }}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
88 |
89 |
108 |
--------------------------------------------------------------------------------
/website/pages/specification/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Communicating Parties
6 |
7 |
8 | Two parties are involved in a Production Performance Management Protocol message exchange: the sender and the receiver. The sender pushes a message to the receiver. Usually the sender is a machine or a sensor of a machine.
9 |
10 |
11 | The receiver waits for messages. It offers a API that allows for sending either measurement payloads or message payloads.
12 |
13 |
14 | The communication is unidirectional. Only the sender can contact the receiver and send messages. No feedback from receiver to sender is provided.
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/website/plugins/prismjs.js:
--------------------------------------------------------------------------------
1 | import 'prismjs';
2 | import 'prismjs/components/prism-json';
3 |
--------------------------------------------------------------------------------
/website/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/favicon.ico
--------------------------------------------------------------------------------
/website/static/images/analysis.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/analysis.jpg
--------------------------------------------------------------------------------
/website/static/images/blog/Transform-PPMP-with-camel-1.uml:
--------------------------------------------------------------------------------
1 | @startuml
2 |
3 | skinparam activity {
4 | StartColor #78be20
5 | BorderColor black
6 | FontColor #50237f
7 | BackgroundColor #f5f5f5
8 | ArrowColor black
9 | EndColor #50237f
10 | }
11 |
12 | partition PRC7000 {
13 | (*) ->[sensor\nvalues] "broadcast data" as send
14 | }
15 |
16 | partition mosquitto {
17 | send -down->[MQTT] "queue data"
18 | }
19 |
20 | partition Camel {
21 | "queue data" -up->[MQTT] "retrieve data" as queue
22 | queue -right->[PRC7000\nformat] "transform payload" as transform
23 | transform ->[PPMP format\nbundle] "split payload" as split
24 | split -->[measurements] "Transform to JSON" as measurementString
25 | split -->[process] "Transform to JSON" as processString
26 |
27 | measurementString -->[String] "sent do backend" as measurementPOST
28 | processString -->[String] "sent do backend" as processPOST
29 | }
30 |
31 | partition "REST Server" {
32 | measurementPOST -->[HTTP POST] "receive data"
33 | processPOST -up>[HTTP POST] "receive data"
34 | "receive data" -up-> "validate message"
35 | "validate message" -up-> "save to db"
36 | "save to db" -> (*)
37 | }
38 |
39 | @enduml
40 |
41 |
--------------------------------------------------------------------------------
/website/static/images/blog/Transform-PPMP-with-camel-2-eclipse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/blog/Transform-PPMP-with-camel-2-eclipse.png
--------------------------------------------------------------------------------
/website/static/images/blog/Transform-PPMP-with-camel-2-logfile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/blog/Transform-PPMP-with-camel-2-logfile.png
--------------------------------------------------------------------------------
/website/static/images/blog/Transform-PPMP-with-camel-camel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/blog/Transform-PPMP-with-camel-camel.png
--------------------------------------------------------------------------------
/website/static/images/blog/Transform-PPMP-with-camel-welding.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/blog/Transform-PPMP-with-camel-welding.jpg
--------------------------------------------------------------------------------
/website/static/images/blog/grinding-machine-ce4iot-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/blog/grinding-machine-ce4iot-dashboard.png
--------------------------------------------------------------------------------
/website/static/images/blog/grinding-machine-grafana-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/blog/grinding-machine-grafana-dashboard.png
--------------------------------------------------------------------------------
/website/static/images/collaborators_v2.uml:
--------------------------------------------------------------------------------
1 | @startuml
2 | skinparam interface {
3 | BackgroundColor #78be20
4 | FontColor #50237f
5 | BorderColor black
6 | }
7 |
8 | skinparam node {
9 | FontColor #50237f
10 | }
11 |
12 | skinparam component {
13 | BorderColor black
14 | FontColor #50237f
15 | BackgroundColor #f5f5f5
16 | ArrowColor black
17 | }
18 |
19 | () "/process" as p
20 | () "/message" as m
21 | () "/measurement" as s
22 |
23 | node "PPMP Producer / Machine" {
24 | [Information / Alarms]
25 | [Sensor values]
26 | [End-to-end process data]
27 | }
28 | node "PPMP Consumer" {
29 | [Machine Messages]
30 | [Measurements]
31 | [Processs]
32 | }
33 |
34 | [Information / Alarms] --> m
35 | m --> [Machine Messages]
36 | [Sensor values] --> s
37 | s --> [Measurements]
38 | [End-to-end process data] --> p
39 | p --> [Processs]
40 | @enduml
41 |
--------------------------------------------------------------------------------
/website/static/images/eclipse-426x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/eclipse-426x100.png
--------------------------------------------------------------------------------
/website/static/images/languages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/languages.png
--------------------------------------------------------------------------------
/website/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/logo.png
--------------------------------------------------------------------------------
/website/static/images/machines.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipse-archived/unide/6cc1a46fbff5fc384a856d390cee660e8fedea16/website/static/images/machines.jpg
--------------------------------------------------------------------------------
/website/static/images/specification/v3/definitions.iuml:
--------------------------------------------------------------------------------
1 | @startuml
2 |
3 | skinparam class {
4 | BorderColor black
5 | FontColor #50237f
6 | BackgroundColor #f5f5f5
7 | ArrowColor black
8 | }
9 | hide class circle
10 |
11 | skinparam stereotype {
12 | CBackgroundColor white
13 | EBackgroundColor white
14 | }
15 |
16 | skinparam legend {
17 | BorderColor white
18 | BackgroundColor white
19 | }
20 |
21 | class Device {
22 | id: String
23 | mode[0..1]: String
24 | state[0..1]: DeviceState
25 | additionalData[0..1]: Object
26 | }
27 |
28 | class Measurement {
29 | code[0..1]: String
30 | context[0..1]: ContextList
31 | result[0..1]: Result
32 | series: Series
33 | ts: Date
34 | additionalData[0..1]: Object
35 | }
36 |
37 | class Context {
38 | limits[0..1]: Limits
39 | namespace[0..1]: String
40 | type[0..1]: ValueType
41 | unit[0..1]: String
42 | additionalData[0..1]: Object
43 | }
44 |
45 | class Limits {
46 | lowerError[0..1]: Float | [Float]
47 | lowerWarn[0..1]: Float | [Float]
48 | target[0..1]: Float | [Float]
49 | upperError[0..1]: Float | [Float]
50 | upperWarn[0..1]: Float | [Float]
51 | }
52 |
53 | class Series {
54 | time[0..1]: [Number]
55 | [1..*]: [Boolean | Number | String]
56 | }
57 |
58 | class Part {
59 | code[0..1]: String
60 | id[0..1]: String
61 | type[0..1]: PartType
62 | typeId[0..1]: String
63 | result[0..1]: Result
64 | additionalData[0..1]: Object
65 | }
66 |
67 | enum DeviceState {
68 | OK
69 | INFO
70 | WARN
71 | ERROR
72 | UNKNOWN
73 | }
74 |
75 | enum Result {
76 | OK
77 | NOK
78 | UNKNOWN
79 | }
80 |
81 | enum PartType {
82 | SINGLE
83 | BATCH
84 | }
85 |
86 | enum ValueType {
87 | BASE64
88 | BOOLEAN
89 | NUMBER
90 | OTHER
91 | REF
92 | STRING
93 | }
94 |
95 | Measurement "1" *-- "0..1" Context : ContextList
96 | Context "1" -- "0..*" Limits
97 |
98 |
99 | sprite $aggregation jar:archimate/aggregation
100 | sprite $composition jar:archimate/composition
101 | sprite $specialisation jar:archimate/specialisation
102 | sprite $association jar:archimate/association
103 | sprite $object jar:archimate/object
104 |
105 | legend right
106 |
107 | endlegend
108 |
109 | @enduml
--------------------------------------------------------------------------------
/website/static/images/specification/v3/measurementPayload.uml:
--------------------------------------------------------------------------------
1 | @startuml
2 | !include definitions.iuml
3 |
4 | class MeasurementPayload {
5 | content-spec : String
6 | device : Device
7 | measurements : TimeMeasurement
8 | part[0..1] : Part
9 | }
10 |
11 | class TimeMeasurement {
12 | code[0..1]: String
13 | context[0..1]: ContextList
14 | result[0..1]: Result
15 | series: TimeSeries
16 | ts: Date
17 | additionalData[0..1]: Object
18 | }
19 |
20 | class TimeSeries {
21 | time: [Number]
22 | [1..*]: [Boolean | Number | String]
23 | }
24 |
25 | hide Measurement
26 | hide Series
27 |
28 | TimeMeasurement "1" *-- "0..1" Context : ContextList
29 |
30 | MeasurementPayload -- Device
31 | MeasurementPayload -- Part
32 | MeasurementPayload "1" o-right- "1..*" TimeMeasurement : Measurements
33 |
34 | Series <|-- TimeSeries
35 |
36 | TimeMeasurement -- TimeSeries
37 |
38 | @enduml
--------------------------------------------------------------------------------
/website/static/images/specification/v3/messagePayload.uml:
--------------------------------------------------------------------------------
1 | @startuml
2 | !include definitions.iuml
3 |
4 | class MessagePayload {
5 | content-spec : String
6 | device : Device
7 | messages : Messages
8 | }
9 |
10 | class Message {
11 | code : String
12 | description[0..1] : String
13 | hint[0..1] : String
14 | origin[0..1] : String
15 | severity[0..1] : Severity
16 | source[0..1] : Source
17 | state[0..1] : State
18 | title[0..1] : String
19 | ts : Date
20 | type[0..1] : Type
21 | additionalData[0..1]: Object
22 | }
23 |
24 | enum Severity {
25 | HIGH
26 | MEDIUM
27 | LOW
28 | UNKNOWN
29 | }
30 |
31 | enum State {
32 | NEW
33 | ENDED
34 | }
35 |
36 | enum Source {
37 | DEVICE
38 | TECHNICAL_INFO
39 | }
40 |
41 | enum Type {
42 | INFO
43 | WARNING
44 | ERROR
45 | UNKNOWN
46 | }
47 |
48 | MessagePayload -- Device
49 | MessagePayload "1" o-- "1..*" Message : Messages
50 |
51 | hide Context
52 | hide Limits
53 | hide Measurement
54 | hide Part
55 | hide PartType
56 | hide Result
57 | hide Series
58 | hide ValueType
59 |
60 | Context "1" -left- "0..*" Limits
61 |
62 | @enduml
--------------------------------------------------------------------------------
/website/static/images/specification/v3/processPayload.uml:
--------------------------------------------------------------------------------
1 | @startuml
2 | !include definitions.iuml
3 |
4 | class ProcessPayload {
5 | content-spec: String
6 | device: Device
7 | measurements: ProcessMeasurement
8 | part[0..1]: Part
9 | process: Process
10 | }
11 |
12 | class Process {
13 | externalId[0..1]: String
14 | program[0..1]: Program
15 | result[0..1]: Result
16 | ts: Date
17 | additionalData[0..1]: Object
18 | }
19 |
20 | class ProcessMeasurement {
21 | code[0..1]: String
22 | context[0..1]: ContextList
23 | name[0..1]: String
24 | phase[0..1] ; String
25 | result[0..1]: Result
26 | series: Series
27 | specialValues[0..1]: SpecialValues
28 | ts: Date
29 | additionalData[0..1]: Object
30 | }
31 |
32 | class Program {
33 | id: String
34 | lastChangeDate[0..1]: Date
35 | name[0..1]: String
36 | additionalData[0..1]: Object
37 | }
38 |
39 | class SpecialValue {
40 | time[0..1]: Number
41 | name[0..1]: String
42 | value:
43 | }
44 |
45 | hide Measurement
46 |
47 | ProcessPayload -- Device
48 | ProcessPayload -- Part
49 | ProcessPayload -- Process
50 | ProcessPayload "1" o-- "0..*" ProcessMeasurement: Measurements
51 |
52 | Process -- Program
53 |
54 | ProcessMeasurement -- Series
55 | ProcessMeasurement "1" *-- "0..1" Context : ContextList
56 |
57 | ProcessMeasurement "1" o-- "0..*" SpecialValue: SpecialValues
58 | @enduml
59 |
--------------------------------------------------------------------------------