setSrc(props.camera.placeholder)}
+ onClick={() => props.handleClick(props.index)}
+ />
+ )
+}
- imageLoad(){
- this.setState({loaded: true});
- }
+CameraFrame.defaultProps = defaultProps;
- render() {
- return (
-
this.props.handleClick(this.props.index) : () => {}}/>
- );
- }
-}
\ No newline at end of file
+export default CameraFrame;
\ No newline at end of file
diff --git a/surface/src/components/Camera/CameraInterface.ts b/surface/src/components/Camera/CameraInterface.ts
index a63faed..39e1d4c 100644
--- a/surface/src/components/Camera/CameraInterface.ts
+++ b/surface/src/components/Camera/CameraInterface.ts
@@ -2,4 +2,5 @@ export default interface CameraInterface{
feed: string
placeholder: string
name: string
+ angle: number
}
\ No newline at end of file
diff --git a/surface/src/components/Camera/camera.json b/surface/src/components/Camera/camera.json
index bc5b8f8..e075564 100644
--- a/surface/src/components/Camera/camera.json
+++ b/surface/src/components/Camera/camera.json
@@ -2,21 +2,37 @@
{
"name": "Camera 1",
"feed": "http://192.168.1.3:8090/cam0",
- "placeholder": "./img/test_cam1.png"
+ "placeholder": "./img/test_cam1.png",
+ "angle": 0
},
{
"name": "Camera 2",
- "feed": "http://192.168.1.4:8090/cam1",
- "placeholder": "./img/test_cam2.png"
+ "feed": "http://192.168.1.3:8090/cam1",
+ "placeholder": "./img/test_cam2.png",
+ "angle": 90
},
{
"name": "Camera 3",
- "feed": "http://192.168.1.2:8090/test.mjpg",
- "placeholder": "./img/test_cam3.png"
+ "feed": "http://192.168.1.4:8090/cam0",
+ "placeholder": "./img/test_cam3.png",
+ "angle": 90
+ },
+ {
+ "name": "Camera 4",
+ "feed": "http://192.168.1.4:8090/cam1",
+ "placeholder": "./img/test_cam4.png",
+ "angle": 0
+ },
+ {
+ "name": "Camera 4",
+ "feed": "http://192.168.1.4:8090/cam2",
+ "placeholder": "./img/test_cam4.png",
+ "angle": 90
},
{
"name": "Camera 4",
- "feed": "./img/test_cam4.png",
- "placeholder": "./img/test_cam4.png"
+ "feed": "http://192.168.1.4:8090/cam3",
+ "placeholder": "./img/test_cam4.png",
+ "angle": 270
}
]
diff --git a/surface/src/components/IMU/IMU.tsx b/surface/src/components/IMU/IMU.tsx
index dafe386..3b0eecb 100644
--- a/surface/src/components/IMU/IMU.tsx
+++ b/surface/src/components/IMU/IMU.tsx
@@ -55,6 +55,7 @@ const animate = (s: T) => {
const IMU: React.FC = () => {
const ref = React.useRef
(null);
+ const [offsets, setOffsets] = React.useState>([0.0, 0.0, 0.0]);
ipcRenderer.on('imu', (e, data) => {
orientation = data;
@@ -82,7 +83,14 @@ const IMU: React.FC = () => {
}, [])
return(
-
+
+
+
+
)
}
diff --git a/surface/src/components/Log/Log.tsx b/surface/src/components/Log/Log.tsx
index d4d33ec..f28eb6c 100644
--- a/surface/src/components/Log/Log.tsx
+++ b/surface/src/components/Log/Log.tsx
@@ -4,37 +4,30 @@ import {ipcRenderer} from 'electron';
import './Log.scss';
import * as channels from './channels';
-interface State{
- items: Array
-}
-export default class Log extends React.Component<{}, State> {
- constructor(props){
- super(props);
+const Log: React.FC = () => {
+ const [items, setItems] = React.useState>([]);
- this.state = {items: []};
- }
- componentDidMount(){
+ React.useEffect(() => {
ipcRenderer.send('logger', 'ready');
channels.default.map(channel => {
ipcRenderer.on(channel, (e, data: LogItem) => {
- this.addItem(data);
+ setItems(old => [...old, data]);
});
});
- }
-
- addItem(item: LogItem){
- this.setState({items: [...this.state.items, item]});
- }
+ }, [])
- render() {
- return (
-
- {this.state.items.map((item, index) => {
- return - {`${item.timestamp ? item.timestamp : ''} (${item.process}): ${item.text}`}
- })}
-
- );
- }
+ return(
+
+ {items.map((item, index) => {
+ return -
+ {`${item.timestamp ? item.timestamp : ''} (${item.process})`}: {item.text}
+
+ })}
+
+ )
}
+
+export default Log;
+
diff --git a/surface/src/components/Log/LogItem.ts b/surface/src/components/Log/LogItem.ts
index 878124c..d4fac50 100644
--- a/surface/src/components/Log/LogItem.ts
+++ b/surface/src/components/Log/LogItem.ts
@@ -1,14 +1,21 @@
export interface LogItem{
timestamp: string
process: string
- text: string
+ text: string,
+ color?: string
}
-export default function(proc: string, text: string) {
+export const LOG_NORMAL = '#f0ffff';
+export const LOG_WARNING = '#FFC900';
+export const LOG_ERROR = '#FF3600';
+export const LOG_SUCCESS = '#7AFF33';
+
+export default (proc: string, text: string, color = LOG_NORMAL) => {
let i: LogItem = {
timestamp: new Date().toISOString().substr(14, 5),
process: proc,
- text: text
+ text: text,
+ color: color
}
return i;
diff --git a/surface/src/components/Log/channels.ts b/surface/src/components/Log/channels.ts
index 427f0c7..d80d32e 100644
--- a/surface/src/components/Log/channels.ts
+++ b/surface/src/components/Log/channels.ts
@@ -5,6 +5,10 @@ export const GAMEPAD = 'gamepad_listener';
export const SERVO = 'servo';
export const THRUSTERS = 'thruster_listener';
export const IMU = 'imu_listener';
+export const COM = 'com_sender';
+export const GENERAL = 'general';
+export const RAILCAP = 'rail_capture';
+export const RAILPROC = 'rail_process';
export default [
SET_IP,
@@ -13,5 +17,9 @@ export default [
GAMEPAD,
SERVO,
THRUSTERS,
- IMU
+ IMU,
+ COM,
+ GENERAL,
+ RAILCAP,
+ RAILPROC
]
\ No newline at end of file
diff --git a/surface/src/components/Mosaic/Mosaic.scss b/surface/src/components/Mosaic/Mosaic.scss
new file mode 100644
index 0000000..f16a9c0
--- /dev/null
+++ b/surface/src/components/Mosaic/Mosaic.scss
@@ -0,0 +1,3 @@
+.mosaic-btn{
+
+}
\ No newline at end of file
diff --git a/surface/src/components/Mosaic/Mosaic.tsx b/surface/src/components/Mosaic/Mosaic.tsx
new file mode 100644
index 0000000..f48c324
--- /dev/null
+++ b/surface/src/components/Mosaic/Mosaic.tsx
@@ -0,0 +1,33 @@
+import { ipcRenderer } from 'electron';
+import * as React from 'react';
+import './Mosaic.scss';
+
+const Mosaic: React.FC = () => {
+ const [counter, setCounter] = React.useState(0);
+
+ React.useEffect(() => {
+ if(counter == 4){
+ ipcRenderer.send('process_frames');
+ }
+ }, [counter])
+
+ return(
+
+
Mosaic Photo {counter}/5
+
+
+ )
+}
+
+export default Mosaic;
\ No newline at end of file
diff --git a/surface/src/components/Profiles/CoM/CoM.scss b/surface/src/components/Profiles/CoM/CoM.scss
new file mode 100644
index 0000000..beb7209
--- /dev/null
+++ b/surface/src/components/Profiles/CoM/CoM.scss
@@ -0,0 +1,16 @@
+.com-container{
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-rows: auto repeat(3, 1fr);
+ gap: 5px;
+}
+
+.com-amount{
+ width: 100%;
+ text-align: center;
+}
+
+.com-title{
+ @extend .com-amount;
+ border-bottom: solid;
+}
\ No newline at end of file
diff --git a/surface/src/components/Profiles/CoM/CoM.tsx b/surface/src/components/Profiles/CoM/CoM.tsx
new file mode 100644
index 0000000..80a0fdc
--- /dev/null
+++ b/surface/src/components/Profiles/CoM/CoM.tsx
@@ -0,0 +1,47 @@
+import { ipcRenderer } from 'electron';
+import * as React from 'react';
+import { GamepadParams } from '../../../../electron/gamepad';
+import Slider from '../../Slider/Slider';
+import './CoM.scss';
+
+interface Props {
+ vals: Array
+}
+
+const CoM: React.FC = (props) => {
+ const [values, setValues] = React.useState>([0.0, 0.0, 0.0]);
+
+ React.useEffect(() => {
+ let temp = [...values];
+ temp = props.vals.map((val, idx) => val);
+ setValues(temp);
+
+ ipcRenderer.send('com_send', temp);
+ }, props.vals);
+
+ return(
+
+
CoM
+ {values.map((val, idx) => {
+ return(
+
{
+ let temp = [...values];
+ temp[idx] = val;
+ setValues(temp);
+
+ ipcRenderer.send('com_send', temp);
+ }}
+ />
+ )
+ })}
+
+ )
+}
+
+export default CoM;
\ No newline at end of file
diff --git a/surface/src/components/Profiles/Compensator/Compensator.scss b/surface/src/components/Profiles/Compensator/Compensator.scss
new file mode 100644
index 0000000..a8abe86
--- /dev/null
+++ b/surface/src/components/Profiles/Compensator/Compensator.scss
@@ -0,0 +1,16 @@
+.compensator-container{
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-rows: auto repeat(3, 1fr);
+ gap: 5px;
+}
+
+.compensator-amount{
+ width: 100%;
+ text-align: center;
+}
+
+.compensator-title{
+ @extend .compensator-amount;
+ border-bottom: solid;
+}
\ No newline at end of file
diff --git a/surface/src/components/Profiles/Compensator/Compensator.tsx b/surface/src/components/Profiles/Compensator/Compensator.tsx
new file mode 100644
index 0000000..3111d61
--- /dev/null
+++ b/surface/src/components/Profiles/Compensator/Compensator.tsx
@@ -0,0 +1,60 @@
+import { ipcRenderer } from 'electron';
+import { GamepadParams } from '../../../../electron/gamepad';
+import * as React from 'react';
+import Slider from '../../Slider/Slider';
+import './Compensator.scss';
+
+interface Props {
+ vals: Array
+}
+
+const Compensator: React.FC = (props) => {
+ const [values, setValues] = React.useState>([0.0, 0.0, 0.0]);
+
+ React.useEffect(() => {
+ let temp = [...values];
+ temp = props.vals.map((val, idx) => val);
+ setValues(temp);
+
+ let params: GamepadParams = {
+ type: 'trim',
+ values: temp
+ }
+
+ console.log(temp);
+
+ ipcRenderer.send('compensator', params);
+ console.log('sent')
+ }, [props.vals]);
+
+ return(
+
+
Buoyancy
+ {values.map((val, idx) => {
+ return(
+
{
+ let temp = [...values];
+ temp[idx] = val;
+ setValues(temp);
+
+ let params: GamepadParams = {
+ type: 'trim',
+ values: temp
+ }
+
+ ipcRenderer.send('compensator', params);
+ }}
+ />
+ )
+ })}
+
+ )
+}
+
+export default Compensator;
\ No newline at end of file
diff --git a/surface/src/components/Profiles/Profiles.scss b/surface/src/components/Profiles/Profiles.scss
new file mode 100644
index 0000000..bb45d83
--- /dev/null
+++ b/surface/src/components/Profiles/Profiles.scss
@@ -0,0 +1,8 @@
+.profile-container{
+ display: flex;
+ flex-direction: column;
+}
+
+.profile-container select{
+ margin: 10px;
+}
\ No newline at end of file
diff --git a/surface/src/components/Profiles/Profiles.tsx b/surface/src/components/Profiles/Profiles.tsx
new file mode 100644
index 0000000..507a465
--- /dev/null
+++ b/surface/src/components/Profiles/Profiles.tsx
@@ -0,0 +1,41 @@
+import * as React from 'react';
+import './Profiles.scss';
+import configs from './configs.json';
+import ThrustTweaker from './Tweaker/ThrustTweaker';
+import Compensator from './Compensator/Compensator';
+import CoM from './CoM/CoM';
+
+interface Config {
+ tweaker: Array
+ com: Array
+ compensator: Array
+}
+
+const Profiles: React.FC = () => {
+ const [config, setConfig] = React.useState('test_config');
+
+ const updateConfig = (which) => setConfig(which);
+
+ return(
+
+
+
+
+
+
+
+ )
+}
+
+export default Profiles;
\ No newline at end of file
diff --git a/surface/src/components/Profiles/Tweaker/ThrustTweaker.scss b/surface/src/components/Profiles/Tweaker/ThrustTweaker.scss
new file mode 100644
index 0000000..21f254e
--- /dev/null
+++ b/surface/src/components/Profiles/Tweaker/ThrustTweaker.scss
@@ -0,0 +1,43 @@
+.tweaker-container{
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-template-rows: auto auto repeat(3, 1fr) auto auto;
+ grid-auto-flow: column;
+ gap: 5px;
+}
+
+.tweaker-title{
+ width: 100%;
+ text-align: center;
+ border-bottom: solid;
+ grid-column: 1 / 3;
+}
+
+.tweaker-subtitle{
+ @extend .tweaker-title;
+ grid-column: span 1;
+}
+
+.tweaker-subtitle-left{
+ @extend .tweaker-title;
+ grid-column: 1;
+ grid-row: 2;
+}
+
+.tweaker-subtitle-right{
+ @extend .tweaker-title;
+ grid-column: 2;
+ grid-row: 2;
+}
+
+.reverse-container{
+ grid-row: 6;
+ grid-column: 1 / 3;
+ display: flex;
+ justify-content: center;
+}
+
+.fine-slider{
+ grid-row: 7;
+ grid-column: 1/3;
+}
\ No newline at end of file
diff --git a/surface/src/components/Profiles/Tweaker/ThrustTweaker.tsx b/surface/src/components/Profiles/Tweaker/ThrustTweaker.tsx
new file mode 100644
index 0000000..f5626d6
--- /dev/null
+++ b/surface/src/components/Profiles/Tweaker/ThrustTweaker.tsx
@@ -0,0 +1,184 @@
+import { ipcRenderer } from 'electron';
+import * as React from 'react';
+import Slider from '../../Slider/Slider';
+import './ThrustTweaker.scss';
+import {GamepadParams} from '../../../../electron/gamepad';
+import Toggle from '../../Toggle/Toggle';
+
+const TRANSLATION_MAX = 1.0;
+const ROTATION_MAX = 1.0;
+const TRANSLATION_STEP = 0.05;
+const ROTATION_STEP = 0.05;
+
+interface Props {
+ vals: Array
+ fine: number
+ reverse: boolean
+ mode: boolean
+ lockout: boolean
+}
+
+const ThrustTweaker: React.FC = (props) => {
+ const [values, setValues] = React.useState([1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
+ const [fine, setFine] = React.useState(1.041)
+ const [reverse, setReverse] = React.useState(false);
+ const [mode, setMode] = React.useState(true);
+ const [lockout, setLockout] = React.useState(true);
+
+ React.useEffect(() => {
+ setFine(props.fine);
+
+ let params: GamepadParams = {
+ type: 'absolute',
+ values: [props.fine]
+ }
+
+ ipcRenderer.send('absolute', params);
+ }, [props.fine])
+
+ React.useEffect(() => {
+ setReverse(props.reverse);
+ setMode(props.mode);
+ setLockout(props.lockout);
+
+ let params: GamepadParams = {
+ type: 'reverse',
+ reverse: 'F'
+ }
+
+ if(props.reverse) params.reverse = 'T';
+
+ ipcRenderer.send('reverse', params);
+
+ params = {
+ type: 'mode',
+ mode: 'F'
+ }
+
+ if(props.mode) params.mode = 'T';
+
+ ipcRenderer.send('mode', params);
+
+ params = {
+ type: 'lockout',
+ lockout: 'F'
+ }
+
+ if(props.lockout) params.lockout = 'T'
+
+ ipcRenderer.send('lockout', params);
+
+ }, [props.reverse, props.mode, props.lockout])
+
+ React.useEffect(() => {
+ let temp = [...values];
+ temp = props.vals.map((val, idx) => val);
+ setValues(temp);
+
+ let params: GamepadParams = {
+ type: 'scale',
+ values: temp
+ }
+
+ ipcRenderer.send('gamepad_sock', params);
+ }, [props.vals])
+
+ const updateSwitch = () => {
+ let params: GamepadParams = {
+ type: 'reverse',
+ reverse: 'F'
+ }
+
+ if(!reverse){
+ params.reverse = 'T';
+ }
+
+ setReverse(!reverse);
+
+ ipcRenderer.send('reverse', params);
+ }
+
+ const updateMode = () => {
+ let params: GamepadParams = {
+ type: 'mode',
+ reverse: 'F'
+ }
+
+ if(!mode){
+ params.mode = 'T';
+ }
+
+ setMode(!mode);
+
+ ipcRenderer.send('mode', params);
+ }
+
+ const updateLockout = () => {
+ let params: GamepadParams = {
+ type: 'lockout',
+ lockout: 'F'
+ }
+
+ if(!lockout){
+ params.lockout = 'T';
+ }
+
+ setLockout(!lockout);
+
+ ipcRenderer.send('lockout', params);
+ }
+
+ return(
+
+
Thrust Tweaker
+
Translation
+
Rotation
+ {values.map((v, idx) => {
+ return(
+
{
+ let temp = [...values];
+ temp[idx] = val;
+ setValues(temp);
+
+ let params: GamepadParams = {
+ type: 'scale',
+ values: values
+ }
+
+ ipcRenderer.send('gamepad_sock', params);
+ }
+ }/>
+ )
+ })}
+
+
+
+
+
+
+ {
+ setFine(val);
+
+ let params: GamepadParams = {
+ type: 'absolute',
+ values: [val]
+ }
+
+ ipcRenderer.send('absolute', params);
+ }}/>
+
+
+ )
+}
+
+export default ThrustTweaker;
\ No newline at end of file
diff --git a/surface/src/components/Profiles/configs.json b/surface/src/components/Profiles/configs.json
new file mode 100644
index 0000000..7be16d9
--- /dev/null
+++ b/surface/src/components/Profiles/configs.json
@@ -0,0 +1,52 @@
+{
+ "test_config":{
+ "name": "Test Config",
+ "tweaker":[1.0, 0.1, 0.5, 1.0, 1.0, 1.0],
+ "fine": 1.041,
+ "mode": true,
+ "reverse": false,
+ "lockout": true,
+ "com": [0, 0, -0.2],
+ "trim": [0, 0, 0]
+ },
+ "other_config":{
+ "name": "Other Config",
+ "tweaker":[0.0, 0.1, 0.2, 1.0, 0.8, 1.0],
+ "fine": 1.041,
+ "mode": true,
+ "reverse": true,
+ "lockout": true,
+ "com": [0, 0, -0.2],
+ "trim": [0, 0, 1]
+ },
+ "seabin":{
+ "name": "Seabin Config",
+ "tweaker":[0.2, 0.5, 0.2, 1.0, 0.2, 0.2],
+ "fine": 1.041,
+ "mode": true,
+ "reverse": false,
+ "lockout": false,
+ "com": [0, 0, -0.2],
+ "trim": [0, 0, -1]
+ },
+ "ghost":{
+ "name": "Ghost Net Config",
+ "tweaker":[0.5, 0.2, 0.3, 1.0, 0.2, 0.2],
+ "fine": 1.041,
+ "mode": true,
+ "reverse": false,
+ "lockout": true,
+ "com": [0, 0, -0.2],
+ "trim": [0, 0, -1]
+ },
+ "sponge":{
+ "name": "Sponge Config",
+ "tweaker":[0.2, 0.5, 0.2, 1.0, 0.2, 0.2],
+ "fine": 1.041,
+ "mode": true,
+ "reverse": true,
+ "lockout": false,
+ "com": [0, 0, -0.2],
+ "trim": [0, 0, -2]
+ }
+}
\ No newline at end of file
diff --git a/surface/src/components/Servo/Servo.scss b/surface/src/components/Servo/Servo.scss
index 36b33fd..cb251f0 100644
--- a/surface/src/components/Servo/Servo.scss
+++ b/surface/src/components/Servo/Servo.scss
@@ -1,7 +1,7 @@
.servo-container{
display: grid;
grid-template-columns: 1fr;
- grid-template-rows: repeat(3, 1fr);
+ grid-template-rows: repeat(3, auto);
gap: 5px;
}
diff --git a/surface/src/components/Servo/Servo.tsx b/surface/src/components/Servo/Servo.tsx
index fd048fa..12ab420 100644
--- a/surface/src/components/Servo/Servo.tsx
+++ b/surface/src/components/Servo/Servo.tsx
@@ -3,33 +3,22 @@ import * as React from 'react';
import Slider from '../Slider/Slider';
import './Servo.scss';
-interface State{
- value: number
-}
-
-export default class Servo extends React.Component<{}, State>{
- constructor(props){
- super(props);
-
- this.state = {
- value: 50
- }
+const Servo: React.FC = () => {
+ const [value, setValue] = React.useState(50);
- this.updateValue = this.updateValue.bind(this);
- }
-
- updateValue(value){
- this.setState({value: value});
- ipcRenderer.send('servo_send', value);
- }
+ return(
+
+
Camera Angle
+
{
+ setValue(val);
+ ipcRenderer.send('servo_send', val);
+ }}
+ />
+ {value}°
+
+ )
+}
- render(){
- return(
-
-
Camera Angle
-
- {this.state.value}°
-
- )
- }
-}
\ No newline at end of file
+export default Servo;
\ No newline at end of file
diff --git a/surface/src/components/Slider/Slider.scss b/surface/src/components/Slider/Slider.scss
index c88b18e..1a3328e 100644
--- a/surface/src/components/Slider/Slider.scss
+++ b/surface/src/components/Slider/Slider.scss
@@ -1,9 +1,32 @@
+.slider-container{
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr 1fr;
+ gap: 5px;
+}
+
+.slider-range{
+ margin: auto;
+}
+
+.slider-top{
+ display: flex;
+ gap: 5px;
+}
+
+.slider-bottom{
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ height: 20px;
+}
+
.slider{
appearance: none;
width: 100%;
height: 10px;
background: #d3d3d3;
outline: none;
+ margin: auto;
opacity: 0.7;
transition: opacity .2;
border-radius: 10px;
@@ -20,4 +43,20 @@
border-radius: 10px;
background: #00eeff;
cursor: pointer;
+}
+
+.zero-btn{
+ background-color: steelblue;
+ border: none;
+ border-radius: 5px;
+ width: 20px;
+ height: 100%;
+}
+
+.inputbox {
+ border-radius: 5px;
+ border: none;
+ //width: 60px;
+ align-items: center;
+ text-align: center;
}
\ No newline at end of file
diff --git a/surface/src/components/Slider/Slider.tsx b/surface/src/components/Slider/Slider.tsx
index 973ddb5..2d84ea6 100644
--- a/surface/src/components/Slider/Slider.tsx
+++ b/surface/src/components/Slider/Slider.tsx
@@ -1,51 +1,147 @@
import * as React from 'react';
import './Slider.scss';
+//FULL SLIDER COMPONENT PROPS
interface Props{
- vertical: boolean
- min: number
- max: number
+ //vertical?: boolean
+ min?: number
+ max?: number
+ step?: number
+ unit?: string
callback(val: number): void
+ value: number
+}
+
+const defaultProps: Props = {
+ //vertical: false,
+ min: 0,
+ max: 100,
+ step: 1,
+ callback: (_) => {},
+ value: 0,
+ unit: ''
}
-interface State{
+//SLIDER BAR PROPS
+interface sliderProps{
+ min?: number
+ max?: number
+ step?: number
+ callback(val: React.ChangeEvent): void
value: number
}
-export default class Slider extends React.Component {
- static defaultProps = {
- vertical: false,
- min: 0,
- max: 100,
- initVal: 50,
- callback: () => {}
- }
+//BOTTOM DISPLAY PROPS
+interface displayProps{
+ value: number
+ unit? : string
+ callback(val: boolean): void
+}
- constructor(props) {
- super(props);
+//BOTTOM INPUT BOX PROPS
+interface inputProps{
+ value: number
+ min?: number
+ max?: number
+ step?: number
+ callback(val: React.FocusEvent): void
+}
- this.state = {
- value: 50
- }
+const SliderBar: React.FC = ({value, min, max, step, callback}) => {
+ return(
+
+ {min}
+ callback(e)}
+ />
+ {max}
+
+ )
+}
- this.handleChange = this.handleChange.bind(this);
- }
+const ValueDisplay: React.FC = ({value, callback, unit}) => {
+ return(
+ {callback(true)}}>{value}{unit}
+
+ )
+}
+
+const Inputbox: React.FC = ({value, max, min, callback, step}) => {
+ return(
+ e.target.select()}
+ onKeyUp={(e) => {
+ if (e.key === 'Enter') {
+ const target = e.target as HTMLInputElement;
+ target.blur();
+ }
+ }}
+ onBlur={(e) => callback(e)}
+ >
+ )
+}
- handleChange(val) {
- this.setState({value: val.target.value});
- this.props.callback(this.state.value);
- }
- render() {
- return (
-
- )
- }
+const Slider: React.FC = (props) => {
+ const [starting, setStarting] = React.useState(0);
+ const [inputActive, setInputActive] = React.useState(false);
+ React.useEffect(() => {
+ setStarting(Object.assign({}, props).value);
+ }, [])
+
+ return (
+
+
{
+ const target = e.target as HTMLInputElement
+ props.callback(parseFloat(target.value))
+ }}>
+
+
+ {inputActive ? (
+ {
+ let target = e.target as HTMLInputElement
+ let changeto = parseFloat(target.value);
+ if(isNaN(changeto) || changeto === Infinity) {
+ changeto = props.value!;
+ }
+ if(changeto > props.max!) {
+ changeto = props.max!;
+ } else if(changeto < props.min!) {
+ changeto = props.min!;
+ }
+ props.callback(changeto);
+ setInputActive(false);
+ }}>
+ ) : (
+ {
+ setInputActive(bool);
+ }}>
+ )}
+
+
+
+ )
}
+
+Slider.defaultProps = defaultProps;
+
+export default Slider;
\ No newline at end of file
diff --git a/surface/src/components/Switch/Switch.scss b/surface/src/components/Switch/Switch.scss
new file mode 100644
index 0000000..ef88ff4
--- /dev/null
+++ b/surface/src/components/Switch/Switch.scss
@@ -0,0 +1,70 @@
+$active: #00eeff;
+$active-inner: #ffffff;
+$focus: 2px rgba(39, 94, 254, 0.302);
+$border: #BBC1E1;
+$border-hover: #00eeff;
+$background: $active-inner;
+
+$b: $background;
+$bc: $border;
+$d-t: .3s;
+$d-t-e: ease;
+$d-o: .2s;
+
+$ab: $border;
+
+.switch-container{
+ display: flex;
+ flex-flow: row;
+ justify-content: center;
+}
+
+.switch{
+ -webkit-appearance: none;
+ height: 50px;
+ width: 150px;
+ outline: none;
+ display: inline-block;
+ vertical-align: top;
+ position: relative;
+ margin: 0;
+ cursor: pointer;
+ border: 1px solid $border;
+ border-radius: 25px;
+ background: $b;
+ transition: background .3s, border-color .3s, box-shadow .2s;
+}
+
+.switch::after{
+ content: '';
+ display: block;
+ position: absolute;
+ transition: transform .3s ease, opacity .2s;
+ left: 9px;
+ top: 4px;
+ border-radius: 50%;
+ width: 40px;
+ height: 40px;
+ background: $ab;
+ transform: translateX(0px);
+}
+
+.switch:hover{
+ border: 1px solid $border-hover;
+}
+
+.switch:checked{
+ background: $active;
+ border: 1px solid $active;
+ &::after{
+ background: $active-inner;
+ transform: translateX(90px);
+ transition: transform .6s cubic-bezier(.2, .85, .32, 1.2), opacity .3s;
+ }
+}
+
+.switch:not(:checked){
+ &::after{
+ opacity: .6;
+ }
+}
\ No newline at end of file
diff --git a/surface/src/components/Switch/Switch.tsx b/surface/src/components/Switch/Switch.tsx
new file mode 100644
index 0000000..f857151
--- /dev/null
+++ b/surface/src/components/Switch/Switch.tsx
@@ -0,0 +1,19 @@
+import * as React from 'react';
+import './Switch.scss';
+
+interface Props {
+ checked: boolean
+ callback(): void
+}
+
+const Switch: React.FC = (props) => {
+
+ return(
+
+
+
+
+ )
+}
+
+export default Switch;
\ No newline at end of file
diff --git a/surface/src/components/Thrusters/ThrusterCircle/ThrusterCircle.tsx b/surface/src/components/Thrusters/ThrusterCircle/ThrusterCircle.tsx
index 93b5da5..6895d4b 100644
--- a/surface/src/components/Thrusters/ThrusterCircle/ThrusterCircle.tsx
+++ b/surface/src/components/Thrusters/ThrusterCircle/ThrusterCircle.tsx
@@ -7,56 +7,42 @@ interface Props{
name: string
}
-export default class ThrusterCircle extends React.Component {
- circleStyle: {}
-
- constructor(props: Props) {
- super(props);
-
- this.circleStyle = {
- backgroundImage: 'linear-gradient(91deg, transparent 50%, #A6A6A6 50%), linear-gradient(90deg, #A6A6A6 50%, transparent 50%)'
- };
-
- this.setValue = this.setValue.bind(this);
- this.setValue();
-
- }
-
- componentDidUpdate(){
- this.setValue();
- }
-
- setValue() {
- var color = '#39B4CC';
- if(this.props.thrust < 127){
- color = '#FF4747';
- }
- var output = Math.round((Math.abs(this.props.thrust - 127) / 127) * 360);
+const l180 = (val: number, original: number) => {
+ return({
+ backgroundImage:
+ `linear-gradient(${val + 90}deg, transparent 50%, #A6A6A6 50%),` +
+ `linear-gradient(90deg, #A6A6A6 50%, transparent 50%)`,
+ backgroundColor: original < 127 ? '#FF4747' : '#39B4CC'
+ })
+}
- if(output <= 180){
- this.circleStyle = {
- backgroundImage: 'linear-gradient(' + (output + 90) +'deg, transparent 50%, #A6A6A6 50%),linear-gradient(90deg, #A6A6A6 50%, transparent 50%)',
- backgroundColor: color
- };
- }else{
- this.circleStyle = {
- backgroundImage: 'linear-gradient(' + (output - 90) +'deg, transparent 50%, ' + color + ' 50%),linear-gradient(90deg, #A6A6A6 50%, transparent 50%)',
- backgroundColor: color
- };
- }
- }
+const g180 = (val: number, original: number) => {
+ return({
+ backgroundImage:
+ `linear-gradient(${val - 90}deg, transparent 50%, ${original < 127 ? '#FF4747' : '#39B4CC'} 50%),` +
+ `linear-gradient(90deg, #A6A6A6 50%, transparent 50%)`,
+ backgroundColor: original < 127 ? '#FF4747' : '#39B4CC'
+ })
+}
- render() {
- return (
-
-
-
- {Math.round(((Math.abs(this.props.thrust) - 127) / 127) * 100)}%
-
- {this.props.name}
-
+const ThrusterCircle: React.FC
= (props) => {
+ const [angle, setAngle] = React.useState(Math.round(((Math.abs(props.thrust) - 127) / 127) * 360));
+
+ React.useEffect(() => {
+ setAngle(Math.round(((Math.abs(props.thrust) - 127) / 127) * 360))
+ }, [props.thrust]);
+
+ return(
+
+
+
+ {Math.round(angle / 360 * 100)}%
+
+ {props.name}
-
- );
- }
+
+
+ )
}
+
+export default ThrusterCircle;
\ No newline at end of file
diff --git a/surface/src/components/Thrusters/ThrusterInfo/ThrusterInfo.tsx b/surface/src/components/Thrusters/ThrusterInfo/ThrusterInfo.tsx
index e91937c..de532fd 100644
--- a/surface/src/components/Thrusters/ThrusterInfo/ThrusterInfo.tsx
+++ b/surface/src/components/Thrusters/ThrusterInfo/ThrusterInfo.tsx
@@ -5,35 +5,25 @@ import './ThrusterInfo.scss';
const INIT_THRUST = 127;
-interface State{
- thrust: Array
-}
-
-export default class ThrusterInfo extends React.Component<{}, State> {
- constructor(props: {}) {
- super(props);
+const ThrusterInfo: React.FC = () => {
+ const [thrust, setThrust] = React.useState>(Array(8).fill(INIT_THRUST));
- this.state = {thrust: [INIT_THRUST, INIT_THRUST, INIT_THRUST, INIT_THRUST, INIT_THRUST, INIT_THRUST, INIT_THRUST, INIT_THRUST]};
- }
+ ipcRenderer.on('thrusters', (e, data: Array) => {
+ setThrust(data)
+ });
- componentDidMount(){
- ipcRenderer.on('thrusters', (e, data: Array) => {
- this.setState({thrust: data})
- });
- }
-
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
- );
- }
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
}
+
+export default ThrusterInfo;
diff --git a/surface/src/components/Thrusters/Tweaker/ThrustTweaker.scss b/surface/src/components/Thrusters/Tweaker/ThrustTweaker.scss
deleted file mode 100644
index 446c097..0000000
--- a/surface/src/components/Thrusters/Tweaker/ThrustTweaker.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-.tweaker-container{
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- grid-template-rows: repeat(5, 1fr);
- gap: 5px;
-}
-
-.tweaker-title{
- width: 100%;
- text-align: center;
- border-bottom: solid;
- grid-column: 1 / 3;
-}
-
-.tweaker-subtitle{
- @extend .tweaker-title;
- grid-column: span 1;
-}
\ No newline at end of file
diff --git a/surface/src/components/Thrusters/Tweaker/ThrustTweaker.tsx b/surface/src/components/Thrusters/Tweaker/ThrustTweaker.tsx
deleted file mode 100644
index bc664b8..0000000
--- a/surface/src/components/Thrusters/Tweaker/ThrustTweaker.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { ipcRenderer } from 'electron';
-import * as React from 'react';
-import Slider from '../../Slider/Slider';
-import './ThrustTweaker.scss';
-
-interface State{
- values: Array
-}
-
-export default class Servo extends React.Component<{}, State>{
- constructor(props){
- super(props);
-
- this.state = {
- values: [0, 0, 0, 0, 0, 0]
- }
-
- this.updateValue = this.updateValue.bind(this);
- }
-
- updateValue(value){
-
- }
-
- render(){
- return(
-
-
Thrust Tweaker
-
Translation
-
Rotation
-
-
-
-
-
-
-
- )
- }
-}
\ No newline at end of file
diff --git a/surface/src/components/Toggle/Toggle.scss b/surface/src/components/Toggle/Toggle.scss
new file mode 100644
index 0000000..a387bc1
--- /dev/null
+++ b/surface/src/components/Toggle/Toggle.scss
@@ -0,0 +1,21 @@
+.toggle-base{
+ border: 2px solid #2e2e2e;
+ border-radius: 10px;
+ &:focus{
+ outline: none;
+ }
+ &:hover{
+ border: 2px solid cyan;
+ }
+ height: 50px;
+}
+
+.toggle-true{
+ @extend .toggle-base;
+ background-color: green;
+}
+
+.toggle-false{
+ @extend .toggle-base;
+ background-color: red;
+}
\ No newline at end of file
diff --git a/surface/src/components/Toggle/Toggle.tsx b/surface/src/components/Toggle/Toggle.tsx
new file mode 100644
index 0000000..5c1246a
--- /dev/null
+++ b/surface/src/components/Toggle/Toggle.tsx
@@ -0,0 +1,17 @@
+import * as React from 'react';
+import './Toggle.scss';
+
+interface Props {
+ name: string
+ toggled: boolean
+ callback(): void
+}
+
+const Toggle: React.FC = (props) => {
+
+ return(
+
+ )
+}
+
+export default Toggle;
\ No newline at end of file
diff --git a/surface/src/main.scss b/surface/src/main.scss
index 5c6161a..8c8d081 100644
--- a/surface/src/main.scss
+++ b/surface/src/main.scss
@@ -31,7 +31,7 @@ html{
body{
background-color: $color-bg;
- color: azure;
+ color: #f0ffff;
margin: 0;
height: 100vh;
display: flex;
diff --git a/surface/webpack.config.js b/surface/webpack.config.js
index dd62293..da7fb4f 100644
--- a/surface/webpack.config.js
+++ b/surface/webpack.config.js
@@ -3,7 +3,6 @@ const path = require('path');
module.exports = [
{
- mode: 'development',
entry: './main.ts',
target: 'electron-main',
module: {
@@ -27,7 +26,6 @@ module.exports = [
}
},
{
- mode: 'development',
entry: './src/index.tsx',
target: 'electron-renderer',
devtool: 'source-map',