12 | {% endif %}
13 | {% endmacro %}
14 |
--------------------------------------------------------------------------------
/car/simulator/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sensor-simulator",
3 | "description": "Node.js app for Cloud Derby game - simulating sensor data from a car",
4 | "version": "1.0.0",
5 | "main": "sensor.js",
6 | "author": "Google Inc.",
7 | "license": "Apache-2.0",
8 | "engines": {
9 | "node": ">=10.0.0"
10 | },
11 | "scripts": {
12 | "start": "node sensor.js"
13 | },
14 | "dependencies": {
15 | "@google-cloud/pubsub": "^0.12.0",
16 | "@google-cloud/storage": "^1.6.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://git-scm.com/docs/gitignore
2 |
3 | # ------------ Ignore
4 | .secrets
5 | project-id.sh*
6 | setenv-local.sh
7 | checkpoint_graph_def
8 | tensorflow-models
9 | **/node_modules
10 | **/target
11 | **/build
12 | **/tmp
13 | **/.idea
14 | **/*.pem
15 | **/*.log
16 | **/log.txt
17 | **/*.bak
18 | **/*.old
19 | **/nohup.out
20 | **/*.class
21 | **/package-lock.json
22 | **/*service-account.json
23 | **/app-generated.yml
24 |
25 | # ------------ Do not ignore
26 | !**/.gitignore
27 | !**/.cloudignore
28 |
--------------------------------------------------------------------------------
/cloud/controller/logs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | gcloud app logs tail --service default
--------------------------------------------------------------------------------
/admin/ml-image-updates/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloud-derby-image-sort",
3 | "description": "Sorting images for additional ML training based on crowdsourced pictures",
4 | "version": "1.0.1",
5 | "main": "app.js",
6 | "author": "Google Inc.",
7 | "license": "Apache-2.0",
8 | "engines": {
9 | "node": ">=8"
10 | },
11 | "scripts": {
12 | "start": "node app.js"
13 | },
14 | "dependencies": {
15 | "@google-cloud/storage": "1.6.0",
16 | "body-parser": "1.17.2",
17 | "dotenv": "5.0.1",
18 | "request": "2.85.0",
19 | "safe-buffer": "5.1.1"
20 | },
21 | "devDependencies": {
22 | "@google-cloud/nodejs-repo-tools": "^2.3.0",
23 | "uuid": "3.1.0"
24 | },
25 | "cloud-repo-tools": {
26 | "requiresProjectId": true,
27 | "requiresKeyFile": true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/cloud/ml/training/tensorboard.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | set -u # This prevents running the script if any of the variables have not been set
20 | set -e # Exit if error is detected during pipeline execution
21 |
22 | source ./setenv.sh
23 |
24 | set_python_path
25 |
26 | echo "Starting Tensorboard..."
27 | nohup tensorboard --logdir=${GCS_ML_BUCKET} --port=$TF_HTTP_PORT &
28 |
29 | sleep 2
30 |
31 | tail -f nohup.out
--------------------------------------------------------------------------------
/cloud/voice/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloud-derbydialogflow-webhook",
3 | "description": "Webhook for voice control of the Cloud Derby Car",
4 | "version": "0.0.1",
5 | "private": true,
6 | "license": "Apache Version 2.0",
7 | "author": "Google Inc.",
8 | "engines": {
9 | "node": ">=6.11.1"
10 | },
11 | "scripts": {
12 | "lint": "eslint --fix \"**/*.js\"",
13 | "start": "firebase serve --only functions",
14 | "deploy": "firebase deploy --only functions",
15 | "test": "npm run lint"
16 | },
17 | "dependencies": {
18 | "actions-on-google": "^1.5.1",
19 | "@google-cloud/pubsub": "^0.12.0",
20 | "firebase-admin": "^4.2.1",
21 | "firebase-functions": "^0.5.9",
22 | "particle-api-js": "^6.4.3"
23 | },
24 | "devDependencies": {
25 | "eslint": "^3.19.0",
26 | "eslint-config-semistandard": "^11.0.0",
27 | "eslint-config-standard": "^10.2.1",
28 | "eslint-plugin-import": "^2.3.0",
29 | "eslint-plugin-node": "^5.0.0",
30 | "eslint-plugin-promise": "^3.5.0",
31 | "eslint-plugin-standard": "^3.0.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Google Cloud Derby
2 | =====
3 |
4 | This project is designed to help you learn Google Cloud Platform (GCP)
5 | in a fun way by building a self driving robot car that plays ball game
6 | against other robots. We provide all the instructions to build hardware and software. With
7 | this project you will learn how to use various GCP services:
8 |
9 | - IoT Core
10 | - TensorFlow Object Detection API
11 | - Pub/Sub
12 | - Cloud Storage
13 | - Compute Engine
14 | - App Engine
15 | - Cloud Functions
16 | - DialogFlow
17 | - Security
18 | - Networking
19 | - IAM
20 |
21 | There are two subsystems in this project: (1) car, and (2) cloud.
22 | Software running on the cloud controls movement of the car.
23 | In order to do it, several interconnected modules work together and
24 | interact with the software running on the car via GCP PubSub and IoT Core.
25 | We use Raspberry Pi based GoPiGo cars made by Dexter for this game with wide angle camera
26 | and laser sensor.
27 |
28 | Architecture diagrams, contact information, and build instructions can be found on the
29 | [Project website](https://www.cloudderby.io).
--------------------------------------------------------------------------------
/setup/create-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #################################################################################
4 | # Create car to cloud communication resources - this is to be used by Dialogflow
5 | #################################################################################
6 |
7 | #
8 | # Copyright 2018 Google LLC
9 | #
10 | # Licensed under the Apache License, Version 2.0 (the "License");
11 | # you may not use this file except in compliance with the License.
12 | # You may obtain a copy of the License at
13 | #
14 | # https://www.apache.org/licenses/LICENSE-2.0
15 | #
16 | # Unless required by applicable law or agreed to in writing, software
17 | # distributed under the License is distributed on an "AS IS" BASIS,
18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | # See the License for the specific language governing permissions and
20 | # limitations under the License.
21 | #
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | source ./setenv-global.sh
27 |
28 | create_resources
--------------------------------------------------------------------------------
/cloud/ml/inference/vm-startup-script.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###########################################################
4 | # Script to be run when Inference VM restarts
5 | # This will start REST inference endpoint and web app
6 | ###########################################################
7 |
8 | #
9 | # Copyright 2018 Google LLC
10 | #
11 | # Licensed under the Apache License, Version 2.0 (the "License");
12 | # you may not use this file except in compliance with the License.
13 | # You may obtain a copy of the License at
14 | #
15 | # https://www.apache.org/licenses/LICENSE-2.0
16 | #
17 | # Unless required by applicable law or agreed to in writing, software
18 | # distributed under the License is distributed on an "AS IS" BASIS,
19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 | # See the License for the specific language governing permissions and
21 | # limitations under the License.
22 | #
23 |
24 | ACCOUNT="${USER}"
25 | #ACCOUNT="" # For example "user1team1pa"
26 | cd /home/${ACCOUNT}/cloud-derby/src/cloud/ml/inference
27 | echo "$(date) - starting inference app..." >> tmp/boot.log
28 | sudo -u ${ACCOUNT} ./run.sh
--------------------------------------------------------------------------------
/cloud/ml/inference/set-startup-script.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###########################################################
4 | # Configure inference VM to start python inference app n restart
5 | ###########################################################
6 |
7 | #
8 | # Copyright 2018 Google LLC
9 | #
10 | # Licensed under the Apache License, Version 2.0 (the "License");
11 | # you may not use this file except in compliance with the License.
12 | # You may obtain a copy of the License at
13 | #
14 | # https://www.apache.org/licenses/LICENSE-2.0
15 | #
16 | # Unless required by applicable law or agreed to in writing, software
17 | # distributed under the License is distributed on an "AS IS" BASIS,
18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | # See the License for the specific language governing permissions and
20 | # limitations under the License.
21 | #
22 |
23 | source ./setenv.sh
24 |
25 | set -u # This prevents running the script if any of the variables have not been set
26 | set -e # Exit if error is detected during pipeline execution
27 |
28 | gcloud compute instances add-metadata ${ML_VM} --metadata-from-file startup-script=vm-startup-script.sh
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google.com/conduct/).
--------------------------------------------------------------------------------
/cloud/controller/js/vision-response.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | /************************************************************
20 | This object is the response from the Vision.class and
21 | contains resulting data from Object Detection API
22 | ************************************************************/
23 | module.exports = class VisionResponse {
24 |
25 | constructor() {
26 | // List of bounding boxes for objects found by Object Detection
27 | this.bBoxes = [];
28 | }
29 |
30 | addBox(boundingBox) {
31 | this.bBoxes.push(boundingBox);
32 | }
33 | };
--------------------------------------------------------------------------------
/cloud/controller/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloud-derby-driving-controller",
3 | "description": "Driving Controller is a Node.js app that makes driving decisions for Cloud Derby",
4 | "version": "1.0.2",
5 | "main": "app.js",
6 | "author": "Google Inc.",
7 | "license": "Apache-2.0",
8 | "engines": {
9 | "node": ">=10.0.0"
10 | },
11 | "scripts": {
12 | "start": "node app.js",
13 | "deploy": "gcloud app deploy",
14 | "postinstall": "npm install @google-cloud/debug-agent",
15 | "test": "mocha 'tests/*_tests.js'"
16 | },
17 | "dependencies": {
18 | "@google-cloud/debug-agent": "^4.0.3",
19 | "@google-cloud/pubsub": "^0.12.0",
20 | "@google-cloud/storage": "^3.3.0",
21 | "body-parser": "^1.19.0",
22 | "cors": "^2.8.5",
23 | "dotenv": "^8.1.0",
24 | "express": "^4.17.1",
25 | "pug": "^2.0.4",
26 | "request": "^2.88.0",
27 | "safe-buffer": "^5.2.0"
28 | },
29 | "devDependencies": {
30 | "@google-cloud/nodejs-repo-tools": "^3.3.0",
31 | "ava": "^2.4.0",
32 | "chai": "^4.2.0",
33 | "mocha": "^6.2.0",
34 | "semistandard": "^14.2.0",
35 | "supertest": "^4.0.2",
36 | "uuid": "^3.3.3"
37 | },
38 | "cloud-repo-tools": {
39 | "requiresProjectId": true,
40 | "requiresKeyFile": true
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/cloud/controller/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ###############################################
20 | # Car Driving Controller Test
21 | ###############################################
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | source ../../setenv-global.sh
27 |
28 | ###############################################
29 | # MAIN
30 | ###############################################
31 | print_header "Testing Driving Controller"
32 |
33 | mkdir -p tmp
34 | CWD=$(pwd)
35 |
36 | cd js
37 | npm test
38 | cd $CWD
39 |
40 | print_footer "Driving Controller test completed."
--------------------------------------------------------------------------------
/third_party/tf-object-detection-sample/python/decorator.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | import os
20 | from functools import wraps
21 | from flask import request
22 | from flask import Response
23 |
24 | USERNAME = os.environ['INFERENCE_USER_NAME']
25 | PASSWORD = os.environ['INFERENCE_PASSWORD']
26 |
27 |
28 | def check_auth(username, password):
29 | return username == USERNAME and password == PASSWORD
30 |
31 |
32 | def authenticate():
33 | return Response(
34 | 'You have to login with proper credentials', 401,
35 | {'WWW-Authenticate': 'Basic realm="Login Required"'})
36 |
37 |
38 | def requires_auth(f):
39 | @wraps(f)
40 | def decorated(*args, **kwargs):
41 | auth = request.authorization
42 | if not auth or not check_auth(auth.username, auth.password):
43 | return authenticate()
44 | return f(*args, **kwargs)
45 | return decorated
46 |
--------------------------------------------------------------------------------
/cloud/ml/annotation/upload.bat:
--------------------------------------------------------------------------------
1 | ::
2 | :: Copyright 2018 Google LLC
3 | ::
4 | :: Licensed under the Apache License, Version 2.0 (the "License");
5 | :: you may not use this file except in compliance with the License.
6 | :: You may obtain a copy of the License at
7 | ::
8 | :: https://www.apache.org/licenses/LICENSE-2.0
9 | ::
10 | :: Unless required by applicable law or agreed to in writing, software
11 | :: distributed under the License is distributed on an "AS IS" BASIS,
12 | :: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | :: See the License for the specific language governing permissions and
14 | :: limitations under the License.
15 | ::
16 |
17 | :: --------------------------------------------------------------------
18 | :: This script uploads user created annotations into GCS for future merge
19 | :: with images from other users.
20 | :: --------------------------------------------------------------------
21 |
22 | :: Put unique GCS bucket name here - must be the same for all team members
23 | set GCS_BUCKET=annotated-images--version-
24 |
25 | :: Put file name here - different for each team member - DO NOT include "zip" extention in the name...
26 | set ZIP_FILE=userXXX
27 |
28 | cd C:\a-robot-images
29 |
30 | :: --- Create archive with user provided annotations and images
31 | "c:\Program Files\7-Zip\7z.exe" a -tzip -r %ZIP_FILE% *.xml *.jpg
32 |
33 | :: --- Upload annotations and images to GCS for transferred learning
34 | call gsutil cp %ZIP_FILE%.zip gs://%GCS_BUCKET%
35 |
36 | del %ZIP_FILE%.zip
37 |
38 | cd C:\cloud-derby\cloud\ml\annotation
--------------------------------------------------------------------------------
/cloud/ml/inference/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *.cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 | local_settings.py
55 |
56 | # Flask stuff:
57 | instance/
58 | .webassets-cache
59 |
60 | # Scrapy stuff:
61 | .scrapy
62 |
63 | # Sphinx documentation
64 | docs/_build/
65 |
66 | # PyBuilder
67 | target/
68 |
69 | # Jupyter Notebook
70 | .ipynb_checkpoints
71 |
72 | # pyenv
73 | .python-version
74 |
75 | # celery beat schedule file
76 | celerybeat-schedule
77 |
78 | # SageMath parsed files
79 | *.sage.py
80 |
81 | # dotenv
82 | .env
83 |
84 | # virtualenv
85 | .venv
86 | venv/
87 | ENV/
88 |
89 | # Spyder project settings
90 | .spyderproject
91 | .spyproject
92 |
93 | # Rope project settings
94 | .ropeproject
95 |
96 | # mkdocs documentation
97 | /site
98 |
99 | # mypy
100 | .mypy_cache/
--------------------------------------------------------------------------------
/third_party/dexter/LICENSE.md:
--------------------------------------------------------------------------------
1 | License Information
2 | ===================
3 |
4 | Software License
5 | ----------------
6 |
7 | **All code (including all scripts, firmware, drivers, examples, and any other
8 | software) is released under the [MIT License].**
9 |
10 | [MIT License]: http://choosealicense.com/licenses/mit/
11 |
12 | MIT License
13 |
14 | Copyright (c) 2017 Dexter Industries
15 |
16 | Permission is hereby granted, free of charge, to any person obtaining a copy
17 | of this software and associated documentation files (the "Software"), to deal
18 | in the Software without restriction, including without limitation the rights
19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20 | copies of the Software, and to permit persons to whom the Software is
21 | furnished to do so, subject to the following conditions:
22 |
23 | The above copyright notice and this permission notice shall be included in all
24 | copies or substantial portions of the Software.
25 |
26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 | SOFTWARE.
33 |
34 |
35 | Hardware License
36 | ----------------
37 |
38 | **The hardware designs are released under the [Creative Commons Attribution
39 | NonCommercial ShareAlike 4.0 License][CC4].**
40 |
41 | [CC4]: https://creativecommons.org/licenses/by-nc-sa/4.0/
--------------------------------------------------------------------------------
/cloud/ml/training/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | .idea/
7 |
8 | # C extensions
9 | *.so
10 |
11 | # Distribution / packaging
12 | .Python
13 | env/
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | *.egg-info/
27 | .installed.cfg
28 | *.egg
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | .hypothesis/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # dotenv
85 | .env
86 |
87 | # virtualenv
88 | .venv
89 | venv/
90 | ENV/
91 |
92 | # Spyder project settings
93 | .spyderproject
94 | .spyproject
95 |
96 | # Rope project settings
97 | .ropeproject
98 |
99 | # mkdocs documentation
100 | /site
101 |
102 | # mypy
103 | .mypy_cache/
104 |
--------------------------------------------------------------------------------
/cloud/ml/training/setenv.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###############################################################
4 | # Shared environment variables for Transferred Learning module
5 | ###############################################################
6 |
7 | #
8 | # Copyright 2018 Google LLC
9 | #
10 | # Licensed under the Apache License, Version 2.0 (the "License");
11 | # you may not use this file except in compliance with the License.
12 | # You may obtain a copy of the License at
13 | #
14 | # https://www.apache.org/licenses/LICENSE-2.0
15 | #
16 | # Unless required by applicable law or agreed to in writing, software
17 | # distributed under the License is distributed on an "AS IS" BASIS,
18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | # See the License for the specific language governing permissions and
20 | # limitations under the License.
21 | #
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | source ../../../setenv-global.sh
27 | source ../setenv-ml.sh
28 |
29 | ### Shall we run training locally on this VM or on the Google Cloud ML Engine?
30 | LOCAL_TRAINING=true
31 |
32 | ### What version of Google CMLE to use for remote training. Local training uses whatever TF you install
33 | CMLE_RUNTIME_VERSION=1.9
34 |
35 | ### What model to use for training. Model zoo: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
36 | MODEL=faster_rcnn_resnet101_coco_2018_01_28
37 |
38 | ### Which pre-trained model to use
39 | MODEL_CONFIG=${MODEL}-cloud-derby.config
40 |
41 | ### Which dataset to use
42 | MODEL_CONFIG_PATH=$(pwd)
43 |
44 | export TF_HTTP_PORT=8081
--------------------------------------------------------------------------------
/cloud/controller/js/bounding-box.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | /************************************************************
20 | Outline of the object as found by object detection API
21 | ************************************************************/
22 | module.exports = class BoundingBox {
23 | constructor(label, x, y, w, h, score) {
24 | // Label of the object - aka 'red_ball', 'home_base', 'border', 'obstacle', etc.
25 | this.label = label;
26 | // x coordinate - lower left (in pixels on the image)
27 | this.x = x;
28 | // y coordinate - lower left (in pixels on the image)
29 | this.y = y;
30 | // object width in pixels - to the right of the x (in pixels on the image)
31 | this.w = w;
32 | // object height in pixels - above of the y (in pixels on the image)
33 | this.h = h;
34 | // object detection probability - between 0 and 1
35 | this.score = score;
36 | }
37 |
38 | // Returns YY coordinate (top border of the object)
39 | yy() {
40 | return this.y + this.h;
41 | }
42 |
43 | // Returns XX coordinate (right border of the object)
44 | xx() {
45 | return this.x + this.w;
46 | }
47 | };
--------------------------------------------------------------------------------
/admin/ml-image-updates/sort.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###############################################
4 | # Sort images into subfolders (after run.sh is completed)
5 | # additional images
6 | ###############################################
7 | #
8 | # Copyright 2018 Google LLC
9 | #
10 | # Licensed under the Apache License, Version 2.0 (the "License");
11 | # you may not use this file except in compliance with the License.
12 | # You may obtain a copy of the License at
13 | #
14 | # https://www.apache.org/licenses/LICENSE-2.0
15 | #
16 | # Unless required by applicable law or agreed to in writing, software
17 | # distributed under the License is distributed on an "AS IS" BASIS,
18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | # See the License for the specific language governing permissions and
20 | # limitations under the License.
21 |
22 | set -u # This prevents running the script if any of the variables have not been set
23 | set -e # Exit if error is detected during pipeline execution
24 | # set -x # Print trace of commands after their arguments are expanded
25 |
26 | source ../../setenv-global.sh
27 |
28 | # This is the original bucket with images to be processed
29 | export SOURCE_BUCKET="derby-images-auto-sorted"
30 | # This is where all sorted images will be copied
31 | export DESTINATION_BUCKET="derby-images-sorted-by-score"
32 |
33 | ###############################################
34 | # MAIN
35 | ###############################################
36 | print_header "Starting image sort process by score"
37 |
38 | mkdir -p tmp
39 |
40 | # The service account is needed to get permissions to create resources
41 | gcloud auth activate-service-account --key-file=$SERVICE_ACCOUNT_SECRET
42 |
43 | cd js
44 |
45 | export GOOGLE_APPLICATION_CREDENTIALS=$SERVICE_ACCOUNT_SECRET
46 | nohup node sort.js &
47 |
48 | print_footer "Image sort by score has completed OK."
--------------------------------------------------------------------------------
/car/simulator/driver.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ###############################################
20 | # Car driving receiver simulator
21 | ###############################################
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | source ../../setenv-global.sh
27 |
28 | print_header "Driver simulator"
29 |
30 | TEST_COMMAND_SUBSCRIPTION=simulator_driving_command_subscription
31 | echo "Listen for messages from the driving controller..."
32 |
33 | if gcloud pubsub subscriptions list | grep $TEST_COMMAND_SUBSCRIPTION; then
34 | echo_my "Subscription '$TEST_COMMAND_SUBSCRIPTION' already exists for topic '$COMMAND_TOPIC'..."
35 | else
36 | echo_my "Creating a subscription '$TEST_COMMAND_SUBSCRIPTION' to topic '$COMMAND_TOPIC'..."
37 | gcloud pubsub subscriptions create $TEST_COMMAND_SUBSCRIPTION --topic $COMMAND_TOPIC | true
38 | fi
39 |
40 | MAX_MSGS=20
41 |
42 | RESULT="blah"
43 | while [ "$RESULT" != "" ]; do
44 | RESULT="$( gcloud beta pubsub subscriptions pull --auto-ack --limit=10 $TEST_COMMAND_SUBSCRIPTION )"
45 | echo "$RESULT"
46 | done
47 |
48 | # In some cases you may need to drop the subscription and re-create it
49 | # gcloud pubsub subscriptions delete $TEST_COMMAND_SUBSCRIPTION
50 |
51 | print_footer "Driver simulator completed - no more messages from the Driving Controller in the Subscription."
52 |
--------------------------------------------------------------------------------
/admin/export-windows-vm-image.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ###############################################################################
20 | # Export Windows VM annotation image into the GCS bucket so others can use it
21 | ###############################################################################
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | source ./setenv.sh
27 |
28 | ### GCS Bucket destination for the export
29 | EXPORT_BUCKET=windows-vm-image-${ADMIN_PROJECT_ID}
30 |
31 | ### Name of the Windows Image to be exported
32 | WINDOWS_IMAGE_NAME="windows-image-labeling-image"
33 |
34 | ###############################################################################
35 | # MAIN
36 | ###############################################################################
37 | print_header "Windows Image export started."
38 |
39 | if gsutil ls -p ${ADMIN_PROJECT_ID} | grep ${EXPORT_BUCKET}; then
40 | echo_my "Bucket ${EXPORT_BUCKET} found OK"
41 | else
42 | echo_my "Create GCS bucket for backup: '${EXPORT_BUCKET}'..."
43 | gsutil mb -p ${ADMIN_PROJECT_ID} gs://${EXPORT_BUCKET}
44 | fi
45 |
46 | gcloud compute images export --destination-uri gs://${EXPORT_BUCKET}/${WINDOWS_IMAGE_NAME} --image ${WINDOWS_IMAGE_NAME} --project ${ADMIN_PROJECT_ID}
47 |
48 | print_footer "SUCCESS: Export is complete and can be found in the GCS bucket '$EXPORT_BUCKET'."
--------------------------------------------------------------------------------
/admin/export-inference-vm-image.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ###############################################################################
20 | # Export Windows VM annotation image into the GCS bucket so others can use it
21 | ###############################################################################
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | source ./setenv.sh
27 |
28 | # GCS Bucket destination for the export
29 | EXPORT_BUCKET=inference-vm-image-${ADMIN_PROJECT_ID}
30 |
31 | # Name of the Windows Image to be exported
32 | IMAGE_NAME="inference-image"
33 |
34 | # Name of the source project with the VM image
35 | SOURCE_PROJECT="roman-test-oct9"
36 |
37 | ###############################################################################
38 | # MAIN
39 | ###############################################################################
40 | print_header "Inference Image export started."
41 |
42 | if gsutil ls -p ${ADMIN_PROJECT_ID} | grep ${EXPORT_BUCKET}; then
43 | echo_my "Bucket ${EXPORT_BUCKET} found OK"
44 | else
45 | echo_my "Create GCS bucket for backup: '${EXPORT_BUCKET}'..."
46 | gsutil mb -p ${ADMIN_PROJECT_ID} gs://${EXPORT_BUCKET}
47 | fi
48 |
49 | gcloud compute images export --destination-uri gs://${EXPORT_BUCKET}/${IMAGE_NAME} --image ${IMAGE_NAME} --project ${SOURCE_PROJECT}
50 |
51 | print_footer "SUCCESS: Export is complete and can be found in the GCS bucket '$EXPORT_BUCKET'."
--------------------------------------------------------------------------------
/car/simulator/sensor.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ###############################################
20 | # Car sensor simulator
21 | #
22 | # Simulates laser and other sensors by publishing data into the well known topic
23 | # from which driving controller reads the data
24 | ###############################################
25 |
26 | set -u # This prevents running the script if any of the variables have not been set
27 | set -e # Exit if error is detected during pipeline execution
28 |
29 | source ../../setenv-global.sh
30 | print_header "Sensor simulator"
31 |
32 | TEMP_DATA=$(pwd)/tmp
33 | INSTALL_FLAG=$TEMP_DATA/install.marker # Location where the install flag is set to avoid repeated installs
34 | mkdir -p $TEMP_DATA
35 |
36 | if [[ ! -f "$INSTALL_FLAG" ]]; then
37 | install_node
38 | touch ${INSTALL_FLAG}
39 | fi
40 |
41 | create_gcs_camera_bucket
42 |
43 | if [ $# -eq 1 ]; then
44 | export TEST_IMAGE_FILE=$1
45 | else
46 | export TEST_IMAGE_FILE=""
47 | fi
48 |
49 | export GOOGLE_APPLICATION_CREDENTIALS=${SERVICE_ACCOUNT_SECRET}
50 |
51 | # Local directory with test images
52 | export TEST_IMAGE_FOLDER=simulation-images
53 | # Delay in seconds to send test messages
54 | export DELAY=1
55 | # How many test messages to send - do not forget to have proper number of test images, otherwise it will circle around and repeat many times
56 | export NUM_ITERATIONS=1
57 |
58 | echo "TEST_IMAGE_FILE='$TEST_IMAGE_FILE'"
59 | cd js
60 | npm start
61 |
62 | print_footer "Sensor simulator has completed sending test messages."
--------------------------------------------------------------------------------
/admin/refresh-training-images.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##########################################################################################
20 | # This script updates annotated images for training in the Admin project
21 | ##########################################################################################
22 | set -u # This prevents running the script if any of the variables have not been set
23 | set -e # Exit if error is detected during pipeline execution
24 |
25 | #############################################
26 | # MAIN
27 | #############################################
28 | echo "Starting refresh of training images..."
29 |
30 | TMP="$(pwd)/tmp/image-refresh"
31 | if [ -d "$TMP" ]; then
32 | rm -rf $TMP
33 | fi
34 |
35 | mkdir -p $TMP
36 | cd $TMP
37 |
38 | echo "Download latest set of images that resulted from user merge.sh command..."
39 | gsutil cp gs://update-this-${PROJECT}-images-for-training-v-${VERSION}/* ./
40 |
41 | unzip annotations.zip
42 | unzip images-for-training.zip
43 | rm annotations.zip
44 | rm images-for-training.zip
45 |
46 | echo "Create fresh set of training images..."
47 | zip -r provided-images *
48 |
49 | echo "Upload new set of training images to shared GCS..."
50 | gsutil cp provided-images.zip gs://$GCS_SOURCE_IMAGES
51 |
52 | rm provided-images.zip
53 |
54 | echo "Make a backup copy of training images..."
55 | NOW=$(date +%Y-%m-%d-%H-%M-%S)
56 | gsutil cp gs://$GCS_SOURCE_IMAGES/provided-images.zip gs://robot-derby-backup/${NOW}-provided-images.zip
57 |
58 | echo "Training image refresh complete."
--------------------------------------------------------------------------------
/admin/ml-image-updates/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###############################################
4 | # Process images for re-training to expand the dataset based on additional images
5 | ###############################################
6 | #
7 | # Copyright 2018 Google LLC
8 | #
9 | # Licensed under the Apache License, Version 2.0 (the "License");
10 | # you may not use this file except in compliance with the License.
11 | # You may obtain a copy of the License at
12 | #
13 | # https://www.apache.org/licenses/LICENSE-2.0
14 | #
15 | # Unless required by applicable law or agreed to in writing, software
16 | # distributed under the License is distributed on an "AS IS" BASIS,
17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | # See the License for the specific language governing permissions and
19 | # limitations under the License.
20 |
21 | set -u # This prevents running the script if any of the variables have not been set
22 | set -e # Exit if error is detected during pipeline execution
23 | # set -x # Print trace of commands after their arguments are expanded
24 |
25 | source ../../setenv-global.sh
26 |
27 | # This is the original bucket with images to be processed
28 | #export CLOUD_BUCKET="robot-derby-backup"
29 | export CLOUD_BUCKET="images-crowdsourcing"
30 | # This is where all sorted images will be copied
31 | export DESTINATION_BUCKET="derby-images-auto-sorted"
32 |
33 | # This REST endpoint will be used for making inferences on images for classification purpose
34 | export INFERENCE_IP="104.197.196.4"
35 |
36 | ###############################################
37 | # MAIN
38 | ###############################################
39 | print_header "Starting image sort process"
40 |
41 | mkdir -p tmp
42 | CWD=$(pwd)
43 | # Location where the install flag is set to avoid repeated installs
44 | INSTALL_FLAG=$CWD/tmp/install.marker
45 |
46 | if [ -f "$INSTALL_FLAG" ]; then
47 | echo_my "File '$INSTALL_FLAG' was found = > no need to do the install since it already has been done."
48 | else
49 | install_node
50 | touch $INSTALL_FLAG
51 | fi
52 |
53 | # The service account is needed to get permissions to create resources
54 | gcloud auth activate-service-account --key-file=$SERVICE_ACCOUNT_SECRET
55 |
56 | cd $CWD/js
57 |
58 | #if [ -f "nohup.out" ] ; then
59 | # rm -rf nohup.out
60 | #fi
61 |
62 | export GOOGLE_APPLICATION_CREDENTIALS=$SERVICE_ACCOUNT_SECRET
63 | npm start
64 |
65 | print_footer "Image sort process has completed OK."
--------------------------------------------------------------------------------
/cloud/controller/js/sensor-message.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | /**************************************************************************
20 | sensorMessage object
21 | - carId - unique ID of the car that sent this message
22 | - balls - how many balls this car has collected so far
23 | - obstacle - is there an obstacle or not (boolean)
24 | - battery - percentage of the battery left (0-100)
25 | - laserDistance - front laser measured distance to the nearest obstacle
26 | - cameraImgPath - GCS path (gs://) to the image file from the front camera
27 | - timestampMs - timestamp in ms when this message was generated by the car
28 | Example of a message:
29 | { "carId": 1,
30 | "timestampMs": 1519065026429,
31 | "carState": {
32 | "ballsCollected": 1,
33 | "batteryLeft": 80
34 | },
35 | "sensors": {
36 | "frontLaserDistanceMm": 60,
37 | "frontCameraImagePath": "https://storage.googleapis.com/my-camera-1/images/image3.jpg"
38 | "frontCameraImagePathGCS": "gs://my-camera-1/images/image3.jpg"
39 | }
40 | }
41 | **************************************************************************/
42 | module.exports = class SensorMessage {
43 | constructor(carId, balls, obstacle, battery, laserDistance, cameraImgPath, cameraImgPathGCS, color) {
44 | // Timestamp is generated at the time of creation of the message, not at the time of sending it
45 | this.timestampMs = new Date().getTime();
46 | this.carId = carId;
47 | this.carState = {};
48 | this.carState.obstacleFound = obstacle;
49 | this.carState.ballsCollected = balls;
50 | this.carState.color = color;
51 | this.carState.batteryLeft = battery;
52 | this.sensors = {};
53 | this.sensors.frontLaserDistanceMm = laserDistance;
54 | this.sensors.frontCameraImagePath = cameraImgPath;
55 | this.sensors.frontCameraImagePathGCS = cameraImgPathGCS;
56 | }
57 | };
--------------------------------------------------------------------------------
/car/test_hardware.py:
--------------------------------------------------------------------------------
1 | # THIS SCRIPT WRITTEN FOR TESTING GOOGLE CLOUD DERBY HARDWARE
2 | # COPYRIGHT DEXTER INDUSTRIES, SEP 2019
3 |
4 | import gopigo3
5 | import time
6 | import easygopigo3 as easy
7 | import sys
8 | import atexit
9 | import urllib2
10 | import os.path
11 |
12 |
13 | gpg = easy.EasyGoPiGo3()
14 | atexit.register(gpg.stop)
15 |
16 | gpg.reset_all()
17 |
18 | def test_motors():
19 | print("Warning: The robot is about to move forward. ")
20 | time.sleep(1) # let's give the reset_all() some time to finish
21 | gpg.set_speed(300)
22 |
23 | print("Motor Test: test motor power.")
24 | gpg.forward()
25 | time.sleep(0.25)
26 | gpg.backward()
27 | time.sleep(0.25)
28 | gpg.stop()
29 | print("Motor Test: end motor power tests.")
30 |
31 | print ("Both motors moving Forward with Dex Eyes On")
32 | gpg.open_eyes()
33 | gpg.drive_cm(5)
34 | print ("Both motors moving back with blinkers On")
35 | gpg.blinker_on(1)
36 | gpg.blinker_on(0)
37 | gpg.drive_cm(-5)
38 | print("Motor Test: Encoders are ok!")
39 |
40 | def test_dist_sensor():
41 | my_distance_sensor = gpg.init_distance_sensor()
42 | # Directly print the values of the sensor.
43 | for i in range(0,5):
44 | print("Distance Sensor Reading (mm): " + str(my_distance_sensor.read_mm()))
45 | time.sleep(1)
46 |
47 | print("Distance sensor test complete!")
48 |
49 | def servo_test():
50 | for i in range(1000, 2001): # count from 1000 to 2000
51 | gpg.set_servo(gpg.SERVO_1, i)
52 | gpg.set_servo(gpg.SERVO_2, 3000-i)
53 | time.sleep(0.001)
54 | for i in range(1000, 2001): # count from 1000 to 2000
55 | gpg.set_servo(gpg.SERVO_2, i)
56 | gpg.set_servo(gpg.SERVO_1, 3000-i)
57 | time.sleep(0.001)
58 |
59 | def test_camera():
60 | print("Starting Camera Test Now.")
61 | # camera = PiCamera()
62 | from picamera import PiCamera
63 | from time import sleep
64 |
65 | camera = PiCamera()
66 | camera.start_preview()
67 | sleep(5)
68 | camera.capture('/home/pi/image.jpg')
69 | camera.stop_preview()
70 | fname = '/home/pi/image.jpg'
71 | if os.path.isfile(fname):
72 | print("Camera Worked OK!")
73 | else:
74 | print("ERROR WITH CAMERA CHECK!")
75 |
76 |
77 | def internet_on():
78 | try:
79 | urllib2.urlopen('http://216.58.192.142', timeout=1)
80 | return True
81 | except urllib2.URLError as err:
82 | return False
83 |
84 | def test_internet():
85 | print("Testing Internet!")
86 | if internet_on():
87 | print("ON!")
88 | else:
89 | print("NOT ON!")
90 |
91 |
92 | test_camera()
93 | test_motors()
94 | test_dist_sensor()
95 | servo_test()
96 | test_internet()
97 | gpg.reset_all() # Clean it up, turn it all off.
98 |
--------------------------------------------------------------------------------
/cloud/controller/js/settings.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 | const process = require('process'); // Required for mocking environment variables
19 |
20 | /************************************************************
21 | Settings for the car robot
22 | ************************************************************/
23 | module.exports = {
24 | // How many balls need to be collected to win the game
25 | BALLS_NEEDED: 3,
26 |
27 | // Diameter of the ball
28 | BALL_SIZE_MM: 60.64,
29 |
30 | // Width of the Home Base sign (letter size is 216mm, but the letter is not printed on 100% of the paper)
31 | HOME_WIDTH_MM: 200,
32 |
33 | // Height of the Home Base sign (letter size is 280mm, but the letter is not printed on 100% of the paper)
34 | HOME_HEIGHT_MM: 250,
35 |
36 | // Various labels returned by vision API, such as - aka "red_ball"
37 | BALL_LABEL_SUFFIX: process.env.BALL_LABEL_SUFFIX,
38 |
39 | // Various labels returned by vision API, such as - aka "red_home"
40 | HOME_LABEL_SUFFIX: process.env.HOME_LABEL_SUFFIX,
41 |
42 | // Distance from the camera to the ball in a fully captured position - this is defined by the location of the
43 | // camera when it is mounted on the ball gripper
44 | MIN_DISTANCE_TO_CAMERA_MM: 21,
45 |
46 | // Max car speed (wheel rotation degrees per second)
47 | MAX_SPEED: 1000,
48 |
49 | // Here is the camera model used in the car:
50 | // https://www.amazon.com/gp/product/B00RMV53Z2
51 | camera: {
52 |
53 | // Horizontal field of view for the camera mounted on the car - degrees out of 360
54 | H_FIELD_OF_VIEW: 120.0,
55 |
56 | // Size of the camera sensor is 1/4 inch - see more details: https://en.wikipedia.org/wiki/Image_sensor_format
57 | SENSOR_HEIGHT_MM: 2.7,
58 | SENSOR_WIDTH_MM: 3.6,
59 |
60 | // Focal length of the camera - it is adjustable, so we need to calibrate it before using this camera for navigation
61 | FOCAL_LENGTH_MM: 2.594,
62 |
63 | // Horizontal resolution
64 | HORIZONTAL_RESOLUTION_PIXELS: process.env.HORIZONTAL_RESOLUTION_PIXELS,
65 | VERTICAL_RESOLUTION_PIXELS: process.env.VERTICAL_RESOLUTION_PIXELS
66 | }
67 | };
--------------------------------------------------------------------------------
/cloud/controller/js/tests/api_failure_tests.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | var app = require('../app');
18 | var chai = require('chai');
19 | var request = require('supertest');
20 |
21 | const BALL_COLORS = require('../validate').BALL_COLORS;
22 | const DRIVING_MODES = require('../validate').DRIVING_MODES;
23 |
24 | var expect = chai.expect;
25 |
26 | describe('API Failure Tests (/api)', function() {
27 |
28 | it('GET /unknown: should return 404', function(done) {
29 | request(app)
30 | .get('/api/unknown')
31 | .end(function(err, res) {
32 | expect(res.statusCode).to.be.equal(404);
33 | expect(res.headers).to.have.property('access-control-allow-origin');
34 | done();
35 | });
36 | });
37 |
38 | it('PUT /config: should return 400 for a bad payload', function(done) {
39 | request(app)
40 | .put('/api/config')
41 | .send({'abc': 123})
42 | .end(function(err, res) {
43 | ///console.log(res);
44 | expect(res.body.success).to.be.false;
45 | expect(res.body.status).to.be.equal(400);
46 | expect(res.statusCode).to.be.equal(400);
47 | expect(res.headers).to.have.property('access-control-allow-origin');
48 | done();
49 | });
50 | });
51 |
52 | it('POST /messages/debug: should return 400 for a bad payload', function(done) {
53 | request(app)
54 | .post('/api/messages/debug')
55 | .send({'abc': 123})
56 | .end(function(err, res) {
57 | expect(res.body.success).to.be.false;
58 | expect(res.body.status).to.be.equal(400);
59 | expect(res.statusCode).to.be.equal(400);
60 | expect(res.headers).to.have.property('access-control-allow-origin');
61 | done();
62 | });
63 | });
64 |
65 | it('POST /messages/driving: should return 400 for a bad payload', function(done) {
66 | request(app)
67 | .post('/api/messages/driving')
68 | .send({'abc': 123})
69 | .end(function(err, res) {
70 | expect(res.body.success).to.be.false;
71 | expect(res.body.status).to.be.equal(400);
72 | expect(res.statusCode).to.be.equal(400);
73 | expect(res.headers).to.have.property('access-control-allow-origin');
74 | done();
75 | });
76 | });
77 |
78 | });
79 |
--------------------------------------------------------------------------------
/cloud/ml/setenv-ml.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #############################################################################
4 | # Shared environment variables for Machine Learning Module
5 | #############################################################################
6 |
7 | #
8 | # Copyright 2018 Google LLC
9 | #
10 | # Licensed under the Apache License, Version 2.0 (the "License");
11 | # you may not use this file except in compliance with the License.
12 | # You may obtain a copy of the License at
13 | #
14 | # https://www.apache.org/licenses/LICENSE-2.0
15 | #
16 | # Unless required by applicable law or agreed to in writing, software
17 | # distributed under the License is distributed on an "AS IS" BASIS,
18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | # See the License for the specific language governing permissions and
20 | # limitations under the License.
21 | #
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | ### Name of the GCE VM that runs local ML training job and inference
27 | ML_VM=ml-training-inference-$VERSION
28 |
29 | ### Where to store all training data in flight for ML
30 | export GCS_ML_BUCKET=gs://${PROJECT}-ml-$VERSION
31 |
32 | ### Where to export the final inference graph for predictions
33 | export ML_EXPORT_BUCKET=gs://${PROJECT}-ml-export-$VERSION
34 | export FROZEN_INFERENCE_GRAPH_GCS=$ML_EXPORT_BUCKET/frozen_inference_graph.pb
35 |
36 | ### Where to export automatically generated label map - from training into predictions
37 | export LABEL_MAP=cloud_derby_label_map.pbtxt
38 | export LABEL_MAP_GCS=$ML_EXPORT_BUCKET/$LABEL_MAP
39 |
40 | ### Version of TensorFlow to use
41 | ### Also used as parameter for Cloud Machine Learning, see https://cloud.google.com/ml-engine/docs/tensorflow/runtime-version-list
42 | export TF_VERSION=1.10
43 |
44 | ### Model configuration
45 | # How many objects of the same class to be found in the image - Default is 100
46 | max_detections_per_class=90
47 | # How many total detections per image for all classes - Default is 300
48 | max_total_detections=250
49 | # Filter all objects with the confidence score lower than this
50 | score_threshold=0.0000001
51 | # How many proposals to have after the first stage - Default is 300
52 | first_stage_max_proposals=300
53 |
54 | ### TF settings
55 | export TF_PATH=~/tensorflow
56 | export TF_MODEL_DIR=$PROJECT_DIR/tensorflow-models
57 | export MODEL_BASE=$TF_MODEL_DIR/models/research
58 | export TMP=$(pwd)/tmp
59 |
60 | ##################################################
61 | # Setup Python path and check TF version
62 | ##################################################
63 | set_python_path() {
64 | echo_my "set_python_path()..."
65 | local CWD=$(pwd)
66 | cd $TF_MODEL_DIR/models/research
67 | export PYTHONPATH=$(pwd):$(pwd)/slim:$(pwd)/object_detection
68 | echo_my "PYTHONPATH=$PYTHONPATH"
69 | cd $CWD
70 | }
71 |
--------------------------------------------------------------------------------
/cloud/ml/training/export-checkpoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #####################################################################################
4 | # If your model failed for any reason or it is still training and has not reached the
5 | # final number of training steps, you can still export the checkpoint to experiment with it.
6 | # If the model is still training, you will need to create a separate VM to do the export
7 | # (same steps as you used to create this VM). In order to do the export, edit the script
8 | # export-checkpoint.sh and update the variable to the checkpoint number found in the GCS
9 | # bucket as shown above (the actual number will be different for your bucket):
10 | # export CHECKPOINT_NUMBER=1735
11 | #####################################################################################
12 |
13 | #
14 | # Copyright 2018 Google LLC
15 | #
16 | # Licensed under the Apache License, Version 2.0 (the "License");
17 | # you may not use this file except in compliance with the License.
18 | # You may obtain a copy of the License at
19 | #
20 | # https://www.apache.org/licenses/LICENSE-2.0
21 | #
22 | # Unless required by applicable law or agreed to in writing, software
23 | # distributed under the License is distributed on an "AS IS" BASIS,
24 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25 | # See the License for the specific language governing permissions and
26 | # limitations under the License.
27 | #
28 |
29 | set -u # This prevents running the script if any of the variables have not been set
30 | set -e # Exit if error is detected during pipeline execution
31 |
32 | source ./setenv.sh
33 |
34 | CWD=$(pwd)
35 | TMP=$CWD/tmp
36 |
37 | #############################################
38 | # MAIN
39 | #############################################
40 | print_header "Exporting Frozen Inference Graph"
41 |
42 | set_python_path
43 |
44 | CHECKPOINT_NUMBER=$TRAINING_STEPS
45 | EXPORT_PATH=$TMP/export
46 |
47 | rm -rf ${EXPORT_PATH} | true # ignore if this does not exist yet
48 | mkdir -p ${EXPORT_PATH}
49 | cd $EXPORT_PATH
50 |
51 | gsutil cp ${GCS_ML_BUCKET}/train/model.ckpt-${CHECKPOINT_NUMBER}.* .
52 |
53 | echo_my "Processing checkpoint data..."
54 | python $TF_MODEL_DIR/models/research/object_detection/export_inference_graph.py \
55 | --input_type image_tensor \
56 | --pipeline_config_path $MODEL_CONFIG_PATH/${MODEL_CONFIG} \
57 | --trained_checkpoint_prefix model.ckpt-${CHECKPOINT_NUMBER} \
58 | --output_directory ./
59 |
60 | echo_my "Prepare GCS bucket..."
61 | gsutil mb -l $REGION -c regional ${ML_EXPORT_BUCKET} | true # ignore if it is already there
62 |
63 | echo_my "Copy frozen inference graph to GCS '$FROZEN_INFERENCE_GRAPH_GCS' for reuse in Obj Detection API..."
64 | gsutil cp frozen_inference_graph.pb $FROZEN_INFERENCE_GRAPH_GCS
65 |
66 | echo_my "Copy label map to GCS '$LABEL_MAP_GCS' for reuse in Obj Detection API..."
67 | cd $TMP
68 | gsutil cp object_detection/annotations/$LABEL_MAP $LABEL_MAP_GCS
69 |
70 | print_footer "Inference Graph export has completed."
--------------------------------------------------------------------------------
/third_party/tf-object-detection-sample/python/templates/upload.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
20 |
21 |
22 | Object Detection API
23 |
24 |
25 |
43 |
44 |
45 |
46 |
47 | {% from "_formhelpers.html" import render_field %}
48 |
Object Detection API
49 |
Upload a color photo file.
50 |
51 |
55 |
56 |
57 |
58 |
59 |
60 | {% if result|length > 0 %}
61 |
original
62 |
63 |
64 | original
65 |
66 | {% for name, img in result.iteritems() %}
67 | {% if name != 'original' and name != 'response_msg' %}
68 | {{ name }}
70 |
71 | {% endif %}
72 | {% endfor %}
73 |
74 |
75 |
76 | {{ result['response_msg'] }}
77 |
78 |
79 | {% endif %}
80 |
81 |
82 |
Detecting Objects...
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/setup/template-setenv-local.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##################################################################################
20 | # Environment settings that are unique to a development machine - these
21 | # do not get committed into repo and allow multi-user development. Example of this
22 | # can be found in "src/setup" folder.
23 | ##################################################################################
24 |
25 | set -u # This prevents running the script if any of the variables have not been set
26 | set -e # Exit if error is detected during pipeline execution
27 |
28 | echo "setenv-local.sh: start..."
29 |
30 | ### Automatically generate unique project ID for the first run and save it into a file. Later read it from file
31 | PROJECT_NAME_FILE="$PROJECT_DIR/project-id.sh"
32 |
33 | if [ -f "$PROJECT_NAME_FILE" ] ; then
34 | echo "Sourcing existing project file '$PROJECT_NAME_FILE'..."
35 | source $PROJECT_NAME_FILE
36 | else
37 | # Infer current project ID from the environment
38 | export PROJECT=$(gcloud info | grep "Project:" | sed -n -e "s/Project: \[//p" | sed -n -e "s/\]//p")
39 | fi
40 |
41 | echo "PROJECT='$PROJECT'"
42 | gcloud config set project "$PROJECT"
43 |
44 | ### Serial number of the car to distinguish it from all other cars possibly on the same project
45 | export CAR_ID=2
46 |
47 | ### How many training steps to take during TensorFlow model training
48 | TRAINING_STEPS=8000
49 |
50 | ### This controls which inference VM REST API will be used by the Driving Controller
51 | # true - use existing inference VM from the DEMO project
52 | # false - use inference VM within the same project as Driving Controller
53 | USE_DEMO_INFERENCE="false"
54 |
55 | ### Where do we want Driving Controller to be deployed? (current VM or App Engine)
56 | # true - deploy in local VM
57 | # false - deploy in App Engine
58 | DRIVING_CONTROLLER_LOCAL="true"
59 |
60 | ### This controls certain automated tasks and allows the script to create resources on behalf of the user
61 | FOUR_HOURS_HACKATHON="false"
62 |
63 | if $FOUR_HOURS_HACKATHON ; then
64 | AUTO_CREATE_IP="true"
65 | AUTO_CREATE_FIREWALL="true"
66 | SKIP_MANUAL_IMAGE_ANNOTATION="true"
67 | fi
68 |
69 | ### Used for multiple ML models to be deployed and compared against each other (this is added to VM names, IP names, GCS bucket names, etc.)
70 | export VERSION=50
71 |
72 | ### These are Region and Zone where you want to run your car controller - feel free to change as you see fit
73 | export REGION="us-central1"
74 | export ZONE="us-central1-f"
75 | export REGION_LEGACY="us-central" # there are corner cases where gcloud still references the legacy nomenclature
76 |
77 | echo "setenv-local.sh: done"
--------------------------------------------------------------------------------
/cloud/ml/inference/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ##################################################
4 | # Run Object Detection based on the model trained earlier by "transferred-learning"
5 | # Based on https://github.com/GoogleCloudPlatform/tensorflow-object-detection-example
6 | #
7 | # This code will run on a special GCE VM $ML_VM
8 | ##################################################
9 |
10 | #
11 | # Copyright 2018 Google LLC
12 | #
13 | # Licensed under the Apache License, Version 2.0 (the "License");
14 | # you may not use this file except in compliance with the License.
15 | # You may obtain a copy of the License at
16 | #
17 | # https://www.apache.org/licenses/LICENSE-2.0
18 | #
19 | # Unless required by applicable law or agreed to in writing, software
20 | # distributed under the License is distributed on an "AS IS" BASIS,
21 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 | # See the License for the specific language governing permissions and
23 | # limitations under the License.
24 | #
25 |
26 | set -u # This prevents running the script if any of the variables have not been set
27 | set -e # Exit if error is detected during pipeline execution
28 |
29 | source ../../../setenv-global.sh
30 | source ../setenv-ml.sh
31 |
32 | export PATH_TO_LABELS=$MODEL_BASE/object_detection/data/$LABEL_MAP
33 | export PATH_TO_CKPT=$PROJECT_DIR/checkpoint_graph_def
34 |
35 | ##################################################
36 | # This changes TF inference model to be used for web app
37 | ##################################################
38 | set_inference_model() {
39 | echo_my "Changing inference model..."
40 |
41 | # Point to the trained model
42 | FROM=$CWD/tmp/import
43 | mkdir -p $FROM
44 |
45 | echo_my "Download label map..."
46 | gsutil cp $LABEL_MAP_GCS $FROM/$LABEL_MAP
47 | PATH_TO_LABELS=${MODEL_BASE}/object_detection/data/$LABEL_MAP
48 | sudo ln -sf $FROM/$LABEL_MAP $PATH_TO_LABELS
49 | echo_my "Label map is setup at '$PATH_TO_LABELS'"
50 |
51 | echo_my "Download frozen inference graph from GCS '$FROZEN_INFERENCE_GRAPH_GCS'..."
52 | gsutil cp $FROZEN_INFERENCE_GRAPH_GCS $FROM/frozen_inference_graph.pb
53 |
54 | local DESTINATION_GRAPH=$PATH_TO_CKPT/frozen_inference_graph.pb
55 | if ! [ -d $PATH_TO_CKPT ] ; then
56 | mkdir $PATH_TO_CKPT
57 | fi
58 | sudo ln -sf $FROM/frozen_inference_graph.pb $DESTINATION_GRAPH
59 | echo_my "Frozen inference graph is setup at '$DESTINATION_GRAPH'"
60 | }
61 |
62 | ###############################################
63 | # MAIN
64 | ###############################################
65 | print_header "Starting Object Detection App..."
66 |
67 | mkdir -p tmp
68 | CWD=$(pwd)
69 | INSTALL_FLAG=$CWD/tmp/install.marker
70 |
71 | if [ -f "$INSTALL_FLAG" ]; then
72 | echo_my "File '$INSTALL_FLAG' was found = > no need to do the install since it already has been done."
73 | else
74 | set_inference_model
75 | echo_my "Setting up web app..."
76 | sudo pip install --upgrade Flask==0.12.2 WTForms==2.1 Flask_WTF==0.14.2 Werkzeug==0.12.2 numpy google-cloud-storage
77 | touch $INSTALL_FLAG
78 | fi
79 |
80 | set_python_path
81 | cd $CWD/python
82 |
83 | if [ -f "*.jpg" ] ; then
84 | # Remove old jpg files
85 | rm *.jpg
86 | fi
87 |
88 | if [ -f "nohup.out" ] ; then
89 | rm -rf nohup.out
90 | fi
91 |
92 | echo_my "Running webapp: Change USERNAME and PASSWORD in decorator.py..."
93 | # -u disables line buffering in python and shows everything in the nohup.out
94 | nohup python -u ./app.py &
95 |
96 | tail -f nohup.out
97 |
98 | echo_my "To watch output of the app log, use command: "
99 |
--------------------------------------------------------------------------------
/admin/README.md:
--------------------------------------------------------------------------------
1 | Cloud Derby Setup
2 | =====
3 |
4 | Scripts in the `admin` folder are used to setup and run Cloud Derby event in your
5 | own GCP organization or inside of the cloudderby.io.
6 |
7 | ## Create new environment to run a public hackathon
8 |
9 | The event settings can be updated in the [setenv.sh](setenv.sh) file:
10 |
11 | - `NUM_TEAMS` - How many teams will participate in the workshop (between 1 and N).
12 | - `NUM_PEOPLE_PER_TEAM` - How many people per team (usually between 1 and 6).
13 | - `EVENT_NAME` - Name of the event (this is added to user and group names, so keep it short, such as "PIT", or "DC").
14 | - `TOP_FOLDER` - Folder that holds all project sub-folders for users (use date of the event, such as "March-11-$EVENT_NAME").
15 | - `DOMAIN` - Domain name (the org name - whatever your org name is, such as "cloudderby.io", or "acme.com").
16 |
17 | In order to host Cloud Derby hackathon as a public event you need to run a hackathon setup script [create-hackathon.sh](create-hackathon.sh), which will do the following:
18 |
19 | - Generate user accounts and associated groups for teams.
20 | - Generate event folder and sub-folders for each team in the IAM structure.
21 | - Grant permissions to teams to their own folders.
22 | - Add groups to proper IAM policies to allow access to source repository and GCS bucket with annotated images.
23 |
24 | In order to run the script, you need to have GCloud SDK installed in Debian or Ubuntu bash command line and run the following:
25 |
26 | - `gcloud init` - this will initialize your admin credentials for the GCP Org.
27 | - `./create-hackathon.sh` - this will create the environment for you.
28 |
29 | After the completion of the script, you will have all users, groups and folders in the IAM structure and a file called users.csv in the `$HOME/cloud-derby/admin/tmp` subfolder.
30 |
31 | ## Collect user images after the hackathon
32 |
33 | During the hackathon your users will be taking photos and running robots around the room with cameras. This creates a
34 | great number of new images that can be used in subsequent training in future events to enhance model accuracy. In order
35 | to capture those user images you can use [collect-images.sh](collect-images.sh) script. This script will scan all user
36 | created folders, projects and buckets and collect all images into a single bucket under the "Administration" project.
37 |
38 | In order to use those images, human being needs to:
39 | - Download those images from one buckets
40 | - Remove repetitive images
41 | - Organize images in a proper structure (see ["Annotation"](https://bit.ly/robotderby) section of the tutorial)
42 | - Annotate said images
43 | - Run training and check model accuracy against the previous model (same steps as in "ML Training" part of the tutorial)
44 | - If the step above improves the accuracy, then merge annotated images with the base set
45 | - Remove collected images from the central bucket to prepare for future events
46 |
47 | ## Cleanup after the hackathon
48 |
49 | After you host the event for your audience, you want to make sure that all users and groups are promptly removed from the IAM and all resources (VMs, buckets and projects)
50 | are deleted to avoid unncecessary charges. This cleanup process is fully automated. Here is what you need to do from the bash command line in your Debian or Ubuntu:
51 |
52 | - `gcloud init` - this will initialize your admin credentials for the GCP Org.
53 | - Verify that the settings in [setenv.sh](setenv.sh) match your environment (aka name of the event, number
54 | of teams and users).
55 | - `./delete-hackathon.sh $FOLDER_ID` - this will erase users, groups, all folders, VMs, projects, etc. under the `$FOLDER_ID` the folder you created while generating new hackathon event.
56 | for the event (for example "March-11-Denver" mapped into folder ID "123456789").
57 | - If you do not want to remove users, groups and projects, you can run a script `./stop-vms.sh $FOLDER_ID` to stop all of the VMs in all nested sub-folders and projects under `$FOLDER_ID`.
58 |
--------------------------------------------------------------------------------
/admin/backup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ###############################################################################
20 | # Make a backup copy of all critical data intofrom the Org Domain a separate
21 | # account in case the Org get compromized and / or removed
22 | ###############################################################################
23 |
24 | set -u # This prevents running the script if any of the variables have not been set
25 | set -e # Exit if error is detected during pipeline execution
26 |
27 | source ./setenv.sh
28 |
29 | ### Directory for temp data
30 | TMP="tmp"
31 |
32 | ### Service account key for backup
33 | BACKUP_SERVICE_ACCOUNT_SECRET="${PROJECT_DIR}/.secrets/backup-service-account-secret.json"
34 |
35 | ###############################################################################
36 | # Prepare backup destinations
37 | ###############################################################################
38 | setup() {
39 | echo_my "Setup backup destinations..."
40 |
41 | if gsutil ls -p ${BACKUP_PROJECT_ID} | grep ${BACKUP_BUCKET}; then
42 | echo_my "Bucket ${BACKUP_BUCKET} found OK"
43 | else
44 | echo_my "Create GCS bucket for backup: '${BACKUP_BUCKET}'..."
45 | gsutil mb -l eu -c coldline -p ${BACKUP_PROJECT_ID} gs://${BACKUP_BUCKET}/
46 | fi
47 | }
48 |
49 | ###############################################################################
50 | # Make a copy of annotated images
51 | ###############################################################################
52 | backup_images() {
53 | local FOLDER=${BACKUP_BUCKET}/$NOW/images
54 | echo_my "Making a copy of annotated images from project '${ADMIN_PROJECT_ID}' bucket '${GCS_SOURCE_IMAGES}' into project '${BACKUP_PROJECT_ID}' bucket '${FOLDER}'"
55 |
56 | gsutil cp gs://${GCS_SOURCE_IMAGES}/* gs://${FOLDER}/
57 | }
58 |
59 | ###############################################################################
60 | # Make a copy of a git repo as plain file structure
61 | ###############################################################################
62 | backup_source() {
63 | local FOLDER=${BACKUP_BUCKET}/${NOW}
64 | echo_my "Making a copy of source files from project '${ADMIN_PROJECT_ID}' into project '${BACKUP_PROJECT_ID}' bucket '${FOLDER}'"
65 |
66 | local CWD=$(pwd)
67 | local TMP=${HOME}/tmp/repo
68 | rm -rf "$TMP" | true # ignore if does not exist
69 | mkdir -p $TMP
70 |
71 | # Clone the repo into a temp directory
72 | git clone https://github.com/GoogleCloudPlatform/cloud-derby $TMP/cloud-derby-source
73 |
74 | cd $TMP/cloud-derby-source
75 |
76 | # We only need to save source files, not the large amount of git metadata
77 | rm -rf .git
78 | zip -r source .
79 |
80 | gsutil cp source.zip gs://${FOLDER}/
81 |
82 | # Free up space
83 | cd $CWD
84 | rm -rf $TMP
85 | }
86 |
87 | ###############################################################################
88 | # MAIN
89 | ###############################################################################
90 | if [ -z ${1+x} ] ; then
91 | echo_my "BACKUP_PROJECT_ID not found. \n Usage: backup.sh [BACKUP_PROJECT_ID] \n Example: \n ./backup.sh cloud-derby-backup \n \n" $ECHO_ERROR
92 | exit 1
93 | fi
94 |
95 | BACKUP_PROJECT_ID=$1
96 | # Where to store backup data
97 | BACKUP_BUCKET="${BACKUP_PROJECT_ID}-bucket"
98 |
99 | NOW=$(date +%Y-%m-%d-%H-%M)
100 | print_header "Project backup started at $NOW."
101 |
102 | echo "Activating service account '${BACKUP_SERVICE_ACCOUNT_SECRET}'..."
103 | gcloud auth activate-service-account --key-file=${BACKUP_SERVICE_ACCOUNT_SECRET}
104 |
105 | setup
106 |
107 | backup_images
108 |
109 | backup_source
110 |
111 | print_footer "SUCCESS: Project backup complete."
--------------------------------------------------------------------------------
/cloud/controller/js/manual-driving.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 | const DriveMessage = require('./drive-message').DriveMessage;
19 |
20 | /************************************************************
21 | Display Manual Driving form on Get
22 | Input:
23 | - inboundMsgHistory
24 | ************************************************************/
25 | module.exports.manualDrivingForm = function (inboundMsgHistory) {
26 | let imageUrl;
27 |
28 | if (inboundMsgHistory.length > 0) {
29 | let msg = inboundMsgHistory[inboundMsgHistory.length - 1];
30 | if ((!(msg.data === undefined)) && (!(JSON.parse(msg.data).sensors === undefined)) && (!(JSON.parse(msg.data).sensors.frontCameraImagePath === undefined))) {
31 | imageUrl = JSON.parse(msg.data).sensors.frontCameraImagePath;
32 | }
33 | }
34 |
35 | let form = `Home / Refresh page
36 |
Manual car control
37 | `;
64 |
65 | // Add an image to the form
66 | if (!(imageUrl === undefined)) {
67 | form = form + '';
68 | }
69 |
70 | return form;
71 | };
72 |
73 | /************************************************************
74 | Send manual driving command to the car
75 | ************************************************************/
76 | module.exports.manualCommand = function (req) {
77 | let command = new DriveMessage();
78 |
79 | command.setModeManual();
80 |
81 | if (req.body.turn_speed_field) {
82 | command.setSpeed(req.body.turn_speed_field);
83 | }
84 |
85 | if (req.body.angle_field) {
86 | command.makeTurn(req.body.angle_field);
87 | }
88 |
89 | if (req.body.drive_speed_field) {
90 | command.setSpeed(req.body.drive_speed_field);
91 | }
92 |
93 | if (req.body.distance_field) {
94 | command.drive(req.body.distance_field);
95 | }
96 |
97 | if (req.body.ondemand_messages) {
98 | command.setOnDemandSensorRate();
99 | command.takePhoto();
100 | }
101 |
102 | if (req.body.gripper_open) {
103 | command.gripperOpen();
104 | }
105 |
106 | if (req.body.gripper_close) {
107 | command.gripperClose();
108 | }
109 |
110 | if (req.body.nonstop_messages) {
111 | command.setContinuousSensorRate();
112 | command.takePhoto();
113 | }
114 |
115 | command.sendSensorMessage();
116 |
117 | return command;
118 | };
--------------------------------------------------------------------------------
/cloud/controller/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###############################################################
4 | # Car Driving Controller
5 | # Can be run locally as node process or deployed into GAE
6 | ###############################################################
7 |
8 | #
9 | # Copyright 2018 Google LLC
10 | #
11 | # Licensed under the Apache License, Version 2.0 (the "License");
12 | # you may not use this file except in compliance with the License.
13 | # You may obtain a copy of the License at
14 | #
15 | # https://www.apache.org/licenses/LICENSE-2.0
16 | #
17 | # Unless required by applicable law or agreed to in writing, software
18 | # distributed under the License is distributed on an "AS IS" BASIS,
19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 | # See the License for the specific language governing permissions and
21 | # limitations under the License.
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 | # set -x # Print trace of commands after their arguments are expanded
26 |
27 | source ../../setenv-global.sh
28 |
29 | TMP="$(pwd)/tmp"
30 | mkdir -p ${TMP}
31 |
32 | ### Defines the name of the App Engine Flex App and forms part of URL
33 | APP_NAME=driving-controller
34 |
35 | ### Configuration of the deployment for App Engine
36 | YAML_FILE=$(pwd)/js/app-generated.yml
37 |
38 | ###############################################
39 | # This generates proper YAML connfig for the app
40 | ###############################################
41 | generate_yaml()
42 | {
43 | echo_my "Generating YAML config..."
44 | # Create 'app.yaml' file for the deployment configuration
45 | cat << EOF > $YAML_FILE
46 | # This file is auto-generated - DO NOT edit it manually as it will be overriden
47 | # Docs: https://cloud.google.com/appengine/docs/standard/nodejs/config/appref
48 |
49 | # App Engine Standard
50 | runtime: nodejs10
51 |
52 | # App Engine Flex
53 | # runtime: nodejs
54 |
55 | # This makes it run in App Engine Flex
56 | # env: flex
57 |
58 | manual_scaling:
59 | instances: 1
60 |
61 | env_variables:
62 | SENSOR_SUBSCRIPTION: $SENSOR_SUBSCRIPTION
63 | COMMAND_TOPIC: $COMMAND_TOPIC
64 | INFERENCE_USER_NAME: $INFERENCE_USER_NAME
65 | INFERENCE_PASSWORD: $INFERENCE_PASSWORD
66 | INFERENCE_IP: $INFERENCE_IP
67 | INFERENCE_URL: $INFERENCE_URL
68 | HTTP_PORT: $HTTP_PORT
69 | CAR_ID: $CAR_ID
70 | BALL_LABEL_SUFFIX: $BALL_LABEL_SUFFIX
71 | HOME_LABEL_SUFFIX: $HOME_LABEL_SUFFIX
72 | BLUE_BALL_LABEL: $BLUE_BALL_LABEL
73 | RED_BALL_LABEL: $RED_BALL_LABEL
74 | YELLOW_BALL_LABEL: $YELLOW_BALL_LABEL
75 | GREEN_BALL_LABEL: $GREEN_BALL_LABEL
76 | BLUE_HOME_LABEL: $BLUE_HOME_LABEL
77 | RED_HOME_LABEL: $RED_HOME_LABEL
78 | YELLOW_HOME_LABEL: $YELLOW_HOME_LABEL
79 | GREEN_HOME_LABEL: $GREEN_HOME_LABEL
80 | EOF
81 | }
82 |
83 | ###############################################
84 | # MAIN
85 | ###############################################
86 | print_header "Starting application '$APP_NAME'"
87 |
88 | CWD=$(pwd)
89 | # Location where the install flag is set to avoid repeated installs
90 | INSTALL_FLAG=${TMP}/install.marker
91 |
92 | if [ -f "$INSTALL_FLAG" ]; then
93 | echo_my "File '$INSTALL_FLAG' was found = > no need to do the install since it already has been done."
94 | else
95 | install_node
96 | touch $INSTALL_FLAG
97 | fi
98 |
99 | create_resources
100 |
101 | # Lookup actual IP address for inference VM from the static reference
102 | if $USE_DEMO_INFERENCE ; then
103 | # Driving controller will be using the inference VM that has been stood up in advance in a different project
104 | export INFERENCE_IP=$(gcloud compute addresses describe $DEMO_INFERENCE_IP_NAME --region us-central1 --format="value(address)" --project $DEMO_PROJECT)
105 | else
106 | # Find the IP of the inference VM that was created in this project
107 | export INFERENCE_IP=$(gcloud compute addresses describe $ML_IP_NAME --region $REGION --format="value(address)")
108 | fi
109 | echo_my "INFERENCE_IP=$INFERENCE_IP"
110 |
111 | cd $CWD/js
112 |
113 | if $DRIVING_CONTROLLER_LOCAL ;
114 | then
115 | echo_my "DRIVING_CONTROLLER_LOCAL='$DRIVING_CONTROLLER_LOCAL' (set it to false to deploy on GCP) - running on local machine (use this for test and dev only)..."
116 | # The default credentials below are needed for the controller to run locally in unix or mac dev environment when deployed locally
117 | export GOOGLE_APPLICATION_CREDENTIALS=$SERVICE_ACCOUNT_SECRET
118 | npm start
119 | else
120 | generate_yaml
121 | URL=https://${APP_NAME}-dot-${PROJECT}.appspot.com/
122 | echo_my "Deploying into Google App Engine using YAML file '${YAML_FILE}'. Current directory is '$(pwd)'..."
123 | yes | gcloud app deploy "${YAML_FILE}" --project ${PROJECT}
124 | # Ping the app to see if it is available
125 | curl -G "${URL}"
126 | echo_my "Running on GCP URL=$URL"
127 | fi
128 |
129 | print_footer "Driving Controller has been started."
130 |
--------------------------------------------------------------------------------
/admin/setenv.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##################################################################################
20 | # Environment settings that are to be kept secret and not exposed to the GitHub repo
21 | ##################################################################################
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | echo "setenv.sh: start..."
27 |
28 | source ../setenv-global.sh
29 |
30 | ### Billing accound ID used to pay for project resources
31 | BILLING_ACCOUNT_ID=""
32 |
33 | ### How many teams will participate in the workshop
34 | NUM_TEAMS=3
35 |
36 | # In case we need to add extra teams - start with some number, otherwise set it to 1
37 | TEAM_START_NUM=1
38 |
39 | ### How many people per team
40 | NUM_PEOPLE_PER_TEAM=2
41 |
42 | ### Name of the event - to be added to user and group names
43 | EVENT_NAME="preGA"
44 |
45 | ### Folder that holds all project sub-folders for users
46 | TOP_FOLDER="Oct-1-$EVENT_NAME"
47 |
48 | ### Directory for temp data
49 | TMP="tmp"
50 |
51 | ### Group that has all users and allows read only access to a whole bunch of shared resources
52 | ADMIN_READ_GROUP="read-only-group@$DOMAIN"
53 |
54 | ### GAM is a wonderful OSS tool to manage Users and Groups
55 | GAM="/home/${USER}/bin/gam/gam"
56 |
57 | ###############################################################################
58 | # Generate team name
59 | # Input:
60 | # 1 - team number
61 | ###############################################################################
62 | team_name() {
63 | echo "team${1}${EVENT_NAME}"
64 | }
65 |
66 | ###############################################################################
67 | # Generate user name
68 | # Input:
69 | # 1 - user number
70 | # 2 - team number
71 | ###############################################################################
72 | user_name() {
73 | echo "user${1}team${2}${EVENT_NAME}"
74 | }
75 |
76 | ###############################################################################
77 | # Generate name for team folder given team number
78 | # Input:
79 | # 1 - team #
80 | ###############################################################################
81 | team_folder_name() {
82 | echo "Team-${1}-resources"
83 | }
84 |
85 | ###############################################################################
86 | # Generate random password
87 | ###############################################################################
88 | generate_password() {
89 | local PASSWORD_LENGTH=10
90 | echo $(gpw 1 $PASSWORD_LENGTH)
91 | }
92 |
93 | ###############################################################################
94 | # Check prereqs and do install
95 | ###############################################################################
96 | setup() {
97 | mkdir -p $TMP
98 | INSTALL_FLAG=$TMP/install.marker
99 |
100 | if [ -f "$INSTALL_FLAG" ]; then
101 | echo_my "Marker file '$INSTALL_FLAG' was found = > no need to do the install."
102 | else
103 | echo_my "Marker file '$INSTALL_FLAG' was NOT found = > starting one time install."
104 | # Password generator
105 | sudo apt-get install gpw
106 | # GAM is an awesome GSuite management OSS tool: https://github.com/jay0lee/GAM/wiki
107 | bash <(curl -s -S -L https://git.io/install-gam)
108 | touch $INSTALL_FLAG
109 | fi
110 | }
111 |
112 | ###############################################################################
113 | # Find folder ID given its display name
114 | # Input:
115 | # 1 - folder display name
116 | ###############################################################################
117 | find_top_folder_id() {
118 | echo $(gcloud alpha resource-manager folders list --organization=$(lookup_org_id) \
119 | --filter=" displayName=$1" | grep $1 | sed -n -e "s/.* //p")
120 | }
121 |
122 | ###############################################################################
123 | # Find folder ID given its display name
124 | # Input:
125 | # 1 - folder display name
126 | # 2 - parent folder ID
127 | ###############################################################################
128 | find_folder_id() {
129 | echo $(gcloud alpha resource-manager folders list --folder=$2 \
130 | --filter=" displayName=$1" | grep $1 | sed -n -e "s/.* //p")
131 | }
132 |
133 | echo "setenv.sh: done"
134 |
--------------------------------------------------------------------------------
/admin/ml-image-updates/js/sort.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | * * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | 'use strict';
17 | console.log(`***Image sorting by score is starting up***`);
18 |
19 | // Imports
20 | const process = require('process'); // Required for mocking environment variables
21 | const request = require('request');
22 |
23 | const Storage = require('@google-cloud/storage');
24 | const storage = new Storage(process.env.PROJECT);
25 |
26 | const DESTINATION_BUCKET = process.env.DESTINATION_BUCKET;
27 | const SOURCE_BUCKET = process.env.SOURCE_BUCKET;
28 |
29 | // Global counter of processed files
30 | let progressCount = 0;
31 | let successCount = 0;
32 |
33 | /************************************************************
34 | Process one file
35 | Input:
36 | - Source File name
37 | Output:
38 | - New file name
39 | ************************************************************/
40 | function createNewName(fileName) {
41 | // Example input file name: 'count_10_high_7232_low_0522_file_4306.jpg'
42 | const keyword = '_low_';
43 |
44 | // If file name does not contain '_low_' keyword, then simply return the same name
45 | const keywordIndex = fileName.indexOf(keyword);
46 | if (keywordIndex < 0) {
47 | return fileName;
48 | }
49 |
50 | const keywordLength = keyword.length;
51 | const lowScoreFirstDigit = fileName[keywordIndex + keywordLength];
52 | const lastSlashPosition = fileName.lastIndexOf('/');
53 | return fileName.substring(0, lastSlashPosition + 1) + lowScoreFirstDigit + '/' + fileName.slice(lastSlashPosition + 1)
54 | }
55 |
56 | /************************************************************
57 | Copy file from one bucket into another
58 | Input:
59 | - source GCS URI
60 | - destination GCS URI
61 | ************************************************************/
62 | async function gcsCopy(srcBucket, srcFile, destBucket, destFile) {
63 | // console.log('Copy from to ');
64 | await storage.bucket(srcBucket).file(srcFile).copy(storage.bucket(destBucket).file(destFile))
65 | .catch(function (error) {
66 | console.error('!!!!!!!!!!!!! ERROR: Failed to copy a file: ' + destFile + ' with error: ' + error);
67 | });
68 | }
69 |
70 | /************************************************************
71 | Delete file from the bucket
72 | Input:
73 | - source GCS URI
74 | ************************************************************/
75 | async function gcsDelete(bucket, file) {
76 | // console.log('Deleting file: '+file);
77 | storage.bucket(bucket).file(file).delete()
78 | .catch(function (error) {
79 | console.error("!!!!!!!!!!!! Failed to delete a file: " + error);
80 | });
81 | }
82 |
83 | /************************************************************
84 | Recursively process list of files
85 | Input:
86 | - List of files to be processed
87 | Output:
88 | - None
89 | ************************************************************/
90 | async function processFilesAsync(files) {
91 | for (let file of files) {
92 | console.log('#' + progressCount);
93 | progressCount++;
94 | let newName = createNewName(file.name);
95 |
96 | // TODO - this needs to be async, but in batches so as to not overflow the memory for 80,000+ files
97 | await gcsCopy(SOURCE_BUCKET, file.name, DESTINATION_BUCKET, newName)
98 | .then(() => {
99 | gcsDelete(SOURCE_BUCKET, file.name);
100 | console.log('completed ' + successCount);
101 | // console.log('completed ' + successCount + ': ' + 'Copy from to'
102 | // + ' ');
103 | successCount++;
104 | })
105 | .catch(function (error) {
106 | console.error('!!! Error processing file <' + file.name + '> with the error: ' + error);
107 | });
108 | }
109 | }
110 |
111 | /************************************************************
112 | MAIN
113 | ************************************************************/
114 | console.log("Starting sorting by score...");
115 |
116 | // let name = createNewName('BlueBall/count_10_high_7232_low_0522_file_4306.jpg');
117 | // console.log('New name = ' + name);
118 |
119 | let bucket = storage.bucket(SOURCE_BUCKET);
120 |
121 | // bucket.getFiles({}, (err, files) => {console.log(err,files)});
122 | bucket.getFiles({}, (err, files) => {
123 | if (err) {
124 | console.error('!!! ERROR listing of files in bucket <: ' + SOURCE_BUCKET + '>: ' + err);
125 | } else {
126 | console.log('Bucket <' + SOURCE_BUCKET + '> contains <' + files.length + '> files.');
127 | processFilesAsync(files).then(() => {
128 | console.log('# of files processed successfully: ' + successCount);
129 | })
130 | }
131 | });
--------------------------------------------------------------------------------
/admin/collect-images.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ###############################################################################
20 | # The script will collect all photos from any cloudderby event into a defined bucket under Source and Administration project
21 | # by scanning all folders and projects and buckets to download user images
22 | ###############################################################################
23 |
24 | set -u # This prevents running the script if any of the variables have not been set
25 | set -e # Exit if error is detected during pipeline execution
26 |
27 | source ./setenv.sh
28 |
29 | # Where do we want to copy all these images
30 | DESTINATION_BUCKET="crowd-sourced-images-archive"
31 |
32 | # How many GCS buckets were found
33 | BUCKETS_TOTAL=0
34 |
35 | # How many buckets were copied
36 | BUCKETS_COPIED=0
37 |
38 | # How many projects were found
39 | PROJECTS_FOUND=0
40 |
41 | # How many folders found
42 | FOLDERS_FOUND=0
43 |
44 | ###############################################################################
45 | # Scan all folders and projects under a designated folder ID
46 | # Input
47 | # 1 - folder ID under which all VMs to be stopped
48 | ###############################################################################
49 | scan_folders() {
50 | local PARENT_FOLDER_ID=$1
51 | local FOLDER_ID
52 | FOLDERS_FOUND=$((FOLDERS_FOUND+1))
53 |
54 | echo_my "Scanning projects under folder id '$PARENT_FOLDER_ID'..."
55 | scan_projects "$PARENT_FOLDER_ID"
56 |
57 | local FOLDER_LIST=$(gcloud alpha resource-manager folders list --folder=$PARENT_FOLDER_ID --format="value(name)")
58 |
59 | while read -r FOLDER_ID; do
60 | if [[ ! -z "$FOLDER_ID" ]] ; then
61 | echo_my "Recursively processing folders under folder id '$FOLDER_ID'..."
62 | scan_folders "$FOLDER_ID"
63 | fi
64 | done <<< "$FOLDER_LIST"
65 | }
66 |
67 | ###############################################################################
68 | # Scan all projects under a given folder
69 | # Inputs
70 | # 1 - Folder ID
71 | ###############################################################################
72 | scan_projects() {
73 | echo_my "Scanning projects under folder '$1'..."
74 | local PROJECT_LIST=$(gcloud projects list --filter="parent.id=$1" --format="value(projectId)")
75 | local PROJ_ID
76 |
77 | while read -r PROJ_ID; do
78 | if [[ ! -z "$PROJ_ID" ]] ; then
79 | PROJECTS_FOUND=$((PROJECTS_FOUND+1))
80 | echo_my "Processing project id '$PROJ_ID'..."
81 | scan_buckets $PROJ_ID
82 | fi
83 | done <<< "$PROJECT_LIST"
84 | }
85 |
86 | ###############################################################################
87 | # Scan and process all GCS buckets in a project
88 | # Inputs
89 | # 1 - project ID
90 | ###############################################################################
91 | scan_buckets() {
92 | local BUCKET
93 | echo_my "Scanning buckets for project '$1'..."
94 |
95 | local BUCKET_LIST=$(gsutil ls -p $1 gs://)
96 |
97 | if [ -z ${BUCKET_LIST+x} ] ; then
98 | return # No buckets found in this project
99 | fi
100 |
101 | while read -r BUCKET; do
102 | if [[ ! -z "$BUCKET" ]] ; then
103 | echo_my "Processing bucket '$BUCKET'"
104 | BUCKETS_TOTAL=$((BUCKETS_TOTAL+1))
105 |
106 | if echo "$BUCKET" | grep -q "annotated-images" ; then
107 | BUCKETS_COPIED=$((BUCKETS_COPIED+1))
108 | echo_my "Copy contents of the bucket '$BUCKET'..."
109 | gsutil -m cp $BUCKET*.zip gs://${DESTINATION_BUCKET}/annotated-images/$1/ | true # Ignore if error or empty
110 | fi
111 |
112 | if echo "$BUCKET" | grep -q "camera-" ; then
113 | BUCKETS_COPIED=$((BUCKETS_COPIED+1))
114 | echo_my "Copy contents of the bucket '$BUCKET'..."
115 | gsutil -m cp $BUCKET*.jpg gs://${DESTINATION_BUCKET}/camera-images/$1/ | true # ignore if error or empty
116 | fi
117 | fi
118 | done <<< "$BUCKET_LIST"
119 | }
120 |
121 | ###############################################################################
122 | # MAIN
123 | ###############################################################################
124 |
125 | if [ -z ${1+x} ] ; then
126 | echo_my "NUMERIC_FOLDER_ID not found. \n Usage: collect-images.sh [NUMERIC_FOLDER_ID] \n Example: \n ./collect-images.sh 8763450985677 \n \n" $ECHO_ERROR
127 | exit 1
128 | fi
129 |
130 | # Process all projects and folders under this starting folder
131 | START_FOLDER=$1
132 |
133 | print_header "Collect all raw and annotated images from folder ID '$1'"
134 |
135 | scan_folders $START_FOLDER
136 |
137 | print_footer "SUCCESS: Found $PROJECTS_FOUND projects, $FOLDERS_FOUND folders, $BUCKETS_TOTAL buckets, including $BUCKETS_COPIED buckets with image content."
--------------------------------------------------------------------------------
/cloud/controller/js/tools.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**************************************************************************
18 | Various useful things
19 | **************************************************************************/
20 | const fs = require('fs');
21 |
22 | /**************************************************************************
23 | Generates random whole number between 0 and MAX
24 | Params:
25 | - Max - upper bound
26 | Returns:
27 | - Random whole number
28 | **************************************************************************/
29 | function randomWholeNum(max) {
30 | return Math.floor(Math.random() * max);
31 | }
32 |
33 | /**************************************************************************
34 | Converts degrees to radians
35 | Params:
36 | - Angle in Degrees
37 | Returns:
38 | - Angle in radians
39 | **************************************************************************/
40 | function toRadians(angle) {
41 | return angle * (Math.PI / 180);
42 | }
43 |
44 | function toDegrees(angle) {
45 | return angle * (180 / Math.PI);
46 | }
47 |
48 | /**************************************************************************
49 | Returns YYYYMMDD format of the date
50 | Params:
51 | - date
52 | Returns:
53 | - time in format YYYYMMDD
54 | **************************************************************************/
55 | function yyyymmdd(date) {
56 | Date.prototype.yyyymmdd = function () {
57 | const mm = this.getMonth() + 1; // getMonth() is zero-based
58 | const dd = this.getDate();
59 |
60 | return [this.getFullYear(), (mm > 9 ? '' : '0') + mm, (dd > 9 ? '' : '0') + dd].join('');
61 | };
62 |
63 | return new Date(date).yyyymmdd();
64 | }
65 |
66 | /**************************************************************************
67 | Returns YYYYMMDDHH format of the date
68 | Params:
69 | - date
70 | Returns:
71 | - time in format YYYYMMDDHH
72 | **************************************************************************/
73 | function yyyymmddhh(date) {
74 | return yyyymmdd(date) + date.toUTCString().substr(17, 2);
75 | }
76 |
77 | /**************************************************************************
78 | Returns HHMMSS format of the date
79 | Params:
80 | - date
81 | Returns:
82 | - time in format HHMMSS
83 | **************************************************************************/
84 | function hhmmss(date) {
85 | return date.toUTCString().substr(17, 8).replace(":", "").replace(":", "");
86 | }
87 |
88 | /**************************************************************************
89 | Generates random date between start and end
90 | Params:
91 | - Start date
92 | - End date
93 | Returns:
94 | - Random date between those two
95 | **************************************************************************/
96 | function randomDate(start, end) {
97 | return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
98 | }
99 |
100 | /**************************************************************************
101 | Capitalizes first letter of the string, or second letter if string starts with
102 | quotation mark
103 | Params:
104 | - String to process
105 | Returns:
106 | - String with the first capital letter
107 | **************************************************************************/
108 | function capitalizeFirstLetter(string) {
109 | if (string.charAt(0) == "\"") {
110 | return "\"" + string.charAt(1).toUpperCase() + string.slice(2);
111 | }
112 | return string.charAt(0).toUpperCase() + string.slice(1);
113 | }
114 |
115 | /**************************************************************************
116 | Get the size of the file in bytes
117 | Params:
118 | - Name of the file
119 | Returns:
120 | - Size in bytes
121 | **************************************************************************/
122 | function getFilesizeInBytes(filename) {
123 | const stats = fs.statSync(filename);
124 | return stats["size"];
125 | }
126 |
127 | /**************************************************************************
128 | Convert millimiters to inches
129 | Params:
130 | - millimiters
131 | Returns:
132 | - inches
133 | **************************************************************************/
134 | function mm2inches(mm) {
135 | return mm * 25.4;
136 | }
137 |
138 | /**************************************************************************
139 | Convert inches to millimiters
140 | Params:
141 | - inches
142 | Returns:
143 | - millimiters
144 | **************************************************************************/
145 | function inches2mm(inches) {
146 | return inches / 25.4;
147 | }
148 |
149 | /**************************************************************************
150 | Export these functions so they can be used outside
151 | **************************************************************************/
152 | module.exports = {
153 | randomWholeNum: randomWholeNum,
154 | toRadians: toRadians,
155 | toDegrees: toDegrees,
156 | mm2inches: mm2inches,
157 | inches2mm: inches2mm,
158 | yyyymmdd: yyyymmdd,
159 | yyyymmddhh: yyyymmddhh,
160 | hhmmss: hhmmss,
161 | randomDate: randomDate,
162 | capitalizeFirstLetter: capitalizeFirstLetter,
163 | getFilesizeInBytes: getFilesizeInBytes
164 | };
--------------------------------------------------------------------------------
/cloud/ml/training/create-training-vm.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###############################################
4 | # This script creates a GCE VM used for training using
5 | # transfer learning object detection with GPU.
6 | ###############################################
7 |
8 | #
9 | # Copyright 2018 Google LLC
10 | #
11 | # Licensed under the Apache License, Version 2.0 (the "License");
12 | # you may not use this file except in compliance with the License.
13 | # You may obtain a copy of the License at
14 | #
15 | # https://www.apache.org/licenses/LICENSE-2.0
16 | #
17 | # Unless required by applicable law or agreed to in writing, software
18 | # distributed under the License is distributed on an "AS IS" BASIS,
19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 | # See the License for the specific language governing permissions and
21 | # limitations under the License.
22 | #
23 |
24 | set -u # This prevents running the script if any of the variables have not been set
25 | set -e # Exit if error is detected during pipeline execution
26 |
27 | source ./setenv.sh
28 |
29 | #############################################
30 | # Create static external IP for the ML VM
31 | #############################################
32 | create_static_inference_ip()
33 | {
34 | if gcloud compute addresses list | grep $ML_IP_NAME; then
35 | echo_my "Static IP address $ML_IP_NAME found OK"
36 | else
37 | if [ ! -z ${AUTO_CREATE_IP+x} ]; then
38 | echo "Creating static external IP for ML VM... '$ML_IP_NAME'"
39 | gcloud compute addresses create $ML_IP_NAME --region $REGION
40 | else
41 | echo "Skipping automatic creation of static IP because AUTO_CREATE_IP variable is not set"
42 | fi
43 | fi
44 | }
45 |
46 | ###############################################
47 | # Open proper ports on a firewall
48 | ###############################################
49 | configure_firewall()
50 | {
51 | # Only configure firewall if we are in automatic "fast path" mode - aka users are not creating these things by hand
52 | if [ ! -z ${AUTO_CREATE_FIREWALL+x} ]; then
53 | open_http_firewall_port $HTTP_PORT
54 | else
55 | echo "Skipping automatic creation of firewall because AUTO_CREATE_FIREWALL variable is not set"
56 | fi
57 |
58 | # Deep Learning VM has pre-installed Python Lab on port 8080
59 | open_http_firewall_port 8080
60 |
61 | # Open Tensorboard port
62 | open_http_firewall_port $TF_HTTP_PORT
63 |
64 | open_ssh_firewall_port
65 | }
66 |
67 | ###############################################
68 | # Open HTTP port on a firewall
69 | # Input:
70 | # 1 - HTTP port to open
71 | ###############################################
72 | open_http_firewall_port()
73 | {
74 | local PORT=$1
75 | if gcloud compute firewall-rules list --format='table(NAME,NETWORK,DIRECTION,PRIORITY,ALLOW,DENY)' | grep "allow-http-$PORT"; then
76 | echo_my "Firewall rule 'allow-http-$PORT' found OK"
77 | else
78 | echo_my "Create firewall rule for port '$PORT'..."
79 | gcloud compute --project="$PROJECT" firewall-rules create \
80 | allow-http-$PORT --direction=INGRESS --priority=1000 \
81 | --network=default --action=ALLOW --rules=tcp:$PORT \
82 | --source-ranges=0.0.0.0/0 --target-tags=${HTTP_TAG} | true # Ignore if the firewall rule already exists
83 | fi
84 | }
85 |
86 | ###############################################
87 | # Open SSH port on a firewall
88 | ###############################################
89 | open_ssh_firewall_port()
90 | {
91 | if gcloud compute firewall-rules list --format='table(NAME,NETWORK,DIRECTION,PRIORITY,ALLOW,DENY)' | grep "allow-${SSH_TAG}"; then
92 | echo_my "Firewall rule for SSH was found"
93 | else
94 | echo_my "Create firewall rule for '$SSH_TAG'..."
95 | gcloud compute --project="$PROJECT" firewall-rules create \
96 | allow-${SSH_TAG} --direction=INGRESS --priority=1000 \
97 | --network=default --action=ALLOW --rules=tcp:22 \
98 | --source-ranges=0.0.0.0/0 --target-tags=${SSH_TAG} | true # Ignore if the firewall rule already exists
99 | fi
100 | }
101 |
102 | ###############################################
103 | # Create a VM on GCE with a certain number of GPUs
104 | # Inputs:
105 | # 1 - name of the VM
106 | # 2 - number of GPUs
107 | ###############################################
108 | create_gpu_vm()
109 | {
110 | local VM_NAME=$1
111 | local GPU_COUNT=$2
112 | echo_my "Create VM instance '$VM_NAME' with '$GPU_COUNT' GPUs in a project '$PROJECT'..."
113 | # See docs: https://cloud.google.com/deep-learning-vm/docs/quickstart-cli
114 | # https://cloud.google.com/deep-learning-vm/docs/tensorflow_start_instance
115 | gcloud compute --project="$PROJECT" instances create $VM_NAME \
116 | --zone $ZONE \
117 | --image-family=tf-latest-gpu \
118 | --image-project=deeplearning-platform-release \
119 | --boot-disk-size=70GB \
120 | --boot-disk-type=pd-ssd \
121 | --machine-type n1-highmem-2 \
122 | --accelerator="type=nvidia-tesla-v100,count=$GPU_COUNT" \
123 | --service-account $ALLMIGHTY_SERVICE_ACCOUNT \
124 | --maintenance-policy TERMINATE \
125 | --restart-on-failure \
126 | --subnet default \
127 | --address $ML_IP_NAME \
128 | --tags $HTTP_TAG,$SSH_TAG \
129 | --metadata="install-nvidia-driver=True" \
130 | --scopes=default,storage-rw,https://www.googleapis.com/auth/source.read_only
131 |
132 | echo_my "List of my instances..."
133 | gcloud compute --project="$PROJECT" instances list
134 | }
135 |
136 | #############################################
137 | # MAIN
138 | #############################################
139 | print_header "Create new Object Detection Training VM"
140 |
141 | configure_firewall
142 |
143 | create_static_inference_ip
144 |
145 | GPU_COUNT=1
146 | create_gpu_vm $ML_VM $GPU_COUNT
147 |
148 | print_footer "ML training VM Creation has completed."
--------------------------------------------------------------------------------
/admin/stop-vms.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ###############################################################################
20 | # This script traverses the org and stops all runnng VMs
21 | ###############################################################################
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | source ./setenv.sh
27 |
28 | # How many total Vms were found
29 | VMS_TOTAL=0
30 |
31 | # How many running Vms were stopped
32 | VMS_RUNNING=0
33 |
34 | # How many projects were found
35 | PROJECTS_FOUND=0
36 |
37 | # How many folders found
38 | FOLDERS_FOUND=0
39 |
40 | ###############################################################################
41 | # Scan all folders and projects under a designated folder ID
42 | # Input
43 | # 1 - folder ID under which all VMs to be stopped
44 | ###############################################################################
45 | scan_folders() {
46 | local PARENT_FOLDER_ID=$1
47 | local FOLDER_ID
48 | FOLDERS_FOUND=$((FOLDERS_FOUND+1))
49 |
50 | echo_my "Scanning projects under folder id '$PARENT_FOLDER_ID'..."
51 | scan_projects $PARENT_FOLDER_ID
52 |
53 | local FOLDER_LIST=$(gcloud alpha resource-manager folders list --folder=$PARENT_FOLDER_ID --format="value(name)")
54 |
55 | while read -r FOLDER_ID; do
56 | if [[ ! -z "$FOLDER_ID" ]] ; then
57 | echo_my "Recursively processing folders under folder id '$FOLDER_ID'..."
58 | scan_folders $FOLDER_ID
59 | fi
60 | done <<< "$FOLDER_LIST"
61 | }
62 |
63 | ###############################################################################
64 | # Scan all projects under a given folder
65 | # Inputs
66 | # 1 - Folder ID
67 | ###############################################################################
68 | scan_projects() {
69 | echo_my "Scanning projects under folder '$1'..."
70 | local PROJECT_LIST=$(gcloud projects list --filter="parent.id=$1" --format="value(projectId)")
71 | local PROJ_ID
72 |
73 | while read -r PROJ_ID; do
74 | if [[ ! -z "$PROJ_ID" ]] ; then
75 | PROJECTS_FOUND=$((PROJECTS_FOUND+1))
76 | echo_my "Processing project id '$PROJ_ID'..."
77 | stop_vms $PROJ_ID
78 | fi
79 | done <<< "$PROJECT_LIST"
80 | }
81 |
82 | ###############################################################################
83 | # Stop all VMs in a project
84 | # Inputs
85 | # 1 - project ID
86 | ###############################################################################
87 | stop_vms() {
88 | local VM_ID
89 | local PROJECT_ID=$1
90 | echo_my "Scanning VMs for project '$PROJECT_ID'..."
91 |
92 | local VM_LIST=$(gcloud compute instances list --project $PROJECT_ID --format="value(name)")
93 |
94 | echo $VM_LIST
95 |
96 | while read -r VM_ID; do
97 | if [[ ! -z "$VM_ID" ]] ; then
98 | VMS_TOTAL=$((VMS_TOTAL+1))
99 | # Get the zone of the instance
100 | local ZONE=$(gcloud compute instances list --filter="name:($VM_ID)" --project $PROJECT_ID --format="value(zone)")
101 | local STATUS=$(gcloud compute instances list --filter="name:($VM_ID)" --project $PROJECT_ID --format="value(status)")
102 | echo_my "Found VM id '$VM_ID' with status '$STATUS' in project '$PROJECT_ID'"
103 | if [ $STATUS = "RUNNING" ] ; then
104 | VMS_RUNNING=$((VMS_RUNNING+1))
105 | if [ $COUNT_RUNNING_VM_ONLY = false ] ; then
106 | echo_my "Stopping VM id '$VM_ID' in project '$PROJECT_ID'..."
107 | yes | gcloud compute instances stop $VM_ID --project $PROJECT_ID --zone=$ZONE | true # Ignore if error and proceed
108 | fi
109 | fi
110 | else
111 | echo_my "No more VMs found in this project"
112 | fi
113 | done <<< "$VM_LIST"
114 | }
115 |
116 | ###############################################################################
117 | # MAIN
118 | ###############################################################################
119 | if [ -z ${1+x} ] ; then
120 | echo_my "NUMERIC_FOLDER_ID not found. \n Usage: stop-vms.sh [NUMERIC_FOLDER_ID] \n Example: \n ./stop-vms.sh 8763450985677 \n \n" $ECHO_ERROR
121 | exit 1
122 | fi
123 |
124 | # Process all projects and folders under this starting folder
125 | START_FOLDER=$1
126 |
127 | print_header "Stop all running VMs"
128 |
129 | # If this is true, then running VMs will be counted, but not stopped
130 | COUNT_RUNNING_VM_ONLY=false
131 |
132 | echo_my "\nATTENTION!!!!!!!!!!!\nATTENTION!!!!!!!!!!!\nATTENTION!!!!!!!!!!!\n"
133 | echo_my "This will stop all running VMs under the folder --- '$START_FOLDER' ---. Are you sure you want to proceed?" $ECHO_WARNING
134 | pause
135 | echo_my "\nAre you sure you want to stop all running VMs ???????" $ECHO_WARNING
136 | pause
137 |
138 | if [ $COUNT_RUNNING_VM_ONLY = false ] ; then
139 | echo_my "COUNT_RUNNING_VM_ONLY=$COUNT_RUNNING_VM_ONLY - this script will STOP all running VMs."
140 | else
141 | echo_my "COUNT_RUNNING_VM_ONLY=$COUNT_RUNNING_VM_ONLY - this script will COUNT all running VMs."
142 | fi
143 |
144 | scan_folders $START_FOLDER
145 |
146 | print_footer "SUCCESS: Found $PROJECTS_FOUND projects, $FOLDERS_FOUND folders, $VMS_TOTAL VMs, including $VMS_RUNNING running VMs."
--------------------------------------------------------------------------------
/cloud/controller/js/simulation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 | const DriveMessage = require('./drive-message');
19 | const VisionResponse = require('./vision-response');
20 | const BoundingBox = require('./bounding-box');
21 | const Settings = require('./settings');
22 |
23 | /************************************************************
24 | Simulation for car commands. Pre-set messages being sent to the car regardless of input
25 | ************************************************************/
26 | class DriveMessageSimulator {
27 |
28 | constructor() {
29 | // Are we simulating driving commands or not?
30 | this.simulate = false;
31 | // Iteration of the fake driving command
32 | this.index = 0;
33 |
34 | // These are fake driving commands
35 | this.COMMANDS = [{turn1: 0, setSpeed: 80, driveForward: 100, turn2: 0, driveBackward: 0}, {
36 | turn1: -90,
37 | setSpeed: 80,
38 | driveForward: 0,
39 | turn2: 180,
40 | driveBackward: 0
41 | }, {turn1: 0, setSpeed: 80, driveForward: 40, turn2: 0, driveBackward: 0}, {
42 | turn1: 45,
43 | setSpeed: 80,
44 | driveForward: 0,
45 | turn2: -12,
46 | driveBackward: 0
47 | }, {turn1: 0, setSpeed: 80, driveForward: 60, turn2: 0, driveBackward: 0}, {
48 | turn1: -10,
49 | setSpeed: 80,
50 | driveForward: 0,
51 | turn2: 0,
52 | driveBackward: 0
53 | }, {turn1: 0, setSpeed: 80, driveForward: 80, turn2: 0, driveBackward: 0}, {
54 | turn1: 25,
55 | setSpeed: 80,
56 | driveForward: 0,
57 | turn2: 0,
58 | driveBackward: 0
59 | }, {turn1: 0, setSpeed: 80, driveForward: 10, turn2: 0, driveBackward: 0}, {
60 | turn1: -5,
61 | setSpeed: 80,
62 | driveForward: 0,
63 | turn2: 0,
64 | driveBackward: 0
65 | }];
66 | }
67 |
68 | /************************************************************
69 | Iterate over multiple fake driving commands and add them to the car message
70 | Input:
71 | - none
72 | Output:
73 | - driving command to be sent to the car
74 | ************************************************************/
75 | nextDrivingCommand() {
76 | let command = new DriveMessage();
77 | let fake = this.COMMANDS[this.index];
78 | command.makeTurn(fake.turn1);
79 | command.setSpeed(fake.setSpeed);
80 | command.driveForward(fake.driveForward);
81 | command.makeTurn(fake.turn2);
82 | command.driveBackward(fake.driveBackward);
83 |
84 | this.index++;
85 | if (this.index >= this.COMMANDS.length) {
86 | this.index = 0;
87 | }
88 | return command;
89 | }
90 | }
91 |
92 | /************************************************************
93 | Simulation for vision API when Object Detection is turned off
94 | ************************************************************/
95 | class VisionSimulator {
96 |
97 | constructor() {
98 | // Are we simulating vision responses or not?
99 | this.simulate = false;
100 | // Iteration of the fake vision responses
101 | this.index = 0;
102 |
103 | // These are fake vision responses
104 | this.COMMANDS = [[new BoundingBox("red_ball", 240, 230, 1, 1, 0.92), new BoundingBox("red_ball", 600, 200, 20, 20, 0.98), new BoundingBox("red_ball", 50, 10, 1, 1, 0.88), new BoundingBox("green_ball", 50, 70, 100, 200, 0.98), new BoundingBox("border", 10, 20, 10, 800, 0.92)], [new BoundingBox("blue_ball", 640, 30, 25, 25, 0.93)], [new BoundingBox("blue_ball", 40, 30, 25, 25, 0.97)], [new BoundingBox("blue_ball", 0, 30, 25, 25, 0.98)], [new BoundingBox("blue_ball", 4, 30, 25, 25, 0.96)], [new BoundingBox("yellow_ball", 440, 130, 30, 30, 0.92), new BoundingBox("green_ball", 50, 70, 100, 100, 0.91)], [new BoundingBox("blue_ball", 640, 30, 25, 25, 0.98)], [new BoundingBox("yellow_ball", 440, 130, 30, 30, 0.94), new BoundingBox("green_ball", 50, 70, 100, 100, 0.88)], [new BoundingBox("red_ball", 640, 30, 25, 25, 0.92)], [new BoundingBox("red_ball", 440, 130, 30, 30, 0.93), new BoundingBox("green_ball", 50, 70, 100, 100, 0.94)], // Ball occupies entire frame - testing capture method
105 | [new BoundingBox("red_ball", 0, 0, Settings.camera.HORIZONTAL_RESOLUTION_PIXELS, Settings.camera.VERTICAL_RESOLUTION_PIXELS, 0.92), new BoundingBox("blue_ball", 150, 170, 200, 200, 0.95)]];
106 | }
107 |
108 | /************************************************************
109 | Iterate over multiple fake driving commands and add them to the car message
110 | Input:
111 | - none
112 | Output:
113 | - vision response (taken from the hard-coded array above)
114 | ************************************************************/
115 | nextVisionResponse() {
116 |
117 | let response = new VisionResponse();
118 | let fake = this.COMMANDS[this.index];
119 | // Randomly generate fake objects
120 | for (let i = fake.length; i--;) {
121 | response.addBox(fake[i]);
122 | }
123 |
124 | this.index++;
125 | if (this.index >= this.COMMANDS.length) {
126 | this.index = 0;
127 | }
128 | return response;
129 | }
130 | }
131 |
132 | /**************************************************************************
133 | Module exports
134 | **************************************************************************/
135 | module.exports.DriveMessageSimulator = DriveMessageSimulator;
136 | module.exports.VisionSimulator = VisionSimulator;
--------------------------------------------------------------------------------
/car/simulator/js/sensor.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const process = require('process'); // Required for mocking environment variables
20 | var SensorMessage = require('../../../cloud/controller/js/sensor-message'); // Library for messages and sensors
21 |
22 | const SIMULATION_IMG_FOLDER = process.env.TEST_IMAGE_FOLDER; // Location of test images
23 | const BUCKET = process.env.CAR_CAMERA_BUCKET; // Bucket where images will be uploaded to
24 | const TOPIC = process.env.SENSOR_TOPIC; // Where to post messages
25 | const ITERATIONS = process.env.NUM_ITERATIONS; // How many messages to send
26 | const THINK_TIME = process.env.DELAY; // How long to wait between sending simulated messages to the topic
27 | const TEST_IMAGE_FILE = process.env.TEST_IMAGE_FILE; // If this is specified, it will be the only file to be tested
28 | const CAR_ID = process.env.CAR_ID;
29 |
30 | // Simulated messages content
31 | let data = [
32 | { "car": CAR_ID, "laser": 90, "balls": 0, "battery": 99, "color": "Blue" },
33 | { "car": CAR_ID, "laser": 80, "balls": 0, "battery": 90, "color": "Blue" },
34 | { "car": CAR_ID, "laser": 70, "balls": 0, "battery": 85, "color": "Blue" },
35 | { "car": CAR_ID, "laser": 60, "balls": 1, "battery": 80, "color": "Blue" },
36 | { "car": CAR_ID, "laser": 50, "balls": 1, "battery": 75, "color": "Blue" },
37 | { "car": CAR_ID, "laser": 40, "balls": 1, "battery": 70, "color": "Blue" },
38 | { "car": CAR_ID, "laser": 30, "balls": 2, "battery": 65, "color": "Blue" },
39 | { "car": CAR_ID, "laser": 20, "balls": 2, "battery": 60, "color": "Blue" },
40 | { "car": CAR_ID, "laser": 11, "balls": 2, "battery": 55, "color": "Blue" },
41 | { "car": CAR_ID, "laser": 10, "balls": 3, "battery": 50, "color": "Blue" },
42 | ];
43 |
44 | log(`START: Simulating car sensors... SIMULATION_IMG_FOLDER=${SIMULATION_IMG_FOLDER}, BUCKET=${BUCKET}, THINK_TIME=${THINK_TIME}, TOPIC=${TOPIC}`);
45 |
46 | // Pubsub client
47 | const PubSub = require('@google-cloud/pubsub');
48 | const pubsub = PubSub();
49 | const sensorTopic = pubsub.topic(TOPIC);
50 |
51 | // GCS client
52 | const Storage = require('@google-cloud/storage');
53 | const storage = new Storage();
54 |
55 | // Internal statistics variables
56 | let messagesSent = 0;
57 | let totalErrors = 0;
58 | let iteration = 0;
59 |
60 | /**************************************************************************
61 | Upload an image to GCS, for details see:
62 | https://github.com/googleapis/nodejs-storage/blob/master/samples/files.js
63 | **************************************************************************/
64 | function uploadImage(image) {
65 | let imagePath = `${SIMULATION_IMG_FOLDER}/${image}`;
66 | log(`uploadImage(): Uploading ${imagePath}...`);
67 | storage
68 | .bucket(BUCKET)
69 | .upload(imagePath)
70 | .then(() => {
71 | log(`${imagePath} uploaded to ${BUCKET}.`);
72 | })
73 | .catch(err => {
74 | console.error('ERROR:', err);
75 | process.exit(1);
76 | });
77 |
78 | return `gs://${BUCKET}/${image}`;
79 | }
80 |
81 | /**************************************************************************
82 | Single iteration of simulator
83 | **************************************************************************/
84 | function sendOneMessage() {
85 | let i = iteration;
86 | if (i >= data.length) {
87 | i = i % data.length;
88 | }
89 | log(`Iteration: ${iteration}, index: ${i}`);
90 | iteration++;
91 |
92 | let image;
93 | log("TEST_IMAGE_FILE='" + TEST_IMAGE_FILE+"'");
94 | if (TEST_IMAGE_FILE.length == 0) {
95 | log("Using images from the subfolder...");
96 | image = `image${i+1}.jpg`;
97 | }
98 | else {
99 | log("Using image " + TEST_IMAGE_FILE);
100 | image = TEST_IMAGE_FILE;
101 | }
102 |
103 | let imageGcsPath = uploadImage(`${image}`);
104 | // Make HTTP URL from the GCS one
105 | let imageUrl = "https://storage.googleapis.com/" + imageGcsPath.substr(5,imageGcsPath.length);
106 |
107 | // Make sure there is type checking and consistency if sensor message constructor is changed
108 | let message = JSON.stringify(
109 | new SensorMessage(data[i].car, data[i].balls, false, data[i].battery, data[i].laser, imageUrl, imageGcsPath, data[i].color));
110 |
111 | // Using timeout to make sure all GCS upload processes have completed before we send the message
112 | const sleepTimeMs = 1000;
113 | setTimeout(function sendMessage() {
114 | if (message.length > 0) {
115 | sensorTopic.publish(message, (err) => {
116 | if (err) {
117 | log(err);
118 | totalErrors++;
119 | return;
120 | }
121 | messagesSent++;
122 | log(`Message #${messagesSent} sent to PubSub: <${message}>`);
123 | });
124 | }
125 | else {
126 | log('Command is empty - Nothing to send');
127 | }
128 | }, sleepTimeMs);
129 |
130 | }
131 |
132 | /**************************************************************************
133 | * Log function
134 | * Returns: nothing
135 | **************************************************************************/
136 | function log(string) {
137 | console.log("simulator.js > " + string);
138 | }
139 |
140 | /************************************************************
141 | * MAIN
142 | ************************************************************/
143 | for (var i = 0; i < ITERATIONS; i++) {
144 | setTimeout(function() {
145 | sendOneMessage();
146 | }, i * THINK_TIME * 1000);
147 | }
148 |
149 | log("Sensor simulation finished.");
150 |
--------------------------------------------------------------------------------
/cloud/controller/js/vision.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 | const process = require('process'); // Required for mocking environment variables
19 | const request = require('request');
20 |
21 | // User credentials to authenticate to remote Inference VM service
22 | const INFERENCE_USER_NAME = process.env.INFERENCE_USER_NAME;
23 | const INFERENCE_PASSWORD = process.env.INFERENCE_PASSWORD;
24 |
25 | const APP_URL = `http://${process.env.INFERENCE_IP}`;
26 | const HTTP_PORT = process.env.HTTP_PORT;
27 | const INFERENCE_URL = process.env.INFERENCE_URL;
28 | const OBJECT_INFERENCE_API_URL = APP_URL + ':' + HTTP_PORT + INFERENCE_URL;
29 |
30 | require('dotenv').config();
31 | const VisionResponse = require('./vision-response');
32 | const BoundingBox = require('./bounding-box');
33 |
34 | // Initialize simulation engine (it may be On or Off)
35 | const VisionSimulator = require('./simulation').VisionSimulator;
36 | let visionSimulator = new VisionSimulator();
37 |
38 | /**************************************************************************
39 | Vision class calls Object Detection API to figure out where are all the
40 | balls in the image so navigation logic can use it for driving decisions
41 | **************************************************************************/
42 | module.exports = class Vision {
43 |
44 | constructor() {
45 | // Whatever needs to be done here...
46 | }
47 |
48 | /************************************************************
49 | Send image to ML and parse it
50 | Input:
51 | - sensorMessage - includes GCS URL to the Image (gs://...)
52 | Output:
53 | - VisionResponse - Coordinates of various objects that were recognized
54 | ************************************************************/
55 | recognizeObjects(sensorMessage) {
56 | console.log("vision.recognizeObjects(): start...");
57 |
58 | // Are we in a simulation mode? If so, return hard coded responses
59 | if (visionSimulator.simulate) {
60 | return Promise.resolve()
61 | .then(() => {
62 | console.log("vision.recognizeObjects(): returning a simulated response");
63 | return visionSimulator.nextVisionResponse();
64 | });
65 | }
66 |
67 | // Call REST API - Object Detection - ML Engine or TensorFlow
68 | // this returns a Promise which when resolved returns the VisionResponse object
69 | return this.recognizeObjectAPIAsync(sensorMessage)
70 | .then((response) => {
71 | return Promise.resolve()
72 | .then(() => {
73 | return this.createVisionResponse(response);
74 | });
75 | })
76 | .catch((error) => {
77 | console.log("vision.recognizeObjects(): Error calling remote Object Detection API: " + error);
78 | // In case of an error, return empty response
79 | return new VisionResponse();
80 | });
81 | }
82 |
83 | /************************************************************
84 | Generate response from the ML Vision
85 | Input:
86 | - jsonAPIResponse - response from the Vision API
87 | Output:
88 | - VisionResponse - Coordinates of various objects that were recognized
89 | ************************************************************/
90 | createVisionResponse(jsonAPIResponse) {
91 | let response = new VisionResponse();
92 | const objResponse = JSON.parse(jsonAPIResponse);
93 |
94 | for (let key in objResponse) {
95 | for (let i = 0; i < objResponse[key].length; i++) {
96 | //console.log("objResponse[key]["+i+"]: "+JSON.stringify(objResponse[key][i]));
97 | const bBox = new BoundingBox(key, objResponse[key][i]["x"], objResponse[key][i]["y"], objResponse[key][i]["w"], objResponse[key][i]["h"], objResponse[key][i]["score"]);
98 | response.addBox(bBox);
99 | }
100 | }
101 | return response;
102 | }
103 |
104 | /************************************************************
105 | Generate response from the ML Vision
106 | Input:
107 | - sensorMessage - message from the car with sensor data
108 | Output:
109 | -
110 | ************************************************************/
111 | recognizeObjectAPIAsync(sensorMessage) {
112 | return new Promise(function (resolve, reject) {
113 | const gcsURI = sensorMessage.sensors.frontCameraImagePathGCS;
114 |
115 | if (!gcsURI) {
116 | reject("Error: No gcURI found in sensorMessage");
117 |
118 | } else if (!gcsURI.startsWith("gs://")) {
119 | reject("Error: gcsURI must start with gs://");
120 |
121 | } else {
122 | // Example request for the inference VM: http://xx.xx.xx.xx:8082/v1/objectInference?gcs_uri=gs%3A%2F%2Fcamera-9-roman-test-oct9%2Fimage1.jpg
123 | const apiUrl = OBJECT_INFERENCE_API_URL + "?gcs_uri=" + encodeURIComponent(gcsURI);
124 | console.log("Vision API URL: " + apiUrl);
125 | // var visionResponse = new VisionResponse();
126 | const auth = {user: INFERENCE_USER_NAME, pass: INFERENCE_PASSWORD};
127 |
128 | // Measure the time it takes to call inference API
129 | const startTime = Date.now();
130 |
131 | request({uri: apiUrl, auth: auth}, function (err, response, body) {
132 | if (err) {
133 | console.log("!!! ERROR !!! calling remote ML API: " + err + ". Please verify that your Inference VM and the App are up and running and proper HTTP port is open in the firewall.");
134 | reject(err);
135 | } else {
136 | console.log("Vision API call took " + (Date.now() - startTime) + " ms. Result: " + body);
137 | if (response.statusCode != 200) {
138 | reject("Error: Received " + response.statusCode + " from API");
139 |
140 | } else {
141 | resolve(body);
142 | }
143 | }
144 | });
145 | }
146 | });
147 | }
148 | };
--------------------------------------------------------------------------------
/car/driver/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # Copyright 2018 Google LLC
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ###############################################
20 | # Control GoPiGo car by sending sensor messages
21 | # to the cloud and receiving driving commands
22 | ###############################################
23 |
24 | set -u # This prevents running the script if any of the variables have not been set
25 | set -e # Exit if error is detected during pipeline execution
26 |
27 | source ../../setenv-global.sh
28 |
29 | export GOOGLE_APPLICATION_CREDENTIALS="$SERVICE_ACCOUNT_SECRET"
30 |
31 | # This is subscription for the car to listen to incoming control messages
32 | export COMMAND_SUBSCRIPTION="driving-command-subscription-$CAR_ID"
33 |
34 | ### Obstacle avoidance - the number of millimeters to stop the car before hitting an object
35 | export BARRIER_DAMPENING="180"
36 |
37 | ### Car Camera position; UPSIDE DOWN=0; NORMAL=1 - this takes effect on the car as the image will be flipped before being sent to the cloud
38 | export CAR_CAMERA_NORMAL="0"
39 |
40 | ### What color ball this car will be playing (default value)
41 | export CAR_COLOR="red"
42 |
43 | ###############################################
44 | # This is run once after creating new environment
45 | ###############################################
46 | install() {
47 | echo_my "Installing Python, Pip and other libraries..."
48 | sudo apt-get install python
49 | sudo apt-get install python-pip
50 | sudo pip install --upgrade pip
51 | sudo pip install --upgrade google-cloud --ignore-installed six
52 | sudo pip install paho-mqtt
53 | sudo pip install --upgrade pip setuptools
54 | sudo pip install curtsies
55 | wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python
56 | sudo apt-get install build-essential libssl-dev libffi-dev python3-dev
57 | sudo pip install pyasn1 pyasn1-modules -U
58 | sudo pip install cryptography
59 | sudo pip install PyJWT
60 | sudo pip install Pillow
61 | wget https://pypi.python.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz#md5=d12789f9baf7e9fb2524c0c64f1773f8
62 | sudo tar -zxvf six-1.11.0.tar.gz
63 | sudo python ./six-1.11.0/setup.py install
64 | }
65 |
66 | ###############################################
67 | # One time car setup tasks
68 | ###############################################
69 | setup() {
70 | echo_my "Setting up car environment for the first run..."
71 | if which pip; then
72 | echo "Python and Pip are already installed, skipping this step."
73 | else
74 | install
75 | fi
76 |
77 | wget https://pki.google.com/roots.pem
78 | }
79 |
80 | ###############################################
81 | # Generate keys for the IOT device
82 | ###############################################
83 | generate_keys() {
84 | echo_my "Generating keys for the IOT device..."
85 | openssl req -x509 -newkey rsa:2048 -days 3650 -keyout rsa_private.pem -nodes -out \
86 | rsa_cert.pem -subj "/CN=unused"
87 | openssl ecparam -genkey -name prime256v1 -noout -out ec_private.pem
88 | openssl ec -in ec_private.pem -pubout -out ec_public.pem
89 | }
90 |
91 | ###############################################
92 | # MAIN
93 | ###############################################
94 | mkdir -p tmp
95 | echo_my "CAR_ID=$CAR_ID"
96 | INSTALL_FLAG=tmp/install.marker # Location where the install flag is set to avoid repeated installs
97 |
98 | if [ -f "$INSTALL_FLAG" ]; then
99 | echo_my "File '$INSTALL_FLAG' was found = > no need to do the install since it already has been done."
100 | else
101 | setup
102 | touch $INSTALL_FLAG
103 | fi
104 |
105 | gcloud config set project $PROJECT
106 |
107 | echo "Activating service account '$SERVICE_ACCOUNT_SECRET'..."
108 | gcloud auth activate-service-account --key-file=$SERVICE_ACCOUNT_SECRET
109 |
110 | echo_my "Using subscription '$COMMAND_SUBSCRIPTION' to read data from the driving controller..."
111 | if gcloud pubsub subscriptions list | grep $COMMAND_SUBSCRIPTION; then
112 | echo_my "Subscription '$COMMAND_SUBSCRIPTION' already exists - dropping it to avoid processing of old messages..."
113 | gcloud pubsub subscriptions delete $COMMAND_SUBSCRIPTION | true # ignore if not found
114 | fi
115 |
116 | echo_my "Creating a subscription '$COMMAND_SUBSCRIPTION' to topic '$COMMAND_TOPIC'..."
117 | gcloud pubsub subscriptions create $COMMAND_SUBSCRIPTION --topic $COMMAND_TOPIC | true
118 |
119 | if gcloud iot registries list --region=$REGION | grep $IOT_CORE_REGISTRY; then
120 | echo_my "IOT Core Registry $IOT_CORE_REGISTRY already exists. Updating for consistency..."
121 | gcloud iot registries update $IOT_CORE_REGISTRY --project=$PROJECT --region=$REGION \
122 | --event-notification-config=topic=projects/$PROJECT/topics/$SENSOR_TOPIC,subfolder=$SENSOR_TOPIC \
123 | --event-notification-config=topic=projects/$PROJECT/topics/$COMMAND_TOPIC,subfolder=$COMMAND_TOPIC \
124 | | true
125 | else
126 | echo_my "Creating an IOT Core Device for Registry: '$IOT_CORE_REGISTRY' "
127 | gcloud iot registries create $IOT_CORE_REGISTRY --project=$PROJECT --region=$REGION \
128 | --event-notification-config=topic=projects/$PROJECT/topics/$SENSOR_TOPIC,subfolder=$SENSOR_TOPIC \
129 | --event-notification-config=topic=projects/$PROJECT/topics/$COMMAND_TOPIC,subfolder=$COMMAND_TOPIC \
130 | | true
131 | fi
132 |
133 | if ls | grep rsa_private.pem; then
134 | echo_my "Private Key Pairs exist for IOT Core"
135 | else
136 | generate_keys
137 | fi
138 |
139 | if gcloud iot devices list --project=$PROJECT --region=$REGION --registry=$IOT_CORE_REGISTRY | grep $IOT_CORE_DEVICE_ID; then
140 | echo_my "IOT Core Device ID '$IOT_CORE_DEVICE_ID' already registered."
141 | else
142 | echo_my "Registering IOT Core Device ID '$IOT_CORE_DEVICE_ID'..."
143 | gcloud iot devices create $IOT_CORE_DEVICE_ID --project=$PROJECT --region=$REGION --registry=$IOT_CORE_REGISTRY \
144 | --public-key path=rsa_cert.pem,type=rs256 | true
145 | fi
146 |
147 | create_gcs_camera_bucket
148 |
149 | cd py
150 |
151 | # Start the car
152 | if [[ $# -gt 0 && "$1" != "" && "$1" == "--non-interactive" ]] ; then
153 | ./drive.py $PROJECT $COMMAND_SUBSCRIPTION --non-interactive
154 | else
155 | ./drive.py $PROJECT $COMMAND_SUBSCRIPTION
156 | fi
--------------------------------------------------------------------------------
/cloud/controller/js/validate.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License. * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | 'use strict';
17 | const MANUAL_MODE = require('./drive-message').MANUAL_MODE;
18 | const AUTOMATIC_MODE = require('./drive-message').AUTOMATIC_MODE;
19 | const DEBUG_MODE = require('./drive-message').DEBUG_MODE;
20 |
21 | /************************************************************
22 | immutable constant options
23 | ************************************************************/
24 | const BALL_COLORS = ["red", "blue", "green", "yellow"];
25 | const DRIVING_MODES = [AUTOMATIC_MODE, MANUAL_MODE, DEBUG_MODE];
26 | const CONFIG_PARAMS = ["ballColor", "currentDrivingMode", "listenerStatus"];
27 | // map the client params to field names for now
28 | const DRIVING_MESSAGE_PARAMS = {
29 | "turnSpeed": "turn_speed_field",
30 | "angle": "angle_field",
31 | "driveSpeed": "drive_speed_field",
32 | "distance": "distance_field",
33 | "gripperOpen": "gripper_open",
34 | "gripperClosed": "gripper_closed",
35 | "ondemandMessages": "ondemand_messages",
36 | "nonstopMessages": "nonstop_messages"
37 | };
38 | const DEBUG_MESSAGE_PARAMS = {
39 | "sendCommand": "send_command", "nextSensorMessage": "next_sensor_message"
40 | };
41 |
42 | /************************************************************
43 | Validate the configuration parameters based of required
44 | Inputs:
45 | - request, response, next
46 | Returns:
47 | - response or next
48 | ************************************************************/
49 | module.exports.configParams = function (req, res, next) {
50 | var errors = [];
51 |
52 | // validParams validation
53 | var valid = false;
54 | var validParams = CONFIG_PARAMS;
55 | for (var i = 0; i < validParams.length; i++) {
56 | if (typeof req.body[validParams[i]] != 'undefined') {
57 | valid = true;
58 | break;
59 | }
60 | }
61 | if (!valid) {
62 | errors.push(`Valid request param required: [${validParams}]`);
63 | }
64 | if (errors.length > 0) {
65 | return res.status(400).json({
66 | success: false, errors: errors, status: 400
67 | })
68 | }
69 |
70 | // ballColor validation
71 | if (typeof req.body.ballColor != 'undefined' && BALL_COLORS.indexOf(req.body.ballColor) == -1) {
72 | errors.push("Valid ballColor required");
73 | }
74 |
75 | // currentDrivingMode validation
76 | if (typeof req.body.currentDrivingMode != 'undefined' && DRIVING_MODES.indexOf(req.body.currentDrivingMode) == -1) {
77 | errors.push("Valid currentDrivingMode required");
78 | }
79 |
80 | if (errors.length > 0) {
81 | return res.status(400).json({
82 | success: false, errors: errors, status: 400
83 | })
84 | }
85 | return next();
86 | };
87 |
88 | /************************************************************
89 | Validate the driving Message parameters
90 | Inputs:
91 | - request, response, next
92 | Returns:
93 | - response or next
94 | ************************************************************/
95 | module.exports.drivingMessageParams = function (req, res, next) {
96 | var errors = [];
97 |
98 | // validParams validation. We need the keys here
99 | var valid = false;
100 | var validParams = Object.keys(DRIVING_MESSAGE_PARAMS);
101 | for (var i = 0; i < validParams.length; i++) {
102 | if (typeof req.body[validParams[i]] != 'undefined') {
103 | valid = true;
104 | break;
105 | }
106 | }
107 | if (!valid) {
108 | errors.push(`Valid request param required: [${validParams}]`);
109 | }
110 | if (errors.length > 0) {
111 | return res.status(400).json({
112 | success: false, errors: errors, status: 400
113 | })
114 | }
115 |
116 | // range checks
117 | var rangeParams = ["turnSpeed", "drivingSpeed"];
118 | for (var i = 0; i < rangeParams.length; i++) {
119 | var paramName = rangeParams[i];
120 | if (typeof req.body[paramName] != 'undefined' && !(req.body[paramName] >= 1 && req.body[paramName] <= 1000)) {
121 | errors.push(`Valid ${paramName} required: number [1-1000]`);
122 | }
123 | }
124 |
125 | // number checks
126 | var numberParams = ["angle", "distance"];
127 | for (var i = 0; i < numberParams.length; i++) {
128 | var paramName = numberParams[i];
129 | if (typeof req.body[paramName] != 'undefined' && isNaN(req.body[paramName])) {
130 | errors.push(`Valid ${paramName} required: number`);
131 | }
132 | }
133 |
134 | // boolean param checks
135 | var booleanParams = ["gripperOpen", "gripperClosed", "ondemandMessages", "nonstopMessages"];
136 | for (var i = 0; i < booleanParams.length; i++) {
137 | var paramName = booleanParams[i];
138 | if (typeof req.body[paramName] != 'undefined' && !(req.body[paramName] === true || req.body[paramName] == false)) {
139 | errors.push(`Valid ${paramName} required: boolean`);
140 | }
141 | }
142 |
143 | if (errors.length > 0) {
144 | return res.status(400).json({
145 | success: false, errors: errors, status: 400
146 | })
147 | }
148 | return next();
149 | };
150 |
151 | /************************************************************
152 | Validate the debug Message parameters
153 | Inputs:
154 | - request, response, next
155 | Returns:
156 | - response or next
157 | ************************************************************/
158 | module.exports.debugMessageParams = function (req, res, next) {
159 | var errors = [];
160 |
161 | // validParams validation. We need the keys here
162 | var valid = false;
163 | var validParams = Object.keys(DEBUG_MESSAGE_PARAMS);
164 | for (var i = 0; i < validParams.length; i++) {
165 | if (typeof req.body[validParams[i]] != 'undefined') {
166 | valid = true;
167 | break;
168 | }
169 | }
170 | if (!valid) {
171 | errors.push(`Valid request param required: [${validParams}]`);
172 | }
173 | if (errors.length > 0) {
174 | return res.status(400).json({
175 | success: false, errors: errors, status: 400
176 | })
177 | }
178 |
179 | // boolean param checks
180 | var booleanParams = ["sendCommand", "nextSensorMessage"];
181 | for (var i = 0; i < booleanParams.length; i++) {
182 | var paramName = booleanParams[i];
183 | if (typeof req.body[paramName] != 'undefined' && !(req.body[paramName] === true || req.body[paramName] == false)) {
184 | errors.push(`Valid ${paramName} required: boolean`);
185 | }
186 | }
187 |
188 | if (errors.length > 0) {
189 | return res.status(400).json({
190 | success: false, errors: errors, status: 400
191 | })
192 | }
193 | return next();
194 | };
195 |
196 | /**************************************************************************
197 | Module exports
198 | **************************************************************************/
199 | module.exports.DRIVING_MESSAGE_PARAMS = DRIVING_MESSAGE_PARAMS;
200 | module.exports.BALL_COLORS = BALL_COLORS;
201 | module.exports.DRIVING_MODES = DRIVING_MODES;
--------------------------------------------------------------------------------
/third_party/dexter/robotderbycar.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # https://github.com/DexterInd/GoPiGo3/blob/master/LICENSE.md
4 | #
5 | # MIT License
6 | # Copyright (c) 2017 Dexter Industries
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
8 | # (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
17 | # IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 | #
19 | # Based on https://github.com/DexterInd/GoPiGo3/blob/master/Software/Python/easygopigo3.py
20 | #
21 |
22 | import easygopigo3
23 | import time
24 |
25 | class RobotDerbyCar(easygopigo3.EasyGoPiGo3):
26 | """
27 | This class is used for controlling a `RobotDerbyCar`_ robot.
28 | With this class you can do the following things with your `RobotDerbyCar`_:
29 | * Drive your robot while avoiding obstacles
30 | * Inheriting all EasyGoPiGo3 functionality: https://github.com/DexterInd/GoPiGo3/blob/master/Software/Python/easygopigo3.py
31 | * Inheriting all GoPiGo3 functionality: https://github.com/DexterInd/GoPiGo3/blob/master/Software/Python/gopigo3.py
32 | * Set the grippers of the robot to Open or Close positions
33 | """
34 |
35 | def __init__(self):
36 | """
37 | This constructor sets the variables to the following values:
38 | : CONST_GRIPPER_FULL_OPEN : Position of gripper servo when open
39 | : CONST_GRIPPER_FULL_CLOSE: Position of gripper servo when closed
40 | : CONST_GRIPPER_FULL_OPEN : Position of gripper servo to grab ball
41 | : easygopigo3.EasyGoPiGo3 Easy_GPG: Initialization of EasyGoPiGo3
42 | : easygopigo3 Easy_GPG: Initialization of EasyGoPiGo3
43 | : easygopigo3.Servo gpgGripper: Initialization of Gripper Servo on Servo Pin 1
44 | : init_distance_sensor my_distance_sensor: Initialization of Distance Sensor
45 | : IOError: When the GoPiGo3 is not detected. It also debugs a message in the terminal.
46 | : gopigo3.FirmwareVersionError: If the GoPiGo3 firmware needs to be updated. It also debugs a message in the terminal.
47 | : Exception: For any other kind of exceptions.
48 | """
49 |
50 | # Settings for cars in US Reston Office (these grippers were built differently)
51 | self.CONST_GRIPPER_FULL_OPEN = 90
52 | self.CONST_GRIPPER_FULL_CLOSE = 0
53 | self.CONST_GRIPPER_GRAB_POSITION = 40
54 |
55 | # Settings for cars in London Office (default method of assembly for grippers)
56 | #self.CONST_GRIPPER_FULL_OPEN = 180
57 | #self.CONST_GRIPPER_FULL_CLOSE = 20
58 | #self.CONST_GRIPPER_GRAB_POSITION = 120
59 |
60 | self.Easy_GPG = easygopigo3.EasyGoPiGo3() # Create an instance of the GoPiGo3 class. GPG will be the GoPiGo3 object.
61 | self.gpgGripper = easygopigo3.Servo("SERVO1", self.Easy_GPG)
62 | self.my_distance_sensor = self.Easy_GPG.init_distance_sensor()
63 | self.SetLEDsGreen()
64 |
65 | def SetLEDsYellow(self):
66 | self.Easy_GPG.set_left_eye_color((255,255,0))
67 | self.Easy_GPG.set_right_eye_color((255,255,0))
68 | self.Easy_GPG.open_left_eye()
69 | self.Easy_GPG.open_right_eye()
70 |
71 | def SetLEDsGreen(self):
72 | self.Easy_GPG.set_left_eye_color((1,255,1))
73 | self.Easy_GPG.set_right_eye_color((1,255,1))
74 | self.Easy_GPG.open_left_eye()
75 | self.Easy_GPG.open_right_eye()
76 |
77 | def SetLEDsRed(self):
78 | self.Easy_GPG.set_left_eye_color((255,1,1))
79 | self.Easy_GPG.set_right_eye_color((255,1,1))
80 | self.Easy_GPG.open_left_eye()
81 | self.Easy_GPG.open_right_eye()
82 |
83 | def GripperClose(self):
84 | self.SetLEDsRed()
85 | self.gpgGripper.rotate_servo(self.CONST_GRIPPER_GRAB_POSITION)
86 | self.SetLEDsGreen()
87 |
88 | def GripperOpen(self):
89 | self.SetLEDsRed()
90 | self.gpgGripper.rotate_servo(self.CONST_GRIPPER_FULL_OPEN)
91 | self.SetLEDsGreen()
92 |
93 | def ReadDistanceMM(self):
94 | return self.my_distance_sensor.read_mm()
95 |
96 | def ReadBatteryVoltage(self):
97 | return self.Easy_GPG.get_voltage_battery()
98 |
99 | def set_speed(self,speed):
100 | self.SetLEDsRed()
101 | self.Easy_GPG.set_speed(speed)
102 | self.SetLEDsGreen()
103 |
104 | def drive_cm(self,distance):
105 | self.SetLEDsRed()
106 | self.Easy_GPG.drive_cm(distance,True)
107 | self.SetLEDsGreen()
108 |
109 | def turn_degrees(self,degress):
110 | self.SetLEDsRed()
111 | self.Easy_GPG.turn_degrees(degress,True)
112 | self.SetLEDsGreen()
113 |
114 | def drive(self,dist_requested,dist_limit):
115 | """
116 | Move the `GoPiGo3`_ forward / backward for ``dist`` amount of miliimeters.
117 | | For moving the `GoPiGo3`_ robot forward, the ``dist`` parameter has to be *positive*.
118 | | For moving the `GoPiGo3`_ robot backward, the ``dist`` parameter has to be *negative*.
119 | """
120 |
121 | # Have we found any obstacles in the path
122 | ObstaclesFound = False
123 |
124 | # the number of degrees each wheel needs to turn
125 | WheelTurnDegrees = ((dist_requested / self.Easy_GPG.WHEEL_CIRCUMFERENCE) * 360)
126 |
127 | # get the starting position of each motor
128 | CurrentPositionLeft = self.Easy_GPG.get_motor_encoder(self.Easy_GPG.MOTOR_LEFT)
129 | CurrentPositionRight = self.Easy_GPG.get_motor_encoder(self.Easy_GPG.MOTOR_RIGHT)
130 |
131 | # determine the end position of each motor
132 | EndPositionLeft = CurrentPositionLeft + WheelTurnDegrees
133 | EndPositionRight = CurrentPositionRight + WheelTurnDegrees
134 |
135 | self.SetLEDsRed()
136 | self.Easy_GPG.set_motor_position(self.Easy_GPG.MOTOR_LEFT, EndPositionLeft)
137 | self.Easy_GPG.set_motor_position(self.Easy_GPG.MOTOR_RIGHT, EndPositionRight)
138 |
139 | while self.Easy_GPG.target_reached(EndPositionLeft, EndPositionRight) is False:
140 | # read the distance of the laser sensor
141 | dist_read = self.ReadDistanceMM()
142 |
143 | # stop car if there is an object within the limit
144 | if ((dist_read is not None) and (int(dist_read) <= int(dist_limit)) and (int(dist_requested) > int(dist_limit))):
145 | print("RobotDerbyCar.drive(): Obstacle Found. Stopping Car before requested distance. Object distance: " + str(dist_read))
146 | ObstaclesFound = True
147 | CurrentPositionLeft = self.Easy_GPG.get_motor_encoder(self.Easy_GPG.MOTOR_LEFT)
148 | CurrentPositionRight = self.Easy_GPG.get_motor_encoder(self.Easy_GPG.MOTOR_RIGHT)
149 | self.Easy_GPG.set_motor_position(self.Easy_GPG.MOTOR_LEFT, CurrentPositionLeft)
150 | self.Easy_GPG.set_motor_position(self.Easy_GPG.MOTOR_RIGHT, CurrentPositionRight)
151 | break
152 |
153 | time.sleep(0.05)
154 |
155 | self.SetLEDsGreen()
156 | return ObstaclesFound
157 |
--------------------------------------------------------------------------------
/admin/delete-hackathon.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###############################################################################
4 | # This script deletes hackathon event, including users and folders
5 | ###############################################################################
6 |
7 | #
8 | # Copyright 2018 Google LLC
9 | #
10 | # Licensed under the Apache License, Version 2.0 (the "License");
11 | # you may not use this file except in compliance with the License.
12 | # You may obtain a copy of the License at
13 | #
14 | # https://www.apache.org/licenses/LICENSE-2.0
15 | #
16 | # Unless required by applicable law or agreed to in writing, software
17 | # distributed under the License is distributed on an "AS IS" BASIS,
18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | # See the License for the specific language governing permissions and
20 | # limitations under the License.
21 | #
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | source ./setenv.sh
27 |
28 | # How many Vms were deleted during the run
29 | VMS_DELETED=0
30 | # How many projects were found
31 | PROJECTS_FOUND=0
32 | # How many folders found
33 | FOLDERS_FOUND=0
34 |
35 | ###############################################################################
36 | # Remove all folders and projects under a designated folder ID
37 | # Input
38 | # 1 - folder ID under which all content is to be deleted
39 | ###############################################################################
40 | delete_folders() {
41 | local PARENT_FOLDER_ID=$1
42 | local FOLDER_ID
43 |
44 | FOLDERS_FOUND=$((FOLDERS_FOUND+1))
45 | echo_my "Deleting projects directly under folder id '$PARENT_FOLDER_ID'..."
46 | delete_projects $PARENT_FOLDER_ID
47 |
48 | local FOLDER_LIST=$(gcloud alpha resource-manager folders list --folder=$PARENT_FOLDER_ID --format="value(name)")
49 |
50 | while read -r FOLDER_ID; do
51 | if [[ ! -z "$FOLDER_ID" ]] ; then
52 | echo_my "Recursively processing folders under folder id '$FOLDER_ID'..."
53 | delete_folders $FOLDER_ID
54 | if [ $DELETE_VM_ONLY = false ] ; then
55 | gcloud alpha resource-manager folders delete $FOLDER_ID | true # Ignore if error and proceed
56 | fi
57 | fi
58 | done <<< "$FOLDER_LIST"
59 |
60 | echo_my "Finally deleting the top folder '$PARENT_FOLDER_ID'..."
61 | gcloud alpha resource-manager folders delete $PARENT_FOLDER_ID | true # Ignore if error
62 | }
63 |
64 | ###############################################################################
65 | # Remove all projects under a given folder
66 | # Inputs
67 | # 1 - Folder ID
68 | ###############################################################################
69 | delete_projects() {
70 | echo_my "Deleting projects for folder '$1'..."
71 | local PROJECT_LIST=$(gcloud projects list --filter="parent.id=$1" --format="value(projectId)")
72 | local PROJ_ID
73 |
74 | while read -r PROJ_ID; do
75 | if [[ ! -z "$PROJ_ID" ]] ; then
76 | PROJECTS_FOUND=$((PROJECTS_FOUND+1))
77 | echo_my "Processing project id '$PROJ_ID'..."
78 | if [ $DELETE_VM_ONLY = false ] ; then
79 | delete_project_liens $PROJ_ID
80 | yes | gcloud projects delete $PROJ_ID | true # Ignore if error and proceed
81 | else
82 | delete_vms $PROJ_ID
83 | fi
84 | fi
85 | done <<< "$PROJECT_LIST"
86 | }
87 |
88 | ###############################################################################
89 | # Remove all VMs in a project
90 | # Inputs
91 | # 1 - project ID
92 | ###############################################################################
93 | delete_vms() {
94 | local VM_ID
95 | echo_my "Deleting VMs for project '$1'..."
96 |
97 | local VM_LIST=$(gcloud compute instances list --project $1 --format="value(name)")
98 |
99 | while read -r VM_ID; do
100 | if [[ ! -z "$VM_ID" ]] ; then
101 | VMS_DELETED=$((VMS_DELETED+1))
102 | # Get the zone of the instance
103 | local ZONE=$(gcloud compute instances list --filter="name:($VM_ID)" --project $1 --format="value(zone)")
104 | echo_my "Deleting VM id '$VM_ID'..."
105 | yes | gcloud compute instances delete $VM_ID --project $1 --zone=$ZONE | true # Ignore if error and proceed
106 | else
107 | echo_my "No more VMs found in this project"
108 | fi
109 | done <<< "$VM_LIST"
110 | }
111 |
112 | ###############################################################################
113 | # Remove all liens placed against projects by Dialogflow or other resources
114 | # Inputs
115 | # 1 - project ID
116 | ###############################################################################
117 | delete_project_liens() {
118 | local VM_ID
119 | echo_my "Deleting project liens for project '$1'..."
120 |
121 | local LIEN_LIST=$(gcloud alpha resource-manager liens list --project $1 --format="value(name)")
122 |
123 | while read -r LIEN_NAME; do
124 | if [[ ! -z "$LIEN_NAME" ]] ; then
125 | echo_my "Deleting lien name '$LIEN_NAME'..."
126 | yes | gcloud alpha resource-manager liens delete $LIEN_NAME | true # Ignore if error and proceed
127 | else
128 | echo_my "No more liens found for this project"
129 | fi
130 | done <<< "$LIEN_LIST"
131 | }
132 |
133 | ###############################################################################
134 | # Remove all users and groups
135 | ###############################################################################
136 | delete_everybody() {
137 | for i in $(seq $TEAM_START_NUM $NUM_TEAMS);
138 | do
139 | for j in $(seq 1 $NUM_PEOPLE_PER_TEAM);
140 | do
141 | $GAM delete user $(user_name $j $i) | true # ignore if error
142 | done
143 |
144 | $GAM delete group "$(team_name $i)" | true # ignore if error
145 | done
146 | }
147 |
148 | ###############################################################################
149 | # Reset all passwords for all auto-generated users
150 | ###############################################################################
151 | reset_passwords() {
152 | # Create empty file and overwrite the existing one
153 | echo "Email,Password" > $USER_LIST
154 |
155 | for i in $(seq $TEAM_START_NUM $NUM_TEAMS);
156 | do
157 | for j in $(seq 1 $NUM_PEOPLE_PER_TEAM);
158 | do
159 | local PASSWORD=$(generate_password)
160 | $GAM update user $(user_name $j $i) password $PASSWORD
161 | echo "$(user_name $j $i),$PASSWORD" >> $USER_LIST
162 | done
163 | done
164 | }
165 |
166 | ###############################################################################
167 | # MAIN
168 | ###############################################################################
169 | if [ -z ${1+x} ] ; then
170 | echo_my "NUMERIC_FOLDER_ID not found. \n Usage: delete-hackathon.sh [NUMERIC_FOLDER_ID] \n Example: \n ./delete-hackathon.sh 8763450985677 \n \n" $ECHO_ERROR
171 | exit 1
172 | fi
173 |
174 | FOLDER_TO_BE_DELETED=$1
175 |
176 | print_header "Delete workshop users, folders, etc."
177 |
178 | echo_my "\nATTENTION!!!!!!!!!!!\nATTENTION!!!!!!!!!!!\nATTENTION!!!!!!!!!!!\n"
179 | echo_my "This will remove all Users, Projects, Folders under the folder --- '$TOP_FOLDER' ---. Are you sure you want to proceed?" $ECHO_WARNING
180 | pause
181 | echo_my "\nAre you sure you want to delete all USERS, PROJECTS and FOLDERS???????" $ECHO_WARNING
182 | pause
183 |
184 | setup
185 |
186 | delete_everybody
187 |
188 | # If this is true, then folders and projects will not be deleted - only VMs
189 | DELETE_VM_ONLY=false
190 |
191 | if [ $DELETE_VM_ONLY = false ] ; then
192 | echo_my "DELETE_VM_ONLY=$DELETE_VM_ONLY - this means all projects, folders and VMs will be deleted."
193 | else
194 | echo_my "DELETE_VM_ONLY=$DELETE_VM_ONLY - this means only VMs will be deleted."
195 | fi
196 |
197 | delete_folders $FOLDER_TO_BE_DELETED
198 |
199 | echo_my "Found $PROJECTS_FOUND projects and $FOLDERS_FOUND folders."
200 | echo_my "Found and deleted $VMS_DELETED virtual machines."
201 | print_footer "SUCCESS: Workshop resources have been removed."
--------------------------------------------------------------------------------
/car/driver/py/robotderbycar.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # https://github.com/DexterInd/GoPiGo3/blob/master/LICENSE.md
4 | #
5 | # MIT License
6 | # Copyright (c) 2017 Dexter Industries
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
8 | # (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
17 | # IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 | #
19 | # Based on https://github.com/DexterInd/GoPiGo3/blob/master/Software/Python/easygopigo3.py
20 | #
21 |
22 | import easygopigo3
23 | import time
24 |
25 | class RobotDerbyCar(easygopigo3.EasyGoPiGo3):
26 | """
27 | This class is used for controlling a `RobotDerbyCar`_ robot.
28 | With this class you can do the following things with your `RobotDerbyCar`_:
29 | * Drive your robot while avoiding obstacles
30 | * Inheriting all EasyGoPiGo3 functionality: https://github.com/DexterInd/GoPiGo3/blob/master/Software/Python/easygopigo3.py
31 | * Inheriting all GoPiGo3 functionality: https://github.com/DexterInd/GoPiGo3/blob/master/Software/Python/gopigo3.py
32 | * Set the grippers of the robot to Open or Close positions
33 | """
34 |
35 | def __init__(self):
36 | """
37 | This constructor sets the variables to the following values:
38 | : CONST_GRIPPER_FULL_OPEN : Position of gripper servo when open
39 | : CONST_GRIPPER_FULL_CLOSE: Position of gripper servo when closed
40 | : CONST_GRIPPER_FULL_OPEN : Position of gripper servo to grab ball
41 | : easygopigo3.EasyGoPiGo3 Easy_GPG: Initialization of EasyGoPiGo3
42 | : easygopigo3 Easy_GPG: Initialization of EasyGoPiGo3
43 | : easygopigo3.Servo gpgGripper: Initialization of Gripper Servo on Servo Pin 1
44 | : init_distance_sensor my_distance_sensor: Initialization of Distance Sensor
45 | : IOError: When the GoPiGo3 is not detected. It also debugs a message in the terminal.
46 | : gopigo3.FirmwareVersionError: If the GoPiGo3 firmware needs to be updated. It also debugs a message in the terminal.
47 | : Exception: For any other kind of exceptions.
48 | """
49 |
50 | # GoPiGo Color Codes
51 | self.YELLOW = (255, 255, 0)
52 | self.GREEN = (0, 255, 0)
53 | self.RED = (255, 0, 0)
54 | self.BLUE = (0, 0, 255)
55 |
56 | # Settings for cars in US Reston Office (these grippers were built differently)
57 | self.CONST_GRIPPER_FULL_OPEN = 90
58 | self.CONST_GRIPPER_FULL_CLOSE = 0
59 | self.CONST_GRIPPER_GRAB_POSITION = 40
60 |
61 | # Settings for cars in London Office (default method of assembly for grippers)
62 | #self.CONST_GRIPPER_FULL_OPEN = 180
63 | #self.CONST_GRIPPER_FULL_CLOSE = 20
64 | #self.CONST_GRIPPER_GRAB_POSITION = 120
65 |
66 | self.Easy_GPG = easygopigo3.EasyGoPiGo3() # Create an instance of the GoPiGo3 class. GPG will be the GoPiGo3 object.
67 | self.gpgGripper1 = easygopigo3.Servo("SERVO1", self.Easy_GPG)
68 | self.gpgGripper2 = easygopigo3.Servo("SERVO2", self.Easy_GPG)
69 | self.my_distance_sensor = self.Easy_GPG.init_distance_sensor()
70 | self.SetCarStatusLED(self.GREEN)
71 |
72 | def SetCarStatusLED(self,color):
73 | self.Easy_GPG.set_right_eye_color(color)
74 | self.Easy_GPG.open_right_eye()
75 |
76 | def SetCarModeLED(self,color):
77 | self.Easy_GPG.set_left_eye_color(color)
78 | self.Easy_GPG.open_left_eye()
79 |
80 | def SetBallModeLED(self,color):
81 | self.Easy_GPG.set_led(self.Easy_GPG.LED_WIFI,color[0],color[1],color[2])
82 |
83 | def GripperClose(self):
84 | self.SetCarStatusLED(self.RED)
85 | self.gpgGripper1.rotate_servo(self.CONST_GRIPPER_GRAB_POSITION)
86 | self.gpgGripper2.rotate_servo(self.CONST_GRIPPER_GRAB_POSITION)
87 | self.SetCarStatusLED(self.GREEN)
88 |
89 | def GripperOpen(self):
90 | self.SetCarStatusLED(self.RED)
91 | self.gpgGripper1.rotate_servo(self.CONST_GRIPPER_FULL_OPEN)
92 | self.gpgGripper2.rotate_servo(self.CONST_GRIPPER_FULL_OPEN)
93 | self.SetCarStatusLED(self.GREEN)
94 |
95 | def ReadDistanceMM(self):
96 | return self.my_distance_sensor.read_mm()
97 |
98 | def ReadBatteryVoltage(self):
99 | return self.Easy_GPG.get_voltage_battery()
100 |
101 | def set_speed(self,speed):
102 | self.SetCarStatusLED(self.RED)
103 | self.Easy_GPG.set_speed(speed)
104 | self.SetCarStatusLED(self.GREEN)
105 |
106 | def drive_cm(self,distance):
107 | self.SetCarStatusLED(self.RED)
108 | self.Easy_GPG.drive_cm(distance,True)
109 | self.SetCarStatusLED(self.GREEN)
110 |
111 | def turn_degrees(self,degress):
112 | self.SetCarStatusLED(self.RED)
113 | self.Easy_GPG.turn_degrees(degress,True)
114 | self.SetCarStatusLED(self.GREEN)
115 |
116 | def drive(self,dist_requested,dist_limit):
117 | """
118 | Move the `GoPiGo3`_ forward / backward for ``dist`` amount of miliimeters.
119 | | For moving the `GoPiGo3`_ robot forward, the ``dist`` parameter has to be *positive*.
120 | | For moving the `GoPiGo3`_ robot backward, the ``dist`` parameter has to be *negative*.
121 | """
122 |
123 | # Have we found any obstacles in the path
124 | ObstaclesFound = False
125 |
126 | # the number of degrees each wheel needs to turn
127 | WheelTurnDegrees = ((dist_requested / self.Easy_GPG.WHEEL_CIRCUMFERENCE) * 360)
128 |
129 | # get the starting position of each motor
130 | CurrentPositionLeft = self.Easy_GPG.get_motor_encoder(self.Easy_GPG.MOTOR_LEFT)
131 | CurrentPositionRight = self.Easy_GPG.get_motor_encoder(self.Easy_GPG.MOTOR_RIGHT)
132 |
133 | # determine the end position of each motor
134 | EndPositionLeft = CurrentPositionLeft + WheelTurnDegrees
135 | EndPositionRight = CurrentPositionRight + WheelTurnDegrees
136 |
137 | self.SetCarStatusLED(self.RED)
138 | self.Easy_GPG.set_motor_position(self.Easy_GPG.MOTOR_LEFT, EndPositionLeft)
139 | self.Easy_GPG.set_motor_position(self.Easy_GPG.MOTOR_RIGHT, EndPositionRight)
140 |
141 | while self.Easy_GPG.target_reached(EndPositionLeft, EndPositionRight) is False:
142 | # read the distance of the laser sensor
143 | dist_read = self.ReadDistanceMM()
144 |
145 | # stop car if there is an object within the limit
146 | if ((dist_read is not None) and (int(dist_read) <= int(dist_limit)) and (int(dist_requested) > int(dist_limit))):
147 | print("RobotDerbyCar.drive(): Obstacle Found. Stopping Car before requested distance. Object distance: " + str(dist_read))
148 | ObstaclesFound = True
149 | CurrentPositionLeft = self.Easy_GPG.get_motor_encoder(self.Easy_GPG.MOTOR_LEFT)
150 | CurrentPositionRight = self.Easy_GPG.get_motor_encoder(self.Easy_GPG.MOTOR_RIGHT)
151 | self.Easy_GPG.set_motor_position(self.Easy_GPG.MOTOR_LEFT, CurrentPositionLeft)
152 | self.Easy_GPG.set_motor_position(self.Easy_GPG.MOTOR_RIGHT, CurrentPositionRight)
153 | break
154 |
155 | time.sleep(0.05)
156 |
157 | self.SetCarStatusLED(self.GREEN)
158 | return ObstaclesFound
159 |
--------------------------------------------------------------------------------
/cloud/controller/js/drive-message.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | // For some reason car gets stuck in limbo when the turn angle is 3 degrees or less
20 | const IGNORE_TURN_DEGREE = 3;
21 |
22 | // Command indicates that car has ball in sight and moving towards it
23 | const GO2BALL = "go2ball";
24 |
25 | // Command indicates that Base is in sight and car is moving towards it
26 | const GO2BASE = "go2base";
27 |
28 | // Command indicates that there is no ball of needed color in sight and car is looking for it by turning
29 | const SEEK_BALL_TURN = "seekBallTurn";
30 |
31 | // Command indicates that there is no home base of needed color in sight and car is looking for it by turning
32 | const SEEK_HOME_TURN = "seekHomeTurn";
33 |
34 | // Command indicates that there is no ball of needed color in sight and car is looking for it by moving
35 | const SEEK_BALL_MOVE = "seekBallMove";
36 |
37 | // Command indicates that ball is within gripping distance - need to grab it now
38 | const CAPTURE_BALL = "captureBall";
39 |
40 | // This means the goal of the command is to verify that ball has been gripped
41 | const CHECK_GRIP = "checkGrip";
42 |
43 | // This indicated the end of the game when the car finished all tasks
44 | const GAME_END = "missionComplete";
45 |
46 | // Command indicates that the car will be operated manually from the cloud user and all incoming sensor messages will be ignored
47 | // If this is not present in the command, this means car is in self-driving mode
48 | const MANUAL_MODE = "manual";
49 |
50 | // Command indicates that the car is controlled by debugger
51 | const DEBUG_MODE = "debug";
52 |
53 | // Command indicates that the car is controlled by debugger
54 | const AUTOMATIC_MODE = "automatic";
55 |
56 | // Tells the car to keep taking pictures and continuously send them to the cloud non-stop
57 | const CONTINUOUS_SENSOR_RATE = "continuous";
58 |
59 | // Tells the car to send sensor messages only when asked
60 | const ON_DEMAND_SENSOR_RATE = "onDemand";
61 |
62 | /**************************************************************************
63 | Driving command message sent from cloud to the car. Example of a message:
64 | { "cloudTimestampMs": 1519592078172,
65 | "carTimestampMs": 1519592078100,
66 | "mode": "go2base",
67 | "actions": [
68 | { "driveForward": 111 },
69 | { "turnRight": 22 },
70 | { "driveBackwardMm": 33 },
71 | { "speed": 44 },
72 | { "turnLeft": 55 },
73 | { "driveForwardMm": 66 },
74 | ...]
75 | }
76 | **************************************************************************/
77 | class DriveMessage {
78 |
79 | constructor() {
80 | // Timestamp is generated at the time of creation of the message, not at the time of sending it
81 | this.cloudTimestampMs = new Date().getTime();
82 | // Timestamp of the original message from the car as correlation ID
83 | // Car needs to validate this field against the latest info that it sent to the cloud
84 | this.carTimestampMs = undefined;
85 | // By default the mode is as follows
86 | this.mode = MANUAL_MODE;
87 | // How often does the car need to send sensor messages
88 | this.sensorRate = ON_DEMAND_SENSOR_RATE;
89 | // Array of commands to execute - could be a long list, in which case car will have to execute
90 | // those in sequence. The list can be arbitrarily long
91 | this.actions = [];
92 | }
93 |
94 | // Tells the car that one more ball has been captured
95 | addBallCount() {
96 | this.ballCaptured = 1;
97 | }
98 |
99 | // Takes four basic colors of balls as input
100 | setColor(color) {
101 | if (color == "Blue" || color == "Red" || color == "Green" || color == "Yellow") {
102 | this.actions.push({"setColor": color});
103 | }
104 | }
105 |
106 | // This takes positive or negative angle and converts it into a proper command
107 | makeTurn(degrees) {
108 | if (degrees > 0) {
109 | this.turnRight(degrees);
110 | } else {
111 | this.turnLeft(degrees);
112 | }
113 | }
114 |
115 | // Cap max turn angle to 1000 degrees in any conditions
116 | turnLeft(degrees) {
117 | if (degrees < 0 && Math.abs(degrees) > IGNORE_TURN_DEGREE) {
118 | this.actions.push({"turnLeft": Math.min(degrees, 1000)});
119 | }
120 | }
121 |
122 | // Cap max turn angle to 1000 degrees in any conditions
123 | turnRight(degrees) {
124 | if (degrees > IGNORE_TURN_DEGREE) {
125 | this.actions.push({"turnRight": Math.min(degrees, 1000)});
126 | }
127 | }
128 |
129 | // This takes positive or negative value and converts it into a proper command
130 | // If speed is not explicitly set in the command, then drive at max speed
131 | drive(mm) {
132 | if (mm > 0) {
133 | this.driveForward(mm);
134 | } else {
135 | this.driveBackward(mm);
136 | }
137 | }
138 |
139 | // Cap max driving distance in any conditions to no more than 5 meters
140 | driveForward(mm) {
141 | if (mm > 0) {
142 | this.actions.push({"driveForwardMm": Math.min(mm, 5000)});
143 | }
144 | }
145 |
146 | // Cap max driving distance in any conditions to no more than 5 meters
147 | driveBackward(mm) {
148 | if (mm <= 0) {
149 | this.actions.push({"driveBackwardMm": Math.min(mm, 5000)});
150 | }
151 | }
152 |
153 | takePhoto() {
154 | this.actions.push({"takePhoto": true});
155 | }
156 |
157 | // Speed must be more than 0 and less than 1000 (per GoPiGo docs, otherwise this command will be ignored
158 | setSpeed(speed) {
159 | if (speed > 0) {
160 | this.actions.push({"setSpeed": Math.min(speed, 1000)});
161 | }
162 | }
163 |
164 | gripperOpen() {
165 | this.actions.push({"gripperPosition": "open"});
166 | }
167 |
168 | gripperClose() {
169 | this.actions.push({"gripperPosition": "close"});
170 | }
171 |
172 | sendSensorMessage() {
173 | this.actions.push({"sendSensorMessage": "true"});
174 | }
175 |
176 | setGoalGo2Ball() {
177 | this.goal = GO2BALL;
178 | }
179 |
180 | setGoalGo2Base() {
181 | this.goal = GO2BASE;
182 | }
183 |
184 | setGoalGameEnd() {
185 | this.goal = GAME_END;
186 | }
187 |
188 | setGoalSeekBallTurn() {
189 | this.goal = SEEK_BALL_TURN;
190 | }
191 |
192 | setGoalSeekHomeTurn() {
193 | this.goal = SEEK_HOME_TURN;
194 | }
195 |
196 | setGoalSeekBallMove() {
197 | this.goal = SEEK_BALL_MOVE;
198 | }
199 |
200 | setGoalCaptureBall() {
201 | this.goal = CAPTURE_BALL;
202 | }
203 |
204 | setGoalCheck4Grip() {
205 | this.goal = CHECK_GRIP;
206 | }
207 |
208 | setModeManual() {
209 | this.mode = MANUAL_MODE;
210 | }
211 |
212 | setModeDebug() {
213 | this.mode = DEBUG_MODE;
214 | }
215 |
216 | setModeAutomatic() {
217 | this.mode = AUTOMATIC_MODE;
218 | }
219 |
220 | setOnDemandSensorRate() {
221 | this.sensorRate = ON_DEMAND_SENSOR_RATE;
222 | }
223 |
224 | setContinuousSensorRate() {
225 | this.sensorRate = CONTINUOUS_SENSOR_RATE;
226 | }
227 |
228 | setCorrelationID(timestampMs) {
229 | this.carTimestampMs = timestampMs;
230 | }
231 | }
232 |
233 | /**************************************************************************
234 | Module exports
235 | **************************************************************************/
236 | module.exports.DriveMessage = DriveMessage;
237 | module.exports.SEEK_BALL_TURN = SEEK_BALL_TURN;
238 | module.exports.MANUAL_MODE = MANUAL_MODE;
239 | module.exports.DEBUG_MODE = DEBUG_MODE;
240 | module.exports.AUTOMATIC_MODE = AUTOMATIC_MODE;
241 | module.exports.CHECK_GRIP = CHECK_GRIP;
242 | module.exports.GO2BASE = GO2BASE;
243 | module.exports.SEEK_HOME_TURN = SEEK_HOME_TURN;
--------------------------------------------------------------------------------
/cloud/controller/js/tests/api_success_tests.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | var app = require('../app');
18 | var chai = require('chai');
19 | var request = require('supertest');
20 |
21 | const BALL_COLORS = require('../validate').BALL_COLORS;
22 | const DRIVING_MODES = require('../validate').DRIVING_MODES;
23 |
24 | var expect = chai.expect;
25 |
26 | describe('API Successful Tests (/api)', function() {
27 |
28 | it('GET /: should return 200 and Welcome to the API! message', function(done) {
29 | request(app)
30 | .get('/api')
31 | .end(function(err, res) {
32 | expect(res.body.message).to.be.an('string').that.does.include('Welcome to the');
33 | expect(res.body.success).to.be.true;
34 | expect(res.body.status).to.be.equal(200);
35 | expect(res.statusCode).to.be.equal(200);
36 | expect(res.headers).to.have.property('access-control-allow-origin');
37 | done();
38 | });
39 | });
40 |
41 | it('GET /stats: should return 200 and stats keys', function(done) {
42 | request(app)
43 | .get('/api/stats')
44 | .end(function(err, res) {
45 | var keys = ["totalErrors", "totalMessagesSent", "totalMessagesReceived",
46 | "rejectedFormatMessages", "rejectedOutOfOrderMessages"];
47 | expect(res.body.data).to.all.keys(keys);
48 | expect(res.body.success).to.be.true;
49 | expect(res.body.status).to.be.equal(200);
50 | expect(res.statusCode).to.be.equal(200);
51 | expect(res.headers).to.have.property('access-control-allow-origin');
52 | done();
53 | });
54 | });
55 |
56 | it('GET /config/options: should return 200 and valid config options', function(done) {
57 | request(app)
58 | .get('/api/config/options')
59 | .end(function(err, res) {
60 | expect(res.body.data.ballColors).to.have.members(BALL_COLORS);
61 | expect(res.body.data.drivingModes).to.have.members(DRIVING_MODES);
62 | expect(res.body.success).to.be.true;
63 | expect(res.body.status).to.be.equal(200);
64 | expect(res.statusCode).to.be.equal(200);
65 | expect(res.headers).to.have.property('access-control-allow-origin');
66 | done();
67 | });
68 | });
69 |
70 | it('GET /config: should return 200 and config keys', function(done) {
71 | request(app)
72 | .get('/api/config')
73 | .end(function(err, res) {
74 | var keys = ["ballColor", "currentDrivingMode", "listenerStatus", "commandTopic",
75 | "sensorSubscription", "carId"];
76 | expect(res.body.data).to.all.keys(keys);
77 | expect(res.body.success).to.be.true;
78 | expect(res.body.status).to.be.equal(200);
79 | expect(res.statusCode).to.be.equal(200);
80 | expect(res.headers).to.have.property('access-control-allow-origin');
81 | done();
82 | });
83 | });
84 |
85 | it('PUT /config: should return 200 for a sample payload', function(done) {
86 | request(app)
87 | .put('/api/config')
88 | .send({'ballColor': 'blue'})
89 | .end(function(err, res) {
90 | expect(res.body.success).to.be.true;
91 | expect(res.body.status).to.be.equal(200);
92 | expect(res.statusCode).to.be.equal(200);
93 | expect(res.headers).to.have.property('access-control-allow-origin');
94 | done();
95 | });
96 | });
97 |
98 | it('GET /messages: should return 200 and messages arrays', function(done) {
99 | request(app)
100 | .get('/api/messages')
101 | .end(function(err, res) {
102 | expect(res.body.data.inboundMsgHistory).to.be.an('array');
103 | expect(res.body.data.outboundMsgHistory).to.be.an('array');
104 | expect(res.body.success).to.be.true;
105 | expect(res.body.status).to.be.equal(200);
106 | expect(res.statusCode).to.be.equal(200);
107 | expect(res.headers).to.have.property('access-control-allow-origin');
108 | done();
109 | });
110 | });
111 |
112 | it('POST /messages/debug: should return 201 for a sample payload', function(done) {
113 | request(app)
114 | .post('/api/messages/debug')
115 | .send({'sendCommand': true})
116 | .end(function(err, res) {
117 | expect(res.body.success).to.be.true;
118 | expect(res.body.status).to.be.equal(201);
119 | expect(res.statusCode).to.be.equal(201);
120 | expect(res.headers).to.have.property('access-control-allow-origin');
121 | done();
122 | });
123 | });
124 |
125 | it('POST /messages/driving: should return 201 for gripperOpen true', function(done) {
126 | request(app)
127 | .post('/api/messages/driving')
128 | .send({'gripperOpen': true})
129 | .end(function(err, res) {
130 | expect(res.body.success).to.be.true;
131 | expect(res.body.status).to.be.equal(201);
132 | expect(res.statusCode).to.be.equal(201);
133 | expect(res.headers).to.have.property('access-control-allow-origin');
134 | done();
135 | });
136 | });
137 |
138 | it('POST /messages/driving: should return 201 for gripperOpen false', function(done) {
139 | request(app)
140 | .post('/api/messages/driving')
141 | .send({'gripperOpen': false})
142 | .end(function(err, res) {
143 | expect(res.body.success).to.be.true;
144 | expect(res.body.status).to.be.equal(201);
145 | expect(res.statusCode).to.be.equal(201);
146 | expect(res.headers).to.have.property('access-control-allow-origin');
147 | done();
148 | });
149 | });
150 |
151 | it('POST /messages/driving: should return 201 for drivingSpeed:100, distance:100', function(done) {
152 | request(app)
153 | .post('/api/messages/driving')
154 | .send({'drivingSpeed': 100, 'distance': 100})
155 | .end(function(err, res) {
156 | expect(res.body.success).to.be.true;
157 | expect(res.body.status).to.be.equal(201);
158 | expect(res.statusCode).to.be.equal(201);
159 | expect(res.headers).to.have.property('access-control-allow-origin');
160 | done();
161 | });
162 | });
163 |
164 | it('POST /messages/driving: should return 201 for turnSpeed:200, angle:180', function(done) {
165 | request(app)
166 | .post('/api/messages/driving')
167 | .send({'turnSpeed': 200, 'angle': 180})
168 | .end(function(err, res) {
169 | expect(res.body.success).to.be.true;
170 | expect(res.body.status).to.be.equal(201);
171 | expect(res.statusCode).to.be.equal(201);
172 | expect(res.headers).to.have.property('access-control-allow-origin');
173 | done();
174 | });
175 | });
176 |
177 | it('POST /messages/driving: should return 201 for turnSpeed:200, angle:-180', function(done) {
178 | request(app)
179 | .post('/api/messages/driving')
180 | .send({'turnSpeed': 100, 'angle': -180})
181 | .end(function(err, res) {
182 | expect(res.body.success).to.be.true;
183 | expect(res.body.status).to.be.equal(201);
184 | expect(res.statusCode).to.be.equal(201);
185 | expect(res.headers).to.have.property('access-control-allow-origin');
186 | done();
187 | });
188 | });
189 |
190 | it('POST /messages/driving: should return 201 for drivingSpeed:100, distance:-100', function(done) {
191 | request(app)
192 | .post('/api/messages/driving')
193 | .send({'drivingSpeed': 100, 'distance': -100})
194 | .end(function(err, res) {
195 | expect(res.body.success).to.be.true;
196 | expect(res.body.status).to.be.equal(201);
197 | expect(res.statusCode).to.be.equal(201);
198 | expect(res.headers).to.have.property('access-control-allow-origin');
199 | done();
200 | });
201 | });
202 |
203 | });
204 |
--------------------------------------------------------------------------------
/admin/ml-image-updates/js/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, Google, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | * * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | 'use strict';
17 | console.log(`***Image processing is starting up***`);
18 |
19 | // Imports
20 | const process = require('process'); // Required for mocking environment variables
21 | const request = require('request');
22 |
23 | const Storage = require('@google-cloud/storage');
24 | const storage = new Storage(process.env.PROJECT);
25 |
26 | const VisionResponse = require('../../../cloud/controller/js/vision-response');
27 | const BoundingBox = require('../../../cloud/controller/js/bounding-box');
28 |
29 | // Constants
30 | const APP_URL = `http://${process.env.INFERENCE_IP}`;
31 | const HTTP_PORT = process.env.HTTP_PORT;
32 | const INFERENCE_URL = process.env.INFERENCE_URL;
33 | const OBJECT_INFERENCE_API_URL = APP_URL + ':' + HTTP_PORT + INFERENCE_URL;
34 | // User credentials to authenticate to remote Inference VM service
35 | const INFERENCE_USER_NAME = process.env.INFERENCE_USER_NAME;
36 | const INFERENCE_PASSWORD = process.env.INFERENCE_PASSWORD;
37 | const ALL_OBJECT_LABELS = process.env.ALL_OBJECT_LABELS.split(" ");
38 | const DESTINATION_BUCKET = process.env.DESTINATION_BUCKET;
39 |
40 | // Global counter of processed files
41 | let successCount = 0;
42 |
43 | /************************************************************
44 | Scan all files in the bucket
45 | Great examples of API use can be found here:
46 | https://github.com/googleapis/nodejs-storage/blob/99e3b17f0b12ea66ed46060ca291124b10772111/samples/files.js#L26
47 | ************************************************************/
48 |
49 | /************************************************************
50 | Process one file
51 | Input:
52 | - Source Bucket
53 | - Source File
54 | Output:
55 | - None
56 | ************************************************************/
57 | async function processOneFileAsync(srcBucket, srcFile) {
58 | let visionResponse = await recognizeObjectsAsync('gs://' + srcBucket + '/' + srcFile);
59 | // console.log('processOneFileAsync(): vision response: ' + JSON.stringify(visionResponse));
60 |
61 | for (let label of ALL_OBJECT_LABELS) {
62 | let prefix = findObject(label, visionResponse);
63 | await gcsCopy(srcBucket, srcFile, DESTINATION_BUCKET, prefix + srcFile);
64 | }
65 |
66 | // After all labels have been processed, we delete the file from the source bucket
67 | gcsDelete(srcBucket, srcFile)
68 | .catch(function (error) {
69 | console.error("!!!!!!!!!!!!! Error deleting file: " + error);
70 | });
71 | // console.log('Finished processing file ' + srcFile);
72 | }
73 |
74 | /************************************************************
75 | Copy file from one bucket into another
76 | Input:
77 | - source GCS URI
78 | - destination GCS URI
79 | ************************************************************/
80 | async function gcsCopy(srcBucket, srcFile, destBucket, destFile) {
81 | console.log('Copy from to ');
82 | await storage.bucket(srcBucket).file(srcFile).copy(storage.bucket(destBucket).file(destFile))
83 | .catch(function (error) {
84 | console.error('!!!!!!!!!!!!! ERROR: Failed to copy a file: ' + destFile + ' with error: ' + error);
85 | });
86 | }
87 |
88 | /************************************************************
89 | Delete file from the bucket
90 | Input:
91 | - source GCS URI
92 | ************************************************************/
93 | async function gcsDelete(bucket, file) {
94 | // console.log('Deleting file: '+file);
95 | storage.bucket(bucket).file(file).delete()
96 | .catch(function (error) {
97 | console.error("!!!!!!!!!!!! Failed to delete a file: " + error);
98 | });
99 | }
100 |
101 | /************************************************************
102 | Find the right object based on objectType input in the image
103 | Input:
104 | - object label
105 | - list of object bounding boxes found by Object Detection
106 | Output:
107 | - prefix file path for the future file in the format:
108 | "/<#objects>___file_"
109 | or for cases when object is not found:
110 | "/not_found/"
111 | ************************************************************/
112 | function findObject(objectType, visionResponse) {
113 | let isFound = false;
114 | let count = 0;
115 | let highScore = 0.0;
116 | let lowScore = 1.0;
117 | for (let i = visionResponse.bBoxes.length; i--;) {
118 | // Is this the right object type?
119 | if (visionResponse.bBoxes[i].label.toLocaleLowerCase().indexOf(objectType.toLowerCase()) >= 0) {
120 | isFound = true;
121 | count++;
122 | let score = parseFloat(visionResponse.bBoxes[i].score);
123 | if (highScore < score) {
124 | highScore = score;
125 | }
126 | if (lowScore > score) {
127 | lowScore = score;
128 | }
129 | }
130 | }
131 |
132 | let response = objectType + '/none/';
133 |
134 | if (isFound) {
135 | response = objectType +
136 | '/count_' + count +
137 | '_high_' + highScore.toFixed(4).split('.')[1] +
138 | '_low_' + lowScore.toFixed(4).split('.')[1] +
139 | '_file_';
140 | }
141 | return response;
142 | }
143 |
144 | /************************************************************
145 | Call Vision API to recognize objects in the file
146 | Input: full path to GCS object
147 | Output: VisionResponse object
148 | ************************************************************/
149 | async function recognizeObjectsAsync(gcsPath) {
150 | console.log('recognizeObjectsAsync(): ' + gcsPath);
151 |
152 | // Call REST API Object Detection
153 | // this returns a Promise which when resolved returns the VisionResponse object
154 | return recognizeObjectAPIAsync(gcsPath)
155 | .then((response) => {
156 | return Promise.resolve()
157 | .then(() => {
158 | return createVisionResponse(response);
159 | });
160 | })
161 | .catch((error) => {
162 | console.error("!!!!!!!!!!!!!!! Error calling remote Object Detection API: " + error);
163 | throw error;
164 | });
165 | }
166 |
167 | /************************************************************
168 | Generate response from the ML Vision
169 | Input:
170 | - jsonAPIResponse - response from the Vision API
171 | Output:
172 | - VisionResponse - Coordinates of various objects that were recognized
173 | ************************************************************/
174 | function createVisionResponse(jsonAPIResponse) {
175 | let response = new VisionResponse();
176 | const objResponse = JSON.parse(jsonAPIResponse);
177 |
178 | for (let key in objResponse) {
179 | for (let i = 0; i < objResponse[key].length; i++) {
180 | //console.log("objResponse[key]["+i+"]: "+JSON.stringify(objResponse[key][i]));
181 | const bBox = new BoundingBox(key, objResponse[key][i]["x"], objResponse[key][i]["y"], objResponse[key][i]["w"], objResponse[key][i]["h"], objResponse[key][i]["score"]);
182 | response.addBox(bBox);
183 | }
184 | }
185 | return response;
186 | }
187 |
188 | /************************************************************
189 | Generate response from the ML Vision
190 | Input:
191 | - sensorMessage - message from the car with sensor data
192 | Output:
193 | -
194 | ************************************************************/
195 | function recognizeObjectAPIAsync(gcsURI) {
196 | return new Promise(function (resolve, reject) {
197 |
198 | if (!gcsURI) {
199 | reject("!!!!!!!!!! Error: No gcURI found in sensorMessage");
200 |
201 | } else if (!gcsURI.startsWith("gs://")) {
202 | reject("!!!!!!!!!! Error: gcsURI must start with gs://");
203 |
204 | } else {
205 | // Example request for the inference VM:
206 | // http://xx.xx.xx.xx:8082/v1/objectInference?gcs_uri=gs%3A%2F%2Fcamera-9-roman-test-oct9%2Fimage1.jpg
207 | const apiUrl = OBJECT_INFERENCE_API_URL + "?gcs_uri=" + encodeURIComponent(gcsURI);
208 | const auth = {user: INFERENCE_USER_NAME, pass: INFERENCE_PASSWORD};
209 |
210 | // Measure the time it takes to call inference API
211 | const startTime = Date.now();
212 |
213 | request({uri: apiUrl, auth: auth}, function (err, response, body) {
214 | if (err) {
215 | console.error("!!!!!!!!! ERROR calling remote ML API: " + err + ". Please verify that your Inference VM and the App are up and running and proper HTTP port is open in the firewall.");
216 | reject(err);
217 | } else {
218 | console.log("Vision API call took " + (Date.now() - startTime) + " ms. URI: " + apiUrl);
219 | if (response.statusCode !== 200) {
220 | reject("!!!!!!!!!!!! Error: Received " + response.statusCode + " from API");
221 | } else {
222 | resolve(body);
223 | }
224 | }
225 | });
226 | }
227 | });
228 | }
229 |
230 | /************************************************************
231 | Recursively process list of files
232 | Input:
233 | - List of files to be processed
234 | Output:
235 | - None
236 | ************************************************************/
237 | async function processFilesAsync(files) {
238 | for (let file of files) {
239 | console.log('--- #' + successCount + ': ' + file.name);
240 | if (DEBUG_COUNT > MAX_COUNT) {
241 | break;
242 | } else {
243 | DEBUG_COUNT++;
244 | }
245 |
246 | await processOneFileAsync(process.env.CLOUD_BUCKET, file.name)
247 | .then(() => {
248 | console.log('=== done: #' + successCount + ': ' + file.name);
249 | successCount++;
250 | })
251 | .catch(function (error) {
252 | console.error('!!! Error processing file <' + file.name + '> with the error: ' + error);
253 | });
254 | }
255 | }
256 |
257 | /************************************************************
258 | MAIN
259 | ************************************************************/
260 | console.log("Starting up: Vroom Vroom...");
261 |
262 | let DEBUG_COUNT = 0;
263 | const MAX_COUNT = 50000;
264 |
265 | let bucket = storage.bucket(process.env.CLOUD_BUCKET);
266 |
267 | // bucket.getFiles({}, (err, files) => {console.log(err,files)});
268 | bucket.getFiles({}, (err, files) => {
269 | if (err) {
270 | console.error('!!! ERROR listing of files in bucket <: ' + process.env.CLOUD_BUCKET + '>: ' + err);
271 | } else {
272 | console.log('Bucket <' + process.env.CLOUD_BUCKET + '> contains <' + files.length + '> files.');
273 | processFilesAsync(files).then(() => {
274 | console.log('# of files processed successfully: ' + successCount);
275 | })
276 | }
277 | });
--------------------------------------------------------------------------------
/admin/create-hackathon.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###############################################################################
4 | # This script creates new hackathon event with users and folders generated
5 | ###############################################################################
6 |
7 | #
8 | # Copyright 2018 Google LLC
9 | #
10 | # Licensed under the Apache License, Version 2.0 (the "License");
11 | # you may not use this file except in compliance with the License.
12 | # You may obtain a copy of the License at
13 | #
14 | # https://www.apache.org/licenses/LICENSE-2.0
15 | #
16 | # Unless required by applicable law or agreed to in writing, software
17 | # distributed under the License is distributed on an "AS IS" BASIS,
18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | # See the License for the specific language governing permissions and
20 | # limitations under the License.
21 | #
22 |
23 | set -u # This prevents running the script if any of the variables have not been set
24 | set -e # Exit if error is detected during pipeline execution
25 |
26 | source ./setenv.sh
27 |
28 | ### File with the list of user names and passwords
29 | USER_LIST="$TMP/users.csv"
30 |
31 | ###############################################################################
32 | # Create special Read Only group
33 | ###############################################################################
34 | create_read_only_group() {
35 | echo_my "Create read only group '$ADMIN_READ_GROUP'..."
36 | $GAM create group "$ADMIN_READ_GROUP" name "Resource Read-Only group" description "Read only access to common resources" | true # ignore if error
37 | echo_my "Permissions for project '$ADMIN_PROJECT_ID'..."
38 | COMMAND="gcloud projects add-iam-policy-binding $ADMIN_PROJECT_ID --member=group:$ADMIN_READ_GROUP --role=roles/"
39 |
40 | echo_my "All users need to be able to read pre-annotated images..."
41 | eval ${COMMAND}storage.objectViewer
42 |
43 | echo_my "All users need to be able to use Windows VM image we provide for annotations with some software pre-installed on it..."
44 | eval ${COMMAND}compute.imageUser
45 |
46 | echo_my "All users need to be able to lookup IP address of the DEMO Inference VM from project '$DEMO_PROJECT'..."
47 | gcloud projects add-iam-policy-binding $DEMO_PROJECT --member=group:$ADMIN_READ_GROUP --role="roles/compute.networkUser"
48 | }
49 |
50 | ###############################################################################
51 | # Add user to the domain: https://developers.google.com/admin-sdk/directory/v1/guides/manage-users
52 | # Input:
53 | # 1 - user number
54 | # 2 - team number
55 | ###############################################################################
56 | add_user() {
57 | local USER_NUM=$1
58 | local TEAM_NUM=$2
59 | local PASSWORD=$(generate_password)
60 |
61 | $GAM create user $(user_name $USER_NUM $TEAM_NUM) firstname "User$USER_NUM" lastname "Member of Team $TEAM_NUM" \
62 | password $PASSWORD
63 |
64 | echo "$(user_name $USER_NUM $TEAM_NUM)@$DOMAIN,$PASSWORD" >> ${USER_LIST}
65 | }
66 |
67 | ###############################################################################
68 | # Add new team to the domain
69 | # Input:
70 | # 1 - team number
71 | ###############################################################################
72 | create_team() {
73 | local TEAM_NUM=$1
74 | echo_my "create_team(): Creating team #$TEAM_NUM..."
75 |
76 | $GAM create group "$(team_name $TEAM_NUM)" name "Car team $TEAM_NUM" description \
77 | "Developers working on the car # $TEAM_NUM" | true # ignore if error
78 |
79 | for j in $(seq 1 $NUM_PEOPLE_PER_TEAM);
80 | do
81 | add_user $j $TEAM_NUM
82 | echo_my "Adding user to his team group..."
83 | $GAM update group "$(team_name $TEAM_NUM)" add member $(user_name $j $TEAM_NUM)@$DOMAIN
84 | echo_my "Adding user to the read-only group for shared resources..."
85 | $GAM update group "$ADMIN_READ_GROUP" add member $(user_name $j $TEAM_NUM)@$DOMAIN
86 | done
87 | }
88 |
89 | ###############################################################################
90 | # Create all groups
91 | ###############################################################################
92 | create_groups_and_users() {
93 | echo_my "create_groups_and_users(): started..."
94 | echo "Email,Password" > $USER_LIST
95 |
96 | for i in $(seq $TEAM_START_NUM $NUM_TEAMS);
97 | do
98 | create_team $i
99 | done
100 | }
101 |
102 | ###############################################################################
103 | # Create folders and projects in GCP
104 | ###############################################################################
105 | create_folders() {
106 | echo_my "create_folders(): Creating event parent folder..."
107 | gcloud alpha resource-manager folders create --display-name=$TOP_FOLDER --organization=$(lookup_org_id) \
108 | | true # ignore if already exists
109 |
110 | echo_my "Creating children folders for each car team..."
111 | local PARENT_FOLDER_ID=$(find_top_folder_id $TOP_FOLDER)
112 |
113 | for i in $(seq $TEAM_START_NUM $NUM_TEAMS);
114 | do
115 | gcloud alpha resource-manager folders create --display-name=$(team_folder_name $i) --folder=$PARENT_FOLDER_ID \
116 | | true # ignore if already exists
117 |
118 | local NEW_FOLDER_ID=$(find_folder_id $(team_folder_name $i) $PARENT_FOLDER_ID)
119 | echo_my "NEW_FOLDER_ID=$NEW_FOLDER_ID"
120 |
121 | # See docs: https://cloud.google.com/iam/docs/understanding-roles
122 | gcloud alpha resource-manager folders add-iam-policy-binding $NEW_FOLDER_ID \
123 | --member=group:$(team_name $i)@$DOMAIN --role="organizations/$(lookup_org_id)/roles/$DERBY_DEV_ROLE"
124 |
125 | local COMMAND="gcloud alpha resource-manager folders add-iam-policy-binding $NEW_FOLDER_ID --member=group:$(team_name $i)@$DOMAIN --role=roles/"
126 |
127 | eval ${COMMAND}resourcemanager.projectCreator
128 | eval ${COMMAND}resourcemanager.folderEditor
129 | eval ${COMMAND}resourcemanager.folderIamAdmin
130 | eval ${COMMAND}resourcemanager.projectIamAdmin
131 | eval ${COMMAND}resourcemanager.folderCreator
132 | eval ${COMMAND}resourcemanager.projectDeleter
133 | eval ${COMMAND}appengine.appAdmin
134 | eval ${COMMAND}dialogflow.admin
135 | eval ${COMMAND}ml.admin
136 | eval ${COMMAND}pubsub.admin
137 | eval ${COMMAND}storage.admin
138 | eval ${COMMAND}iam.serviceAccountAdmin
139 | eval ${COMMAND}iam.serviceAccountKeyAdmin
140 | eval ${COMMAND}iam.serviceAccountTokenCreator
141 | eval ${COMMAND}iam.serviceAccountUser
142 | eval ${COMMAND}iam.securityReviewer
143 | eval ${COMMAND}servicemanagement.quotaAdmin
144 | eval ${COMMAND}errorreporting.admin
145 | eval ${COMMAND}logging.admin
146 | eval ${COMMAND}monitoring.admin
147 | eval ${COMMAND}cloudiot.admin
148 | eval ${COMMAND}compute.instanceAdmin.v1
149 | eval ${COMMAND}compute.imageUser
150 | eval ${COMMAND}compute.networkAdmin
151 | eval ${COMMAND}compute.securityAdmin
152 | eval ${COMMAND}source.admin
153 | eval ${COMMAND}clouddebugger.user
154 | eval ${COMMAND}editor
155 | done
156 | }
157 |
158 | #############################################
159 | # Create Special Cloud Derby Role - this is only used for the service account permissions
160 | #############################################
161 | create_role() {
162 | if gcloud iam roles list --organization $(lookup_org_id) | grep -q $DERBY_DEV_ROLE; then
163 | echo_my "Role '$DERBY_DEV_ROLE' already exists - updating it..."
164 | ACTION="update"
165 | else
166 | echo "Creating a role '$DERBY_DEV_ROLE'..."
167 | ACTION="create"
168 | fi
169 |
170 | PERMISSIONS="\
171 | compute.projects.get,\
172 | compute.projects.setCommonInstanceMetadata,\
173 | pubsub.subscriptions.consume,\
174 | pubsub.subscriptions.create,\
175 | pubsub.subscriptions.delete,\
176 | pubsub.subscriptions.get,\
177 | pubsub.subscriptions.list,\
178 | pubsub.subscriptions.update,\
179 | pubsub.topics.attachSubscription,\
180 | pubsub.topics.create,\
181 | pubsub.topics.delete,\
182 | pubsub.topics.get,\
183 | pubsub.topics.list,\
184 | pubsub.topics.publish,\
185 | pubsub.topics.update,\
186 | resourcemanager.organizations.get,\
187 | resourcemanager.projects.get,\
188 | resourcemanager.projects.getIamPolicy,\
189 | resourcemanager.projects.list,\
190 | storage.buckets.create,\
191 | storage.buckets.delete,\
192 | storage.buckets.get,\
193 | storage.buckets.getIamPolicy,\
194 | storage.buckets.list,\
195 | storage.buckets.setIamPolicy,\
196 | storage.buckets.update,\
197 | storage.objects.create,\
198 | storage.objects.delete,\
199 | storage.objects.get,\
200 | storage.objects.getIamPolicy,\
201 | storage.objects.list,\
202 | storage.objects.setIamPolicy,\
203 | storage.objects.update,\
204 | clouddebugger.breakpoints.create,\
205 | clouddebugger.breakpoints.delete,\
206 | clouddebugger.breakpoints.get,\
207 | clouddebugger.breakpoints.list,\
208 | clouddebugger.breakpoints.listActive,\
209 | clouddebugger.breakpoints.update,\
210 | clouddebugger.debuggees.create,\
211 | clouddebugger.debuggees.list,\
212 | cloudiot.devices.bindGateway,\
213 | cloudiot.devices.create,\
214 | cloudiot.devices.delete,\
215 | cloudiot.devices.get,\
216 | cloudiot.devices.list,\
217 | cloudiot.devices.sendCommand,\
218 | cloudiot.devices.unbindGateway,\
219 | cloudiot.devices.update,\
220 | cloudiot.devices.updateConfig,\
221 | cloudiot.registries.create,\
222 | cloudiot.registries.delete,\
223 | cloudiot.registries.get,\
224 | cloudiot.registries.getIamPolicy,\
225 | cloudiot.registries.list,\
226 | cloudiot.registries.setIamPolicy,\
227 | cloudiot.registries.update"
228 |
229 | #resourcemanager.projects.update,\
230 | #resourcemanager.projects.updateLiens,\
231 | #appengine.applications.create,\
232 | #appengine.applications.get,\
233 | #appengine.applications.update,\
234 | #appengine.instances.delete,\
235 | #appengine.instances.get,\
236 | #appengine.instances.list,\
237 | #appengine.services.delete,\
238 | #appengine.services.get,\
239 | #appengine.services.list,\
240 | #appengine.services.update,\
241 | #appengine.versions.create,\
242 | #appengine.versions.delete,\
243 | #appengine.versions.get,\
244 | #appengine.versions.list,\
245 | #appengine.versions.update,\
246 | #iam.serviceAccountKeys.create,\
247 | #iam.serviceAccountKeys.delete,\
248 | #iam.serviceAccountKeys.get,\
249 | #iam.serviceAccountKeys.list,\
250 | #iam.serviceAccounts.actAs,\
251 | #iam.serviceAccounts.create,\
252 | #iam.serviceAccounts.delete,\
253 | #iam.serviceAccounts.get,\
254 | #iam.serviceAccounts.getAccessToken,\
255 | #iam.serviceAccounts.getIamPolicy,\
256 | #iam.serviceAccounts.implicitDelegation,\
257 | #iam.serviceAccounts.list,\
258 | #iam.serviceAccounts.setIamPolicy,\
259 | #iam.serviceAccounts.signBlob,\
260 | #iam.serviceAccounts.signJwt,\
261 | #iam.serviceAccounts.update
262 |
263 | gcloud iam roles ${ACTION} $DERBY_DEV_ROLE \
264 | --organization $(lookup_org_id) \
265 | --title "Cloud Derby Developer Role" \
266 | --description "Access to resources needed to develop and deploy Cloud Derby" \
267 | --stage "GA" \
268 | --permissions $PERMISSIONS
269 |
270 | sleep 3
271 | }
272 |
273 | ###############################################################################
274 | # MAIN
275 | ###############################################################################
276 | print_header "Creating workshop users, folders, etc..."
277 |
278 | setup
279 |
280 | create_read_only_group
281 |
282 | create_role
283 |
284 | create_groups_and_users
285 |
286 | create_folders
287 |
288 | print_footer "SUCCESS: New workshop configuration created."
289 |
--------------------------------------------------------------------------------