A big fuck-off commit with a bunch of bug fixes, bug additions, and a lot of work put into navigationd to the point that it actually does shit

This commit is contained in:
Innovation 2024-05-04 12:01:15 +01:00
parent 1d403fc838
commit d973245735
5 changed files with 280 additions and 9 deletions

View file

@ -0,0 +1,107 @@
import gpsd
import time
import requests
import geopandas as gpd
import osmnx as ox
import networkx as nx
import os
urlBase = 'http://localhost:5000'
urlLocation = urlBase + '/api/navigation/position'
urlImage = urlBase + '/api/navigation/image'
urlAddCyberware = urlBase + '/api/cyberware/add'
urlRemoveCyberware = urlBase + '/api/cyberware/remove'
uuid = ""
def connectCyberware():
global uuid
# Add to Cyberware and get UUID
try:
uuidRequest = requests.post(urlAddCyberware, json={ 'name': 'GPS', 'hotpluggable': True, 'canSet': [ '/api/navigation/position', '/api/navigation/image' ] })
uuid = uuidRequest.json()[0]['uuid']
except:
print("Cannot connect to NightCall. Stopping.")
exit()
def disconnectCyberware():
try:
disconnectRequest = requests.post(urlRemoveCyberware, json={ 'uuid': uuid })
except:
print("Cannot disconnect Cyberware.")
gpsdData = None
def getMapImage(distance):
ox.settings.use_cache = True
if(gpsdData.lat == 0 and gpsdData.lon == 0):
point = (42.352593, -83.2640164)
try:
G = ox.graph_from_point(point, dist=distance, dist_type="bbox", network_type="drive")
# impute edge (driving) speeds and calculate edge travel times
G = ox.routing.add_edge_speeds(G)
G = ox.routing.add_edge_travel_times(G)
# I'm not sure how to handle this short of saving it and then reading it. :(
filepath = uuid+".png"
ox.plot_graph(G, show=True, node_size=0, edge_linewidth=2, bgcolor="#0B1629", edge_color="#5FE0E9", save=True, filepath=filepath)
except ox._errors.InsufficientResponseError:
print("Insufficient response. Skipping.")
#def getMapImage(area):
# #lat, lon = gpsdData.position()
# lat, lon = 41.7573113, -93.8128377
# bbox = lat-(area/2.0), lat+(area/2.0), lon+(area/2.0), lon-(area/2.0)
#
# G = ox.graph_from_bbox(bbox=bbox, network_type="drive_service")
def sendLocation():
try:
#request = requests.post(urlLocation, json={ 'x': gpsdData.lat, 'y': gpsdData.lon, 'z': gpsdData.alt, 'o': gpsdData.track })
request = requests.post(urlLocation, json={ 'x': None, 'y': None, 'z': None, 'o': None, 'uuid': uuid })
except:
print('Could not contact NightCall')
def sendMapImage():
try:
files = { 'image': open(uuid+".png", 'rb') }
request = requests.post(urlImage, files=files)
except:
print("Could not contact NightCall")
print("Connecting to gpsd")
gpsd.connect() # Connect to gpsd
continueFlag = True
print("Adding Cyberware")
connectCyberware()
while continueFlag:
try:
time.sleep(1)
try:
gpsdData = gpsd.get_current()
except gpsd.NoFixError:
gpsdData = None
print("No GPS fix yet! Using default coordinates.")
getMapImage(1000)
sendLocation()
sendMapImage()
#print(packet.position())
except KeyboardInterrupt:
print("Exiting.")
continueFlag = False;
print("Removing Cyberware")
disconnectCyberware()
print("Cleaning up")
os.remove(uuid+".png")

View file

@ -1 +1,7 @@
pygarmin gpsd-py3
osmnx
gdal==3.6.2
geopandas
matplotlib
networkx
numpy

View file

