Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pyinstaller==5.6.*
pyinstaller==5.13.2
parsimonious
nox==2022.8.*
types-PyYAML
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ setuptools
numpy
importlib_resources; python_version < "3.7"
proxy_tools
Flask==2.2.*
Flask==3.*
python-can==4.*
requests
websockets==10.*
Expand Down
4 changes: 2 additions & 2 deletions yukon/electron_folder/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ let menuTemplate = [
app.whenReady().then(() => {
// Send a GET request to http://locahost:5000/api/announce_running_in_electron
// to announce that the app is running in electron
http.get(`http://localhost:${yukon_server_port}/api/announce_running_in_electron`, (resp) => {
http.get(`http://127.0.0.1:${yukon_server_port}/api/announce_running_in_electron`, (resp) => {
});
console.log("Announcing that running in electron")
ipcMain.handle('dialog:openPath', handlePathOpen);
Expand Down Expand Up @@ -232,7 +232,7 @@ app.whenReady().then(() => {

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
http.get(`http://localhost:${yukon_server_port}/api/close_yukon`, (resp) => {
http.get(`http://127.0.0.1:${yukon_server_port}/api/close_yukon`, (resp) => {
});
app.quit();
}
Expand Down
11 changes: 10 additions & 1 deletion yukon/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import socket
import threading
import time
import traceback
import webbrowser
import typing
from typing import Optional, Any
Expand Down Expand Up @@ -172,6 +173,12 @@ def run_webbrowser_open() -> None:


def open_webbrowser(state: GodState) -> None:
# Make sure the splash screen is not blocking the screen any more.
try:
import pyi_splash
pyi_splash.close()
except:
pass
while not state.gui.is_port_decided:
sleep(0.5)
logger.info("Opening web browser")
Expand Down Expand Up @@ -241,8 +248,10 @@ def run_server(state: GodState) -> None:
continue
state.gui.is_port_decided = True
try:
server.run(host="0.0.0.0", port=state.gui.server_port, threaded=True)
server.run(host="127.0.0.1", port=state.gui.server_port, threaded=True)
except: # pylint: disable=bare-except
tb = traceback.format_exc()
logger.critical(str(tb))
logger.exception("Server was unable to start or crashed.")


Expand Down
17 changes: 9 additions & 8 deletions yukon/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@

from inspect import signature
import sys
from flask import Flask, Response, jsonify, request
from flask.blueprints import T_after_request
from flask import Flask, Response, request
from werkzeug.serving import WSGIRequestHandler

from yukon.domain.subject_specifier import SubjectSpecifier
from yukon.services._dumper import Dumper

from yukon.services.enhanced_json_encoder import EnhancedJSONEncoder
from yukon.services.enhanced_json_encoder import EnhancedJSONEncoder, jsonify
from yukon.domain.god_state import GodState
from yukon.services.api import Api

Expand All @@ -28,14 +27,13 @@

server = Flask(__name__, static_folder=gui_dir, template_folder=gui_dir, static_url_path="")
server.config["SEND_FILE_MAX_AGE_DEFAULT"] = 1 # disable caching
server.json_encoder = EnhancedJSONEncoder
WSGIRequestHandler.protocol_version = "HTTP/1.1"
our_token = "ABC"
logger = logging.getLogger(__name__)


@server.after_request
def add_header(response: T_after_request) -> T_after_request:
def add_header(response):
response.headers["Cache-Control"] = "no-store"
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Methods"] = "GET,HEAD,OPTIONS,POST,PUT"
Expand All @@ -48,10 +46,13 @@ def make_landing_and_bridge(state: GodState, api: Api) -> None:
def landing_and_bridge(path: str) -> typing.Any:
_object: typing.Any = {"arguments": []}
try:
_object = request.get_json()
request_data: bytes = request.get_data()
request_data_string: str = request_data.decode("utf-8")
_object = json.loads(request_data_string)
print(_object)
except Exception as _: # pylint: disable=broad-except
pass
# logger.warning("There was no json data attached")
tb = traceback.format_exc()
logger.critical(tb)
try:
found_method = getattr(api, path)
except Exception: # pylint: disable=broad-except
Expand Down
3 changes: 2 additions & 1 deletion yukon/services/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
except ImportError:
from yaml import Loader # type: ignore
import websockets
from flask import jsonify, Response
from flask import Response
from yukon.services.enhanced_json_encoder import jsonify

from pycyphal.presentation.subscription_synchronizer import get_local_reception_timestamp
from pycyphal.presentation.subscription_synchronizer.monotonic_clustering import MonotonicClusteringSynchronizer
Expand Down
43 changes: 43 additions & 0 deletions yukon/services/enhanced_json_encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from json.encoder import encode_basestring_ascii, encode_basestring, c_make_encoder, _make_iterencode # type: ignore
import typing
from uuid import UUID
from flask import Response
from flask.json.provider import DefaultJSONProvider as FlaskJSONProvider

import pycyphal

Expand All @@ -30,6 +32,9 @@

_logger = logging.getLogger(__name__)

# Flask has some broken code around this, making my own
def jsonify(obj: typing.Any) -> Response:
return Response(json.dumps(obj, cls=EnhancedJSONEncoder), mimetype="application/json")

class EnhancedJSONEncoder(json.JSONEncoder):
def default(self, o: typing.Any) -> typing.Any:
Expand Down Expand Up @@ -302,3 +307,41 @@ def default(self, o: typing.Any) -> typing.Any:
# Return a dict that doesn't contain keys that start with __id__
return {k: v for k, v in o.value.items() if not k.startswith("__id__")}
return super().default(o)


class FlaskEnhancedJSONEncoder(FlaskJSONProvider):
# Implement the FlaskJSONProvider API by using the Python JSON API
def dumps(self, obj: typing.Any, **kwargs: typing.Any) -> str:
"""Serialize data as JSON.

:param obj: The data to serialize.
:param kwargs: May be passed to the underlying JSON library.
"""
return json.dumps(obj, cls=EnhancedJSONEncoder, **kwargs)


def dump(self, obj: typing.Any, fp: typing.IO[str], **kwargs: typing.Any) -> None:
"""Serialize data as JSON and write to a file.

:param obj: The data to serialize.
:param fp: A file opened for writing text. Should use the UTF-8
encoding to be valid JSON.
:param kwargs: May be passed to the underlying JSON library.
"""
json.dump(obj, fp, cls=EnhancedJSONEncoder, **kwargs)

def loads(self, s: str | bytes, **kwargs: typing.Any) -> typing.Any:
"""Deserialize data as JSON.

:param s: Text or UTF-8 bytes.
:param kwargs: May be passed to the underlying JSON library.
"""
return json.loads(s, cls=EnhancedJSONEncoder, **kwargs)

def load(self, fp: typing.IO[typing.AnyStr], **kwargs: typing.Any) -> typing.Any:
"""Deserialize data as JSON read from a file.

:param fp: A file opened for reading text or UTF-8 bytes.
:param kwargs: May be passed to the underlying JSON library.
"""
return json.load(fp, cls=EnhancedJSONEncoder, **kwargs)
8 changes: 8 additions & 0 deletions yukon/web/main/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ window.console = new Proxy(old_console, {
}

yukon_state.addLocalMessage = function (message, severity) {
if(message === undefined) {
console.error("Message is undefined");
return;
}
if (severity === undefined) {
console.error("Severity is undefined");
return;
}
zubax_api.add_local_message(message, severity);
}
yukon_state.addLocalMessage("Press CTRL+SPACE to maximize the panel under your mouse", 30);
Expand Down
6 changes: 6 additions & 0 deletions yukon/web/modules/context-menu.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,12 @@ export function make_context_menus(yukon_state) {
click: async (e, elementOpenedOn) => {
const portNr = parseInt(elementOpenedOn.getAttribute("data-port"));
const datatypes = await getDatatypesForPort(portNr, "pub", yukon_state);
if(!datatypes[0]) {
console.error(```
Please make sure that registers exist for data type names for publication/subscription/client/server.
https://github.com/OpenCyphal/public_regulated_data_types/blob/935973babe11755d8070e67452b3508b4b6833e2/uavcan/register/384.Access.1.0.dsdl#L154-L162
```);
}
const response = await yukon_state.zubax_apij.make_simple_publisher_with_datatype_and_port_id(datatypes[0], portNr);
const portType = elementOpenedOn.getAttribute("data-port-type"); // sub or pub or cln or srv
if (response && response.success) {
Expand Down
21 changes: 20 additions & 1 deletion yukon/web/modules/panels/monitor2/monitor2.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,16 @@ async function update_monitor2(containerElement, monitor2Div, yukon_state, force
currentLinkDsdlDatatype = currentLinkObject.name + ":" + currentLinkDsdlDatatype;
}
} else {
currentLinkDsdlDatatype = fixed_datatype_full || "There is no info about this link";
currentLinkDsdlDatatype = fixed_datatype_full;
}
if(currentLinkDsdlDatatype) {
currentLinkDsdlDatatype = fixed_datatype_full;
} else {
// Handling a special case where the developer of a Cyphal node hasn't got registers set up for data type names
// https://github.com/OpenCyphal/public_regulated_data_types/blob/935973babe11755d8070e67452b3508b4b6833e2/uavcan/register/384.Access.1.0.dsdl#L154-L162
// https://forum.opencyphal.org/t/developing-pico-node-using-yukon/1978
// addHorizontalElements is going to give this a message and also make it clickable and hinted so that the link can be had from the label.
currentLinkDsdlDatatype = null;
}
let isLast = false;
// If this is the last iteration of the loop, set a variable to true
Expand Down Expand Up @@ -989,6 +998,16 @@ function addHorizontalElements(monitor2Div, matchingPort, currentLinkDsdlDatatyp
if (currentLinkDsdlDatatype.endsWith(".Response") || currentLinkDsdlDatatype.endsWith(".Request")) {
currentLinkDsdlDatatype = currentLinkDsdlDatatype.replace(".Response", "").replace(".Request", "");
}
if(!currentLinkDsdlDatatype) {
horizontal_line_label.addEventListener("mousedown", () => {
console.error(```
Please make sure that registers exist for data type names for publication/subscription/client/server.
https://github.com/OpenCyphal/public_regulated_data_types/blob/935973babe11755d8070e67452b3508b4b6833e2/uavcan/register/384.Access.1.0.dsdl#L154-L162
```);
});
horizontal_line_label.title = "Click for more information in console."
currentLinkDsdlDatatype = "Missing data type name registers.";
}
horizontal_line_label.innerHTML = currentLinkDsdlDatatype;
horizontal_line_label.style.zIndex = "3";
// horizontal_line_label.style.backgroundColor = settings["LinkLabelColor"];
Expand Down