├── .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 | ![Unide logo](website/static/logo.svg) 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 | 32 | 33 | 90 | 91 | 116 | 117 | -------------------------------------------------------------------------------- /clients/binsa/src/components/dataConcent.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 31 | 32 | -------------------------------------------------------------------------------- /clients/binsa/src/components/login.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 97 | -------------------------------------------------------------------------------- /clients/binsa/src/components/messageForm.vue: -------------------------------------------------------------------------------- 1 | 65 | 66 | 111 | 112 | 126 | -------------------------------------------------------------------------------- /clients/binsa/src/components/selectLang.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 56 | 57 | 60 | -------------------------------------------------------------------------------- /clients/binsa/src/components/url.vue: -------------------------------------------------------------------------------- 1 | 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 | 6 | 7 | 16 | 17 | 44 | -------------------------------------------------------------------------------- /clients/binsa/src/pages/configuration/preferences.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 65 | 66 | 75 | -------------------------------------------------------------------------------- /clients/binsa/src/pages/home.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | -------------------------------------------------------------------------------- /clients/binsa/src/pages/notFound.vue: -------------------------------------------------------------------------------- 1 | 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. ![alt text](/unide/images/processPayload.png) 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 | 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 |
10 |

 Did you know?

11 |
12 |
13 |
14 | The projects name is derived from understand industry devices. Other ideas like connect industry machines 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 | 16 | 17 | 37 | 38 | 45 | -------------------------------------------------------------------------------- /website/components/collapsibleCard.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 91 | 92 | 124 | -------------------------------------------------------------------------------- /website/components/cookieCheck.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 59 | 60 | 103 | -------------------------------------------------------------------------------- /website/components/eclipseFooter.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 39 | -------------------------------------------------------------------------------- /website/components/navbar.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 40 | 41 | 111 | -------------------------------------------------------------------------------- /website/components/recentPosts.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 27 | 28 | 35 | -------------------------------------------------------------------------------- /website/components/schemaLink.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 46 | -------------------------------------------------------------------------------- /website/components/schemaToc.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 35 | 36 | 42 | -------------------------------------------------------------------------------- /website/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 39 | -------------------------------------------------------------------------------- /website/layouts/sidebar.vue: -------------------------------------------------------------------------------- 1 | 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 | 33 | 34 | 67 | 68 | 128 | -------------------------------------------------------------------------------- /website/pages/blog.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 88 | 89 | 108 | -------------------------------------------------------------------------------- /website/pages/specification/index.vue: -------------------------------------------------------------------------------- 1 | 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 | --------------------------------------------------------------------------------