@ -1,10 +1,11 @@
from flask import Flask, render_template, jsonify, request from flask import Flask, render_template, jsonify, request, send_file
from flask_apscheduler import APScheduler from flask_apscheduler import APScheduler
from copy import copy from copy import copy
import uuid import uuid
import time import time
from datetime import datetime from datetime import datetime
import requests import requests
import io
app = Flask(__name__) app = Flask(__name__)
@ -432,6 +433,103 @@ def setEnvironmentHumidity():
return 'Incorrect usage.\nUsage: { humidity: INT, uuid: STRING }\n', 400 return 'Incorrect usage.\nUsage: { humidity: INT, uuid: STRING }\n', 400
return '', 204 return '', 204
navigationX = None
navigationY = None
navigationZ = None
navigationO = None
navigationImage = None
# Navigation
@app.route('/api/navigation')
def getNavigation():
returnArr = [ { 'x': navigationX, 'y': navigationY, 'z': navigationZ, 'o': navigationO, 'img': navigationImage } ]
return jsonify(returnArr), 200
# Navigation//Position
# Position consists of: { x: FLOAT, y: FLOAT, z: FLOAT, o: FLOAT }
# o stands for ORIENTATION. o may be NoneType to indicate that such data is not available.
@app.route('/api/navigation/position')
def getNavigationPosition():
returnArr = [ { 'x': navigationX, 'y': navigationY, 'z': navigationZ, 'o': navigationO } ]
return jsonify(returnArr), 200
@app.route('/api/navigation/position', methods=['POST'])
def setNavigationPosition():
global navigationX
global navigationY
global navigationZ
global navigationO
json = request.get_json()
try:
uuid = json['uuid']
if not authenticate(uuid, '/api/navigation/position'):
return 'Forbidden.', 403
tempX = json['x']
tempY = json['y']
tempZ = json['z']
tempO = json['o']
navigationX = tempX
navigationY = tempY
navigationZ = tempZ
navigationO = tempO
except:
return 'Incorrect usage.\nUsage: { x: FLOAT, y: FLOAT, z: FLOAT, o: FLOAT }\n', 400
return '', 204
# Navigation//Position//x
#@app.route('/api/navigation/position/x')
#def getNavigationPositionX():
# return jsonify( [ 'x': navigationX ]), 200
#@app.route('/api/navigation/position/x', methods=['POST'])
#def setNavigationPositionX():
# global navigationX
#
# json = request.get_json()
#
# try:
# if not authenticate(url,
# except:
#
# Navigation//Image
@app.route('/api/navigation/image')
def getNavigationImage():
#returnArr = [ { 'img': navigationImage } ]
#return jsonify(returnArr), 200
if(navigationImage != None):
imageStream = io.BytesIO(navigationImage)
return send_file(imageStream, mimetype="image/png", download_name="minimap.png", max_age=0) # We don't want this to cache ever
else:
return '', 200
@app.route('/api/navigation/image', methods=['POST'])
def setNavigationImage():
global navigationImage
try:
filename = request.files['image'].filename
uuid = filename.split('.')[0]
if not authenticate(uuid, '/api/navigation/image'):
return "Forbidden.", 403
file = request.files['image'].read()
navigationImage = file
except:
return 'Auth failed, or no file was sent.\n', 400
return '', 204
def resetNavigationImage():
global navigationImage
navigationImage = None
# Authentication method # Authentication method
# This authorizes the given UUID to determine whether the request is # This authorizes the given UUID to determine whether the request is
# allowed to set the requested endpoint. # allowed to set the requested endpoint.
@ -441,12 +539,16 @@ def authenticate(uuid, endpoint):
if uuid == internalUUID: if uuid == internalUUID:
return True return True
requestedHardware = None
for c in cyberware: # UUID Match for c in cyberware: # UUID Match
if c['uuid'] == uuid: if c['uuid'] == uuid:
requestedHardware = c requestedHardware = c
c['lastContact'] = datetime.now() # Update last contact c['lastContact'] = datetime.now() # Update last contact
break break
if requestedHardware == None:
return False
if requestedHardware['canSet'] == None: if requestedHardware['canSet'] == None:
return False return False
@ -458,14 +560,16 @@ def authenticate(uuid, endpoint):
@app.route('/') @app.route('/')
def uiindex(): def uiindex():
return render_template('index.html') address = request.base_url
return render_template('index.html', access_addr=address )
# Maintenance functions # Maintenance functions
# The jank, my oh my # The jank, my oh my
valuesToValidate = [ '/api/vitals/heartrate', '/api/vitals/oxygen', '/api/vitals/bodytemp', valuesToValidate = [ '/api/vitals/heartrate', '/api/vitals/oxygen', '/api/vitals/bodytemp',
'/api/fitness/steps', '/api/fitness/steps',
'/api/environment/temperature', '/api/environment/humidity'] '/api/environment/temperature', '/api/environment/humidity',
'/api/navigation/position', '/api/navigation/image']
baseURL = "http://localhost:5000" baseURL = "http://localhost:5000"
# Value invalidation. A value is deemed invalid when there's no hardware attached # Value invalidation. A value is deemed invalid when there's no hardware attached
# that can set it. # that can set it.
@ -490,7 +594,14 @@ def valueInvalidation():
endpointToReset = baseURL + invalidValue endpointToReset = baseURL + invalidValue
requests.post(endpointToReset, json={ key: None, 'uuid': internalUUID }) # Don't look at the special cases. STOP LOOKING AT IT
match invalidValue:
case '/api/navigation/position':
requests.post(endpointToReset, json={ 'x': None, 'y': None, 'z': None, 'o': None, 'uuid': internalUUID })
case '/api/navigation/image':
resetNavigationImage()
case _:
requests.post(endpointToReset, json={ key: None, 'uuid': internalUUID })
invalidStr = invalidStr + invalidValue + ", " invalidStr = invalidStr + invalidValue + ", "
print("Values invalidated: " + invalidStr) print("Values invalidated: " + invalidStr)

