├── .gitignore ├── LICENSE ├── README.md ├── THIRD-PARTY ├── cdk8s ├── .gitignore ├── __snapshots__ │ └── main.test.ts.snap ├── cdk8s.yaml ├── help ├── imports │ └── k8s.ts ├── jest.config.js ├── main.test.ts ├── main.ts ├── package-lock.json ├── package.json ├── signalmeshpod.yaml ├── tsconfig.json └── vsim.ts ├── cloud ├── .projen │ ├── deps.json │ ├── files.json │ └── tasks.json ├── .projenrc.py ├── README.md ├── app.py ├── cdk-outputs.json ├── cdk.json ├── data │ └── my_model.dbc ├── fast-campaign.json ├── requirements-dev.txt ├── requirements.txt └── src │ ├── __init__.py │ └── main.py ├── cloud9 ├── .gitignore ├── app.py ├── cdk.json ├── requirements.txt ├── src │ ├── __init__.py │ ├── lambda.py │ └── mainstack.py └── synth.sh ├── dbc └── mymodel.dbc ├── docs ├── architecture.png ├── createstack.png ├── preview menu.png └── qrcode.png ├── scripts ├── build-vsim.sh ├── deploy-cdk8s.sh ├── deploy-cloud.sh ├── deploy-docker.sh ├── deploy-k3s.sh ├── resize-c9.sh └── resize.sh └── vsim ├── .gitignore ├── Dockerfile ├── api ├── .flaskenv ├── api.py ├── canigen.py ├── cansim.py ├── hscan.dbc ├── mymodel.dbc ├── requirements.txt └── start-api.sh ├── package.json ├── public ├── Slide1.jpg ├── car.png ├── index.html ├── manifest.json └── robots.txt ├── src ├── App.css ├── App.js ├── App.test.js ├── Demo.js ├── Gallery.js ├── TopNav.js ├── index.css ├── index.js ├── reportWebVitals.js └── setupTests.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | node_modules/ 3 | !/.gitattributes 4 | !/.projen/tasks.json 5 | !/.projen/deps.json 6 | !/.projen/files.json 7 | !/.github/workflows/pull-request-lint.yml 8 | /.env 9 | !/requirements.txt 10 | !/requirements-dev.txt 11 | __pycache__/ 12 | *.py[cod] 13 | *$py.class 14 | *.so 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | *.manifest 34 | *.spec 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | htmlcov/ 38 | .tox/ 39 | .nox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | *.py,cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | cover/ 50 | *.mo 51 | *.pot 52 | *.log 53 | local_settings.py 54 | db.sqlite3 55 | db.sqlite3-journal 56 | instance/ 57 | .webassets-cache 58 | .scrapy 59 | docs/_build/ 60 | .pybuilder/ 61 | target/ 62 | .ipynb_checkpoints 63 | profile_default/ 64 | ipython_config.py 65 | __pypackages__/ 66 | celerybeat-schedule 67 | celerybeat.pid 68 | *.sage.py 69 | .env 70 | .venv 71 | env/ 72 | venv/ 73 | ENV/ 74 | env.bak/ 75 | venv.bak/ 76 | .spyderproject 77 | .spyproject 78 | .ropeproject 79 | /site 80 | .mypy_cache/ 81 | .dmypy.json 82 | dmypy.json 83 | .pyre/ 84 | .pytype/ 85 | 86 | cython_debug/ 87 | !/cdk.json 88 | /cdk.out/ 89 | .cdk.staging/ 90 | !/app.py 91 | /cloud/.venv/ 92 | /cloud/cdk.out/ 93 | .DS_Store 94 | certificate.pem 95 | private-key.key 96 | .tmp 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to the SOAFEE AWS IoT Fleetwise demo 2 | 3 | The repository contains the instructions and code that allows you to reproduce the demo shown in the session [**Achieving environmental parity through multiple layers of abstractions helps to support AWS IoT FleeWise edge on EWAOL**](https://www.youtube.com/watch?v=Wd1isAmTtp8) at [SOAFEE Virtual Symposium 2022](https://soafee.io/blog/2022/virtual_symposium/). 4 | 5 | The demo will walk you through the exercise of running on [EWAOL](https://github.com/aws4embeddedlinux/meta-aws-ewaol) the [AWS IoT FleeWise](https://aws.amazon.com/iot-fleetwise/) Edge. The [AWS IoT FleeWise Edge](https://github.com/aws/aws-iot-fleetwise-edge) will run in a [container](https://gallery.ecr.aws/aws-iot-fleetwise-edge/aws-iot-fleetwise-edge) and the orchestration will be done with [k3s](https://k3s.io/). We can use the exact same container image both in the cloud and on a physical target, as long as they are based on an ARM v8 core. We also show how the [cdk8s](https://cdk8s.io/) framework can improve the software development process abstracting the orchestration layer. 6 | 7 | ![Architecture](docs/architecture.png) 8 | 9 | ## Getting started 10 | 11 | Deploy Cloud 9 in one of the supported regions 12 | 13 | [![Launch](https://samdengler.github.io/cloudformation-launch-stack-button-svg/images/us-east-1.svg)](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?stackName=demo-soafee-aws-iotfleetwise-cloud9&templateURL=https://demo-soafee-aws-iot-fleetwise-eu-central-1.s3.eu-central-1.amazonaws.com/cloud9-env.template.json) 14 | 15 | [![Launch](https://samdengler.github.io/cloudformation-launch-stack-button-svg/images/eu-central-1.svg)](https://eu-central-1.console.aws.amazon.com/cloudformation/home?region=eu-central-1#/stacks/create/review?stackName=demo-soafee-aws-iotfleetwise-cloud9&templateURL=https://demo-soafee-aws-iot-fleetwise-eu-central-1.s3.eu-central-1.amazonaws.com/cloud9-env.template.json) 16 | 17 | Acknowledge the creation of the stack and press the button **Create stack** on the bottom right. 18 | 19 | ![Create Stack](docs/createstack.png) 20 | 21 | The ```demo-soafee-aws-iotfleetwise-cloud9``` CloudFormation stack will take about **3 minutes** to be created. 22 | 23 | When stack creation has finished, [open Cloud9](https://console.aws.amazon.com/cloud9/home#) and, in a terminal, run the following script to create the cdk stack that will deploy all the cloud resources as shown on the architecture above 24 | 25 | ```sh 26 | cd ~/environment/demo-soafee-aws-iotfleetwise 27 | ./scripts/resize-c9.sh 20 28 | ./scripts/deploy-cloud.sh 29 | ``` 30 | 31 | The above commands will take about **10 minutes** to complete. While you wait we encorage you to appreciate how the cloud resources gets deployed through [CDK](https://aws.amazon.com/cdk/) leveraging the [AWS IoT FleetWise Construct Library](https://github.com/aws-samples/cdk-aws-iotfleetwise) having a look to [this file](https://github.com/aws-samples/demo-soafee-aws-iotfleetwise/blob/main/cloud/src/main.py). 32 | 33 | ### Get AWS FleetWise Edge running on the Build Host 34 | 35 | This is the quickest option to see AWS IoT FleetWise running without having to build the EWAOL AMI, as detailed in the next paragraph. 36 | 37 | We will be using the same orchestrator (k3s) used in EWAOL, so let's get started by installing it 38 | 39 | ```sh 40 | curl -sfL https://get.k3s.io | sh - 41 | sudo ln -s /usr/local/bin/kubectl /usr/bin/kubectl 42 | ``` 43 | 44 | Build the Vehicle Simulator container image that will feeding signals on the CAN Bus Data where the AWS IoT FleetWise Edge is listening on 45 | 46 | ```sh 47 | sudo ./scripts/build-vsim.sh 48 | ``` 49 | 50 | Load certificate and private key for the vehicle into k3s secrets. These have been created by ```./scripts/deploy-cloud.sh``` above 51 | 52 | ```sh 53 | sudo /usr/local/bin/kubectl create secret generic private-key --from-file=./.tmp/private-key.key 54 | sudo /usr/local/bin/kubectl create secret generic certificate --from-file=./.tmp/certificate.pem 55 | ``` 56 | 57 | Deploy the kubernetes manifest to k3s 58 | 59 | ```sh 60 | ./scripts/deploy-k3s.sh 61 | ``` 62 | The script shows you the AWS IoT FleetWise Edge log. If you stop the script with CTRL+C, this will terminate the containers. As such, if you want to run other commands without stopping the containers, open another terminal. 63 | 64 | Now you can connect to the Vehicle Simulator Webapp opening the Cloud9 preview 65 | 66 | ![Preview menu](docs/preview%20menu.png) 67 | 68 | You can try changing things such as opening/closing the vehicle doors and observe how the data signals values are fed into our Amazon Timestream table. You can either use [Amazon Timestream console](https://console.aws.amazon.com/timestream/home?#query-editor:) to run the query or you can paste the command below in one of the Cloud9 terminals. 69 | 70 | ```sh 71 | aws timestream-query query --query-string \ 72 | "SELECT * FROM FleetWise.FleetWise WHERE time > ago(5m) ORDER BY time DESC LIMIT 2" \ 73 | | jq -r '.Rows[].Data[].ScalarValue' 74 | ``` 75 | 76 | Please note that DoorsState is encoded on 5 bits and the value associated with each door is shown below. The left and right reference are from the point of view of facing the road ahead, while seated inside the vehicle. 77 | 78 | |Value|Door| 79 | |-|-| 80 | |1|Front left| 81 | |2|Front right| 82 | |4|Rear left| 83 | |8|Rear right| 84 | |16|Vehicle trunk lid| 85 | 86 | ### Get AWS FleetWise Edge running on an EWAOL Virtual Target 87 | 88 | > :warning: THIS SECTION IS UNDER CONSTRUCTION, STAY TUNED !!! 89 | 90 | ### Get AWS FleetWise Edge running on an EWAOL Physical Target 91 | 92 | > :warning: THIS SECTION IS UNDER CONSTRUCTION, STAY TUNED !!! 93 | 94 | ### Cleanup 95 | 96 | From [CloudFormation](https://console.aws.amazon.com/cloudformation/home) just delete `demo-soafee-aws-iotfleetwise` and `demo-soafee-aws-iotfleetwise-cloud9` stacks. 97 | 98 | --- 99 | 100 | This repository depends on and may incorporate or retrieve a number of third-party 101 | software packages (such as open source packages) at install-time or build-time 102 | or run-time ("External Dependencies"). The External Dependencies are subject to 103 | license terms that you must accept in order to use this package. If you do not 104 | accept all of the applicable license terms, you should not use this package. We 105 | recommend that you consult your company’s open source approval policy before 106 | proceeding. 107 | 108 | Provided below is a list of External Dependencies and the applicable license 109 | identification as indicated by the documentation associated with the External 110 | Dependencies as of Amazon's most recent review. 111 | 112 | THIS INFORMATION IS PROVIDED FOR CONVENIENCE ONLY. AMAZON DOES NOT PROMISE THAT 113 | THE LIST OR THE APPLICABLE TERMS AND CONDITIONS ARE COMPLETE, ACCURATE, OR 114 | UP-TO-DATE, AND AMAZON WILL HAVE NO LIABILITY FOR ANY INACCURACIES. YOU SHOULD 115 | CONSULT THE DOWNLOAD SITES FOR THE EXTERNAL DEPENDENCIES FOR THE MOST COMPLETE 116 | AND UP-TO-DATE LICENSING INFORMATION. 117 | 118 | YOUR USE OF THE EXTERNAL DEPENDENCIES IS AT YOUR SOLE RISK. IN NO EVENT WILL 119 | AMAZON BE LIABLE FOR ANY DAMAGES, INCLUDING WITHOUT LIMITATION ANY DIRECT, 120 | INDIRECT, CONSEQUENTIAL, SPECIAL, INCIDENTAL, OR PUNITIVE DAMAGES (INCLUDING 121 | FOR ANY LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS OR DATA, OR 122 | COMPUTER FAILURE OR MALFUNCTION) ARISING FROM OR RELATING TO THE EXTERNAL 123 | DEPENDENCIES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, EVEN 124 | IF AMAZON HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS 125 | AND DISCLAIMERS APPLY EXCEPT TO THE EXTENT PROHIBITED BY APPLICABLE LAW. 126 | 127 | 128 | vsim/Dockerfile depends on third party **docker/library/node** container image, please refer to [license section](https://gallery.ecr.aws/docker/library/node) 129 | 130 | vsim/Dockerfile depends on third party **docker/library/python** container image, please refer to [license section](https://gallery.ecr.aws/docker/library/python) 131 | -------------------------------------------------------------------------------- /THIRD-PARTY: -------------------------------------------------------------------------------- 1 | The demo-soafee-aws-iotfleetwise includes the following third-party software/licensing: 2 | 3 | vsim/public/car.png from https://publicdomainvectors.org/en/free-clipart/Top-view-car-vector/2260.html 4 | terms: https://creativecommons.org/publicdomain/zero/1.0/ 5 | 6 | ---------------- -------------------------------------------------------------------------------- /cdk8s/.gitignore: -------------------------------------------------------------------------------- 1 | *.d.ts 2 | *.js 3 | !jest.config.js 4 | node_modules 5 | dist/ 6 | -------------------------------------------------------------------------------- /cdk8s/__snapshots__/main.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Placeholder Empty 1`] = ` 4 | Array [ 5 | Object { 6 | "apiVersion": "v1", 7 | "kind": "Pod", 8 | "metadata": Object { 9 | "labels": Object { 10 | "cdk8s.io/metadata.addr": "test-chart-Pod-c823839a", 11 | }, 12 | "name": "test-chart-pod-c83f3b56", 13 | }, 14 | "spec": Object { 15 | "automountServiceAccountToken": false, 16 | "containers": Array [ 17 | Object { 18 | "image": "vsim", 19 | "imagePullPolicy": "Always", 20 | "name": "main", 21 | "resources": Object { 22 | "limits": Object { 23 | "cpu": "1500m", 24 | "memory": "2048Mi", 25 | }, 26 | "requests": Object { 27 | "cpu": "1000m", 28 | "memory": "512Mi", 29 | }, 30 | }, 31 | "securityContext": Object { 32 | "allowPrivilegeEscalation": false, 33 | "privileged": false, 34 | "readOnlyRootFilesystem": true, 35 | "runAsGroup": 26000, 36 | "runAsNonRoot": true, 37 | "runAsUser": 25000, 38 | }, 39 | }, 40 | Object { 41 | "env": Array [ 42 | Object { 43 | "name": "CAN_IF", 44 | "value": "vcan0", 45 | }, 46 | Object { 47 | "name": "FW_ENDPOINT", 48 | "value": "a1q6dgk6qorfqj-ats.iot.eu-central-1.amazonaws.com", 49 | }, 50 | Object { 51 | "name": "VEHICLE_NAME", 52 | "value": "vin100", 53 | }, 54 | Object { 55 | "name": "TRACE", 56 | "value": "off", 57 | }, 58 | ], 59 | "image": "fwe:lastest", 60 | "imagePullPolicy": "Always", 61 | "name": "main", 62 | "resources": Object { 63 | "limits": Object { 64 | "cpu": "1500m", 65 | "memory": "2048Mi", 66 | }, 67 | "requests": Object { 68 | "cpu": "1000m", 69 | "memory": "512Mi", 70 | }, 71 | }, 72 | "securityContext": Object { 73 | "allowPrivilegeEscalation": false, 74 | "privileged": false, 75 | "readOnlyRootFilesystem": true, 76 | "runAsGroup": 26000, 77 | "runAsNonRoot": true, 78 | "runAsUser": 25000, 79 | }, 80 | }, 81 | ], 82 | "dnsPolicy": "ClusterFirst", 83 | "restartPolicy": "Always", 84 | "securityContext": Object { 85 | "fsGroupChangePolicy": "Always", 86 | "runAsNonRoot": true, 87 | }, 88 | "setHostnameAsFQDN": false, 89 | }, 90 | }, 91 | ] 92 | `; 93 | -------------------------------------------------------------------------------- /cdk8s/cdk8s.yaml: -------------------------------------------------------------------------------- 1 | language: typescript 2 | app: node main.js 3 | imports: 4 | - k8s 5 | -------------------------------------------------------------------------------- /cdk8s/help: -------------------------------------------------------------------------------- 1 | ======================================================================================================== 2 | 3 | Your cdk8s typescript project is ready! 4 | 5 | cat help Print this message 6 | 7 | Compile: 8 | npm run compile Compile typescript code to javascript (or "yarn watch") 9 | npm run watch Watch for changes and compile typescript in the background 10 | npm run build Compile + synth 11 | 12 | Synthesize: 13 | npm run synth Synthesize k8s manifests from charts to dist/ (ready for 'kubectl apply -f') 14 | 15 | Deploy: 16 | kubectl apply -f dist/ 17 | 18 | Upgrades: 19 | npm run import Import/update k8s apis (you should check-in this directory) 20 | npm run upgrade Upgrade cdk8s modules to latest version 21 | npm run upgrade:next Upgrade cdk8s modules to latest "@next" version (last commit) 22 | 23 | ======================================================================================================== 24 | -------------------------------------------------------------------------------- /cdk8s/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "roots": [ 3 | "" 4 | ], 5 | testMatch: [ '**/*.test.ts'], 6 | "transform": { 7 | "^.+\\.tsx?$": "ts-jest" 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /cdk8s/main.test.ts: -------------------------------------------------------------------------------- 1 | import {MyChart} from './main'; 2 | import {Testing} from 'cdk8s'; 3 | 4 | describe('Placeholder', () => { 5 | test('Empty', () => { 6 | const app = Testing.app(); 7 | const chart = new MyChart(app, 'test-chart'); 8 | const results = Testing.synth(chart) 9 | expect(results).toMatchSnapshot(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /cdk8s/main.ts: -------------------------------------------------------------------------------- 1 | import { Construct } from 'constructs'; 2 | import { App, Chart, ChartProps } from 'cdk8s'; 3 | import * as kplus from 'cdk8s-plus-25'; 4 | const fs = require('fs'); 5 | import { VehicleSimulator } from './vsim'; 6 | 7 | 8 | export class MyChart extends Chart { 9 | constructor(scope: Construct, id: string, props: ChartProps = { }) { 10 | super(scope, id, props); 11 | 12 | const privatekey = kplus.Secret.fromSecretName(this, 'PrivateKey', 'private-key'); 13 | const certificate = kplus.Secret.fromSecretName(this, 'Certificate', 'certificate'); 14 | 15 | const pod = new kplus.Pod(this, 'Pod'); 16 | const fwe = pod.addContainer({ 17 | name: 'fwe', 18 | image: 'public.ecr.aws/aws-iot-fleetwise-edge/aws-iot-fleetwise-edge:v1.0.3', 19 | imagePullPolicy: kplus.ImagePullPolicy.IF_NOT_PRESENT, 20 | securityContext: { 21 | readOnlyRootFilesystem: false, 22 | ensureNonRoot: false, 23 | user: 0, 24 | group: 0 25 | } 26 | 27 | }); 28 | fwe.env.addVariable('CAN_BUS0', kplus.EnvValue.fromValue(process.env.CAN_BUS0!)); 29 | fwe.env.addVariable('ENDPOINT_URL', kplus.EnvValue.fromValue(process.env.ENDPOINT_URL!)); 30 | fwe.env.addVariable('VEHICLE_NAME', kplus.EnvValue.fromValue(process.env.VEHICLE_NAME!)); 31 | fwe.env.addVariable('TRACE', kplus.EnvValue.fromValue(process.env.TRACE!)); 32 | fwe.mount( 33 | '/etc/aws-iot-fleetwise/private-key.key', 34 | kplus.Volume.fromSecret(this, 'PrivateKeyVolume', privatekey), 35 | { 36 | readOnly: true, 37 | subPath: 'private-key.key' 38 | } 39 | ); 40 | fwe.mount( 41 | '/etc/aws-iot-fleetwise/certificate.pem', 42 | kplus.Volume.fromSecret(this, 'CertificateVolume', certificate), 43 | { 44 | readOnly: true, 45 | subPath: 'certificate.pem' 46 | } 47 | ); 48 | 49 | if (fs.existsSync('/etc/virtual')) { 50 | new VehicleSimulator(this, 'vsim', { pod }); 51 | } 52 | } 53 | } 54 | 55 | const app = new App(); 56 | new MyChart(app, 'demo-soafee-aws-iotfleetwise', { 57 | labels:{ 58 | app: 'demo-soafee-aws-iotfleetwise' 59 | } 60 | }); 61 | 62 | app.synth(); 63 | -------------------------------------------------------------------------------- /cdk8s/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "k3s", 3 | "version": "1.0.0", 4 | "main": "main.js", 5 | "types": "main.ts", 6 | "license": "Apache-2.0", 7 | "private": true, 8 | "scripts": { 9 | "import": "cdk8s import", 10 | "synth": "cdk8s synth", 11 | "compile": "tsc --build", 12 | "watch": "tsc --build -w", 13 | "test": "jest", 14 | "build": "npm run compile && npm run synth", 15 | "upgrade": "npm i cdk8s@latest cdk8s-cli@latest", 16 | "upgrade:next": "npm i cdk8s@next cdk8s-cli@next" 17 | }, 18 | "dependencies": { 19 | "cdk8s": "^2.5.32", 20 | "cdk8s-plus-22": "^2.0.0-rc.158", 21 | "cdk8s-plus-25": "^2.0.8", 22 | "constructs": "^10.1.144" 23 | }, 24 | "devDependencies": { 25 | "@types/jest": "^26.0.24", 26 | "@types/node": "^14.18.33", 27 | "cdk8s-cli": "^2.1.31", 28 | "jest": "^26.6.3", 29 | "ts-jest": "^26.5.6", 30 | "typescript": "^4.8.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cdk8s/signalmeshpod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: signalmeshpod 5 | spec: 6 | hostNetwork: true 7 | hostPID: true 8 | containers: 9 | - name: signalmesh 10 | image: signalmesh:latest 11 | imagePullPolicy: IfNotPresent 12 | securityContext: 13 | privileged: true 14 | capabilities: 15 | add: ["CAP_NET_ADMIN"] 16 | volumeMounts: 17 | - name: root 18 | mountPath: /root 19 | volumes: 20 | - name: root 21 | hostPath: 22 | path: / 23 | -------------------------------------------------------------------------------- /cdk8s/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "charset": "utf8", 5 | "declaration": true, 6 | "experimentalDecorators": true, 7 | "inlineSourceMap": true, 8 | "inlineSources": true, 9 | "lib": [ 10 | "es2016" 11 | ], 12 | "module": "CommonJS", 13 | "noEmitOnError": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "resolveJsonModule": true, 21 | "strict": true, 22 | "strictNullChecks": true, 23 | "strictPropertyInitialization": true, 24 | "stripInternal": true, 25 | "target": "ES2017" 26 | }, 27 | "include": [ 28 | "**/*.ts" 29 | ], 30 | "exclude": [ 31 | "node_modules" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /cdk8s/vsim.ts: -------------------------------------------------------------------------------- 1 | import { Construct } from 'constructs'; 2 | import * as kplus from 'cdk8s-plus-25'; 3 | 4 | 5 | export interface VehicleSimulatorProps { 6 | pod: kplus.Pod 7 | } 8 | 9 | export class VehicleSimulator extends Construct { 10 | constructor(scope: Construct, id: string, props: VehicleSimulatorProps) { 11 | super(scope, id); 12 | 13 | props.pod.addContainer({ 14 | name: 'signalmesh', 15 | image: 'public.ecr.aws/docker/library/alpine:3', 16 | command: ["/bin/sh","-c"], 17 | args: [`ip link add dev ${process.env.CAN_BUS0!} type vcan && ip link set ${process.env.CAN_BUS0!} up; tail -f /dev/null`], 18 | securityContext: { 19 | privileged: true, 20 | allowPrivilegeEscalation: true, 21 | ensureNonRoot: false, 22 | user: 0, 23 | group: 0 24 | } 25 | }) 26 | 27 | const vsim = props.pod.addContainer({ 28 | name: 'vsim', 29 | image: 'docker.io/library/vsim:latest', 30 | imagePullPolicy: kplus.ImagePullPolicy.NEVER, 31 | securityContext: { 32 | readOnlyRootFilesystem: false, 33 | ensureNonRoot: false, 34 | user: 0, 35 | group: 0 36 | } 37 | }); 38 | vsim.addPort({ number: 3000 }); 39 | vsim.env.addVariable('CAN_IF', kplus.EnvValue.fromValue(process.env.CAN_BUS0!)); 40 | 41 | const ui = new kplus.Service(this, 'UI', { 42 | type: kplus.ServiceType.NODE_PORT 43 | }); 44 | ui.select(props.pod); 45 | ui.bind(3000); 46 | 47 | const ingress = new kplus.Ingress(this, 'Ingress'); 48 | ingress.metadata.addAnnotation('traefik.ingress.kubernetes.io/router.entrypoints', 'web'); 49 | 50 | ingress.addRule('/', kplus.IngressBackend.fromService(ui)) 51 | } 52 | } -------------------------------------------------------------------------------- /cloud/.projen/deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | { 4 | "name": "projen", 5 | "version": "0.56.19", 6 | "type": "devenv" 7 | }, 8 | { 9 | "name": "pytest", 10 | "version": "6.2.1", 11 | "type": "devenv" 12 | }, 13 | { 14 | "name": "aws-cdk-lib", 15 | "version": "^2.27.0", 16 | "type": "runtime" 17 | }, 18 | { 19 | "name": "cdk-aws-iotfleetwise==0.3.2", 20 | "type": "runtime" 21 | }, 22 | { 23 | "name": "constructs", 24 | "version": "^10.0.5", 25 | "type": "runtime" 26 | } 27 | ], 28 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 29 | } 30 | -------------------------------------------------------------------------------- /cloud/.projen/files.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | ".gitattributes", 4 | ".github/workflows/pull-request-lint.yml", 5 | ".gitignore", 6 | ".projen/deps.json", 7 | ".projen/files.json", 8 | ".projen/tasks.json", 9 | "cdk.json", 10 | "requirements-dev.txt", 11 | "requirements.txt" 12 | ], 13 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 14 | } 15 | -------------------------------------------------------------------------------- /cloud/.projen/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "build": { 4 | "name": "build", 5 | "description": "Full release build", 6 | "steps": [ 7 | { 8 | "spawn": "default" 9 | }, 10 | { 11 | "spawn": "pre-compile" 12 | }, 13 | { 14 | "spawn": "compile" 15 | }, 16 | { 17 | "spawn": "post-compile" 18 | }, 19 | { 20 | "spawn": "test" 21 | }, 22 | { 23 | "spawn": "package" 24 | } 25 | ] 26 | }, 27 | "clobber": { 28 | "name": "clobber", 29 | "description": "hard resets to HEAD of origin and cleans the local repo", 30 | "env": { 31 | "BRANCH": "$(git branch --show-current)" 32 | }, 33 | "steps": [ 34 | { 35 | "exec": "git checkout -b scratch", 36 | "name": "save current HEAD in \"scratch\" branch" 37 | }, 38 | { 39 | "exec": "git checkout $BRANCH" 40 | }, 41 | { 42 | "exec": "git fetch origin", 43 | "name": "fetch latest changes from origin" 44 | }, 45 | { 46 | "exec": "git reset --hard origin/$BRANCH", 47 | "name": "hard reset to origin commit" 48 | }, 49 | { 50 | "exec": "git clean -fdx", 51 | "name": "clean all untracked files" 52 | }, 53 | { 54 | "say": "ready to rock! (unpushed commits are under the \"scratch\" branch)" 55 | } 56 | ], 57 | "condition": "git diff --exit-code > /dev/null" 58 | }, 59 | "compile": { 60 | "name": "compile", 61 | "description": "Only compile" 62 | }, 63 | "default": { 64 | "name": "default", 65 | "description": "Synthesize project files", 66 | "steps": [ 67 | { 68 | "exec": "python .projenrc.py" 69 | } 70 | ] 71 | }, 72 | "deploy": { 73 | "name": "deploy", 74 | "description": "Deploys your CDK app to the AWS cloud", 75 | "steps": [ 76 | { 77 | "exec": "cdk deploy" 78 | } 79 | ] 80 | }, 81 | "destroy": { 82 | "name": "destroy", 83 | "description": "Destroys your cdk app in the AWS cloud", 84 | "steps": [ 85 | { 86 | "exec": "cdk destroy" 87 | } 88 | ] 89 | }, 90 | "diff": { 91 | "name": "diff", 92 | "description": "Diffs the currently deployed app against your code", 93 | "steps": [ 94 | { 95 | "exec": "cdk diff" 96 | } 97 | ] 98 | }, 99 | "eject": { 100 | "name": "eject", 101 | "description": "Remove projen from the project", 102 | "env": { 103 | "PROJEN_EJECTING": "true" 104 | }, 105 | "steps": [ 106 | { 107 | "spawn": "default" 108 | } 109 | ] 110 | }, 111 | "install": { 112 | "name": "install", 113 | "description": "Install and upgrade dependencies", 114 | "steps": [ 115 | { 116 | "exec": "pip install --upgrade pip" 117 | }, 118 | { 119 | "exec": "pip install -r requirements.txt" 120 | }, 121 | { 122 | "exec": "pip install -r requirements-dev.txt" 123 | } 124 | ] 125 | }, 126 | "package": { 127 | "name": "package", 128 | "description": "Creates the distribution package" 129 | }, 130 | "post-compile": { 131 | "name": "post-compile", 132 | "description": "Runs after successful compilation", 133 | "steps": [ 134 | { 135 | "spawn": "synth:silent" 136 | } 137 | ] 138 | }, 139 | "pre-compile": { 140 | "name": "pre-compile", 141 | "description": "Prepare the project for compilation" 142 | }, 143 | "synth": { 144 | "name": "synth", 145 | "description": "Synthesizes your cdk app into cdk.out", 146 | "steps": [ 147 | { 148 | "exec": "cdk synth" 149 | } 150 | ] 151 | }, 152 | "synth:silent": { 153 | "name": "synth:silent", 154 | "description": "Synthesizes your cdk app into cdk.out and suppresses the template in stdout (part of \"yarn build\")", 155 | "steps": [ 156 | { 157 | "exec": "cdk synth -q" 158 | } 159 | ] 160 | }, 161 | "test": { 162 | "name": "test", 163 | "description": "Run tests", 164 | "steps": [ 165 | { 166 | "exec": "pytest" 167 | } 168 | ] 169 | }, 170 | "watch": { 171 | "name": "watch", 172 | "description": "Watches changes in your source code and rebuilds and deploys to the current account", 173 | "steps": [ 174 | { 175 | "exec": "cdk deploy --hotswap" 176 | }, 177 | { 178 | "exec": "cdk watch" 179 | } 180 | ] 181 | } 182 | }, 183 | "env": { 184 | "VIRTUAL_ENV": "$(echo $PWD/.env)", 185 | "PATH": "$(echo $PWD/.env/bin:$PATH)" 186 | }, 187 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 188 | } 189 | -------------------------------------------------------------------------------- /cloud/.projenrc.py: -------------------------------------------------------------------------------- 1 | from projen.awscdk import AwsCdkPythonApp 2 | 3 | project = AwsCdkPythonApp( 4 | author_email="salamida@amazon.com", 5 | author_name="Francesco Salamida", 6 | cdk_version="2.13.0", 7 | module_name="src", 8 | name="demo-soafee-aws-iotfleetwise", 9 | version="0.1.0", 10 | deps=[ 11 | 'cdk-aws-iotfleetwise==0.3.2', 12 | ] 13 | ) 14 | 15 | project.synth() 16 | -------------------------------------------------------------------------------- /cloud/README.md: -------------------------------------------------------------------------------- 1 | # replace this -------------------------------------------------------------------------------- /cloud/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | from aws_cdk import App, Environment 3 | from src.main import MyStack 4 | 5 | env = Environment( 6 | account=os.getenv('CDK_DEFAULT_ACCOUNT'), 7 | region=os.getenv('CDK_DEFAULT_REGION'), 8 | ) 9 | 10 | app = App() 11 | MyStack(app, "demo-soafee-aws-iotfleetwise", env=env) 12 | 13 | app.synth() -------------------------------------------------------------------------------- /cloud/cdk-outputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "demo-soafee-aws-iotfleetwise": { 3 | "privatekey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwUBPwTR8iLGX3xaMojilENXb5yJ4Oq4o0O275nmaN182MXoa\nIlRZgOwle/haPEnl3I/xskbh/kROTBULQN/sFDDY8Xe3zUg3/TSsSVF9pTmxYkbt\nYBPxwbNJWVRMvD8aw7Hn2CSQ1bYbgZkBKYIfWqz25JR+nRG4ruULDJp9ZXBXc/Hg\n4vmQ+IJZbXebnDrEaiNSQi9OtlEQL0sphrVGkNgZS/B6Jw//KTuao/U9yKPtUtHD\nbngflMV3qBFv9oRDN0EBL5wwWfSGEBbWUwV3LZcR0qj7wEjJRyyqDTx/P2IfSh15\n6h3bkiOF0qmt51SaXdHsyqcoargUguSbzvOEiwIDAQABAoIBAGf7Ma60jhmC7UPF\nP1B+CBsh5xvbyTNRyHZ6pHLjlV4b5e4DVBKKrkoXRigJPoxX+s2Nyi/xweHtYO/B\nFvgGRGGs0t5CW2yiYjXk5JritSd0/oPxxAHBWYCu9lWRbWSQ09beX/BF5ISJYw19\nhUmPUU2Z6ZKeb+4VvfilFyfl7HjqZVQWGCc+Io3u/sKgVfGNijxMYX4NH6+dE55D\nXCEiCI0CYf3mlxFm/AJJ79xu6yl0Ar0fYcBI3qEm/phrvAuMGa5fqt9KJgtyXKG4\npOPhSJzWOF3DCsjBJjytuq4uM4LI2F6g6N7FqSrbdPnrCD7iVmfSc+OX1whBZI6m\nFWZDAwkCgYEA4OuY/kDooT2C85B2MjeEf0fRs52CxdguxsN0w6Zaqw14rDfgYv04\npv2UEdt7QVW5h+6bvGuCJ8Hl1wInJH46VFNcnRY93XataTR3hBnGWLKOCgxN4+Aq\nX5JJtOx7tEBJX//Hdz1vhgOklbL0YnzIP9xxSKxleYApEZbFdSb1+q8CgYEA2/Rx\nCz+zsaMwYa1b83IARSKeh+U3PbPMANBts9XIVzS+OUN6rZmGSVgbLkfUD4Kf2go2\nFEF4ScV9RGkA7z174OPWu9GbF7O+z4bP5/k+VNkOq3IW0ZpLj676NTXUNwkzGzAa\nwphdSXck3h/PTDra1h97JCaI3iAnd5m573jzmuUCgYBnBiOPGqYz7RwFer9ZIyry\nri/0yQo0m2BZtP/P+mv/e+0xXWrPjNOdUfu37kPX2m9gP0BgnxIAKgWgZaEOSy9m\nnsSGQ8DUp16pGNpBzRiYWjlLivVGktB19GkEkWncV7IEYgG4sZBa32JNG6prTmLf\nNZdCRY05iSXGo7wQfGchiwKBgQCprderAthfl/FR2MePdQlqS/uaACRuUA53LlMC\noU/snj6EtF6mH0ItwG8vc6/Oy0j/jlMLqzndRyGMrahP5tFyurDDsT4AuePf+jLW\n1DrWf5/GeLvLtEjILgbNF5p/XnRza9eo+Js2elgRlt53AwwglKaNS8DYAlwP8qIy\nb88VVQKBgHCShRxhpou/+Xvq7svtN6o2P4QPY+mT2AGbGR4imqtCOb+1yxqtps6T\nnfAQvRs7He7IAiCIVSibJH2LkYZVBiFuhLlGvxT/sAvg02bfrr5e8SUP0yTOjfF+\n3WhWLtJRn0kB8xqT/Ne+W0v6Dn4d/u8NpiJd/sXbhdUruoGjg6mY\n-----END RSA PRIVATE KEY-----\n", 4 | "certificate": "-----BEGIN CERTIFICATE-----\nMIIDWTCCAkGgAwIBAgIUfNQ3X/3GN0DiJro4d51DoA/2JQcwDQYJKoZIhvcNAQEL\nBQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g\nSW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTIyMTExMTEzMjA1\nMFoXDTQ5MTIzMTIzNTk1OVowHjEcMBoGA1UEAwwTQVdTIElvVCBDZXJ0aWZpY2F0\nZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMFAT8E0fIixl98WjKI4\npRDV2+cieDquKNDtu+Z5mjdfNjF6GiJUWYDsJXv4WjxJ5dyP8bJG4f5ETkwVC0Df\n7BQw2PF3t81IN/00rElRfaU5sWJG7WAT8cGzSVlUTLw/GsOx59gkkNW2G4GZASmC\nH1qs9uSUfp0RuK7lCwyafWVwV3Px4OL5kPiCWW13m5w6xGojUkIvTrZREC9LKYa1\nRpDYGUvweicP/yk7mqP1Pcij7VLRw254H5TFd6gRb/aEQzdBAS+cMFn0hhAW1lMF\ndy2XEdKo+8BIyUcsqg08fz9iH0odeeod25IjhdKpredUml3R7MqnKGq4FILkm87z\nhIsCAwEAAaNgMF4wHwYDVR0jBBgwFoAU5JGRgT9D7lIMbBJ6l/PJsV0TY1IwHQYD\nVR0OBBYEFEYIc/BGj/O5/mRgBmOohAlB25KwMAwGA1UdEwEB/wQCMAAwDgYDVR0P\nAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQCyzsSBXAMZPXpjJVXleMkdjLNe\nP8yoegU8xcauG0VsQ82R9iDtYYjgrjsq0ZRi/NTZkFIAqp6ZxBG6eg+CUQddRvD5\n4LfoQOjm4zIam6IA3uQ20RdR7j+vPBOtSjBg9cKBa37HoDSW7VG3wXzOl28E0QcD\nsjeYzptos4zB2WnLTTQXH8zRoACU58x23pXYgKhvJP4vXoeeCgagGEjMWD/Ct3du\nrlf8sxVl0HeX2k1xdakzhqUFexeVKXMiAjM0y6To5gdOKYvNhhP8Gv0KzZkAmHv5\n3DeA+5CM1zqpTdUS8v+geL0Y5RrcQR67RFE6e8OfimE/hZJXOym6xHYG97F0\n-----END CERTIFICATE-----\n", 5 | "endpointaddress": "a1q6dgk6qorfqj-ats.iot.eu-central-1.amazonaws.com" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /cloud/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python app.py", 3 | "output": "cdk.out", 4 | "watch": { 5 | "include": [ 6 | "**" 7 | ], 8 | "exclude": [ 9 | "README.md", 10 | "cdk*.json", 11 | "requirements*.txt", 12 | "source.bat", 13 | "**/__init__.py", 14 | "python/__pycache__", 15 | "tests" 16 | ] 17 | }, 18 | "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"npx projen\"." 19 | } 20 | -------------------------------------------------------------------------------- /cloud/data/my_model.dbc: -------------------------------------------------------------------------------- 1 | VERSION "" 2 | 3 | NS_ : 4 | NS_DESC_ 5 | CM_ 6 | BA_DEF_ 7 | BA_ 8 | VAL_ 9 | CAT_DEF_ 10 | CAT_ 11 | FILTER 12 | BA_DEF_DEF_ 13 | EV_DATA_ 14 | ENVVAR_DATA_ 15 | SGTYPE_ 16 | SGTYPE_VAL_ 17 | BA_DEF_SGTYPE_ 18 | BA_SGTYPE_ 19 | SIG_TYPE_REF_ 20 | VAL_TABLE_ 21 | SIG_GROUP_ 22 | SIG_VALTYPE_ 23 | SIGTYPE_VALTYPE_ 24 | BO_TX_BU_ 25 | BA_DEF_REL_ 26 | BA_REL_ 27 | BA_DEF_DEF_REL_ 28 | BU_SG_REL_ 29 | BU_EV_REL_ 30 | BU_BO_REL_ 31 | SG_MUL_VAL_ 32 | 33 | BS_: 34 | 35 | BU_: UC_Frein UC_Car CMM UC_Dira Capt_Vol BV AAS BVMP SBW BSI ASR_plus ESM MDD Capt_ALG OUTIL_DIAG XCPonCAN Bosch_Plant Unknown MultipleNodes 36 | 37 | BO_ 1042 HS1_DAT_BSI_412: 8 UC_Car 38 | SG_ HS1_ETAT_OUVRANTS : 55|5@0+ (1,0) [0|31] "NOTDEF" UC_Frein 39 | SG_ HS1_REQ_DECON_ASR_ESP : 23|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 40 | SG_ HS1_CONTACT_FREIN1_HS : 0|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 41 | SG_ HS1_MARCHE_AR_BVM : 2|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 42 | SG_ HS1_CONTACT_FREIN_PRK : 3|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 43 | SG_ HS1_CONTACT_FREIN1 : 5|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 44 | 45 | BO_ 1458 HS1_CONTEXTE_1_5B2: 8 BSI 46 | SG_ HS1_TEMP_AIR_EXT : 23|8@0+ (0.5,-40) [-40|85] "C" UC_Frein 47 | 48 | BA_DEF_ SG_ "GenSigMappingName" STRING; 49 | BA_DEF_ BO_ "GenMsgSendType" ENUM "PERIODICAL","PERIODICAL-EVENT","EVENT","ONCE","ONCE-EVENT","NOTDEF"; 50 | BA_DEF_ SG_ "GenSigErrorValue" STRING; 51 | BA_DEF_ SG_ "GenSigStartValue" INT 0 65535; 52 | BA_DEF_ BO_ "GenMsgCycleTime" INT 0 65535; 53 | BA_DEF_ BO_ "GenMsgStartDelayTime" INT 0 65535; 54 | BA_DEF_ BO_ "GenMsgTimeoutTime" INT 0 65535; 55 | 56 | BA_DEF_ EV_ "GenEnvAutoGenCtrl" ENUM "No","Yes"; 57 | BA_DEF_ EV_ "GenEnvMsgOffset" INT 0 2147483647; 58 | BA_DEF_ EV_ "GenEnvMsgName" STRING; 59 | BA_DEF_ EV_ "GenEnvIsGeneratedSnd" ENUM "No","Yes"; 60 | BA_DEF_ SG_ "GenSigEnvVarType" ENUM "int","float","undef"; 61 | BA_DEF_ "GenEnvVarPrefix" STRING; 62 | BA_DEF_ "GenEnvVarEndingSnd" STRING; 63 | BA_DEF_ "GenEnvVarEndingDsp" STRING; 64 | 65 | BA_DEF_DEF_ "GenSigMappingName" "NOTDEF"; 66 | BA_DEF_DEF_ "GenMsgSendType" "NOTDEF"; 67 | BA_DEF_DEF_ "GenSigErrorValue" "NOTDEF"; 68 | BA_DEF_DEF_ "GenSigStartValue" 65535; 69 | BA_DEF_DEF_ "GenMsgCycleTime" 65535; 70 | BA_DEF_DEF_ "GenMsgStartDelayTime" 65535; 71 | BA_DEF_DEF_ "GenMsgTimeoutTime" 65535; 72 | 73 | BA_DEF_DEF_ "GenEnvAutoGenCtrl" "No"; 74 | BA_DEF_DEF_ "GenEnvMsgOffset" 0; 75 | BA_DEF_DEF_ "GenEnvMsgName" ""; 76 | BA_DEF_DEF_ "GenEnvIsGeneratedSnd" "No"; 77 | BA_DEF_DEF_ "GenSigEnvVarType" "undef"; 78 | BA_DEF_DEF_ "GenEnvVarPrefix" "Env"; 79 | BA_DEF_DEF_ "GenEnvVarEndingSnd" "_"; 80 | BA_DEF_DEF_ "GenEnvVarEndingDsp" "Dsp_"; 81 | 82 | BA_ "GenMsgTimeoutTime" BO_ 1042 500; 83 | BA_ "GenMsgStartDelayTime" BO_ 1042 0; 84 | BA_ "GenMsgCycleTime" BO_ 1042 50; 85 | BA_ "GenMsgSendType" BO_ 1042 0; 86 | BA_ "GenSigMappingName" SG_ 1042 HS1_ETAT_OUVRANTS "HS1_ETAT_OUVRANTS"; 87 | BA_ "GenSigErrorValue" SG_ 1042 HS1_ETAT_OUVRANTS "NOTDEF;NOTDEF"; 88 | BA_ "GenSigStartValue" SG_ 1042 HS1_ETAT_OUVRANTS 0; 89 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_ETAT_OUVRANTS 1; 90 | BA_ "GenSigMappingName" SG_ 1042 HS1_REQ_DECON_ASR_ESP "HS1_REQ_DECON_ASR_ESP"; 91 | BA_ "GenSigErrorValue" SG_ 1042 HS1_REQ_DECON_ASR_ESP "NOTDEF;NOTDEF"; 92 | BA_ "GenSigStartValue" SG_ 1042 HS1_REQ_DECON_ASR_ESP 0; 93 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_REQ_DECON_ASR_ESP 1; 94 | BA_ "GenSigMappingName" SG_ 1042 HS1_CONTACT_FREIN1_HS "HS1_CONTACT_FREIN1_HS"; 95 | BA_ "GenSigErrorValue" SG_ 1042 HS1_CONTACT_FREIN1_HS "NOTDEF;NOTDEF"; 96 | BA_ "GenSigStartValue" SG_ 1042 HS1_CONTACT_FREIN1_HS 0; 97 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_CONTACT_FREIN1_HS 1; 98 | BA_ "GenSigMappingName" SG_ 1042 HS1_MARCHE_AR_BVM "HS1_MARCHE_AR_BVM"; 99 | BA_ "GenSigErrorValue" SG_ 1042 HS1_MARCHE_AR_BVM "NOTDEF;NOTDEF"; 100 | BA_ "GenSigStartValue" SG_ 1042 HS1_MARCHE_AR_BVM 0; 101 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_MARCHE_AR_BVM 1; 102 | BA_ "GenSigMappingName" SG_ 1042 HS1_CONTACT_FREIN_PRK "HS1_CONTACT_FREIN_PRK"; 103 | BA_ "GenSigErrorValue" SG_ 1042 HS1_CONTACT_FREIN_PRK "NOTDEF;NOTDEF"; 104 | BA_ "GenSigStartValue" SG_ 1042 HS1_CONTACT_FREIN_PRK 0; 105 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_CONTACT_FREIN_PRK 1; 106 | BA_ "GenSigMappingName" SG_ 1042 HS1_CONTACT_FREIN1 "HS1_CONTACT_FREIN1"; 107 | BA_ "GenSigErrorValue" SG_ 1042 HS1_CONTACT_FREIN1 "NOTDEF;NOTDEF"; 108 | BA_ "GenSigStartValue" SG_ 1042 HS1_CONTACT_FREIN1 0; 109 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_CONTACT_FREIN1 1; 110 | BA_ "GenMsgTimeoutTime" BO_ 1458 5000; 111 | BA_ "GenMsgStartDelayTime" BO_ 1458 0; 112 | BA_ "GenMsgCycleTime" BO_ 1458 1000; 113 | BA_ "GenMsgSendType" BO_ 1458 0; -------------------------------------------------------------------------------- /cloud/fast-campaign.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fast-campaign", 3 | "targetArn": "arn:aws:iotfleetwise:eu-central-1:642733357784:vehicle/vin200", 4 | "signalCatalogArn": "arn:aws:iotfleetwise:eu-central-1:642733357784:signal-catalog/default", 5 | "collectionScheme": { 6 | "conditionBasedCollectionScheme": { 7 | "conditionLanguageVersion": 1, 8 | "expression": "true", 9 | "minimumTriggerIntervalMs": 1000, 10 | "triggerMode": "ALWAYS" 11 | } 12 | }, 13 | "compression": "SNAPPY", 14 | "diagnosticsMode": "OFF", 15 | "postTriggerCollectionDuration": 1000, 16 | "priority": 0, 17 | "signalsToCollect": [ 18 | { 19 | "maxSampleCount": 100, 20 | "minimumSamplingIntervalMs": 0, 21 | "name": "Vehicle.HS1_TEMP_AIR_EXT" 22 | }, 23 | { 24 | "maxSampleCount": 100, 25 | "minimumSamplingIntervalMs": 0, 26 | "name": "Vehicle.HS1_ETAT_OUVRANTS" 27 | } 28 | ], 29 | "spoolingMode": "TO_DISK" 30 | } -------------------------------------------------------------------------------- /cloud/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | projen==0.56.19 3 | pytest==6.2.1 4 | -------------------------------------------------------------------------------- /cloud/requirements.txt: -------------------------------------------------------------------------------- 1 | # ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen". 2 | aws-cdk-lib>=2.13.0, <3.0.0 3 | cdk-aws-iotfleetwise==0.3.2 4 | constructs>=10.0.5, <11.0.0 5 | -------------------------------------------------------------------------------- /cloud/src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/demo-soafee-aws-iotfleetwise/58a17a0e3992099ee25aeecee8ad759f6c97f6d3/cloud/src/__init__.py -------------------------------------------------------------------------------- /cloud/src/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from aws_cdk import Stack, Duration, CfnOutput 4 | from aws_cdk import aws_timestream as ts 5 | from aws_cdk import aws_iam as iam 6 | import cdk_aws_iotfleetwise as ifw 7 | from constructs import Construct 8 | 9 | vehicle_name = 'vin100' 10 | vehicle_can_interface = 'vcan0' 11 | 12 | class MyStack(Stack): 13 | 14 | 15 | def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: 16 | super().__init__(scope, construct_id, **kwargs) 17 | 18 | role = iam.Role(self, "MyRole", 19 | assumed_by=iam.ServicePrincipal("iotfleetwise.amazonaws.com"), 20 | managed_policies=[ 21 | iam.ManagedPolicy.from_aws_managed_policy_name("AdministratorAccess") 22 | ]) 23 | 24 | database_name = "FleetWise" 25 | table_name = "FleetWise" 26 | database = ts.CfnDatabase(self, "MyDatabase", 27 | database_name=database_name) 28 | 29 | table = ts.CfnTable(self, "MyTable", 30 | database_name=database_name, 31 | table_name=table_name) 32 | 33 | table.node.add_dependency(database) 34 | 35 | nodes = [ifw.SignalCatalogBranch(fully_qualified_name='Vehicle')] 36 | signals_map_my_model = {} 37 | with open('../dbc/mymodel.dbc') as f: 38 | lines = f.readlines() 39 | for line in lines: 40 | found = re.search(r'^\s+SG_\s+(\w+)\s+.*', line) 41 | if found: 42 | signal_name = found.group(1) 43 | nodes.append(ifw.SignalCatalogSensor(fully_qualified_name=f'Vehicle.{signal_name}', data_type='DOUBLE')) 44 | signals_map_my_model[signal_name] = f'Vehicle.{signal_name}' 45 | 46 | signal_catalog = ifw.SignalCatalog(self, "FwSignalCatalog", 47 | description='my signal catalog', 48 | nodes=nodes) 49 | 50 | 51 | 52 | with open('../dbc/mymodel.dbc') as f: 53 | my_model = ifw.VehicleModel(self, 'MyModel1', 54 | signal_catalog=signal_catalog, 55 | name='my_model', 56 | description='My Model vehicle', 57 | network_interfaces=[ifw.CanVehicleInterface(interface_id='1', name=f'{vehicle_can_interface}')], 58 | network_file_definitions=[ifw.CanDefinition( 59 | '1', 60 | signals_map_my_model, 61 | [f.read()])]) 62 | 63 | vin100 = ifw.Vehicle(self, vehicle_name, 64 | vehicle_name=vehicle_name, 65 | vehicle_model=my_model, 66 | create_iot_thing=True) 67 | 68 | ifw.Fleet(self, 'fleet1', 69 | fleet_id='fleet1', 70 | signal_catalog=signal_catalog, 71 | description='my fleet1', 72 | vehicles=[vin100]) 73 | 74 | CfnOutput(self, 'privateKey', value=vin100.private_key) 75 | CfnOutput(self, 'certificate', value=vin100.certificate_pem) 76 | CfnOutput(self, 'endpointAddress', value=vin100.endpoint_address) 77 | CfnOutput(self, 'vehicleName', value=vehicle_name) 78 | CfnOutput(self, 'vehicleCanInterface', value=vehicle_can_interface) 79 | 80 | ifw.Campaign(self, 'MyCampaign', 81 | name='my-campaign', 82 | target=vin100, 83 | collection_scheme=ifw.TimeBasedCollectionScheme(Duration.seconds(10)), 84 | signals=[ 85 | ifw.CampaignSignal('Vehicle.AmbientAirTemperature'), 86 | ifw.CampaignSignal('Vehicle.DoorsState'), 87 | ], 88 | data_destination_configs=[ifw.TimestreamConfigProperty(role.role_arn, table.attr_arn)], 89 | auto_approve=True) 90 | -------------------------------------------------------------------------------- /cloud9/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | package-lock.json 3 | __pycache__ 4 | .pytest_cache 5 | .venv 6 | *.egg-info 7 | cdk.out/ -------------------------------------------------------------------------------- /cloud9/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | 4 | import aws_cdk as cdk 5 | 6 | from src.mainstack import MainStack 7 | 8 | 9 | app = cdk.App() 10 | MainStack(app, "cloud9-env", 11 | synthesizer=cdk.BootstraplessSynthesizer() 12 | ) 13 | 14 | app.synth() 15 | -------------------------------------------------------------------------------- /cloud9/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "requirements*.txt", 11 | "**/__init__.py", 12 | "python/__pycache__", 13 | "tests" 14 | ] 15 | }, 16 | "context": { 17 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 18 | "@aws-cdk/core:stackRelativeExports": true, 19 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 20 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 21 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 22 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 23 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 24 | "@aws-cdk/aws-iam:minimizePolicies": true, 25 | "@aws-cdk/core:target-partitions": [ 26 | "aws", 27 | "aws-cn" 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cloud9/requirements.txt: -------------------------------------------------------------------------------- 1 | aws-cdk-lib==2.51.1 2 | constructs>=10.0.0,<11.0.0 3 | -------------------------------------------------------------------------------- /cloud9/src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/demo-soafee-aws-iotfleetwise/58a17a0e3992099ee25aeecee8ad759f6c97f6d3/cloud9/src/__init__.py -------------------------------------------------------------------------------- /cloud9/src/lambda.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import boto3 3 | import json 4 | import os 5 | import time 6 | import traceback 7 | import cfnresponse 8 | 9 | def on_event(event, context): 10 | print('event: {}'.format(event)) 11 | print('context: {}'.format(context)) 12 | responseData = {} 13 | 14 | if event['RequestType'] == 'Create': 15 | try: 16 | # Open AWS clients 17 | ec2 = boto3.client('ec2') 18 | 19 | # Get the InstanceId of the Cloud9 IDE 20 | print(ec2.describe_instances(Filters=[{'Name': 'tag:SSMBootstrap','Values': ['Active']}])) 21 | instance = ec2.describe_instances( 22 | Filters=[ 23 | {'Name': 'tag:SSMBootstrap','Values': ['Active']}, 24 | {'Name': 'instance-state-name','Values': ['pending', 'running']} 25 | ] 26 | )['Reservations'][0]['Instances'][0] 27 | print('instance: {}'.format(instance)) 28 | 29 | # Create the IamInstanceProfile request object 30 | iam_instance_profile = { 31 | 'Arn': event['ResourceProperties']['InstanceProfileArn'], 32 | 'Name': event['ResourceProperties']['InstanceProfileName'] 33 | } 34 | print('iam_instance_profile: {}'.format(iam_instance_profile)) 35 | 36 | # Wait for Instance to become ready before adding Role 37 | instance_state = instance['State']['Name'] 38 | print('instance_state: {}'.format(instance_state)) 39 | while instance_state != 'running': 40 | time.sleep(5) 41 | instance_state = ec2.describe_instances(InstanceIds=[instance['InstanceId']]) 42 | print('instance_state: {}'.format(instance_state)) 43 | 44 | # attach instance profile 45 | response = ec2.associate_iam_instance_profile(IamInstanceProfile=iam_instance_profile, InstanceId=instance['InstanceId']) 46 | print('response - associate_iam_instance_profile: {}'.format(response)) 47 | 48 | responseData = {'Success': 'Started bootstrapping for instance: '+instance['InstanceId']} 49 | cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID') 50 | 51 | except Exception as e: 52 | print(e) 53 | responseData = {'Error': 'error'} 54 | cfnresponse.send(event, context, cfnresponse.FAILED, responseData, 'CustomResourcePhysicalID') 55 | else: 56 | cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID') 57 | -------------------------------------------------------------------------------- /cloud9/src/mainstack.py: -------------------------------------------------------------------------------- 1 | import os 2 | from aws_cdk import ( 3 | Duration, 4 | CfnTag, 5 | Stack, 6 | CustomResource, 7 | CfnParameter, 8 | CfnCondition, 9 | Fn, 10 | Aws, 11 | aws_cloud9 as cloud9, 12 | aws_ssm as ssm, 13 | aws_lambda as lambda_, 14 | aws_iam as iam, 15 | ) 16 | from constructs import Construct 17 | 18 | class MainStack(Stack): 19 | def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: 20 | super().__init__(scope, construct_id, **kwargs) 21 | 22 | c9 = cloud9.CfnEnvironmentEC2(self,'MyCfnEnvironmentEC2', 23 | instance_type='m5.large', 24 | automatic_stop_time_minutes=120, 25 | image_id='ubuntu-22.04-x86_64', 26 | name='Demo SOAFEE AWS IoT Fleetwise', 27 | description='Demo SOAFEE AWS IoT Fleetwise', 28 | repositories=[ 29 | cloud9.CfnEnvironmentEC2.RepositoryProperty( 30 | path_component='/demo-soafee-aws-iotfleetwise', 31 | repository_url='https://github.com/aws-samples/demo-soafee-aws-iotfleetwise.git')], 32 | 33 | 34 | tags=[CfnTag(key='SSMBootstrap', value='Active')] 35 | ) 36 | 37 | lambda_role = iam.Role(self, 'LambdaRole', 38 | assumed_by =iam.ServicePrincipal('lambda.amazonaws.com'), 39 | managed_policies=[ 40 | iam.ManagedPolicy.from_aws_managed_policy_name('service-role/AWSLambdaBasicExecutionRole'), 41 | iam.ManagedPolicy.from_aws_managed_policy_name('AdministratorAccess') 42 | ] 43 | ) 44 | 45 | with open(os.path.join(os.path.dirname(__file__), 'lambda.py'), encoding='utf8') as fp: 46 | handler_code = fp.read() 47 | 48 | on_event_fn = lambda_.Function(self, 'SetInstanceProfileLambda', 49 | code=lambda_.InlineCode(handler_code), 50 | handler='index.on_event', 51 | timeout=Duration.seconds(300), 52 | runtime=lambda_.Runtime.PYTHON_3_9, 53 | role=lambda_role 54 | ) 55 | 56 | c9_role = iam.Role(self, 'C9Role', 57 | assumed_by =iam.ServicePrincipal('ec2.amazonaws.com'), 58 | managed_policies=[ 59 | iam.ManagedPolicy.from_aws_managed_policy_name('AdministratorAccess') 60 | ] 61 | ) 62 | 63 | c9_instance_profile = iam.CfnInstanceProfile(self, 'C9InstanceProfile', 64 | roles=[c9_role.role_name] 65 | ) 66 | 67 | CustomResource(self, 'SetInstanceProfileResource', 68 | service_token=on_event_fn.function_arn, 69 | properties={ 70 | 'InstanceProfileArn': c9_instance_profile.attr_arn, 71 | 'InstanceProfileName': c9_instance_profile.ref 72 | } 73 | ) 74 | 75 | 76 | -------------------------------------------------------------------------------- /cloud9/synth.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | REGIONS="eu-central-1 us-east-1" 5 | 6 | cdk synth -q 7 | for region in $REGIONS 8 | do 9 | aws s3 cp cdk.out/cloud9-env.template.json s3://demo-soafee-aws-iot-fleetwise-$region/ 10 | aws s3api put-object-acl --bucket demo-soafee-aws-iot-fleetwise-$region --key cloud9-env.template.json --acl public-read 11 | done 12 | 13 | -------------------------------------------------------------------------------- /dbc/mymodel.dbc: -------------------------------------------------------------------------------- 1 | VERSION "" 2 | 3 | NS_ : 4 | NS_DESC_ 5 | CM_ 6 | BA_DEF_ 7 | BA_ 8 | VAL_ 9 | CAT_DEF_ 10 | CAT_ 11 | FILTER 12 | BA_DEF_DEF_ 13 | EV_DATA_ 14 | ENVVAR_DATA_ 15 | SGTYPE_ 16 | SGTYPE_VAL_ 17 | BA_DEF_SGTYPE_ 18 | BA_SGTYPE_ 19 | SIG_TYPE_REF_ 20 | VAL_TABLE_ 21 | SIG_GROUP_ 22 | SIG_VALTYPE_ 23 | SIGTYPE_VALTYPE_ 24 | BO_TX_BU_ 25 | BA_DEF_REL_ 26 | BA_REL_ 27 | BA_DEF_DEF_REL_ 28 | BU_SG_REL_ 29 | BU_EV_REL_ 30 | BU_BO_REL_ 31 | 32 | BS_: 33 | 34 | BU_: 35 | 36 | BO_ 2000 CABIN: 8 Vector__XXX 37 | SG_ DoorsState : 0|5@0+ (1,0) [0|31] "NOTDEF" Vector__XXX 38 | 39 | BO_ 2001 ENV: 8 Vector__XXX 40 | SG_ AmbientAirTemperature : 0|8@0+ (0.5,-40) [-40|85] "C" Vector__XXX 41 | 42 | BA_DEF_ SG_ "SignalType" STRING ; 43 | BA_DEF_ SG_ "SignalLongName" STRING ; 44 | BA_DEF_ BO_ "GenMsgCycleTime" INT 0 10000; 45 | BA_DEF_DEF_ "SignalType" ""; 46 | BA_DEF_DEF_ "SignalLongName" ""; 47 | BA_DEF_DEF_ "GenMsgCycleTime" 0; 48 | 49 | BA_ "GenMsgCycleTime" BO_ 2000 1000; 50 | BA_ "GenMsgCycleTime" BO_ 2001 1000; 51 | -------------------------------------------------------------------------------- /docs/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/demo-soafee-aws-iotfleetwise/58a17a0e3992099ee25aeecee8ad759f6c97f6d3/docs/architecture.png -------------------------------------------------------------------------------- /docs/createstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/demo-soafee-aws-iotfleetwise/58a17a0e3992099ee25aeecee8ad759f6c97f6d3/docs/createstack.png -------------------------------------------------------------------------------- /docs/preview menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/demo-soafee-aws-iotfleetwise/58a17a0e3992099ee25aeecee8ad759f6c97f6d3/docs/preview menu.png -------------------------------------------------------------------------------- /docs/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/demo-soafee-aws-iotfleetwise/58a17a0e3992099ee25aeecee8ad759f6c97f6d3/docs/qrcode.png -------------------------------------------------------------------------------- /scripts/build-vsim.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pushd vsim 4 | tar -czh . | docker build -t vsim - 5 | sleep 3 6 | if [ -e /run/k3s/containerd/containerd.sock ]; then 7 | docker save vsim:latest | ctr -a /run/k3s/containerd/containerd.sock -n=k8s.io images import - 8 | fi 9 | popd 10 | -------------------------------------------------------------------------------- /scripts/deploy-cdk8s.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export CAN_BUS0=$(cat .tmp/vehicle_can_interface.txt) 3 | export ENDPOINT_URL=$(cat .tmp/endpoint_address.txt) 4 | export VEHICLE_NAME=$(cat .tmp/vehicle_name.txt) 5 | export TRACE=off 6 | 7 | trap ctrl_c INT 8 | 9 | function ctrl_c() { 10 | echo "** Trapped CTRL-C" 11 | echo "cleaning up..." 12 | # Clean up 13 | sudo kubectl delete all --all 14 | echo "done" 15 | } 16 | 17 | # Make sure secrets are there for key and cert 18 | # kubectl create secret generic private-key --from-file=./.tmp/private-key.key 19 | # kubectl create secret generic certificate --from-file=./.tmp/certificate.pem 20 | 21 | # Deploy 22 | pushd cdk8s 23 | npm run build 24 | popd 25 | sudo kubectl apply -f cdk8s/dist/demo-soafee-aws-iotfleetwise.k8s.yaml 26 | sudo kubectl wait --for=condition=ready pod -l app=demo-soafee-aws-iotfleetwise 27 | if [ ! -f /etc/virtual ]; then 28 | CID=$(sudo crictl ps -name fwe -q) 29 | PID=$(sudo crictl inspect --output go-template --template '{{.info.pid}}' $CID) 30 | sudo ip link add vcan0 type vxcan peer name vxcan1 31 | sudo ip link set vcan0 netns $PID 32 | sudo ip link set vxcan1 up 33 | sudo nsenter -t $PID -n ip link set vcan0 up 34 | #sudo cangw -A -s can0 -d vxcan1 -e 35 | #sudo cangw -A -s vxcan1 -d can0 -e 36 | fi 37 | 38 | # Show log from fwe 39 | sudo kubectl logs -f -l app=demo-soafee-aws-iotfleetwise -c fwe 40 | -------------------------------------------------------------------------------- /scripts/deploy-cloud.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # Wait for any existing package install to finish 5 | i=0 6 | while true; do 7 | if sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; then 8 | i=0 9 | else 10 | i=`expr $i + 1` 11 | if expr $i \>= 10 > /dev/null; then 12 | break 13 | fi 14 | fi 15 | sleep 1 16 | done 17 | 18 | 19 | sudo apt-get -y update 20 | sudo apt-get -y install jq gettext bash-completion moreutils linux-modules-extra-$(uname -r) 21 | export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) 22 | export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region') 23 | echo "export ACCOUNT_ID=${ACCOUNT_ID}" | tee -a /home/ubuntu/.bash_profile 24 | echo "export AWS_REGION=${AWS_REGION}" | tee -a /home/ubuntu/.bash_profile 25 | aws configure set default.region ${AWS_REGION} 26 | aws configure set default.account ${ACCOUNT_ID} 27 | aws iotfleetwise register-account 28 | git config --global core.autocrlf false 29 | cdk bootstrap aws://${ACCOUNT_ID}/${AWS_REGION} 30 | 31 | mkdir -p .tmp 32 | pushd cloud 33 | python -m venv venv 34 | source venv/bin/activate 35 | pip install -r requirements.txt 36 | cdk deploy --require-approval never --outputs-file ../.tmp/cdk-outputs.json 37 | popd 38 | cat .tmp/cdk-outputs.json | jq -r '."demo-soafee-aws-iotfleetwise".privateKey' > .tmp/private-key.key 39 | cat .tmp/cdk-outputs.json | jq -r '."demo-soafee-aws-iotfleetwise".certificate' > .tmp/certificate.pem 40 | cat .tmp/cdk-outputs.json | jq -r '."demo-soafee-aws-iotfleetwise".endpointAddress' > .tmp/endpoint_address.txt 41 | cat .tmp/cdk-outputs.json | jq -r '."demo-soafee-aws-iotfleetwise".vehicleCanInterface' > .tmp/vehicle_can_interface.txt 42 | cat .tmp/cdk-outputs.json | jq -r '."demo-soafee-aws-iotfleetwise".vehicleName' > .tmp/vehicle_name.txt 43 | -------------------------------------------------------------------------------- /scripts/deploy-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CAN_BUS0=$(cat .tmp/vehicle_can_interface.txt) 3 | ENDPOINT_URL=$(cat .tmp/endpoint_address.txt) 4 | VEHICLE_NAME=$(cat .tmp/vehicle_name.txt) 5 | 6 | trap ctrl_c INT 7 | 8 | function ctrl_c() { 9 | echo "** Trapped CTRL-C" 10 | echo "cleaning up..." 11 | # Clean up 12 | echo -n "Cleaning up..." 13 | docker kill $(docker ps -q) > /dev/null 2>&1 14 | docker rm $(docker ps -aq) > /dev/null 2>&1 15 | ip link delete $CAN_BUS0 > /dev/null 2>&1 16 | echo "done" 17 | } 18 | 19 | 20 | echo -n "Starting fwe container..." 21 | docker run -d \ 22 | -e CAN_BUS0=$CAN_BUS0 \ 23 | -e ENDPOINT_URL=$ENDPOINT_URL \ 24 | -e VEHICLE_NAME=$VEHICLE_NAME \ 25 | -e TRACE=off \ 26 | --mount type=bind,source=$(pwd)/.tmp/private-key.key,target=/etc/aws-iot-fleetwise/private-key.key,readonly \ 27 | --mount type=bind,source=$(pwd)/.tmp/certificate.pem,target=/etc/aws-iot-fleetwise/certificate.pem,readonly \ 28 | -v /sys/fs/cgroup:/sys/fs/cgroup:ro \ 29 | --tmpfs /tmp \ 30 | --tmpfs /run \ 31 | --tmpfs /run/lock \ 32 | --name fwe \ 33 | public.ecr.aws/aws-iot-fleetwise-edge/aws-iot-fleetwise-edge:v1.0.3 34 | echo "done" 35 | 36 | echo -n "Starting vsim container..." 37 | docker run -d \ 38 | -e CAN_IF=vcansim \ 39 | -p 3000:3000 \ 40 | --name vsim \ 41 | vsim 42 | echo "done" 43 | 44 | echo -n "Bringing up CAN bus..." 45 | DOCKERPID_FWE=$(docker inspect -f '{{ .State.Pid }}' fwe) 46 | DOCKERPID_VSIM=$(docker inspect -f '{{ .State.Pid }}' vsim) 47 | ip link add $CAN_BUS0 type vxcan peer name vcansim 48 | ip link set $CAN_BUS0 netns $DOCKERPID_FWE 49 | ip link set vcansim netns $DOCKERPID_VSIM 50 | nsenter -t $DOCKERPID_FWE -n ip link set $CAN_BUS0 up 51 | nsenter -t $DOCKERPID_VSIM -n ip link set vcansim up 52 | echo "done" 53 | 54 | echo "Press CTRL+C to exit" 55 | echo "---" 56 | docker logs -f fwe 57 | -------------------------------------------------------------------------------- /scripts/deploy-k3s.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | CAN_BUS0=$(cat .tmp/vehicle_can_interface.txt) 4 | ENDPOINT_URL=$(cat .tmp/endpoint_address.txt) 5 | VEHICLE_NAME=$(cat .tmp/vehicle_name.txt) 6 | TRACE=off 7 | 8 | trap ctrl_c INT 9 | 10 | function ctrl_c() { 11 | echo "** Trapped CTRL-C" 12 | echo "cleaning up..." 13 | # Clean up 14 | sudo kubectl delete all --all 15 | } 16 | 17 | # Make sure secrets are there for key and cert 18 | # kubectl create secret generic private-key --from-file=./.tmp/private-key.key 19 | # kubectl create secret generic certificate --from-file=./.tmp/certificate.pem 20 | 21 | # Deploy 22 | sudo modprobe vcan 23 | sudo kubectl apply -f - <', methods = ['GET', 'POST']) 24 | def signal(signal_name): 25 | print(f"{request.method} {signal_name}", file=sys.stderr) 26 | if request.method == 'GET': 27 | return { 28 | 'signal_name': signal_name, 29 | 'value': can_sim.get_sig(signal_name) 30 | } 31 | if request.method == 'POST': 32 | data = request.get_json() 33 | print(f"{data}", file=sys.stderr) 34 | can_sim.set_sig(signal_name, data['value']) 35 | return { 36 | 'signal_name': signal_name, 37 | 'value': can_sim.get_sig(signal_name) 38 | } -------------------------------------------------------------------------------- /vsim/api/canigen.py: -------------------------------------------------------------------------------- 1 | import cantools 2 | import can 3 | import isotp 4 | from threading import Thread 5 | import time 6 | import sys 7 | import json 8 | from datetime import datetime 9 | if __name__ == '__main__': 10 | from prompt_toolkit import PromptSession 11 | from prompt_toolkit.completion import NestedCompleter 12 | from prompt_toolkit.completion import PathCompleter 13 | from prompt_toolkit.completion import WordCompleter 14 | import argparse 15 | 16 | class canigen: 17 | def __init__(self, interface, output_filename=None, database_filename=None, values_filename=None, obd_config_filename=None): 18 | self.__stop = False 19 | self.__interface = interface 20 | self.__output_file = None 21 | if not output_filename is None: 22 | self.__output_file = open(output_filename, "w") 23 | else: 24 | self.__can_bus = can.interface.Bus(self.__interface, bustype='socketcan') 25 | self.__sig_names = [] 26 | self.__values = {'sig':{},'pid':{},'dtc':{}} 27 | if not database_filename is None: 28 | self.__db = cantools.database.load_file(database_filename) 29 | print(self.__db, file=sys.stderr) 30 | for msg in self.__db.messages: 31 | for sig in msg.signals: 32 | self.__sig_names.append(sig.name) 33 | self.__values['sig'][sig.name] = 0.0 if sig.initial is None else sig.initial 34 | if not values_filename is None: 35 | self.__values = self.__load_json(values_filename) 36 | self.__obd_config = {} 37 | self.__pid_names = [] 38 | self.__dtc_names = [] 39 | if not obd_config_filename is None: 40 | self.__obd_config = self.__load_json(obd_config_filename) 41 | for ecu in self.__obd_config['ecus']: 42 | for pid_name in ecu['pids']: 43 | self.__pid_names.append(pid_name) 44 | if not pid_name in self.__values['pid']: 45 | self.__values['pid'][pid_name] = 0.0 46 | for dtc_name in ecu['dtcs']: 47 | self.__dtc_names.append(dtc_name) 48 | if not dtc_name in self.__values['dtc']: 49 | self.__values['dtc'][dtc_name] = 0.0 50 | 51 | self.__threads = [] 52 | if not database_filename is None: 53 | for msg in self.__db.messages: 54 | if msg.cycle_time == 0: 55 | print("Warning: Ignoring frame '%s' with zero cycle time" % msg.name) 56 | else: 57 | thread = Thread(target=self.__sig_thread, args=(msg.name,)) 58 | thread.start() 59 | self.__threads.append(thread) 60 | if not obd_config_filename is None: 61 | for ecu in self.__obd_config['ecus']: 62 | for rx_id in ecu['rx_ids']: 63 | thread = Thread(target=self.__obd_thread, args=(rx_id, ecu)) 64 | thread.start() 65 | self.__threads.append(thread) 66 | 67 | def stop(self): 68 | self.__stop = True 69 | for thread in self.__threads: 70 | thread.join() 71 | if self.__output_file: 72 | self.__output_file.close() 73 | 74 | def __load_json(self, filename): 75 | try: 76 | with open(filename, 'r') as fp: 77 | return json.load(fp) 78 | except: 79 | print('error: failed to load '+filename) 80 | raise 81 | 82 | def __save_json(self, filename, data): 83 | try: 84 | with open(filename, 'w') as fp: 85 | return json.dump(data, fp, sort_keys=True, indent=4) 86 | except: 87 | print('error: failed to save '+filename) 88 | 89 | def __write_frame(self, msg, data): 90 | if msg.is_extended_frame: 91 | can_id = '%07X' % msg.frame_id 92 | else: 93 | can_id = '%03X' % msg.frame_id 94 | data_hex = '' 95 | for byte in data: 96 | data_hex += '%02X' % byte 97 | self.__output_file.write('(%f) %s %s#%s\n' % (datetime.now().timestamp(), self.__interface, can_id, data_hex)) 98 | 99 | def __sig_thread(self, msg_name): 100 | while not self.__stop: 101 | msg = self.__db.get_message_by_name(msg_name) 102 | vals = {} 103 | for sig in msg.signals: 104 | if not sig.name in self.__values['sig']: 105 | val = self.__values['sig'][sig.name] = 0 106 | else: 107 | val = self.__values['sig'][sig.name] 108 | vals[sig.name] = 0 if val is None else val 109 | data = msg.encode(vals) 110 | if not self.__output_file is None: 111 | self.__write_frame(msg, data) 112 | else: 113 | frame = can.Message(is_extended_id=msg.is_extended_frame, arbitration_id=msg.frame_id, data=data) 114 | self.__can_bus.send(frame) 115 | time.sleep(msg.cycle_time / 1000.0) 116 | 117 | def __get_supported_pids(self, num_range, ecu): 118 | out = [0, 0, 0, 0] 119 | for name, data in ecu['pids'].items(): 120 | pid_num = int(data['num'], 0) 121 | if pid_num >= num_range and pid_num < (num_range + 0x20): 122 | i = int((pid_num - num_range - 1) / 8) 123 | j = (pid_num - num_range - 1) % 8 124 | out[i] |= 1 << (7 - j) 125 | return out 126 | 127 | def __encode_pid_data(self, num, ecu): 128 | for name, data in ecu['pids'].items(): 129 | if num == int(data['num'], 0): 130 | val = int((self.__values['pid'][name] + data['offset']) * data['scale']) 131 | out = [] 132 | for i in range(data['size']): 133 | out.append((val >> ((data['size'] - i - 1) * 8)) & 0xFF) 134 | return out 135 | return None 136 | 137 | def __obd_thread(self, rx_id, ecu): 138 | create_socket = True 139 | while not self.__stop: 140 | if create_socket: 141 | create_socket = False 142 | isotp_socket = isotp.socket(timeout=0.5) 143 | if ecu['zero_padding']: 144 | isotp_socket.set_opts(txpad=0, rxpad=0) 145 | rxid=int(rx_id, 0) 146 | txid=int(ecu['tx_id'], 0) 147 | addressing_mode = isotp.AddressingMode.Normal_11bits if txid <= 0x7FF else isotp.AddressingMode.Normal_29bits 148 | isotp_socket.bind(self.__interface, isotp.Address(addressing_mode=addressing_mode, rxid=rxid, txid=txid)) 149 | try: 150 | rx = isotp_socket.recv() 151 | except OSError: 152 | create_socket = True 153 | time.sleep(1) # Wait one sec, to avoid high CPU usage in the case of persistent bus errors 154 | continue 155 | 156 | if not rx is None: 157 | rx = list(rx) 158 | #print(ecu['name']+' rx: '+str(rx)) 159 | sid = rx.pop(0) 160 | tx = [sid | 0x40] 161 | if sid == 0x01: # PID 162 | while len(rx) > 0: 163 | pid_num = rx.pop(0) 164 | if (pid_num % 0x20) == 0: # Supported PIDs 165 | tx += [pid_num] + self.__get_supported_pids(pid_num, ecu) 166 | else: 167 | data = self.__encode_pid_data(pid_num, ecu) 168 | if not data is None: 169 | tx += [pid_num] + data 170 | elif sid == 0x03: # DTCs 171 | num_dtcs = 0 172 | dtc_data = [] 173 | for dtc_name in ecu['dtcs']: 174 | if self.__values['dtc'][dtc_name]: 175 | dtc_num = int(ecu['dtcs'][dtc_name]['num'], 16) 176 | dtc_data.append((dtc_num >> 8) & 0xFF) 177 | dtc_data.append(dtc_num & 0xFF) 178 | num_dtcs += 1 179 | tx += [num_dtcs] + dtc_data 180 | else: 181 | tx = [0x7F, sid, 0x11] # NRC Service not supported 182 | #print(ecu['name']+' tx: '+str(tx)) 183 | isotp_socket.send(bytearray(tx)) 184 | 185 | def get_sig_names(self): 186 | return self.__sig_names 187 | def get_pid_names(self): 188 | return self.__pid_names 189 | def get_dtc_names(self): 190 | return self.__dtc_names 191 | def set_value(self, val_type, name, value): 192 | self.__values[val_type][name] = value 193 | def set_sig(self, name, value): 194 | self.__values['sig'][name] = value 195 | def set_pid(self, name, value): 196 | self.__values['pid'][name] = value 197 | def set_dtc(self, name, value): 198 | self.__values['dtc'][name] = value 199 | def get_value(self, val_type, name): 200 | return self.__values[val_type][name] 201 | def get_sig(self, name): 202 | return self.__values['sig'][name] 203 | def get_pid(self, name): 204 | return self.__values['pid'][name] 205 | def get_dtc(self, name): 206 | return self.__values['dtc'][name] 207 | def load_values(self, filename): 208 | self.__values = self.__load_json(filename) 209 | def save_values(self, filename): 210 | self.__save_json(filename, self.__values) 211 | 212 | if __name__ == '__main__': 213 | parser = argparse.ArgumentParser(description='Generates SocketCAN messages interactively according to a DBC file and OBD config') 214 | parser.add_argument('-i', '--interface', required=True, help='CAN interface, e.g. vcan0') 215 | parser.add_argument('-d', '--database', help='DBC file') 216 | parser.add_argument('-f', '--output_filename', help='Output to file in canplayer format') 217 | parser.add_argument('-o', '--obd_config', help='OBD config JSON file') 218 | parser.add_argument('-v', '--values', help='Values JSON file') 219 | args = parser.parse_args() 220 | 221 | if args.output_filename is None and args.interface is None: 222 | print("error: --interface argument is required") 223 | exit(1) 224 | 225 | c = canigen(args.interface, args.output_filename, args.database, args.values, args.obd_config) 226 | 227 | sig_completer = WordCompleter(c.get_sig_names()) 228 | path_completer = PathCompleter() 229 | pid_completer = WordCompleter(c.get_pid_names()) 230 | dtc_completer = WordCompleter(c.get_dtc_names()) 231 | cmd_completion_dict = { 232 | 'set': {'sig':sig_completer,'pid':pid_completer,'dtc':dtc_completer}, 233 | 'get': {'sig':sig_completer,'pid':pid_completer,'dtc':dtc_completer}, 234 | 'exit': None, 235 | 'load': path_completer, 236 | 'save': path_completer 237 | } 238 | cmd_completer = NestedCompleter.from_nested_dict(cmd_completion_dict) 239 | 240 | def print_help(): 241 | print("Usage:") 242 | print(" set sig ") 243 | print(" get sig ") 244 | print(" set pid ") 245 | print(" get pid ") 246 | print(" set dtc ") 247 | print(" get dtc ") 248 | print(" load ") 249 | print(" save ") 250 | 251 | session = PromptSession() 252 | try: 253 | while True: 254 | cmd = session.prompt('canigen$ ', completer=cmd_completer).split() 255 | try: 256 | if len(cmd) == 0: 257 | pass 258 | elif cmd[0] == 'exit' or cmd[0] == 'quit': 259 | break 260 | elif cmd[0] == 'set': 261 | try: 262 | c.set_value(cmd[1], cmd[2], float(cmd[3])) 263 | except: 264 | print('error: invalid value') 265 | elif cmd[0] == 'get': 266 | print(c.get_value(cmd[1], cmd[2])) 267 | elif cmd[0] == 'load': 268 | try: 269 | c.load_values(cmd[1]) 270 | except: 271 | pass 272 | elif cmd[0] == 'save': 273 | c.save_values(cmd[1]) 274 | else: 275 | print_help() 276 | except: 277 | print("error: invalid command") 278 | print_help() 279 | except KeyboardInterrupt: 280 | pass 281 | except: 282 | print("error: unknown") 283 | c.stop() 284 | -------------------------------------------------------------------------------- /vsim/api/cansim.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # Copyright 2020 Amazon.com, Inc. and its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0 4 | # Licensed under the Amazon Software License (the "License"). 5 | # You may not use this file except in compliance with the License. 6 | # A copy of the License is located at 7 | # http://aws.amazon.com/asl/ 8 | # or in the "license" file accompanying this file. This file is distributed 9 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 10 | # express or implied. See the License for the specific language governing 11 | # permissions and limitations under the License. 12 | 13 | import canigen 14 | import time 15 | import datetime 16 | import argparse 17 | 18 | parser = argparse.ArgumentParser(description='Generates SocketCAN messages for AWS IoT FleetWise demo') 19 | parser.add_argument('-i', '--interface', default='vcan0', help='CAN interface, e.g. vcan0') 20 | parser.add_argument('-o', '--only-obd', action='store_true', help='Only generate OBD messages') 21 | args = parser.parse_args() 22 | 23 | can_sim = canigen.canigen( 24 | interface=args.interface, 25 | database_filename=None if args.only_obd else 'hscan.dbc') 26 | 27 | def set_with_print(func, name, val): 28 | print(str(datetime.datetime.now())+" Set "+name+" to "+str(val)) 29 | func(name, val) 30 | 31 | try: 32 | while True: 33 | for i in range(1, 7): 34 | set_with_print(can_sim.set_sig, 'Gear', i) 35 | set_with_print(can_sim.set_sig, 'VehicleSpeed', i*10) 36 | time.sleep(2) 37 | except KeyboardInterrupt: 38 | print("Stopping...") 39 | can_sim.stop() 40 | except: 41 | raise 42 | 43 | -------------------------------------------------------------------------------- /vsim/api/hscan.dbc: -------------------------------------------------------------------------------- 1 | VERSION "" 2 | 3 | NS_ : 4 | NS_DESC_ 5 | CM_ 6 | BA_DEF_ 7 | BA_ 8 | VAL_ 9 | CAT_DEF_ 10 | CAT_ 11 | FILTER 12 | BA_DEF_DEF_ 13 | EV_DATA_ 14 | ENVVAR_DATA_ 15 | SGTYPE_ 16 | SGTYPE_VAL_ 17 | BA_DEF_SGTYPE_ 18 | BA_SGTYPE_ 19 | SIG_TYPE_REF_ 20 | VAL_TABLE_ 21 | SIG_GROUP_ 22 | SIG_VALTYPE_ 23 | SIGTYPE_VALTYPE_ 24 | BO_TX_BU_ 25 | BA_DEF_REL_ 26 | BA_REL_ 27 | BA_DEF_DEF_REL_ 28 | BU_SG_REL_ 29 | BU_EV_REL_ 30 | BU_BO_REL_ 31 | SG_MUL_VAL_ 32 | 33 | BS_: 34 | 35 | BU_: UC_Frein UC_Car CMM UC_Dira Capt_Vol BV AAS BVMP SBW BSI ASR_plus ESM MDD Capt_ALG OUTIL_DIAG XCPonCAN Bosch_Plant Unknown MultipleNodes 36 | 37 | BO_ 1042 HS1_DAT_BSI_412: 8 UC_Car 38 | SG_ HS1_ETAT_OUVRANTS : 55|5@0+ (1,0) [0|31] "NOTDEF" UC_Frein 39 | SG_ HS1_REQ_DECON_ASR_ESP : 23|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 40 | SG_ HS1_CONTACT_FREIN1_HS : 0|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 41 | SG_ HS1_MARCHE_AR_BVM : 2|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 42 | SG_ HS1_CONTACT_FREIN_PRK : 3|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 43 | SG_ HS1_CONTACT_FREIN1 : 5|1@0+ (1,0) [0|1] "NOTDEF" UC_Frein 44 | 45 | BO_ 1458 HS1_CONTEXTE_1_5B2: 8 BSI 46 | SG_ HS1_TEMP_AIR_EXT : 23|8@0+ (0.5,-40) [-40|85] "C" UC_Frein 47 | 48 | BA_DEF_ SG_ "GenSigMappingName" STRING; 49 | BA_DEF_ BO_ "GenMsgSendType" ENUM "PERIODICAL","PERIODICAL-EVENT","EVENT","ONCE","ONCE-EVENT","NOTDEF"; 50 | BA_DEF_ SG_ "GenSigErrorValue" STRING; 51 | BA_DEF_ SG_ "GenSigStartValue" INT 0 65535; 52 | BA_DEF_ BO_ "GenMsgCycleTime" INT 0 65535; 53 | BA_DEF_ BO_ "GenMsgStartDelayTime" INT 0 65535; 54 | BA_DEF_ BO_ "GenMsgTimeoutTime" INT 0 65535; 55 | 56 | BA_DEF_ EV_ "GenEnvAutoGenCtrl" ENUM "No","Yes"; 57 | BA_DEF_ EV_ "GenEnvMsgOffset" INT 0 2147483647; 58 | BA_DEF_ EV_ "GenEnvMsgName" STRING; 59 | BA_DEF_ EV_ "GenEnvIsGeneratedSnd" ENUM "No","Yes"; 60 | BA_DEF_ SG_ "GenSigEnvVarType" ENUM "int","float","undef"; 61 | BA_DEF_ "GenEnvVarPrefix" STRING; 62 | BA_DEF_ "GenEnvVarEndingSnd" STRING; 63 | BA_DEF_ "GenEnvVarEndingDsp" STRING; 64 | 65 | BA_DEF_DEF_ "GenSigMappingName" "NOTDEF"; 66 | BA_DEF_DEF_ "GenMsgSendType" "NOTDEF"; 67 | BA_DEF_DEF_ "GenSigErrorValue" "NOTDEF"; 68 | BA_DEF_DEF_ "GenSigStartValue" 65535; 69 | BA_DEF_DEF_ "GenMsgCycleTime" 65535; 70 | BA_DEF_DEF_ "GenMsgStartDelayTime" 65535; 71 | BA_DEF_DEF_ "GenMsgTimeoutTime" 65535; 72 | 73 | BA_DEF_DEF_ "GenEnvAutoGenCtrl" "No"; 74 | BA_DEF_DEF_ "GenEnvMsgOffset" 0; 75 | BA_DEF_DEF_ "GenEnvMsgName" ""; 76 | BA_DEF_DEF_ "GenEnvIsGeneratedSnd" "No"; 77 | BA_DEF_DEF_ "GenSigEnvVarType" "undef"; 78 | BA_DEF_DEF_ "GenEnvVarPrefix" "Env"; 79 | BA_DEF_DEF_ "GenEnvVarEndingSnd" "_"; 80 | BA_DEF_DEF_ "GenEnvVarEndingDsp" "Dsp_"; 81 | 82 | BA_ "GenMsgTimeoutTime" BO_ 1042 500; 83 | BA_ "GenMsgStartDelayTime" BO_ 1042 0; 84 | BA_ "GenMsgCycleTime" BO_ 1042 1000; 85 | BA_ "GenMsgSendType" BO_ 1042 0; 86 | BA_ "GenSigMappingName" SG_ 1042 HS1_ETAT_OUVRANTS "HS1_ETAT_OUVRANTS"; 87 | BA_ "GenSigErrorValue" SG_ 1042 HS1_ETAT_OUVRANTS "NOTDEF;NOTDEF"; 88 | BA_ "GenSigStartValue" SG_ 1042 HS1_ETAT_OUVRANTS 0; 89 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_ETAT_OUVRANTS 1; 90 | BA_ "GenSigMappingName" SG_ 1042 HS1_REQ_DECON_ASR_ESP "HS1_REQ_DECON_ASR_ESP"; 91 | BA_ "GenSigErrorValue" SG_ 1042 HS1_REQ_DECON_ASR_ESP "NOTDEF;NOTDEF"; 92 | BA_ "GenSigStartValue" SG_ 1042 HS1_REQ_DECON_ASR_ESP 0; 93 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_REQ_DECON_ASR_ESP 1; 94 | BA_ "GenSigMappingName" SG_ 1042 HS1_CONTACT_FREIN1_HS "HS1_CONTACT_FREIN1_HS"; 95 | BA_ "GenSigErrorValue" SG_ 1042 HS1_CONTACT_FREIN1_HS "NOTDEF;NOTDEF"; 96 | BA_ "GenSigStartValue" SG_ 1042 HS1_CONTACT_FREIN1_HS 0; 97 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_CONTACT_FREIN1_HS 1; 98 | BA_ "GenSigMappingName" SG_ 1042 HS1_MARCHE_AR_BVM "HS1_MARCHE_AR_BVM"; 99 | BA_ "GenSigErrorValue" SG_ 1042 HS1_MARCHE_AR_BVM "NOTDEF;NOTDEF"; 100 | BA_ "GenSigStartValue" SG_ 1042 HS1_MARCHE_AR_BVM 0; 101 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_MARCHE_AR_BVM 1; 102 | BA_ "GenSigMappingName" SG_ 1042 HS1_CONTACT_FREIN_PRK "HS1_CONTACT_FREIN_PRK"; 103 | BA_ "GenSigErrorValue" SG_ 1042 HS1_CONTACT_FREIN_PRK "NOTDEF;NOTDEF"; 104 | BA_ "GenSigStartValue" SG_ 1042 HS1_CONTACT_FREIN_PRK 0; 105 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_CONTACT_FREIN_PRK 1; 106 | BA_ "GenSigMappingName" SG_ 1042 HS1_CONTACT_FREIN1 "HS1_CONTACT_FREIN1"; 107 | BA_ "GenSigErrorValue" SG_ 1042 HS1_CONTACT_FREIN1 "NOTDEF;NOTDEF"; 108 | BA_ "GenSigStartValue" SG_ 1042 HS1_CONTACT_FREIN1 0; 109 | BA_ "GenSigEnvVarType" SG_ 1042 HS1_CONTACT_FREIN1 1; 110 | BA_ "GenMsgTimeoutTime" BO_ 1458 5000; 111 | BA_ "GenMsgStartDelayTime" BO_ 1458 0; 112 | BA_ "GenMsgCycleTime" BO_ 1458 1000; 113 | BA_ "GenMsgSendType" BO_ 1458 0; 114 | -------------------------------------------------------------------------------- /vsim/api/mymodel.dbc: -------------------------------------------------------------------------------- 1 | ../../dbc/mymodel.dbc -------------------------------------------------------------------------------- /vsim/api/requirements.txt: -------------------------------------------------------------------------------- 1 | cantools 2 | python-can 3 | flask 4 | gunicorn 5 | can-isotp 6 | python-dotenv -------------------------------------------------------------------------------- /vsim/api/start-api.sh: -------------------------------------------------------------------------------- 1 | function ifup { 2 | typeset output 3 | output=$(ip link show "$1" up) && [[ -n $output ]] 4 | } 5 | 6 | while : ; do 7 | if ifup $CAN_IF; then 8 | break 9 | else 10 | echo "Waiting for $CAN_IF" 11 | sleep 3 12 | fi 13 | done 14 | 15 | gunicorn -b :3000 api:app -------------------------------------------------------------------------------- /vsim/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@cloudscape-design/components": "^3.0.103", 7 | "@cloudscape-design/global-styles": "^1.0.1", 8 | "@testing-library/jest-dom": "^5.16.5", 9 | "@testing-library/react": "^13.4.0", 10 | "@testing-library/user-event": "^13.5.0", 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0", 13 | "react-scripts": "5.0.1", 14 | "react-simple-image-slider": "^2.4.1", 15 | "web-vitals": "^2.1.4" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "start-api": "export CAN_IF=vcan0 && cd api && venv/bin/flask run --no-debugger", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "proxy": "http://localhost:5000", 43 | "devDependencies": { 44 | "bootstrap": "^5.2.2", 45 | "react-bootstrap": "^2.5.0", 46 | "react-bootstrap-range-slider": "^3.0.8" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vsim/public/Slide1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/demo-soafee-aws-iotfleetwise/58a17a0e3992099ee25aeecee8ad759f6c97f6d3/vsim/public/Slide1.jpg -------------------------------------------------------------------------------- /vsim/public/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/demo-soafee-aws-iotfleetwise/58a17a0e3992099ee25aeecee8ad759f6c97f6d3/vsim/public/car.png -------------------------------------------------------------------------------- /vsim/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 26 | Vehicle Simulator 27 | 60 | 61 | 62 | 63 |
64 | 72 | 73 | -------------------------------------------------------------------------------- /vsim/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /vsim/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /vsim/src/App.css: -------------------------------------------------------------------------------- 1 | .app-nav { 2 | position: sticky; 3 | top: 0; 4 | z-index: 1002; 5 | } 6 | 7 | .demo-content { 8 | min-height: calc(100vh - 310px); 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | justify-content: center; 13 | } 14 | 15 | .car-container { 16 | display: flex; 17 | align-items: center; 18 | justify-content: center; 19 | } 20 | 21 | .car-container .button-container { 22 | display: flex; 23 | height: calc(100vh - 800px); 24 | max-height: 250px; 25 | flex-direction: column; 26 | justify-content: space-between; 27 | } 28 | 29 | .gallery { 30 | display: flex; 31 | flex-direction: column; 32 | align-items: center; 33 | } 34 | 35 | .car { 36 | height: calc(100vh - 350px); 37 | } 38 | 39 | .bottom { 40 | margin-top: 20px 41 | } 42 | 43 | -------------------------------------------------------------------------------- /vsim/src/App.js: -------------------------------------------------------------------------------- 1 | import { Container, Tabs } from '@cloudscape-design/components'; 2 | import "@cloudscape-design/global-styles/index.css"; 3 | import React from 'react'; 4 | import 'react-bootstrap-range-slider/dist/react-bootstrap-range-slider.css'; 5 | import './App.css'; 6 | import Demo from './Demo'; 7 | import Gallery from './Gallery'; 8 | import TopNav from './TopNav'; 9 | 10 | 11 | function App() { 12 | 13 | return ( 14 |
15 | 16 | 17 | 23 | }, 24 | { 25 | label: "Architecture", 26 | id: "gallery", 27 | content: 28 | } 29 | ]} 30 | /> 31 | 32 | {/**/} 33 |
34 | ); 35 | } 36 | 37 | export default App; -------------------------------------------------------------------------------- /vsim/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /vsim/src/Demo.js: -------------------------------------------------------------------------------- 1 | import { Box, Button } from '@cloudscape-design/components'; 2 | import "@cloudscape-design/global-styles/index.css"; 3 | import React, { useEffect, useState } from 'react'; 4 | import { FormLabel } from 'react-bootstrap'; 5 | import RangeSlider from 'react-bootstrap-range-slider'; 6 | import 'react-bootstrap-range-slider/dist/react-bootstrap-range-slider.css'; 7 | import './App.css'; 8 | 9 | 10 | const Demo = () => { 11 | const [stateDoors, setStateDoor] = useState(0); 12 | const [airTemperature, setAirTemperature] = useState(0); 13 | const FRONT_LEFT_DOOR = 0b00001; 14 | const FRONT_RIGHT_DOOR = 0b00010; 15 | const REAR_LEFT_DOOR = 0b00100; 16 | const REAR_RIGHT_DOOR = 0b01000; 17 | const TRUNK_DOOR = 0b10000; 18 | 19 | useEffect(() => { 20 | fetch('/api/signal/DoorsState', { 21 | method: 'GET', 22 | headers: { 23 | 'Accept': 'application/json', 24 | 'Content-Type': 'application/json' 25 | } 26 | }).then(res => res.json()).then(data => { 27 | setStateDoor(data.value); 28 | }); 29 | fetch('/api/signal/AmbientAirTemperature', { 30 | method: 'GET', 31 | headers: { 32 | 'Accept': 'application/json', 33 | 'Content-Type': 'application/json' 34 | } 35 | }).then(res => res.json()).then(data => { 36 | setAirTemperature(data.value); 37 | }); 38 | }, []); 39 | 40 | const toggleDoor = (door) => { 41 | fetch('/api/signal/DoorsState', { 42 | method: 'POST', 43 | headers: { 44 | 'Accept': 'application/json', 45 | 'Content-Type': 'application/json' 46 | }, 47 | body: JSON.stringify({ 48 | value: (stateDoors & door) ? stateDoors & ~door : stateDoors | door 49 | }) 50 | }).then(res => res.json()).then(data => { 51 | setStateDoor(data.value); 52 | }); 53 | }; 54 | 55 | const setTemperature = (value) => { 56 | fetch('/api/signal/AmbientAirTemperature', { 57 | method: 'POST', 58 | headers: { 59 | 'Accept': 'application/json', 60 | 'Content-Type': 'application/json' 61 | }, 62 | body: JSON.stringify({ 63 | value: parseFloat(value) 64 | }) 65 | }).then(res => res.json()).then(data => { 66 | setAirTemperature(data.value); 67 | }); 68 | } 69 | 70 | const getStateDoor = (door) => { 71 | return (stateDoors & door) ? 'Opened' : 'Closed' 72 | } 73 | 74 | return ( 75 | 76 |
77 | External Air Temperature {airTemperature} °C 78 | setTemperature(changeEvent.target.value)} 84 | /> 85 |
86 |
87 |
88 |
89 | 90 |

91 | 92 |

93 |
94 |
95 | car 96 |
97 |
98 |
99 | 100 |

101 | 102 |

103 |
104 |
105 |
106 | 107 |
108 |
109 | ) 110 | } 111 | 112 | export default Demo; 113 | -------------------------------------------------------------------------------- /vsim/src/Gallery.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SimpleImageSlider from "react-simple-image-slider"; 3 | 4 | const images = [ 5 | { url: "Slide1.jpg" }, 6 | ]; 7 | 8 | const Gallery = () => { 9 | return ( 10 |
11 | 18 |
19 | ); 20 | }; 21 | 22 | export default Gallery; -------------------------------------------------------------------------------- /vsim/src/TopNav.js: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import TopNavigation from "@cloudscape-design/components/top-navigation"; 3 | 4 | const TopNav = () => { 5 | return ( 6 |
7 | 26 |
27 | ); 28 | } 29 | 30 | export default TopNav; -------------------------------------------------------------------------------- /vsim/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | table, th, td { 16 | border: 1px 17 | } -------------------------------------------------------------------------------- /vsim/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import 'bootstrap/dist/css/bootstrap.min.css'; 7 | 8 | 9 | const root = ReactDOM.createRoot(document.getElementById('root')); 10 | root.render( 11 | 12 | 13 | 14 | ); 15 | 16 | // If you want to start measuring performance in your app, pass a function 17 | // to log results (for example: reportWebVitals(console.log)) 18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 19 | reportWebVitals(); 20 | -------------------------------------------------------------------------------- /vsim/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /vsim/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | --------------------------------------------------------------------------------