From 147898439d37a3e86aebc64d2724a832cad5ec9c Mon Sep 17 00:00:00 2001 From: Jesse Florig Date: Tue, 21 Mar 2023 17:39:22 +0100 Subject: [PATCH] added ability to update intel msn data from control --- dashboard/package-lock.json | 91 ------------------------------ dashboard/package.json | 1 - dashboard/src/Control.jsx | 19 +++++-- dashboard/src/msnActions.js | 45 +++++++++++++-- msn-api-server/msn-api-server.py | 17 ++++-- msn-api-server/msn-data.json | 2 +- msn-data-server/log | 4 -- msn-data-server/msn-data-server.py | 45 +++++++++++++-- msn-data-server/msn-data.json | 2 +- 9 files changed, 108 insertions(+), 118 deletions(-) diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 1bfa9ad..2a19d48 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -13,7 +13,6 @@ "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@tanstack/react-router": "^0.0.1-beta.52", - "axios": "^1.2.3", "framer-motion": "^6.5.1", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -2546,11 +2545,6 @@ "dev": true, "peer": true }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, "node_modules/axe-core": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.1.tgz", @@ -2561,16 +2555,6 @@ "node": ">=4" } }, - "node_modules/axios": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.3.tgz", - "integrity": "sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -2702,17 +2686,6 @@ "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.0.tgz", "integrity": "sha512-DWX9eXOC4fbJNiuvdH4QSHvvfLWyFo9TuFp7V9OzdsbPAdrWAuYc8qvFP2bIQ/LKh4LrAVnJ6vhiQYPvAHdtTg==" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/compute-scroll-into-view": { "version": "1.0.14", "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz", @@ -2842,14 +2815,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -3614,38 +3579,6 @@ "node": ">=10" } }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/framer-motion": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", @@ -4380,25 +4313,6 @@ "node": ">=12" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4745,11 +4659,6 @@ "react-is": "^16.13.1" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", diff --git a/dashboard/package.json b/dashboard/package.json index e5f6292..3a26462 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -14,7 +14,6 @@ "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@tanstack/react-router": "^0.0.1-beta.52", - "axios": "^1.2.3", "framer-motion": "^6.5.1", "prop-types": "^15.8.1", "react": "^18.2.0", diff --git a/dashboard/src/Control.jsx b/dashboard/src/Control.jsx index 81ed374..c26464e 100644 --- a/dashboard/src/Control.jsx +++ b/dashboard/src/Control.jsx @@ -19,6 +19,7 @@ import { InfoIcon } from "@chakra-ui/icons" import msnActions from './msnActions' import mxActions from './mxActions' import useActionLog from './useActionLog' +import useLocalStorage from './useLocalStorage' import usePersistentSocket from './usePersistentSocket' import ConnectionStatus from './ConnectionStatus' @@ -34,6 +35,7 @@ const { function ActionButton({label, desc, handler, connectionCode}){ const isDisabled = connectionCode !== 1 + return ( @@ -52,15 +54,22 @@ ActionButton.propTypes = { connectionCode: PropTypes.number } -function ActionInput({label, desc, connectionCode}){ +function ActionInput({label, desc, localStorageKey, defaultValue, connectionCode}){ + const [localVal, setLocalVal] = useLocalStorage(localStorageKey, defaultValue) const isDisabled = connectionCode !== 1 + + // Hacky way to force the default value to be written to localStorage before the user edits the default value + // This is needed since the unmodified value is not initially written - there is an action that relies on this being in localstorage + if(!window.localStorage.getItem(localStorageKey)) setLocalVal(defaultValue) + + const handleChange = (e) => setLocalVal(e.target.value) return ( {label} - + ) } @@ -72,6 +81,8 @@ ActionInput.defaultProps = { ActionInput.propTypes = { label: PropTypes.string.isRequired, desc: PropTypes.string.isRequired, + localStorageKey: PropTypes.string.isRequired, + defaultValue: PropTypes.string.isRequired, connectionCode: PropTypes.number } @@ -140,7 +151,7 @@ function ActionsCard({title, actions, wsUrl, logKey}){ - { actions.map(({type, label, desc, handler, options}) => { + { actions.map(({type, label, desc, handler, localStorageKey, defaultValue, options}) => { let component if(type === "button"){ component = ( @@ -148,7 +159,7 @@ function ActionsCard({title, actions, wsUrl, logKey}){ ) } else if(type === "input"){ - component = () + component = () } else if(type === "select"){ component = ( handler(sendMessage)} connectionCode={statusCode} />) diff --git a/dashboard/src/msnActions.js b/dashboard/src/msnActions.js index 056bdda..ce024ec 100644 --- a/dashboard/src/msnActions.js +++ b/dashboard/src/msnActions.js @@ -1,4 +1,3 @@ - function handleBuildATO(send){ send('build') } @@ -7,31 +6,65 @@ function handlePublishATO(send){ send('publish') } +function handleWriteMsnData(send){ + function getLocalStorageItem(key){ + return JSON.parse(window.localStorage.getItem(key)) + } + + const msnId = getLocalStorageItem('tv-msn-id') + const msnTakeoff = getLocalStorageItem('tv-msn-takeoff') + const msnReturn = getLocalStorageItem('tv-msn-return') + const msnPlatform = getLocalStorageItem('tv-msn-platform') + const msnTarget = getLocalStorageItem('tv-msn-target') + const postData = {msn_id: msnId, msn_takeoff: msnTakeoff, msn_return: msnReturn, msn_platform: msnPlatform, msn_target: msnTarget} + const encodedData = btoa(JSON.stringify(postData)) + + console.log(postData) + + send(`updateIntel ${encodedData}`) +} + export default [ { type: "input", label: "Mission ID", - desc: "Mission ID on the dashboard" + desc: "Mission ID on the dashboard", + localStorageKey: 'tv-msn-id', + defaultValue: 'UNK-TV-XX' }, { type: "input", label: "Takeoff Time", - desc: "Mission takeoff on the dashboard" + desc: "Mission takeoff on the dashboard", + localStorageKey: 'tv-msn-takeoff', + defaultValue: '1000' }, { type: "input", label: "Return Time", - desc: "Mission return on the dashboard" + desc: "Mission return on the dashboard", + localStorageKey: 'tv-msn-return', + defaultValue: '1100' }, { type: "input", label: "Mission Platform", - desc: "Mission platform on the dashboard" + desc: "Mission platform on the dashboard", + localStorageKey: 'tv-msn-platform', + defaultValue: 'F-35' }, { type: "input", label: "Mission Target", - desc: "Mission target on the dashboard" + desc: "Mission target on the dashboard", + localStorageKey: 'tv-msn-target', + defaultValue: 'SAM Sites' + }, + { + type: "button", + label: "Push Intel Mission Data", + desc: "Control will send mission data to API Server and write to Data Server", + handler: handleWriteMsnData }, { type: "button", diff --git a/msn-api-server/msn-api-server.py b/msn-api-server/msn-api-server.py index 44881e1..e97fb18 100644 --- a/msn-api-server/msn-api-server.py +++ b/msn-api-server/msn-api-server.py @@ -1,5 +1,5 @@ from sys import argv -from urllib.request import urlopen +from urllib import request, parse import asyncio import json import socket @@ -10,7 +10,7 @@ "http://localhost:8012", "http://localhost:8013", "http://localhost:8014", - "http://localhost:8015", + "http://localhost:8015", #Intel "http://localhost:8016", "http://localhost:8017" ] @@ -25,7 +25,7 @@ def build_msn_data(path='/'): with open(MSN_DATA_FILE, 'w') as msn_data_file: data = {} for idx, data_server in enumerate(DATA_SERVERS): - content = urlopen(f"{data_server}{path}").read().decode() + content = request.urlopen(f"{data_server}{path}").read().decode() json_data = json.loads(content) key = list(json_data.keys())[0] # Grab the first key (shop name) @@ -37,7 +37,7 @@ def build_msn_data(path='/'): def restore_msn_data(): for idx, data_server in enumerate(DATA_SERVERS): - urlopen(f"{data_server}/restore") + request.urlopen(f"{data_server}/restore") websockets.broadcast(connections['/msn-controller'], "Restored MSN Data on MSN Data Servers") async def socket_handler(websocket, path): @@ -65,6 +65,15 @@ async def socket_handler(websocket, path): msn_data_file.write(f"[]") msn_data_file.close() websockets.broadcast(connections['/msn-controller'], "Reset mission data on MSN API Server") + elif message.startswith("updateIntel"): + data = { 'msn_data': message.split(' ')[1] } + + post_url = DATA_SERVERS[3] + '/updateMsn' + post_data = parse.urlencode(data).encode() + req = request.Request(post_url, data=post_data) + resp = request.urlopen(req) + + websockets.broadcast(connections['/msn-controller'], "Updated Intel mission data") finally: # Unregister connection diff --git a/msn-api-server/msn-data.json b/msn-api-server/msn-data.json index b759154..5f82622 100644 --- a/msn-api-server/msn-data.json +++ b/msn-api-server/msn-data.json @@ -1 +1 @@ -{"bcd": {"status": "good"}, "gccs": {"status": "good"}, "iamd": {"status": "good"}, "intel": {"status": "good", "msn_id": "01-JAN-23-TV-01", "msn_takeoff": "1000", "msn_return": "1100", "msn_platform": "f-35", "msn_target": "staging area"}, "freq": {"status": "good"}, "wx": {"status": "good"}} \ No newline at end of file +{"bcd": {"status": "good"}, "gccs": {"status": "good"}, "iamd": {"status": "good"}, "intel": {"status": "good", "msn_id": "UNK-TV-XXX", "msn_takeoff": "1000", "msn_return": "1100", "msn_platform": "F-35", "msn_target": "Staging Area", "msnId": "\"UNK-TV-XX\"", "msnTakeoff": "\"1000\"", "msnReturn": "\"1100\"", "msnPlatform": "\"F-35\"", "msnTarget": "\"No Way\""}, "freq": {"status": "good"}, "wx": {"status": "good"}} \ No newline at end of file diff --git a/msn-data-server/log b/msn-data-server/log index 9486800..8b13789 100644 --- a/msn-data-server/log +++ b/msn-data-server/log @@ -1,5 +1 @@ -test -test2 -test -something else diff --git a/msn-data-server/msn-data-server.py b/msn-data-server/msn-data-server.py index b02c8a0..a7d35ad 100644 --- a/msn-data-server/msn-data-server.py +++ b/msn-data-server/msn-data-server.py @@ -1,4 +1,6 @@ from http.server import BaseHTTPRequestHandler, HTTPServer +from urllib import parse +import base64 import json import shutil @@ -8,6 +10,13 @@ def _set_headers(self): self.send_header('Content-type', 'application/json') self.end_headers() + def do_OPTIONS(self): + self.send_response(200, "ok") + self.send_header('Access-Control-Allow-Credentials', 'true') + self.send_header('Access-Control-Allow-Origin', 'http://192.168.0.89:5173') + self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') + self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-type") + def do_HEAD(self): self._set_headers() @@ -45,14 +54,38 @@ def do_GET(self): file.close() def do_POST(self): - with open('log', 'a') as log: - content = self.rfile.read(int(self.headers.get('Content-Length'))) - log.write(f'{content.decode()}\n') + if self.path == "/": + with open("log", "a") as log: + content = self.rfile.read(int(self.headers.get("Content-Length"))) + log.write(f'{content.decode()}\n') - self._set_headers() - self.wfile.write(content) + self._set_headers() + self.wfile.write(content) + + log.close() + elif self.path == "/updateMsn": + base64_content = parse.unquote_plus(self.rfile.read(int(self.headers.get("Content-Length"))).decode("utf-8").split("data=")[1]) + new_content = json.loads(base64.b64decode(base64_content)) + content = {} + + # Read file + with open("msn-data.json", "r") as msnfile: + content = json.load(msnfile) + msnfile.close() - log.close() + # Update content + for key in new_content: + content[key] = new_content[key] + + # Write file + with open("msn-data.json", "w") as msnfile: + json.dump(content, msnfile) + msnfile.close() + + message = { "message" : "Intel mission data updated"} + + self._set_headers() + self.wfile.write(json.dumps(message).encode('utf-8')) def run(server_class=HTTPServer, handler_class=Server, port='8008', shop='unk'): server_address = ('', port) diff --git a/msn-data-server/msn-data.json b/msn-data-server/msn-data.json index b2c40ca..67fdf6b 100644 --- a/msn-data-server/msn-data.json +++ b/msn-data-server/msn-data.json @@ -1 +1 @@ -{ "status": "good", "msn_id":"01-JAN-23-TV-01", "msn_takeoff": "1000", "msn_return": "1100", "msn_platform": "f-35", "msn_target": "staging area"} +{"status": "good", "msn_id": "UNK-TV-XXX", "msn_takeoff": "1000", "msn_return": "1100", "msn_platform": "F-35", "msn_target": "Staging Area", "msnId": "\"UNK-TV-XX\"", "msnTakeoff": "\"1000\"", "msnReturn": "\"1100\"", "msnPlatform": "\"F-35\"", "msnTarget": "\"No Way\""} \ No newline at end of file