View file

@ -1,4 +1,9 @@
flask flask
Flask-APScheduler Flask-APScheduler
pygarmin gpsd-py3
gdal=3.6.2
geopandas
osmnx
matplotlib
numpy
requests requests

View file

@ -7,6 +7,7 @@
} }
p { p {
font-size 24px;
color: red; color: red;
} }
@ -114,6 +115,30 @@
width: 100%; width: 100%;
border: 1px solid #5FE0E9; border: 1px solid #5FE0E9;
max-width: 100%; max-width: 100%;
overflow: hidden;
position: relative;
/*display: flex;
justify-content: center;*/
/*align-items: center;*/
}
#mapImage {
width: 150%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#mapDot {
width: 5px;
height: 5px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #F00;
border-radius: 50%;
} }
/* Contains the time */ /* Contains the time */
@ -191,7 +216,8 @@
</div> </div>
</div> </div>
<div id="mapProper"> <div id="mapProper">
<img id="mapImage" alt=" "></img>
<div id="mapDot"></div>
</div> </div>
<div id="mapTime"> <div id="mapTime">
<p id="time">00:00:00</p> <p id="time">00:00:00</p>
@ -216,12 +242,13 @@
</div> </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.2/jquery.min.js" integrity="sha512-tWHlutFnuG0C6nQRlpvrEhE4QpkG1nn2MOUMWmUeRePl4e3Aki0VB6W1v3oLjFtd0hVOtRQ9PHpSfN6u6/QXkQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.2/jquery.min.js" integrity="sha512-tWHlutFnuG0C6nQRlpvrEhE4QpkG1nn2MOUMWmUeRePl4e3Aki0VB6W1v3oLjFtd0hVOtRQ9PHpSfN6u6/QXkQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript"> <script type="text/javascript">
// TODO: Make baseUrl be set by Flask //var baseUrl = "http://192.168.6.48:5000";
var baseUrl = "http://localhost:5000"; var baseUrl = "{{ access_addr }}";
var vitalsUrl = baseUrl + "/api/vitals"; var vitalsUrl = baseUrl + "/api/vitals";
var environmentUrl = baseUrl + "/api/environment"; var environmentUrl = baseUrl + "/api/environment";
var malfunctionUrl = baseUrl + "/api/cyberware/malfunctions"; var malfunctionUrl = baseUrl + "/api/cyberware/malfunctions";
var messageUrl = baseUrl + "/api/cyberware/messages"; var messageUrl = baseUrl + "/api/cyberware/messages";
var navImageUrl = baseUrl + "/api/navigation/image";
// Malfunction Messages (human-friendly) // Malfunction Messages (human-friendly)
malfunctionOrigin = [ "API Malfunction" ]; malfunctionOrigin = [ "API Malfunction" ];
@ -297,7 +324,20 @@
} }
function setEnvironmentHumidity(humidity) { function setEnvironmentHumidity(humidity) {
}
//function handleMapImage() {
// document.getElementById("mapProper").innerHTML = "<img src=" + navImageUrl + "></img>";
//}
//function displayMapStatic() {
// document.getElementById("mapProper").innerHTML = "";
//}
function updateNavigationImage() {
//handleMapImage()
var image = document.getElementById("mapImage");
image.src = navImageUrl + "?" + new Date().getTime();
} }
// Malfunction handling // Malfunction handling
@ -347,6 +387,8 @@
} }
}); });
//stopInterval(t) //stopInterval(t)
updateNavigationImage()
} }
var t=setInterval(updateAll, 1000) var t=setInterval(updateAll, 1000)
</script> </script>