├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── SpheroSDKLicense2Oct2019.pdf ├── package.json ├── public ├── dashboard │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── package.json │ ├── public │ │ ├── RVR-Logo-Black.png │ │ ├── RVR-Logo-WhiteTransparent.png │ │ ├── favicon.png │ │ ├── index.html │ │ ├── manifest.json │ │ └── robots.txt │ ├── sdk-v4-convenience-raspberry-pi-client-js-0.0.0.tgz │ └── src │ │ ├── App.js │ │ ├── App.test.js │ │ ├── components │ │ ├── PageBody.js │ │ ├── SleepWakeButtons.js │ │ ├── TopBar.js │ │ ├── colorWheel │ │ │ └── colorWheel.js │ │ ├── control │ │ │ ├── NightNightButton.js │ │ │ ├── RVRJoystick.js │ │ │ └── WakeUpButton.js │ │ ├── power │ │ │ ├── BatteryPercentage.js │ │ │ ├── BatteryVoltage.js │ │ │ ├── Power.js │ │ │ └── VoltageState.js │ │ ├── sensorStreaming │ │ │ ├── AccelerometerChart.js │ │ │ ├── AmbientLightChart.js │ │ │ ├── AttitudeChart.js │ │ │ ├── GyroscopeChart.js │ │ │ ├── LocatorChart.js │ │ │ ├── SensorStreaming.js │ │ │ ├── SpeedChart.js │ │ │ └── VelocityChart.js │ │ └── systemInformation │ │ │ ├── BluetoothAdvertisingName.js │ │ │ ├── BoardRevision.js │ │ │ ├── BootloaderVersion.js │ │ │ ├── MACAddress.js │ │ │ ├── MainAppVersion.js │ │ │ ├── SKU.js │ │ │ └── SystemInformation.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── serviceWorker.js ├── drive-control-tests │ ├── aim-start-stop.html │ ├── drive-regular.html │ ├── roll-start-stop.html │ └── turn.html ├── generic.html ├── graph-mocks │ ├── ambient-light.html │ └── js-client │ │ └── index.js ├── infrared-control-tests │ ├── broadcast-ir.html │ └── listen-for-and-send-ir.html ├── led-control-tests │ └── single-multiple-all.html └── sensor-control-tests │ ├── all-slots-not-all-sensors.html │ ├── all-twice.html │ ├── all.html │ ├── none.html │ ├── not-all-slots.html │ └── st-only.html ├── src ├── api │ ├── control-routers │ │ ├── drive-control-router.ts │ │ ├── drive-control-swagger.json │ │ ├── index.ts │ │ ├── infrared-control-router.ts │ │ ├── infrared-control-swagger.json │ │ ├── led-control-router.ts │ │ ├── led-control-swagger.json │ │ ├── sensor-streaming-control-router.ts │ │ └── sensor-streaming-swagger.json │ ├── device-router-base.ts │ ├── diagnostics-router.ts │ ├── generic-command-router.ts │ ├── index.ts │ ├── router-base.ts │ ├── router-interfaces.ts │ └── v1.0 │ │ ├── 0x10-api-and-shell-device-router.ts │ │ ├── 0x11-system-info-device-router.ts │ │ ├── 0x13-power-device-router.ts │ │ ├── 0x16-drive-device-router.ts │ │ ├── 0x18-sensor-device-router.ts │ │ ├── 0x19-connection-device-router.ts │ │ ├── 0x1A-io-device-router.ts │ │ ├── command-parsers │ │ ├── 0x10-api-and-shell │ │ │ ├── 0x00-echo-command-parser.ts │ │ │ └── metadata.txt │ │ ├── 0x11-system-info │ │ │ ├── 0x00-get-main-application-version-command-parser.ts │ │ │ ├── 0x01-get-bootloader-version-command-parser.ts │ │ │ ├── 0x03-get-board-revision-command-parser.ts │ │ │ ├── 0x06-get-mac-address-command-parser.ts │ │ │ ├── 0x13-get-stats-id-command-parser.ts │ │ │ ├── 0x1F-get-processor-name-command-parser.ts │ │ │ ├── 0x38-get-sku-command-parser.ts │ │ │ ├── 0x39-get-core-up-time-in-milliseconds-command-parser.ts │ │ │ └── metadata.txt │ │ ├── 0x13-power │ │ │ ├── 0x10-get-battery-percentage-command-parser.ts │ │ │ ├── 0x17-get-battery-voltage-state-command-parser.ts │ │ │ ├── 0x1B-enable-battery-voltage-state-change-notify-command-parser.ts │ │ │ ├── 0x1C-battery-voltage-state-change-notify-command-parser.ts │ │ │ ├── 0x25-get-battery-voltage-in-volts-command-parser.ts │ │ │ ├── 0x26-get-battery-voltage-state-thresholds-command-parser.ts │ │ │ ├── 0x27-get-current-sense-amplifier-current-command-parser.ts │ │ │ └── metadata.txt │ │ ├── 0x16-drive │ │ │ ├── 0x01-raw-motors-command-parser.ts │ │ │ ├── 0x07-drive-with-heading-command-parser.ts │ │ │ ├── 0x25-enable-motor-stall-notify-command-parser.ts │ │ │ ├── 0x26-motor-stall-notify-command-parser.ts │ │ │ ├── 0x27-enable-motor-fault-notify-command-parser.ts │ │ │ ├── 0x28-motor-fault-notify-command-parser.ts │ │ │ ├── 0x29-get-motor-fault-state-command-parser.ts │ │ │ └── metadata.txt │ │ ├── 0x18-sensor │ │ │ ├── 0x0F-enable-gyro-max-notify-command-parser.ts │ │ │ ├── 0x10-gyro-max-notify-command-parser.ts │ │ │ ├── 0x17-set-locator-flags-command-parser.ts │ │ │ ├── 0x22-get-bot-to-bot-infrared-readings-command-parser.ts │ │ │ ├── 0x23-get-rgbc-sensor-values-command-parser.ts │ │ │ ├── 0x27-start-robot-to-robot-infrared-broadcasting-command-parser.ts │ │ │ ├── 0x28-start-robot-to-robot-infrared-following-command-parser.ts │ │ │ ├── 0x2C-robot-to-robot-infrared-message-received-notify-command-parser.ts │ │ │ ├── 0x30-get-ambient-light-sensor-value-command-parser.ts │ │ │ ├── 0x33-start-robot-to-robot-infrared-evading-command-parser.ts │ │ │ ├── 0x35-enable-color-detection-notify-command-parser.ts │ │ │ ├── 0x36-color-detection-notify-command-parser.ts │ │ │ ├── 0x38-enable-color-detection-command-parser.ts │ │ │ ├── 0x39-configure-streaming-service-command-parser.ts │ │ │ ├── 0x3A-start-streaming-service-command-parser.ts │ │ │ ├── 0x3D-streaming-service-data-notify-command-parser.ts │ │ │ ├── 0x3E-enable-robot-infrared-message-notify-command-parser.ts │ │ │ ├── 0x3F-send-infrared-message-command-parser.ts │ │ │ ├── 0x42-get-motor-temperature-command-parser.ts │ │ │ ├── 0x4B-get-motor-thermal-protection-status-command-parser.ts │ │ │ ├── 0x4C-enable-motor-thermal-protection-status-notify-command-parser.ts │ │ │ ├── 0x4D-motor-thermal-protection-status-notify-command-parser.ts │ │ │ └── metadata.txt │ │ ├── 0x19-connection │ │ │ ├── 0x05-get-bluetooth-advertising-name-command-parser.ts │ │ │ └── metadata.txt │ │ └── 0x1A-io │ │ │ ├── 0x1A-set-all-leds-command-parser.ts │ │ │ ├── 0x44-get-active-color-palette-command-parser.ts │ │ │ ├── 0x45-set-active-color-palette-command-parser.ts │ │ │ ├── 0x46-get-color-identification-report-command-parser.ts │ │ │ ├── 0x47-load-color-palette-command-parser.ts │ │ │ ├── 0x48-save-color-palette-command-parser.ts │ │ │ └── metadata.txt │ │ ├── index.ts │ │ ├── metadata.txt │ │ └── swagger.json ├── configuration.ts ├── constants.ts ├── models │ ├── api-command-message.ts │ ├── api-message-lite.ts │ ├── api-message.ts │ ├── api-response-message.ts │ ├── deferred-promise.ts │ ├── key-value-pair.ts │ ├── pretty-print.ts │ ├── streaming-provider.ts │ ├── streaming-service-attribute.ts │ ├── streaming-service.ts │ ├── streaming-slot.ts │ └── version.ts ├── modules │ ├── api-dal-interface.ts │ ├── api-dal-mock.ts │ ├── api-dal-uart.ts │ ├── api-parser.ts │ ├── app.ts │ ├── check-firmware-version.ts │ ├── command-parser-factory.ts │ ├── controls │ │ └── v1.0 │ │ │ ├── drive-control.ts │ │ │ ├── infrared-control.ts │ │ │ ├── led-control.ts │ │ │ └── sensor-control.ts │ └── logger.ts ├── server.ts └── utilities │ ├── byte-conversion-utilities.ts │ ├── date-time-utilities.ts │ ├── math-utilities.ts │ ├── merge-swagger-specs.ts │ └── string-utilities.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Specific files 2 | package-lock.json 3 | *.DS_Store 4 | 5 | # Specific file types 6 | *.d.ts 7 | *.js.map 8 | 9 | # Node Modules directory 10 | node_modules 11 | dist 12 | 13 | # JetBrains WebStorm directory 14 | .idea/* 15 | 16 | #misc 17 | npm-debug.log* 18 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .idea 3 | node_modules 4 | tests 5 | documentation 6 | .npmignore 7 | test.js 8 | tsconfig.json 9 | dist -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sphero SDK 2 | 3 | Greetings adventurous students, developers, hackers, and makers! RVR is one of the best starting points into the vast world of robotics, and we’re here to help you get started with using our approachable development tools. 4 | 5 | ## First things first 6 | 7 | ### Getting Started 8 | 9 | Visit our [Getting Started](https://sdk.sphero.com/getting_started) to learn more about the ins-and-outs of working with RVR, including some important details on the getting started process. 10 | 11 | ### More information and documentation 12 | 13 | Visit our [SDK website](https://sdk.sphero.com) to find more information about RVR, the SDK and the API! 14 | 15 | ### Where to get help 16 | 17 | Visit our [community forum](https://community.sphero.com/c/advanced-programming) to get help, share your project, or help others! 18 | 19 | ### Staying up to date 20 | 21 | Consider [signing up](https://sdk.sphero.com/sign-up) for our SDK email list to stay current on new features being released in our robot firmware as well as our SDKs, including new platform / language support. 22 | 23 | ## About the Raspberry Pi Node.js SDK 24 | 25 | Our SDK is meant for beginners and experts alike. If you are just getting started on your journey, and would like some helpful guides on using RVR’s features, you can find plenty of examples in our `public` directory. 26 | 27 | The best way to get familiar with either version of our SDK is to dive into the `public` samples, and follow along with code. Feel free to modify the programs and experiment to gain a better understanding of what different values, and inputs affect the operation of RVR. If you find yourself needing assistance, we are always happy to provide help through our community portal where other Sphero enthusiasts, as well as our developers can share knowledge, and help you succeed with your ideas. 28 | 29 | Happy coding! 30 | -------------------------------------------------------------------------------- /SpheroSDKLicense2Oct2019.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphero-inc/sphero-sdk-raspberrypi-nodejs/8de9ec73c0b8752b72b671843f77199711ef93cf/SpheroSDKLicense2Oct2019.pdf -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sphero-sdk-raspberrypi-nodejs", 3 | "description": "Sphero SDK - Raspberry Pi Node.js", 4 | "version": "1.0.0", 5 | "main": "./dist/server.js", 6 | "types": "./dist/server.d.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "start": "node dist/server.js" 10 | }, 11 | "dependencies": { 12 | "@material-ui/icons": "^4.4.3", 13 | "body-parser": "^1.18.2", 14 | "compression": "^1.7.1", 15 | "cors": "^2.8.5", 16 | "express": "^4.16.2", 17 | "ip": "^1.1.5", 18 | "nvd3": "^1.8.6", 19 | "react-joystick-component": "^1.4.0", 20 | "serialport": "^7.1.3", 21 | "swagger-ui-express": "^2.0.13", 22 | "winston": "^2.4.0", 23 | "ws": "^6.1.2" 24 | }, 25 | "devDependencies": { 26 | "@types/body-parser": "^1.16.8", 27 | "@types/compression": "0.0.35", 28 | "@types/cors": "^2.8.4", 29 | "@types/express": "^4.11.0", 30 | "@types/ip": "0.0.30", 31 | "@types/node": "^8.5.2", 32 | "@types/serialport": "^7.0.1", 33 | "@types/winston": "^2.3.7", 34 | "@types/ws": "^6.0.2", 35 | "nodemon": "^1.19.3", 36 | "ts-node": "^4.1.0", 37 | "typescript": "^2.6.2" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "https://github.com/sphero-inc/sphero-sdk-raspberrypi-nodejs" 42 | }, 43 | "author": "Sphero", 44 | "license": "SEE LICENSE IN LICENSE.md" 45 | } 46 | -------------------------------------------------------------------------------- /public/dashboard/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | -------------------------------------------------------------------------------- /public/dashboard/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.jsxSingleQuote": true, 3 | "prettier.singleQuote": true, 4 | "prettier.useTabs": true, 5 | "editor.wordWrap": "on" 6 | } -------------------------------------------------------------------------------- /public/dashboard/README.md: -------------------------------------------------------------------------------- 1 | # Welcome! 2 | 3 | We are so happy to see you diving in with the Node.js SDK! We've added some comments to the code here, but have also created a more in-depth write-up for this example on our [SDK site](https://sdk.sphero.com). Below are some details on running your app, once you've completed the tutorial: 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.
15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.
25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.
28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | -------------------------------------------------------------------------------- /public/dashboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashboard", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.7.0", 7 | "@material-ui/icons": "^4.4.3", 8 | "c3": "^0.7.10", 9 | "d3": "^5.12.0", 10 | "nvd3": "^1.8.6", 11 | "react": "^16.10.1", 12 | "react-c3js": "^0.1.20", 13 | "react-color": "^2.17.3", 14 | "react-dom": "^16.10.1", 15 | "react-joystick-component": "^1.4.0", 16 | "react-scripts": "3.1.2", 17 | "sdk-v4-convenience-raspberry-pi-client-js": "file:sdk-v4-convenience-raspberry-pi-client-js-0.0.0.tgz", 18 | "styled-components": "^4.4.0" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": "react-app" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | }, 41 | "devDependencies": { 42 | "@material-ui/styles": "^4.6.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /public/dashboard/public/RVR-Logo-Black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphero-inc/sphero-sdk-raspberrypi-nodejs/8de9ec73c0b8752b72b671843f77199711ef93cf/public/dashboard/public/RVR-Logo-Black.png -------------------------------------------------------------------------------- /public/dashboard/public/RVR-Logo-WhiteTransparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphero-inc/sphero-sdk-raspberrypi-nodejs/8de9ec73c0b8752b72b671843f77199711ef93cf/public/dashboard/public/RVR-Logo-WhiteTransparent.png -------------------------------------------------------------------------------- /public/dashboard/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphero-inc/sphero-sdk-raspberrypi-nodejs/8de9ec73c0b8752b72b671843f77199711ef93cf/public/dashboard/public/favicon.png -------------------------------------------------------------------------------- /public/dashboard/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Sphero Public SDK 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/dashboard/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "SDK", 3 | "name": "Sphero Public SDK", 4 | "icons": [ 5 | { 6 | "src": "favicon.png", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "RVR-Logo-Black.png", 12 | "type": "image/png", 13 | "sizes": "1498×634" 14 | }, 15 | { 16 | "src": "RVR-logo-WhiteTransparent.png", 17 | "type": "image/png", 18 | "sizes": "1498×634" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "aliceblue" 25 | } 26 | -------------------------------------------------------------------------------- /public/dashboard/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /public/dashboard/sdk-v4-convenience-raspberry-pi-client-js-0.0.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphero-inc/sphero-sdk-raspberrypi-nodejs/8de9ec73c0b8752b72b671843f77199711ef93cf/public/dashboard/sdk-v4-convenience-raspberry-pi-client-js-0.0.0.tgz -------------------------------------------------------------------------------- /public/dashboard/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SpheroRvrToy } from 'sdk-v4-convenience-raspberry-pi-client-js'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | import PageBody from './components/PageBody'; 6 | import TopBar from './components/TopBar'; 7 | 8 | // Because we'll be communicating with the RVR throughout the application, we initiate our specific instance of it (with our IP address and port) here, at the top level, in the App.js file 9 | let rvrToy = new SpheroRvrToy('10.211.2.18', '2010'); 10 | 11 | // This is a styled component; we are using the ones specific to Material UI, as opposed to the original, vanilla version 12 | const TheBigRoot = styled('div')({ 13 | display: 'flex', 14 | font: 'Roboto' 15 | }); 16 | 17 | const App = () => { 18 | return ( 19 | 20 | {/* TopBar is the header bar across the top of the page (where you could expand by adding a search function or the like) */} 21 | 22 | {/* PageBody is where all of the action happens. We import our instance of rvrToy so that we can utilize it in the different areas of the dashboard */} 23 | 24 | 25 | ); 26 | }; 27 | 28 | export default App; 29 | -------------------------------------------------------------------------------- /public/dashboard/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /public/dashboard/src/components/PageBody.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Paper, Grid } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | // The page body is basically a "holder" for all of the different areas where we display information about the connected RVR. We want to keep each file in and of itself as simple as possible and not include logic where it doesn't need to be, making each file's purpose clear and increasing the ability to reuse components. Below are all the different components that we display on our page body: 6 | import ColorWheel from './colorWheel/colorWheel'; 7 | import Power from './power/Power'; 8 | import RVRJoystick from './control/RVRJoystick'; 9 | import SensorStreaming from './sensorStreaming/SensorStreaming'; 10 | import SleepWakeButtons from './SleepWakeButtons'; 11 | import SystemInformation from './systemInformation/SystemInformation'; 12 | 13 | const BodyContent = styled('div')({ 14 | flexGrow: 1, 15 | backgroundColor: 'aliceblue', 16 | padding: '20px' 17 | }); 18 | 19 | const PageRow = styled(Paper)({ 20 | margin: '20px auto 0px auto', 21 | background: 'transparent', 22 | boxShadow: 'none' 23 | }); 24 | 25 | const TopRow = styled(PageRow)({ 26 | marginTop: '50px' 27 | }); 28 | 29 | const MainPaper = styled(Paper)({ 30 | padding: '10px', 31 | marginRight: '50px', 32 | textAlign: 'center' 33 | }); 34 | 35 | const PageBody = props => { 36 | return ( 37 | 38 | 39 | {/* Making the grid below a container allows us to position items in rows on the page, rather than them stacking themselves */} 40 | 41 | 42 | {/* Making the grid below a container allows us to position the elements within this card ("Paper") next to each other, rather than on top of one another */} 43 | 44 | 45 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {/* For Sensor Streaming, we only need the sensors part of RVR. So that we are not having to type "props.rvrToy.getSensorControl().thingYouWant" every time we do something with the sensors, we just pass a "sensorControl" prop into Sensor Streaming */} 62 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | ); 93 | }; 94 | 95 | export default PageBody; 96 | -------------------------------------------------------------------------------- /public/dashboard/src/components/SleepWakeButtons.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import WakeUpButton from './control/WakeUpButton'; 3 | import NightNightButton from './control/NightNightButton'; 4 | import { Paper, Card, Grid } from '@material-ui/core'; 5 | import { styled } from '@material-ui/styles'; 6 | 7 | const SleepWakeSection = styled(Paper)({ 8 | padding: '10px', 9 | background: 'transparent', 10 | boxShadow: 'none' 11 | }); 12 | 13 | const ButtonCard = styled(Card)({ 14 | margin: '15px', 15 | padding: '10px', 16 | textAlign: 'center' 17 | }); 18 | 19 | // Sleep/Wake Buttons allows us to group together the buttons that you can press to send RVR into soft sleep or to wake RVR up so that you can begin sending it commands. We place the buttons on a card to organize them, but we don't actually want to see the card containing them, so we make the background transparent in the SleepWakeSection styled component above. 20 | const SleepWakeButtons = props => { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ); 39 | }; 40 | 41 | export default SleepWakeButtons; 42 | -------------------------------------------------------------------------------- /public/dashboard/src/components/TopBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styled } from '@material-ui/styles'; 3 | import { AppBar, Toolbar, Typography } from '@material-ui/core'; 4 | 5 | const drawerWidth = 240; 6 | 7 | const StyledTopBar = styled(AppBar)({ 8 | width: '100%', 9 | marginLeft: drawerWidth, 10 | backgroundColor: '#4200b7' 11 | }); 12 | 13 | const TopBar = () => { 14 | return ( 15 | 16 | 17 | 18 | RVR Dashboard 19 | 20 | 21 | 22 | ); 23 | }; 24 | 25 | export default TopBar; 26 | -------------------------------------------------------------------------------- /public/dashboard/src/components/control/NightNightButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const SleepButton = styled(Button)({ 6 | margin: '10px', 7 | borderRadius: '100px', 8 | borderWidth: '5px', 9 | fontSize: '20px', 10 | padding: '20px 15px', 11 | borderColor: '#4200b7', 12 | color: '#4200b7', 13 | textAlign: 'center' 14 | }); 15 | 16 | const NightNightButton = props => { 17 | function rvrLullaby() { 18 | // We use the sleep method on the RVR to put it into a "soft" sleep to save battery. 19 | props.rvrToy.sleep(); 20 | } 21 | 22 | return ( 23 | 24 | Sleepy 25 |
26 | Time! 27 |
28 | ); 29 | }; 30 | 31 | export default NightNightButton; 32 | -------------------------------------------------------------------------------- /public/dashboard/src/components/control/WakeUpButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '@material-ui/core/Button'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const WakeButton = styled(Button)({ 6 | margin: '10px', 7 | borderRadius: '100px', 8 | borderWidth: '5px', 9 | fontSize: '20px', 10 | padding: '20px 25px', 11 | borderColor: '#4200b7', 12 | color: '#4200b7', 13 | textAlign: 'center' 14 | }); 15 | 16 | const WakeUpButton = props => { 17 | function wakeUpRvr() { 18 | // In order to make requests of RVR to *do* anything (drive, change lights, etc), the RVR must be powered on and not in soft- or deep sleep. We've included a button that calls the RVR wake function to allow users to quickly and easily get their RVR ready for action. 19 | props.rvrToy.wake(); 20 | } 21 | 22 | return ( 23 | 24 | WAKE 25 |
26 | UP! 27 |
28 | ); 29 | }; 30 | 31 | export default WakeUpButton; 32 | -------------------------------------------------------------------------------- /public/dashboard/src/components/power/BatteryPercentage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const BatteryPercentageCard = styled(Card)({ 6 | padding: '10px', 7 | textAlign: 'center', 8 | color: 'dark-gray', 9 | width: '250px' 10 | }); 11 | 12 | const Title = styled(Typography)({ 13 | fontSize: '20px' 14 | }); 15 | 16 | class BatteryPercentage extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { data: '' }; 20 | } 21 | 22 | componentDidMount() { 23 | const { rvrToy } = this.props; 24 | // We can get the battery percentage from the Nordic Chip (which has a permanent address of "1") using the getBatteryPercentage command. 25 | rvrToy.getBatteryPercentage(1).then(data => { 26 | this.setState({ 27 | data: JSON.parse(data) 28 | }); 29 | }); 30 | 31 | this.updateBatteryPercent(); 32 | } 33 | 34 | updateBatteryPercent = () => { 35 | const interval = setInterval(() => { 36 | // In order to have the battery percentage stay up to date, we call the getBatteryPercentage command on an interval (but only frequently enough to stay up to date, not so frequently as to drain the battery unnecessarily or clog up the communication channels). 37 | this.props.rvrToy.getBatteryPercentage(1).then(data => { 38 | this.setState({ 39 | data: JSON.parse(data) 40 | }); 41 | }); 42 | }, 900000); 43 | return () => clearInterval(interval); 44 | }; 45 | 46 | render() { 47 | return ( 48 | 49 | 50 | Battery Percentage 51 | {this.state.data && ( 52 | {this.state.data.percentage}% 53 | )} 54 | 55 | 56 | ); 57 | } 58 | } 59 | 60 | export default BatteryPercentage; 61 | -------------------------------------------------------------------------------- /public/dashboard/src/components/power/BatteryVoltage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const BatteryVoltageCard = styled(Card)({ 6 | padding: '10px', 7 | textAlign: 'center', 8 | color: 'dark-gray', 9 | width: '200px' 10 | }); 11 | 12 | const Title = styled(Typography)({ 13 | fontSize: '20px' 14 | }); 15 | 16 | class BatteryVoltage extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { data: '' }; 20 | } 21 | 22 | componentDidMount() { 23 | const { rvrToy } = this.props; 24 | // When calling the getBatteryVoltageInVolts command, a Reading Type of 0 is used for most normal usage. Other values are used internally for testing purposes. 25 | rvrToy.getBatteryVoltageInVolts(0).then(data => { 26 | this.setState({ 27 | data: JSON.parse(data) 28 | }); 29 | }); 30 | 31 | this.updateBatteryVoltage(); 32 | } 33 | 34 | updateBatteryVoltage = () => { 35 | const interval = setInterval(() => { 36 | // In order to have the battery voltage stay up to date, we call the getBatteryVoltageInVolts command on an interval (but only frequently enough to stay up to date, not so frequently as to drain the battery unnecessarily or clog up the communication channels). 37 | this.props.rvrToy.getBatteryVoltageInVolts(0).then(data => { 38 | this.setState({ 39 | data: JSON.parse(data) 40 | }); 41 | }); 42 | }, 900000); 43 | return () => clearInterval(interval); 44 | }; 45 | 46 | render() { 47 | return ( 48 | 49 | 50 | Battery Voltage 51 | {this.state.data && ( 52 | {this.state.data.voltage} 53 | )} 54 | 55 | 56 | ); 57 | } 58 | } 59 | 60 | export default BatteryVoltage; 61 | -------------------------------------------------------------------------------- /public/dashboard/src/components/power/Power.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Paper, Grid, Typography } from '@material-ui/core'; 3 | import BatteryPercentage from './BatteryPercentage'; 4 | import BatteryVoltage from './BatteryVoltage'; 5 | import VoltageState from './VoltageState'; 6 | import { styled } from '@material-ui/styles'; 7 | 8 | const PowerPaper = styled(Paper)({ 9 | padding: '10px', 10 | margin: '20px', 11 | color: 'dark-gray', 12 | height: '415px' 13 | }); 14 | 15 | const Title = styled(Typography)({ 16 | fontSize: '24px' 17 | }); 18 | 19 | const StyledSubGrid = styled(Grid)({ 20 | margin: '30px 30px' 21 | }); 22 | 23 | // We pass rvrToy into each of our components via props, as the commands to retrieve battery information are all called directly on the SpheroRvrToy object (which we created an instance of in App.js, called rvrToy, to use across this application). 24 | 25 | const VersionDetails = props => { 26 | return ( 27 | 28 | 29 | Power Info 30 | 31 | 32 | 33 | xs=3 34 | 35 | 36 | xs=3 37 | 38 | 39 | 40 | 41 | xs=3 42 | 43 | 44 | 45 | 46 | 47 | 48 | ); 49 | }; 50 | 51 | export default VersionDetails; 52 | -------------------------------------------------------------------------------- /public/dashboard/src/components/power/VoltageState.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const VoltageStateCard = styled(Card)({ 6 | padding: '10px', 7 | textAlign: 'center', 8 | color: 'dark-gray', 9 | width: '200px' 10 | }); 11 | 12 | const Title = styled(Typography)({ 13 | fontSize: '20px' 14 | }); 15 | 16 | class VoltageState extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { data: '' }; 20 | } 21 | 22 | componentDidMount() { 23 | const { rvrToy } = this.props; 24 | // We can get the battery voltage state from the Nordic Chip (which has a permanent address of "1") using the getBatteryVoltageState command. 25 | rvrToy.getBatteryVoltageState(1).then(data => { 26 | this.setState({ 27 | data: JSON.parse(data), 28 | definition: 29 | JSON.parse(data).state === 3 30 | ? ' (Critical)' 31 | : JSON.parse(data).state === 2 32 | ? ' (Low)' 33 | : JSON.parse(data).state === 1 34 | ? ' (OK)' 35 | : ' (Unknown)' 36 | }); 37 | }); 38 | 39 | this.updateVoltageState(); 40 | } 41 | 42 | updateVoltageState = () => { 43 | const interval = setInterval(() => { 44 | // In order to have the battery voltage state stay up to date, we call the getBatteryVoltageState command on an interval (but only frequently enough to stay up to date, not so frequently as to drain the battery unnecessarily or clog up the communication channels). 45 | this.props.rvrToy.getBatteryVoltageState(1).then(data => { 46 | this.setState({ 47 | data: JSON.parse(data), 48 | definition: 49 | JSON.parse(data).state === 3 50 | ? ' (Critical)' 51 | : JSON.parse(data).state === 2 52 | ? ' (Low)' 53 | : JSON.parse(data).state === 1 54 | ? ' (OK)' 55 | : ' (Unknown)' 56 | }); 57 | }); 58 | }, 900000); 59 | return () => clearInterval(interval); 60 | }; 61 | 62 | render() { 63 | return ( 64 | 65 | 66 | Voltage State 67 | {this.state.data && ( 68 | 69 | {this.state.data.state} 70 | {this.state.definition} 71 | 72 | )} 73 | 74 | 75 | ); 76 | } 77 | } 78 | 79 | export default VoltageState; 80 | -------------------------------------------------------------------------------- /public/dashboard/src/components/sensorStreaming/AccelerometerChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import c3 from 'c3'; 3 | 4 | class AccelerometerChart extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | x: ['x'], 9 | y: ['y'], 10 | z: ['z'] 11 | }; 12 | } 13 | 14 | renderChart() { 15 | c3.generate({ 16 | bindto: '#accelerometerChart', 17 | data: { 18 | columns: [this.state.x, this.state.y, this.state.z], 19 | type: 'scatter', 20 | labels: true 21 | }, 22 | point: { show: true }, 23 | axis: { 24 | y: { 25 | max: 26 | Math.max( 27 | Math.max(this.state.x), 28 | Math.max(this.state.y), 29 | Math.max(this.state.z) 30 | ) + 10, 31 | min: 32 | Math.min( 33 | Math.min(this.state.x), 34 | Math.min(this.state.y), 35 | Math.min(this.state.z) 36 | ) - 10 37 | }, 38 | x: { show: true } 39 | } 40 | }); 41 | } 42 | 43 | componentDidMount() { 44 | const { sensorControl } = this.props; 45 | // The each sensor we'd like to stream must be enabled and we must let the system know what to do once it is. In this case (for each of the sensors we'll be plotting), we add each new value from the stream to an array, which is subsequently plotted against time. 46 | sensorControl.enableSensor(sensorControl.accelerometer, data => { 47 | this.setState({ 48 | x: this.state.x.concat(data.X), 49 | y: this.state.y.concat(data.Y), 50 | z: this.state.z.concat(data.Z) 51 | }, this.renderChart()); 52 | }); 53 | } 54 | 55 | render() { 56 | return ( 57 |
58 |
59 |
60 | ); 61 | } 62 | } 63 | 64 | export default AccelerometerChart; 65 | -------------------------------------------------------------------------------- /public/dashboard/src/components/sensorStreaming/AmbientLightChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import c3 from 'c3'; 3 | 4 | class AmbientLightChart extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { ambientLight: ['Ambient Light Value'] }; 8 | } 9 | 10 | // Labels on scatter plot points are not available in c3.js 11 | renderChart() { 12 | c3.generate({ 13 | bindto: '#ambientLight', 14 | data: { 15 | columns: [this.state.ambientLight], 16 | type: 'scatter' 17 | }, 18 | // You can adjust the size of the points on your graph by adjusting the radius 19 | point: { 20 | r: 10 21 | }, 22 | axis: { 23 | y: { 24 | max: Math.max(this.state.ambientLight) + 5, 25 | min: Math.min(this.state.ambientLight) - 5 26 | }, 27 | x: { show: true } 28 | } 29 | }); 30 | } 31 | 32 | async componentDidMount() { 33 | const { sensorControl } = this.props; 34 | // The each sensor we'd like to stream must be enabled and we must let the system know what to do once it is. In this case (for each of the sensors we'll be plotting), we add each new value from the stream to an array, which is subsequently plotted against time. 35 | sensorControl.enableSensor(sensorControl.ambientLight, data => { 36 | this.setState({ 37 | ambientLight: this.state.ambientLight.concat(data.Light) 38 | }, this.renderChart()); 39 | }); 40 | } 41 | 42 | render() { 43 | return ( 44 |
45 |
46 |
47 | ); 48 | } 49 | } 50 | 51 | export default AmbientLightChart; 52 | -------------------------------------------------------------------------------- /public/dashboard/src/components/sensorStreaming/AttitudeChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import c3 from 'c3'; 3 | 4 | class AttitudeChart extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | pitch: ['pitch'], 9 | roll: ['roll'], 10 | yaw: ['yaw'] 11 | }; 12 | } 13 | 14 | renderChart() { 15 | c3.generate({ 16 | bindto: '#attitudeChart', 17 | data: { 18 | columns: [this.state.pitch, this.state.roll, this.state.yaw], 19 | type: 'scatter', 20 | labels: true 21 | }, 22 | point: { show: true }, 23 | axis: { 24 | y: { 25 | max: 26 | Math.max( 27 | Math.max(this.state.pitch), 28 | Math.max(this.state.roll), 29 | Math.max(this.state.yaw) 30 | ) + 10, 31 | min: 32 | Math.min( 33 | Math.min(this.state.pitch), 34 | Math.min(this.state.roll), 35 | Math.min(this.state.yaw) 36 | ) - 10 37 | }, 38 | x: { show: true } 39 | } 40 | }); 41 | } 42 | 43 | componentDidMount() { 44 | const { sensorControl } = this.props; 45 | // The each sensor we'd like to stream must be enabled and we must let the system know what to do once it is. In this case (for each of the sensors we'll be plotting), we add each new value from the stream to an array, which is subsequently plotted against time. 46 | sensorControl.enableSensor(sensorControl.attitude, data => { 47 | this.setState({ 48 | pitch: this.state.pitch.concat(data.Pitch), 49 | roll: this.state.roll.concat(data.Roll), 50 | yaw: this.state.yaw.concat(data.Yaw) 51 | }, this.renderChart()); 52 | }); 53 | } 54 | 55 | render() { 56 | return ( 57 |
58 |
59 |
60 | ); 61 | } 62 | } 63 | 64 | export default AttitudeChart; 65 | -------------------------------------------------------------------------------- /public/dashboard/src/components/sensorStreaming/GyroscopeChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import c3 from 'c3'; 3 | 4 | class GyroscopeChart extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | x: ['x'], 9 | y: ['y'], 10 | z: ['z'] 11 | }; 12 | } 13 | 14 | renderChart() { 15 | c3.generate({ 16 | bindto: '#gyroscopeChart', 17 | data: { 18 | columns: [this.state.x, this.state.y, this.state.z], 19 | type: 'scatter', 20 | labels: true 21 | }, 22 | point: { show: true }, 23 | axis: { 24 | y: { 25 | max: 26 | Math.max( 27 | Math.max(this.state.x), 28 | Math.max(this.state.y), 29 | Math.max(this.state.z) 30 | ) + 10, 31 | min: 32 | Math.min( 33 | Math.min(this.state.x), 34 | Math.min(this.state.y), 35 | Math.min(this.state.z) 36 | ) - 10 37 | }, 38 | x: { show: true } 39 | } 40 | }); 41 | } 42 | 43 | componentDidMount() { 44 | const { sensorControl } = this.props; 45 | // The each sensor we'd like to stream must be enabled and we must let the system know what to do once it is. In this case (for each of the sensors we'll be plotting), we add each new value from the stream to an array, which is subsequently plotted against time. 46 | sensorControl.enableSensor(sensorControl.gyroscope, data => { 47 | this.setState({ 48 | x: this.state.x.concat(data.X), 49 | y: this.state.y.concat(data.Y), 50 | z: this.state.z.concat(data.Z) 51 | }, this.renderChart()); 52 | }); 53 | } 54 | 55 | render() { 56 | return ( 57 |
58 |
59 |
60 | ); 61 | } 62 | } 63 | 64 | export default GyroscopeChart; 65 | -------------------------------------------------------------------------------- /public/dashboard/src/components/sensorStreaming/LocatorChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import c3 from 'c3'; 3 | 4 | class LocatorChart extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | x: ['x'], 9 | y: ['y'] 10 | }; 11 | } 12 | 13 | renderChart() { 14 | c3.generate({ 15 | bindto: '#locatorChart', 16 | data: { 17 | columns: [this.state.x, this.state.y], 18 | type: 'scatter', 19 | labels: true 20 | }, 21 | point: { show: true }, 22 | axis: { 23 | y: { 24 | max: Math.max(Math.max(this.state.x), Math.max(this.state.y)) + 10, 25 | min: Math.min(Math.min(this.state.x), Math.min(this.state.y)) - 10 26 | }, 27 | x: { show: true } 28 | } 29 | }); 30 | } 31 | 32 | componentDidMount() { 33 | const { sensorControl } = this.props; 34 | // The each sensor we'd like to stream must be enabled and we must let the system know what to do once it is. In this case (for each of the sensors we'll be plotting), we add each new value from the stream to an array, which is subsequently plotted against time. 35 | sensorControl.enableSensor(sensorControl.locator, data => { 36 | this.setState({ 37 | x: this.state.x.concat(data.X), 38 | y: this.state.y.concat(data.Y) 39 | }, this.renderChart()); 40 | }); 41 | } 42 | 43 | render() { 44 | return ( 45 |
46 |
47 |
48 | ); 49 | } 50 | } 51 | 52 | export default LocatorChart; 53 | -------------------------------------------------------------------------------- /public/dashboard/src/components/sensorStreaming/SensorStreaming.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Tabs, Tab, Typography, Box, AppBar } from '@material-ui/core'; 4 | import { styled } from '@material-ui/styles'; 5 | import AmbientLightChart from './AmbientLightChart'; 6 | import AttitudeChart from './AttitudeChart'; 7 | import AccelerometerChart from './AccelerometerChart'; 8 | import GyroscopeChart from './GyroscopeChart'; 9 | import VelocityChart from './VelocityChart'; 10 | import SpeedChart from './SpeedChart'; 11 | import LocatorChart from './LocatorChart'; 12 | 13 | function TabPanel(props) { 14 | const { children, value, index, ...other } = props; 15 | 16 | return ( 17 | 27 | ); 28 | } 29 | 30 | TabPanel.propTypes = { 31 | children: PropTypes.node, 32 | index: PropTypes.any.isRequired, 33 | value: PropTypes.any.isRequired 34 | }; 35 | 36 | function a11yProps(index) { 37 | return { 38 | id: `simple-tab-${index}`, 39 | 'aria-controls': `simple-tabpanel-${index}` 40 | }; 41 | } 42 | 43 | const GraphsContainer = styled('div')({ 44 | flexGrow: 1, 45 | backgroundColor: '#fff', 46 | width: '1350px' 47 | }); 48 | 49 | const TabsBar = styled(AppBar)({ 50 | backgroundColor: '#4200b7' 51 | }); 52 | 53 | class SensorStreaming extends React.Component { 54 | constructor(props) { 55 | super(props); 56 | this.state = { 57 | value: 0 58 | }; 59 | } 60 | 61 | handleChange = (event, newValue) => { 62 | this.setState({ value: newValue }); 63 | }; 64 | 65 | componentDidMount() { 66 | const { sensorControl } = this.props; 67 | if (!sensorControl.isStreaming) { 68 | sensorControl.startSensorStreaming(1000); 69 | } 70 | setTimeout(() => { 71 | sensorControl.clearSensorStreaming(); 72 | }, 60000); 73 | } 74 | 75 | // Because each of the streaming services is called on the sensor control property of the rvrToy object (which we fed into this SensorStreaming object as simply sensorControl, in the PageBody component), we feed sensorControl into each of the streaming graph components. 76 | 77 | render() { 78 | const { value } = this.state; 79 | const { sensorControl } = this.props; 80 | return ( 81 | 82 | 83 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 101 | 102 | 103 | 106 | 107 | 108 | 111 | 112 | 113 | 116 | 117 | 118 | 119 | 120 | 121 | 124 | 125 | 126 | 127 | 128 | 129 | ); 130 | } 131 | } 132 | 133 | export default SensorStreaming; 134 | -------------------------------------------------------------------------------- /public/dashboard/src/components/sensorStreaming/SpeedChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import c3 from 'c3'; 3 | 4 | class SpeedChart extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { speed: ['Speed (mph)'] }; 8 | } 9 | 10 | renderChart() { 11 | c3.generate({ 12 | bindto: '#speed', 13 | data: { 14 | columns: [this.state.speed], 15 | type: 'scatter', 16 | labels: true 17 | }, 18 | point: { show: true }, 19 | axis: { 20 | y: { 21 | max: Math.max(this.state.speed) + 5, 22 | min: Math.min(this.state.speed) - 5 23 | }, 24 | x: { show: true } 25 | } 26 | }); 27 | } 28 | 29 | async componentDidMount() { 30 | const { sensorControl } = this.props; 31 | // The each sensor we'd like to stream must be enabled and we must let the system know what to do once it is. In this case (for each of the sensors we'll be plotting), we add each new value from the stream to an array, which is subsequently plotted against time. 32 | sensorControl.enableSensor(sensorControl.speed, data => { 33 | this.setState({ 34 | speed: this.state.speed.concat(data.MPS) 35 | }, this.renderChart()); 36 | }); 37 | } 38 | 39 | render() { 40 | return ( 41 |
42 |
43 |
44 | ); 45 | } 46 | } 47 | 48 | export default SpeedChart; 49 | -------------------------------------------------------------------------------- /public/dashboard/src/components/sensorStreaming/VelocityChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import c3 from 'c3'; 3 | 4 | class VelocityChart extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | x: ['x'], 9 | y: ['y'] 10 | }; 11 | } 12 | 13 | renderChart() { 14 | c3.generate({ 15 | bindto: '#velocityChart', 16 | data: { 17 | columns: [this.state.x, this.state.y], 18 | type: 'scatter', 19 | labels: true 20 | }, 21 | point: { show: true }, 22 | axis: { 23 | y: { 24 | max: Math.max(Math.max(this.state.x), Math.max(this.state.y)) + 10, 25 | min: Math.min(Math.min(this.state.x), Math.min(this.state.y)) - 10 26 | }, 27 | x: { show: true } 28 | } 29 | }); 30 | } 31 | 32 | componentDidMount() { 33 | const { sensorControl } = this.props; 34 | // The each sensor we'd like to stream must be enabled and we must let the system know what to do once it is. In this case (for each of the sensors we'll be plotting), we add each new value from the stream to an array, which is subsequently plotted against time. 35 | sensorControl.enableSensor(sensorControl.velocity, data => { 36 | this.setState({ 37 | x: this.state.x.concat(data.X), 38 | y: this.state.y.concat(data.Y) 39 | }, this.renderChart()); 40 | }); 41 | } 42 | 43 | render() { 44 | return ( 45 |
46 |
47 |
48 | ); 49 | } 50 | } 51 | 52 | export default VelocityChart; 53 | -------------------------------------------------------------------------------- /public/dashboard/src/components/systemInformation/BluetoothAdvertisingName.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const BluetoothAdvertisingNameCard = styled(Card)({ 6 | width: '240px', 7 | padding: '10px', 8 | textAlign: 'center', 9 | color: 'dark-gray' 10 | }); 11 | 12 | const Title = styled(Typography)({ 13 | fontSize: '20px' 14 | }); 15 | 16 | class BluetoothAdvertisingName extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { data: '' }; 20 | } 21 | 22 | componentDidMount(){ 23 | // The we chose to include the Nordic Chip in the RVR for its bluetooth capabilities. You can use get getBluetoothAdvertisingName on the Nordic Chip (which has a permanent address of "1") to retrieve the name that can be used to find the RVR via bluetooth. 24 | this.props.rvrToy.getBluetoothAdvertisingName(1).then(data => { 25 | this.setState({ 26 | data: JSON.parse(data) 27 | }); 28 | }); 29 | } 30 | 31 | render() { 32 | return ( 33 | 34 | 35 | Bluetooth Advertising Name 36 | {this.state.data && {this.state.data.name}} 37 | 38 | 39 | ); 40 | } 41 | } 42 | 43 | export default BluetoothAdvertisingName; 44 | -------------------------------------------------------------------------------- /public/dashboard/src/components/systemInformation/BoardRevision.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const BoardRevisionCard = styled(Card)({ 6 | width: '240px', 7 | padding: '10px', 8 | textAlign: 'center', 9 | color: 'dark-gray' 10 | }); 11 | 12 | const Title = styled(Typography)({ 13 | fontSize: '20px' 14 | }); 15 | 16 | class BoardRevision extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { data: '' }; 20 | } 21 | 22 | componentDidMount() { 23 | // Of our two processors, the Nordic chip is the one that will return a board revision and has a permanent address of "1", so our argument for this method is "1" 24 | this.props.rvrToy.getBoardRevision(1).then(data => { 25 | this.setState({ 26 | data: JSON.parse(data) 27 | }); 28 | });} 29 | 30 | render() { 31 | return ( 32 | 33 | 34 | Board Revision 35 | {this.state.data && ( 36 | {this.state.data.revision} 37 | )} 38 | 39 | 40 | ); 41 | } 42 | } 43 | 44 | export default BoardRevision; 45 | -------------------------------------------------------------------------------- /public/dashboard/src/components/systemInformation/BootloaderVersion.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const BootloaderVersionCard = styled(Card)({ 6 | width: '240px', 7 | padding: '10px', 8 | textAlign: 'center', 9 | color: 'dark-gray' 10 | }); 11 | 12 | const Title = styled(Typography)({ 13 | fontSize: '20px' 14 | }); 15 | 16 | class BootloaderVersion extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { data1: '', data2: '' }; 20 | } 21 | 22 | componentDidMount() { 23 | // There are two processors in the RVR to handle different capabilities like bluetooth or driving, each with its own software. We call the getBootloaderVersion method on both processors, which have been given permanent addresses of "1" and "2". 24 | this.props.rvrToy.getBootloaderVersion(1).then(data => { 25 | this.setState({ 26 | data1: JSON.parse(data) 27 | }); 28 | }); 29 | this.props.rvrToy.getBootloaderVersion(2).then(data => { 30 | this.setState({ 31 | data2: JSON.parse(data) 32 | }); 33 | }); 34 | } 35 | 36 | render() { 37 | return ( 38 | 39 | 40 | Bootloader Version 41 | {this.state.data1 && ( 42 | 43 | Nordic: {this.state.data1.major}.{this.state.data1.minor}. 44 | {this.state.data1.revision} 45 | 46 | )} 47 | {this.state.data2 && ( 48 | 49 | ST: {this.state.data2.major}.{this.state.data2.minor}. 50 | {this.state.data2.revision} 51 | 52 | )} 53 | 54 | 55 | ); 56 | } 57 | } 58 | 59 | export default BootloaderVersion; 60 | -------------------------------------------------------------------------------- /public/dashboard/src/components/systemInformation/MACAddress.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const MACAddressCard = styled(Card)({ 6 | width: '240px', 7 | padding: '10px', 8 | textAlign: 'center', 9 | color: 'dark-gray' 10 | }); 11 | 12 | const Title = styled(Typography)({ 13 | fontSize: '20px' 14 | }); 15 | 16 | class MACAddress extends React.Component { 17 | constructor(props) { 18 | super(props); 19 | this.state = { data: '' }; 20 | } 21 | 22 | componentDidMount() { 23 | // Of our two processors, the Nordic chip is the one that will return a true MAC address and has a permanent address of "1" (the ST chip returns its UID as a UID, whereas the Nordic chip returns its UID as a MAC address), so our argument for this method is "1" 24 | this.props.rvrToy.getMacAddress(1).then(data => { 25 | this.setState({ 26 | data: JSON.parse(data) 27 | }); 28 | });} 29 | 30 | render() { 31 | return ( 32 | 33 | 34 | MAC Address 35 | {this.state.data && ( 36 | {this.state.data.macAddress} 37 | )} 38 | 39 | 40 | ); 41 | } 42 | } 43 | 44 | export default MACAddress; 45 | -------------------------------------------------------------------------------- /public/dashboard/src/components/systemInformation/MainAppVersion.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | import { SpheroRvrToy } from 'sdk-v4-convenience-raspberry-pi-client-js'; 5 | 6 | const MainAppVersionCard = styled(Card)({ 7 | width: '240px', 8 | padding: '10px', 9 | textAlign: 'center', 10 | color: 'dark-gray' 11 | }); 12 | 13 | const Title = styled(Typography)({ 14 | fontSize: '20px' 15 | }); 16 | 17 | class MainAppVersion extends React.Component { 18 | constructor(props) { 19 | super(props); 20 | this.state = { data1: '', data2: '' }; 21 | } 22 | 23 | componentDidMount() { 24 | // There are two processors in the RVR to handle different capabilities like bluetooth or driving, each with its own software. We call the getMainApplicationVersion method on both processors, which, behind the scenes, have been given permanent addresses of "1" and "2", hence the arguments of "primaryTarget" and "secondaryTarget". 25 | this.props.rvrToy 26 | .getMainApplicationVersion(SpheroRvrToy.primaryTarget) 27 | .then(data => { 28 | this.setState({ 29 | data1: JSON.parse(data) 30 | }); 31 | }); 32 | this.props.rvrToy 33 | .getMainApplicationVersion(SpheroRvrToy.secondaryTarget) 34 | .then(data => { 35 | this.setState({ 36 | data2: JSON.parse(data) 37 | }); 38 | }); 39 | } 40 | 41 | render() { 42 | return ( 43 | 44 | 45 | Main Application Version 46 | {this.state.data1 && ( 47 | 48 | Nordic: {this.state.data1.major}.{this.state.data1.minor}. 49 | {this.state.data1.revision} 50 | 51 | )} 52 | {this.state.data2 && ( 53 | 54 | ST: {this.state.data2.major}.{this.state.data2.minor}. 55 | {this.state.data2.revision} 56 | 57 | )} 58 | 59 | 60 | ); 61 | } 62 | } 63 | 64 | export default MainAppVersion; 65 | -------------------------------------------------------------------------------- /public/dashboard/src/components/systemInformation/SKU.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography } from '@material-ui/core'; 3 | import { styled } from '@material-ui/styles'; 4 | 5 | const SKUCard = styled(Card)({ 6 | width: '240px', 7 | padding: '10px', 8 | textAlign: 'center', 9 | color: 'dark-gray', 10 | height: '130px' 11 | }); 12 | 13 | const Title = styled(Typography)({ 14 | fontSize: '20px', 15 | padding: '10px' 16 | }); 17 | 18 | class SKU extends React.Component { 19 | constructor(props) { 20 | super(props); 21 | this.state = { data: '' }; 22 | } 23 | 24 | componentDidMount() { 25 | // You can use get getSku on the Nordic Chip (which has a permanent address of "1") to retrieve the SKU of the RVR. 26 | this.props.rvrToy.getSku(1).then(data => { 27 | this.setState({ 28 | data: JSON.parse(data) 29 | }); 30 | });} 31 | 32 | render() { 33 | return ( 34 | 35 | 36 | SKU 37 | {this.state.data && {this.state.data.sku}} 38 | 39 | 40 | ); 41 | } 42 | } 43 | 44 | export default SKU; 45 | -------------------------------------------------------------------------------- /public/dashboard/src/components/systemInformation/SystemInformation.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Paper, Grid, Typography } from '@material-ui/core'; 3 | import MainAppVersion from './MainAppVersion'; 4 | import BootloaderVersion from './BootloaderVersion'; 5 | import BoardRevision from './BoardRevision'; 6 | import { styled } from '@material-ui/styles'; 7 | import MACAddress from './MACAddress'; 8 | import SKU from './SKU'; 9 | import BluetoothAdvertisingName from './BluetoothAdvertisingName'; 10 | 11 | const SystemInformationPaper = styled(Paper)({ 12 | padding: '10px', 13 | margin: '20px', 14 | color: 'dark-gray', 15 | height: '415px' 16 | }); 17 | 18 | const Title = styled(Typography)({ 19 | fontSize: '24px' 20 | }); 21 | 22 | const StyledSubGrid = styled(Grid)({ 23 | margin: '20px 30px' 24 | }); 25 | 26 | // We pass rvrToy into each of our components via props, as the commands to retrieve system information are all called directly on the SpheroRvrToy object (which we created an instance of in App.js, called rvrToy, to use across this application). 27 | 28 | const SystemInformation = props => { 29 | return ( 30 | 31 | 32 | System Information 33 | 34 | 35 | 36 | xs=3 37 | 38 | 39 | xs=3 40 | 41 | 42 | xs=3 43 | 44 | 45 | 46 | 47 | xs=3 48 | 49 | 50 | xs=3 51 | 52 | 53 | 56 | 57 | 58 | 59 | 60 | 61 | ); 62 | }; 63 | 64 | export default SystemInformation; 65 | -------------------------------------------------------------------------------- /public/dashboard/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: 'Roboto'; 4 | -webkit-font-smoothing: antialiased; 5 | -moz-osx-font-smoothing: grayscale; 6 | } 7 | 8 | code { 9 | font-family: 'Roboto'; 10 | } 11 | -------------------------------------------------------------------------------- /public/dashboard/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /public/drive-control-tests/aim-start-stop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Aim Start and Stop 5 | 6 | 7 | 8 |

Drive Control

9 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /public/drive-control-tests/drive-regular.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Drive Regular 5 | 6 | 7 | 8 |

Drive Control

9 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /public/drive-control-tests/roll-start-stop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Roll Start and Stop 5 | 6 | 7 | 8 |

Drive Control

9 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /public/drive-control-tests/turn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Turn 5 | 6 | 7 | 8 |

Drive Control

9 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /public/generic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generic Commands 5 | 19 | 20 | 21 | 22 |

Send Generic Command

23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 | 31 | 32 | 33 |
34 | 35 |
36 | 37 | 38 | 39 |
40 | 41 |
42 | 43 | 44 |
45 | 46 |
47 | 48 | 49 |
50 | 51 | 52 | 53 | 54 | 55 |
56 | 57 | 58 |
59 | 60 |
61 | 62 | 63 |
64 | 65 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /public/graph-mocks/ambient-light.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphero-inc/sphero-sdk-raspberrypi-nodejs/8de9ec73c0b8752b72b671843f77199711ef93cf/public/graph-mocks/ambient-light.html -------------------------------------------------------------------------------- /public/graph-mocks/js-client/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphero-inc/sphero-sdk-raspberrypi-nodejs/8de9ec73c0b8752b72b671843f77199711ef93cf/public/graph-mocks/js-client/index.js -------------------------------------------------------------------------------- /public/infrared-control-tests/broadcast-ir.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Broadcast IR 5 | 6 | 7 | 8 |

Infrared control

9 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /public/infrared-control-tests/listen-for-and-send-ir.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Broadcast IR 5 | 6 | 7 | 8 |

Infrared control

9 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /public/sensor-control-tests/all-slots-not-all-sensors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | All slots not all sensors 5 | 6 | 7 | 8 |

Sensor Streaming

9 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /public/sensor-control-tests/all-twice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | All twice 5 | 6 | 7 | 8 |

Sensor Streaming

9 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /public/sensor-control-tests/all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | All 5 | 6 | 7 | 8 |

Sensor Streaming

9 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /public/sensor-control-tests/none.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | All 5 | 6 | 7 | 8 |

Sensor Streaming

9 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /public/sensor-control-tests/not-all-slots.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Not all slots 5 | 6 | 7 | 8 |

Sensor Streaming

9 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /public/sensor-control-tests/st-only.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ST only 5 | 6 | 7 | 8 |

Sensor Streaming

9 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/api/control-routers/index.ts: -------------------------------------------------------------------------------- 1 | // external imports 2 | import {Application} from 'express'; 3 | 4 | // internal imports 5 | import {createLogger, ILogger} from '../../modules/logger'; 6 | import {IConfiguration} from '../../configuration'; 7 | 8 | // route imports 9 | import {IApiDal} from '../../modules/api-dal-interface'; 10 | import {DriveControlRouter} from './drive-control-router'; 11 | import {InfraredControlRouter} from './infrared-control-router'; 12 | import {LedControlRouter} from './led-control-router'; 13 | import {SensorStreamingControlRouter} from './sensor-streaming-control-router'; 14 | import {RouterBase} from '../router-base'; 15 | 16 | let logger: ILogger = createLogger('api index'); 17 | 18 | 19 | export function initializeRoutes(app: Application, apiDal: IApiDal, configuration: IConfiguration): void { 20 | logger.debug('Initializing API v1.0 Controller routes'); 21 | 22 | initializeRoute(app, new LedControlRouter(apiDal, configuration)); 23 | initializeRoute(app, new InfraredControlRouter(apiDal, configuration)); 24 | initializeRoute(app, new DriveControlRouter(apiDal, configuration)); 25 | initializeRoute(app, new SensorStreamingControlRouter(apiDal, configuration)); 26 | } 27 | 28 | function initializeRoute(app: Application, controlRouter: RouterBase) { 29 | controlRouter.initialize(); 30 | app.use('/api/v1.0/', controlRouter.router); 31 | } 32 | -------------------------------------------------------------------------------- /src/api/control-routers/sensor-streaming-control-router.ts: -------------------------------------------------------------------------------- 1 | // external imports 2 | import {Request, Response} from 'express'; 3 | 4 | // internal imports 5 | import {RouterBase} from '../router-base'; 6 | import {IConfiguration} from '../../configuration'; 7 | import {IApiDal} from '../../modules/api-dal-interface'; 8 | import {SensorControl} from '../../modules/controls/v1.0/sensor-control'; 9 | 10 | 11 | export class SensorStreamingControlRouter extends RouterBase { 12 | private static readonly _routerName: string = 'SensorStreamingControl'; 13 | private readonly _sensorControl: SensorControl; 14 | 15 | constructor(apiDal: IApiDal, configuration: IConfiguration) { 16 | super(SensorStreamingControlRouter._routerName, apiDal, configuration); 17 | this._sensorControl = new SensorControl(this._apiDal); 18 | } 19 | 20 | protected initializeRoutes(): void { 21 | this.router.route('/sensorControl/getAvailableSensorsToStream') 22 | .get((request: Request, response: Response) => this.getAvailableSensorsToStream(request, response)); 23 | 24 | this.router.route('/sensorControl/startSensorStreaming') 25 | .put((request: Request, response: Response) => this.startSensorStreaming(request, response)); 26 | 27 | this.router.route('/sensorControl/stopSensorStreaming') 28 | .put((request: Request, response: Response) => this.stopStreamingSensors(request, response)); 29 | } 30 | 31 | 32 | public getAvailableSensorsToStream(request: Request, response: Response) { 33 | let responsePayload: Array = this._sensorControl.getSupportedStreamingServices(); 34 | response.status(200).json(responsePayload); 35 | } 36 | 37 | public startSensorStreaming(request: Request, response: Response) { 38 | if (!request.body) { 39 | let errorCode: number = 400; 40 | let errorDetail: string = 'Payload is required!'; 41 | response.status(errorCode).json({'error': errorDetail}); 42 | return; 43 | } 44 | 45 | try { 46 | this._sensorControl.startStreaming(request.body.sensors, request.body.interval); 47 | response.sendStatus(200); 48 | } catch (reason) { 49 | let errorCode: number = 400; 50 | let errorDetail: string = `Error in startSensorStreaming: ${reason}`; 51 | 52 | // this.routeError(request.path, request.method, errorCode, errorDetail); 53 | 54 | response.status(errorCode).json({'error': errorDetail}); 55 | } 56 | } 57 | 58 | 59 | public stopStreamingSensors(request: Request, response: Response) { 60 | try { 61 | this._sensorControl.stopStreaming(); 62 | response.sendStatus(200); 63 | 64 | } catch (reason) { 65 | let errorCode: number = 400; 66 | let errorDetail: string = `Error in stopStreamingSensors: ${reason}`; 67 | 68 | // this.routeError(request.path, request.method, errorCode, errorDetail); 69 | 70 | response.status(errorCode).json({'error': errorDetail}); 71 | 72 | } 73 | } 74 | 75 | } 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/api/control-routers/sensor-streaming-swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "paths":{ 3 | "/sensorControl/getAvailableSensorsToStream": { 4 | "get": { 5 | "responses": { 6 | "200": { 7 | "description": "OK", 8 | "schema": { 9 | "required": [ 10 | "sensors" 11 | ], 12 | "type": "array", 13 | "items": { 14 | "type": "string" 15 | } 16 | } 17 | }, 18 | "404": { 19 | "description": "Resource not found" 20 | }, 21 | "400": { 22 | "description": "Bad request" 23 | } 24 | }, 25 | "tags": [ 26 | "SensorControl" 27 | ], 28 | "produces": [ 29 | "application/json" 30 | ], 31 | "summary": "", 32 | "consumes": [ 33 | "application/json" 34 | ], 35 | "description": "" 36 | } 37 | }, 38 | "/sensorControl/startSensorStreaming": { 39 | "put": { 40 | "responses": { 41 | "200": { 42 | "description": "OK" 43 | }, 44 | "404": { 45 | "description": "Resource not found" 46 | }, 47 | "400": { 48 | "description": "Bad request" 49 | } 50 | }, 51 | "tags": [ 52 | "SensorControl" 53 | ], 54 | "produces": [ 55 | "application/json" 56 | ], 57 | "summary": "", 58 | "consumes": [ 59 | "application/json" 60 | ], 61 | "description": "" 62 | }, 63 | "parameters": [ 64 | { 65 | "schema": { 66 | "required": [ 67 | "sensors", 68 | "interval" 69 | ], 70 | "type": "object", 71 | "properties": { 72 | "sensors": { 73 | "type": "array", 74 | "items": { 75 | "type": "string" 76 | } 77 | }, 78 | "interval": { 79 | "type": "integer" 80 | } 81 | } 82 | }, 83 | "required": true, 84 | "name": "body", 85 | "in": "body" 86 | } 87 | ] 88 | }, 89 | "/sensorControl/stopStreaming": { 90 | "put": { 91 | "responses": { 92 | "200": { 93 | "description": "OK" 94 | }, 95 | "404": { 96 | "description": "Resource not found" 97 | }, 98 | "400": { 99 | "description": "Bad request" 100 | } 101 | }, 102 | "tags": [ 103 | "SensorControl" 104 | ], 105 | "produces": [ 106 | "application/json" 107 | ], 108 | "summary": "", 109 | "consumes": [ 110 | "application/json" 111 | ], 112 | "description": "" 113 | } 114 | } 115 | }, 116 | "tags": [ 117 | { 118 | "name": "SensorControl", 119 | "description": "" 120 | } 121 | ] 122 | } 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/api/device-router-base.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {IDeviceRouter} from './router-interfaces'; 3 | import {RouterBase} from './router-base'; 4 | import {IGenericKeyValuePair} from '../models/key-value-pair'; 5 | import {ByteConversionUtilities} from '../utilities/byte-conversion-utilities' 6 | import {IConfiguration} from '../configuration'; 7 | import {IApiDal} from '../modules/api-dal-interface'; 8 | import {ApiParserFactory, IApiParser} from '../modules/api-parser'; 9 | import {isStringNullOrWhitespace} from '../utilities/string-utilities' 10 | 11 | 12 | export abstract class DeviceRouterBase extends RouterBase implements IDeviceRouter { 13 | private readonly _deviceId: number; 14 | 15 | protected readonly _apiParser: IApiParser; 16 | 17 | protected readonly _commandIds: Array; 18 | protected readonly _commandNameById: Array>; 19 | 20 | public get deviceId(): number { 21 | return this._deviceId; 22 | } 23 | 24 | protected constructor(name: string, apiDal: IApiDal, configuration: IConfiguration, deviceId: number) { 25 | super(name, apiDal, configuration); 26 | 27 | this._apiParser = ApiParserFactory.getApiParser(); 28 | 29 | this._deviceId = deviceId; 30 | 31 | this._commandIds = []; 32 | this._commandNameById = []; 33 | } 34 | 35 | protected registerCommand(id: number, name: string): void { 36 | if (isStringNullOrWhitespace(name)) { 37 | throw new Error('Command name is required!'); 38 | } 39 | 40 | if (this._commandIds.indexOf(id) > 0) { 41 | this._logger.warning('Route \'' + name + '\' (' + String(id) + ') is already registered!'); 42 | 43 | return; 44 | } 45 | 46 | this._commandIds.push(id); 47 | this._commandNameById.push({key: id, value: name}); 48 | 49 | this._logger.debug('Route \'' + name + '\' (' + ByteConversionUtilities.convertNumberToHexString(id) + ') registered successfully!'); 50 | } 51 | 52 | protected getCommandName(id: number): string { 53 | for (let i = 0; i < this._commandNameById.length; i++) { 54 | let commandKeyValuePair: IGenericKeyValuePair = this._commandNameById[i]; 55 | 56 | if (commandKeyValuePair.key == id) { 57 | return commandKeyValuePair.value; 58 | } 59 | } 60 | 61 | // this should never happen if the command was registered 62 | throw new Error('Command name was not found!'); 63 | } 64 | 65 | protected logRequest(route: string, method: string, deviceId: number, deviceName: string, commandId: number, commandName: string, sourceId: number, targetId: number, requestJsonPayload: string = '') { 66 | this.routeExecuted('Request', 67 | route, method, 68 | deviceId, deviceName, 69 | commandId, commandName, 70 | sourceId, targetId, 71 | requestJsonPayload); 72 | } 73 | protected logResponse(route: string, method: string, deviceId: number, deviceName: string, commandId: number, commandName: string, sourceId: number, targetId: number, responseJsonPayload: string = '') { 74 | this.routeExecuted('Response', 75 | route, method, 76 | deviceId, deviceName, 77 | commandId, commandName, 78 | sourceId, targetId, 79 | responseJsonPayload); 80 | } 81 | private routeExecuted(messagePrefix: string, route: string, method: string, deviceId: number, deviceName: string, commandId: number, commandName: string, sourceId: number, targetId: number, jsonPayload: string = '') { 82 | this._logger.info(`${messagePrefix} -- Route: ${route} | Method: ${method}`); 83 | this._logger.debug(`${messagePrefix} -- Route: ${route} | Method: ${method} | Device: ${deviceName} (${deviceId}) | Command: ${commandName} (${commandId}) | SourceId: ${sourceId} | TargetId: ${targetId} | Payload: ${jsonPayload}`); 84 | } 85 | protected routeError(route: string, method: string, errorCode: number, errorDetail: string) { 86 | this._logger.error(`Route Error -- Route: ${route} | Method: ${method} | ErrorCode: ${errorCode} | ErrorDetail: ${errorDetail}`); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/api/diagnostics-router.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphero-inc/sphero-sdk-raspberrypi-nodejs/8de9ec73c0b8752b72b671843f77199711ef93cf/src/api/diagnostics-router.ts -------------------------------------------------------------------------------- /src/api/generic-command-router.ts: -------------------------------------------------------------------------------- 1 | // external imports 2 | import {Request, Response} from 'express'; 3 | 4 | // internal imports 5 | import {RouterBase} from './router-base'; 6 | import {IApiCommandMessage, buildApiCommandMessageWithDefaultFlags} from '../models/api-command-message'; 7 | import {IConfiguration} from '../configuration'; 8 | import {IApiDal} from '../modules/api-dal-interface'; 9 | import {ByteConversionUtilities} from '../utilities/byte-conversion-utilities' 10 | import {ApiTargetsAndSources} from '../constants'; 11 | 12 | 13 | export class GenericCommandRouter extends RouterBase { 14 | private static readonly _routerName: string = 'GenericCommand'; 15 | 16 | constructor(apiDal: IApiDal, configuration: IConfiguration) { 17 | super(GenericCommandRouter._routerName, apiDal, configuration); 18 | } 19 | 20 | protected initializeRoutes(): void { 21 | this.router.route('/genericCommand/:targetId') 22 | .put((request: Request, response: Response) => this.getBytesFromGenericCommand(request, response)); 23 | } 24 | 25 | public getBytesFromGenericCommand(request: Request, response: Response) { 26 | if (!request.body) { 27 | let errorCode: number = 400; 28 | 29 | let errorDetail: string = 'Payload is required!'; 30 | response.status(errorCode).json({'error': errorDetail}); 31 | 32 | return; 33 | } 34 | 35 | if (!request.params.targetId) { 36 | let errorCode: number = 400; 37 | let errorDetail: string = 'targetId is required!'; 38 | 39 | response.status(errorCode).json({'error': errorDetail}); 40 | 41 | return; 42 | } 43 | 44 | let targetId: number = ByteConversionUtilities.nibblesToByte([1, parseInt(request.params.targetId)].reverse()); 45 | 46 | let deviceId: number = request.body.deviceId; 47 | let deviceName: string = 'generic'; 48 | let commandId: number = request.body.commandId; 49 | let commandName: string = 'generic'; 50 | 51 | let dataRawBytes: number[] | null = request.body.data; 52 | 53 | // TODO: log this -- the logger helpers are not available in this base class 54 | // this.logRequest(request.path, request.method, 55 | // deviceId, deviceName, 56 | // commandId, commandName, 57 | // sourceId, targetId, 58 | // JSON.stringify(dataRawBytes) 59 | // ); 60 | 61 | let apiCommandMessage: IApiCommandMessage = buildApiCommandMessageWithDefaultFlags( 62 | targetId, ApiTargetsAndSources.serviceSource, 63 | deviceId, deviceName, 64 | commandId, commandName, 65 | dataRawBytes 66 | ); 67 | 68 | apiCommandMessage.generateMessageRawBytes(); 69 | this._apiDal.sendApiCommandMessage(apiCommandMessage).then(apiResponseMessage => { 70 | response.status(200).json(apiResponseMessage.messageRawBytes); 71 | 72 | }).catch(reason => { 73 | let errorCode: number = 400; 74 | let errorDetail: string = `Error in getBytesFromGenericCommand while sending API Command: ${reason}`; 75 | 76 | // TODO: log this -- the logger helpers are not available in this base class 77 | // this.routeError(request.path, request.method, errorCode, errorDetail); 78 | 79 | response.status(errorCode).json({'error': errorDetail}); 80 | }); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | // external imports 2 | import {Application} from 'express'; 3 | 4 | // internal imports 5 | import {createLogger, ILogger} from '../modules/logger'; 6 | import {IConfiguration} from '../configuration'; 7 | 8 | // route imports 9 | import * as v1dot0Router from './v1.0'; 10 | import * as v1dot0ControlRouter from './control-routers' 11 | import {GenericCommandRouter} from './generic-command-router'; 12 | import {IApiDal} from '../modules/api-dal-interface'; 13 | let logger: ILogger = createLogger('api index'); 14 | 15 | 16 | export function initializeRoutes(app: Application, apiDal: IApiDal, configuration: IConfiguration): void { 17 | logger.debug('Initializing API routes'); 18 | 19 | v1dot0Router.initializeRoutes(app, apiDal, configuration); 20 | v1dot0ControlRouter.initializeRoutes(app, apiDal, configuration); 21 | 22 | let genericCommandRouter = new GenericCommandRouter(apiDal, configuration); 23 | genericCommandRouter.initialize(); 24 | app.use('/api/', genericCommandRouter.router); 25 | } 26 | 27 | 28 | export function initializeCommandMappings(app: Application, apiDal: IApiDal, configuration: IConfiguration) { 29 | logger.debug('Initializing command mappings'); 30 | v1dot0Router.registerCommandParserFactory(app, apiDal, configuration); 31 | } 32 | -------------------------------------------------------------------------------- /src/api/router-base.ts: -------------------------------------------------------------------------------- 1 | // external imports 2 | import {Router} from 'express'; 3 | 4 | // internal imports 5 | import {IRouter} from './router-interfaces' 6 | import {createLogger, ILogger} from '../modules/logger'; 7 | import {IConfiguration} from '../configuration'; 8 | import {IApiDal} from '../modules/api-dal-interface'; 9 | 10 | 11 | export abstract class RouterBase implements IRouter { 12 | private readonly _name: string; 13 | private readonly _router: Router; 14 | protected readonly _apiDal: IApiDal; 15 | private readonly _configuration: IConfiguration; 16 | 17 | protected readonly _logger: ILogger; 18 | 19 | public get name(): string { 20 | return this._name; 21 | } 22 | 23 | public get router(): Router { 24 | return this._router; 25 | } 26 | 27 | protected constructor(name: string, apiDal: IApiDal, configuration: IConfiguration) { 28 | this._name = name; 29 | this._router = Router({mergeParams: true}); 30 | this._apiDal = apiDal; 31 | this._configuration = configuration; 32 | 33 | this._logger = createLogger(name); 34 | } 35 | 36 | public initialize(): void { 37 | this._logger.debug('Initializing routes...'); 38 | 39 | this.initializeRoutes(); 40 | } 41 | 42 | protected abstract initializeRoutes(): void; 43 | } 44 | -------------------------------------------------------------------------------- /src/api/router-interfaces.ts: -------------------------------------------------------------------------------- 1 | // external imports 2 | import {Router} from 'express'; 3 | 4 | 5 | export interface IRouter { 6 | readonly name: string; 7 | readonly router: Router; 8 | 9 | initialize(): void; 10 | } 11 | 12 | export interface IDeviceRouter extends IRouter{ 13 | readonly deviceId: number; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x10-api-and-shell/0x00-echo-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseEchoRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'data' | Type: 'uint8_t' | Size: 16 | IS_OPTIONAL 9 | for (let i: number = 0; i < requestBody.data.length && i < 16; i++) { 10 | let data: number = requestBody.data[i]; 11 | let dataBytes: Array = ByteConversionUtilities.int8ToByteArray(data); 12 | dataRawBytes = dataRawBytes.concat(dataBytes); 13 | } 14 | 15 | return dataRawBytes; 16 | } 17 | 18 | export function parseEchoResponse(dataRawBytes: Array): IEchoResponse { 19 | let currentIndex: number = 0; 20 | 21 | // Index: 0 | Name: 'data' | Type: 'uint8_t' | Size: 16 22 | let dataValues: Array = [] 23 | for (let i: number = 0; i < 16; i++) { 24 | if (currentIndex >= dataRawBytes.length) { 25 | break; 26 | } 27 | 28 | let dataBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 29 | let data: number = ByteConversionUtilities.byteArrayToInt8(dataBytes); 30 | currentIndex += dataBytes.length; 31 | dataValues.push(data); 32 | } 33 | 34 | let echoResponse: IEchoResponse = { 35 | data: dataValues 36 | }; 37 | 38 | return echoResponse; 39 | } 40 | 41 | export interface IEchoResponse { 42 | readonly data: Array; 43 | } 44 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x10-api-and-shell/metadata.txt: -------------------------------------------------------------------------------- 1 | // ************************************************************ 2 | // The contents in this directory were automatically generated! 3 | // Device ID (DID): 0x10 4 | // Device Name: api_and_shell 5 | // Device Description: 6 | // Command Count: 1 7 | // Source File: 0x10-api_and_shell.json 8 | // Timestamp: 10/12/2019 @ 01:57:06.918390 (UTC) 9 | // ************************************************************ 10 | 11 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x11-system-info/0x00-get-main-application-version-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetMainApplicationVersionResponse(dataRawBytes: Array): IGetMainApplicationVersionResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'major' | Type: 'uint16_t' | Size: 1 9 | let majorBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 10 | let major: number = ByteConversionUtilities.byteArrayToInt16(majorBytes.reverse()); 11 | currentIndex += majorBytes.length; 12 | 13 | // Index: 1 | Name: 'minor' | Type: 'uint16_t' | Size: 1 14 | let minorBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 15 | let minor: number = ByteConversionUtilities.byteArrayToInt16(minorBytes.reverse()); 16 | currentIndex += minorBytes.length; 17 | 18 | // Index: 2 | Name: 'revision' | Type: 'uint16_t' | Size: 1 19 | let revisionBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 20 | let revision: number = ByteConversionUtilities.byteArrayToInt16(revisionBytes.reverse()); 21 | currentIndex += revisionBytes.length; 22 | 23 | let getMainApplicationVersionResponse: IGetMainApplicationVersionResponse = { 24 | major: major, 25 | minor: minor, 26 | revision: revision 27 | }; 28 | 29 | return getMainApplicationVersionResponse; 30 | } 31 | 32 | export interface IGetMainApplicationVersionResponse { 33 | readonly major: number; 34 | readonly minor: number; 35 | readonly revision: number; 36 | } 37 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x11-system-info/0x01-get-bootloader-version-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetBootloaderVersionResponse(dataRawBytes: Array): IGetBootloaderVersionResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'major' | Type: 'uint16_t' | Size: 1 9 | let majorBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 10 | let major: number = ByteConversionUtilities.byteArrayToInt16(majorBytes.reverse()); 11 | currentIndex += majorBytes.length; 12 | 13 | // Index: 1 | Name: 'minor' | Type: 'uint16_t' | Size: 1 14 | let minorBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 15 | let minor: number = ByteConversionUtilities.byteArrayToInt16(minorBytes.reverse()); 16 | currentIndex += minorBytes.length; 17 | 18 | // Index: 2 | Name: 'revision' | Type: 'uint16_t' | Size: 1 19 | let revisionBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 20 | let revision: number = ByteConversionUtilities.byteArrayToInt16(revisionBytes.reverse()); 21 | currentIndex += revisionBytes.length; 22 | 23 | let getBootloaderVersionResponse: IGetBootloaderVersionResponse = { 24 | major: major, 25 | minor: minor, 26 | revision: revision 27 | }; 28 | 29 | return getBootloaderVersionResponse; 30 | } 31 | 32 | export interface IGetBootloaderVersionResponse { 33 | readonly major: number; 34 | readonly minor: number; 35 | readonly revision: number; 36 | } 37 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x11-system-info/0x03-get-board-revision-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetBoardRevisionResponse(dataRawBytes: Array): IGetBoardRevisionResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'revision' | Type: 'uint8_t' | Size: 1 9 | let revisionBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 10 | let revision: number = ByteConversionUtilities.byteArrayToInt8(revisionBytes); 11 | currentIndex += revisionBytes.length; 12 | 13 | let getBoardRevisionResponse: IGetBoardRevisionResponse = { 14 | revision: revision 15 | }; 16 | 17 | return getBoardRevisionResponse; 18 | } 19 | 20 | export interface IGetBoardRevisionResponse { 21 | readonly revision: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x11-system-info/0x06-get-mac-address-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetMacAddressResponse(dataRawBytes: Array): IGetMacAddressResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'macAddress' | Type: 'std::string' | Size: 1 9 | let macAddressBytes: Array = ByteConversionUtilities.getStringBytes(dataRawBytes, currentIndex); 10 | let macAddress: string = ByteConversionUtilities.byteArrayToString(macAddressBytes); 11 | currentIndex += macAddressBytes.length; 12 | 13 | let getMacAddressResponse: IGetMacAddressResponse = { 14 | macAddress: macAddress 15 | }; 16 | 17 | return getMacAddressResponse; 18 | } 19 | 20 | export interface IGetMacAddressResponse { 21 | readonly macAddress: string; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x11-system-info/0x13-get-stats-id-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetStatsIdResponse(dataRawBytes: Array): IGetStatsIdResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'statsId' | Type: 'uint16_t' | Size: 1 9 | let statsIdBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 10 | let statsId: number = ByteConversionUtilities.byteArrayToInt16(statsIdBytes.reverse()); 11 | currentIndex += statsIdBytes.length; 12 | 13 | let getStatsIdResponse: IGetStatsIdResponse = { 14 | statsId: statsId 15 | }; 16 | 17 | return getStatsIdResponse; 18 | } 19 | 20 | export interface IGetStatsIdResponse { 21 | readonly statsId: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x11-system-info/0x1F-get-processor-name-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetProcessorNameResponse(dataRawBytes: Array): IGetProcessorNameResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'name' | Type: 'std::string' | Size: 1 9 | let nameBytes: Array = ByteConversionUtilities.getStringBytes(dataRawBytes, currentIndex); 10 | let name: string = ByteConversionUtilities.byteArrayToString(nameBytes); 11 | currentIndex += nameBytes.length; 12 | 13 | let getProcessorNameResponse: IGetProcessorNameResponse = { 14 | name: name 15 | }; 16 | 17 | return getProcessorNameResponse; 18 | } 19 | 20 | export interface IGetProcessorNameResponse { 21 | readonly name: string; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x11-system-info/0x38-get-sku-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetSkuResponse(dataRawBytes: Array): IGetSkuResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'sku' | Type: 'std::string' | Size: 1 9 | let skuBytes: Array = ByteConversionUtilities.getStringBytes(dataRawBytes, currentIndex); 10 | let sku: string = ByteConversionUtilities.byteArrayToString(skuBytes); 11 | currentIndex += skuBytes.length; 12 | 13 | let getSkuResponse: IGetSkuResponse = { 14 | sku: sku 15 | }; 16 | 17 | return getSkuResponse; 18 | } 19 | 20 | export interface IGetSkuResponse { 21 | readonly sku: string; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x11-system-info/0x39-get-core-up-time-in-milliseconds-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetCoreUpTimeInMillisecondsResponse(dataRawBytes: Array): IGetCoreUpTimeInMillisecondsResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'upTime' | Type: 'uint64_t' | Size: 1 | Units: milliseconds 9 | let upTimeBytes: Array = ByteConversionUtilities.getInt64Bytes(dataRawBytes, currentIndex); 10 | let upTime: number = ByteConversionUtilities.byteArrayToInt64(upTimeBytes.reverse()); 11 | currentIndex += upTimeBytes.length; 12 | 13 | let getCoreUpTimeInMillisecondsResponse: IGetCoreUpTimeInMillisecondsResponse = { 14 | upTime: upTime 15 | }; 16 | 17 | return getCoreUpTimeInMillisecondsResponse; 18 | } 19 | 20 | export interface IGetCoreUpTimeInMillisecondsResponse { 21 | readonly upTime: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x11-system-info/metadata.txt: -------------------------------------------------------------------------------- 1 | // ************************************************************ 2 | // The contents in this directory were automatically generated! 3 | // Device ID (DID): 0x11 4 | // Device Name: system_info 5 | // Device Description: 6 | // Command Count: 8 7 | // Source File: 0x11-system_info.json 8 | // Timestamp: 10/12/2019 @ 01:57:06.918904 (UTC) 9 | // ************************************************************ 10 | 11 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x13-power/0x10-get-battery-percentage-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetBatteryPercentageResponse(dataRawBytes: Array): IGetBatteryPercentageResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'percentage' | Type: 'uint8_t' | Size: 1 9 | let percentageBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 10 | let percentage: number = ByteConversionUtilities.byteArrayToInt8(percentageBytes); 11 | currentIndex += percentageBytes.length; 12 | 13 | let getBatteryPercentageResponse: IGetBatteryPercentageResponse = { 14 | percentage: percentage 15 | }; 16 | 17 | return getBatteryPercentageResponse; 18 | } 19 | 20 | export interface IGetBatteryPercentageResponse { 21 | readonly percentage: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x13-power/0x17-get-battery-voltage-state-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetBatteryVoltageStateResponse(dataRawBytes: Array): IGetBatteryVoltageStateResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'state' | Type: 'uint8_t' (ENUM) | Size: 1 9 | let stateBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 10 | let state: number = ByteConversionUtilities.byteArrayToInt8(stateBytes); 11 | currentIndex += stateBytes.length; 12 | 13 | let getBatteryVoltageStateResponse: IGetBatteryVoltageStateResponse = { 14 | state: state 15 | }; 16 | 17 | return getBatteryVoltageStateResponse; 18 | } 19 | 20 | export interface IGetBatteryVoltageStateResponse { 21 | readonly state: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x13-power/0x1B-enable-battery-voltage-state-change-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseEnableBatteryVoltageStateChangeNotifyRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'isEnabled' | Type: 'bool' | Size: 1 9 | let isEnabled: boolean = requestBody.isEnabled; 10 | let isEnabledBytes: Array = ByteConversionUtilities.boolToByteArray(isEnabled); 11 | dataRawBytes = dataRawBytes.concat(isEnabledBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x13-power/0x1C-battery-voltage-state-change-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseBatteryVoltageStateChangeNotifyResponse(dataRawBytes: Array): IBatteryVoltageStateChangeNotifyResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'state' | Type: 'uint8_t' (ENUM) | Size: 1 9 | let stateBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 10 | let state: number = ByteConversionUtilities.byteArrayToInt8(stateBytes); 11 | currentIndex += stateBytes.length; 12 | 13 | let batteryVoltageStateChangeNotifyResponse: IBatteryVoltageStateChangeNotifyResponse = { 14 | state: state 15 | }; 16 | 17 | return batteryVoltageStateChangeNotifyResponse; 18 | } 19 | 20 | export interface IBatteryVoltageStateChangeNotifyResponse { 21 | readonly state: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x13-power/0x25-get-battery-voltage-in-volts-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetBatteryVoltageInVoltsRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'readingType' | Type: 'uint8_t' (ENUM) | Size: 1 9 | let readingType: number = requestBody.readingType; 10 | let readingTypeBytes: Array = ByteConversionUtilities.int8ToByteArray(readingType); 11 | dataRawBytes = dataRawBytes.concat(readingTypeBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | 16 | export function parseGetBatteryVoltageInVoltsResponse(dataRawBytes: Array): IGetBatteryVoltageInVoltsResponse { 17 | let currentIndex: number = 0; 18 | 19 | // Index: 0 | Name: 'voltage' | Type: 'float' | Size: 1 | Units: volts 20 | let voltageBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 21 | let voltage: number = ByteConversionUtilities.byteArrayToFloat(voltageBytes.reverse()); 22 | currentIndex += voltageBytes.length; 23 | 24 | let getBatteryVoltageInVoltsResponse: IGetBatteryVoltageInVoltsResponse = { 25 | voltage: voltage 26 | }; 27 | 28 | return getBatteryVoltageInVoltsResponse; 29 | } 30 | 31 | export interface IGetBatteryVoltageInVoltsResponse { 32 | readonly voltage: number; 33 | } 34 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x13-power/0x26-get-battery-voltage-state-thresholds-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetBatteryVoltageStateThresholdsResponse(dataRawBytes: Array): IGetBatteryVoltageStateThresholdsResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'criticalThreshold' | Type: 'float' | Size: 1 9 | let criticalThresholdBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 10 | let criticalThreshold: number = ByteConversionUtilities.byteArrayToFloat(criticalThresholdBytes.reverse()); 11 | currentIndex += criticalThresholdBytes.length; 12 | 13 | // Index: 1 | Name: 'lowThreshold' | Type: 'float' | Size: 1 14 | let lowThresholdBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 15 | let lowThreshold: number = ByteConversionUtilities.byteArrayToFloat(lowThresholdBytes.reverse()); 16 | currentIndex += lowThresholdBytes.length; 17 | 18 | // Index: 2 | Name: 'hysteresis' | Type: 'float' | Size: 1 19 | let hysteresisBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 20 | let hysteresis: number = ByteConversionUtilities.byteArrayToFloat(hysteresisBytes.reverse()); 21 | currentIndex += hysteresisBytes.length; 22 | 23 | let getBatteryVoltageStateThresholdsResponse: IGetBatteryVoltageStateThresholdsResponse = { 24 | criticalThreshold: criticalThreshold, 25 | lowThreshold: lowThreshold, 26 | hysteresis: hysteresis 27 | }; 28 | 29 | return getBatteryVoltageStateThresholdsResponse; 30 | } 31 | 32 | export interface IGetBatteryVoltageStateThresholdsResponse { 33 | readonly criticalThreshold: number; 34 | readonly lowThreshold: number; 35 | readonly hysteresis: number; 36 | } 37 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x13-power/0x27-get-current-sense-amplifier-current-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetCurrentSenseAmplifierCurrentRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'amplifierId' | Type: 'uint8_t' (ENUM) | Size: 1 9 | let amplifierId: number = requestBody.amplifierId; 10 | let amplifierIdBytes: Array = ByteConversionUtilities.int8ToByteArray(amplifierId); 11 | dataRawBytes = dataRawBytes.concat(amplifierIdBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | 16 | export function parseGetCurrentSenseAmplifierCurrentResponse(dataRawBytes: Array): IGetCurrentSenseAmplifierCurrentResponse { 17 | let currentIndex: number = 0; 18 | 19 | // Index: 0 | Name: 'amplifierCurrent' | Type: 'float' | Size: 1 20 | let amplifierCurrentBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 21 | let amplifierCurrent: number = ByteConversionUtilities.byteArrayToFloat(amplifierCurrentBytes.reverse()); 22 | currentIndex += amplifierCurrentBytes.length; 23 | 24 | let getCurrentSenseAmplifierCurrentResponse: IGetCurrentSenseAmplifierCurrentResponse = { 25 | amplifierCurrent: amplifierCurrent 26 | }; 27 | 28 | return getCurrentSenseAmplifierCurrentResponse; 29 | } 30 | 31 | export interface IGetCurrentSenseAmplifierCurrentResponse { 32 | readonly amplifierCurrent: number; 33 | } 34 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x13-power/metadata.txt: -------------------------------------------------------------------------------- 1 | // ************************************************************ 2 | // The contents in this directory were automatically generated! 3 | // Device ID (DID): 0x13 4 | // Device Name: power 5 | // Device Description: 6 | // Command Count: 11 7 | // Source File: 0x13-power.json 8 | // Timestamp: 10/12/2019 @ 01:57:06.918746 (UTC) 9 | // ************************************************************ 10 | 11 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x16-drive/0x01-raw-motors-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseRawMotorsRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'leftMode' | Type: 'uint8_t' (ENUM) | Size: 1 9 | let leftMode: number = requestBody.leftMode; 10 | let leftModeBytes: Array = ByteConversionUtilities.int8ToByteArray(leftMode); 11 | dataRawBytes = dataRawBytes.concat(leftModeBytes); 12 | 13 | // Index: 1 | Name: 'leftSpeed' | Type: 'uint8_t' | Size: 1 14 | let leftSpeed: number = requestBody.leftSpeed; 15 | let leftSpeedBytes: Array = ByteConversionUtilities.int8ToByteArray(leftSpeed); 16 | dataRawBytes = dataRawBytes.concat(leftSpeedBytes); 17 | 18 | // Index: 2 | Name: 'rightMode' | Type: 'uint8_t' (ENUM) | Size: 1 19 | let rightMode: number = requestBody.rightMode; 20 | let rightModeBytes: Array = ByteConversionUtilities.int8ToByteArray(rightMode); 21 | dataRawBytes = dataRawBytes.concat(rightModeBytes); 22 | 23 | // Index: 3 | Name: 'rightSpeed' | Type: 'uint8_t' | Size: 1 24 | let rightSpeed: number = requestBody.rightSpeed; 25 | let rightSpeedBytes: Array = ByteConversionUtilities.int8ToByteArray(rightSpeed); 26 | dataRawBytes = dataRawBytes.concat(rightSpeedBytes); 27 | 28 | return dataRawBytes; 29 | } 30 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x16-drive/0x07-drive-with-heading-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseDriveWithHeadingRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'speed' | Type: 'uint8_t' | Size: 1 9 | let speed: number = requestBody.speed; 10 | let speedBytes: Array = ByteConversionUtilities.int8ToByteArray(speed); 11 | dataRawBytes = dataRawBytes.concat(speedBytes); 12 | 13 | // Index: 1 | Name: 'heading' | Type: 'uint16_t' | Size: 1 14 | let heading: number = requestBody.heading; 15 | let headingBytes: Array = ByteConversionUtilities.int16ToByteArray(heading).reverse(); 16 | dataRawBytes = dataRawBytes.concat(headingBytes); 17 | 18 | // Index: 2 | Name: 'flags' | Type: 'uint8_t' (BITMASK) | Size: 1 19 | let flags: number = requestBody.flags; 20 | let flagsBytes: Array = ByteConversionUtilities.int8ToByteArray(flags); 21 | dataRawBytes = dataRawBytes.concat(flagsBytes); 22 | 23 | return dataRawBytes; 24 | } 25 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x16-drive/0x25-enable-motor-stall-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseEnableMotorStallNotifyRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'isEnabled' | Type: 'bool' | Size: 1 9 | let isEnabled: boolean = requestBody.isEnabled; 10 | let isEnabledBytes: Array = ByteConversionUtilities.boolToByteArray(isEnabled); 11 | dataRawBytes = dataRawBytes.concat(isEnabledBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x16-drive/0x26-motor-stall-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseMotorStallNotifyResponse(dataRawBytes: Array): IMotorStallNotifyResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'motorIndex' | Type: 'uint8_t' (ENUM) | Size: 1 9 | let motorIndexBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 10 | let motorIndex: number = ByteConversionUtilities.byteArrayToInt8(motorIndexBytes); 11 | currentIndex += motorIndexBytes.length; 12 | 13 | // Index: 1 | Name: 'isTriggered' | Type: 'bool' | Size: 1 14 | let isTriggeredBytes: Array = ByteConversionUtilities.getBoolBytes(dataRawBytes, currentIndex); 15 | let isTriggered: boolean = ByteConversionUtilities.byteArrayToBool(isTriggeredBytes); 16 | currentIndex += isTriggeredBytes.length; 17 | 18 | let motorStallNotifyResponse: IMotorStallNotifyResponse = { 19 | motorIndex: motorIndex, 20 | isTriggered: isTriggered 21 | }; 22 | 23 | return motorStallNotifyResponse; 24 | } 25 | 26 | export interface IMotorStallNotifyResponse { 27 | readonly motorIndex: number; 28 | readonly isTriggered: boolean; 29 | } 30 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x16-drive/0x27-enable-motor-fault-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseEnableMotorFaultNotifyRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'isEnabled' | Type: 'bool' | Size: 1 9 | let isEnabled: boolean = requestBody.isEnabled; 10 | let isEnabledBytes: Array = ByteConversionUtilities.boolToByteArray(isEnabled); 11 | dataRawBytes = dataRawBytes.concat(isEnabledBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x16-drive/0x28-motor-fault-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseMotorFaultNotifyResponse(dataRawBytes: Array): IMotorFaultNotifyResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'isFault' | Type: 'bool' | Size: 1 9 | let isFaultBytes: Array = ByteConversionUtilities.getBoolBytes(dataRawBytes, currentIndex); 10 | let isFault: boolean = ByteConversionUtilities.byteArrayToBool(isFaultBytes); 11 | currentIndex += isFaultBytes.length; 12 | 13 | let motorFaultNotifyResponse: IMotorFaultNotifyResponse = { 14 | isFault: isFault 15 | }; 16 | 17 | return motorFaultNotifyResponse; 18 | } 19 | 20 | export interface IMotorFaultNotifyResponse { 21 | readonly isFault: boolean; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x16-drive/0x29-get-motor-fault-state-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetMotorFaultStateResponse(dataRawBytes: Array): IGetMotorFaultStateResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'isFault' | Type: 'bool' | Size: 1 9 | let isFaultBytes: Array = ByteConversionUtilities.getBoolBytes(dataRawBytes, currentIndex); 10 | let isFault: boolean = ByteConversionUtilities.byteArrayToBool(isFaultBytes); 11 | currentIndex += isFaultBytes.length; 12 | 13 | let getMotorFaultStateResponse: IGetMotorFaultStateResponse = { 14 | isFault: isFault 15 | }; 16 | 17 | return getMotorFaultStateResponse; 18 | } 19 | 20 | export interface IGetMotorFaultStateResponse { 21 | readonly isFault: boolean; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x16-drive/metadata.txt: -------------------------------------------------------------------------------- 1 | // ************************************************************ 2 | // The contents in this directory were automatically generated! 3 | // Device ID (DID): 0x16 4 | // Device Name: drive 5 | // Device Description: 6 | // Command Count: 8 7 | // Source File: 0x16-driving.json 8 | // Timestamp: 10/12/2019 @ 01:57:06.918572 (UTC) 9 | // ************************************************************ 10 | 11 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x0F-enable-gyro-max-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseEnableGyroMaxNotifyRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'isEnabled' | Type: 'bool' | Size: 1 9 | let isEnabled: boolean = requestBody.isEnabled; 10 | let isEnabledBytes: Array = ByteConversionUtilities.boolToByteArray(isEnabled); 11 | dataRawBytes = dataRawBytes.concat(isEnabledBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x10-gyro-max-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGyroMaxNotifyResponse(dataRawBytes: Array): IGyroMaxNotifyResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'flags' | Type: 'uint8_t' (BITMASK) | Size: 1 9 | let flagsBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 10 | let flags: number = ByteConversionUtilities.byteArrayToInt8(flagsBytes); 11 | currentIndex += flagsBytes.length; 12 | 13 | let gyroMaxNotifyResponse: IGyroMaxNotifyResponse = { 14 | flags: flags 15 | }; 16 | 17 | return gyroMaxNotifyResponse; 18 | } 19 | 20 | export interface IGyroMaxNotifyResponse { 21 | readonly flags: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x17-set-locator-flags-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseSetLocatorFlagsRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'flags' | Type: 'uint8_t' (BITMASK) | Size: 1 9 | let flags: number = requestBody.flags; 10 | let flagsBytes: Array = ByteConversionUtilities.int8ToByteArray(flags); 11 | dataRawBytes = dataRawBytes.concat(flagsBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x22-get-bot-to-bot-infrared-readings-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetBotToBotInfraredReadingsResponse(dataRawBytes: Array): IGetBotToBotInfraredReadingsResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'sensorData' | Type: 'uint32_t' (BITMASK) | Size: 1 9 | let sensorDataBytes: Array = ByteConversionUtilities.getInt32Bytes(dataRawBytes, currentIndex); 10 | let sensorData: number = ByteConversionUtilities.byteArrayToInt32(sensorDataBytes.reverse()); 11 | currentIndex += sensorDataBytes.length; 12 | 13 | let getBotToBotInfraredReadingsResponse: IGetBotToBotInfraredReadingsResponse = { 14 | sensorData: sensorData 15 | }; 16 | 17 | return getBotToBotInfraredReadingsResponse; 18 | } 19 | 20 | export interface IGetBotToBotInfraredReadingsResponse { 21 | readonly sensorData: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x23-get-rgbc-sensor-values-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetRgbcSensorValuesResponse(dataRawBytes: Array): IGetRgbcSensorValuesResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'redChannelValue' | Type: 'uint16_t' | Size: 1 9 | let redChannelValueBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 10 | let redChannelValue: number = ByteConversionUtilities.byteArrayToInt16(redChannelValueBytes.reverse()); 11 | currentIndex += redChannelValueBytes.length; 12 | 13 | // Index: 1 | Name: 'greenChannelValue' | Type: 'uint16_t' | Size: 1 14 | let greenChannelValueBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 15 | let greenChannelValue: number = ByteConversionUtilities.byteArrayToInt16(greenChannelValueBytes.reverse()); 16 | currentIndex += greenChannelValueBytes.length; 17 | 18 | // Index: 2 | Name: 'blueChannelValue' | Type: 'uint16_t' | Size: 1 19 | let blueChannelValueBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 20 | let blueChannelValue: number = ByteConversionUtilities.byteArrayToInt16(blueChannelValueBytes.reverse()); 21 | currentIndex += blueChannelValueBytes.length; 22 | 23 | // Index: 3 | Name: 'clearChannelValue' | Type: 'uint16_t' | Size: 1 24 | let clearChannelValueBytes: Array = ByteConversionUtilities.getInt16Bytes(dataRawBytes, currentIndex); 25 | let clearChannelValue: number = ByteConversionUtilities.byteArrayToInt16(clearChannelValueBytes.reverse()); 26 | currentIndex += clearChannelValueBytes.length; 27 | 28 | let getRgbcSensorValuesResponse: IGetRgbcSensorValuesResponse = { 29 | redChannelValue: redChannelValue, 30 | greenChannelValue: greenChannelValue, 31 | blueChannelValue: blueChannelValue, 32 | clearChannelValue: clearChannelValue 33 | }; 34 | 35 | return getRgbcSensorValuesResponse; 36 | } 37 | 38 | export interface IGetRgbcSensorValuesResponse { 39 | readonly redChannelValue: number; 40 | readonly greenChannelValue: number; 41 | readonly blueChannelValue: number; 42 | readonly clearChannelValue: number; 43 | } 44 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x27-start-robot-to-robot-infrared-broadcasting-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseStartRobotToRobotInfraredBroadcastingRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'farCode' | Type: 'uint8_t' | Size: 1 9 | let farCode: number = requestBody.farCode; 10 | let farCodeBytes: Array = ByteConversionUtilities.int8ToByteArray(farCode); 11 | dataRawBytes = dataRawBytes.concat(farCodeBytes); 12 | 13 | // Index: 1 | Name: 'nearCode' | Type: 'uint8_t' | Size: 1 14 | let nearCode: number = requestBody.nearCode; 15 | let nearCodeBytes: Array = ByteConversionUtilities.int8ToByteArray(nearCode); 16 | dataRawBytes = dataRawBytes.concat(nearCodeBytes); 17 | 18 | return dataRawBytes; 19 | } 20 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x28-start-robot-to-robot-infrared-following-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseStartRobotToRobotInfraredFollowingRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'farCode' | Type: 'uint8_t' | Size: 1 9 | let farCode: number = requestBody.farCode; 10 | let farCodeBytes: Array = ByteConversionUtilities.int8ToByteArray(farCode); 11 | dataRawBytes = dataRawBytes.concat(farCodeBytes); 12 | 13 | // Index: 1 | Name: 'nearCode' | Type: 'uint8_t' | Size: 1 14 | let nearCode: number = requestBody.nearCode; 15 | let nearCodeBytes: Array = ByteConversionUtilities.int8ToByteArray(nearCode); 16 | dataRawBytes = dataRawBytes.concat(nearCodeBytes); 17 | 18 | return dataRawBytes; 19 | } 20 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x2C-robot-to-robot-infrared-message-received-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseRobotToRobotInfraredMessageReceivedNotifyResponse(dataRawBytes: Array): IRobotToRobotInfraredMessageReceivedNotifyResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'infraredCode' | Type: 'uint8_t' | Size: 1 9 | let infraredCodeBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 10 | let infraredCode: number = ByteConversionUtilities.byteArrayToInt8(infraredCodeBytes); 11 | currentIndex += infraredCodeBytes.length; 12 | 13 | let robotToRobotInfraredMessageReceivedNotifyResponse: IRobotToRobotInfraredMessageReceivedNotifyResponse = { 14 | infraredCode: infraredCode 15 | }; 16 | 17 | return robotToRobotInfraredMessageReceivedNotifyResponse; 18 | } 19 | 20 | export interface IRobotToRobotInfraredMessageReceivedNotifyResponse { 21 | readonly infraredCode: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x30-get-ambient-light-sensor-value-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetAmbientLightSensorValueResponse(dataRawBytes: Array): IGetAmbientLightSensorValueResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'ambientLightValue' | Type: 'float' | Size: 1 9 | let ambientLightValueBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 10 | let ambientLightValue: number = ByteConversionUtilities.byteArrayToFloat(ambientLightValueBytes.reverse()); 11 | currentIndex += ambientLightValueBytes.length; 12 | 13 | let getAmbientLightSensorValueResponse: IGetAmbientLightSensorValueResponse = { 14 | ambientLightValue: ambientLightValue 15 | }; 16 | 17 | return getAmbientLightSensorValueResponse; 18 | } 19 | 20 | export interface IGetAmbientLightSensorValueResponse { 21 | readonly ambientLightValue: number; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x33-start-robot-to-robot-infrared-evading-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseStartRobotToRobotInfraredEvadingRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'farCode' | Type: 'uint8_t' | Size: 1 9 | let farCode: number = requestBody.farCode; 10 | let farCodeBytes: Array = ByteConversionUtilities.int8ToByteArray(farCode); 11 | dataRawBytes = dataRawBytes.concat(farCodeBytes); 12 | 13 | // Index: 1 | Name: 'nearCode' | Type: 'uint8_t' | Size: 1 14 | let nearCode: number = requestBody.nearCode; 15 | let nearCodeBytes: Array = ByteConversionUtilities.int8ToByteArray(nearCode); 16 | dataRawBytes = dataRawBytes.concat(nearCodeBytes); 17 | 18 | return dataRawBytes; 19 | } 20 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x35-enable-color-detection-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseEnableColorDetectionNotifyRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'isEnabled' | Type: 'bool' | Size: 1 9 | let isEnabled: boolean = requestBody.isEnabled; 10 | let isEnabledBytes: Array = ByteConversionUtilities.boolToByteArray(isEnabled); 11 | dataRawBytes = dataRawBytes.concat(isEnabledBytes); 12 | 13 | // Index: 1 | Name: 'interval' | Type: 'uint16_t' | Size: 1 | Units: milliseconds 14 | let interval: number = requestBody.interval; 15 | let intervalBytes: Array = ByteConversionUtilities.int16ToByteArray(interval).reverse(); 16 | dataRawBytes = dataRawBytes.concat(intervalBytes); 17 | 18 | // Index: 2 | Name: 'minimumConfidenceThreshold' | Type: 'uint8_t' | Size: 1 19 | let minimumConfidenceThreshold: number = requestBody.minimumConfidenceThreshold; 20 | let minimumConfidenceThresholdBytes: Array = ByteConversionUtilities.int8ToByteArray(minimumConfidenceThreshold); 21 | dataRawBytes = dataRawBytes.concat(minimumConfidenceThresholdBytes); 22 | 23 | return dataRawBytes; 24 | } 25 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x36-color-detection-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseColorDetectionNotifyResponse(dataRawBytes: Array): IColorDetectionNotifyResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'red' | Type: 'uint8_t' | Size: 1 9 | let redBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 10 | let red: number = ByteConversionUtilities.byteArrayToInt8(redBytes); 11 | currentIndex += redBytes.length; 12 | 13 | // Index: 1 | Name: 'green' | Type: 'uint8_t' | Size: 1 14 | let greenBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 15 | let green: number = ByteConversionUtilities.byteArrayToInt8(greenBytes); 16 | currentIndex += greenBytes.length; 17 | 18 | // Index: 2 | Name: 'blue' | Type: 'uint8_t' | Size: 1 19 | let blueBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 20 | let blue: number = ByteConversionUtilities.byteArrayToInt8(blueBytes); 21 | currentIndex += blueBytes.length; 22 | 23 | // Index: 3 | Name: 'confidence' | Type: 'uint8_t' | Size: 1 24 | let confidenceBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 25 | let confidence: number = ByteConversionUtilities.byteArrayToInt8(confidenceBytes); 26 | currentIndex += confidenceBytes.length; 27 | 28 | // Index: 4 | Name: 'colorClassificationId' | Type: 'uint8_t' | Size: 1 29 | let colorClassificationIdBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 30 | let colorClassificationId: number = ByteConversionUtilities.byteArrayToInt8(colorClassificationIdBytes); 31 | currentIndex += colorClassificationIdBytes.length; 32 | 33 | let colorDetectionNotifyResponse: IColorDetectionNotifyResponse = { 34 | red: red, 35 | green: green, 36 | blue: blue, 37 | confidence: confidence, 38 | colorClassificationId: colorClassificationId 39 | }; 40 | 41 | return colorDetectionNotifyResponse; 42 | } 43 | 44 | export interface IColorDetectionNotifyResponse { 45 | readonly red: number; 46 | readonly green: number; 47 | readonly blue: number; 48 | readonly confidence: number; 49 | readonly colorClassificationId: number; 50 | } 51 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x38-enable-color-detection-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseEnableColorDetectionRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'isEnabled' | Type: 'bool' | Size: 1 9 | let isEnabled: boolean = requestBody.isEnabled; 10 | let isEnabledBytes: Array = ByteConversionUtilities.boolToByteArray(isEnabled); 11 | dataRawBytes = dataRawBytes.concat(isEnabledBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x39-configure-streaming-service-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseConfigureStreamingServiceRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'token' | Type: 'uint8_t' | Size: 1 9 | let token: number = requestBody.token; 10 | let tokenBytes: Array = ByteConversionUtilities.int8ToByteArray(token); 11 | dataRawBytes = dataRawBytes.concat(tokenBytes); 12 | 13 | // Index: 1 | Name: 'configuration' | Type: 'uint8_t' (ENUM) | Size: 15 14 | for (let i: number = 0; i < requestBody.configuration.length && i < 15; i++) { 15 | let configuration: number = requestBody.configuration[i]; 16 | let configurationBytes: Array = ByteConversionUtilities.int8ToByteArray(configuration); 17 | dataRawBytes = dataRawBytes.concat(configurationBytes); 18 | } 19 | 20 | return dataRawBytes; 21 | } 22 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x3A-start-streaming-service-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseStartStreamingServiceRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'period' | Type: 'uint16_t' | Size: 1 9 | let period: number = requestBody.period; 10 | let periodBytes: Array = ByteConversionUtilities.int16ToByteArray(period).reverse(); 11 | dataRawBytes = dataRawBytes.concat(periodBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x3D-streaming-service-data-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseStreamingServiceDataNotifyResponse(dataRawBytes: Array): IStreamingServiceDataNotifyResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'token' | Type: 'uint8_t' | Size: 1 9 | let tokenBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 10 | let token: number = ByteConversionUtilities.byteArrayToInt8(tokenBytes); 11 | currentIndex += tokenBytes.length; 12 | 13 | // Index: 1 | Name: 'sensorData' | Type: 'uint8_t' | Size: 9999 14 | let sensorDataValues: Array = [] 15 | for (let i: number = 0; i < 9999; i++) { 16 | if (currentIndex >= dataRawBytes.length) { 17 | break; 18 | } 19 | 20 | let sensorDataBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 21 | let sensorData: number = ByteConversionUtilities.byteArrayToInt8(sensorDataBytes); 22 | currentIndex += sensorDataBytes.length; 23 | sensorDataValues.push(sensorData); 24 | } 25 | 26 | let streamingServiceDataNotifyResponse: IStreamingServiceDataNotifyResponse = { 27 | token: token, 28 | sensorData: sensorDataValues 29 | }; 30 | 31 | return streamingServiceDataNotifyResponse; 32 | } 33 | 34 | export interface IStreamingServiceDataNotifyResponse { 35 | readonly token: number; 36 | readonly sensorData: Array; 37 | } 38 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x3E-enable-robot-infrared-message-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseEnableRobotInfraredMessageNotifyRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'isEnabled' | Type: 'bool' | Size: 1 9 | let isEnabled: boolean = requestBody.isEnabled; 10 | let isEnabledBytes: Array = ByteConversionUtilities.boolToByteArray(isEnabled); 11 | dataRawBytes = dataRawBytes.concat(isEnabledBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x3F-send-infrared-message-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseSendInfraredMessageRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'infraredCode' | Type: 'uint8_t' | Size: 1 9 | let infraredCode: number = requestBody.infraredCode; 10 | let infraredCodeBytes: Array = ByteConversionUtilities.int8ToByteArray(infraredCode); 11 | dataRawBytes = dataRawBytes.concat(infraredCodeBytes); 12 | 13 | // Index: 1 | Name: 'frontStrength' | Type: 'uint8_t' | Size: 1 14 | let frontStrength: number = requestBody.frontStrength; 15 | let frontStrengthBytes: Array = ByteConversionUtilities.int8ToByteArray(frontStrength); 16 | dataRawBytes = dataRawBytes.concat(frontStrengthBytes); 17 | 18 | // Index: 2 | Name: 'leftStrength' | Type: 'uint8_t' | Size: 1 19 | let leftStrength: number = requestBody.leftStrength; 20 | let leftStrengthBytes: Array = ByteConversionUtilities.int8ToByteArray(leftStrength); 21 | dataRawBytes = dataRawBytes.concat(leftStrengthBytes); 22 | 23 | // Index: 3 | Name: 'rightStrength' | Type: 'uint8_t' | Size: 1 24 | let rightStrength: number = requestBody.rightStrength; 25 | let rightStrengthBytes: Array = ByteConversionUtilities.int8ToByteArray(rightStrength); 26 | dataRawBytes = dataRawBytes.concat(rightStrengthBytes); 27 | 28 | // Index: 4 | Name: 'rearStrength' | Type: 'uint8_t' | Size: 1 29 | let rearStrength: number = requestBody.rearStrength; 30 | let rearStrengthBytes: Array = ByteConversionUtilities.int8ToByteArray(rearStrength); 31 | dataRawBytes = dataRawBytes.concat(rearStrengthBytes); 32 | 33 | return dataRawBytes; 34 | } 35 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x42-get-motor-temperature-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetMotorTemperatureRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'motorIndex' | Type: 'uint8_t' (ENUM) | Size: 1 9 | let motorIndex: number = requestBody.motorIndex; 10 | let motorIndexBytes: Array = ByteConversionUtilities.int8ToByteArray(motorIndex); 11 | dataRawBytes = dataRawBytes.concat(motorIndexBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | 16 | export function parseGetMotorTemperatureResponse(dataRawBytes: Array): IGetMotorTemperatureResponse { 17 | let currentIndex: number = 0; 18 | 19 | // Index: 0 | Name: 'windingCoilTemperature' | Type: 'float' | Size: 1 20 | let windingCoilTemperatureBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 21 | let windingCoilTemperature: number = ByteConversionUtilities.byteArrayToFloat(windingCoilTemperatureBytes.reverse()); 22 | currentIndex += windingCoilTemperatureBytes.length; 23 | 24 | // Index: 1 | Name: 'caseTemperature' | Type: 'float' | Size: 1 25 | let caseTemperatureBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 26 | let caseTemperature: number = ByteConversionUtilities.byteArrayToFloat(caseTemperatureBytes.reverse()); 27 | currentIndex += caseTemperatureBytes.length; 28 | 29 | let getMotorTemperatureResponse: IGetMotorTemperatureResponse = { 30 | windingCoilTemperature: windingCoilTemperature, 31 | caseTemperature: caseTemperature 32 | }; 33 | 34 | return getMotorTemperatureResponse; 35 | } 36 | 37 | export interface IGetMotorTemperatureResponse { 38 | readonly windingCoilTemperature: number; 39 | readonly caseTemperature: number; 40 | } 41 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x4B-get-motor-thermal-protection-status-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetMotorThermalProtectionStatusResponse(dataRawBytes: Array): IGetMotorThermalProtectionStatusResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'leftMotorTemperature' | Type: 'float' | Size: 1 | Units: celsius 9 | let leftMotorTemperatureBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 10 | let leftMotorTemperature: number = ByteConversionUtilities.byteArrayToFloat(leftMotorTemperatureBytes.reverse()); 11 | currentIndex += leftMotorTemperatureBytes.length; 12 | 13 | // Index: 1 | Name: 'leftMotorStatus' | Type: 'uint8_t' (ENUM) | Size: 1 14 | let leftMotorStatusBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 15 | let leftMotorStatus: number = ByteConversionUtilities.byteArrayToInt8(leftMotorStatusBytes); 16 | currentIndex += leftMotorStatusBytes.length; 17 | 18 | // Index: 2 | Name: 'rightMotorTemperature' | Type: 'float' | Size: 1 | Units: celsius 19 | let rightMotorTemperatureBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 20 | let rightMotorTemperature: number = ByteConversionUtilities.byteArrayToFloat(rightMotorTemperatureBytes.reverse()); 21 | currentIndex += rightMotorTemperatureBytes.length; 22 | 23 | // Index: 3 | Name: 'rightMotorStatus' | Type: 'uint8_t' (ENUM) | Size: 1 24 | let rightMotorStatusBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 25 | let rightMotorStatus: number = ByteConversionUtilities.byteArrayToInt8(rightMotorStatusBytes); 26 | currentIndex += rightMotorStatusBytes.length; 27 | 28 | let getMotorThermalProtectionStatusResponse: IGetMotorThermalProtectionStatusResponse = { 29 | leftMotorTemperature: leftMotorTemperature, 30 | leftMotorStatus: leftMotorStatus, 31 | rightMotorTemperature: rightMotorTemperature, 32 | rightMotorStatus: rightMotorStatus 33 | }; 34 | 35 | return getMotorThermalProtectionStatusResponse; 36 | } 37 | 38 | export interface IGetMotorThermalProtectionStatusResponse { 39 | readonly leftMotorTemperature: number; 40 | readonly leftMotorStatus: number; 41 | readonly rightMotorTemperature: number; 42 | readonly rightMotorStatus: number; 43 | } 44 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x4C-enable-motor-thermal-protection-status-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseEnableMotorThermalProtectionStatusNotifyRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'isEnabled' | Type: 'bool' | Size: 1 9 | let isEnabled: boolean = requestBody.isEnabled; 10 | let isEnabledBytes: Array = ByteConversionUtilities.boolToByteArray(isEnabled); 11 | dataRawBytes = dataRawBytes.concat(isEnabledBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/0x4D-motor-thermal-protection-status-notify-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseMotorThermalProtectionStatusNotifyResponse(dataRawBytes: Array): IMotorThermalProtectionStatusNotifyResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'leftMotorTemperature' | Type: 'float' | Size: 1 | Units: celsius 9 | let leftMotorTemperatureBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 10 | let leftMotorTemperature: number = ByteConversionUtilities.byteArrayToFloat(leftMotorTemperatureBytes.reverse()); 11 | currentIndex += leftMotorTemperatureBytes.length; 12 | 13 | // Index: 1 | Name: 'leftMotorStatus' | Type: 'uint8_t' (ENUM) | Size: 1 14 | let leftMotorStatusBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 15 | let leftMotorStatus: number = ByteConversionUtilities.byteArrayToInt8(leftMotorStatusBytes); 16 | currentIndex += leftMotorStatusBytes.length; 17 | 18 | // Index: 2 | Name: 'rightMotorTemperature' | Type: 'float' | Size: 1 | Units: celsius 19 | let rightMotorTemperatureBytes: Array = ByteConversionUtilities.getFloatBytes(dataRawBytes, currentIndex); 20 | let rightMotorTemperature: number = ByteConversionUtilities.byteArrayToFloat(rightMotorTemperatureBytes.reverse()); 21 | currentIndex += rightMotorTemperatureBytes.length; 22 | 23 | // Index: 3 | Name: 'rightMotorStatus' | Type: 'uint8_t' (ENUM) | Size: 1 24 | let rightMotorStatusBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 25 | let rightMotorStatus: number = ByteConversionUtilities.byteArrayToInt8(rightMotorStatusBytes); 26 | currentIndex += rightMotorStatusBytes.length; 27 | 28 | let motorThermalProtectionStatusNotifyResponse: IMotorThermalProtectionStatusNotifyResponse = { 29 | leftMotorTemperature: leftMotorTemperature, 30 | leftMotorStatus: leftMotorStatus, 31 | rightMotorTemperature: rightMotorTemperature, 32 | rightMotorStatus: rightMotorStatus 33 | }; 34 | 35 | return motorThermalProtectionStatusNotifyResponse; 36 | } 37 | 38 | export interface IMotorThermalProtectionStatusNotifyResponse { 39 | readonly leftMotorTemperature: number; 40 | readonly leftMotorStatus: number; 41 | readonly rightMotorTemperature: number; 42 | readonly rightMotorStatus: number; 43 | } 44 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x18-sensor/metadata.txt: -------------------------------------------------------------------------------- 1 | // ************************************************************ 2 | // The contents in this directory were automatically generated! 3 | // Device ID (DID): 0x18 4 | // Device Name: sensor 5 | // Device Description: 6 | // Command Count: 29 7 | // Source File: 0x18-sensors.json 8 | // Timestamp: 10/12/2019 @ 01:57:06.918664 (UTC) 9 | // ************************************************************ 10 | 11 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x19-connection/0x05-get-bluetooth-advertising-name-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetBluetoothAdvertisingNameResponse(dataRawBytes: Array): IGetBluetoothAdvertisingNameResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'name' | Type: 'std::string' | Size: 1 9 | let nameBytes: Array = ByteConversionUtilities.getStringBytes(dataRawBytes, currentIndex); 10 | let name: string = ByteConversionUtilities.byteArrayToString(nameBytes); 11 | currentIndex += nameBytes.length; 12 | 13 | let getBluetoothAdvertisingNameResponse: IGetBluetoothAdvertisingNameResponse = { 14 | name: name 15 | }; 16 | 17 | return getBluetoothAdvertisingNameResponse; 18 | } 19 | 20 | export interface IGetBluetoothAdvertisingNameResponse { 21 | readonly name: string; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x19-connection/metadata.txt: -------------------------------------------------------------------------------- 1 | // ************************************************************ 2 | // The contents in this directory were automatically generated! 3 | // Device ID (DID): 0x19 4 | // Device Name: connection 5 | // Device Description: 6 | // Command Count: 1 7 | // Source File: 0x19-peer_connection.json 8 | // Timestamp: 10/12/2019 @ 01:57:06.918820 (UTC) 9 | // ************************************************************ 10 | 11 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x1A-io/0x1A-set-all-leds-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseSetAllLedsRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'ledGroup' | Type: 'uint32_t' (BITMASK) | Size: 1 9 | let ledGroup: number = requestBody.ledGroup; 10 | let ledGroupBytes: Array = ByteConversionUtilities.int32ToByteArray(ledGroup).reverse(); 11 | dataRawBytes = dataRawBytes.concat(ledGroupBytes); 12 | 13 | // Index: 1 | Name: 'ledBrightnessValues' | Type: 'uint8_t' | Size: 32 14 | for (let i: number = 0; i < requestBody.ledBrightnessValues.length && i < 32; i++) { 15 | let ledBrightnessValues: number = requestBody.ledBrightnessValues[i]; 16 | let ledBrightnessValuesBytes: Array = ByteConversionUtilities.int8ToByteArray(ledBrightnessValues); 17 | dataRawBytes = dataRawBytes.concat(ledBrightnessValuesBytes); 18 | } 19 | 20 | return dataRawBytes; 21 | } 22 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x1A-io/0x44-get-active-color-palette-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetActiveColorPaletteResponse(dataRawBytes: Array): IGetActiveColorPaletteResponse { 6 | let currentIndex: number = 0; 7 | 8 | // Index: 0 | Name: 'rgbIndexBytes' | Type: 'uint8_t' | Size: 48 9 | let rgbIndexBytesValues: Array = [] 10 | for (let i: number = 0; i < 48; i++) { 11 | if (currentIndex >= dataRawBytes.length) { 12 | break; 13 | } 14 | 15 | let rgbIndexBytesBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 16 | let rgbIndexBytes: number = ByteConversionUtilities.byteArrayToInt8(rgbIndexBytesBytes); 17 | currentIndex += rgbIndexBytesBytes.length; 18 | rgbIndexBytesValues.push(rgbIndexBytes); 19 | } 20 | 21 | let getActiveColorPaletteResponse: IGetActiveColorPaletteResponse = { 22 | rgbIndexBytes: rgbIndexBytesValues 23 | }; 24 | 25 | return getActiveColorPaletteResponse; 26 | } 27 | 28 | export interface IGetActiveColorPaletteResponse { 29 | readonly rgbIndexBytes: Array; 30 | } 31 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x1A-io/0x45-set-active-color-palette-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseSetActiveColorPaletteRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'rgbIndexBytes' | Type: 'uint8_t' | Size: 48 9 | for (let i: number = 0; i < requestBody.rgbIndexBytes.length && i < 48; i++) { 10 | let rgbIndexBytes: number = requestBody.rgbIndexBytes[i]; 11 | let rgbIndexBytesBytes: Array = ByteConversionUtilities.int8ToByteArray(rgbIndexBytes); 12 | dataRawBytes = dataRawBytes.concat(rgbIndexBytesBytes); 13 | } 14 | 15 | return dataRawBytes; 16 | } 17 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x1A-io/0x46-get-color-identification-report-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseGetColorIdentificationReportRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'red' | Type: 'uint8_t' | Size: 1 9 | let red: number = requestBody.red; 10 | let redBytes: Array = ByteConversionUtilities.int8ToByteArray(red); 11 | dataRawBytes = dataRawBytes.concat(redBytes); 12 | 13 | // Index: 1 | Name: 'green' | Type: 'uint8_t' | Size: 1 14 | let green: number = requestBody.green; 15 | let greenBytes: Array = ByteConversionUtilities.int8ToByteArray(green); 16 | dataRawBytes = dataRawBytes.concat(greenBytes); 17 | 18 | // Index: 2 | Name: 'blue' | Type: 'uint8_t' | Size: 1 19 | let blue: number = requestBody.blue; 20 | let blueBytes: Array = ByteConversionUtilities.int8ToByteArray(blue); 21 | dataRawBytes = dataRawBytes.concat(blueBytes); 22 | 23 | // Index: 3 | Name: 'confidenceThreshold' | Type: 'uint8_t' | Size: 1 24 | let confidenceThreshold: number = requestBody.confidenceThreshold; 25 | let confidenceThresholdBytes: Array = ByteConversionUtilities.int8ToByteArray(confidenceThreshold); 26 | dataRawBytes = dataRawBytes.concat(confidenceThresholdBytes); 27 | 28 | return dataRawBytes; 29 | } 30 | 31 | export function parseGetColorIdentificationReportResponse(dataRawBytes: Array): IGetColorIdentificationReportResponse { 32 | let currentIndex: number = 0; 33 | 34 | // Index: 0 | Name: 'indexConfidenceByte' | Type: 'uint8_t' | Size: 24 35 | let indexConfidenceByteValues: Array = [] 36 | for (let i: number = 0; i < 24; i++) { 37 | if (currentIndex >= dataRawBytes.length) { 38 | break; 39 | } 40 | 41 | let indexConfidenceByteBytes: Array = ByteConversionUtilities.getInt8Bytes(dataRawBytes, currentIndex); 42 | let indexConfidenceByte: number = ByteConversionUtilities.byteArrayToInt8(indexConfidenceByteBytes); 43 | currentIndex += indexConfidenceByteBytes.length; 44 | indexConfidenceByteValues.push(indexConfidenceByte); 45 | } 46 | 47 | let getColorIdentificationReportResponse: IGetColorIdentificationReportResponse = { 48 | indexConfidenceByte: indexConfidenceByteValues 49 | }; 50 | 51 | return getColorIdentificationReportResponse; 52 | } 53 | 54 | export interface IGetColorIdentificationReportResponse { 55 | readonly indexConfidenceByte: Array; 56 | } 57 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x1A-io/0x47-load-color-palette-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseLoadColorPaletteRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'paletteIndex' | Type: 'uint8_t' (ENUM) | Size: 1 9 | let paletteIndex: number = requestBody.paletteIndex; 10 | let paletteIndexBytes: Array = ByteConversionUtilities.int8ToByteArray(paletteIndex); 11 | dataRawBytes = dataRawBytes.concat(paletteIndexBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x1A-io/0x48-save-color-palette-command-parser.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {ByteConversionUtilities} from '../../../../utilities/byte-conversion-utilities' 3 | 4 | 5 | export function parseSaveColorPaletteRequest(requestBody: any): Array { 6 | let dataRawBytes: Array = []; 7 | 8 | // Index: 0 | Name: 'paletteIndex' | Type: 'uint8_t' (ENUM) | Size: 1 9 | let paletteIndex: number = requestBody.paletteIndex; 10 | let paletteIndexBytes: Array = ByteConversionUtilities.int8ToByteArray(paletteIndex); 11 | dataRawBytes = dataRawBytes.concat(paletteIndexBytes); 12 | 13 | return dataRawBytes; 14 | } 15 | -------------------------------------------------------------------------------- /src/api/v1.0/command-parsers/0x1A-io/metadata.txt: -------------------------------------------------------------------------------- 1 | // ************************************************************ 2 | // The contents in this directory were automatically generated! 3 | // Device ID (DID): 0x1A 4 | // Device Name: io 5 | // Device Description: 6 | // Command Count: 7 7 | // Source File: 0x1A-user_io.json 8 | // Timestamp: 10/12/2019 @ 01:57:06.918489 (UTC) 9 | // ************************************************************ 10 | 11 | -------------------------------------------------------------------------------- /src/api/v1.0/metadata.txt: -------------------------------------------------------------------------------- 1 | // ************************************************************ 2 | // The contents in this directory were automatically generated! 3 | // Timestamp: 10/12/2019 @ 01:57:06.918253 (UTC) 4 | // ************************************************************ 5 | 6 | -------------------------------------------------------------------------------- /src/configuration.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {createLogger, ILogger} from './modules/logger'; 3 | 4 | 5 | let logger: ILogger = createLogger('configuration'); 6 | 7 | 8 | export interface IConfiguration { 9 | readonly applicationTitle: string; 10 | 11 | readonly address: string; 12 | readonly port: number; 13 | readonly fullAddress: string; 14 | 15 | readonly webSocketPath: string; 16 | 17 | initialize(address: string, port: number, webSocketPath: string): void; 18 | } 19 | 20 | class Configuration implements IConfiguration { 21 | private _isInitialized: boolean = false; 22 | 23 | private readonly _applicationTitle: string = ''; 24 | public get applicationTitle(): string { 25 | return this._applicationTitle; 26 | } 27 | 28 | private _address: string = ''; 29 | public get address(): string { 30 | return this._address; 31 | } 32 | 33 | private _port: number = 0; 34 | public get port(): number { 35 | return this._port; 36 | } 37 | 38 | private _webSocketPath: string = ''; 39 | public get webSocketPath(): string { 40 | return this._webSocketPath; 41 | } 42 | 43 | public get fullAddress(): string { 44 | return this._address + ':' + String(this._port); 45 | } 46 | 47 | constructor() { 48 | this._isInitialized = false; 49 | 50 | this._applicationTitle = 'Sphero SDK API'; 51 | 52 | this._address = ''; 53 | this._port = 0; 54 | this._webSocketPath = ''; 55 | } 56 | 57 | public initialize(address: string, port: number, webSocketPath: string): void { 58 | if (this._isInitialized) { 59 | logger.warning('Configuration has already been initialized!'); 60 | return; 61 | } 62 | 63 | logger.debug('Initializing Configuration'); 64 | logger.debug(`Address: ${address}`); 65 | logger.debug(`Port: ${port}`); 66 | logger.debug(`WebSocket Path: ${webSocketPath}`); 67 | 68 | // TODO: validate args 69 | 70 | this._address = address; 71 | this._port = port; 72 | this._webSocketPath = webSocketPath; 73 | 74 | this._isInitialized = true; 75 | } 76 | } 77 | 78 | export let defaultConfiguration: IConfiguration = new Configuration(); 79 | -------------------------------------------------------------------------------- /src/models/api-command-message.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {IApiMessage, ApiBaseMessage} from './api-message'; 3 | import {ApiFlags} from '../constants'; 4 | 5 | 6 | export interface IApiCommandMessage extends IApiMessage { 7 | } 8 | 9 | let _sequenceNumber: number = 0; 10 | function getNextSequenceNumber(): number { 11 | return _sequenceNumber++ % 256; 12 | } 13 | 14 | 15 | class ApiCommandMessage extends ApiBaseMessage implements IApiCommandMessage { 16 | constructor(flags: number, sequenceNumber: number, 17 | targetId: number, sourceId: number, 18 | deviceId: number, deviceName: string, 19 | commandId: number, commandName: string, 20 | dataRawBytes: Array | null = null) { 21 | 22 | super( 23 | flags, sequenceNumber, 24 | targetId, sourceId, 25 | deviceId, deviceName, 26 | commandId, commandName, 27 | dataRawBytes 28 | ); 29 | } 30 | } 31 | 32 | export function buildApiCommandMessage(flags: number, sequenceNumber: number | null, 33 | targetId: number, sourceId: number, 34 | deviceId: number, deviceName: string, 35 | commandId: number, commandName: string, 36 | dataRawBytes: Array | null = null): IApiCommandMessage { 37 | 38 | if (sequenceNumber == null) { 39 | sequenceNumber = 0x00; // TODO: own sequence number here? 40 | } 41 | 42 | let apiMessage: IApiCommandMessage = new ApiCommandMessage( 43 | flags, sequenceNumber, 44 | targetId, sourceId, 45 | deviceId, deviceName, 46 | commandId, commandName, 47 | dataRawBytes 48 | ); 49 | 50 | apiMessage.generateMessageRawBytes(); 51 | 52 | return apiMessage; 53 | } 54 | 55 | export function buildApiCommandMessageWithDefaultFlags(targetId: number, sourceId: number, 56 | deviceId: number, deviceName: string, 57 | commandId: number, commandName: string, 58 | dataRawBytes: Array | null = null): IApiCommandMessage { 59 | 60 | let flags: number = ApiFlags.defaultRequestWithResponseFlags; 61 | let sequenceNumber: number = getNextSequenceNumber(); // TODO: own sequence number here? 62 | 63 | let apiMessage: IApiCommandMessage = new ApiCommandMessage( 64 | flags, sequenceNumber, 65 | targetId, sourceId, 66 | deviceId, deviceName, 67 | commandId, commandName, 68 | dataRawBytes 69 | ); 70 | 71 | apiMessage.generateMessageRawBytes(); 72 | 73 | return apiMessage; 74 | } 75 | 76 | export function buildApiCommandMessageWithNoResponseDefaultFlags(targetId: number, sourceId: number, 77 | deviceId: number, deviceName: string, 78 | commandId: number, commandName: string, 79 | dataRawBytes: Array | null = null): IApiCommandMessage { 80 | 81 | let flags: number = ApiFlags.defaultRequestWithNoResponseFlags; 82 | let sequenceNumber: number = getNextSequenceNumber(); // TODO: own sequence number here? 83 | 84 | let apiMessage: IApiCommandMessage = new ApiCommandMessage( 85 | flags, sequenceNumber, 86 | targetId, sourceId, 87 | deviceId, deviceName, 88 | commandId, commandName, 89 | dataRawBytes 90 | ); 91 | 92 | apiMessage.generateMessageRawBytes(); 93 | 94 | return apiMessage; 95 | } 96 | -------------------------------------------------------------------------------- /src/models/api-message-lite.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export interface IApiMessageLite { 4 | readonly deviceId: number; 5 | readonly deviceName: string; 6 | 7 | readonly commandId: number; 8 | readonly commandName: string; 9 | 10 | readonly sourceId: number; 11 | 12 | readonly data: object | null; 13 | } 14 | 15 | export class ApiMessageLite implements IApiMessageLite { 16 | protected _deviceId: number = 0x00; 17 | public get deviceId(): number { 18 | return this._deviceId; 19 | } 20 | 21 | protected _deviceName: string = ''; 22 | public get deviceName(): string { 23 | return this._deviceName; 24 | } 25 | 26 | protected _commandId: number = 0x00; 27 | public get commandId(): number { 28 | return this._commandId; 29 | } 30 | 31 | protected _commandName: string = ''; 32 | public get commandName(): string { 33 | return this._commandName; 34 | } 35 | 36 | protected _sourceId: number = 0x00; 37 | public get sourceId(): number { 38 | return this._sourceId 39 | } 40 | 41 | protected _data: object | null = null; 42 | public get data(): object | null { 43 | return this._data; 44 | } 45 | 46 | constructor(deviceId: number, deviceName: string, 47 | commandId: number, commandName: string, 48 | sourceId: number, data: object | null ) { 49 | 50 | this._deviceId = deviceId; 51 | this._deviceName = deviceName; 52 | 53 | this._commandId = commandId; 54 | this._commandName = commandName; 55 | 56 | this._sourceId = sourceId; 57 | 58 | this._data = data; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/models/api-response-message.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {IApiMessage, ApiBaseMessage} from './api-message'; 3 | import {ApiErrorCodes, ApiFlags} from '../constants'; 4 | 5 | 6 | export interface IApiResponseMessage extends IApiMessage { 7 | } 8 | 9 | class ApiResponseMessage extends ApiBaseMessage implements IApiResponseMessage { 10 | constructor(flags: number, sequenceNumber: number, 11 | targetId: number, sourceId: number, 12 | deviceId: number, deviceName: string, 13 | commandId: number, commandName: string, 14 | errorCode: number, 15 | dataRawBytes: Array | null = null) { 16 | 17 | super( 18 | flags, sequenceNumber, 19 | targetId, sourceId, 20 | deviceId, deviceName, 21 | commandId, commandName, 22 | dataRawBytes 23 | ); 24 | 25 | this._errorCode = errorCode; 26 | this._hasError = errorCode != ApiErrorCodes.success; 27 | this._errorMessage = ApiErrorCodes.getApiErrorMessageFromCode(errorCode); 28 | } 29 | } 30 | 31 | export function buildApiResponseMessage(flags: number, sequenceNumber: number | null, 32 | targetId: number, sourceId: number, 33 | deviceId: number, deviceName: string, 34 | commandId: number, commandName: string, 35 | errorCode: number, 36 | dataRawBytes: Array | null = null): IApiResponseMessage { 37 | 38 | if (sequenceNumber == null) { 39 | sequenceNumber = 0x00; // TODO: own sequence number here? 40 | } 41 | 42 | let apiMessage: IApiResponseMessage = new ApiResponseMessage( 43 | flags, sequenceNumber, 44 | targetId, sourceId, 45 | deviceId, deviceName, 46 | commandId, commandName, 47 | errorCode, 48 | dataRawBytes 49 | ); 50 | 51 | apiMessage.generateMessageRawBytes(); 52 | 53 | return apiMessage; 54 | } 55 | 56 | export function buildApiResponseMessageWithDefaultFlags(targetId: number, sourceId: number, 57 | deviceId: number, deviceName: string, 58 | commandId: number, commandName: string, 59 | errorCode: number, 60 | dataRawBytes: Array | null = null): IApiResponseMessage { 61 | 62 | let flags: number = ApiFlags.defaultResponseFlags; 63 | let sequenceNumber: number = 0x00; // TODO: own sequence number here? 64 | 65 | let apiMessage: IApiResponseMessage = new ApiResponseMessage( 66 | flags, sequenceNumber, 67 | targetId, sourceId, 68 | deviceId, deviceName, 69 | commandId, commandName, 70 | errorCode, 71 | dataRawBytes 72 | ); 73 | 74 | apiMessage.generateMessageRawBytes(); 75 | 76 | return apiMessage; 77 | } 78 | -------------------------------------------------------------------------------- /src/models/deferred-promise.ts: -------------------------------------------------------------------------------- 1 | // based on the implementation provided from https://romkevandermeulen.nl/2016/09/18/deferred-typescript.html 2 | 3 | 4 | export class DeferredPromise { 5 | private _fate: 'resolved' | 'unresolved'; 6 | private _state: 'pending' | 'fulfilled' | 'rejected'; 7 | 8 | private _resolve: Function; 9 | private _reject: Function; 10 | 11 | private readonly _promise: Promise; 12 | public get promise(): Promise { 13 | return this._promise; 14 | } 15 | 16 | public get isResolved() { 17 | return this._fate === 'resolved'; 18 | } 19 | 20 | public get isPending() { 21 | return this._state === 'pending'; 22 | } 23 | 24 | public get isFulfilled() { 25 | return this._state === 'fulfilled'; 26 | } 27 | 28 | public get isRejected() { 29 | return this._state === 'rejected'; 30 | } 31 | 32 | constructor() { 33 | this._fate = 'unresolved'; 34 | this._state = 'pending'; 35 | 36 | this._promise = new Promise((resolve, reject) => { 37 | this._resolve = resolve; 38 | this._reject = reject; 39 | }); 40 | 41 | this.promise.then( 42 | () => this._state = 'fulfilled', 43 | () => this._state = 'rejected' 44 | ); 45 | } 46 | 47 | public resolve(value?: any) { 48 | if (this.isResolved) { 49 | throw new Error('Promise is already resolved!'); 50 | } 51 | 52 | this._fate = 'resolved'; 53 | this._resolve(value); 54 | } 55 | 56 | public reject(reason?: any) { 57 | if (this.isResolved) { 58 | throw new Error('Promise is already resolved!'); 59 | } 60 | 61 | this._fate = 'resolved'; 62 | this._reject(reason); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/models/key-value-pair.ts: -------------------------------------------------------------------------------- 1 | 2 | // https://www.dustinhorne.com/post/2016/06/09/implementing-a-dictionary-in-typescript 3 | export interface IGenericKeyValuePair { 4 | readonly key: K; 5 | readonly value: V; 6 | } 7 | 8 | export interface IKeyValuePair extends IGenericKeyValuePair { 9 | readonly key: string; 10 | readonly value: any; 11 | } 12 | -------------------------------------------------------------------------------- /src/models/pretty-print.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export interface IPrettyPrint { 4 | prettyPrint(): string; 5 | } -------------------------------------------------------------------------------- /src/models/streaming-service-attribute.ts: -------------------------------------------------------------------------------- 1 | import {ByteConversionUtilities} from '../utilities/byte-conversion-utilities' 2 | 3 | export interface IStreamingServiceAttribute { 4 | readonly name: string; 5 | 6 | readonly minimumValue: number; 7 | readonly maximumValue: number; 8 | 9 | parseAttributeBytesToFloatValues(dataRawBytes: Array, previousMin: number, previousMax: number): number; 10 | } 11 | 12 | 13 | export class StreamingServiceAttribute implements IStreamingServiceAttribute { 14 | protected _name: string = ''; 15 | public get name(): string { 16 | return this._name; 17 | } 18 | 19 | protected _minimumValue: number = 0; 20 | public get minimumValue(): number { 21 | return this._minimumValue; 22 | } 23 | 24 | protected _maximumValue: number = 0; 25 | public get maximumValue(): number { 26 | return this._maximumValue; 27 | } 28 | 29 | constructor(name: string, minimumValue: number, maximumValue: number) { 30 | this._name = name; 31 | 32 | this._minimumValue = minimumValue; 33 | this._maximumValue = maximumValue; 34 | } 35 | 36 | public parseAttributeBytesToFloatValues(dataRawBytes: Array, minimumIn: number, maximumIn: number): number { 37 | let streamingServiceAttributeData: number = ByteConversionUtilities.byteArrayToNumber(dataRawBytes.reverse()); 38 | return ByteConversionUtilities.normalize(streamingServiceAttributeData, minimumIn, maximumIn, this._minimumValue, this._maximumValue); 39 | } 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/models/streaming-service.ts: -------------------------------------------------------------------------------- 1 | import {IStreamingServiceAttribute} from './streaming-service-attribute' 2 | import {ByteConversionUtilities} from '../utilities/byte-conversion-utilities' 3 | 4 | export interface IStreamingService { 5 | readonly id: number; 6 | readonly name: string; 7 | readonly isEnabled: boolean; 8 | 9 | readonly dataSizeEnum: number; 10 | readonly bytesPerStreamingServiceData: number; 11 | 12 | parseStreamingServiceBytesToObject(dataRawBytes: Array): IStreamingServiceData; 13 | enable(): void; 14 | disable(): void; 15 | 16 | } 17 | 18 | 19 | export class StreamingService implements IStreamingService { 20 | protected _id: number = 0; 21 | public get id(): number { 22 | return this._id; 23 | } 24 | 25 | protected _name: string = ''; 26 | public get name(): string { 27 | return this._name; 28 | } 29 | 30 | private readonly _streamingServiceAttributes: Array; 31 | 32 | private _isEnabled: boolean; 33 | public get isEnabled(): boolean { 34 | return this._isEnabled; 35 | } 36 | 37 | protected _dataSizeEnum: number = 0; 38 | public get dataSizeEnum(): number { 39 | return this._dataSizeEnum; 40 | } 41 | 42 | public get bytesPerStreamingServiceData(): number { 43 | return this._bytesPerStreamingServiceAttributeData * this._streamingServiceAttributes.length; 44 | } 45 | 46 | constructor(id: number, name: string, 47 | streamingServiceAttributes: Array, dataSizeEnum: number) { 48 | 49 | this._id = id; 50 | this._name = name; 51 | 52 | this._streamingServiceAttributes = streamingServiceAttributes; 53 | 54 | this._dataSizeEnum = dataSizeEnum; 55 | } 56 | 57 | public enable(): void { 58 | this._isEnabled = true; 59 | } 60 | 61 | public disable(): void { 62 | this._isEnabled = false; 63 | } 64 | 65 | public parseStreamingServiceBytesToObject(dataRawBytes: Array): IStreamingServiceData { 66 | if (dataRawBytes.length != this.bytesPerStreamingServiceData) 67 | throw new Error('input bytes length and expected bytes length mismatch'); 68 | 69 | let streamingServiceData: IStreamingServiceData = {}; 70 | 71 | let currentIndex: number = 0; 72 | for (let streamingServiceAttribute of this._streamingServiceAttributes) { 73 | let streamingServiceAttributeDataBytes: Array = ByteConversionUtilities.sliceBytes(dataRawBytes, currentIndex, this._bytesPerStreamingServiceAttributeData); 74 | streamingServiceData[streamingServiceAttribute.name] = streamingServiceAttribute.parseAttributeBytesToFloatValues(streamingServiceAttributeDataBytes, 0, this._dataSizeMaximum); 75 | currentIndex += streamingServiceAttributeDataBytes.length; 76 | } 77 | return streamingServiceData; 78 | } 79 | 80 | private get _bytesPerStreamingServiceAttributeData(): number { 81 | let bitSize: number | undefined = StreamingService.dataSizeToBits.get(this._dataSizeEnum); 82 | if (bitSize == undefined) 83 | return 0; 84 | return bitSize / 8; 85 | } 86 | 87 | private get _dataSizeMaximum(): number{ 88 | let maxValue: number | undefined = StreamingService.dataSizeToMaximumValue.get(this._dataSizeEnum); 89 | if (maxValue == undefined) 90 | return 0; 91 | return maxValue; 92 | } 93 | 94 | private static readonly dataSizeToBits: Map = new Map([[0x00, 8], [0x01, 16], [0x02, 32]]); 95 | 96 | private static readonly dataSizeToMaximumValue: Map = new Map([[0x00, ByteConversionUtilities.uint8MaxValue], 97 | [0x01, ByteConversionUtilities.uint16MaxValue], [0x02, ByteConversionUtilities.uint32MaxValue]]); 98 | 99 | } 100 | 101 | export interface IStreamingServiceData { 102 | [streamingServiceAttributeName: string]: number; 103 | } 104 | 105 | 106 | -------------------------------------------------------------------------------- /src/models/streaming-slot.ts: -------------------------------------------------------------------------------- 1 | import {IStreamingService, IStreamingServiceData} from './streaming-service'; 2 | import {ByteConversionUtilities} from '../utilities/byte-conversion-utilities' 3 | 4 | export interface IStreamingSlot { 5 | readonly tokenId: number; 6 | readonly hasEnabledStreamingServices: boolean; 7 | 8 | disableStreamingServices(): void; 9 | getConfigurationBytes(): Array; 10 | parseStreamingSlotDataToObject(sensorDataRawBytes: Array): IStreamingSlotData; 11 | } 12 | 13 | 14 | export class StreamingSlot implements IStreamingSlot{ 15 | 16 | protected _tokenId: number = 0; 17 | public get tokenId(): number { 18 | return this._tokenId; 19 | } 20 | 21 | private readonly _supportedStreamingServices: Array = []; 22 | 23 | public get hasEnabledStreamingServices(): boolean { 24 | for(let streamingService of this._supportedStreamingServices) { 25 | if (streamingService.isEnabled) 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | constructor(tokenId: number, supportedStreamingServices: Array) { 32 | this._tokenId = tokenId; 33 | this._supportedStreamingServices = supportedStreamingServices; 34 | } 35 | 36 | public disableStreamingServices(): void { 37 | for(let streamingService of this._supportedStreamingServices) 38 | streamingService.disable(); 39 | } 40 | 41 | public getConfigurationBytes(): Array { 42 | if (!this.hasEnabledStreamingServices){ 43 | throw new Error('no enabled streaming services to configure'); 44 | } 45 | 46 | let dataRawBytes: Array = []; 47 | 48 | let tokenBytes: Array = ByteConversionUtilities.int8ToByteArray(this._tokenId); 49 | dataRawBytes = dataRawBytes.concat(tokenBytes); 50 | 51 | for(let streamingService of this._enabledStreamingServices()){ 52 | dataRawBytes = dataRawBytes.concat(ByteConversionUtilities.int16ToByteArray(streamingService.id).reverse()); 53 | dataRawBytes = dataRawBytes.concat(ByteConversionUtilities.int8ToByteArray(streamingService.dataSizeEnum)); 54 | } 55 | return dataRawBytes; 56 | } 57 | 58 | public parseStreamingSlotDataToObject(sensorDataRawBytes: Array): IStreamingSlotData { 59 | let streamingSlotData: IStreamingSlotData = {}; 60 | 61 | let currentIndex: number = 0; 62 | for(let streamingService of this._enabledStreamingServices()){ 63 | let streamingServiceDataBytes: Array = ByteConversionUtilities.sliceBytes(sensorDataRawBytes, currentIndex, streamingService.bytesPerStreamingServiceData); 64 | streamingSlotData[streamingService.name] = streamingService.parseStreamingServiceBytesToObject(streamingServiceDataBytes); 65 | currentIndex += streamingServiceDataBytes.length; 66 | } 67 | return streamingSlotData; 68 | } 69 | 70 | private _enabledStreamingServices(): Array { 71 | let enabledStreamingServices: Array = []; 72 | for(let streamingService of this._supportedStreamingServices) { 73 | if (streamingService.isEnabled) 74 | enabledStreamingServices.push(streamingService); 75 | } 76 | return enabledStreamingServices; 77 | } 78 | } 79 | 80 | 81 | export interface IStreamingSlotData { 82 | [streamingServiceName: string]: IStreamingServiceData; 83 | } 84 | -------------------------------------------------------------------------------- /src/models/version.ts: -------------------------------------------------------------------------------- 1 | 2 | export class Version { 3 | private readonly _major: number; 4 | public get major(): number { 5 | return this._major; 6 | } 7 | private readonly _minor: number; 8 | public get minor(): number { 9 | return this._minor; 10 | } 11 | private readonly _revision: number; 12 | public get revision(): number { 13 | return this._revision; 14 | } 15 | 16 | private static readonly _version0: Version = new Version(0, 0, 0); 17 | 18 | constructor(major: number, minor: number, revision: number) { 19 | this._major = major; 20 | this._minor = minor; 21 | this._revision = revision; 22 | } 23 | 24 | public isGreaterThan(other: Version) { 25 | if (this._major != other._major) { 26 | if (this._major > other._major) 27 | return true; 28 | else if (this._major < other._major) 29 | return false; 30 | } 31 | 32 | if (this._minor != other._minor) { 33 | if (this._minor > other._minor) 34 | return true; 35 | else if (this._minor < other._minor) 36 | return false; 37 | } 38 | 39 | if (this._revision != other._revision) { 40 | if (this._revision > other._revision) 41 | return true; 42 | else if (this._revision < other._revision) 43 | return false; 44 | } 45 | 46 | return false; 47 | } 48 | 49 | public equals(other: Version) { 50 | return (this._major == other._major && this._minor == other._minor && this._revision == other._revision) ? true : false; 51 | } 52 | 53 | public static versionStringToVersionObject(versionString: string): Version { 54 | let versionComponents: Array = versionString.split('.'); 55 | return new Version(parseInt(versionComponents[0]), parseInt(versionComponents[1]), parseInt(versionComponents[2])); 56 | } 57 | 58 | public static versionObjectToVersionString(versionObject: Version): string { 59 | return `${versionObject.major}.${versionObject.minor}.${versionObject.revision}`; 60 | } 61 | 62 | public static latestVersionOf(versions: Array): Version { 63 | if(versions.length < 1) { 64 | throw new Error('Expected list of at least one element as input.'); 65 | } 66 | let latestVersionNumber: Version = Version._version0; 67 | for(let version of versions) { 68 | if (version.isGreaterThan(latestVersionNumber)) 69 | latestVersionNumber = version; 70 | } 71 | 72 | return latestVersionNumber; 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /src/modules/api-dal-interface.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {IApiCommandMessage} from '../models/api-command-message' 3 | import {IApiResponseMessage} from '../models/api-response-message' 4 | import {IApiMessageLite} from '../models/api-message-lite'; 5 | import {ICommandParserHandler} from './command-parser-factory'; 6 | 7 | 8 | export enum ApiDalTypes { 9 | Uart, 10 | Mock 11 | } 12 | 13 | export interface IApiMessageNotificationObserver { 14 | apiCommandMessageReceivedNotification(apiCommandMessage: IApiCommandMessage): void; // TODO: eventually this needs to return an IApiResponseMessage 15 | } 16 | 17 | export interface IApiDal { 18 | readonly type: ApiDalTypes; 19 | 20 | sendApiCommandMessage(apiCommandMessage: IApiCommandMessage): Promise; 21 | 22 | registerApiMessageNotificationObserver(apiMessageNotificationObserver: IApiMessageNotificationObserver): void; 23 | 24 | sendCommandToClientHandler: (message: IApiMessageLite) => void; 25 | 26 | getCommandParserHandler: (sourceId: number, deviceId: number, commandId: number) => ICommandParserHandler | null; 27 | } 28 | 29 | export abstract class ApiDalBase implements IApiDal { 30 | private readonly _apiMessageNotificationObservers: Array = []; 31 | 32 | public abstract get type(): ApiDalTypes; 33 | 34 | protected constructor() { 35 | // do nothing... 36 | } 37 | 38 | public async sendApiCommandMessage(apiMessage: IApiCommandMessage): Promise { 39 | return await this.sendApiCommandMessageInternal(apiMessage); 40 | } 41 | 42 | protected abstract sendApiCommandMessageInternal(apiCommandMessage: IApiCommandMessage): Promise; 43 | 44 | public registerApiMessageNotificationObserver(apiMessageNotificationObserver: IApiMessageNotificationObserver): void { 45 | this._apiMessageNotificationObservers.push(apiMessageNotificationObserver); 46 | } 47 | 48 | protected invokeReceivedApiCommandMessageCallback(apiCommandMessage: IApiCommandMessage): void { 49 | for (let i = 0; i < this._apiMessageNotificationObservers.length; i++) { 50 | this._apiMessageNotificationObservers[i].apiCommandMessageReceivedNotification(apiCommandMessage); 51 | } 52 | } 53 | 54 | public sendCommandToClientHandler: (message: IApiMessageLite) => void; 55 | 56 | public getCommandParserHandler: (sourceId: number, deviceId: number, commandId: number) => ICommandParserHandler | null; 57 | } 58 | -------------------------------------------------------------------------------- /src/modules/api-dal-mock.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {IApiCommandMessage} from '../models/api-command-message'; 3 | import {buildApiResponseMessage, IApiResponseMessage} from '../models/api-response-message'; 4 | import {ApiDalBase, ApiDalTypes, IApiDal} from './api-dal-interface'; 5 | 6 | 7 | class ApiDalMock extends ApiDalBase { 8 | public get type(): ApiDalTypes { 9 | return ApiDalTypes.Mock; 10 | } 11 | 12 | constructor() { 13 | super(); 14 | } 15 | 16 | protected sendApiCommandMessageInternal(apiCommandMessage: IApiCommandMessage): Promise { 17 | // return buildApiResponseMessage(); 18 | throw new Error(''); 19 | } 20 | } 21 | 22 | let _mockApiDal: ApiDalMock | null = null; 23 | export function buildMockApiDal(): IApiDal { 24 | if (_mockApiDal == null) { 25 | _mockApiDal = new ApiDalMock(); 26 | } 27 | 28 | return _mockApiDal; 29 | } 30 | -------------------------------------------------------------------------------- /src/modules/check-firmware-version.ts: -------------------------------------------------------------------------------- 1 | import {IApiDal} from './api-dal-interface'; 2 | import {buildApiCommandMessageWithDefaultFlags, IApiCommandMessage} from '../models/api-command-message'; 3 | import {ApiTargetsAndSources} from '../constants'; 4 | import * as http from 'http'; 5 | import { 6 | IGetMainApplicationVersionResponse, 7 | parseGetMainApplicationVersionResponse 8 | } from '../api/v1.0/command-parsers/0x11-system-info/0x00-get-main-application-version-command-parser'; 9 | import {IApiResponseMessage} from '../models/api-response-message'; 10 | import {createLogger, ILogger} from './logger'; 11 | import {Version} from '../models/version'; 12 | 13 | let logger: ILogger = createLogger('check firmware version'); 14 | 15 | export class FirmwareVersionChecker { 16 | private readonly _apiDal: IApiDal; 17 | 18 | constructor(apiDal: IApiDal) { 19 | this._apiDal = apiDal; 20 | } 21 | 22 | public checkVersions(): void { 23 | this.checkProcessorVersion(1, 'Nordic', 'http://cms-api-production.platform.sphero.com/api/v1/products/rvr/content_packs/nordic_mainapp_ota/versions/published'); 24 | this.checkProcessorVersion(2, 'ST', 'http://cms-api-production.platform.sphero.com/api/v1/products/rvr/content_packs/st_mainapp_ota/versions/published'); 25 | } 26 | 27 | private checkProcessorVersion(processorId: number, processorName: string, url: string): void { 28 | http.get(url, response => { 29 | response.setEncoding('utf8'); 30 | let body = ''; 31 | response.on('data', data => { 32 | body += data; 33 | }); 34 | response.on('end', () => { 35 | this.sendGetFwVersionCommand(processorId).then(apiResponseMessage => { 36 | let versionResponse: IGetMainApplicationVersionResponse = parseGetMainApplicationVersionResponse(apiResponseMessage.dataRawBytes); 37 | let usedVersion = new Version(versionResponse.major, versionResponse.minor, versionResponse.revision); 38 | 39 | let availableVersions: Array = JSON.parse(body).versions.map((versionString: string) => Version.versionStringToVersionObject(versionString)); 40 | let latestAvailableVersion: Version = Version.latestVersionOf(availableVersions); 41 | 42 | if(!usedVersion.equals(latestAvailableVersion)) { 43 | logger.warning(this.warningMessage(processorName, usedVersion, latestAvailableVersion)); 44 | } 45 | }); 46 | }); 47 | }); 48 | } 49 | 50 | private warningMessage(processorName: string, usedVersion: Version, latestAvailableVersion: Version): string { 51 | return `The firmware version on RVR's ${processorName} processor is ${Version.versionObjectToVersionString(usedVersion)} but the latest available version is ${Version.versionObjectToVersionString(latestAvailableVersion)}. Update recommended.`; 52 | } 53 | 54 | private sendGetFwVersionCommand(targetId: number): Promise { 55 | let deviceId: number = 0x11; 56 | let deviceName: string = 'system info'; 57 | 58 | let commandId: number = 0x00; 59 | let commandName: string = ''; 60 | 61 | let apiCommandMessage: IApiCommandMessage = buildApiCommandMessageWithDefaultFlags( 62 | targetId, ApiTargetsAndSources.serviceSource, 63 | deviceId, deviceName, 64 | commandId, commandName, 65 | null 66 | ); 67 | 68 | apiCommandMessage.generateMessageRawBytes(); 69 | return this._apiDal.sendApiCommandMessage(apiCommandMessage); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/modules/command-parser-factory.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface ICommandParserFactory { 3 | 4 | addParser(processorId: number, deviceId: number, commandId: number, commandParserHandler: ICommandParserHandler): void; 5 | 6 | getParser(processorId: number, deviceId: number, commandId: number): ICommandParserHandler | null 7 | 8 | 9 | } 10 | 11 | export interface ICommandParserHandler { 12 | (dataRawBytes: Array): object; 13 | } 14 | 15 | class CommandParserFactory implements ICommandParserFactory { 16 | private _commandParserMapping : Map; 17 | 18 | constructor() { 19 | this._commandParserMapping = new Map(); 20 | } 21 | 22 | public addParser(sourceId: number, deviceId: number, commandId: number, commandParserHandler: ICommandParserHandler): void { 23 | let key: string = this.getMapKey(sourceId, deviceId, commandId); 24 | 25 | this._commandParserMapping.set(key, commandParserHandler); 26 | } 27 | 28 | public getParser(sourceId: number, deviceId: number, commandId: number): ICommandParserHandler | null { 29 | let key: string = this.getMapKey(sourceId, deviceId, commandId); 30 | 31 | let commandParserHandler: ICommandParserHandler | undefined | null = null; 32 | if (this._commandParserMapping.has(key)) { 33 | commandParserHandler = this._commandParserMapping.get(key); 34 | } 35 | 36 | return !commandParserHandler ? null : commandParserHandler; 37 | } 38 | 39 | private getMapKey(sourceId: number, deviceId: number, commandId: number): string { 40 | return `${sourceId}, ${deviceId}, ${commandId}`; 41 | } 42 | } 43 | 44 | let _commandParserFactory: ICommandParserFactory | null = null; 45 | export function getCommandParserFactory(): ICommandParserFactory { 46 | if (_commandParserFactory == null) { 47 | _commandParserFactory = new CommandParserFactory(); 48 | } 49 | 50 | return _commandParserFactory; 51 | } 52 | -------------------------------------------------------------------------------- /src/modules/logger.ts: -------------------------------------------------------------------------------- 1 | // external imports 2 | import * as winston from 'winston'; 3 | 4 | // internal imports 5 | import {IGenericKeyValuePair} from '../models/key-value-pair'; 6 | import {isStringNullOrWhitespace} from '../utilities/string-utilities'; 7 | 8 | 9 | export interface ILogger { 10 | readonly name: string; 11 | 12 | log(level: string, message: string): void; 13 | debug(message: string): void; 14 | info(message: string): void; 15 | warn(message: string): void; 16 | warning(message: string): void; 17 | error(message: string): void; 18 | exception(error: Error, message: string): void; 19 | } 20 | 21 | 22 | let logger = new (winston.Logger)({ 23 | transports: [ 24 | new (winston.transports.Console)() 25 | ] 26 | }); 27 | 28 | // when debugging, it's helpful to add this line in the transports 29 | // above to see the logging since you won't see it when deployed 30 | //new (winston.transports.File)({ filename: 'SDK-API.log'}) 31 | 32 | 33 | class WinstonLogger implements ILogger { 34 | private readonly _name: string; 35 | private readonly _separator: string = ' >> '; 36 | 37 | public get name(): string { 38 | return this._name; 39 | } 40 | 41 | constructor(name: string) { 42 | this._name = name; 43 | 44 | logger.level = 'debug'; 45 | 46 | //if (process.env == 'development') { 47 | // logger.level = 'debug'; 48 | //} else { 49 | // logger.level = 'warn'; 50 | //} 51 | } 52 | 53 | log(level: string, message: string): void { 54 | if (!level || !message) 55 | return; 56 | 57 | level = level.toLowerCase(); 58 | 59 | switch (level) { 60 | case 'debug': 61 | this.debug(message); 62 | break; 63 | case 'info': 64 | this.info(message); 65 | break; 66 | case 'warn': 67 | case 'warning': 68 | this.warn(message); 69 | break; 70 | case 'error': 71 | case 'exception': 72 | this.error(message); 73 | break; 74 | } 75 | } 76 | 77 | debug(message: string): void { 78 | logger.debug(this.name + this._separator + message); 79 | } 80 | 81 | info(message: string): void { 82 | logger.info(this.name + this._separator + message); 83 | } 84 | 85 | warn(message: string): void { 86 | logger.warn(this.name + this._separator + message); 87 | } 88 | 89 | warning(message: string): void { 90 | this.warn(this.name + this._separator + message); 91 | } 92 | 93 | error(message: string): void { 94 | logger.error(this.name + this._separator + message); 95 | } 96 | 97 | exception(error: Error, message: string): void { 98 | if (!error) { 99 | this.error(this.name + this._separator + message); 100 | } else { 101 | this.error(this.name + this._separator + message + this._separator + error.stack); 102 | } 103 | } 104 | } 105 | 106 | let _loggers: Array> = []; 107 | 108 | let defaultLogger: ILogger = new WinstonLogger('default'); 109 | _loggers.push({key: defaultLogger.name, value: defaultLogger}); 110 | 111 | export function createLogger(name: string): ILogger { 112 | if (isStringNullOrWhitespace(name)) { 113 | name = 'default'; 114 | } 115 | 116 | name = name.toUpperCase().trim(); 117 | 118 | let logger: ILogger | null = getLoggerByName(name); 119 | if (!logger) { 120 | logger = new WinstonLogger(name); 121 | } 122 | 123 | return logger; 124 | } 125 | 126 | function getLoggerByName(name: string): ILogger | null { 127 | for (let i = 0; i < _loggers.length; i++) { 128 | let loggerKeyValuePair: IGenericKeyValuePair = _loggers[i]; 129 | 130 | if (loggerKeyValuePair.key == name) { 131 | return loggerKeyValuePair.value; 132 | } 133 | } 134 | 135 | return null; 136 | } 137 | -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | // external imports 2 | import * as http from 'http'; 3 | import * as ip from 'ip'; 4 | 5 | // internal imports 6 | import {createLogger, ILogger} from './modules/logger'; 7 | import {App} from './modules/app'; 8 | import configuration = require('./configuration'); 9 | import {IApiDal} from './modules/api-dal-interface'; 10 | import {buildUartApiDal} from './modules/api-dal-uart'; 11 | 12 | 13 | let logger: ILogger = createLogger('server'); 14 | 15 | // TODO: need to inject dal type into here... 16 | let apiDal: IApiDal = buildUartApiDal(process.env.SERIAL_PORT_PATH || '/dev/ttyS0', 115200); 17 | 18 | 19 | logger.debug('Server is starting'); 20 | configuration.defaultConfiguration.initialize(ip.address(), 2010, '/stream'); 21 | logger.debug('Initializing with configuration: ' + JSON.stringify(configuration.defaultConfiguration)); 22 | 23 | logger.debug('Creating server and app'); 24 | const app = new App(); 25 | const server = http.createServer(app.expressApp); 26 | 27 | logger.debug('Initializing app module'); 28 | app.initialize(server, apiDal, configuration.defaultConfiguration); 29 | 30 | server.listen(configuration.defaultConfiguration.port, function () { 31 | logger.info('Server is listening on: ' 32 | + configuration.defaultConfiguration.address 33 | + ':' 34 | + configuration.defaultConfiguration.port 35 | + '(' + server.address().family + ')'); 36 | }); 37 | -------------------------------------------------------------------------------- /src/utilities/date-time-utilities.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import {padWithZeros} from './string-utilities' 3 | 4 | 5 | export function getDateAsString(dt: Date): string { 6 | if (!dt) { 7 | throw new Error('Date is required!'); 8 | } 9 | 10 | let dtText: string = padWithZeros(dt.getMonth() + 1, 2) + '/' 11 | + padWithZeros(dt.getDate(), 2) + '/' 12 | + dt.getFullYear() + ' @ ' 13 | + padWithZeros(dt.getHours() + 1, 2) + ':' 14 | + padWithZeros(dt.getMinutes(), 2) + ':' 15 | + padWithZeros(dt.getSeconds(), 2) + '.' 16 | + padWithZeros(dt.getMilliseconds(), 3); 17 | 18 | return dtText; 19 | } 20 | -------------------------------------------------------------------------------- /src/utilities/math-utilities.ts: -------------------------------------------------------------------------------- 1 | export class MathUtilities { 2 | public static mod(n: number, m: number): number { 3 | return ((n % m) + m) % m; 4 | } 5 | } -------------------------------------------------------------------------------- /src/utilities/merge-swagger-specs.ts: -------------------------------------------------------------------------------- 1 | // internal imports 2 | import mainDocumentSwaggerJSON = require('../api/v1.0/swagger.json'); 3 | import ledControlSwaggerJSON = require('../api/control-routers/led-control-swagger.json'); 4 | import driveControlSwaggerJSON = require('../api/control-routers/drive-control-swagger.json'); 5 | import infraredControlSwaggerJSON = require('../api/control-routers/infrared-control-swagger.json'); 6 | import sensorStreamingControlSwaggerJSON = require('../api/control-routers/sensor-streaming-swagger.json'); 7 | 8 | 9 | export function buildSwaggerDoc(): object { 10 | let mainDocumentSwagger: SwaggerObject = mainDocumentSwaggerJSON; 11 | let ledControlSwagger: SwaggerObject = ledControlSwaggerJSON; 12 | let driveControlSwagger: SwaggerObject = driveControlSwaggerJSON; 13 | let infraredControlSwagger: SwaggerObject = infraredControlSwaggerJSON; 14 | let sensorStreamingControlSwagger: SwaggerObject = sensorStreamingControlSwaggerJSON; 15 | 16 | let swaggerObjects: Array = [ledControlSwagger, driveControlSwagger, 17 | infraredControlSwagger, sensorStreamingControlSwagger]; 18 | 19 | for(let swaggerObject of swaggerObjects) { 20 | 21 | for(let path in swaggerObject.paths){ 22 | mainDocumentSwagger.paths[path] = swaggerObject.paths[path]; 23 | } 24 | 25 | for(let tag of swaggerObject.tags){ 26 | mainDocumentSwagger.tags.push(tag); 27 | } 28 | } 29 | return mainDocumentSwagger; 30 | } 31 | 32 | interface SwaggerObject { 33 | 'paths': SwaggerPaths, 34 | 'tags': Array 35 | [key: string]: any; // the swaggerMainDocument may potentially have more fields that we don't care about 36 | } 37 | 38 | interface SwaggerPaths { 39 | [path: string]: object; 40 | } 41 | -------------------------------------------------------------------------------- /src/utilities/string-utilities.ts: -------------------------------------------------------------------------------- 1 | export function padWithZeros(value: any, padCount: number, padValue?: string): string { 2 | return Array(padCount - String(value).length + 1).join(padValue || '0') + value; 3 | } 4 | 5 | export function isStringNullOrEmpty(value: string | null): boolean { 6 | if (!value) { 7 | return true; 8 | } 9 | 10 | if (value.length == 0) { 11 | return true; 12 | } 13 | 14 | return false; 15 | } 16 | 17 | export function isStringNullOrWhitespace(value: string | null): boolean { 18 | if (!value) { 19 | return true; 20 | } 21 | 22 | if (value.trim().length == 0) { 23 | return true; 24 | } 25 | 26 | return false; 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": [ "es2015", "es2016" ], 5 | "target": "es5", 6 | "sourceMap": true, 7 | "noImplicitAny": true, 8 | "strictNullChecks": true, 9 | "removeComments": false, 10 | "moduleResolution": "node", 11 | "declaration": true, 12 | "outDir": "./dist", 13 | "resolveJsonModule": true 14 | }, 15 | "exclude": [ 16 | "node_modules", 17 | "dist" 18 | ] 19 | } 20 | --------------------------------------------------------------------------------