├── src
├── api
│ └── .gitkeep
├── utils
│ ├── .gitkeep
│ ├── PropTypes.js
│ ├── fonts.js
│ ├── icons.js
│ └── units.js
├── App.css
├── config
│ ├── parameters
│ │ ├── Text.css
│ │ ├── VisibleOn.css
│ │ ├── Text.js
│ │ ├── ParameterList.js
│ │ ├── Enabled.js
│ │ ├── Scale.js
│ │ ├── SpeedScaleType.js
│ │ ├── index.js
│ │ ├── Radius.js
│ │ ├── VideoMode.js
│ │ ├── Units.js
│ │ ├── AltitudeScaleType.js
│ │ ├── ScaleAlignment.js
│ │ ├── FontSize.js
│ │ ├── Select.js
│ │ ├── HorizontalAlignment.js
│ │ ├── VerticalAlignment.js
│ │ ├── Position.js
│ │ └── VisibleOn.js
│ ├── settings
│ │ ├── Watt.js
│ │ ├── GpsHdop.js
│ │ ├── ArmState.js
│ │ ├── Gps2Hdop.js
│ │ ├── SpeedAir.js
│ │ ├── TotalTrip.js
│ │ ├── Efficiency.js
│ │ ├── FlightMode.js
│ │ ├── Gps2Status.js
│ │ ├── GpsStatus.js
│ │ ├── GpsLatitude.js
│ │ ├── SpeedGround.js
│ │ ├── WPDistance.js
│ │ ├── Gps2Latitude.js
│ │ ├── GpsLongitude.js
│ │ ├── HomeDistance.js
│ │ ├── HomeLatitude.js
│ │ ├── BatteryCurrent.js
│ │ ├── BatteryVoltage.js
│ │ ├── Gps2Longitude.js
│ │ ├── HomeLongitude.js
│ │ ├── BatteryConsumed.js
│ │ ├── AbsoluteAltitude.js
│ │ ├── BatteryRemaining.js
│ │ ├── RelativeAltitude.js
│ │ ├── Startup.js
│ │ ├── VarioGraph.js
│ │ ├── Wind.js
│ │ ├── RCChannels.js
│ │ ├── HomeDirection.js
│ │ ├── Compass.js
│ │ ├── HomeDirectionDebugInfo.js
│ │ ├── Time.js
│ │ ├── ClimbRate.js
│ │ ├── Video.js
│ │ ├── AltitudeScale.js
│ │ ├── SpeedScale.js
│ │ ├── Attitude3d.js
│ │ ├── Map.js
│ │ ├── ArtificialHorizon.js
│ │ ├── Serial.js
│ │ ├── Radar.js
│ │ ├── SimpleSettings.js
│ │ ├── Throttle.js
│ │ ├── index.js
│ │ ├── LinkQuality.js
│ │ ├── Rssi.js
│ │ └── Switching.js
│ ├── Config.js
│ └── reducer.js
├── app.png
├── pixler
│ ├── Preview.css
│ ├── Editor.css
│ ├── Pixel.css
│ ├── Preview.js
│ ├── Pixel.js
│ ├── Pixler.js
│ ├── actions.js
│ └── Editor.js
├── store
│ ├── configureStore.js
│ ├── configureStore.production.js
│ └── configureStore.development.js
├── data
│ └── icons
│ │ ├── lookup.js
│ │ ├── small.js
│ │ └── medium.js
├── components
│ ├── Label.js
│ ├── Column.js
│ └── Input.js
├── preview
│ ├── Grid.css
│ ├── ArmState.js
│ ├── GpsLatitude.js
│ ├── GpsLongitude.js
│ ├── BatteryCurrent.js
│ ├── BatteryVoltage.js
│ ├── BatteryConsumed.js
│ ├── BatteryRemaining.js
│ ├── HomeLatitude.js
│ ├── HomeLongitude.js
│ ├── actions.js
│ ├── GpsHdop.js
│ ├── AbsoluteAltitude.js
│ ├── RelativeAltitude.js
│ ├── Watt.js
│ ├── TotalTrip.js
│ ├── WpDistance.js
│ ├── HomeDistance.js
│ ├── SpeedAir.js
│ ├── SpeedGround.js
│ ├── reducer.js
│ ├── Alarms.js
│ ├── Grid.js
│ ├── Efficiency.js
│ ├── LinkQuality.js
│ ├── Rssi.js
│ ├── GpsStatus.js
│ ├── Compass.js
│ ├── PreviewBase.js
│ ├── Time.js
│ ├── VarioGraph.js
│ ├── ArtificialHorizon.js
│ ├── FlightMode.js
│ ├── HomeDirectionDebugInfo.js
│ ├── Wind.js
│ ├── StringPreview.js
│ ├── index.js
│ ├── HomeDirection.js
│ ├── SpeedScale.js
│ ├── AltitudeScale.js
│ ├── ClimbRate.js
│ ├── Throttle.js
│ ├── Radar.js
│ └── RCChannels.js
├── routes.js
├── reducers.js
├── DevTools.js
├── index.js
├── App.js
├── eventPage.js
└── app.global.css
├── .node-version
├── .eslintignore
├── .gitattributes
├── preview.png
├── app
├── icon_128.png
├── main.html
└── manifest.json
├── static
└── background.png
├── .csslintrc
├── test
├── .eslintrc
├── preview
│ └── reducer.spec.js
├── setup.js
├── util
│ └── fonts.spec.js
└── pixler
│ └── reducer.spec.js
├── .gitignore
├── etc
└── dummy.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── sergiovilar.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ ├── xcuserdata
│ └── sergiovilar.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── xcshareddata
│ └── xcschemes
│ ├── scheme.xcscheme
│ ├── scheme copy.xcscheme
│ └── blablabla.xcscheme
├── webpack.config.node.js
├── .editorconfig
├── .babelrc
├── .eslintrc
├── .travis.yml
├── .codeclimate.yml
├── server.js
├── wallaby.js
├── .changelogrc
├── LICENSE
├── README.md
└── webpack.config.js
/src/api/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/utils/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 5.9.0
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*{.,-}min.js
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .mainContent {
2 | height: 100%;
3 | }
4 |
--------------------------------------------------------------------------------
/src/config/parameters/Text.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 2rem 1rem;
3 | }
4 |
--------------------------------------------------------------------------------
/src/config/parameters/VisibleOn.css:
--------------------------------------------------------------------------------
1 | .base {
2 | padding: 2rem 1rem;
3 | }
4 |
--------------------------------------------------------------------------------
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasBales/PlayuavOSDConfigurator/HEAD/preview.png
--------------------------------------------------------------------------------
/src/app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasBales/PlayuavOSDConfigurator/HEAD/src/app.png
--------------------------------------------------------------------------------
/app/icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasBales/PlayuavOSDConfigurator/HEAD/app/icon_128.png
--------------------------------------------------------------------------------
/static/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasBales/PlayuavOSDConfigurator/HEAD/static/background.png
--------------------------------------------------------------------------------
/.csslintrc:
--------------------------------------------------------------------------------
1 | --exclude-exts=.min.css
2 | --ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes
3 |
--------------------------------------------------------------------------------
/src/pixler/Preview.css:
--------------------------------------------------------------------------------
1 | .canvas {
2 | background-color: #CCCCCC;
3 | position: absolute;
4 | left: 300px;
5 | top: 250px;
6 | }
7 |
--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "rules": {
6 | "no-unused-expressions": 0,
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/config/settings/Watt.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class Watt extends SimpleSettings {
4 | label = 'watt';
5 | name = 'watt';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/GpsHdop.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class GpsHdop extends SimpleSettings {
4 | label = 'gps hdop';
5 | name = 'gpsHdop';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/ArmState.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class ArmState extends SimpleSettings {
4 | label = 'arm state';
5 | name = 'armState';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/Gps2Hdop.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class Gps2Hdop extends SimpleSettings {
4 | label = 'gps 2 hdop';
5 | name = 'gps2Hdop';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/SpeedAir.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class SpeedAir extends SimpleSettings {
4 | label = 'speed air';
5 | name = 'speedAir';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/TotalTrip.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class TotalTrip extends SimpleSettings {
4 | label = 'total trip';
5 | name = 'totalTrip';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/Efficiency.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class Efficiency extends SimpleSettings {
4 | label = 'efficiency';
5 | name = 'efficiency';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/FlightMode.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class FlightMode extends SimpleSettings {
4 | label = 'flight mode';
5 | name = 'flightMode';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/Gps2Status.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class Gps2Status extends SimpleSettings {
4 | label = 'gps 2 status';
5 | name = 'gps2Status';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/GpsStatus.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class GpsStatus extends SimpleSettings {
4 | labels = 'gps status';
5 | name = 'gpsStatus';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/GpsLatitude.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class GPSLatitude extends SimpleSettings {
4 | label = 'gps latitude';
5 | name = 'gpsLatitude';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/SpeedGround.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class SpeedGround extends SimpleSettings {
4 | label = 'speed ground';
5 | name = 'speedGround';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/WPDistance.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class WPDistance extends SimpleSettings {
4 | label = 'way-point distance';
5 | name = 'wpDistance'
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/Gps2Latitude.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class GPS2Latitude extends SimpleSettings {
4 | label = 'gps 2 latitude';
5 | name = 'gps2Latitude';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/GpsLongitude.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class GPSLongitude extends SimpleSettings {
4 | label = 'gps longitude';
5 | name = 'gpsLongitude';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/HomeDistance.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class HomeDistance extends SimpleSettings {
4 | label = 'home distance';
5 | name = 'homeDistance';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/HomeLatitude.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class HomeLatitude extends SimpleSettings {
4 | label = 'home latitude';
5 | name = 'homeLatitude';
6 | }
7 |
--------------------------------------------------------------------------------
/src/store/configureStore.js:
--------------------------------------------------------------------------------
1 | if (process.env.NODE_ENV === 'production') {
2 | module.exports = require('./configureStore.production');
3 | } else {
4 | module.exports = require('./configureStore.development');
5 | }
6 |
--------------------------------------------------------------------------------
/src/config/settings/BatteryCurrent.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class BatteryCurrent extends SimpleSettings {
4 | label = 'battery current';
5 | name = 'batteryCurrent';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/BatteryVoltage.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class BatteryVoltage extends SimpleSettings {
4 | label = 'battery voltage'
5 | name = 'batteryVoltage';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/Gps2Longitude.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class Gps2Longitude extends SimpleSettings {
4 | label = 'gps 2 longitude';
5 | name = 'gps2Longitude';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/HomeLongitude.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class HomeLongitude extends SimpleSettings {
4 | label = 'home longitude';
5 | name = 'homeLongitude';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/BatteryConsumed.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class BatteryConsumed extends SimpleSettings {
4 | label = 'battery consumed';
5 | name = 'batteryConsumed';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/AbsoluteAltitude.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class AbsoluteAltitude extends SimpleSettings {
4 | label = 'absolute altitude';
5 | name = 'absoluteAltitude';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/BatteryRemaining.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class BatteryRemaining extends SimpleSettings {
4 | label = 'battery remaining';
5 | name = 'batteryRemaining';
6 | }
7 |
--------------------------------------------------------------------------------
/src/config/settings/RelativeAltitude.js:
--------------------------------------------------------------------------------
1 | import SimpleSettings from './SimpleSettings';
2 |
3 | export default class RelativeAltitude extends SimpleSettings {
4 | label = 'relative altitude';
5 | name = 'relativeAltitude';
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .grunt
3 | .jshintignore
4 | .jshintrc
5 | .lock-wscript
6 | *.log
7 | *.pid
8 | *.seed
9 | app/dist
10 | coverage
11 | default.conf
12 | lib-cov
13 | logs
14 | node_modules
15 | test-results.xml
16 | 0.*.zip
17 |
--------------------------------------------------------------------------------
/etc/dummy.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/pixler/Editor.css:
--------------------------------------------------------------------------------
1 | .row {
2 | display: flex;
3 | flex-direction: row;
4 | }
5 |
6 | .square {
7 | height: 20px;
8 | width: 20px;
9 | border: 1px transparent;
10 | text-align: center;
11 | line-height: 20px;
12 | }
13 |
--------------------------------------------------------------------------------
/src/data/icons/lookup.js:
--------------------------------------------------------------------------------
1 | export const GPS = 0;
2 | export const HDOP = 1;
3 | export const TIME = 2;
4 | export const WP_DISTANCE = 3;
5 | export const TOTAL_TRIP = 4;
6 | export const RSSI = 5;
7 | export const LINK_QUALITY = 6;
8 | export const HOME_DISTANCE = 7;
9 |
--------------------------------------------------------------------------------
/src/utils/PropTypes.js:
--------------------------------------------------------------------------------
1 | import ImmutablePropTypes from 'react-immutable-proptypes';
2 |
3 | const value = (type) =>
4 | ImmutablePropTypes.contains({
5 | value: type,
6 | originalValue: type,
7 | });
8 |
9 | export default {
10 | value
11 | };
12 |
--------------------------------------------------------------------------------
/etc/dummy.xcodeproj/project.xcworkspace/xcuserdata/sergiovilar.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasBales/PlayuavOSDConfigurator/HEAD/etc/dummy.xcodeproj/project.xcworkspace/xcuserdata/sergiovilar.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/webpack.config.node.js:
--------------------------------------------------------------------------------
1 | // for babel-plugin-webpack-loaders
2 | const devConfigs = require('./webpack.config');
3 |
4 | module.exports = {
5 | output: {
6 | libraryTarget: 'commonjs2'
7 | },
8 | module: {
9 | loaders: devConfigs.module.loaders // remove babel-loader
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/src/components/Label.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | export default function Label(props) {
4 | const { text } = props;
5 | return (
6 |
7 | );
8 | }
9 |
10 | Label.propTypes = {
11 | text: PropTypes.string.isRequired,
12 | };
13 |
--------------------------------------------------------------------------------
/app/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/pixler/Pixel.css:
--------------------------------------------------------------------------------
1 | .pixel {
2 | height: 20px;
3 | width: 20px;
4 | border: 1px solid #AAAAAA;
5 | cursor: pointer;
6 | }
7 |
8 | .empty {
9 | background-color: #CCCCCC;
10 |
11 | }
12 |
13 | .outline {
14 | background-color: #000000;
15 | }
16 |
17 | .shape {
18 | background-color: #FFFFFF;
19 | }
20 |
--------------------------------------------------------------------------------
/src/preview/Grid.css:
--------------------------------------------------------------------------------
1 | .container {
2 | position: absolute;
3 | left: 0;
4 | top: 0;
5 | width: 100%;
6 | height: 100%;
7 | }
8 |
9 | .row {
10 | height: 10px;
11 | border-bottom: 1px solid #999999;
12 | }
13 | .column {
14 | width: 10px;
15 | border-right: 1px solid #999999;
16 | display: inline-block;
17 | height: 100%;
18 | }
19 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | end_of_line = lf
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [*.{json,js,jsx,html,css}]
11 | indent_style = space
12 | indent_size = 2
13 |
14 | [.eslintrc]
15 | indent_style = space
16 | indent_size = 2
17 |
18 | [*.md]
19 | trim_trailing_whitespace = false
20 |
--------------------------------------------------------------------------------
/src/preview/ArmState.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class ArmState extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | armState: PropTypes.number.isRequired,
8 | }
9 | content() {
10 | return this.props.armState ? 'ARMED' : 'DISARMED';
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, IndexRoute } from 'react-router';
3 | import App from './App';
4 | import Config from './config/Config';
5 | import Pixler from './pixler/Pixler';
6 |
7 | export default (
8 |
9 |
10 |
11 |
12 | );
13 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"],
3 | "plugins": ["add-module-exports", ["extensible-destructuring", {"mode": "optout"}]],
4 | "env": {
5 | "development": {
6 | "presets": ["react-hmre"]
7 | },
8 | "test": {
9 | "plugins": [
10 | ["webpack-loaders", { "config": "webpack.config.node.js", "verbose": false }]
11 | ]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer as routing } from 'react-router-redux';
3 | import parameters from './config/reducer';
4 | import pixler from './pixler/reducer';
5 | import preview from './preview/reducer';
6 |
7 | const rootReducer = combineReducers({
8 | parameters,
9 | pixler,
10 | preview,
11 | routing
12 | });
13 |
14 | export default rootReducer;
15 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": "airbnb",
4 | "env": {
5 | "browser": true,
6 | },
7 | "globals": {
8 | "chrome": false,
9 | "VERSION": true
10 | },
11 | "rules": {
12 | "arrow-body-style": 0,
13 | "comma-dangle": 0,
14 | "consistent-return": 0,
15 | "no-use-before-define": 0,
16 | "react/jsx-no-bind": 0
17 | },
18 | "plugins": [
19 | "react"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/src/DevTools.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createDevTools } from 'redux-devtools';
3 | import LogMonitor from 'redux-devtools-log-monitor';
4 | import DockMonitor from 'redux-devtools-dock-monitor';
5 |
6 | export default createDevTools(
7 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/src/preview/GpsLatitude.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class GpsLatitude extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | gpsLatitude: PropTypes.number.isRequired,
8 | }
9 |
10 | content() {
11 | const { gpsLatitude } = this.props;
12 | return (gpsLatitude / 10000000).toFixed(5);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/preview/reducer.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import preview from '../../src/preview/reducer';
3 | import { ALARM } from '../../src/preview/actions';
4 |
5 | describe('reducers', () => {
6 | describe('preview', () => {
7 | it('should handle ALARM', () => {
8 | const state = preview(undefined, { type: ALARM, payload: 3 });
9 | expect(state.get('alarm')).to.equal(3);
10 | });
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/preview/GpsLongitude.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class GpsLongitude extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | gpsLongitude: PropTypes.number.isRequired,
8 | }
9 |
10 | content() {
11 | const { gpsLongitude } = this.props;
12 | return (gpsLongitude / 10000000).toFixed(5);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/preview/BatteryCurrent.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class BatteryCurrent extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | batteryCurrent: PropTypes.number.isRequired,
8 | }
9 |
10 | content() {
11 | const { batteryCurrent } = this.props;
12 |
13 | return `${batteryCurrent.toFixed(1)}A`;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/preview/BatteryVoltage.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class BatteryVoltage extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | batteryVoltage: PropTypes.number.isRequired,
8 | }
9 |
10 | content() {
11 | const { batteryVoltage } = this.props;
12 |
13 | return `${batteryVoltage.toFixed(1)}V`;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/preview/BatteryConsumed.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class BatteryConsumed extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | batteryConsumed: PropTypes.number.isRequired,
8 | }
9 |
10 | content() {
11 | const { batteryConsumed } = this.props;
12 |
13 | return `${batteryConsumed.toFixed(0)}mah`;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/preview/BatteryRemaining.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class BatteryRemaining extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | batteryCurrent: PropTypes.number.isRequired,
8 | }
9 |
10 | content() {
11 | const { batteryRemaining } = this.props;
12 |
13 | return `${batteryRemaining.toFixed(0)}%`;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 |
4 | matrix:
5 | include:
6 | - os: linux
7 | sudo: required
8 | dist: trusty
9 | node_js:
10 | - "5.9.0"
11 |
12 | addons:
13 | apt:
14 | packages:
15 | - libnotify4
16 | - icnsutils
17 | - graphicsmagick
18 |
19 | install:
20 | - nvm install 5.9.0
21 | - npm prune
22 | - npm install
23 |
24 | script:
25 | - npm run lint
26 | - npm run test-ci
27 | - npm run validate
28 |
--------------------------------------------------------------------------------
/src/preview/HomeLatitude.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class HomeLatitude extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | homeLatitude: PropTypes.number.isRequired,
8 | }
9 |
10 | content() {
11 | const { gpsLatitude } = this.props;
12 | const lat = (gpsLatitude / 10000000).toFixed(5);
13 | return `H ${lat}`;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/preview/HomeLongitude.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class HomeLongitude extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | homeLongitude: PropTypes.number.isRequired,
8 | }
9 |
10 | content() {
11 | const { gpsLongitude } = this.props;
12 | const long = (gpsLongitude / 10000000).toFixed(5);
13 | return `H ${long}`;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/utils/fonts.js:
--------------------------------------------------------------------------------
1 | import small from '../data/fonts/small';
2 | import medium from '../data/fonts/medium';
3 | import large from '../data/fonts/large';
4 |
5 | function getFont(size) {
6 | switch (size) {
7 | case 0:
8 | return small;
9 | case 1:
10 | return medium;
11 | case 2:
12 | return large;
13 | default:
14 | throw new Error(`trying to get unsupported font for size ${size}`);
15 | }
16 | }
17 |
18 | export default { getFont };
19 |
--------------------------------------------------------------------------------
/src/utils/icons.js:
--------------------------------------------------------------------------------
1 | import small from '../data/icons/small';
2 | import medium from '../data/icons/medium';
3 |
4 | function getFont(size) {
5 | switch (size) {
6 | case 0:
7 | return small;
8 | case 1:
9 | return medium;
10 | case 2:
11 | // TODO: pixel icons for large font
12 | return medium;
13 | default:
14 | throw new Error('trying to get unsupported icon font for size', size);
15 | }
16 | }
17 |
18 | export default { getFont };
19 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | ---
2 | engines:
3 | csslint:
4 | enabled: true
5 | duplication:
6 | enabled: true
7 | config:
8 | languages:
9 | - ruby
10 | - javascript
11 | - python
12 | - php
13 | eslint:
14 | enabled: true
15 | fixme:
16 | enabled: true
17 | ratings:
18 | paths:
19 | - "**.css"
20 | - "**.inc"
21 | - "**.js"
22 | - "**.jsx"
23 | - "**.module"
24 | - "**.php"
25 | - "**.py"
26 | - "**.rb"
27 | exclude_paths:
28 | - test/
29 |
--------------------------------------------------------------------------------
/src/preview/actions.js:
--------------------------------------------------------------------------------
1 | export const PANEL = 'preview/panels';
2 | export const TOGGLE_GRID = 'parameters/toggle_grid';
3 | export const ALARM = 'parameters/alarm';
4 |
5 | function setAlarm(alarm) {
6 | return { type: ALARM, payload: alarm };
7 | }
8 |
9 | function setPanel(panel) {
10 | return { type: PANEL, payload: panel };
11 | }
12 |
13 | function toggleGrid() {
14 | return { type: TOGGLE_GRID };
15 | }
16 |
17 | export default {
18 | setAlarm,
19 | setPanel,
20 | toggleGrid,
21 | };
22 |
--------------------------------------------------------------------------------
/src/preview/GpsHdop.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import * as icons from '../data/icons/lookup';
4 |
5 | export default class GpsHdop extends StringPreview {
6 | static propTypes = {
7 | ...StringPreview.propTypes,
8 | gpsHdop: PropTypes.number.isRequired,
9 | }
10 |
11 | icon() {
12 | return icons.HDOP;
13 | }
14 |
15 | content() {
16 | const { gpsHdop } = this.props;
17 | return `${(gpsHdop / 100).toFixed(1)}`;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/store/configureStore.production.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import { hashHistory } from 'react-router';
4 | import { routerMiddleware } from 'react-router-redux';
5 | import rootReducer from '../reducers';
6 |
7 | const router = routerMiddleware(hashHistory);
8 |
9 | const enhancer = applyMiddleware(thunk, router);
10 |
11 | export default function configureStore(initialState) {
12 | return createStore(rootReducer, initialState, enhancer);
13 | }
14 |
--------------------------------------------------------------------------------
/src/preview/AbsoluteAltitude.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import units from '../utils/units';
4 |
5 | export default class AbsoluteAltitude extends StringPreview {
6 | static propTypes = {
7 | ...StringPreview.propTypes,
8 | absoluteAltitude: PropTypes.number.isRequired,
9 | units: PropTypes.number.isRequired,
10 | }
11 |
12 | content() {
13 | return `AA ${units.convertDistance(this.props.absoluteAltitude, this.props.units)}`;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/preview/RelativeAltitude.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import units from '../utils/units';
4 |
5 | export default class RelativeAltitude extends StringPreview {
6 | static propTypes = {
7 | ...StringPreview.propTypes,
8 | relativeAltitude: PropTypes.number.isRequired,
9 | units: PropTypes.number.isRequired,
10 | }
11 |
12 | content() {
13 | return `A ${units.convertDistance(this.props.relativeAltitude, this.props.units)}`;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/preview/Watt.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class Watt extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | batteryCurrent: PropTypes.number.isRequired,
8 | batteryVoltage: PropTypes.number.isRequired,
9 | }
10 |
11 | content() {
12 | const { batteryCurrent, batteryVoltage } = this.props;
13 | const watt = batteryVoltage * batteryCurrent;
14 |
15 | return `${watt.toFixed(1)}W`;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/config/parameters/Text.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Label from '../../components/Label';
3 | import styles from './Text.css';
4 |
5 | export default function Text(props) {
6 | const { label, text } = props;
7 | return (
8 |
12 | );
13 | }
14 |
15 | Text.propTypes = {
16 | label: PropTypes.string.isRequired,
17 | text: PropTypes.oneOfType([
18 | PropTypes.string, PropTypes.number,
19 | ]).isRequired,
20 | };
21 |
--------------------------------------------------------------------------------
/src/preview/TotalTrip.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import units from '../utils/units';
4 | import * as icons from '../data/icons/lookup';
5 |
6 | export default class TotalTrip extends StringPreview {
7 | static propTypes = {
8 | ...StringPreview.propTypes,
9 | totalTrip: PropTypes.number.isRequired,
10 | units: PropTypes.number.isRequired,
11 | }
12 |
13 | icon() {
14 | return icons.TOTAL_TRIP;
15 | }
16 |
17 | content() {
18 | return units.convertDistance(this.props.totalTrip, this.props.units);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/preview/WpDistance.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import units from '../utils/units';
4 | import * as icons from '../data/icons/lookup';
5 |
6 | export default class WpDistance extends StringPreview {
7 | static propTypes = {
8 | ...StringPreview.propTypes,
9 | wpDistance: PropTypes.number.isRequired,
10 | units: PropTypes.number.isRequired,
11 | }
12 |
13 | icon() {
14 | return icons.WP_DISTANCE;
15 | }
16 |
17 | content() {
18 | return `${units.convertDistance(this.props.wpDistance, this.props.units)}`;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/preview/HomeDistance.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import units from '../utils/units';
4 | import * as icons from '../data/icons/lookup';
5 |
6 | export default class HomeDistance extends StringPreview {
7 | static propTypes = {
8 | ...StringPreview.propTypes,
9 | homeDistance: PropTypes.number.isRequired,
10 | units: PropTypes.number.isRequired,
11 | }
12 |
13 | icon() {
14 | return icons.HOME_DISTANCE;
15 | }
16 |
17 | content() {
18 | return `${units.convertDistance(this.props.homeDistance, this.props.units)}`;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/Column.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classNames from 'classnames';
3 |
4 | export default function Column(props) {
5 | const { children, width } = props;
6 | const style = props.style || {};
7 | style.width = `${width}%`;
8 | const classes = classNames('column', props.classes);
9 |
10 | return (
11 |
12 | {children}
13 |
14 | );
15 | }
16 |
17 | Column.propTypes = {
18 | children: PropTypes.node,
19 | style: PropTypes.object,
20 | classes: PropTypes.string,
21 | width: PropTypes.number.isRequired,
22 | };
23 |
--------------------------------------------------------------------------------
/src/preview/SpeedAir.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import units from '../utils/units';
4 |
5 | export default class SpeedAir extends StringPreview {
6 | static propTypes = {
7 | ...StringPreview.propTypes,
8 | speedAir: PropTypes.number.isRequired,
9 | units: PropTypes.number.isRequired,
10 | }
11 |
12 | content() {
13 | var speedUnitString = units.speedUnits(this.props.units);
14 | const speed = units.convertSpeedWithoutUnits(this.props.speedAir, this.props.units);
15 | return `AS ${speed.toFixed(0)}${speedUnitString}`;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/preview/SpeedGround.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import units from '../utils/units';
4 |
5 | export default class SpeedGround extends StringPreview {
6 | static propTypes = {
7 | ...StringPreview.propTypes,
8 | speedGround: PropTypes.number.isRequired,
9 | units: PropTypes.number.isRequired,
10 | }
11 |
12 | content() {
13 | var speedUnitString = units.speedUnits(this.props.units);
14 | const speed = units.convertSpeedWithoutUnits(this.props.speedGround, this.props.units);
15 | return `GS ${speed.toFixed(0)}${speedUnitString}`;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/config/parameters/ParameterList.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { Card, CardTitle, CardText } from 'react-toolbox/lib/card';
3 |
4 | export default function ParamterList(props) {
5 | const { children, name } = props;
6 | const contentStyle = { padding: 0 };
7 | return (
8 |
9 |
10 |
11 |
12 | {children}
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | ParamterList.propTypes = {
20 | children: PropTypes.node,
21 | name: PropTypes.string.isRequired
22 | };
23 |
--------------------------------------------------------------------------------
/src/preview/reducer.js:
--------------------------------------------------------------------------------
1 | import * as actions from './actions';
2 | import Immutable from 'immutable';
3 |
4 | const initialState = Immutable.fromJS({
5 | panel: 0,
6 | alarm: 0,
7 | showGrid: false,
8 | });
9 |
10 | export default function preview(state = initialState, action = {}) {
11 | const payload = action.payload;
12 |
13 | switch (action.type) {
14 | case actions.ALARM:
15 | return state.set('alarm', payload);
16 | case actions.PANEL:
17 | return state.set('panel', payload);
18 | case actions.TOGGLE_GRID:
19 | return state.update('showGrid', (show) => !show);
20 | default:
21 | return state;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/test/setup.js:
--------------------------------------------------------------------------------
1 | import extensiblePolyfill from 'extensible-polyfill';
2 | extensiblePolyfill('immutable');
3 |
4 | import chai from 'chai';
5 | import chaiImmutable from 'chai-immutable';
6 | import { jsdom } from 'jsdom';
7 |
8 | chai.use(chaiImmutable);
9 |
10 | global.document = jsdom('');
11 | global.window = document.defaultView;
12 | global.navigator = global.window.navigator;
13 | window.localStorage = window.sessionStorage = {
14 | getItem(key) {
15 | return this[key];
16 | },
17 | setItem(key, value) {
18 | this[key] = value;
19 | },
20 | removeItem(key) {
21 | this[key] = undefined;
22 | },
23 | };
24 |
--------------------------------------------------------------------------------
/src/config/parameters/Enabled.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Checkbox from 'react-toolbox/lib/checkbox/Checkbox';
3 |
4 | export default class ParameterEnabled extends Component {
5 | static propTypes = {
6 | enabled: PropTypes.bool.isRequired,
7 | setEnabled: PropTypes.func.isRequired
8 | }
9 |
10 | _onChange = () => {
11 | this.props.setEnabled(!this.props.enabled);
12 | }
13 |
14 | render() {
15 | const { enabled } = this.props;
16 |
17 | return (
18 |
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/preview/Alarms.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class Alarms extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | alarm: PropTypes.number.isRequired,
8 | }
9 |
10 | content() {
11 | switch (this.props.alarm) {
12 | case 1:
13 | return 'NO GPS FIX';
14 | case 2:
15 | return 'LOW BATTERY';
16 | case 3:
17 | return 'SPEED LOW';
18 | case 4:
19 | return 'OVER SPEED';
20 | case 5:
21 | return 'LOW ALT';
22 | case 6:
23 | return 'HIGH ALT';
24 | default:
25 | return null;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /* eslint strict: 0, no-console: 0 */
2 | 'use strict';
3 |
4 | const express = require('express');
5 | const webpack = require('webpack');
6 | const config = require('./webpack.config.development');
7 |
8 | const app = express();
9 | const compiler = webpack(config);
10 |
11 | const PORT = 3000;
12 |
13 | app.use(require('webpack-dev-middleware')(compiler, {
14 | publicPath: config.output.publicPath,
15 | stats: {
16 | colors: true
17 | }
18 | }));
19 |
20 | app.use(require('webpack-hot-middleware')(compiler));
21 |
22 | app.listen(PORT, 'localhost', err => {
23 | if (err) {
24 | console.log(err);
25 | return;
26 | }
27 |
28 | console.log(`Listening at http://localhost:${PORT}`);
29 | });
30 |
--------------------------------------------------------------------------------
/wallaby.js:
--------------------------------------------------------------------------------
1 | /* eslint no-var: 0, func-names: 0, strict: 0, object-shorthand: 0 */
2 | 'use strict';
3 |
4 | var webpackConfig = require('./webpack.config.development');
5 | var wallabyWebpack = require('wallaby-webpack');
6 | var webpackPostprocessor = wallabyWebpack(webpackConfig);
7 |
8 | module.exports = function (wallaby) {
9 | return {
10 | files: [
11 | { pattern: 'app/**/*.js', load: false }
12 | ],
13 |
14 | tests: [
15 | { pattern: 'test/**/*.js', load: false }
16 | ],
17 |
18 | compilers: {
19 | '**/*.js': wallaby.compilers.babel()
20 | },
21 |
22 | postprocessor: webpackPostprocessor,
23 |
24 | bootstrap: function () {
25 | window.__moduleBundler.loadTests();
26 | }
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/src/preview/Grid.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import classnames from 'classnames';
3 | import styles from './Grid.css';
4 |
5 | export default function Grid(props) {
6 | if (!props.visible) {
7 | return null;
8 | }
9 |
10 | return (
11 |
12 |
13 | {[...Array(26)].map((_, i) =>
14 |
)}
15 |
16 |
17 | {[...Array(35)].map((_, i) =>
18 |
)}
19 |
20 |
21 | );
22 | }
23 |
24 | Grid.propTypes = {
25 | visible: PropTypes.bool.isRequired,
26 | };
27 |
--------------------------------------------------------------------------------
/src/preview/Efficiency.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import units from '../utils/units';
4 |
5 | export default class Efficiency extends StringPreview {
6 | static propTypes = {
7 | ...StringPreview.propTypes,
8 | batteryCurrent: PropTypes.number.isRequired,
9 | batteryVoltage: PropTypes.number.isRequired,
10 | speedGround: PropTypes.number.isRequired,
11 | units: PropTypes.number.isRequired,
12 | }
13 |
14 | content() {
15 | const { batteryCurrent, batteryVoltage, speedGround } = this.props;
16 | const wattage = batteryVoltage * batteryCurrent;
17 | const efficiency = wattage / speedGround;
18 | const distanceString = units.longDistanceUnits(this.props.units);
19 |
20 | return `${efficiency.toFixed(1)}W/${distanceString}`;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/config/parameters/Scale.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Input from '../../components/Input';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 |
5 | export default class Scale extends Component {
6 | static propTypes = {
7 | name: PropTypes.string.isRequired,
8 | scale: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
9 | setScale: PropTypes.func.isRequired,
10 | }
11 |
12 | _onChange = (scale) => {
13 | this.props.setScale(this.props.name, parseFloat(scale));
14 | }
15 |
16 | render() {
17 | const { scale } = this.props;
18 | return (
19 |
20 |
27 |
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.changelogrc:
--------------------------------------------------------------------------------
1 | {
2 | "app_name": "Playuav OSD Configurator",
3 | "tag": false,
4 | "sections": [
5 | {
6 | "title": "Bug Fixes",
7 | "grep": "^fix"
8 | },
9 | {
10 | "title": "Features",
11 | "grep": "^feat"
12 | },
13 | {
14 | "title": "Documentation",
15 | "grep": "^docs"
16 | },
17 | {
18 | "title": "Breaking changes",
19 | "grep": "BREAKING"
20 | },
21 | {
22 | "title": "Refactor",
23 | "grep": "^refactor"
24 | },
25 | {
26 | "title": "Style",
27 | "grep": "^style"
28 | },
29 | {
30 | "title": "Test",
31 | "grep": "^test"
32 | },
33 | {
34 | "title": "Chore",
35 | "grep": "^chore"
36 | },
37 | {
38 | "title": "Branchs merged",
39 | "grep": "^Merge branch"
40 | },
41 | {
42 | "title" : "Pull requests merged",
43 | "grep": "^Merge pull request"
44 | }
45 | ]
46 | }
47 |
--------------------------------------------------------------------------------
/src/config/parameters/SpeedScaleType.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Dropdown from 'react-toolbox/lib/dropdown/Dropdown';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 |
5 | export default class SpeedScaleType extends Component {
6 | static propTypes = {
7 | name: PropTypes.string.isRequired,
8 | scaleType: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
9 | setScaleType: PropTypes.func.isRequired,
10 | }
11 |
12 | _onChange = (scaleType) => {
13 | this.props.setScaleType(this.props.name, scaleType);
14 | }
15 |
16 | render() {
17 | const { scaleType } = this.props;
18 | const options = [
19 | { value: 0, label: 'ground' },
20 | { value: 1, label: 'air' },
21 | ];
22 |
23 | return (
24 |
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/etc/dummy.xcodeproj/xcuserdata/sergiovilar.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | blablabla.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 | scheme.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 1
16 |
17 |
18 | SuppressBuildableAutocreation
19 |
20 | 0396ABFD1B605094004125D8
21 |
22 | primary
23 |
24 |
25 | 0396AC131B605094004125D8
26 |
27 | primary
28 |
29 |
30 | 0396AC1E1B605094004125D8
31 |
32 | primary
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/config/parameters/index.js:
--------------------------------------------------------------------------------
1 | import AltitudeScaleType from './AltitudeScaleType';
2 | import Enabled from './Enabled';
3 | import FontSize from './FontSize';
4 | import HorizontalAlignment from './HorizontalAlignment';
5 | import ParameterList from './ParameterList';
6 | import Position from './Position';
7 | import Radius from './Radius';
8 | import Scale from './Scale';
9 | import ScaleAlignment from './ScaleAlignment';
10 | import Select from './Select';
11 | import SpeedScaleType from './SpeedScaleType';
12 | import Text from './Text';
13 | import Units from './Units';
14 | import VerticalAlignment from './VerticalAlignment';
15 | import VideoMode from './VideoMode';
16 | import VisibleOn from './VisibleOn';
17 |
18 | export default {
19 | AltitudeScaleType,
20 | Enabled,
21 | FontSize,
22 | HorizontalAlignment,
23 | ParameterList,
24 | Position,
25 | Radius,
26 | Scale,
27 | ScaleAlignment,
28 | Select,
29 | SpeedScaleType,
30 | Text,
31 | Units,
32 | VerticalAlignment,
33 | VideoMode,
34 | VisibleOn,
35 | };
36 |
--------------------------------------------------------------------------------
/src/preview/LinkQuality.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import * as icons from '../data/icons/lookup';
4 |
5 | export default class LinkQuality extends StringPreview {
6 | static propTypes = {
7 | ...StringPreview.propTypes,
8 | max: PropTypes.number.isRequired,
9 | min: PropTypes.number.isRequired,
10 | raw: PropTypes.number.isRequired,
11 | linkQuality: PropTypes.number.isRequired,
12 | }
13 |
14 | icon() {
15 | return icons.LINK_QUALITY;
16 | }
17 |
18 | content() {
19 | const { min, max, raw } = this.props;
20 | let { linkQuality } = this.props;
21 | let content = '';
22 |
23 | if (raw === 0) {
24 | if ((max - min) > 0) {
25 | linkQuality = (linkQuality - min) / (max - min) * 100;
26 | }
27 |
28 | linkQuality = Math.max(0, linkQuality);
29 | content = `${linkQuality.toFixed(0)}%`;
30 | } else {
31 | content = `${linkQuality.toFixed(0)}`;
32 | }
33 | return content;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "minimum_chrome_version": "38",
4 | "version": "0.12.1",
5 | "author": "TobiasBales",
6 | "name": "PlayUAV OSD Configurator",
7 | "short_name": "playuavosdconfigurator",
8 | "description": "Crossplatform configuration tool for PlayUAV OSD",
9 | "offline_enabled": true,
10 | "app": {
11 | "background": {
12 | "scripts": ["./dist/eventPage.js"],
13 | "persistent": false
14 | }
15 | },
16 | "permissions": [
17 | "https://www.google-analytics.com/",
18 | "https://*.github.com/",
19 | "https://*.githubusercontent.com/",
20 | "serial",
21 | "usb",
22 | "storage",
23 | "fileSystem",
24 | "fileSystem.write",
25 | "fileSystem.retainEntries",
26 | "notifications"
27 | ],
28 | "optional_permissions": [
29 | {"usbDevices": [
30 | {"vendorId": 9900, "productId": 2}
31 | ]}
32 | ],
33 | "icons": {
34 | "128": "icon_128.png"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/Input.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import BaseInput from 'react-toolbox/lib/input';
3 | import CustomPropTypes from '../utils/PropTypes';
4 | import classNames from 'classnames';
5 |
6 | export default class Input extends Component {
7 | static propTypes = {
8 | label: PropTypes.string,
9 | onChange: PropTypes.func.isRequired,
10 | step: PropTypes.number,
11 | type: PropTypes.string,
12 | value: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | }
14 |
15 | static defaultProps = {
16 | type: 'string',
17 | }
18 |
19 | render() {
20 | const value = this.props.value;
21 | const classes = classNames(
22 | { modified: value.get('value') !== value.get('originalValue'), },
23 | );
24 |
25 | return (
26 |
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/config/parameters/Radius.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Input from '../../components/Input';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 |
5 | export default class Radius extends Component {
6 | static propTypes = {
7 | label: PropTypes.string.isRequired,
8 | name: PropTypes.string.isRequired,
9 | radius: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
10 | radiusKey: PropTypes.string.isRequired,
11 | setRadius: PropTypes.func.isRequired,
12 | step: PropTypes.number,
13 | }
14 |
15 | static defaultProps = {
16 | step: 0.1,
17 | }
18 |
19 | _onChange(radius) {
20 | this.props.setRadius(this.props.name, this.props.radiusKey, parseInt(radius, 10));
21 | }
22 |
23 | render() {
24 | const { radius, label } = this.props;
25 |
26 | return (
27 |
28 |
35 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/preview/Rssi.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import * as icons from '../data/icons/lookup';
4 |
5 | export default class Rssi extends StringPreview {
6 | static propTypes = {
7 | ...StringPreview.propTypes,
8 | max: PropTypes.number.isRequired,
9 | min: PropTypes.number.isRequired,
10 | raw: PropTypes.number.isRequired,
11 | rssi: PropTypes.number.isRequired,
12 | type: PropTypes.number.isRequired,
13 | }
14 |
15 | icon() {
16 | return icons.RSSI;
17 | }
18 |
19 | content() {
20 | const { raw, type } = this.props;
21 | let { max, min, rssi } = this.props;
22 | let content = '';
23 |
24 | if (raw === 0) {
25 | if (type === 0) {
26 | min = Math.max(0, min);
27 | max = Math.min(255, max);
28 | }
29 |
30 | if ((max - min) > 0) {
31 | rssi = (rssi - min) / (max - min) * 100;
32 | }
33 |
34 | rssi = Math.max(0, rssi);
35 | content = `${rssi.toFixed(0)}%`;
36 | } else {
37 | content = `${rssi.toFixed(0)}`;
38 | }
39 | return content;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/config/parameters/VideoMode.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Dropdown from 'react-toolbox/lib/dropdown/Dropdown';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 | import classNames from 'classnames';
5 |
6 | export default class VideoMode extends Component {
7 | static propTypes = {
8 | videoMode: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
9 | setMode: PropTypes.func.isRequired,
10 | }
11 |
12 | _onChange = (videoMode) => {
13 | this.props.setMode('video', 'video', videoMode);
14 | }
15 |
16 | render() {
17 | const { videoMode } = this.props;
18 | const options = [
19 | { value: 0, label: 'ntsc' },
20 | { value: 1, label: 'pal' },
21 | ];
22 | const classes = classNames({
23 | modified: videoMode.get('value') !== videoMode.get('originalValue')
24 | });
25 |
26 | return (
27 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 C. T. Lin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/src/config/parameters/Units.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Dropdown from 'react-toolbox/lib/dropdown/Dropdown';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 | import classNames from 'classnames';
5 |
6 | export default class Units extends Component {
7 | static propTypes = {
8 | units: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
9 | setUnits: PropTypes.func.isRequired,
10 | name: PropTypes.string.isRequired,
11 | }
12 |
13 | _onChange = (units) => {
14 | this.props.setUnits(this.props.name, units);
15 | }
16 |
17 | render() {
18 | const { units } = this.props;
19 | const options = [
20 | { value: 0, label: 'metric' },
21 | { value: 1, label: 'imperial' },
22 | ];
23 | const classes = classNames({
24 | modified: units.get('value') !== units.get('originalValue')
25 | });
26 |
27 | return (
28 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/config/parameters/AltitudeScaleType.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Dropdown from 'react-toolbox/lib/dropdown/Dropdown';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 | import classNames from 'classnames';
5 |
6 | export default class AltitudeScaleType extends Component {
7 | static propTypes = {
8 | scaleType: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
9 | setScaleType: PropTypes.func.isRequired,
10 | }
11 |
12 | _onChange = (scaleType) => {
13 | this.props.setScaleType('altitudeScale', scaleType);
14 | }
15 |
16 | render() {
17 | const value = this.props.scaleType;
18 | const options = [
19 | { value: 0, label: 'absolute' },
20 | { value: 1, label: 'relative' },
21 | ];
22 | const classes = classNames(
23 | { modified: value.get('value') !== value.get('originalValue') }
24 | );
25 |
26 | return (
27 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/pixler/Preview.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import fonts from '../utils/fonts';
3 | import Canvas from '../utils/Canvas';
4 | import styles from './Preview.css';
5 |
6 | export default class Preview extends Component {
7 | static propTypes = {
8 | fontSize: PropTypes.number.isRequired,
9 | outline: PropTypes.arrayOf(PropTypes.number).isRequired,
10 | shape: PropTypes.arrayOf(PropTypes.number).isRequired,
11 | }
12 |
13 | componentDidMount() {
14 | this.canvas = new Canvas(this.refs.canvas);
15 | this.draw();
16 | }
17 |
18 | componentDidUpdate() {
19 | this.draw();
20 | }
21 |
22 | draw() {
23 | const font = fonts.getFont(this.props.fontSize);
24 | const { height, width } = font.dimensions;
25 | const { shape, outline } = this.props;
26 | this.canvas.clear();
27 | this.canvas.drawCharacterData(height / 2, width / 2, shape, outline, width, height);
28 | }
29 |
30 | render() {
31 | const { height, width } = fonts.getFont(this.props.fontSize).dimensions;
32 | return (
33 |
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/preview/GpsStatus.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import * as icons from '../data/icons/lookup';
4 |
5 | const NO_GPS = 0;
6 | const NO_FIX = 1;
7 | const FIX_2D = 2;
8 | const FIX_3D = 3;
9 | const FIX_3D_DGPS = 4;
10 |
11 | export default class GpsStatus extends StringPreview {
12 | static propTypes = {
13 | ...StringPreview.propTypes,
14 | gpsSattelites: PropTypes.number.isRequired,
15 | gpsStatus: PropTypes.number.isRequired,
16 | }
17 |
18 | icon() {
19 | return icons.GPS;
20 | }
21 |
22 | content() {
23 | const { gpsSattelites, gpsStatus } = this.props;
24 | let content = '';
25 |
26 | switch (gpsStatus) {
27 | case NO_GPS:
28 | content = 'NOFIX';
29 | break;
30 | case NO_FIX:
31 | content = 'NOFIX';
32 | break;
33 | case FIX_2D:
34 | content = `${gpsSattelites}`;
35 | break;
36 | case FIX_3D:
37 | content = `${gpsSattelites}`;
38 | break;
39 | case FIX_3D_DGPS:
40 | content = `${gpsSattelites}`;
41 | break;
42 | default:
43 | content = 'unknown';
44 | }
45 | return content;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/pixler/Pixel.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import styles from './Pixel.css';
3 | import classNames from 'classnames';
4 | import { EMPTY, OUTLINE, SHAPE } from './actions';
5 |
6 | export default class Pixel extends Component {
7 | static propTypes = {
8 | setPixel: PropTypes.func.isRequired,
9 | type: PropTypes.oneOf([EMPTY, SHAPE, OUTLINE]).isRequired,
10 | }
11 |
12 | _onClick = (e) => {
13 | let targetType = EMPTY;
14 |
15 | if (e.button === 0) {
16 | targetType = SHAPE;
17 | } else if (e.button === 2) {
18 | targetType = OUTLINE;
19 | }
20 |
21 | if (targetType === this.props.type) {
22 | targetType = EMPTY;
23 | }
24 | this.props.setPixel(targetType);
25 | };
26 |
27 | render() {
28 | const classDescriptions = {};
29 | classDescriptions[styles.empty] = this.props.type === EMPTY;
30 | classDescriptions[styles.outline] = this.props.type === OUTLINE;
31 | classDescriptions[styles.shape] = this.props.type === SHAPE;
32 | const classes = classNames(styles.pixel, classDescriptions);
33 | return (
34 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/config/parameters/ScaleAlignment.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Dropdown from 'react-toolbox/lib/dropdown/Dropdown';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 | import classNames from 'classnames';
5 |
6 | export default class ScaleAlignment extends Component {
7 | static propTypes = {
8 | name: PropTypes.string.isRequired,
9 | scaleAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
10 | setScaleAlignment: PropTypes.func.isRequired,
11 | }
12 |
13 | _onChange = (scaleAlignment) => {
14 | this.props.setScaleAlignment(this.props.name, scaleAlignment);
15 | }
16 |
17 | render() {
18 | const value = this.props.scaleAlignment;
19 | const options = [
20 | { value: 0, label: 'left' },
21 | { value: 1, label: 'right' },
22 | ];
23 | const classes = classNames(
24 | { modified: value.get('value') !== value.get('originalValue') }
25 | );
26 |
27 | return (
28 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/preview/Compass.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import PreviewBase from './PreviewBase';
3 |
4 | export default class Compass extends PreviewBase {
5 | static propTypes = {
6 | heading: PropTypes.number.isRequired,
7 | panel: PropTypes.number.isRequired,
8 | positionY: PropTypes.number.isRequired,
9 | visibleOn: PropTypes.number.isRequired,
10 | }
11 |
12 | draw() {
13 | this.canvas.clear();
14 | const posY = 8;
15 |
16 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
17 | this.canvas.drawCompass(this.props.heading, posY);
18 | }
19 | }
20 |
21 | render() {
22 | const { positionY } = this.props;
23 | const positionX = 180; // should be half width
24 | const width = 180;
25 | const height = 40;
26 |
27 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
28 |
29 | return (
30 | !visible ?
31 | :
32 |
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/config/parameters/FontSize.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Dropdown from 'react-toolbox/lib/dropdown/Dropdown';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 | import classNames from 'classnames';
5 |
6 | export default class Position extends Component {
7 | static propTypes = {
8 | fontSize: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
9 | setFontSize: PropTypes.func.isRequired,
10 | name: PropTypes.string.isRequired,
11 | }
12 |
13 | _onChange = (fontSize) => {
14 | this.props.setFontSize(this.props.name, fontSize);
15 | }
16 |
17 | render() {
18 | const { fontSize } = this.props;
19 | const options = [
20 | { value: 0, label: 'small' },
21 | { value: 1, label: 'medium' },
22 | { value: 2, label: 'large' },
23 | ];
24 |
25 | const classes = classNames(
26 | { modified: fontSize.get('value') !== fontSize.get('originalValue') },
27 | );
28 |
29 | return (
30 |
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/config/parameters/Select.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Dropdown from 'react-toolbox/lib/dropdown/Dropdown';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 | import classNames from 'classnames';
5 |
6 | export default class Select extends Component {
7 | static propTypes = {
8 | label: PropTypes.string.isRequired,
9 | setValue: PropTypes.func.isRequired,
10 | options: React.PropTypes.arrayOf(
11 | React.PropTypes.shape({
12 | value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
13 | label: PropTypes.string.isRequired,
14 | })).isRequired,
15 | value: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | }
17 |
18 | _onChange = (value) => {
19 | this.props.setValue(value);
20 | }
21 |
22 | render() {
23 | const { value, options, label } = this.props;
24 | const classes = classNames(
25 | { modified: value.get('value') !== value.get('originalValue') }
26 | );
27 |
28 | return (
29 |
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/config/parameters/HorizontalAlignment.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Dropdown from 'react-toolbox/lib/dropdown/Dropdown';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 | import classNames from 'classnames';
5 |
6 | export default class HorizontalAlignment extends Component {
7 | static propTypes = {
8 | hAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
9 | setHAlignment: PropTypes.func.isRequired,
10 | name: PropTypes.string.isRequired,
11 | }
12 |
13 | _onChange = (hAlignment) => {
14 | this.props.setHAlignment(this.props.name, hAlignment);
15 | }
16 |
17 | render() {
18 | const { hAlignment } = this.props;
19 | const options = [
20 | { value: 0, label: 'left' },
21 | { value: 1, label: 'middle' },
22 | { value: 2, label: 'right' },
23 | ];
24 | const classes = classNames(
25 | { modified: hAlignment.get('value') !== hAlignment.get('originalValue') }
26 | );
27 |
28 | return (
29 |
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/config/parameters/VerticalAlignment.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Dropdown from 'react-toolbox/lib/dropdown/Dropdown';
3 | import CustomPropTypes from '../../utils/PropTypes';
4 | import classNames from 'classnames';
5 |
6 | export default class VerticalAlignment extends Component {
7 | static propTypes = {
8 | name: PropTypes.string.isRequired,
9 | setVAlignment: PropTypes.func.isRequired,
10 | vAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | }
12 |
13 | _onChange = (vAlignment) => {
14 | this.props.setVAlignment(this.props.name, parseInt(vAlignment, 10));
15 | }
16 |
17 | render() {
18 | const { vAlignment } = this.props;
19 | const options = [
20 | { value: 0, label: 'top' },
21 | { value: 1, label: 'middle' },
22 | { value: 2, label: 'bottom' },
23 | ];
24 | const classes = classNames({
25 | modified: vAlignment.get('value') !== vAlignment.get('originalValue')
26 | });
27 |
28 | return (
29 |
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import extensiblePolyfill from 'extensible-polyfill';
2 | extensiblePolyfill('immutable');
3 |
4 | import React from 'react';
5 | import { render } from 'react-dom';
6 | import { Provider } from 'react-redux';
7 | import { Router, hashHistory } from 'react-router';
8 | import { syncHistoryWithStore } from 'react-router-redux';
9 | import routes from './routes';
10 | import configureStore from './store/configureStore';
11 | import 'material-design-icons-iconfont/dist/material-design-icons.css';
12 | import './app.global.css';
13 | import 'react-toolbox/lib/commons.scss';
14 | import 'roboto-fontface/css/roboto-fontface';
15 | import injectTapEventPlugin from 'react-tap-event-plugin';
16 |
17 | const store = configureStore();
18 | const history = syncHistoryWithStore(hashHistory, store);
19 |
20 | injectTapEventPlugin();
21 |
22 | render(
23 |
24 |
25 | ,
26 | document.getElementById('root')
27 | );
28 |
29 | if (process.env.NODE_ENV !== 'production') {
30 | // Use require because imports can't be conditional.
31 | // In production, you should ensure process.env.NODE_ENV
32 | // is envified so that Uglify can eliminate this
33 | // module and its dependencies as dead code.
34 | // require('./createDevToolsWindow')(store);
35 | }
36 |
--------------------------------------------------------------------------------
/src/pixler/Pixler.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import { setPixel } from './actions';
3 | import { bindActionCreators } from 'redux';
4 | import { connect } from 'react-redux';
5 | import Editor from './Editor';
6 | import Preview from './Preview';
7 | import Column from '../components/Column';
8 | import Output from './Output';
9 |
10 | function Pixler(props) {
11 | const { fontSize, outline, shape } = props;
12 | return (
13 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | );
28 | }
29 |
30 | Pixler.propTypes = {
31 | setPixel: PropTypes.func.isRequired,
32 | outline: PropTypes.arrayOf(PropTypes.number).isRequired,
33 | fontSize: PropTypes.number.isRequired,
34 | shape: PropTypes.arrayOf(PropTypes.number).isRequired,
35 | };
36 |
37 | function mapStateToProps(state) {
38 | return state.pixler.toJS();
39 | }
40 |
41 | function mapDispatchersToProps(dispatch) {
42 | return bindActionCreators({ setPixel }, dispatch);
43 | }
44 |
45 | export default connect(mapStateToProps, mapDispatchersToProps)(Pixler);
46 |
--------------------------------------------------------------------------------
/src/config/settings/Startup.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class Startup extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | versionSplashMilliseconds: CustomPropTypes.value(PropTypes.number.isRequired).isRequired
11 | }).isRequired,
12 | setVersionSplashMilliseconds: PropTypes.func.isRequired
13 | }
14 |
15 | shouldComponentUpdate(nextProps) {
16 | return !this.props.parameters.equals(nextProps.parameters);
17 | }
18 |
19 | _setValue(key, value) {
20 | this.props.setValue('switching', key, parseInt(value, 10));
21 | }
22 |
23 | render() {
24 | const { versionSplashMilliseconds } = this.props.parameters;
25 | const { setVersionSplashMilliseconds } = this.props;
26 |
27 | return (
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/preview/PreviewBase.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Canvas from '../utils/Canvas';
3 |
4 | export default class PreviewBase extends Component {
5 | static propTypes = {
6 | positionX: PropTypes.number.isRequired,
7 | positionY: PropTypes.number.isRequired,
8 | setPosition: PropTypes.func.isRequired,
9 | }
10 |
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | left: this.props.positionX,
15 | top: this.props.positionY,
16 | };
17 | }
18 |
19 | componentDidMount() {
20 | this.canvas = new Canvas(this.refs.canvas);
21 | this.draw();
22 | }
23 |
24 | shouldComponentUpdate(nextProps) {
25 | return Object.keys(this.props).reduce((shouldUpdate, key) => {
26 | if (key.startsWith('set')) {
27 | return shouldUpdate;
28 | }
29 | return shouldUpdate || this.props[key] !== nextProps[key];
30 | }, false);
31 | }
32 |
33 | componentDidUpdate() {
34 | this.draw();
35 | }
36 |
37 | draw() {
38 | }
39 |
40 | _onDragStop = () => {
41 | const { left, top } = this.state;
42 | this.props.setPosition(left, top);
43 | // this.setState({ ...this.state, left: 0, top: 0 });
44 | }
45 |
46 | _onDrag = (e, ui) => {
47 | const { left, top } = this.state;
48 | this.setState({
49 | ...this.state,
50 | left: left + ui.deltaX,
51 | top: top + ui.deltaY
52 | });
53 | }
54 |
55 | render() {
56 | return ;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/preview/Time.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 | import * as icons from '../data/icons/lookup';
4 |
5 | function padNumber(number, digits) {
6 | return (`0000000000000${number}`).slice(-digits);
7 | }
8 |
9 | export default class Time extends StringPreview {
10 | static propTypes = {
11 | ...StringPreview.propTypes,
12 | timeSinceArming: PropTypes.number.isRequired,
13 | timeSinceHeartbeat: PropTypes.number.isRequired,
14 | timeSinceStartup: PropTypes.number.isRequired,
15 | type: PropTypes.number.isRequired,
16 | }
17 |
18 | icon() {
19 | return icons.TIME;
20 | }
21 |
22 | content() {
23 | const { timeSinceArming, timeSinceHeartbeat, timeSinceStartup, type } = this.props;
24 | let time = 0;
25 | switch (type) {
26 | case 0:
27 | time = timeSinceStartup;
28 | break;
29 | case 1:
30 | time = timeSinceHeartbeat;
31 | break;
32 | case 2:
33 | time = timeSinceArming;
34 | break;
35 | default:
36 | break;
37 | }
38 | let content = '';
39 |
40 | const hours = Math.floor(time / 3600000);
41 | const minutes = Math.floor(time / 60000);
42 | const seconds = Math.floor(time / 1000);
43 |
44 | if (hours > 0) {
45 | content = `${padNumber(hours, 2)}:${padNumber(minutes, 2)}:${padNumber(seconds, 2)}`;
46 | } else {
47 | content = `${padNumber(minutes, 2)}:${padNumber(seconds, 2)}`;
48 | }
49 |
50 | return content;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/preview/VarioGraph.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import PreviewBase from './PreviewBase';
3 |
4 | export default class VarioGraphPreview extends PreviewBase {
5 | static propTypes = {
6 | panel: PropTypes.number.isRequired,
7 | positionX: PropTypes.number.isRequired,
8 | positionY: PropTypes.number.isRequired,
9 | varioData: PropTypes.arrayOf(PropTypes.number).isRequired,
10 | visibleOn: PropTypes.number.isRequired,
11 | }
12 |
13 | draw() {
14 | this.canvas.clear();
15 |
16 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
17 | const { height, width } = this.refs.canvas;
18 | const { varioData } = this.props;
19 | this.canvas.drawRectangle(2, 2, width - 4, height - 4, true, true);
20 | const points = [];
21 |
22 | for (let i = 0; i < varioData.length - 1; i++) {
23 | const x = i;
24 | const y = height - (varioData[x] + height / 2);
25 | points.push(x + 3, y + 2);
26 | }
27 | this.canvas.drawSegmentedLine(true, false, points);
28 | }
29 | }
30 |
31 | render() {
32 | const { positionX, positionY } = this.props;
33 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
34 |
35 | return (
36 | !visible ?
37 | :
38 |
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/store/configureStore.development.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux';
2 | import { persistState } from 'redux-devtools';
3 | import thunk from 'redux-thunk';
4 | import createLogger from 'redux-logger';
5 | import { hashHistory } from 'react-router';
6 | import { routerMiddleware } from 'react-router-redux';
7 | import rootReducer from '../reducers';
8 | import DevTools from '../DevTools';
9 |
10 | const logger = createLogger({
11 | actionTransformer: (action) => (
12 | Object.assign({}, action, {
13 | type: String(action.type),
14 | })
15 | ),
16 | stateTransformer: (state) => {
17 | const plainState = {};
18 | Object.keys(state).forEach((key) => {
19 | if (typeof state[key].toJS === 'function') {
20 | plainState[key] = state[key].toJS();
21 | } else {
22 | plainState[key] = state[key];
23 | }
24 | });
25 | return plainState;
26 | },
27 | level: 'info',
28 | collapsed: true,
29 | });
30 |
31 | const router = routerMiddleware(hashHistory);
32 |
33 | const enhancer = compose(
34 | applyMiddleware(thunk, router, logger),
35 | DevTools.instrument(),
36 | persistState(
37 | window.location.href.match(
38 | /[?&]debug_session=([^&]+)\b/
39 | )
40 | )
41 | );
42 |
43 | export default function configureStore(initialState) {
44 | const store = createStore(rootReducer, initialState, enhancer);
45 |
46 | if (module.hot) {
47 | module.hot.accept('../reducers', () =>
48 | store.replaceReducer(require('../reducers'))
49 | );
50 | }
51 |
52 | return store;
53 | }
54 |
--------------------------------------------------------------------------------
/src/data/icons/small.js:
--------------------------------------------------------------------------------
1 | /* eslint max-len: 0 */
2 |
3 | const shape = [
4 | [0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x38, 0x7c, 0xfe], // GPS
5 | [0x0, 0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x0, 0x0], // HDOP
6 | [0x0, 0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x0, 0x0], // TIME
7 | [0xf8, 0xf8, 0xfe, 0xff, 0xff, 0xef, 0xe0, 0xe0, 0xe0, 0xe0], // WP_DISTANCE
8 | [0x7c, 0x1fe, 0xfc, 0x70, 0x38, 0x38, 0x70, 0xfc, 0x1fe, 0x7c], // TOTAL_TRIP
9 | [0xe0, 0xe0, 0xf8, 0xf8, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe], // RSSI
10 | [0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x38, 0x38, 0x38, 0x38], // LINK_QUALITY
11 | [0x0, 0x24, 0x7e, 0xff, 0xff, 0xff, 0x7e, 0x24, 0x0, 0x0], // HOME_DISTANCE
12 | ];
13 |
14 | const outline = [
15 | [0xc7, 0xbb, 0x7d, 0x6d, 0x7d, 0xbb, 0xc7, 0xd7, 0xbb, 0x1], // GPS
16 | [0x0, 0xc7, 0xab, 0x6d, 0x1, 0x6d, 0xab, 0xc7, 0x0, 0x0], // HDOP
17 | [0x0, 0xc7, 0xab, 0x6d, 0x61, 0x7d, 0xbb, 0xc7, 0x0, 0x0], // TIME
18 | [0x7, 0x57, 0x51, 0x54, 0x45, 0x50, 0x5f, 0x5f, 0x5f, 0x1f], // WP_DISTANCE
19 | [0x83, 0x7d, 0x43, 0xaf, 0xd7, 0xd7, 0xaf, 0x43, 0x7d, 0x83], // TOTAL_TRIP
20 | [0x1f, 0x5f, 0x47, 0x57, 0x51, 0x55, 0x55, 0x55, 0x55, 0x0], // RSSI
21 | [0xef, 0xd7, 0x93, 0x55, 0xbb, 0xd7, 0xd7, 0xd7, 0xd7, 0xc7], // LINK_QUALITY
22 | [0x80, 0xdb, 0xa5, 0x42, 0x7e, 0x42, 0xa5, 0xdb, 0xff, 0xff], // HOME_DISTANCE
23 | ];
24 |
25 | const dimensions = { width: 8, height: 14 };
26 |
27 | function getData(index) {
28 | return { shape: shape[index], outline: outline[index].map((b) => (~b >>> 0) & Math.pow(2, dimensions.width) - 1) };
29 | }
30 |
31 | export default { dimensions, getData };
32 |
--------------------------------------------------------------------------------
/src/config/settings/VarioGraph.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import CustomPropTypes from '../../utils/PropTypes';
5 |
6 | export default class VarioGraph extends Component {
7 | static propTypes = {
8 | parameters: ImmutablePropTypes.contains({
9 | numberOfPanels: PropTypes.number.isRequired,
10 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | }).isRequired,
14 | setPosition: PropTypes.func.isRequired,
15 | setVisibleOn: PropTypes.func.isRequired,
16 | }
17 |
18 | shouldComponentUpdate(nextProps) {
19 | return !this.props.parameters.equals(nextProps.parameters);
20 | }
21 |
22 | render() {
23 | const { setPosition, setVisibleOn } = this.props;
24 | const { numberOfPanels, positionX, positionY, visibleOn } = this.props.parameters;
25 |
26 | return (
27 |
28 |
31 |
34 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/config/settings/Wind.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import CustomPropTypes from '../../utils/PropTypes';
5 |
6 | export default class Wind extends Component {
7 | static propTypes = {
8 | parameters: ImmutablePropTypes.contains({
9 | numberOfPanels: PropTypes.number.isRequired,
10 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | }).isRequired,
14 | setPosition: PropTypes.func.isRequired,
15 | setVisibleOn: PropTypes.func.isRequired,
16 | }
17 |
18 | shouldComponentUpdate(nextProps) {
19 | return !this.props.parameters.equals(nextProps.parameters);
20 | }
21 |
22 | render() {
23 | const {
24 | setPosition,
25 | setVisibleOn,
26 | } = this.props;
27 | const {
28 | numberOfPanels,
29 | positionX,
30 | positionY,
31 | visibleOn,
32 | } = this.props.parameters;
33 |
34 | return (
35 |
36 |
39 |
42 |
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/config/settings/RCChannels.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class RCChannels extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | numberOfPanels: PropTypes.number.isRequired,
11 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | }).isRequired,
15 | setPosition: PropTypes.func.isRequired,
16 | setVisibleOn: PropTypes.func.isRequired,
17 | }
18 |
19 | shouldComponentUpdate(nextProps) {
20 | return !this.props.parameters.equals(nextProps.parameters);
21 | }
22 |
23 | render() {
24 | const { setPosition, setVisibleOn } = this.props;
25 | const {
26 | numberOfPanels, positionX, positionY, visibleOn
27 | } = this.props.parameters;
28 | return (
29 |
30 |
33 |
36 |
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/preview/ArtificialHorizon.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import PreviewBase from './PreviewBase';
3 |
4 | export default class AttitudeMp extends PreviewBase {
5 | static propTypes = {
6 | panel: PropTypes.number.isRequired,
7 | pitch: PropTypes.number.isRequired,
8 | positionX: PropTypes.number.isRequired,
9 | positionY: PropTypes.number.isRequired,
10 | roll: PropTypes.number.isRequired,
11 | scale: PropTypes.number.isRequired,
12 | type: PropTypes.number.isRequired,
13 | visibleOn: PropTypes.number.isRequired,
14 | yaw: PropTypes.number.isRequired,
15 | }
16 |
17 | draw() {
18 | this.canvas.clear();
19 | const width = this.refs.canvas.width;
20 | const height = this.refs.canvas.height;
21 |
22 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
23 | if (this.props.type === 0) {
24 | this.canvas.drawAttitudeMp(width, height,
25 | this.props.roll, this.props.pitch, this.props.scale);
26 | } else {
27 | this.canvas.drawAttitudeSimple(width, height,
28 | this.props.roll, this.props.pitch, this.props.scale);
29 | }
30 | }
31 | }
32 |
33 | render() {
34 | const { positionX, positionY } = this.props;
35 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
36 |
37 | return (
38 | !visible ?
39 | :
40 |
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/config/settings/HomeDirection.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import CustomPropTypes from '../../utils/PropTypes';
5 |
6 | export default class HomeDirection extends Component {
7 | static propTypes = {
8 | parameters: ImmutablePropTypes.contains({
9 | numberOfPanels: PropTypes.number.isRequired,
10 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | }).isRequired,
14 | setPosition: PropTypes.func.isRequired,
15 | setVisibleOn: PropTypes.func.isRequired,
16 | }
17 |
18 | shouldComponentUpdate(nextProps) {
19 | return !this.props.parameters.equals(nextProps.parameters);
20 | }
21 |
22 | render() {
23 | const {
24 | setPosition,
25 | setVisibleOn,
26 | } = this.props;
27 | const {
28 | numberOfPanels,
29 | positionX,
30 | positionY,
31 | visibleOn,
32 | } = this.props.parameters;
33 |
34 | return (
35 |
36 |
39 |
42 |
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/config/settings/Compass.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Input from '../../components/Input';
4 | import Parameters from '../parameters';
5 | import Column from '../../components/Column';
6 | import CustomPropTypes from '../../utils/PropTypes';
7 |
8 | export default class Compass extends Component {
9 | static propTypes = {
10 | parameters: ImmutablePropTypes.contains({
11 | numberOfPanels: PropTypes.number.isRequired,
12 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | }).isRequired,
15 | setPosition: PropTypes.func.isRequired,
16 | setVisibleOn: PropTypes.func.isRequired,
17 | }
18 |
19 | shouldComponentUpdate(nextProps) {
20 | return !this.props.parameters.equals(nextProps.parameters);
21 | }
22 |
23 | _setPosition = (position) => {
24 | this.props.setPosition('compass', null, parseInt(position, 10));
25 | }
26 |
27 | render() {
28 | const {
29 | setVisibleOn,
30 | } = this.props;
31 | const {
32 | numberOfPanels,
33 | positionY,
34 | visibleOn,
35 | } = this.props.parameters;
36 |
37 | return (
38 |
39 |
40 |
41 |
42 |
45 |
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/config/parameters/Position.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Input from '../../components/Input';
3 | import Column from '../../components//Column';
4 | import CustomPropTypes from '../../utils/PropTypes';
5 |
6 | export default class Position extends Component {
7 | static propTypes = {
8 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
9 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
10 | labelX: PropTypes.string.isRequired,
11 | labelY: PropTypes.string.isRequired,
12 | setPosition: PropTypes.func.isRequired,
13 | xMin: PropTypes.number,
14 | xMax: PropTypes.number,
15 | yMin: PropTypes.number,
16 | yMax: PropTypes.number,
17 | name: PropTypes.string.isRequired,
18 | }
19 |
20 | _onChange(axis, position) {
21 | const name = this.props.name;
22 | const x = axis === 'x' ? parseInt(position, 10) : this.props.positionX.get('value');
23 | const y = axis === 'y' ? parseInt(position, 10) : this.props.positionY.get('value');
24 | this.props.setPosition(name, x, y);
25 | }
26 |
27 | render() {
28 | const { labelX, labelY, positionX, positionY, xMin, xMax, yMin, yMax } = this.props;
29 | return (
30 |
31 |
32 |
35 |
36 |
37 |
40 |
41 |
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/config/settings/HomeDirectionDebugInfo.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import CustomPropTypes from '../../utils/PropTypes';
5 |
6 | export default class HomeDirectionDebugInfo extends Component {
7 | static propTypes = {
8 | parameters: ImmutablePropTypes.contains({
9 | numberOfPanels: PropTypes.number.isRequired,
10 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | }).isRequired,
14 | setPosition: PropTypes.func.isRequired,
15 | setVisibleOn: PropTypes.func.isRequired,
16 | }
17 |
18 | shouldComponentUpdate(nextProps) {
19 | return !this.props.parameters.equals(nextProps.parameters);
20 | }
21 |
22 | render() {
23 | const {
24 | setPosition,
25 | setVisibleOn,
26 | } = this.props;
27 | const {
28 | numberOfPanels,
29 | positionX,
30 | positionY,
31 | visibleOn,
32 | } = this.props.parameters;
33 |
34 | return (
35 |
36 |
39 |
42 |
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/config/parameters/VisibleOn.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Checkbox from 'react-toolbox/lib/checkbox';
3 | import Label from '../../components/Label';
4 | import CustomPropTypes from '../../utils/PropTypes';
5 | import classNames from 'classnames';
6 | import styles from './VisibleOn.css';
7 |
8 | export default class ParameterPanels extends Component {
9 | static propTypes = {
10 | name: PropTypes.string.isRequired,
11 | numberOfPanels: PropTypes.number.isRequired,
12 | setVisibleOn: PropTypes.func.isRequired,
13 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | }
15 |
16 | _onChange(index, value) {
17 | const visibleOn = value ?
18 | this.props.visibleOn.get('value') | Math.pow(2, index) :
19 | this.props.visibleOn.get('value') ^ Math.pow(2, index);
20 |
21 | this.props.setVisibleOn(this.props.name, visibleOn);
22 | }
23 |
24 | render() {
25 | const { numberOfPanels, visibleOn } = this.props;
26 | const classes = classNames({
27 | modified: visibleOn.get('value') !== visibleOn.get('originalValue'),
28 | }, styles.base);
29 |
30 | return (
31 |
32 |
33 |
34 |
35 |
36 | {[...Array(numberOfPanels)].map((_, i) =>
37 |
38 |
43 |
44 | )}
45 |
46 |
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/preview/FlightMode.js:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react';
2 | import StringPreview from './StringPreview';
3 |
4 | export default class FlightMode extends StringPreview {
5 | static propTypes = {
6 | ...StringPreview.propTypes,
7 | vehicleType: PropTypes.number.isRequired,
8 | flightMode: PropTypes.number.isRequired,
9 | }
10 |
11 | content() {
12 | const { flightMode, vehicleType } = this.props;
13 |
14 | switch (vehicleType) {
15 | case 0:
16 | switch (flightMode) {
17 | case 0: return 'STAB';
18 | case 1: return 'ACRO';
19 | case 2: return 'ALTH';
20 | case 3: return 'AUTO';
21 | case 4: return 'GUID';
22 | case 5: return 'LOIT';
23 | case 6: return 'RETL';
24 | case 7: return 'CIRC';
25 | case 8: return 'POSI';
26 | case 9: return 'LAND';
27 | case 10: return 'OFLO';
28 | case 11: return 'DRIF';
29 | case 13: return 'SPRT';
30 | case 14: return 'FLIP';
31 | case 15: return 'ATUN';
32 | case 16: return 'POSH';
33 | case 17: return 'BRAK';
34 | default: return 'unknown';
35 | }
36 | case 1:
37 | switch (flightMode) {
38 | case 0: return 'MANU';
39 | case 1: return 'CIRC';
40 | case 2: return 'STAB';
41 | case 3: return 'TRNG';
42 | case 4: return 'ACRO';
43 | case 5: return 'FBWA';
44 | case 6: return 'FBWB';
45 | case 7: return 'CRUI';
46 | case 8: return 'ATUN';
47 | case 10: return 'AUTO';
48 | case 11: return 'RETL';
49 | case 12: return 'LOIT';
50 | case 15: return 'GUID';
51 | case 16: return 'INIT';
52 | default: return 'unknown';
53 | }
54 | default: return 'unknown';
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import AppBar from 'react-toolbox/lib/app_bar';
3 | import { Layout, NavDrawer, Panel, List, ListItem, IconButton } from 'react-toolbox';
4 | import styles from './App.css';
5 |
6 | export default class App extends Component {
7 |
8 | static propTypes = {
9 | children: PropTypes.element.isRequired
10 | };
11 |
12 | constructor(props) {
13 | super(props);
14 |
15 | this.state = {
16 | showDrawer: false,
17 | };
18 | }
19 |
20 | _toggleDrawer = () => {
21 | this.setState({ ...this.state, showDrawer: !this.state.showDrawer });
22 | }
23 |
24 | render() {
25 | return (
26 |
27 |
28 |
29 |
35 |
41 |
42 |
43 |
44 |
45 |
46 | PlayUAV OSD Configurator
47 |
48 |
49 | {this.props.children}
50 |
51 | {
52 | (() => {
53 | if (process.env.NODE_ENV !== 'production') {
54 | const DevTools = require('./DevTools');
55 | return ;
56 | }
57 | })()
58 | }
59 |
60 |
61 | );
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/config/Config.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import ParametersModule from './ParametersModule';
3 | import Snackbar from 'react-toolbox/lib/snackbar';
4 | import Sidebar from './Sidebar';
5 | import Preview from '../preview/Preview';
6 |
7 |
8 | export default class Index extends Component {
9 | state = {
10 | errorMessage: '',
11 | infoMessage: '',
12 | }
13 |
14 | _showErrorMessage = (error) => {
15 | this.setState({ ...this.state, errorMessage: error });
16 | }
17 |
18 | _showInfoMessage = (message) => {
19 | this.setState({ ...this.state, infoMessage: message });
20 | }
21 |
22 | _closeInfoSnackbar = () => {
23 | this.setState({ ...this.state, infoMessage: '' });
24 | }
25 |
26 | _closeErrorSnackbar = () => {
27 | this.setState({ ...this.state, errorMessage: '' });
28 | }
29 |
30 | render() {
31 | return (
32 |
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/config/settings/Time.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import SimpleSettings from './SimpleSettings';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class Time extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | fontSize: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | hAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | numberOfPanels: PropTypes.number.isRequired,
13 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | type: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
17 | }).isRequired,
18 | setFontSize: PropTypes.func.isRequired,
19 | setHAlignment: PropTypes.func.isRequired,
20 | setPosition: PropTypes.func.isRequired,
21 | setType: PropTypes.func.isRequired,
22 | setVisibleOn: PropTypes.func.isRequired,
23 | }
24 |
25 | shouldComponentUpdate(nextProps) {
26 | return !this.props.parameters.equals(nextProps.parameters);
27 | }
28 |
29 | _setType = (type) => {
30 | this.props.setType('time', type);
31 | }
32 |
33 | render() {
34 | const { type } = this.props.parameters;
35 | const typeOptions = [
36 | { value: 0, label: 'power on' },
37 | { value: 1, label: 'last heartbeat' },
38 | { value: 2, label: 'armed' },
39 | ];
40 |
41 | return (
42 |
43 |
46 |
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/data/icons/medium.js:
--------------------------------------------------------------------------------
1 | /* eslint max-len: 0 */
2 |
3 | const shape = [
4 | [0x0, 0x0, 0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x38, 0x7c, 0xfe, 0x0, 0x0, 0x0], // GPS
5 | [0x0, 0x0, 0x0, 0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0], // HDOP
6 | [0x0, 0x0, 0x0, 0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0], // TIME
7 | [0xf8, 0xf8, 0xfe, 0xff, 0xff, 0xef, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0x0], // WP_DISTANCE
8 | [0x7e, 0xff, 0xfe, 0x70, 0x70, 0x38, 0x1c, 0x1c, 0x38, 0x70, 0x70, 0xfe, 0xff, 0x7e], // TOTAL_TRIP
9 | [0xe0, 0xe0, 0xe0, 0xf8, 0xf8, 0xf8, 0xf8, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe], // RSSI
10 | [0x10, 0x38, 0x38, 0x7c, 0x1fe, 0x7c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x10], // LINK_QUALITY
11 | [0x0, 0x0, 0x0, 0x24, 0x7e, 0xff, 0xff, 0xff, 0x7e, 0x24, 0x0, 0x0, 0x0, 0x0], // HOME_DISTANCE
12 | ];
13 |
14 | const outline = [
15 | [0x0, 0x0, 0xc7, 0xbb, 0x7d, 0x6d, 0x7d, 0xbb, 0xc7, 0xd7, 0xbb, 0x1, 0x0, 0x0], // GPS
16 | [0x0, 0x0, 0x0, 0xc7, 0xab, 0x6d, 0x1, 0x6d, 0xab, 0xc7, 0x0, 0x0, 0x0, 0x0], // HDOP
17 | [0x0, 0x0, 0x0, 0xc7, 0xab, 0x6d, 0x61, 0x7d, 0xbb, 0xc7, 0x0, 0x0, 0x0, 0x0], // TIME
18 | [0x7, 0x57, 0x51, 0x54, 0x45, 0x50, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x1f], // WP_DISTANCE
19 | [0x81, 0x7e, 0x41, 0xaf, 0xaf, 0xd7, 0xeb, 0xeb, 0xd7, 0xaf, 0xaf, 0x41, 0x7e, 0x81], // TOTAL_TRIP
20 | [0x1f, 0x5f, 0x5f, 0x47, 0x57, 0x57, 0x57, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x0], // RSSI
21 | [0xee, 0xd6, 0xd6, 0x92, 0x55, 0xba, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xee], // LINK_QUALITY
22 | [0x80, 0x80, 0x80, 0xdb, 0xa5, 0x42, 0x7e, 0x42, 0xa5, 0xdb, 0x80, 0x80, 0x80, 0x80], // HOME_DISTANCE
23 | ];
24 |
25 | const dimensions = { width: 8, height: 14 };
26 |
27 | function getData(index) {
28 | return { shape: shape[index], outline: outline[index].map((b) => (~b >>> 0) & Math.pow(2, dimensions.width) - 1) };
29 | }
30 |
31 | export default { dimensions, getData };
32 |
--------------------------------------------------------------------------------
/src/utils/units.js:
--------------------------------------------------------------------------------
1 | const speedMultiplier = [3.6, 2.23];
2 | const speedUnitsLong = ['KM/H', 'Mi/H'];
3 | const distanceMultiplyer = [1.0, 3.28];
4 | const distanceDivider = [1000, 5280];
5 | const unitsShort = ['M', 'F'];
6 | const unitsLong = ['KM', 'Mi'];
7 |
8 | function longDistanceUnits(units) {
9 | return unitsLong[units];
10 | }
11 |
12 | function shortDistanceUnits(units) {
13 | return unitsShort[units];
14 | }
15 |
16 | function speedUnits(units) {
17 | return speedUnitsLong[units];
18 | }
19 |
20 | function convertSpeed(speed, units) {
21 | const unitsString = speedUnitsLong[units];
22 | const convertedSpeed = convertSpeedWithoutUnits(speed, units);
23 |
24 | return `${convertedSpeed}${unitsString}`;
25 | }
26 |
27 | function convertSpeedWithoutUnits(speed, units) {
28 | const convert = speedMultiplier[units];
29 | return speed * convert;
30 | }
31 |
32 | function convertDistance(distance, units) {
33 | const convertedDistance = convertDistanceWithoutUnits(distance, units);
34 | const divider = distanceDivider[units];
35 | let unitsString = '';
36 | let distanceString = '';
37 |
38 | if (distance < divider) {
39 | unitsString = unitsShort[units];
40 | distanceString = convertedDistance.toFixed(0);
41 | } else {
42 | unitsString = unitsLong[units];
43 | distanceString = convertedDistance.toFixed(2);
44 | }
45 |
46 | return `${distanceString}${unitsString}`;
47 | }
48 |
49 | function convertDistanceWithoutUnits(distance, units) {
50 | const multiplier = distanceMultiplyer[units];
51 | const divider = distanceDivider[units];
52 |
53 | const correctedDistance = distance * multiplier;
54 |
55 | if (correctedDistance >= divider) {
56 | return correctedDistance / divider;
57 | }
58 | return correctedDistance;
59 | }
60 |
61 | export default {
62 | convertDistance,
63 | convertSpeed,
64 | convertDistanceWithoutUnits,
65 | convertSpeedWithoutUnits,
66 | longDistanceUnits,
67 | shortDistanceUnits,
68 | speedUnits
69 | };
70 |
--------------------------------------------------------------------------------
/src/pixler/actions.js:
--------------------------------------------------------------------------------
1 | export const EMPTY = 'pixler/empty';
2 | export const SHAPE = 'pixler/shape';
3 | export const OUTLINE = 'pixler/outline';
4 | export const CLEAR = 'pixler/clear';
5 | export const INVERT_OUTLINE = 'pixler/invert_outline';
6 | export const LOAD_CHARACTER = 'pixler/load_character';
7 | export const LOAD_ICON = 'pixler/load_icon';
8 | export const MIRROR = 'pixler/mirror';
9 | export const SET_FONT_SIZE = 'pixler/set_font_size';
10 | export const SET_OUTLINE = 'pixler/set_outline';
11 | export const SET_PIXEL = 'pixler/set_pixel';
12 | export const SET_SHAPE = 'pixler/set_shape';
13 | export const SHIFT_DOWN = 'pixler/shift_down';
14 | export const SHIFT_LEFT = 'pixler/shift_left';
15 | export const SHIFT_RIGHT = 'pixler/shift_right';
16 | export const SHIFT_UP = 'pixler/shift_up';
17 |
18 | export function clear() {
19 | return { type: CLEAR };
20 | }
21 |
22 | export function invertOutline() {
23 | return { type: INVERT_OUTLINE };
24 | }
25 |
26 | export function loadCharacter(character) {
27 | return { type: LOAD_CHARACTER, payload: character };
28 | }
29 |
30 | export function loadIcon(icon) {
31 | return { type: LOAD_ICON, payload: icon };
32 | }
33 |
34 | export function mirror() {
35 | return { type: MIRROR };
36 | }
37 |
38 | export function setFontSize(fontSize) {
39 | return { type: SET_FONT_SIZE, payload: fontSize };
40 | }
41 |
42 | export function setOutline(outline) {
43 | return { type: SET_OUTLINE, payload: outline };
44 | }
45 |
46 | export function setPixel(row, column, pixelType) {
47 | return { type: SET_PIXEL, column, row, pixelType };
48 | }
49 |
50 | export function setShape(shape) {
51 | return { type: SET_SHAPE, payload: shape };
52 | }
53 |
54 | export function shiftDown() {
55 | return { type: SHIFT_DOWN };
56 | }
57 |
58 | export function shiftLeft() {
59 | return { type: SHIFT_LEFT };
60 | }
61 |
62 | export function shiftRight() {
63 | return { type: SHIFT_RIGHT };
64 | }
65 |
66 | export function shiftUp() {
67 | return { type: SHIFT_UP };
68 | }
69 |
--------------------------------------------------------------------------------
/src/config/settings/ClimbRate.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class ClimbRate extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | fontSize: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | numberOfPanels: PropTypes.number.isRequired,
12 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | }).isRequired,
16 | setFontSize: PropTypes.func.isRequired,
17 | setVisibleOn: PropTypes.func.isRequired,
18 | setPosition: PropTypes.func.isRequired,
19 | }
20 |
21 | shouldComponentUpdate(nextProps) {
22 | return !this.props.parameters.equals(nextProps.parameters);
23 | }
24 |
25 | render() {
26 | const {
27 | setFontSize,
28 | setPosition,
29 | setVisibleOn,
30 | } = this.props;
31 | const {
32 | fontSize,
33 | numberOfPanels,
34 | positionX,
35 | positionY,
36 | visibleOn,
37 | } = this.props.parameters;
38 |
39 | return (
40 |
41 |
44 |
45 |
46 |
47 |
50 |
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/preview/HomeDirectionDebugInfo.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import * as Canvas_types from '../utils/Canvas';
3 | import Canvas from '../utils/Canvas';
4 | import PreviewBase from './PreviewBase';
5 | import fonts from '../utils/fonts';
6 |
7 | export default class HomeDirectionDebugInfo extends PreviewBase {
8 | static propTypes = {
9 | panel: PropTypes.number.isRequired,
10 | positionX: PropTypes.number.isRequired,
11 | positionY: PropTypes.number.isRequired,
12 | visibleOn: PropTypes.number.isRequired,
13 | }
14 |
15 | draw() {
16 | const font = fonts.getFont(0);
17 | this.canvas.clear();
18 | const height = this.refs.canvas.height;
19 |
20 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
21 | var debugLines = [
22 | 'abs h.b. 68',
23 | 'rel h.b. -7',
24 | 'comp. b 75'
25 | ];
26 | var currentLineYOffset = 0;
27 | for (let debugLineIndex = 0; debugLineIndex < debugLines.length; debugLineIndex++) {
28 | // Get the current line
29 | var currentDebugLine = debugLines[debugLineIndex];
30 | // Draw the current line
31 | var debugLineStringPosition = Canvas.calculateStringPosition(currentDebugLine, 0, currentLineYOffset, Canvas_types.H_ALIGNMENT_LEFT, Canvas_types.V_ALIGNMENT_TOP, font);
32 | this.canvas.drawString(currentDebugLine, 0, currentLineYOffset, font);
33 | // Move Y position down one line
34 | currentLineYOffset += debugLineStringPosition.height;
35 | }
36 | }
37 | }
38 |
39 | render() {
40 | const { positionX, positionY } = this.props;
41 | const height = 90;
42 | const width = 300;
43 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
44 |
45 | return (
46 | !visible ?
47 | :
48 |
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/config/settings/Video.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Input from '../../components/Input';
4 | import Parameters from '../parameters';
5 | import Column from '../../components/Column';
6 | import CustomPropTypes from '../../utils/PropTypes';
7 |
8 | export default class Video extends Component {
9 | static propTypes = {
10 | parameters: ImmutablePropTypes.contains({
11 | maxPanels: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | offsetX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | offsetY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | units: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | videoMode: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | }).isRequired,
17 | setMaxPanels: PropTypes.func.isRequired,
18 | setOffset: PropTypes.func.isRequired,
19 | setUnits: PropTypes.func.isRequired,
20 | setMode: PropTypes.func.isRequired,
21 | }
22 |
23 | shouldComponentUpdate(nextProps) {
24 | return !this.props.parameters.equals(nextProps.parameters);
25 | }
26 |
27 | render() {
28 | const { setMode, setUnits, setMaxPanels, setOffset } = this.props;
29 | const { videoMode, units, offsetX, offsetY, maxPanels } = this.props.parameters;
30 |
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
43 |
44 |
50 |
51 |
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/pixler/Editor.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Pixel from './Pixel';
3 | import { EMPTY, OUTLINE, SHAPE } from './actions';
4 | import styles from './Editor.css';
5 | import fonts from '../utils/fonts';
6 | import { Card, CardText, CardTitle } from 'react-toolbox/lib/card';
7 |
8 | export default class Editor extends Component {
9 | static propTypes = {
10 | setPixel: PropTypes.func.isRequired,
11 | outline: PropTypes.arrayOf(PropTypes.number).isRequired,
12 | shape: PropTypes.arrayOf(PropTypes.number).isRequired,
13 | fontSize: PropTypes.number.isRequired,
14 | }
15 |
16 | _setPixel = (row, column, type) => {
17 | this.props.setPixel(row, column, type);
18 | }
19 |
20 | render() {
21 | const { height, width } = fonts.getFont(this.props.fontSize).dimensions;
22 |
23 | const pixels = [...Array(height)].map((_, row) => {
24 | const shape = this.props.shape[row];
25 | const outline = this.props.outline[row];
26 |
27 | const rowPixels = [...Array(width)].map((__, i) => {
28 | const column = width - i - 1;
29 | const key = `${row}${column}`;
30 | let type = null;
31 | if (shape & outline & Math.pow(2, column)) {
32 | type = OUTLINE;
33 | } else if (shape & Math.pow(2, column)) {
34 | type = SHAPE;
35 | } else {
36 | type = EMPTY;
37 | }
38 |
39 | return ();
40 | });
41 |
42 | return (
43 |
44 | {rowPixels}
45 |
{row + 1}
46 |
47 | );
48 | });
49 |
50 | const columnNumbers = [...Array(width)].map((_, i) => (
51 | {i + 1}
52 | ));
53 |
54 | return (
55 |
56 |
60 |
61 |
62 | {columnNumbers}
63 |
64 | {pixels}
65 |
66 |
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/preview/Wind.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Canvas from '../utils/Canvas';
3 | import PreviewBase from './PreviewBase';
4 | import fonts from '../utils/fonts';
5 | import units from '../utils/units';
6 |
7 | export default class Wind extends PreviewBase {
8 | static propTypes = {
9 | panel: PropTypes.number.isRequired,
10 | positionX: PropTypes.number.isRequired,
11 | positionY: PropTypes.number.isRequired,
12 | units: PropTypes.number.isRequired,
13 | visibleOn: PropTypes.number.isRequired,
14 | windDirection: PropTypes.number.isRequired,
15 | windSpeed: PropTypes.number.isRequired,
16 | }
17 |
18 | draw() {
19 | this.canvas.clear();
20 | const height = this.refs.canvas.height;
21 | const posY = height / 2;
22 | const font = fonts.getFont(0);
23 |
24 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
25 | const windString = units.convertSpeed(this.props.windSpeed, this.props.units);
26 | const windPosition = Canvas.calculateStringPosition(windString, 20, posY, 0, 1, font);
27 | this.canvas.drawString(windString, windPosition.left, windPosition.top, font);
28 |
29 | this.canvas.save();
30 | this.canvas.translate(9, 9);
31 | this.canvas.rotate(this.props.windDirection * Math.PI / 180);
32 | this.canvas.translate(-9, -9);
33 | this.canvas.drawLine(6, 7, 9, 1);
34 | this.canvas.drawLine(9, 1, 12, 7);
35 | this.canvas.drawLine(9, 16, 9, 7, true);
36 | this.canvas.restore();
37 | }
38 | }
39 |
40 | render() {
41 | const { positionX, positionY } = this.props;
42 | const font = fonts.getFont(0);
43 | const windString = units.convertSpeed(this.props.windSpeed, this.props.units);
44 | const windPosition = Canvas.calculateStringPosition(windString, 0, 0, 0, 1, font);
45 | const width = windPosition.width + 20;
46 | const height = 18;
47 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
48 |
49 | return (
50 | !visible ?
51 | :
52 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/preview/StringPreview.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Canvas from '../utils/Canvas';
3 | import PreviewBase from './PreviewBase';
4 | import fonts from '../utils/fonts';
5 | import icons from '../utils/icons';
6 |
7 | export default class StringPreview extends PreviewBase {
8 | static propTypes = {
9 | fontSize: PropTypes.number.isRequired,
10 | hAlignment: PropTypes.number.isRequired,
11 | panel: PropTypes.number.isRequired,
12 | positionX: PropTypes.number.isRequired,
13 | positionY: PropTypes.number.isRequired,
14 | setPosition: PropTypes.func.isRequired,
15 | vAlignment: PropTypes.number,
16 | visibleOn: PropTypes.number.isRequired,
17 | }
18 |
19 | static defaultProps = {
20 | vAlignment: 0,
21 | }
22 |
23 | icon() {
24 | return null;
25 | }
26 |
27 | content() {
28 | return '';
29 | }
30 |
31 | draw() {
32 | const font = fonts.getFont(this.props.fontSize);
33 | const content = this.content();
34 | this.canvas.clear();
35 |
36 | if (content && (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
37 | const icon = this.icon();
38 | const hasIcon = icon !== null;
39 | const x = hasIcon ? font.dimensions.width + 2 : 0;
40 | if (hasIcon) {
41 | const iconFont = icons.getFont(this.props.fontSize);
42 | this.canvas.drawCharacter(icon, 0, 0, iconFont);
43 | }
44 | this.canvas.drawString(content, x, 0, font);
45 | }
46 | }
47 |
48 | render() {
49 | const { hAlignment, positionX, positionY, vAlignment } = this.props;
50 | const content = this.content();
51 | const font = fonts.getFont(this.props.fontSize);
52 | const hasIcon = this.icon() !== null;
53 | const position = Canvas.calculateStringPosition(
54 | content, positionX, positionY, hAlignment, vAlignment, font, 0, hasIcon);
55 |
56 | const visible = this.content() &&
57 | (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
58 |
59 | return (
60 | !visible ?
61 | :
62 |
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/test/util/fonts.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import fonts from '../../src/utils/fonts';
3 |
4 | describe('fonts', () => {
5 | describe('small', () => {
6 | it('should be loadable', () => {
7 | fonts.getFont(0);
8 | });
9 |
10 | it('should have proper dimensions', () => {
11 | const font = fonts.getFont(0);
12 | expect(font.dimensions).to.have.all.keys('height', 'width');
13 | });
14 |
15 | it('should have proper character data', () => {
16 | const font = fonts.getFont(0);
17 | const character = font.getData('a'.charCodeAt(0));
18 | expect(character).to.have.all.keys('outline', 'shape');
19 | expect(character.outline.length).to.equal(10);
20 | expect(character.shape.length).to.equal(10);
21 | });
22 | });
23 |
24 | describe('medium', () => {
25 | it('should be loadable', () => {
26 | fonts.getFont(1);
27 | });
28 |
29 | it('should have proper dimensions', () => {
30 | const font = fonts.getFont(1);
31 | expect(font.dimensions).to.have.all.keys('height', 'width');
32 | });
33 |
34 | it('should have proper character data', () => {
35 | const font = fonts.getFont(1);
36 | const character = font.getData('a'.charCodeAt(0));
37 | expect(character).to.have.all.keys('outline', 'shape');
38 | expect(character.outline.length).to.equal(14);
39 | expect(character.shape.length).to.equal(14);
40 | });
41 | });
42 |
43 | describe('large', () => {
44 | it('should be loadable', () => {
45 | fonts.getFont(2);
46 | });
47 |
48 | it('should have proper dimensions', () => {
49 | const font = fonts.getFont(2);
50 | expect(font.dimensions).to.have.all.keys('height', 'width');
51 | });
52 |
53 | it('should have proper character data', () => {
54 | const font = fonts.getFont(2);
55 | const character = font.getData('a'.charCodeAt(0));
56 | expect(character).to.have.all.keys('outline', 'shape');
57 | expect(character.outline.length).to.equal(18);
58 | expect(character.shape.length).to.equal(18);
59 | });
60 | });
61 |
62 | describe('invalid', () => {
63 | it('should throw an error', () => {
64 | const errorMessage = /trying to get unsupported font for size/;
65 | expect(fonts.getFont).to.throw(Error, errorMessage);
66 | });
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/src/preview/index.js:
--------------------------------------------------------------------------------
1 | import AbsoluteAltitude from './AbsoluteAltitude';
2 | import Alarms from './Alarms';
3 | import AltitudeScale from './AltitudeScale';
4 | import ArmState from './ArmState';
5 | import ArtificialHorizon from './ArtificialHorizon';
6 | import BatteryConsumed from './BatteryConsumed';
7 | import BatteryCurrent from './BatteryCurrent';
8 | import BatteryRemaining from './BatteryRemaining';
9 | import BatteryVoltage from './BatteryVoltage';
10 | import ClimbRate from './ClimbRate';
11 | import Compass from './Compass';
12 | import Efficiency from './Efficiency';
13 | import FlightMode from './FlightMode';
14 | import HomeLatitude from './HomeLatitude';
15 | import HomeLongitude from './HomeLongitude';
16 | import GpsHdop from './GpsHdop';
17 | import GpsLatitude from './GpsLatitude';
18 | import GpsLongitude from './GpsLongitude';
19 | import GpsStatus from './GpsStatus';
20 | import HomeDirection from './HomeDirection';
21 | import HomeDirectionDebugInfo from './HomeDirectionDebugInfo';
22 | import HomeDistance from './HomeDistance';
23 | import LinkQuality from './LinkQuality';
24 | import RCChannels from './RCChannels';
25 | import Radar from './Radar';
26 | import RelativeAltitude from './RelativeAltitude';
27 | import Rssi from './Rssi';
28 | import SpeedAir from './SpeedAir';
29 | import SpeedGround from './SpeedGround';
30 | import SpeedScale from './SpeedScale';
31 | import Throttle from './Throttle';
32 | import Time from './Time';
33 | import TotalTrip from './TotalTrip';
34 | import VarioGraph from './VarioGraph';
35 | import Watt from './Watt';
36 | import Wind from './Wind';
37 | import WpDistance from './WpDistance';
38 |
39 | export default {
40 | AbsoluteAltitude,
41 | Alarms,
42 | AltitudeScale,
43 | ArmState,
44 | ArtificialHorizon,
45 | BatteryConsumed,
46 | BatteryCurrent,
47 | BatteryRemaining,
48 | BatteryVoltage,
49 | ClimbRate,
50 | Compass,
51 | Efficiency,
52 | FlightMode,
53 | HomeLatitude,
54 | HomeLongitude,
55 | GpsHdop,
56 | GpsLatitude,
57 | GpsLongitude,
58 | GpsStatus,
59 | HomeDirection,
60 | HomeDirectionDebugInfo,
61 | HomeDistance,
62 | LinkQuality,
63 | RCChannels,
64 | Radar,
65 | RelativeAltitude,
66 | Rssi,
67 | SpeedAir,
68 | SpeedGround,
69 | SpeedScale,
70 | Throttle,
71 | Time,
72 | TotalTrip,
73 | VarioGraph,
74 | Watt,
75 | Wind,
76 | WpDistance,
77 | };
78 |
--------------------------------------------------------------------------------
/src/config/settings/AltitudeScale.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class AltitudeScale extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | numberOfPanels: PropTypes.number.isRequired,
11 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | scaleAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | scaleType: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | }).isRequired,
17 | setPosition: PropTypes.func.isRequired,
18 | setScaleAlignment: PropTypes.func.isRequired,
19 | setScaleType: PropTypes.func.isRequired,
20 | setVisibleOn: PropTypes.func.isRequired,
21 | }
22 |
23 | shouldComponentUpdate(nextProps) {
24 | return !this.props.parameters.equals(nextProps.parameters);
25 | }
26 |
27 | render() {
28 | const {
29 | setPosition, setScaleAlignment, setScaleType, setVisibleOn
30 | } = this.props;
31 |
32 | const {
33 | numberOfPanels, positionX, positionY, scaleAlignment, scaleType, visibleOn
34 | } = this.props.parameters;
35 |
36 | return (
37 |
38 |
39 |
40 |
41 |
42 |
45 |
46 |
49 |
52 |
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Playuav OSD Configurator
2 |
3 | A simple cross platform tool the playuav osd.
4 |
5 |
6 | [](https://travis-ci.org/TobiasBales/PlayuavOSDConfigurator)
7 | [](https://coveralls.io/github/TobiasBales/PlayuavOSDConfigurator?branch=master)
8 | [](https://codeclimate.com/github/TobiasBales/PlayuavOSDConfigurator)
9 |
10 | ## What does it look like?
11 | 
12 |
13 | ## Releases
14 |
15 | Can be found in the [chrome webstore](https://chrome.google.com/webstore/detail/playuav-osd-configurator/clledgfbcikcmblfhbkhjeoebioekcnb)
16 |
17 | ## Features
18 |
19 | #### Config
20 | The part to configure the osd
21 | * change settings
22 | * flash firmware
23 | * write/read settings to osd
24 | * write/read settings to file
25 | * reset settings to default
26 | * load settings from default.conf on startup (located in same directory as executable)
27 |
28 | #### Pixler
29 | Helper to make new icons/characters
30 | * Create glyphs for small/medium/large fonts
31 | * mirror image
32 | * invert mask (black/white handling)
33 | * move pixels around in frame
34 |
35 | ## Local setup
36 |
37 | ### Installing node
38 | The prefered way is using [ndenv](https://github.com/riywo/ndenv) and running node `5.9.0`
39 |
40 |
41 | ### Development
42 | - `npm install`
43 | - `npm start`
44 | - enable [chrome extension developer mode](chrome://extensions/)
45 | - load unpacked extension from `./src`
46 |
47 |
48 | ### Commit messages, ci, linting etc
49 | - Commit messages should follow the [angular commit message format](https://gist.github.com/stephenparish/9941e89d80e2bc58a153#format-of-the-commit-message)
50 | - Pull requests will be automatically built on circle ci
51 | - Run eslint against your changes (npm run lint)
52 |
53 | ## Warning
54 | This software comes with no guarantees, it has worked great for me so far but if you blow something up or brick your board, I might be willing to help you but no guarantees.
55 |
56 | ## Special thanks
57 | Special thanks go to the people who made/make the osd software/hardware and also the amazing people who made the electron-react-boilerplate which made the start of this project so much easier!
58 |
--------------------------------------------------------------------------------
/src/config/settings/SpeedScale.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class SpeedScale extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | numberOfPanels: PropTypes.number.isRequired,
11 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | scaleAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | scaleType: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | }).isRequired,
17 | setPosition: PropTypes.func.isRequired,
18 | setScaleAlignment: PropTypes.func.isRequired,
19 | setScaleType: PropTypes.func.isRequired,
20 | setVisibleOn: PropTypes.func.isRequired,
21 | }
22 |
23 | shouldComponentUpdate(nextProps) {
24 | return !this.props.parameters.equals(nextProps.parameters);
25 | }
26 |
27 | render() {
28 | const {
29 | setPosition, setScaleAlignment, setScaleType, setVisibleOn
30 | } = this.props;
31 | const {
32 | numberOfPanels, positionX, positionY, scaleAlignment, scaleType, visibleOn
33 | } = this.props.parameters;
34 |
35 | return (
36 |
37 |
38 |
41 |
42 |
43 |
46 |
47 |
50 |
53 |
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/config/settings/Attitude3d.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class Attitude3d extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | mapRadius: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | numberOfPanels: PropTypes.number.isRequired,
12 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | scale: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | }).isRequired,
17 | setPosition: PropTypes.func.isRequired,
18 | setRadius: PropTypes.func.isRequired,
19 | setScale: PropTypes.func.isRequired,
20 | setVisibleOn: PropTypes.func.isRequired,
21 | }
22 |
23 | shouldComponentUpdate(nextProps) {
24 | return !this.props.parameters.equals(nextProps.parameters);
25 | }
26 |
27 | render() {
28 | const {
29 | setPosition,
30 | setRadius,
31 | setScale,
32 | setVisibleOn,
33 | } = this.props;
34 | const {
35 | numberOfPanels,
36 | positionX,
37 | positionY,
38 | mapRadius,
39 | scale,
40 | visibleOn,
41 | } = this.props.parameters;
42 |
43 | return (
44 |
45 |
48 |
49 |
50 |
51 |
52 |
55 |
56 |
59 |
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/preview/HomeDirection.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import PreviewBase from './PreviewBase';
3 |
4 | export default class Wind extends PreviewBase {
5 | static propTypes = {
6 | heading: PropTypes.number.isRequired,
7 | homeBearing: PropTypes.number.isRequired,
8 | panel: PropTypes.number.isRequired,
9 | positionX: PropTypes.number.isRequired,
10 | positionY: PropTypes.number.isRequired,
11 | visibleOn: PropTypes.number.isRequired,
12 | }
13 |
14 | draw() {
15 | this.canvas.clear();
16 | const height = this.refs.canvas.height;
17 |
18 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
19 | const { homeBearing, heading } = this.props;
20 | const bearing = homeBearing - heading;
21 |
22 | this.canvas.save();
23 | this.canvas.translate(height / 2, height / 2);
24 | this.canvas.rotate(bearing * Math.PI / 180);
25 |
26 | this.canvas.drawLine(-2, -2, -2, 8);
27 | this.canvas.drawLine(-1, -2, -1, 8);
28 | this.canvas.drawLine(0, -2, 0, 8);
29 | this.canvas.drawLine(1, -2, 1, 8);
30 | this.canvas.drawLine(2, -2, 2, 8);
31 | this.canvas.drawLine(-5, -3, 5, -3);
32 | this.canvas.drawLine(-4, -4, 4, -4);
33 | this.canvas.drawLine(-3, -5, 3, -5);
34 | this.canvas.drawLine(-2, -6, 2, -6);
35 | this.canvas.drawLine(-1, -7, 1, -7);
36 | this.canvas.drawLine(0, -8, 0, -8);
37 |
38 | this.canvas.drawLine(-7, -2, 0, -9, false, true);
39 | this.canvas.drawLine(0, -9, 7, -2, false, true);
40 | this.canvas.drawLine(7, -2, 3, -2, false, true);
41 | this.canvas.drawLine(-7, -2, -3, -2, false, true);
42 | this.canvas.drawLine(3, -2, 3, 9, false, true);
43 | this.canvas.drawLine(-3, -2, -3, 9, false, true);
44 | this.canvas.drawLine(-3, 9, 3, 9, false, true);
45 | this.canvas.restore();
46 | }
47 | }
48 |
49 | render() {
50 | const { positionX, positionY } = this.props;
51 | const height = 20;
52 | const width = 20;
53 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
54 |
55 | return (
56 | !visible ?
57 | :
58 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/eventPage.js:
--------------------------------------------------------------------------------
1 | function startApplication() {
2 | const applicationStartTime = new Date().getTime();
3 |
4 | chrome.app.window.create('main.html', {
5 | id: 'main-window',
6 | frame: 'chrome',
7 | state: 'maximized',
8 | innerBounds: {
9 | minWidth: 700,
10 | minHeight: 625
11 | }
12 | }, (createdWindow) => {
13 | createdWindow.contentWindow.addEventListener('load', () => {
14 | // createdWindow.contentWindow.catch_startup_time(applicationStartTime);
15 | });
16 |
17 | createdWindow.onClosed.addListener(() => {
18 | // autoamtically close the port when application closes
19 | // save connectionId in separate variable before createdWindow.contentWindow is destroyed
20 | const connectionId = createdWindow.contentWindow.serial.connectionId;
21 |
22 | if (connectionId) {
23 | chrome.serial.disconnect(connectionId, (result) => {
24 | console.log(`SERIAL: Connection closed - ${result}`);
25 | });
26 | }
27 | });
28 | });
29 | }
30 |
31 | chrome.app.runtime.onLaunched.addListener(startApplication);
32 |
33 | chrome.runtime.onInstalled.addListener((details) => {
34 | if (details.reason === 'update') {
35 | const previousVersionArr = details.previousVersion.split('.');
36 | const currentVersionArr = chrome.runtime.getManifest().version.split('.');
37 |
38 | if (currentVersionArr[0] > previousVersionArr[0]) {
39 | chrome.storage.local.get('update_notify', (result) => {
40 | if (result.update_notify === 'undefined' || result.update_notify) {
41 | const manifest = chrome.runtime.getManifest();
42 | const message = chrome.i18n.getMessage(
43 | 'notifications_app_just_updated_to_version', [manifest.version]);
44 | const buttonTitle = chrome.i18n.getMessage('notifications_click_here_to_start_app');
45 | const options = {
46 | priority: 0,
47 | type: 'basic',
48 | title: manifest.name,
49 | message,
50 | iconUrl: '/images/icon_128.png',
51 | buttons: [{ title: buttonTitle }]
52 | };
53 |
54 | chrome.notifications.create('baseflight_update', options, () => {
55 | // empty
56 | });
57 | }
58 | });
59 | }
60 | }
61 | });
62 |
63 | chrome.notifications.onButtonClicked.addListener((notificationId, buttonIndex) => {
64 | if (notificationId === 'baseflight_update') {
65 | startApplication();
66 | }
67 | });
68 |
--------------------------------------------------------------------------------
/src/config/settings/Map.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class Map extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | fontSize: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | hAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | numberOfPanels: PropTypes.number.isRequired,
13 | radius: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | vAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | }).isRequired,
17 | setFontSize: PropTypes.func.isRequired,
18 | setHAlignment: PropTypes.func.isRequired,
19 | setRadius: PropTypes.func.isRequired,
20 | setVAlignment: PropTypes.func.isRequired,
21 | setVisibleOn: PropTypes.func.isRequired,
22 | }
23 |
24 | shouldComponentUpdate(nextProps) {
25 | return !this.props.parameters.equals(nextProps.parameters);
26 | }
27 |
28 | render() {
29 | const {
30 | fontSize, radius, vAlignment, hAlignment, visibleOn, numberOfPanels
31 | } = this.props.parameters;
32 | const {
33 | setFontSize, setRadius, setVAlignment, setHAlignment, setVisibleOn
34 | } = this.props;
35 |
36 | return (
37 |
38 |
39 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
52 |
55 |
56 |
59 |
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/preview/SpeedScale.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Canvas from '../utils/Canvas';
3 | import PreviewBase from './PreviewBase';
4 | import fonts from '../utils/fonts';
5 | import units from '../utils/units';
6 |
7 | export default class SpeedScale extends PreviewBase {
8 | static propTypes = {
9 | speedGround: PropTypes.number.isRequired,
10 | panel: PropTypes.number.isRequired,
11 | positionX: PropTypes.number.isRequired,
12 | positionY: PropTypes.number.isRequired,
13 | scaleAlignment: PropTypes.number.isRequired,
14 | scaleType: PropTypes.number.isRequired,
15 | speedAir: PropTypes.number.isRequired,
16 | units: PropTypes.number.isRequired,
17 | visibleOn: PropTypes.number.isRequired,
18 | }
19 |
20 | draw() {
21 | const font = fonts.getFont(0);
22 | this.canvas.clear();
23 |
24 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
25 | const { speedAir, speedGround, scaleAlignment } = this.props;
26 | const hAlignment = scaleAlignment === 0 ? 0 : 2;
27 | const prefix = this.props.scaleType === 0 ? 'GS' : 'AS';
28 | const prefixPosition = Canvas.calculateStringPosition(prefix, 0, 0, hAlignment, 0, font);
29 | const speedUnitString = units.speedUnits(this.props.units);
30 | const unitPosition = Canvas.calculateStringPosition(
31 | speedUnitString, 0, 0, hAlignment, 0, font);
32 | const speed = this.props.scaleType === 0 ? speedGround : speedAir;
33 | const posX = scaleAlignment === 0 ? 0 : 75;
34 | const posY = 50;
35 | const scaleSpeed = units.convertSpeedWithoutUnits(speed, this.props.units);
36 | this.canvas.drawVerticalScale(
37 | Math.round(scaleSpeed, 0),
38 | 60, scaleAlignment, posX, posY, 72, 10, 20, 5, 8, 11, 100, font);
39 | this.canvas.drawString(prefix, posX + prefixPosition.left, posY - 50, font);
40 | this.canvas.drawString(speedUnitString, posX + unitPosition.left, posY + 40, font);
41 | }
42 | }
43 |
44 | render() {
45 | const { positionX, positionY, scaleAlignment } = this.props;
46 | const xOffset = scaleAlignment === 0 ? 0 : 75;
47 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
48 |
49 | return (
50 | !visible ?
51 | :
52 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/config/settings/ArtificialHorizon.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class ArtificialHorizon extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | numberOfPanels: PropTypes.number.isRequired,
11 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | scale: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | type: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | }).isRequired,
17 | setPosition: PropTypes.func.isRequired,
18 | setScale: PropTypes.func.isRequired,
19 | setType: PropTypes.func.isRequired,
20 | setVisibleOn: PropTypes.func.isRequired,
21 | }
22 |
23 | shouldComponentUpdate(nextProps) {
24 | return !this.props.parameters.equals(nextProps.parameters);
25 | }
26 |
27 | _setType = (type) => {
28 | this.props.setType('artificialHorizon', type);
29 | }
30 |
31 | render() {
32 | const {
33 | setPosition,
34 | setScale,
35 | setVisibleOn,
36 | } = this.props;
37 | const {
38 | numberOfPanels,
39 | positionX,
40 | positionY,
41 | scale,
42 | type,
43 | visibleOn,
44 | } = this.props.parameters;
45 |
46 | const typeOptions = [{ value: 0, label: 'mission planner' }, { value: 1, label: 'simple ' }];
47 |
48 | return (
49 |
50 |
53 |
54 |
55 |
56 |
57 |
60 |
61 |
64 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/preview/AltitudeScale.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Canvas from '../utils/Canvas';
3 | import PreviewBase from './PreviewBase';
4 | import fonts from '../utils/fonts';
5 | import units from '../utils/units';
6 |
7 | export default class AltitudeScale extends PreviewBase {
8 | static propTypes = {
9 | absoluteAltitude: PropTypes.number.isRequired,
10 | panel: PropTypes.number.isRequired,
11 | positionX: PropTypes.number.isRequired,
12 | positionY: PropTypes.number.isRequired,
13 | relativeAltitude: PropTypes.number.isRequired,
14 | scaleAlignment: PropTypes.number.isRequired,
15 | scaleType: PropTypes.number.isRequired,
16 | units: PropTypes.number.isRequired,
17 | visibleOn: PropTypes.number.isRequired,
18 | }
19 |
20 | draw() {
21 | const font = fonts.getFont(0);
22 | this.canvas.clear();
23 |
24 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
25 | const { absoluteAltitude, relativeAltitude, scaleAlignment } = this.props;
26 | const hAlignment = scaleAlignment === 0 ? 0 : 2;
27 | const prefix = this.props.scaleType === 0 ? 'AAlt' : 'Alt';
28 | const prefixPosition = Canvas.calculateStringPosition(prefix, 0, 0, hAlignment, 0, font);
29 | const shortDistanceUnitString = units.shortDistanceUnits(this.props.units);
30 | const unitPosition = Canvas.calculateStringPosition(
31 | shortDistanceUnitString, 0, 0, hAlignment, 0, font);
32 | const altitude = this.props.scaleType === 0 ? absoluteAltitude : relativeAltitude;
33 | const posX = scaleAlignment === 0 ? 0 : 75;
34 | const posY = 50;
35 | const scaleAltitude = units.convertDistanceWithoutUnits(altitude, this.props.units);
36 | this.canvas.drawVerticalScale(
37 | Math.round(scaleAltitude, 0),
38 | 60, scaleAlignment, posX, posY, 72, 10, 20, 5, 8, 11, 10000, font);
39 | this.canvas.drawString(prefix, posX + prefixPosition.left, posY - 50, font);
40 | this.canvas.drawString(shortDistanceUnitString, posX + unitPosition.left, posY + 40, font);
41 | }
42 | }
43 |
44 | render() {
45 | const { positionX, positionY } = this.props;
46 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
47 |
48 | return (
49 | !visible ?
50 | :
51 |
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/config/settings/Serial.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 | import Input from '../../components/Input';
7 |
8 | export default class Serial extends Component {
9 | static propTypes = {
10 | parameters: ImmutablePropTypes.contains({
11 | baudRate: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | fcType: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | splashMillisecondsToShowValue: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | }).isRequired,
15 | setBaudRate: PropTypes.func.isRequired,
16 | setFcType: PropTypes.func.isRequired,
17 | setValue: PropTypes.func.isRequired,
18 | }
19 |
20 | shouldComponentUpdate(nextProps) {
21 | return !this.props.parameters.equals(nextProps.parameters);
22 | }
23 |
24 | _setValue(serial) {
25 | return (value) => {
26 | this.props.setValue('serial', serial, parseInt(value, 10));
27 | };
28 | }
29 |
30 | render() {
31 | const { baudRate, fcType, splashMillisecondsToShowValue } = this.props.parameters;
32 | const { setBaudRate, setFcType } = this.props;
33 | const fcTypeOptions = [
34 | { value: 0, label: 'apm/pixhawk' }, { value: 1, label: 'cc3d/revo' }
35 | ];
36 |
37 |
38 | const baudRateOptions = [
39 | { value: 1, label: '4800' }, { value: 2, label: '9600' },
40 | { value: 3, label: '19200' }, { value: 4, label: '38400' },
41 | { value: 5, label: '43000' }, { value: 6, label: '56000' },
42 | { value: '7', label: '57600' }, { value: 8, label: '115200' }
43 | ];
44 |
45 | return (
46 |
47 |
48 |
51 |
52 |
53 |
56 |
57 |
58 |
59 |
62 |
63 |
64 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/preview/ClimbRate.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Canvas from '../utils/Canvas';
3 | import PreviewBase from './PreviewBase';
4 | import fonts from '../utils/fonts';
5 |
6 | export default class ClimbRate extends PreviewBase {
7 | static propTypes = {
8 | climbRate: PropTypes.number.isRequired,
9 | fontSize: PropTypes.number.isRequired,
10 | panel: PropTypes.number.isRequired,
11 | positionX: PropTypes.number.isRequired,
12 | positionY: PropTypes.number.isRequired,
13 | visibleOn: PropTypes.number.isRequired,
14 | }
15 |
16 | static defaultProps = {
17 | vAlignment: 0,
18 | }
19 |
20 | draw() {
21 | const font = fonts.getFont(this.props.fontSize);
22 | const arrowLength = this.props.fontSize === 0 ? 6 : 8;
23 | this.canvas.clear();
24 |
25 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
26 | const xPos = 3;
27 | const yPos = this.refs.canvas.height / 2;
28 | this.canvas.drawString(this.content(), xPos + 5, yPos - font.dimensions.height / 2, font);
29 | if (this.props.climbRate > 0) {
30 | this.canvas.drawLine(xPos, yPos - arrowLength, xPos, yPos + arrowLength, true);
31 | this.canvas.drawLine(xPos - 3, yPos - arrowLength + 3, xPos, yPos - arrowLength);
32 | this.canvas.drawLine(xPos + 3, yPos - arrowLength + 3, xPos, yPos - arrowLength);
33 | } else if (this.props.climbRate < 0) {
34 | this.canvas.drawLine(xPos, yPos - arrowLength, xPos, yPos + arrowLength, true);
35 | this.canvas.drawLine(xPos - 3, yPos + arrowLength - 3, xPos, yPos + arrowLength);
36 | this.canvas.drawLine(xPos + 3, yPos + arrowLength - 3, xPos, yPos + arrowLength);
37 | }
38 | }
39 | }
40 |
41 | content() {
42 | return Math.abs(this.props.climbRate).toFixed(2);
43 | }
44 |
45 | render() {
46 | const { positionX, positionY } = this.props;
47 | const hAlignment = 0;
48 | const vAlignment = 1;
49 | const font = fonts.getFont(this.props.fontSize);
50 | const position = Canvas.calculateStringPosition(
51 | this.content(), positionX, positionY, hAlignment, vAlignment, font);
52 | const arrowLength = this.props.fontSize === 0 ? 6 : 8;
53 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
54 |
55 | return (
56 | !visible ?
57 | :
58 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/config/settings/Radar.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class Radar extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | homeRadius: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
11 | numberOfPanels: PropTypes.number.isRequired,
12 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | radius: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | wpRadius: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
17 | }).isRequired,
18 | setPosition: PropTypes.func.isRequired,
19 | setRadius: PropTypes.func.isRequired,
20 | setVisibleOn: PropTypes.func.isRequired,
21 | }
22 |
23 | shouldComponentUpdate(nextProps) {
24 | return !this.props.parameters.equals(nextProps.parameters);
25 | }
26 |
27 | render() {
28 | const {
29 | setPosition,
30 | setRadius,
31 | setVisibleOn,
32 | } = this.props;
33 | const {
34 | homeRadius,
35 | numberOfPanels,
36 | positionX,
37 | positionY,
38 | radius,
39 | visibleOn,
40 | wpRadius,
41 | } = this.props.parameters;
42 |
43 | return (
44 |
45 |
48 |
49 |
52 |
53 |
54 |
57 |
58 |
59 |
62 |
63 |
66 |
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/config/settings/SimpleSettings.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class SimpleSettings extends Component {
8 | static propTypes = {
9 | name: PropTypes.string,
10 | label: PropTypes.string,
11 | children: PropTypes.node,
12 | parameters: ImmutablePropTypes.contains({
13 | fontSize: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | hAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | numberOfPanels: PropTypes.number.isRequired,
16 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
17 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
18 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
19 | }).isRequired,
20 | setFontSize: PropTypes.func.isRequired,
21 | setHAlignment: PropTypes.func.isRequired,
22 | setPosition: PropTypes.func.isRequired,
23 | setVisibleOn: PropTypes.func.isRequired,
24 | }
25 |
26 | shouldComponentUpdate(nextProps) {
27 | return !this.props.parameters.equals(nextProps.parameters);
28 | }
29 |
30 | parameterModified(key) {
31 | return this.props.parameters.get(key) !== this.props.parameters.get(`base${key}`);
32 | }
33 |
34 | render() {
35 | const {
36 | children,
37 | setFontSize,
38 | setHAlignment,
39 | setPosition,
40 | setVisibleOn,
41 | } = this.props;
42 | const {
43 | fontSize,
44 | hAlignment,
45 | numberOfPanels,
46 | positionX,
47 | positionY,
48 | visibleOn,
49 | } = this.props.parameters;
50 |
51 | const name = this.props.name || this.name;
52 | const label = this.props.label || this.label || name;
53 |
54 | return (
55 |
56 |
59 |
60 |
61 |
62 |
63 |
66 |
67 | {children}
68 |
71 |
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/config/settings/Throttle.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import CustomPropTypes from '../../utils/PropTypes';
6 |
7 | export default class Throttle extends Component {
8 | static propTypes = {
9 | parameters: ImmutablePropTypes.contains({
10 | numberOfPanels: PropTypes.number.isRequired,
11 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | scaleEnabled: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | scaleType: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | }).isRequired,
17 | setPosition: PropTypes.func.isRequired,
18 | setScaleEnabled: PropTypes.func.isRequired,
19 | setScaleType: PropTypes.func.isRequired,
20 | setVisibleOn: PropTypes.func.isRequired,
21 | }
22 |
23 | shouldComponentUpdate(nextProps) {
24 | return !this.props.parameters.equals(nextProps.parameters);
25 | }
26 |
27 | _setScaleEnabled = (enabled) => {
28 | this.props.setScaleEnabled('throttle', enabled);
29 | }
30 |
31 | _setScaleType = (type) => {
32 | this.props.setScaleType('throttle', type);
33 | }
34 |
35 | render() {
36 | const { setPosition, setVisibleOn } = this.props;
37 | const {
38 | numberOfPanels, positionX, positionY, visibleOn, scaleEnabled, scaleType
39 | } = this.props.parameters;
40 | const scaleEnabledOptions = [
41 | { value: 0, label: 'number' }, { value: 1, label: 'scale' },
42 | ];
43 | const scaleTypeOptions = [
44 | { value: 0, label: 'vertical' }, { value: 1, label: 'horizontal' }
45 | ];
46 |
47 | return (
48 |
49 |
52 |
53 |
56 |
57 |
58 |
61 |
62 |
65 |
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/config/settings/index.js:
--------------------------------------------------------------------------------
1 | import AbsoluteAltitude from './AbsoluteAltitude';
2 | import Alarms from './Alarms';
3 | import AltitudeScale from './AltitudeScale';
4 | import ArmState from './ArmState';
5 | import ArtificialHorizon from './ArtificialHorizon';
6 | import Attitude3d from './Attitude3d';
7 | import BatteryConsumed from './BatteryConsumed';
8 | import BatteryCurrent from './BatteryCurrent';
9 | import BatteryRemaining from './BatteryRemaining';
10 | import BatteryVoltage from './BatteryVoltage';
11 | import ClimbRate from './ClimbRate';
12 | import Compass from './Compass';
13 | import Efficiency from './Efficiency';
14 | import FlightMode from './FlightMode';
15 | import HomeLatitude from './HomeLatitude';
16 | import HomeLongitude from './HomeLongitude';
17 | import Gps2Hdop from './Gps2Hdop';
18 | import Gps2Latitude from './Gps2Latitude';
19 | import Gps2Longitude from './Gps2Longitude';
20 | import Gps2Status from './Gps2Status';
21 | import GpsHdop from './GpsHdop';
22 | import GpsLatitude from './GpsLatitude';
23 | import GpsLongitude from './GpsLongitude';
24 | import GpsStatus from './GpsStatus';
25 | import HomeDirection from './HomeDirection';
26 | import HomeDirectionDebugInfo from './HomeDirectionDebugInfo';
27 | import HomeDistance from './HomeDistance';
28 | import LinkQuality from './LinkQuality';
29 | import RCChannels from './RCChannels';
30 | import Map from './Map';
31 | import Radar from './Radar';
32 | import RelativeAltitude from './RelativeAltitude';
33 | import Rssi from './Rssi';
34 | import Serial from './Serial';
35 | import SpeedAir from './SpeedAir';
36 | import SpeedGround from './SpeedGround';
37 | import SpeedScale from './SpeedScale';
38 | import Switching from './Switching';
39 | import Throttle from './Throttle';
40 | import Time from './Time';
41 | import TotalTrip from './TotalTrip';
42 | import VarioGraph from './VarioGraph';
43 | import Video from './Video';
44 | import Watt from './Watt';
45 | import Wind from './Wind';
46 | import WPDistance from './WPDistance';
47 |
48 | export default {
49 | AbsoluteAltitude,
50 | Alarms,
51 | AltitudeScale,
52 | ArmState,
53 | ArtificialHorizon,
54 | Attitude3d,
55 | BatteryConsumed,
56 | BatteryCurrent,
57 | BatteryRemaining,
58 | BatteryVoltage,
59 | ClimbRate,
60 | Compass,
61 | Efficiency,
62 | FlightMode,
63 | HomeLatitude,
64 | HomeLongitude,
65 | Gps2Hdop,
66 | Gps2Latitude,
67 | Gps2Longitude,
68 | Gps2Status,
69 | GpsHdop,
70 | GpsLatitude,
71 | GpsLongitude,
72 | GpsStatus,
73 | HomeDirection,
74 | HomeDirectionDebugInfo,
75 | HomeDistance,
76 | LinkQuality,
77 | RCChannels,
78 | Map,
79 | Radar,
80 | RelativeAltitude,
81 | Rssi,
82 | Serial,
83 | SpeedAir,
84 | SpeedScale,
85 | Switching,
86 | SpeedGround,
87 | Throttle,
88 | Time,
89 | TotalTrip,
90 | VarioGraph,
91 | Video,
92 | Watt,
93 | Wind,
94 | WPDistance,
95 | };
96 |
--------------------------------------------------------------------------------
/src/preview/Throttle.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Canvas from '../utils/Canvas';
3 | import PreviewBase from './PreviewBase';
4 | import fonts from '../utils/fonts';
5 |
6 | export default class ThrottlePreview extends PreviewBase {
7 | static propTypes = {
8 | panel: PropTypes.number.isRequired,
9 | positionX: PropTypes.number.isRequired,
10 | positionY: PropTypes.number.isRequired,
11 | scaleEnabled: PropTypes.number.isRequired,
12 | scaleType: PropTypes.number,
13 | throttle: PropTypes.number.isRequired,
14 | visibleOn: PropTypes.number.isRequired,
15 | }
16 |
17 | draw() {
18 | const font = fonts.getFont(0);
19 | this.canvas.clear();
20 |
21 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
22 | if (this.props.scaleEnabled) {
23 | const string = `${this.props.throttle.toFixed(0)}%`;
24 | const position = Canvas.calculateStringPosition(string, 0, 0, 2, 2, font);
25 | const posX = 75;
26 | const posY = 25;
27 | this.canvas.drawString(string, posX - position.width, posY, font);
28 | const posThY = Math.round(this.props.throttle * 0.5, 0);
29 | const posThX = posX - 25 + posThY;
30 |
31 | if (this.props.scaleType === 0) {
32 | this.canvas.drawFilledRectangle(posX + 3, posY + 25 - posThY, 5, posThY);
33 | this.canvas.drawLine(posX + 3, posY - 25, posX + 7, posY - 25);
34 | this.canvas.drawLine(posX + 3, posY + 25 - posThY, posX + 7, posY + 25 - posThY);
35 | this.canvas.drawLine(posX + 3, posY - 25, posX + 3, posY + 25 - posThY);
36 | this.canvas.drawLine(posX + 7, posY - 25, posX + 7, posY + 25 - posThY);
37 | } else {
38 | this.canvas.drawFilledRectangle(posX - 25, posY + 10, posThY, 5);
39 | this.canvas.drawLine(posThX, posY + 10, posX + 25, posY + 10);
40 | this.canvas.drawLine(posThX, posY + 14, posX + 25, posY + 14);
41 | this.canvas.drawLine(posX + 25, posY + 10, posX + 25, posY + 14);
42 | this.canvas.drawLine(posX - 25, posY + 10, posX - 25, posY + 14);
43 | }
44 | } else {
45 | const string = `thr ${this.props.throttle.toFixed(0)}%`;
46 | const position = Canvas.calculateStringPosition(string, 0, 0, 2, 2, font);
47 | const posX = 75;
48 | const posY = 25;
49 | this.canvas.drawString(string, posX - position.width, posY, font);
50 | }
51 | }
52 | }
53 |
54 | render() {
55 | const { positionX, positionY } = this.props;
56 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
57 |
58 | return (
59 | !visible ?
60 | :
61 |
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/preview/Radar.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import Canvas from '../utils/Canvas';
3 | import PreviewBase from './PreviewBase';
4 | import fonts from '../utils/fonts';
5 |
6 | export default class Radar extends PreviewBase {
7 | static propTypes = {
8 | heading: PropTypes.number.isRequired,
9 | homeBearing: PropTypes.number.isRequired,
10 | homeRadius: PropTypes.number.isRequired,
11 | panel: PropTypes.number.isRequired,
12 | positionX: PropTypes.number.isRequired,
13 | positionY: PropTypes.number.isRequired,
14 | radius: PropTypes.number.isRequired,
15 | visibleOn: PropTypes.number.isRequired,
16 | wpBearing: PropTypes.number.isRequired,
17 | wpNumber: PropTypes.number.isRequired,
18 | wpRadius: PropTypes.number.isRequired,
19 | }
20 |
21 | draw() {
22 | this.canvas.clear();
23 |
24 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
25 | const width = this.refs.canvas.width;
26 | const height = this.refs.canvas.height;
27 | const { homeRadius, radius, wpRadius } = this.props;
28 |
29 | const posX = width / 2;
30 | const posY = height / 2;
31 | this.canvas.drawCircle(posX, posY, radius, true);
32 | this.canvas.save();
33 | this.canvas.translate(width / 2, height / 2);
34 | this.canvas.rotate(this.props.heading * Math.PI / 180);
35 | this.canvas.translate(- width / 2, - height / 2);
36 | this.canvas.drawLine(posX, posY - 7, posX - 3, posY + 7, true);
37 | this.canvas.drawLine(posX, posY - 7, posX + 3, posY + 7, true);
38 | this.canvas.restore();
39 |
40 | const font = fonts.getFont(0);
41 | const homeString = 'H';
42 | const homeHeading = this.props.homeBearing;
43 | const homeX = posX + homeRadius * Math.sin(homeHeading * Math.PI / 180);
44 | const homeY = posY - homeRadius * Math.cos(homeHeading * Math.PI / 180);
45 | const homePosition = Canvas.calculateStringPosition(
46 | homeString, homeX, homeY, 1, 1, font);
47 | this.canvas.drawString(homeString, homePosition.left, homePosition.top, font);
48 |
49 | const wpString = this.props.wpNumber.toFixed(0);
50 | const wpHeading = this.props.wpBearing;
51 | const wpX = posX + wpRadius * Math.sin(wpHeading * Math.PI / 180);
52 | const wpY = posY - wpRadius * Math.cos(wpHeading * Math.PI / 180);
53 | const wpPosition = Canvas.calculateStringPosition(wpString, wpX, wpY, 1, 1, font);
54 | this.canvas.drawString(wpString, wpPosition.left, wpPosition.top, font);
55 | }
56 | }
57 |
58 | render() {
59 | const { positionX, positionY } = this.props;
60 | const radius = this.props.radius;
61 | const width = radius * 2 + 20;
62 | const height = radius * 2 + 20;
63 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
64 |
65 | return (
66 | !visible ?
67 | :
68 |
75 | );
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/app.global.css:
--------------------------------------------------------------------------------
1 | label, span, div, select, option, input[readonly] {
2 | -webkit-user-select: none;
3 | }
4 |
5 | html {
6 | font-size: 10px;
7 | }
8 |
9 | #root {
10 | height: 100%;
11 | }
12 |
13 | .column {
14 | display: inline-block;
15 | vertical-align: top;
16 | }
17 |
18 | .main {
19 | position: absolute;
20 | left: 0;
21 | top: 64px;
22 | right: 0;
23 | bottom: 0;
24 | }
25 |
26 | .parameters {
27 | position: absolute;
28 | right: 0px;
29 | padding: 5px;
30 | left: 400px;
31 | top: 0;
32 | display:block;
33 | bottom: 0;
34 | overflow-y: auto;
35 | }
36 |
37 | [data-react-toolbox= "dropdown"] label {
38 | white-space: nowrap;
39 | }
40 |
41 | .sidebar {
42 | padding: 5px;
43 | position: absolute;
44 | width: 400px;
45 | top: 5px;
46 | left: 5px;
47 | bottom: 0;
48 | overflow-y: auto;
49 | }
50 |
51 | .sidebar .panel label {
52 | display: inline-block;
53 | margin-right: 0.5rem;
54 | }
55 |
56 | .sidebar .connection {
57 | overflow: visible;
58 | margin: 5px 0;
59 | }
60 |
61 | .sidebar button {
62 | margin-right: 0.5rem;
63 | }
64 |
65 | .preview-card {
66 | overflow: visible;
67 | margin: 5px 0;
68 | }
69 |
70 | .preview {
71 | position: relative;
72 | overflow: hidden;
73 | }
74 |
75 | .preview img {
76 | width: 100%;
77 | height: 100%;
78 | }
79 |
80 | .preview .preview-widget {
81 | position: absolute;
82 | overflow: hidden;
83 | /*cursor: move;*/
84 | }
85 |
86 | /*.preview .preview-widget:hover {
87 | box-shadow: inset 0 0 5px white, 0 0 5px black;
88 | }*/
89 |
90 | .parameters .parameter-list {
91 | overflow: visible;
92 | padding: 5px;
93 | display: inline-block;
94 | width: 100%;
95 | vertical-align: top;
96 | }
97 |
98 | .parameters .parameter-list > div {
99 | overflow: visible;
100 | }
101 |
102 | @media(min-width: 1090px) {
103 | .parameters .parameter-list {
104 | width: 50%;
105 | }
106 | }
107 |
108 | @media(min-width: 1390px) {
109 | .parameters .parameter-list {
110 | width: 33%;
111 | }
112 | }
113 |
114 | @media(min-width: 1690px) {
115 | .parameters .parameter-list {
116 | width: 25%;
117 | }
118 | }
119 |
120 | @media(min-width: 1990px) {
121 | .parameters .parameter-list {
122 | width: 20%;
123 | }
124 | }
125 |
126 | .modified {
127 | background-color: #EEEEEE;
128 | }
129 |
130 | .label {
131 | line-height: 1.6rem;
132 | color: rgba(0, 0, 0, 0.26);
133 | font-size: 1.2rem;
134 | margin-bottom: 0.6rem;
135 | display: inline-block;
136 | }
137 |
138 | .material-icons {
139 | font-family: 'Material Icons';
140 | font-weight: normal;
141 | font-style: normal;
142 | font-size: 24px;
143 | /* Preferred icon size */
144 | display: inline-block;
145 | line-height: 1;
146 | text-transform: none;
147 | letter-spacing: normal;
148 | word-wrap: normal;
149 | /* Support for all WebKit browsers. */
150 | -webkit-font-smoothing: antialiased;
151 | /* Support for Safari and Chrome. */
152 | text-rendering: optimizeLegibility;
153 | /* Support for Firefox. */
154 | -moz-osx-font-smoothing: grayscale;
155 | /* Support for IE. */
156 | font-feature-settings: 'liga'; }
157 |
158 | [data-react-toolbox="input"] {
159 | padding: 2rem 1rem;
160 | }
161 |
162 | [data-react-toolbox="input"] label {
163 | left: 1rem;
164 | }
165 |
--------------------------------------------------------------------------------
/etc/dummy.xcodeproj/xcshareddata/xcschemes/scheme.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/etc/dummy.xcodeproj/xcshareddata/xcschemes/scheme copy.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/src/config/settings/LinkQuality.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Input from '../../components/Input';
3 | import Column from '../../components/Column';
4 | import Select from '../parameters/Select';
5 | import SimpleSettings from './SimpleSettings';
6 | import ImmutablePropTypes from 'react-immutable-proptypes';
7 | import CustomPropTypes from '../../utils/PropTypes';
8 |
9 | export default class LinkQuality extends Component {
10 | static propTypes = {
11 | parameters: ImmutablePropTypes.contains({
12 | fontSize: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | hAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | max: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | min: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | numberOfPanels: PropTypes.number.isRequired,
17 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
18 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
19 | raw: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
20 | type: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
21 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
22 | }).isRequired,
23 | setFontSize: PropTypes.func.isRequired,
24 | setHAlignment: PropTypes.func.isRequired,
25 | setMax: PropTypes.func.isRequired,
26 | setMin: PropTypes.func.isRequired,
27 | setPosition: PropTypes.func.isRequired,
28 | setRaw: PropTypes.func.isRequired,
29 | setType: PropTypes.func.isRequired,
30 | setVisibleOn: PropTypes.func.isRequired,
31 | }
32 |
33 | shouldComponentUpdate(nextProps) {
34 | return !this.props.parameters.equals(nextProps.parameters);
35 | }
36 |
37 | _setMin = (min) => {
38 | this.props.setMin('linkQuality', parseInt(min, 10));
39 | }
40 |
41 | _setMax = (max) => {
42 | this.props.setMax('linkQuality', parseInt(max, 10));
43 | }
44 |
45 | _setType = (type) => {
46 | this.props.setType('linkQuality', type);
47 | }
48 |
49 | _setRaw = (raw) => {
50 | this.props.setRaw('linkQuality', raw);
51 | }
52 |
53 | render() {
54 | const { max, min, raw, type } = this.props.parameters;
55 | const rawOptions = [
56 | { value: 0, label: 'percentage' }, { value: 1, label: 'raw value' }
57 | ];
58 | const typeOptions = [
59 | { value: 5, label: 'rc 5' }, { value: 6, label: 'rc 6' },
60 | { value: 7, label: 'rc 7' }, { value: 8, label: 'rc 8' },
61 | { value: 9, label: 'rc 9' }, { value: 10, label: 'rc 10' },
62 | { value: 11, label: 'rc 11' }, { value: 12, label: 'rc 12' },
63 | { value: 13, label: 'rc 13' }, { value: 14, label: 'rc 14' },
64 | { value: 15, label: 'rc 15' }, { value: 16, label: 'rc 16' }
65 | ];
66 | const minInput = 1000;
67 | const maxInput = 2000;
68 |
69 | return (
70 |
71 |
72 |
75 |
76 |
77 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | );
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/config/settings/Rssi.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import Input from '../../components/Input';
3 | import Column from '../../components/Column';
4 | import Select from '../parameters/Select';
5 | import SimpleSettings from './SimpleSettings';
6 | import ImmutablePropTypes from 'react-immutable-proptypes';
7 | import CustomPropTypes from '../../utils/PropTypes';
8 |
9 | export default class Rssi extends Component {
10 | static propTypes = {
11 | parameters: ImmutablePropTypes.contains({
12 | fontSize: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | hAlignment: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | max: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | min: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | numberOfPanels: PropTypes.number.isRequired,
17 | positionX: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
18 | positionY: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
19 | raw: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
20 | type: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
21 | visibleOn: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
22 | }).isRequired,
23 | setFontSize: PropTypes.func.isRequired,
24 | setHAlignment: PropTypes.func.isRequired,
25 | setMax: PropTypes.func.isRequired,
26 | setMin: PropTypes.func.isRequired,
27 | setPosition: PropTypes.func.isRequired,
28 | setRaw: PropTypes.func.isRequired,
29 | setType: PropTypes.func.isRequired,
30 | setVisibleOn: PropTypes.func.isRequired,
31 | }
32 |
33 | shouldComponentUpdate(nextProps) {
34 | return !this.props.parameters.equals(nextProps.parameters);
35 | }
36 |
37 | _setMin = (min) => {
38 | this.props.setMin('rssi', parseInt(min, 10));
39 | }
40 |
41 | _setMax = (max) => {
42 | this.props.setMax('rssi', parseInt(max, 10));
43 | }
44 |
45 | _setType = (type) => {
46 | this.props.setType('rssi', type);
47 | }
48 |
49 | _setRaw = (raw) => {
50 | this.props.setRaw('rssi', raw);
51 | }
52 |
53 | render() {
54 | const { max, min, raw, type } = this.props.parameters;
55 | const rawOptions = [
56 | { value: 0, label: 'percentage' }, { value: 1, label: 'raw value' }
57 | ];
58 | const typeOptions = [
59 | { value: 0, label: 'mavlink' }, { value: 5, label: 'rc 5' },
60 | { value: 6, label: 'rc 6' }, { value: 7, label: 'rc 7' },
61 | { value: 8, label: 'rc 8' }, { value: 9, label: 'rc 9' },
62 | { value: 10, label: 'rc 10' }, { value: 11, label: 'rc 11' },
63 | { value: 12, label: 'rc 12' }, { value: 13, label: 'rc 13' },
64 | { value: 14, label: 'rc 14' }, { value: 15, label: 'rc 15' },
65 | { value: 16, label: 'rc 16' }
66 | ];
67 | const minInput = type === 0 ? 0 : 1000;
68 | const maxInput = type === 0 ? 255 : 2000;
69 |
70 | return (
71 |
72 |
73 |
76 |
77 |
78 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | );
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* eslint strict: 0 */
2 | 'use strict';
3 |
4 | // const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const DefinePlugin = require('webpack/lib/DefinePlugin');
6 | const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');
7 | const NoErrorsPlugin = require('webpack/lib/NoErrorsPlugin');
8 | const OccurenceOrderPlugin = require('webpack/lib/optimize/OccurenceOrderPlugin');
9 | const path = require('path');
10 | const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin');
11 | const WebpackNotifierPlugin = require('webpack-notifier');
12 | const fs = require('fs');
13 | const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, 'app', 'manifest.json')));
14 |
15 | const production = Boolean(process.env.PRODUCTION);
16 |
17 | const plugins = production ?
18 | [
19 | new OccurenceOrderPlugin(),
20 | new DefinePlugin({
21 | __DEV__: false,
22 | 'process.env': {
23 | NODE_ENV: JSON.stringify('production')
24 | },
25 | VERSION: JSON.stringify(manifest.version),
26 | }),
27 | new UglifyJsPlugin({
28 | compressor: {
29 | screw_ie8: true,
30 | warnings: false
31 | }
32 | }),
33 | // new ExtractTextPlugin('style.css', { allChunks: true })
34 | ] :
35 | [
36 | new HotModuleReplacementPlugin(),
37 | new NoErrorsPlugin(),
38 | new WebpackNotifierPlugin(),
39 | new DefinePlugin({
40 | __DEV__: true,
41 | 'process.env': {
42 | NODE_ENV: JSON.stringify('development')
43 | },
44 | VERSION: JSON.stringify(manifest.version),
45 | })
46 | ];
47 |
48 | module.exports = {
49 | debug: !production,
50 | devTool: production ? 'source-map' : 'cheap-module-eval-source-map',
51 | entry: {
52 | index: './src/index',
53 | eventPage: './src/eventPage',
54 | },
55 | module: {
56 | loaders: [{
57 | test: /\.jsx?$/,
58 | loaders: ['babel-loader'],
59 | exclude: /node_modules/
60 | }, {
61 | test: /\.json$/,
62 | loader: 'json-loader'
63 | }, {
64 | test: /\.scss$/,
65 | loaders: ['style', 'css?modules', 'sass'],
66 | include: path.resolve(__dirname, './node_modules'),
67 | }, {
68 | test: /\.css$/,
69 | loaders: ['style', 'css?modules'],
70 | include: path.resolve(__dirname, './node_modules'),
71 | }, {
72 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
73 | loader: 'url-loader?limit=10000&mimetype=application/font-woff'
74 | }, {
75 | test: /\.png$/,
76 | loader: 'url-loader?limit=10000&mimetype=application/font-woff'
77 | }, {
78 | test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
79 | loader: 'file-loader'
80 | }, {
81 | test: /\.global\.css$/,
82 | loaders: ['style-loader', 'css-loader'],
83 | exclude: path.resolve(__dirname, './node_modules'),
84 | }, {
85 | test: /^((?!\.global).)*\.css$/,
86 | loaders: [
87 | // ExtractTextPlugin.extract(
88 | 'style-loader',
89 | production ?
90 | 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]' :
91 | 'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
92 | ],
93 | exclude: path.resolve(__dirname, './node_modules'),
94 | // )
95 | }
96 | ]
97 | },
98 | output: {
99 | path: path.join(__dirname, 'app', 'dist'),
100 | filename: '[name].js',
101 | libraryTarget: 'var',
102 | publicPath: './dist/',
103 | },
104 | resolve: {
105 | extensions: ['', '.js', '.jsx', '.scss'],
106 | packageMains: ['webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main']
107 | },
108 | node: {
109 | child_process: 'empty',
110 | fs: 'empty',
111 | net: 'empty',
112 | },
113 | plugins,
114 | externals: [
115 | ]
116 | };
117 |
--------------------------------------------------------------------------------
/src/preview/RCChannels.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import * as Canvas_types from '../utils/Canvas';
3 | import Canvas from '../utils/Canvas';
4 | import PreviewBase from './PreviewBase';
5 | import fonts from '../utils/fonts';
6 |
7 | export default class RCChannelsPreview extends PreviewBase {
8 | static propTypes = {
9 | panel: PropTypes.number.isRequired,
10 | positionX: PropTypes.number.isRequired,
11 | positionY: PropTypes.number.isRequired,
12 | visibleOn: PropTypes.number.isRequired,
13 | }
14 |
15 | draw() {
16 | const font = fonts.getFont(0);
17 | this.canvas.clear();
18 |
19 | if ((this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0) {
20 | var default_channel_count = 8;
21 |
22 | // Just some sample channel values, possibly not realistic
23 | var channelValues = [ 1524, 1520, 1750, 1250, 1400, 1800, 1950, 1515];
24 |
25 | var bar_x_offset = 85;
26 | var currentLineYOffset = 0;
27 | // TODO: Could make bar width configurable in firmware, but we'll see if anyone cares first
28 | var bar_width = 40;
29 | for (let chanNumber = 1; chanNumber <= default_channel_count; chanNumber++) {
30 | var currentChannelPwmValue = channelValues[chanNumber-1];
31 | // Draw the channel label and numeric value ('CH 2 1250')
32 | var curChanString = 'CH ' + chanNumber + ' ' + currentChannelPwmValue + ' ';
33 | var chanStringPosition = Canvas.calculateStringPosition(curChanString, 0, currentLineYOffset, Canvas_types.H_ALIGNMENT_LEFT, Canvas_types.V_ALIGNMENT_TOP, font);
34 | this.canvas.drawString(curChanString, 0, currentLineYOffset, font);
35 |
36 | // Draw the bar to the side of the text that represents the current numeric value
37 | var barXPos = bar_x_offset;
38 | var barYPos = currentLineYOffset;
39 | var barHeight = chanStringPosition.height;
40 | var isBlack = false;
41 | var isOutline = true;
42 | this.canvas.drawRectangle(barXPos, barYPos, bar_width, chanStringPosition.height, isBlack, isOutline);
43 |
44 | // Normalize 1000-2000 PPM value to 0-1000
45 | var normalized_channel_value = currentChannelPwmValue - 1000;
46 | if (normalized_channel_value < 0) {
47 | normalized_channel_value = 0;
48 | } else if (normalized_channel_value > 1000) {
49 | normalized_channel_value = 1000;
50 | }
51 |
52 | // X offset position for the stripe relative to the bar
53 | var stripe_offset_x = (normalized_channel_value * bar_width) / 1000;
54 |
55 | // Draw vertical stripe marking channel value on bar rectangle
56 | var stripeXPos = barXPos + stripe_offset_x;
57 | this.canvas.drawLine(stripeXPos - 1, barYPos, stripeXPos - 1, barYPos + chanStringPosition.height, false, true);
58 | this.canvas.drawLine(stripeXPos, barYPos, stripeXPos, barYPos + chanStringPosition.height, false, true);
59 | this.canvas.drawLine(stripeXPos + 1, barYPos, stripeXPos + 1, barYPos + chanStringPosition.height, false, true);
60 |
61 | // Move Y position down one line
62 | currentLineYOffset += chanStringPosition.height;
63 | }
64 | }
65 | }
66 |
67 | render() {
68 | const { positionX, positionY } = this.props;
69 | const visible = (this.props.visibleOn & Math.pow(2, this.props.panel)) !== 0;
70 |
71 | return (
72 | !visible ?
73 | :
74 |
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/config/settings/Switching.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import ImmutablePropTypes from 'react-immutable-proptypes';
3 | import Parameters from '../parameters';
4 | import Column from '../../components/Column';
5 | import Input from '../../components/Input';
6 | import CustomPropTypes from '../../utils/PropTypes';
7 |
8 | export default class Switching extends Component {
9 | static propTypes = {
10 | parameters: ImmutablePropTypes.contains({
11 | panelChannel: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
12 | panelMode: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
13 | panelValue: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
14 | videoChannel: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
15 | videoMode: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
16 | videoValue: CustomPropTypes.value(PropTypes.number.isRequired).isRequired,
17 | }).isRequired,
18 | setChannel: PropTypes.func.isRequired,
19 | setMode: PropTypes.func.isRequired,
20 | setValue: PropTypes.func.isRequired,
21 | }
22 |
23 | shouldComponentUpdate(nextProps) {
24 | return !this.props.parameters.equals(nextProps.parameters);
25 | }
26 |
27 | _setChannel(key, value) {
28 | this.props.setChannel('switching', key, value);
29 | }
30 |
31 | _setValue(key, value) {
32 | this.props.setValue('switching', key, parseInt(value, 10));
33 | }
34 |
35 | render() {
36 | const {
37 | panelChannel, panelMode, panelValue, videoChannel, videoMode, videoValue
38 | } = this.props.parameters;
39 | const channelOptions = [
40 | { value: 0, label: 'disabled' }, { value: 1, label: 'rc 1' },
41 | { value: 2, label: 'rc 2' }, { value: 3, label: 'rc 3' },
42 | { value: 4, label: 'rc 4' }, { value: 5, label: 'rc 5' },
43 | { value: 6, label: 'rc 6' }, { value: 7, label: 'rc 7' },
44 | { value: 8, label: 'rc 8' }, { value: 9, label: 'rc 9' },
45 | { value: 10, label: 'rc 10' }, { value: 11, label: 'rc 11' },
46 | { value: 12, label: 'rc 12' }, { value: 13, label: 'rc 13' },
47 | { value: 14, label: 'rc 14' }, { value: 15, label: 'rc 15' },
48 | { value: 16, label: 'rc 16' }
49 | ];
50 |
51 | const modeOptions = [
52 | { value: 0, label: 'cycle' }, { value: 1, label: 'switch' }
53 | ];
54 |
55 | return (
56 |
57 |
58 |
61 |
62 |
63 |
66 |
67 |
68 |
71 |
72 |
73 |
76 |
77 |
78 | { panelMode.get('value') === 0 ?
79 |
82 | : null }
83 |
84 |
85 | { videoMode.get('value') === 0 ?
86 |
89 | : null }
90 |
91 |
92 | );
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/config/reducer.js:
--------------------------------------------------------------------------------
1 | import eeprom from '../utils/eeprom';
2 |
3 | import {
4 | ALARM_ENABLED, ALARM_VALUE, AS_BASE_STATE, BAUD_RATE, CHANNEL,
5 | FC_TYPE, FONT_SIZE, H_ALIGNMENT, MAX, MAX_PANELS, MIN, OFFSET,
6 | PARAMS_FROM_EEPROM, POSITION, RADIUS, RAW, SCALE, SCALE_ALIGNMENT,
7 | SCALE_ENABLED, SCALE_TYPE, TYPE, UNITS, V_ALIGNMENT, VALUE,
8 | MODE, VISIBLE_ON,
9 | } from './actions';
10 |
11 | function setAsBaseState(state, baseState = null) {
12 | return state.updateIn(['originalState'], () => baseState || state);
13 | }
14 |
15 | const initialState = setAsBaseState(eeprom.toParameters(eeprom.defaultEEPROM));
16 |
17 | export default function parameters(state = initialState, action) {
18 | const parameterName = action.parameter;
19 | const payload = action.payload;
20 |
21 | switch (action.type) {
22 | case ALARM_ENABLED:
23 | return state.setIn([parameterName, `${payload.alarm}Enabled`], payload.enabled);
24 | case ALARM_VALUE:
25 | return state.setIn([parameterName, `${payload.alarm}Value`], payload.value);
26 | case AS_BASE_STATE:
27 | return setAsBaseState(state, action.state);
28 | case BAUD_RATE:
29 | return state.setIn([parameterName, 'baudRate'], payload);
30 | case CHANNEL:
31 | return state.setIn([parameterName, `${payload.key}Channel`], payload.channel);
32 | case FC_TYPE:
33 | return state.updateIn([parameterName, 'fcType'], () => action.fcType);
34 | case FONT_SIZE:
35 | return state.setIn([parameterName, 'fontSize'], payload);
36 | case H_ALIGNMENT:
37 | return state.setIn([parameterName, 'hAlignment'], payload);
38 | case MAX:
39 | return state.setIn([parameterName, 'max'], payload);
40 | case MAX_PANELS:
41 | return state.setIn([parameterName, 'maxPanels'], payload);
42 | case MIN:
43 | return state.setIn([parameterName, 'min'], payload);
44 | case OFFSET:
45 | return state
46 | .setIn([parameterName, 'offsetX'], payload.x)
47 | .setIn([parameterName, 'offsetY'], payload.y);
48 | case PARAMS_FROM_EEPROM: {
49 | let eepromData = action.eepromData;
50 | for (let i = eepromData.length; i > 0; i--) {
51 | if (eepromData[i - 1] !== 0) {
52 | eepromData = eepromData.slice(0, i);
53 | break;
54 | }
55 | }
56 |
57 | const defaultEEPROM = eeprom.defaultEEPROM;
58 | if (eepromData.length < defaultEEPROM.length) {
59 | const missingData = defaultEEPROM.slice(eepromData.length, defaultEEPROM.length);
60 | eepromData = eepromData.concat(missingData);
61 | }
62 | return setAsBaseState(eeprom.toParameters(eepromData));
63 | }
64 | case POSITION:
65 | return state
66 | .setIn([parameterName, 'positionX'], payload.x)
67 | .setIn([parameterName, 'positionY'], payload.y);
68 | case RADIUS:
69 | return state.updateIn([parameterName, action.key], () => action.radius);
70 | case RAW:
71 | return state.updateIn([parameterName, 'raw'], () => action.raw);
72 | case SCALE:
73 | return state.updateIn([parameterName, 'scale'], () => action.scale);
74 | case SCALE_ALIGNMENT:
75 | return state.updateIn([parameterName, 'scaleAlignment'], () => action.scaleAlignment);
76 | case SCALE_ENABLED:
77 | return state.updateIn([parameterName, 'scaleEnabled'], () => action.scaleEnabled);
78 | case SCALE_TYPE:
79 | return state.updateIn([parameterName, 'scaleType'], () => action.scaleType);
80 | case TYPE:
81 | return state.updateIn([parameterName, 'type'], () => action.typeValue);
82 | case UNITS:
83 | return state.updateIn([parameterName, 'units'], () => action.units);
84 | case V_ALIGNMENT:
85 | return state.updateIn([parameterName, 'vAlignment'], () => action.vAlignment);
86 | case VALUE:
87 | return state.updateIn([parameterName, `${action.key}Value`], () => action.value);
88 | case MODE:
89 | return state.updateIn([parameterName, `${action.prefix}Mode`], () => action.mode);
90 | case VISIBLE_ON:
91 | return state.setIn([parameterName, 'visibleOn'], payload);
92 | default:
93 | return state;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/test/pixler/reducer.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import Immutable from 'immutable';
3 | import pixler from '../../src/pixler/reducer';
4 | import {
5 | EMPTY, SHAPE, OUTLINE, CLEAR, MIRROR, SET_FONT_SIZE, SET_OUTLINE, SET_SHAPE, SET_PIXEL,
6 | } from '../../src/pixler/actions';
7 |
8 |
9 | describe('reducers', () => {
10 | describe('pixler', () => {
11 | it('should handle initial state', () => {
12 | const state = pixler(undefined, {});
13 | expect(state).keys('fontSize', 'outline', 'shape');
14 | expect(state.get('outline')).to.equal(
15 | Immutable.List.of(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
16 | expect(state.get('shape')).to.equal(Immutable.List.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
17 | });
18 |
19 | it('should handle CLEAR', () => {
20 | const state = Immutable.fromJS({
21 | outline: [1, 1, 1, 1, 1, 1, 1, 1],
22 | shape: [1, 1, 1, 1, 1, 1, 1, 1],
23 | fontSize: 0,
24 | });
25 | const newState = pixler(state, { type: CLEAR });
26 | const zeroList = Immutable.List.of(0, 0, 0, 0, 0, 0, 0, 0);
27 | const ffList = Immutable.List.of(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
28 |
29 | expect(newState.get('outline')).to.equal(ffList);
30 | expect(newState.get('shape')).to.equal(zeroList);
31 | });
32 |
33 | it('should handle MIRROR', () => {
34 | const state = Immutable.fromJS({
35 | outline: [0x80, 0x8, 0, 0, 0, 0, 0, 0],
36 | shape: [0x81, 0x38, 0, 0, 0, 0, 0, 0],
37 | fontSize: 0,
38 | });
39 | const newState = pixler(state, { type: MIRROR });
40 |
41 | expect(newState.get('outline')).to.equal(Immutable.List.of(0x1, 0x10, 0, 0, 0, 0, 0, 0));
42 | expect(newState.get('shape')).to.equal(Immutable.List.of(0x81, 0x1c, 0, 0, 0, 0, 0, 0));
43 | });
44 |
45 | it('should handle SET_FONT_SIZE', () => {
46 | const state = pixler(undefined, { type: SET_FONT_SIZE, payload: 1 });
47 |
48 | expect(state.get('fontSize')).to.equal(1);
49 | expect(state.get('outline')).to.have.size(14);
50 | expect(state.get('shape')).to.have.size(14);
51 |
52 | const newState = pixler(state, { type: SET_FONT_SIZE, payload: 0 });
53 |
54 | expect(newState.get('fontSize')).to.equal(0);
55 | expect(newState.get('outline')).to.have.size(10);
56 | expect(newState.get('shape')).to.have.size(10);
57 |
58 | const lastState = pixler(newState, { type: SET_FONT_SIZE, payload: 2 });
59 |
60 | expect(lastState.get('fontSize')).to.equal(2);
61 | expect(lastState.get('outline')).to.have.size(18);
62 | expect(lastState.get('shape')).to.have.size(18);
63 | });
64 |
65 | it('should handle SET_OUTLINE', () => {
66 | const state = pixler(undefined, {
67 | type: SET_OUTLINE, payload: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
68 | });
69 | expect(state.get('outline')).to.equal(
70 | Immutable.List.of(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
71 | });
72 |
73 | it('should handle SET_SHAPE', () => {
74 | const state = pixler(undefined, {
75 | type: SET_SHAPE, payload: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
76 | });
77 | expect(state.get('shape')).to.equal(
78 | Immutable.List.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
79 | });
80 |
81 | it('should handle SET_PIXEL', () => {
82 | const state = pixler(undefined, { type: SET_PIXEL, pixelType: SHAPE, row: 0, column: 0 });
83 | it('for SHAPE', () => {
84 | expect(state.get('shape')).to.equal(Immutable.List.of(0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0));
85 | expect(state.get('outline')).to.equal(
86 | Immutable.List.of(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
87 | });
88 |
89 | const newState = pixler(state, { type: SET_PIXEL, pixelType: OUTLINE, row: 0, column: 1 });
90 | it('for OUTLINE', () => {
91 | expect(newState.get('shape')).to.equal(Immutable.List.of(0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0));
92 | expect(newState.get('outline')).to.equal(
93 | Immutable.List.of(0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
94 | });
95 |
96 | const lastState = pixler(newState, { type: SET_PIXEL, pixelType: EMPTY, row: 0, column: 0 });
97 | it('for EMPTY', () => {
98 | expect(lastState.get('shape')).to.equal(Immutable.List.of(0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0));
99 | expect(lastState.get('outline')).to.equal(
100 | Immutable.List.of(0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff));
101 | });
102 | });
103 | });
104 | });
105 |
--------------------------------------------------------------------------------
/etc/dummy.xcodeproj/xcshareddata/xcschemes/blablabla.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
95 |
101 |
102 |
103 |
104 |
106 |
107 |
110 |
111 |
112 |
--------------------------------------------------------------------------------