From 61d42ce07645310a252844ddeba161a98aeca25e Mon Sep 17 00:00:00 2001 From: md8n Date: Fri, 16 Dec 2022 11:43:16 +0900 Subject: [PATCH 01/34] Bump it for Python 3.10.5 --- README.md | 11 ++++++----- requirements.in | 6 ------ requirements.txt | 8 +------- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 7de6d65..a0eef99 100644 --- a/README.md +++ b/README.md @@ -148,13 +148,13 @@ To manage multiple python versions and virtual environments get `pyenv` (or [`py Here's a well-written [walkthrough](https://community.cisco.com/t5/developer-general-knowledge-base/pyenv-for-managing-python-versions-and-environments/ta-p/4696819) of setting up your system (macOS, Ubuntu/Debian, Fedora/CentOS/RHEL, Windows) for `pyenv` and then using `pyenv` to set up your virtual environment. Once you've prepared your system and installed `pyenv` -- get the latest version of Python that we know works with WebControl, currently that's `3.9.13`: - `pyenv install 3.9.13` +- get the latest version of Python that we know works with WebControl, currently that's `3.10.5`: + `pyenv install 3.10.5` - create a virtual environment with it: - `pyenv virtualenv 3.9.13 webcontrol_3_9` - The `webcontrol_3_9` name is arbitrary + `pyenv virtualenv 3.10.5 webcontrol_3_10` + The `webcontrol_3_10` name is arbitrary - activate the virtual environment - this will help isolate what you do from the rest of your system: - `pyenv activate webcontrol_3_9` + `pyenv activate webcontrol_3_10` #### Prepare the Virtual Environment itself @@ -164,6 +164,7 @@ This next stuff should only need to be done once in your virtual environment. `pip install pip-tools` `pip install black` - rebuild the list of requirements needed by WebControl (optional) + You should do this step if you're using a different version of Python from 3.10.5 shown above `rm requirements.txt` `pip-compile -r requirements.in --resolver=backtracking --verbose` - install the requirements diff --git a/requirements.in b/requirements.in index a5e7f74..d27706c 100644 --- a/requirements.in +++ b/requirements.in @@ -11,12 +11,6 @@ Flask-Mobility Flask-SocketIO gevent -# greenlet # used by gevent -# itsdangerous -# Jinja2 -# MarkupSafe -# python-engineio -# python-socketio # six Werkzeug diff --git a/requirements.txt b/requirements.txt index 7074ce1..098da1a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile --resolver=backtracking requirements.in @@ -39,10 +39,6 @@ greenlet==2.0.1 # via gevent idna==3.4 # via requests -importlib-metadata==5.1.0 - # via - # flask - # markdown itsdangerous==2.1.2 # via flask jinja2==3.1.2 @@ -105,8 +101,6 @@ werkzeug==2.2.2 # flask wrapt==1.14.1 # via deprecated -zipp==3.11.0 - # via importlib-metadata zope-event==4.6 # via gevent zope-interface==5.5.2 From e6fb334bea8e6d1f33288cd9fcd05e1ac5451cba Mon Sep 17 00:00:00 2001 From: md8n Date: Thu, 22 Dec 2022 10:00:06 +0900 Subject: [PATCH 02/34] WIP - but works-ish --- .gitignore | 3 + App/__init__.py | 0 App/data.py | 37 + App/main.py | 886 +++++++++++ App/route.py | 565 +++++++ __init__.py | 0 app.py | 6 + main.py | 47 +- requirements.in | 6 +- static/package-lock.json | 203 +++ static/package.json | 34 + static/scripts/OrbitControls.js | 1289 ++++++++++++++++ static/scripts/{ => archive}/ace.js | 0 static/scripts/{ => archive}/feather.min.js | 0 .../scripts/{ => archive}/jquery-3.3.1.min.js | 0 .../scripts/{ => archive}/mode-javascript.js | 0 static/scripts/{ => archive}/orbitcontrols.js | 0 static/scripts/{ => archive}/pako.min.js | 0 .../scripts/{ => archive}/socket.io-2.1.1.js | 0 static/scripts/{ => archive}/sprite3D.js | 0 .../scripts/{ => archive}/theme-twilight.js | 0 static/scripts/{ => archive}/three.js | 0 .../{ => archive}/worker-javascript.js | 0 static/scripts/baseSocket.js | 16 +- static/scripts/bootstrap.bundle.min.js | 7 - static/scripts/frontpage3d.js | 1334 ++++++++--------- static/scripts/frontpage3dcontrols.js | 14 +- static/scripts/frontpageControls.js | 262 ++-- static/scripts/opticalCalibration.js | 5 +- templates/archive/frontpage.html | 4 +- templates/archive/frontpage_mobile.html | 4 +- templates/base.html | 6 +- templates/editGCode.html | 2 +- templates/frontpage3d.html | 11 +- templates/frontpage3d_mobile.html | 11 +- templates/frontpage3d_mobilecontrols.html | 2 +- templates/frontpageText.html | 2 +- templates/logs.html | 6 +- templates/resetChains.html | 2 +- templates/resetChains_mobile.html | 2 +- templates/screenAction.html | 2 +- templates/sendGCode.html | 2 +- templates/setSprockets.html | 2 +- templates/setSprockets_mobile.html | 2 +- templates/setZaxis.html | 2 +- templates/setZaxis_mobile.html | 2 +- templates/zaxis.html | 2 +- templates/zaxis_mobile.html | 2 +- 48 files changed, 3872 insertions(+), 910 deletions(-) create mode 100644 App/__init__.py create mode 100644 App/data.py create mode 100644 App/main.py create mode 100644 App/route.py create mode 100644 __init__.py create mode 100644 static/package-lock.json create mode 100644 static/package.json create mode 100644 static/scripts/OrbitControls.js rename static/scripts/{ => archive}/ace.js (100%) rename static/scripts/{ => archive}/feather.min.js (100%) rename static/scripts/{ => archive}/jquery-3.3.1.min.js (100%) rename static/scripts/{ => archive}/mode-javascript.js (100%) rename static/scripts/{ => archive}/orbitcontrols.js (100%) rename static/scripts/{ => archive}/pako.min.js (100%) rename static/scripts/{ => archive}/socket.io-2.1.1.js (100%) rename static/scripts/{ => archive}/sprite3D.js (100%) rename static/scripts/{ => archive}/theme-twilight.js (100%) rename static/scripts/{ => archive}/three.js (100%) rename static/scripts/{ => archive}/worker-javascript.js (100%) delete mode 100644 static/scripts/bootstrap.bundle.min.js diff --git a/.gitignore b/.gitignore index 76d34f1..7628b7e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ alog.txt __pycache__/ *.py[cod] *$py.class + +# node +static/node_modules/ diff --git a/App/__init__.py b/App/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/App/data.py b/App/data.py new file mode 100644 index 0000000..38f50e8 --- /dev/null +++ b/App/data.py @@ -0,0 +1,37 @@ +import sys + +from DataStructures.data import Data + + +def init_data(app): + """ + Initialize the Flask app's data member + """ + app.data = Data() + app.data.config.computeSettings(None, None, None, True) + app.data.config.parseFirmwareVersions() + app.data.units = app.data.config.getValue("Computed Settings", "units") + app.data.tolerance = app.data.config.getValue("Computed Settings", "tolerance") + app.data.distToMove = app.data.config.getValue("Computed Settings", "distToMove") + app.data.distToMoveZ = app.data.config.getValue("Computed Settings", "distToMoveZ") + app.data.unitsZ = app.data.config.getValue("Computed Settings", "unitsZ") + app.data.comport = app.data.config.getValue("Maslow Settings", "COMport") + app.data.gcodeShift = [ + float(app.data.config.getValue("Advanced Settings", "homeX")), + float(app.data.config.getValue("Advanced Settings", "homeY")), + ] + + version = sys.version_info + + if version[:2] > (3, 5): + app.data.pythonVersion35 = False + print("Using routines for Python > 3.5") + else: + app.data.pythonVersion35 = True + print("Using routines for Python == 3.5") + + app.data.firstRun = False + # app.previousPosX = 0.0 + # app.previousPosY = 0.0 + + return app diff --git a/App/main.py b/App/main.py new file mode 100644 index 0000000..09eb74f --- /dev/null +++ b/App/main.py @@ -0,0 +1,886 @@ +# main.py +from gevent import monkey + +monkey.patch_all() + +from app import app, socketio + +import webbrowser +import socket +import os +import sys + +from werkzeug.utils import secure_filename + +import schedule +import time +import threading +import json + +from flask import ( + jsonify, + render_template, + current_app, + request, + send_file, + send_from_directory, +) +from flask_mobility.decorators import mobile_template + +from Background.UIProcessor import UIProcessor # do this after socketio is declared +from Background.LogStreamer import LogStreamer # do this after socketio is declared +from Connection.nonVisibleWidgets import NonVisibleWidgets +from WebPageProcessor.webPageProcessor import WebPageProcessor + + +app.nonVisibleWidgets = NonVisibleWidgets() +app.nonVisibleWidgets.setUpData(app.data) + + +app.UIProcessor = UIProcessor() +app.webPageProcessor = WebPageProcessor(app.data) +app.LogStreamer = LogStreamer() + +## this defines the schedule for running the serial port open connection +def run_schedule(): + while 1: + schedule.run_pending() + time.sleep(1) + + +## this runs the scheduler to check for connections +app.th = threading.Thread(target=run_schedule) +app.th.daemon = True +app.th.start() + +## this runs the thread that processes messages from the controller +app.th1 = threading.Thread(target=app.data.messageProcessor.start) +app.th1.daemon = True +app.th1.start() + +## this runs the thread that sends debugging messages to the terminal and webmcp (if active) +app.th2 = threading.Thread(target=app.data.consoleProcessor.start) +app.th2.daemon = True +app.th2.start() + +## uithread set to None.. will be activated upon first websocket connection from browser +app.uithread = None + +## uithread set to None.. will be activated upon first websocket connection from webmcp +app.mcpthread = None + +## logstreamerthread set to None.. will be activated upon first websocket connection from log streamer browser +app.logstreamerthread = None + + +@app.route("/") +@mobile_template("{mobile/}") +def index(template): + app.data.logger.resetIdler() + macro1Title = (app.data.config.getValue("Maslow Settings", "macro1_title"))[:6] + macro2Title = (app.data.config.getValue("Maslow Settings", "macro2_title"))[:6] + if template == "mobile/": + return render_template( + "frontpage3d_mobile.html", + modalStyle="modal-lg", + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + else: + return render_template( + "frontpage3d.html", + modalStyle="mw-100 w-75", + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + + +@app.route("/controls") +@mobile_template("/controls/{mobile/}") +def controls(template): + app.data.logger.resetIdler() + macro1Title = (app.data.config.getValue("Maslow Settings", "macro1_title"))[:6] + macro2Title = (app.data.config.getValue("Maslow Settings", "macro2_title"))[:6] + if template == "/controls/mobile/": + return render_template( + "frontpage3d_mobilecontrols.html", + modalStyle="modal-lg", + isControls=True, + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + else: + return render_template( + "frontpage3d.html", + modalStyle="mw-100 w-75", + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + + +@app.route("/text") +@mobile_template("/text/{mobile/}") +def text(template): + app.data.logger.resetIdler() + macro1Title = (app.data.config.getValue("Maslow Settings", "macro1_title"))[:6] + macro2Title = (app.data.config.getValue("Maslow Settings", "macro2_title"))[:6] + if template == "/text/mobile": + return render_template( + "frontpageText_mobile.html", + modalStyle="modal-lg", + isControls=True, + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + else: + return render_template( + "frontpageText.html", + modalStyle="mw-100 w-75", + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + + +@app.route("/logs") +@mobile_template("/logs/{mobile/}") +def logs(template): + print("here") + app.data.logger.resetIdler() + if template == "/logs/mobile/": + return render_template("logs.html") + else: + return render_template("logs.html") + + +@app.route("/maslowSettings", methods=["POST"]) +def maslowSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("Maslow Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + +@app.route("/advancedSettings", methods=["POST"]) +def advancedSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("Advanced Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + +@app.route("/webControlSettings", methods=["POST"]) +def webControlSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("WebControl Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + +@app.route("/cameraSettings", methods=["POST"]) +def cameraSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("Camera Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + +@app.route("/gpioSettings", methods=["POST"]) +def gpioSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("GPIO Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + +@app.route("/uploadGCode", methods=["POST"]) +def uploadGCode(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + directory = result["selectedDirectory"] + # print(directory) + f = request.files.getlist("file[]") + print(f) + home = app.data.config.getHome() + app.data.config.setValue( + "Computed Settings", "lastSelectedDirectory", directory + ) + + if len(f) > 0: + firstFile = f[0] + for file in f: + app.data.gcodeFile.filename = ( + home + + "/.WebControl/gcode/" + + directory + + "/" + + secure_filename(file.filename) + ) + file.save(app.data.gcodeFile.filename) + app.data.gcodeFile.filename = ( + home + + "/.WebControl/gcode/" + + directory + + "/" + + secure_filename(firstFile.filename) + ) + returnVal = app.data.gcodeFile.loadUpdateFile() + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/openGCode", methods=["POST"]) +def openGCode(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.form["selectedGCode"] + app.data.console_queue.put(f"selectedGcode={f}") + tDir = f.split("/") + app.data.config.setValue("Computed Settings", "lastSelectedDirectory", tDir[0]) + home = app.data.config.getHome() + app.data.gcodeFile.filename = home + "/.WebControl/gcode/" + f + app.data.config.setValue("Maslow Settings", "openFile", tDir[1]) + returnVal = app.data.gcodeFile.loadUpdateFile() + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/saveGCode", methods=["POST"]) +def saveGCode(): + app.data.logger.resetIdler() + if request.method == "POST": + print(request.form) + f = request.form["fileName"] + d = request.form["selectedDirectory"] + app.data.console_queue.put(f"selectedGcode={f}") + app.data.config.setValue("Computed Settings", "lastSelectedDirectory", d) + home = app.data.config.getHome() + returnVal = app.data.gcodeFile.saveFile(f, home + "/.WebControl/gcode/" + d) + """ + tDir = f.split("/") + app.data.config.setValue("Computed Settings","lastSelectedDirectory",tDir[0]) + home = app.data.config.getHome() + app.data.gcodeFile.filename = home+"/.WebControl/gcode/" + f + app.data.config.setValue("Maslow Settings", "openFile", tDir[1]) + returnVal = app.data.gcodeFile.loadUpdateFile() + """ + if returnVal: + app.data.config.setValue("Maslow Settings", "openFile", f) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/openBoard", methods=["POST"]) +def openBoard(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.form["selectedBoard"] + app.data.console_queue.put(f"selectedBoard={f}") + tDir = f.split("/") + app.data.config.setValue( + "Computed Settings", "lastSelectedBoardDirectory", tDir[0] + ) + home = app.data.config.getHome() + app.data.gcodeFile.filename = home + "/.WebControl/boards/" + f + app.data.config.setValue("Maslow Settings", "openBoardFile", tDir[1]) + returnVal = app.data.boardManager.loadBoard(home + "/.WebControl/boards/" + f) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/saveBoard", methods=["POST"]) +def saveBoard(): + app.data.logger.resetIdler() + if request.method == "POST": + print(request.form) + f = request.form["fileName"] + d = request.form["selectedDirectory"] + app.data.console_queue.put(f"selectedBoard={f}") + app.data.config.setValue("Computed Settings", "lastSelectedBoardDirectory", d) + home = app.data.config.getHome() + returnVal = app.data.boardManager.saveBoard( + f, home + "/.WebControl/boards/" + d + ) + app.data.config.setValue("Maslow Settings", "openBoardFile", f) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/importFile", methods=["POST"]) +def importFile(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.files["file"] + home = app.data.config.getHome() + secureFilename = home + "/.WebControl/imports/" + secure_filename(f.filename) + f.save(secureFilename) + returnVal = app.data.importFile.importGCini(secureFilename) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/importFileWCJSON", methods=["POST"]) +def importFileJSON(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.files["file"] + home = app.data.config.getHome() + secureFilename = home + "/.WebControl/imports/" + secure_filename(f.filename) + f.save(secureFilename) + returnVal = app.data.importFile.importWebControlJSON(secureFilename) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/importRestoreWebControl", methods=["POST"]) +def importRestoreWebControl(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.files["file"] + home = app.data.config.getHome() + secureFilename = home + "/" + secure_filename(f.filename) + f.save(secureFilename) + returnVal = app.data.actions.restoreWebControl(secureFilename) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/sendGCode", methods=["POST"]) +def sendGcode(): + app.data.logger.resetIdler() + # print(request.form)#["gcodeInput"]) + if request.method == "POST": + returnVal = app.data.actions.sendGCode(request.form["gcode"].rstrip()) + if returnVal: + message = {"status": 200} + resp = jsonify("success") + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify("failed") + resp.status_code = 500 + return resp + + +@app.route("/triangularCalibration", methods=["POST"]) +def triangularCalibration(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + ( + motorYoffsetEst, + rotationRadiusEst, + chainSagCorrectionEst, + cut34YoffsetEst, + ) = app.data.actions.calibrate(result) + # print(returnVal) + if motorYoffsetEst: + message = { + "status": 200, + "data": { + "motorYoffset": motorYoffsetEst, + "rotationRadius": rotationRadiusEst, + "chainSagCorrection": chainSagCorrectionEst, + "calibrationError": cut34YoffsetEst, + }, + } + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/holeyCalibration", methods=["POST"]) +def holeyCalibration(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + ( + motorYoffsetEst, + distanceBetweenMotors, + leftChainTolerance, + rightChainTolerance, + calibrationError, + ) = app.data.actions.holeyCalibrate(result) + # print(returnVal) + if motorYoffsetEst: + message = { + "status": 200, + "data": { + "motorYoffset": motorYoffsetEst, + "distanceBetweenMotors": distanceBetweenMotors, + "leftChainTolerance": leftChainTolerance, + "rightChainTolerance": rightChainTolerance, + "calibrationError": calibrationError, + }, + } + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/opticalCalibration", methods=["POST"]) +def opticalCalibration(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + +@app.route("/quickConfigure", methods=["POST"]) +def quickConfigure(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateQuickConfigure(result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + +@app.route("/editGCode", methods=["POST"]) +def editGCode(): + app.data.logger.resetIdler() + # print(request.form["gcode"]) + if request.method == "POST": + returnVal = app.data.actions.updateGCode(request.form["gcode"].rstrip()) + if returnVal: + message = {"status": 200} + resp = jsonify("success") + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify("failed") + resp.status_code = 500 + return resp + + +@app.route("/downloadDiagnostics", methods=["GET"]) +def downloadDiagnostics(): + app.data.logger.resetIdler() + if request.method == "GET": + returnVal = app.data.actions.downloadDiagnostics() + if returnVal != False: + print(returnVal) + return send_file(returnVal, as_attachment=True) + else: + resp = jsonify("failed") + resp.status_code = 500 + return resp + + +@app.route("/backupWebControl", methods=["GET"]) +def backupWebControl(): + app.data.logger.resetIdler() + if request.method == "GET": + returnVal = app.data.actions.backupWebControl() + if returnVal != False: + print(returnVal) + return send_file(returnVal) + else: + resp = jsonify("failed") + resp.status_code = 500 + return resp + + +@app.route("/editBoard", methods=["POST"]) +def editBoard(): + app.data.logger.resetIdler() + if request.method == "POST": + returnVal = app.data.boardManager.editBoard(request.form) + if returnVal: + resp = jsonify("success") + resp.status_code = 200 + return resp + else: + resp = jsonify("failed") + resp.status_code = 500 + return resp + + +@app.route("/trimBoard", methods=["POST"]) +def trimBoard(): + app.data.logger.resetIdler() + if request.method == "POST": + returnVal = app.data.boardManager.trimBoard(request.form) + if returnVal: + resp = jsonify("success") + resp.status_code = 200 + return resp + else: + resp = jsonify("failed") + resp.status_code = 500 + return resp + + +@app.route("/assets/") +def sendDocs(path): + print(path) + return send_from_directory("docs/assets/", path) + + +@socketio.on("checkInRequested", namespace="/WebMCP") +def checkInRequested(): + socketio.emit("checkIn", namespace="/WebMCP") + + +@socketio.on("connect", namespace="/WebMCP") +def watchdog_connect(): + app.data.console_queue.put("watchdog connected") + app.data.console_queue.put(request.sid) + socketio.emit("connect", namespace="/WebMCP") + if app.mcpthread == None: + app.data.console_queue.put("going to start mcp thread") + app.mcpthread = socketio.start_background_task( + app.data.mcpProcessor.start, current_app._get_current_object() + ) + app.data.console_queue.put("created mcp thread") + app.mcpthread.start() + app.data.console_queue.put("started mcp thread") + + +@socketio.on("my event", namespace="/MaslowCNC") +def my_event(msg): + app.data.console_queue.put(msg["data"]) + + +@socketio.on("modalClosed", namespace="/MaslowCNC") +def modalClosed(msg): + app.data.logger.resetIdler() + data = json.dumps({"title": msg["data"]}) + socketio.emit( + "message", + {"command": "closeModals", "data": data, "dataFormat": "json"}, + namespace="/MaslowCNC", + ) + + +@socketio.on("contentModalClosed", namespace="/MaslowCNC") +def contentModalClosed(msg): + # Note, this shouldn't be called anymore + # todo: cleanup + app.data.logger.resetIdler() + data = json.dumps({"title": msg["data"]}) + print(data) + # socketio.emit("message", {"command": "closeContentModals", "data": data, "dataFormat": "json"}, + # namespace="/MaslowCNC", ) + + +""" +todo: cleanup +not used +@socketio.on("actionModalClosed", namespace="/MaslowCNC") +def actionModalClosed(msg): + app.data.logger.resetIdler() + data = json.dumps({"title": msg["data"]}) + socketio.emit("message", {"command": "closeActionModals", "data": data, "dataFormat": "json"}, + namespace="/MaslowCNC", ) +""" + + +@socketio.on("alertModalClosed", namespace="/MaslowCNC") +def alertModalClosed(msg): + app.data.logger.resetIdler() + data = json.dumps({"title": msg["data"]}) + socketio.emit( + "message", + {"command": "closeAlertModals", "data": data, "dataFormat": "json"}, + namespace="/MaslowCNC", + ) + + +@socketio.on("requestPage", namespace="/MaslowCNC") +def requestPage(msg): + app.data.logger.resetIdler() + app.data.console_queue.put(request.sid) + client = request.sid + try: + ( + page, + title, + isStatic, + modalSize, + modalType, + resume, + ) = app.webPageProcessor.createWebPage( + msg["data"]["page"], msg["data"]["isMobile"], msg["data"]["args"] + ) + # if msg["data"]["page"] != "help": + # client = "all" + data = json.dumps( + { + "title": title, + "message": page, + "isStatic": isStatic, + "modalSize": modalSize, + "modalType": modalType, + "resume": resume, + "client": client, + } + ) + socketio.emit( + "message", + {"command": "activateModal", "data": data, "dataFormat": "json"}, + namespace="/MaslowCNC", + ) + except Exception as e: + app.data.console_queue.put(e) + + +@socketio.on("connect", namespace="/MaslowCNC") +def test_connect(): + app.data.console_queue.put("connected") + app.data.console_queue.put(request.sid) + if app.uithread == None: + app.uithread = socketio.start_background_task( + app.UIProcessor.start, current_app._get_current_object() + ) + app.uithread.start() + + if not app.data.connectionStatus: + app.data.console_queue.put( + "Attempting to re-establish connection to controller" + ) + app.data.serialPort.openConnection() + + socketio.emit("my response", {"data": "Connected", "count": 0}) + address = app.data.hostAddress + data = json.dumps({"hostAddress": address}) + print(data) + socketio.emit( + "message", + {"command": "hostAddress", "data": data, "dataFormat": "json"}, + namespace="/MaslowCNC", + ) + if app.data.pyInstallUpdateAvailable: + app.data.ui_queue1.put("Action", "pyinstallUpdate", "on") + + +@socketio.on("disconnect", namespace="/MaslowCNC") +def test_disconnect(): + app.data.console_queue.put("Client disconnected") + + +@socketio.on("action", namespace="/MaslowCNC") +def command(msg): + app.data.logger.resetIdler() + retval = app.data.actions.processAction(msg) + if retval == "Shutdown": + print("Shutting Down") + socketio.stop() + print("Shutdown") + if retval == "TurnOffRPI": + print("Turning off RPI") + os.system("sudo poweroff") + + +@socketio.on("settingRequest", namespace="/MaslowCNC") +def settingRequest(msg): + app.data.logger.resetIdler() + # didn't move to actions.. this request is just to send it computed values.. keeping it here makes it faster than putting it through the UIProcessor + setting, value = app.data.actions.processSettingRequest( + msg["data"]["section"], msg["data"]["setting"] + ) + if setting is not None: + data = json.dumps({"setting": setting, "value": value}) + socketio.emit( + "message", + {"command": "requestedSetting", "data": data, "dataFormat": "json"}, + namespace="/MaslowCNC", + ) + + +@socketio.on("updateSetting", namespace="/MaslowCNC") +def updateSetting(msg): + app.data.logger.resetIdler() + if not app.data.actions.updateSetting(msg["data"]["setting"], msg["data"]["value"]): + app.data.ui_queue1.put("Alert", "Alert", "Error updating setting") + + +@socketio.on("checkForGCodeUpdate", namespace="/MaslowCNC") +def checkForGCodeUpdate(msg): + app.data.logger.resetIdler() + # this currently doesn't check for updated gcode, it just resends it.. + ## the gcode file might change the active units so we need to inform the UI of the change. + app.data.ui_queue1.put("Action", "unitsUpdate", "") + app.data.ui_queue1.put("Action", "gcodeUpdate", "") + + +@socketio.on("checkForBoardUpdate", namespace="/MaslowCNC") +def checkForBoardUpdate(msg): + app.data.logger.resetIdler() + # this currently doesn't check for updated board, it just resends it.. + app.data.ui_queue1.put("Action", "boardUpdate", "") + + +@socketio.on("connect", namespace="/MaslowCNCLogs") +def log_connect(): + app.data.console_queue.put("connected to log") + app.data.console_queue.put(request.sid) + if app.logstreamerthread == None: + app.logstreamerthread = socketio.start_background_task( + app.LogStreamer.start, current_app._get_current_object() + ) + app.logstreamerthread.start() + + socketio.emit( + "my response", {"data": "Connected", "count": 0}, namespace="/MaslowCNCLog" + ) + + +@socketio.on("disconnect", namespace="/MaslowCNCLogs") +def log_disconnect(): + app.data.console_queue.put("Client disconnected") + + +@app.template_filter("isnumber") +def isnumber(s): + try: + float(s) + return True + except ValueError: + return False + + +# def shutdown(): +# print("Shutdown") + + +if __name__ == "__main__": + app.debug = False + app.config["SECRET_KEY"] = "secret!" + # look for touched file + app.data.config.checkForTouchedPort() + webPort = app.data.config.getValue("WebControl Settings", "webPort") + webPortInt = 5000 + try: + webPortInt = int(webPort) + if webPortInt < 0 or webPortInt > 65535: + webPortInt = 5000 + except Exception as e: + app.data.console_queue.put(e) + app.data.console_queue.put("Invalid port assignment found in webcontrol.json") + + print("-$$$$$-") + print(os.path.abspath(__file__)) + app.data.releaseManager.processAbsolutePath(os.path.abspath(__file__)) + print("-$$$$$-") + + print("opening browser") + webPortStr = str(webPortInt) + webbrowser.open_new_tab("http://localhost:" + webPortStr) + host_name = socket.gethostname() + host_ip = socket.gethostbyname(host_name) + app.data.hostAddress = host_ip + ":" + webPortStr + + # app.data.shutdown = shutdown + socketio.run(app, use_reloader=False, host="0.0.0.0", port=webPortInt) + # socketio.run(app, host='0.0.0.0') diff --git a/App/route.py b/App/route.py new file mode 100644 index 0000000..aa622b7 --- /dev/null +++ b/App/route.py @@ -0,0 +1,565 @@ +from werkzeug.utils import secure_filename + +from flask import ( + jsonify, + render_template, + request, + send_file, + send_from_directory, +) +from flask_mobility.decorators import mobile_template + + +def init_route(app): + @app.route("/") + @mobile_template("{mobile/}") + def index(template): + app.data.logger.resetIdler() + macro1Title = (app.data.config.getValue("Maslow Settings", "macro1_title"))[:6] + macro2Title = (app.data.config.getValue("Maslow Settings", "macro2_title"))[:6] + if template == "mobile/": + return render_template( + "frontpage3d_mobile.html", + modalStyle="modal-lg", + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + else: + return render_template( + "frontpage3d.html", + modalStyle="mw-100 w-75", + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + + + @app.route("/controls") + @mobile_template("/controls/{mobile/}") + def controls(template): + app.data.logger.resetIdler() + macro1Title = (app.data.config.getValue("Maslow Settings", "macro1_title"))[:6] + macro2Title = (app.data.config.getValue("Maslow Settings", "macro2_title"))[:6] + if template == "/controls/mobile/": + return render_template( + "frontpage3d_mobilecontrols.html", + modalStyle="modal-lg", + isControls=True, + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + else: + return render_template( + "frontpage3d.html", + modalStyle="mw-100 w-75", + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + + + @app.route("/text") + @mobile_template("/text/{mobile/}") + def text(template): + app.data.logger.resetIdler() + macro1Title = (app.data.config.getValue("Maslow Settings", "macro1_title"))[:6] + macro2Title = (app.data.config.getValue("Maslow Settings", "macro2_title"))[:6] + if template == "/text/mobile": + return render_template( + "frontpageText_mobile.html", + modalStyle="modal-lg", + isControls=True, + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + else: + return render_template( + "frontpageText.html", + modalStyle="mw-100 w-75", + macro1_title=macro1Title, + macro2_title=macro2Title, + ) + + + @app.route("/logs") + @mobile_template("/logs/{mobile/}") + def logs(template): + print("here") + app.data.logger.resetIdler() + if template == "/logs/mobile/": + return render_template("logs.html") + else: + return render_template("logs.html") + + + @app.route("/maslowSettings", methods=["POST"]) + def maslowSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("Maslow Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + + @app.route("/advancedSettings", methods=["POST"]) + def advancedSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("Advanced Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + + @app.route("/webControlSettings", methods=["POST"]) + def webControlSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("WebControl Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + + @app.route("/cameraSettings", methods=["POST"]) + def cameraSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("Camera Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + + @app.route("/gpioSettings", methods=["POST"]) + def gpioSettings(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateSettings("GPIO Settings", result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + + @app.route("/uploadGCode", methods=["POST"]) + def uploadGCode(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + directory = result["selectedDirectory"] + # print(directory) + f = request.files.getlist("file[]") + print(f) + home = app.data.config.getHome() + app.data.config.setValue( + "Computed Settings", "lastSelectedDirectory", directory + ) + + if len(f) > 0: + firstFile = f[0] + for file in f: + app.data.gcodeFile.filename = ( + home + + "/.WebControl/gcode/" + + directory + + "/" + + secure_filename(file.filename) + ) + file.save(app.data.gcodeFile.filename) + app.data.gcodeFile.filename = ( + home + + "/.WebControl/gcode/" + + directory + + "/" + + secure_filename(firstFile.filename) + ) + returnVal = app.data.gcodeFile.loadUpdateFile() + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/openGCode", methods=["POST"]) + def openGCode(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.form["selectedGCode"] + app.data.console_queue.put(f"selectedGcode={f}") + tDir = f.split("/") + app.data.config.setValue("Computed Settings", "lastSelectedDirectory", tDir[0]) + home = app.data.config.getHome() + app.data.gcodeFile.filename = home + "/.WebControl/gcode/" + f + app.data.config.setValue("Maslow Settings", "openFile", tDir[1]) + returnVal = app.data.gcodeFile.loadUpdateFile() + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/saveGCode", methods=["POST"]) + def saveGCode(): + app.data.logger.resetIdler() + if request.method == "POST": + print(request.form) + f = request.form["fileName"] + d = request.form["selectedDirectory"] + app.data.console_queue.put(f"selectedGcode={f}") + app.data.config.setValue("Computed Settings", "lastSelectedDirectory", d) + home = app.data.config.getHome() + returnVal = app.data.gcodeFile.saveFile(f, home + "/.WebControl/gcode/" + d) + """ + tDir = f.split("/") + app.data.config.setValue("Computed Settings","lastSelectedDirectory",tDir[0]) + home = app.data.config.getHome() + app.data.gcodeFile.filename = home+"/.WebControl/gcode/" + f + app.data.config.setValue("Maslow Settings", "openFile", tDir[1]) + returnVal = app.data.gcodeFile.loadUpdateFile() + """ + if returnVal: + app.data.config.setValue("Maslow Settings", "openFile", f) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/openBoard", methods=["POST"]) + def openBoard(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.form["selectedBoard"] + app.data.console_queue.put(f"selectedBoard={f}") + tDir = f.split("/") + app.data.config.setValue( + "Computed Settings", "lastSelectedBoardDirectory", tDir[0] + ) + home = app.data.config.getHome() + app.data.gcodeFile.filename = home + "/.WebControl/boards/" + f + app.data.config.setValue("Maslow Settings", "openBoardFile", tDir[1]) + returnVal = app.data.boardManager.loadBoard(home + "/.WebControl/boards/" + f) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/saveBoard", methods=["POST"]) + def saveBoard(): + app.data.logger.resetIdler() + if request.method == "POST": + print(request.form) + f = request.form["fileName"] + d = request.form["selectedDirectory"] + app.data.console_queue.put(f"selectedBoard={f}") + app.data.config.setValue("Computed Settings", "lastSelectedBoardDirectory", d) + home = app.data.config.getHome() + returnVal = app.data.boardManager.saveBoard( + f, home + "/.WebControl/boards/" + d + ) + app.data.config.setValue("Maslow Settings", "openBoardFile", f) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/importFile", methods=["POST"]) + def importFile(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.files["file"] + home = app.data.config.getHome() + secureFilename = home + "/.WebControl/imports/" + secure_filename(f.filename) + f.save(secureFilename) + returnVal = app.data.importFile.importGCini(secureFilename) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/importFileWCJSON", methods=["POST"]) + def importFileJSON(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.files["file"] + home = app.data.config.getHome() + secureFilename = home + "/.WebControl/imports/" + secure_filename(f.filename) + f.save(secureFilename) + returnVal = app.data.importFile.importWebControlJSON(secureFilename) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/importRestoreWebControl", methods=["POST"]) + def importRestoreWebControl(): + app.data.logger.resetIdler() + if request.method == "POST": + f = request.files["file"] + home = app.data.config.getHome() + secureFilename = home + "/" + secure_filename(f.filename) + f.save(secureFilename) + returnVal = app.data.actions.restoreWebControl(secureFilename) + if returnVal: + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/sendGCode", methods=["POST"]) + def sendGcode(): + app.data.logger.resetIdler() + # print(request.form)#["gcodeInput"]) + if request.method == "POST": + returnVal = app.data.actions.sendGCode(request.form["gcode"].rstrip()) + if returnVal: + message = {"status": 200} + resp = jsonify("success") + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify("failed") + resp.status_code = 500 + return resp + + + @app.route("/triangularCalibration", methods=["POST"]) + def triangularCalibration(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + ( + motorYoffsetEst, + rotationRadiusEst, + chainSagCorrectionEst, + cut34YoffsetEst, + ) = app.data.actions.calibrate(result) + # print(returnVal) + if motorYoffsetEst: + message = { + "status": 200, + "data": { + "motorYoffset": motorYoffsetEst, + "rotationRadius": rotationRadiusEst, + "chainSagCorrection": chainSagCorrectionEst, + "calibrationError": cut34YoffsetEst, + }, + } + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/holeyCalibration", methods=["POST"]) + def holeyCalibration(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + ( + motorYoffsetEst, + distanceBetweenMotors, + leftChainTolerance, + rightChainTolerance, + calibrationError, + ) = app.data.actions.holeyCalibrate(result) + # print(returnVal) + if motorYoffsetEst: + message = { + "status": 200, + "data": { + "motorYoffset": motorYoffsetEst, + "distanceBetweenMotors": distanceBetweenMotors, + "leftChainTolerance": leftChainTolerance, + "rightChainTolerance": rightChainTolerance, + "calibrationError": calibrationError, + }, + } + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/opticalCalibration", methods=["POST"]) + def opticalCalibration(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify(message) + resp.status_code = 500 + return resp + + + @app.route("/quickConfigure", methods=["POST"]) + def quickConfigure(): + app.data.logger.resetIdler() + if request.method == "POST": + result = request.form + app.data.config.updateQuickConfigure(result) + message = {"status": 200} + resp = jsonify(message) + resp.status_code = 200 + return resp + + + @app.route("/editGCode", methods=["POST"]) + def editGCode(): + app.data.logger.resetIdler() + # print(request.form["gcode"]) + if request.method == "POST": + returnVal = app.data.actions.updateGCode(request.form["gcode"].rstrip()) + if returnVal: + message = {"status": 200} + resp = jsonify("success") + resp.status_code = 200 + return resp + else: + message = {"status": 500} + resp = jsonify("failed") + resp.status_code = 500 + return resp + + + @app.route("/downloadDiagnostics", methods=["GET"]) + def downloadDiagnostics(): + app.data.logger.resetIdler() + if request.method == "GET": + returnVal = app.data.actions.downloadDiagnostics() + if returnVal != False: + print(returnVal) + return send_file(returnVal, as_attachment=True) + else: + resp = jsonify("failed") + resp.status_code = 500 + return resp + + + @app.route("/backupWebControl", methods=["GET"]) + def backupWebControl(): + app.data.logger.resetIdler() + if request.method == "GET": + returnVal = app.data.actions.backupWebControl() + if returnVal != False: + print(returnVal) + return send_file(returnVal) + else: + resp = jsonify("failed") + resp.status_code = 500 + return resp + + + @app.route("/editBoard", methods=["POST"]) + def editBoard(): + app.data.logger.resetIdler() + if request.method == "POST": + returnVal = app.data.boardManager.editBoard(request.form) + if returnVal: + resp = jsonify("success") + resp.status_code = 200 + return resp + else: + resp = jsonify("failed") + resp.status_code = 500 + return resp + + + @app.route("/trimBoard", methods=["POST"]) + def trimBoard(): + app.data.logger.resetIdler() + if request.method == "POST": + returnVal = app.data.boardManager.trimBoard(request.form) + if returnVal: + resp = jsonify("success") + resp.status_code = 200 + return resp + else: + resp = jsonify("failed") + resp.status_code = 500 + return resp + + + @app.route("/assets/") + def sendDocs(path): + print(path) + return send_from_directory("docs/assets/", path) diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app.py b/app.py index 5e6bec9..19616b9 100644 --- a/app.py +++ b/app.py @@ -1,3 +1,5 @@ +# from pyjion.wsgi import PyjionWsgiMiddleware + from flask import Flask from flask_mobility import Mobility from flask_socketio import SocketIO @@ -18,6 +20,10 @@ template_folder=os.path.join(base_dir, "templates"), ) app.debug = True + +# Override the app wsgi_app property +# app.wsgi_app = PyjionWsgiMiddleware(app.wsgi_app) + socketio = SocketIO(app) mobility = Mobility(app) # md.init_app(app) diff --git a/main.py b/main.py index da57114..58b7c71 100644 --- a/main.py +++ b/main.py @@ -5,17 +5,14 @@ from app import app, socketio -import webbrowser -import socket +import json import os -import sys - -from werkzeug.utils import secure_filename +import socket +import threading +import time +import webbrowser import schedule -import time -import threading -import json from flask import ( jsonify, @@ -26,42 +23,20 @@ send_from_directory, ) from flask_mobility.decorators import mobile_template +from werkzeug.utils import secure_filename + +from App.data import init_data +from App.route import init_route from Background.UIProcessor import UIProcessor # do this after socketio is declared from Background.LogStreamer import LogStreamer # do this after socketio is declared -from DataStructures.data import Data from Connection.nonVisibleWidgets import NonVisibleWidgets from WebPageProcessor.webPageProcessor import WebPageProcessor +app = init_data(app) -app.data = Data() app.nonVisibleWidgets = NonVisibleWidgets() app.nonVisibleWidgets.setUpData(app.data) -app.data.config.computeSettings(None, None, None, True) -app.data.config.parseFirmwareVersions() -app.data.units = app.data.config.getValue("Computed Settings", "units") -app.data.tolerance = app.data.config.getValue("Computed Settings", "tolerance") -app.data.distToMove = app.data.config.getValue("Computed Settings", "distToMove") -app.data.distToMoveZ = app.data.config.getValue("Computed Settings", "distToMoveZ") -app.data.unitsZ = app.data.config.getValue("Computed Settings", "unitsZ") -app.data.comport = app.data.config.getValue("Maslow Settings", "COMport") -app.data.gcodeShift = [ - float(app.data.config.getValue("Advanced Settings", "homeX")), - float(app.data.config.getValue("Advanced Settings", "homeY")), -] - -version = sys.version_info - -if version[:2] > (3, 5): - app.data.pythonVersion35 = False - print("Using routines for Python > 3.5") -else: - app.data.pythonVersion35 = True - print("Using routines for Python == 3.5") - -app.data.firstRun = False -# app.previousPosX = 0.0 -# app.previousPosY = 0.0 app.UIProcessor = UIProcessor() app.webPageProcessor = WebPageProcessor(app.data) @@ -98,6 +73,7 @@ def run_schedule(): ## logstreamerthread set to None.. will be activated upon first websocket connection from log streamer browser app.logstreamerthread = None +# init_route(app) @app.route("/") @mobile_template("{mobile/}") @@ -725,6 +701,7 @@ def alertModalClosed(msg): @socketio.on("requestPage", namespace="/MaslowCNC") def requestPage(msg): + print(f"requestPage: {msg}") app.data.logger.resetIdler() app.data.console_queue.put(request.sid) client = request.sid diff --git a/requirements.in b/requirements.in index d27706c..62b7531 100644 --- a/requirements.in +++ b/requirements.in @@ -1,4 +1,5 @@ -r ./Actions/requirements.in +# -r ./App/requirements.in -r ./Background/requirements.in -r ./Boards/requirements.in -r ./Connection/requirements.in @@ -7,10 +8,13 @@ -r ./ReleaseManager/requirements.in -r ./WebPageProcessor/requirements.in +eventlet # Flask-Misaka Flask-Mobility Flask-SocketIO gevent -# six +# Pyjion requires Python 3.10+ and .net 7+ to work +pyjion + Werkzeug diff --git a/static/package-lock.json b/static/package-lock.json new file mode 100644 index 0000000..ae4dbb3 --- /dev/null +++ b/static/package-lock.json @@ -0,0 +1,203 @@ +{ + "name": "webcontrol", + "version": "0.94.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "webcontrol", + "version": "0.94.0", + "license": "GPL-3.0", + "dependencies": { + "ace-builds": "^1.14.0", + "bootstrap": "^5.2.3", + "feather-icons": "^4.29.0", + "jquery": "^3.6.3", + "pako": "^2.1.0", + "socket.io-client": "^4.5.4", + "three-spritetext": "^1.6.5" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/ace-builds": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.14.0.tgz", + "integrity": "sha512-3q8LvawomApRCt4cC0OzxVjDsZ609lDbm8l0Xl9uqG06dKEq4RT0YXLUyk7J2SxmqIp5YXzZNw767Dr8GKUruw==" + }, + "node_modules/bootstrap": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", + "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.6" + } + }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, + "node_modules/core-js": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.0.tgz", + "integrity": "sha512-wY6cKosevs430KRkHUIsvepDXHGjlXOZO3hYXNyqpD6JvB0X28aXyv0t1Y1vZMwE7SoKmtfa6IASHCPN52FwBQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/feather-icons": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/feather-icons/-/feather-icons-4.29.0.tgz", + "integrity": "sha512-Y7VqN9FYb8KdaSF0qD1081HCkm0v4Eq/fpfQYQnubpqi0hXx14k+gF9iqtRys1SIcTEi97xDi/fmsPFZ8xo0GQ==", + "dependencies": { + "classnames": "^2.2.5", + "core-js": "^3.1.3" + } + }, + "node_modules/jquery": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz", + "integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, + "node_modules/socket.io-client": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.4.tgz", + "integrity": "sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", + "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/three": { + "version": "0.148.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.148.0.tgz", + "integrity": "sha512-8uzVV+qhTPi0bOFs/3te3RW6hb3urL8jYEl6irjCWo/l6sr8MPNMcClFev/MMYeIxr0gmDcoXTy/8LXh/LXkfw==", + "peer": true + }, + "node_modules/three-spritetext": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/three-spritetext/-/three-spritetext-1.6.5.tgz", + "integrity": "sha512-ttA1ce3tJz6OFojLGY3VjtqF9johetq50TztYcPFWqdEUrwPmej5XXcHahVyQGd88FHRDzJmW2rW3zSifUsdYA==", + "peerDependencies": { + "three": ">=0.86.0" + } + }, + "node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + } + } +} diff --git a/static/package.json b/static/package.json new file mode 100644 index 0000000..8a1b224 --- /dev/null +++ b/static/package.json @@ -0,0 +1,34 @@ +{ + "name": "webcontrol", + "version": "0.94.0", + "description": "Web interface for the Maslow CNC router", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/WebControlCNC/WebControl.git" + }, + "keywords": [ + "Maslow", + "CNC", + "WebControl", + "GroundControl" + ], + "author": "", + "license": "GPL-3.0", + "bugs": { + "url": "https://github.com/WebControlCNC/WebControl/issues" + }, + "homepage": "https://github.com/WebControlCNC/WebControl#readme", + "dependencies": { + "ace-builds": "^1.14.0", + "bootstrap": "^5.2.3", + "feather-icons": "^4.29.0", + "jquery": "^3.6.3", + "pako": "^2.1.0", + "socket.io-client": "^4.5.4", + "three-spritetext": "^1.6.5" + } +} diff --git a/static/scripts/OrbitControls.js b/static/scripts/OrbitControls.js new file mode 100644 index 0000000..01b5b8c --- /dev/null +++ b/static/scripts/OrbitControls.js @@ -0,0 +1,1289 @@ +// This is the same as that found in /node_modules/three/examples/jsm/controls/OrbitControls.js +// However, the import location is changed so that it can be used raw without relying on some transpiler +import { + EventDispatcher, + MOUSE, + Quaternion, + Spherical, + TOUCH, + Vector2, + Vector3 +} from "../node_modules/three/build/three.module.js"; + +// This set of controls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// +// Orbit - left mouse / touch: one-finger move +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move + +const _changeEvent = { type: 'change' }; +const _startEvent = { type: 'start' }; +const _endEvent = { type: 'end' }; + +class OrbitControls extends EventDispatcher { + + constructor( object, domElement ) { + + super(); + + this.object = object; + this.domElement = domElement; + this.domElement.style.touchAction = 'none'; // disable touch scroll + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new Vector3(); + + // How far you can dolly in and out ( PerspectiveCamera only ) + this.minDistance = 0; + this.maxDistance = Infinity; + + // How far you can zoom in and out ( OrthographicCamera only ) + this.minZoom = 0; + this.maxZoom = Infinity; + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI ) + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.05; + + // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.panSpeed = 1.0; + this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60 + + // The four arrow keys + this.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' }; + + // Mouse buttons + this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN }; + + // Touch fingers + this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // the target DOM element for key events + this._domElementKeyEvents = null; + + // + // public methods + // + + this.getPolarAngle = function () { + + return spherical.phi; + + }; + + this.getAzimuthalAngle = function () { + + return spherical.theta; + + }; + + this.getDistance = function () { + + return this.object.position.distanceTo( this.target ); + + }; + + this.listenToKeyEvents = function ( domElement ) { + + domElement.addEventListener( 'keydown', onKeyDown ); + this._domElementKeyEvents = domElement; + + }; + + this.saveState = function () { + + scope.target0.copy( scope.target ); + scope.position0.copy( scope.object.position ); + scope.zoom0 = scope.object.zoom; + + }; + + this.reset = function () { + + scope.target.copy( scope.target0 ); + scope.object.position.copy( scope.position0 ); + scope.object.zoom = scope.zoom0; + + scope.object.updateProjectionMatrix(); + scope.dispatchEvent( _changeEvent ); + + scope.update(); + + state = STATE.NONE; + + }; + + // this method is exposed, but perhaps it would be better if we can make it private... + this.update = function () { + + const offset = new Vector3(); + + // so camera.up is the orbit axis + const quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) ); + const quatInverse = quat.clone().invert(); + + const lastPosition = new Vector3(); + const lastQuaternion = new Quaternion(); + + const twoPI = 2 * Math.PI; + + return function update() { + + const position = scope.object.position; + + offset.copy( position ).sub( scope.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + spherical.setFromVector3( offset ); + + if ( scope.autoRotate && state === STATE.NONE ) { + + rotateLeft( getAutoRotationAngle() ); + + } + + if ( scope.enableDamping ) { + + spherical.theta += sphericalDelta.theta * scope.dampingFactor; + spherical.phi += sphericalDelta.phi * scope.dampingFactor; + + } else { + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + } + + // restrict theta to be between desired limits + + let min = scope.minAzimuthAngle; + let max = scope.maxAzimuthAngle; + + if ( isFinite( min ) && isFinite( max ) ) { + + if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI; + + if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI; + + if ( min <= max ) { + + spherical.theta = Math.max( min, Math.min( max, spherical.theta ) ); + + } else { + + spherical.theta = ( spherical.theta > ( min + max ) / 2 ) ? + Math.max( min, spherical.theta ) : + Math.min( max, spherical.theta ); + + } + + } + + // restrict phi to be between desired limits + spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); + + spherical.makeSafe(); + + + spherical.radius *= scale; + + // restrict radius to be between desired limits + spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) ); + + // move target to panned location + + if ( scope.enableDamping === true ) { + + scope.target.addScaledVector( panOffset, scope.dampingFactor ); + + } else { + + scope.target.add( panOffset ); + + } + + offset.setFromSpherical( spherical ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( scope.target ).add( offset ); + + scope.object.lookAt( scope.target ); + + if ( scope.enableDamping === true ) { + + sphericalDelta.theta *= ( 1 - scope.dampingFactor ); + sphericalDelta.phi *= ( 1 - scope.dampingFactor ); + + panOffset.multiplyScalar( 1 - scope.dampingFactor ); + + } else { + + sphericalDelta.set( 0, 0, 0 ); + + panOffset.set( 0, 0, 0 ); + + } + + scale = 1; + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( zoomChanged || + lastPosition.distanceToSquared( scope.object.position ) > EPS || + 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { + + scope.dispatchEvent( _changeEvent ); + + lastPosition.copy( scope.object.position ); + lastQuaternion.copy( scope.object.quaternion ); + zoomChanged = false; + + return true; + + } + + return false; + + }; + + }(); + + this.dispose = function () { + + scope.domElement.removeEventListener( 'contextmenu', onContextMenu ); + + scope.domElement.removeEventListener( 'pointerdown', onPointerDown ); + scope.domElement.removeEventListener( 'pointercancel', onPointerCancel ); + scope.domElement.removeEventListener( 'wheel', onMouseWheel ); + + scope.domElement.removeEventListener( 'pointermove', onPointerMove ); + scope.domElement.removeEventListener( 'pointerup', onPointerUp ); + + + if ( scope._domElementKeyEvents !== null ) { + + scope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown ); + + } + + //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? + + }; + + // + // internals + // + + const scope = this; + + const STATE = { + NONE: - 1, + ROTATE: 0, + DOLLY: 1, + PAN: 2, + TOUCH_ROTATE: 3, + TOUCH_PAN: 4, + TOUCH_DOLLY_PAN: 5, + TOUCH_DOLLY_ROTATE: 6 + }; + + let state = STATE.NONE; + + const EPS = 0.000001; + + // current position in spherical coordinates + const spherical = new Spherical(); + const sphericalDelta = new Spherical(); + + let scale = 1; + const panOffset = new Vector3(); + let zoomChanged = false; + + const rotateStart = new Vector2(); + const rotateEnd = new Vector2(); + const rotateDelta = new Vector2(); + + const panStart = new Vector2(); + const panEnd = new Vector2(); + const panDelta = new Vector2(); + + const dollyStart = new Vector2(); + const dollyEnd = new Vector2(); + const dollyDelta = new Vector2(); + + const pointers = []; + const pointerPositions = {}; + + function getAutoRotationAngle() { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + + function rotateLeft( angle ) { + + sphericalDelta.theta -= angle; + + } + + function rotateUp( angle ) { + + sphericalDelta.phi -= angle; + + } + + const panLeft = function () { + + const v = new Vector3(); + + return function panLeft( distance, objectMatrix ) { + + v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix + v.multiplyScalar( - distance ); + + panOffset.add( v ); + + }; + + }(); + + const panUp = function () { + + const v = new Vector3(); + + return function panUp( distance, objectMatrix ) { + + if ( scope.screenSpacePanning === true ) { + + v.setFromMatrixColumn( objectMatrix, 1 ); + + } else { + + v.setFromMatrixColumn( objectMatrix, 0 ); + v.crossVectors( scope.object.up, v ); + + } + + v.multiplyScalar( distance ); + + panOffset.add( v ); + + }; + + }(); + + // deltaX and deltaY are in pixels; right and down are positive + const pan = function () { + + const offset = new Vector3(); + + return function pan( deltaX, deltaY ) { + + const element = scope.domElement; + + if ( scope.object.isPerspectiveCamera ) { + + // perspective + const position = scope.object.position; + offset.copy( position ).sub( scope.target ); + let targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we use only clientHeight here so aspect ratio does not distort speed + panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); + panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); + + } else if ( scope.object.isOrthographicCamera ) { + + // orthographic + panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); + panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); + + } else { + + // camera neither orthographic nor perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); + scope.enablePan = false; + + } + + }; + + }(); + + function dollyOut( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale /= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function dollyIn( dollyScale ) { + + if ( scope.object.isPerspectiveCamera ) { + + scale *= dollyScale; + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + // + // event callbacks - update the object state + // + + function handleMouseDownRotate( event ) { + + rotateStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownDolly( event ) { + + dollyStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownPan( event ) { + + panStart.set( event.clientX, event.clientY ); + + } + + function handleMouseMoveRotate( event ) { + + rotateEnd.set( event.clientX, event.clientY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + const element = scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleMouseMoveDolly( event ) { + + dollyEnd.set( event.clientX, event.clientY ); + + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + dollyOut( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + dollyIn( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + + } + + function handleMouseMovePan( event ) { + + panEnd.set( event.clientX, event.clientY ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + + } + + function handleMouseWheel( event ) { + + if ( event.deltaY < 0 ) { + + dollyIn( getZoomScale() ); + + } else if ( event.deltaY > 0 ) { + + dollyOut( getZoomScale() ); + + } + + scope.update(); + + } + + function handleKeyDown( event ) { + + let needsUpdate = false; + + switch ( event.code ) { + + case scope.keys.UP: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateUp( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( 0, scope.keyPanSpeed ); + + } + + needsUpdate = true; + break; + + case scope.keys.BOTTOM: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateUp( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( 0, - scope.keyPanSpeed ); + + } + + needsUpdate = true; + break; + + case scope.keys.LEFT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateLeft( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( scope.keyPanSpeed, 0 ); + + } + + needsUpdate = true; + break; + + case scope.keys.RIGHT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateLeft( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( - scope.keyPanSpeed, 0 ); + + } + + needsUpdate = true; + break; + + } + + if ( needsUpdate ) { + + // prevent the browser from scrolling on cursor keys + event.preventDefault(); + + scope.update(); + + } + + + } + + function handleTouchStartRotate() { + + if ( pointers.length === 1 ) { + + rotateStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY ); + + } else { + + const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX ); + const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY ); + + rotateStart.set( x, y ); + + } + + } + + function handleTouchStartPan() { + + if ( pointers.length === 1 ) { + + panStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY ); + + } else { + + const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX ); + const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY ); + + panStart.set( x, y ); + + } + + } + + function handleTouchStartDolly() { + + const dx = pointers[ 0 ].pageX - pointers[ 1 ].pageX; + const dy = pointers[ 0 ].pageY - pointers[ 1 ].pageY; + + const distance = Math.sqrt( dx * dx + dy * dy ); + + dollyStart.set( 0, distance ); + + } + + function handleTouchStartDollyPan() { + + if ( scope.enableZoom ) handleTouchStartDolly(); + + if ( scope.enablePan ) handleTouchStartPan(); + + } + + function handleTouchStartDollyRotate() { + + if ( scope.enableZoom ) handleTouchStartDolly(); + + if ( scope.enableRotate ) handleTouchStartRotate(); + + } + + function handleTouchMoveRotate( event ) { + + if ( pointers.length == 1 ) { + + rotateEnd.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + rotateEnd.set( x, y ); + + } + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + const element = scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + } + + function handleTouchMovePan( event ) { + + if ( pointers.length === 1 ) { + + panEnd.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + panEnd.set( x, y ); + + } + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + } + + function handleTouchMoveDolly( event ) { + + const position = getSecondPointerPosition( event ); + + const dx = event.pageX - position.x; + const dy = event.pageY - position.y; + + const distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + + dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); + + dollyOut( dollyDelta.y ); + + dollyStart.copy( dollyEnd ); + + } + + function handleTouchMoveDollyPan( event ) { + + if ( scope.enableZoom ) handleTouchMoveDolly( event ); + + if ( scope.enablePan ) handleTouchMovePan( event ); + + } + + function handleTouchMoveDollyRotate( event ) { + + if ( scope.enableZoom ) handleTouchMoveDolly( event ); + + if ( scope.enableRotate ) handleTouchMoveRotate( event ); + + } + + // + // event handlers - FSM: listen for events and reset state + // + + function onPointerDown( event ) { + + if ( scope.enabled === false ) return; + + if ( pointers.length === 0 ) { + + scope.domElement.setPointerCapture( event.pointerId ); + + scope.domElement.addEventListener( 'pointermove', onPointerMove ); + scope.domElement.addEventListener( 'pointerup', onPointerUp ); + + } + + // + + addPointer( event ); + + if ( event.pointerType === 'touch' ) { + + onTouchStart( event ); + + } else { + + onMouseDown( event ); + + } + + } + + function onPointerMove( event ) { + + if ( scope.enabled === false ) return; + + if ( event.pointerType === 'touch' ) { + + onTouchMove( event ); + + } else { + + onMouseMove( event ); + + } + + } + + function onPointerUp( event ) { + + removePointer( event ); + + if ( pointers.length === 0 ) { + + scope.domElement.releasePointerCapture( event.pointerId ); + + scope.domElement.removeEventListener( 'pointermove', onPointerMove ); + scope.domElement.removeEventListener( 'pointerup', onPointerUp ); + + } + + scope.dispatchEvent( _endEvent ); + + state = STATE.NONE; + + } + + function onPointerCancel( event ) { + + removePointer( event ); + + } + + function onMouseDown( event ) { + + let mouseAction; + + switch ( event.button ) { + + case 0: + + mouseAction = scope.mouseButtons.LEFT; + break; + + case 1: + + mouseAction = scope.mouseButtons.MIDDLE; + break; + + case 2: + + mouseAction = scope.mouseButtons.RIGHT; + break; + + default: + + mouseAction = - 1; + + } + + switch ( mouseAction ) { + + case MOUSE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseDownDolly( event ); + + state = STATE.DOLLY; + + break; + + case MOUSE.ROTATE: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } else { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } + + break; + + case MOUSE.PAN: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } else { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( _startEvent ); + + } + + } + + function onMouseMove( event ) { + + switch ( state ) { + + case STATE.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleMouseMoveRotate( event ); + + break; + + case STATE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseMoveDolly( event ); + + break; + + case STATE.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseMovePan( event ); + + break; + + } + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return; + + event.preventDefault(); + + scope.dispatchEvent( _startEvent ); + + handleMouseWheel( event ); + + scope.dispatchEvent( _endEvent ); + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.enablePan === false ) return; + + handleKeyDown( event ); + + } + + function onTouchStart( event ) { + + trackPointer( event ); + + switch ( pointers.length ) { + + case 1: + + switch ( scope.touches.ONE ) { + + case TOUCH.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate(); + + state = STATE.TOUCH_ROTATE; + + break; + + case TOUCH.PAN: + + if ( scope.enablePan === false ) return; + + handleTouchStartPan(); + + state = STATE.TOUCH_PAN; + + break; + + default: + + state = STATE.NONE; + + } + + break; + + case 2: + + switch ( scope.touches.TWO ) { + + case TOUCH.DOLLY_PAN: + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchStartDollyPan(); + + state = STATE.TOUCH_DOLLY_PAN; + + break; + + case TOUCH.DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; + + handleTouchStartDollyRotate(); + + state = STATE.TOUCH_DOLLY_ROTATE; + + break; + + default: + + state = STATE.NONE; + + } + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( _startEvent ); + + } + + } + + function onTouchMove( event ) { + + trackPointer( event ); + + switch ( state ) { + + case STATE.TOUCH_ROTATE: + + if ( scope.enableRotate === false ) return; + + handleTouchMoveRotate( event ); + + scope.update(); + + break; + + case STATE.TOUCH_PAN: + + if ( scope.enablePan === false ) return; + + handleTouchMovePan( event ); + + scope.update(); + + break; + + case STATE.TOUCH_DOLLY_PAN: + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchMoveDollyPan( event ); + + scope.update(); + + break; + + case STATE.TOUCH_DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; + + handleTouchMoveDollyRotate( event ); + + scope.update(); + + break; + + default: + + state = STATE.NONE; + + } + + } + + function onContextMenu( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + } + + function addPointer( event ) { + + pointers.push( event ); + + } + + function removePointer( event ) { + + delete pointerPositions[ event.pointerId ]; + + for ( let i = 0; i < pointers.length; i ++ ) { + + if ( pointers[ i ].pointerId == event.pointerId ) { + + pointers.splice( i, 1 ); + return; + + } + + } + + } + + function trackPointer( event ) { + + let position = pointerPositions[ event.pointerId ]; + + if ( position === undefined ) { + + position = new Vector2(); + pointerPositions[ event.pointerId ] = position; + + } + + position.set( event.pageX, event.pageY ); + + } + + function getSecondPointerPosition( event ) { + + const pointer = ( event.pointerId === pointers[ 0 ].pointerId ) ? pointers[ 1 ] : pointers[ 0 ]; + + return pointerPositions[ pointer.pointerId ]; + + } + + // + + scope.domElement.addEventListener( 'contextmenu', onContextMenu ); + + scope.domElement.addEventListener( 'pointerdown', onPointerDown ); + scope.domElement.addEventListener( 'pointercancel', onPointerCancel ); + scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } ); + + // force an update at start + + this.update(); + + } + +} + + +// This set of controls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// This is very similar to OrbitControls, another set of touch behavior +// +// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - left mouse, or arrow keys / touch: one-finger move + +class MapControls extends OrbitControls { + + constructor( object, domElement ) { + + super( object, domElement ); + + this.screenSpacePanning = false; // pan orthogonal to world-space direction camera.up + + this.mouseButtons.LEFT = MOUSE.PAN; + this.mouseButtons.RIGHT = MOUSE.ROTATE; + + this.touches.ONE = TOUCH.PAN; + this.touches.TWO = TOUCH.DOLLY_ROTATE; + + } + +} + +export { OrbitControls, MapControls }; diff --git a/static/scripts/ace.js b/static/scripts/archive/ace.js similarity index 100% rename from static/scripts/ace.js rename to static/scripts/archive/ace.js diff --git a/static/scripts/feather.min.js b/static/scripts/archive/feather.min.js similarity index 100% rename from static/scripts/feather.min.js rename to static/scripts/archive/feather.min.js diff --git a/static/scripts/jquery-3.3.1.min.js b/static/scripts/archive/jquery-3.3.1.min.js similarity index 100% rename from static/scripts/jquery-3.3.1.min.js rename to static/scripts/archive/jquery-3.3.1.min.js diff --git a/static/scripts/mode-javascript.js b/static/scripts/archive/mode-javascript.js similarity index 100% rename from static/scripts/mode-javascript.js rename to static/scripts/archive/mode-javascript.js diff --git a/static/scripts/orbitcontrols.js b/static/scripts/archive/orbitcontrols.js similarity index 100% rename from static/scripts/orbitcontrols.js rename to static/scripts/archive/orbitcontrols.js diff --git a/static/scripts/pako.min.js b/static/scripts/archive/pako.min.js similarity index 100% rename from static/scripts/pako.min.js rename to static/scripts/archive/pako.min.js diff --git a/static/scripts/socket.io-2.1.1.js b/static/scripts/archive/socket.io-2.1.1.js similarity index 100% rename from static/scripts/socket.io-2.1.1.js rename to static/scripts/archive/socket.io-2.1.1.js diff --git a/static/scripts/sprite3D.js b/static/scripts/archive/sprite3D.js similarity index 100% rename from static/scripts/sprite3D.js rename to static/scripts/archive/sprite3D.js diff --git a/static/scripts/theme-twilight.js b/static/scripts/archive/theme-twilight.js similarity index 100% rename from static/scripts/theme-twilight.js rename to static/scripts/archive/theme-twilight.js diff --git a/static/scripts/three.js b/static/scripts/archive/three.js similarity index 100% rename from static/scripts/three.js rename to static/scripts/archive/three.js diff --git a/static/scripts/worker-javascript.js b/static/scripts/archive/worker-javascript.js similarity index 100% rename from static/scripts/worker-javascript.js rename to static/scripts/archive/worker-javascript.js diff --git a/static/scripts/baseSocket.js b/static/scripts/baseSocket.js index 6beb1aa..6f3d815 100644 --- a/static/scripts/baseSocket.js +++ b/static/scripts/baseSocket.js @@ -1,13 +1,16 @@ -var socket; +let socket; var socketClientID; var controllerMessages = []; var hostAddress = "..." var enable3D = true; + $(document).ready(function(){ namespace = '/MaslowCNC'; // change to an empty string to use the global namespace // the socket.io documentation recommends sending an explicit package upon connection // this is specially important when using the global namespace - socket = io.connect('//' + document.domain + ':' + location.port + namespace, {'forceNew':true}); + const serverURL = `//${location.hostname}:${location.port}${namespace}`; + console.log(serverURL); + socket = io(serverURL, {'forceNew': true}); setListeners(); setupStatusButtons(); @@ -173,10 +176,11 @@ function setListeners(){ showFPSpinner(msg.message); break; case 'gcodeUpdateCompressed': - if (enable3D) - gcodeUpdateCompressed(data); - else + if (enable3D) { + frontpage3d.gcodeUpdateCompressed(data); + } else { $("#fpCircle").hide(); + } break; case 'boardDataUpdate': boardDataUpdate(data); @@ -284,7 +288,7 @@ function statusRequest(status){ } function requestPage(page, args=""){ - console.log("requesting page..") + console.log(`requesting page: ${page}`) socket.emit('requestPage',{data:{page:page, isMobile:isMobile, args:args}}); } diff --git a/static/scripts/bootstrap.bundle.min.js b/static/scripts/bootstrap.bundle.min.js deleted file mode 100644 index 72a46cf..0000000 --- a/static/scripts/bootstrap.bundle.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v4.1.3 (https://getbootstrap.com/) - * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],t):t(e.bootstrap={},e.jQuery)}(this,function(e,t){"use strict";function i(e,t){for(var n=0;nthis._items.length-1||e<0))if(this._isSliding)k(this._element).one(q.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),u=0l[e]&&!i.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),Ve({},t,n)}};return c.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=ze({},u,f[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,r=e.placement.split("-")[0],o=Math.floor,s=-1!==["top","bottom"].indexOf(r),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]o(i[a])&&(e.offsets.popper[l]=o(i[a])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!pt(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],o=e.offsets,s=o.popper,a=o.reference,l=-1!==["left","right"].indexOf(r),c=l?"height":"width",u=l?"Top":"Left",f=u.toLowerCase(),h=l?"left":"top",d=l?"bottom":"right",p=nt(i)[c];a[d]-ps[d]&&(e.offsets.popper[f]+=a[f]+p-s[d]),e.offsets.popper=Ge(e.offsets.popper);var m=a[f]+a[c]/2-p/2,g=Pe(e.instance.popper),_=parseFloat(g["margin"+u],10),v=parseFloat(g["border"+u+"Width"],10),y=m-e.offsets.popper[f]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),e.arrowElement=i,e.offsets.arrow=(Ve(n={},f,Math.round(y)),Ve(n,h,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,m){if(at(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var g=$e(p.instance.popper,p.instance.reference,m.padding,m.boundariesElement,p.positionFixed),_=p.placement.split("-")[0],v=it(_),y=p.placement.split("-")[1]||"",E=[];switch(m.behavior){case vt:E=[_,v];break;case yt:E=_t(_);break;case Et:E=_t(_,!0);break;default:E=m.behavior}return E.forEach(function(e,t){if(_!==e||E.length===t+1)return p;_=p.placement.split("-")[0],v=it(_);var n,i=p.offsets.popper,r=p.offsets.reference,o=Math.floor,s="left"===_&&o(i.right)>o(r.left)||"right"===_&&o(i.left)o(r.top)||"bottom"===_&&o(i.top)o(g.right),c=o(i.top)o(g.bottom),f="left"===_&&a||"right"===_&&l||"top"===_&&c||"bottom"===_&&u,h=-1!==["top","bottom"].indexOf(_),d=!!m.flipVariations&&(h&&"start"===y&&a||h&&"end"===y&&l||!h&&"start"===y&&c||!h&&"end"===y&&u);(s||f||d)&&(p.flipped=!0,(s||f)&&(_=E[t+1]),d&&(y="end"===(n=y)?"start":"start"===n?"end":n),p.placement=_+(y?"-"+y:""),p.offsets.popper=ze({},p.offsets.popper,rt(p.instance.popper,p.offsets.reference,p.placement)),p=st(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,r=i.popper,o=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return r[s?"left":"top"]=o[n]-(a?r[s?"width":"height"]:0),e.placement=it(t),e.offsets.popper=Ge(r),e}},hide:{order:800,enabled:!0,fn:function(e){if(!pt(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=ot(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
',trigger:"hover focus",title:"",delay:0,html:!(An={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Dn={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},Nn="out",kn={HIDE:"hide"+wn,HIDDEN:"hidden"+wn,SHOW:(On="show")+wn,SHOWN:"shown"+wn,INSERTED:"inserted"+wn,CLICK:"click"+wn,FOCUSIN:"focusin"+wn,FOCUSOUT:"focusout"+wn,MOUSEENTER:"mouseenter"+wn,MOUSELEAVE:"mouseleave"+wn},xn="fade",Pn="show",Ln=".tooltip-inner",jn=".arrow",Hn="hover",Mn="focus",Fn="click",Wn="manual",Rn=function(){function i(e,t){if("undefined"==typeof Ct)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=yn(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(yn(this.getTipElement()).hasClass(Pn))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),yn.removeData(this.element,this.constructor.DATA_KEY),yn(this.element).off(this.constructor.EVENT_KEY),yn(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&yn(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===yn(this.element).css("display"))throw new Error("Please use show on visible elements");var e=yn.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){yn(this.element).trigger(e);var n=yn.contains(this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=we.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&yn(i).addClass(xn);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:yn(document).find(this.config.container);yn(i).data(this.constructor.DATA_KEY,this),yn.contains(this.element.ownerDocument.documentElement,this.tip)||yn(i).appendTo(a),yn(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Ct(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:jn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){t._handlePopperPlacementChange(e)}}),yn(i).addClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().on("mouseover",null,yn.noop);var l=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,yn(t.element).trigger(t.constructor.Event.SHOWN),e===Nn&&t._leave(null,t)};if(yn(this.tip).hasClass(xn)){var c=we.getTransitionDurationFromElement(this.tip);yn(this.tip).one(we.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},e.hide=function(e){var t=this,n=this.getTipElement(),i=yn.Event(this.constructor.Event.HIDE),r=function(){t._hoverState!==On&&n.parentNode&&n.parentNode.removeChild(n),t._cleanTipClass(),t.element.removeAttribute("aria-describedby"),yn(t.element).trigger(t.constructor.Event.HIDDEN),null!==t._popper&&t._popper.destroy(),e&&e()};if(yn(this.element).trigger(i),!i.isDefaultPrevented()){if(yn(n).removeClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().off("mouseover",null,yn.noop),this._activeTrigger[Fn]=!1,this._activeTrigger[Mn]=!1,this._activeTrigger[Hn]=!1,yn(this.tip).hasClass(xn)){var o=we.getTransitionDurationFromElement(n);yn(n).one(we.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){yn(this.getTipElement()).addClass(Tn+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||yn(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(yn(e.querySelectorAll(Ln)),this.getTitle()),yn(e).removeClass(xn+" "+Pn)},e.setElementContent=function(e,t){var n=this.config.html;"object"==typeof t&&(t.nodeType||t.jquery)?n?yn(t).parent().is(e)||e.empty().append(t):e.text(yn(t).text()):e[n?"html":"text"](t)},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e||(e="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),e},e._getAttachment=function(e){return An[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)yn(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==Wn){var t=e===Hn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Hn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;yn(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}yn(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==e)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Mn:Hn]=!0),yn(t.getTipElement()).hasClass(Pn)||t._hoverState===On?t._hoverState=On:(clearTimeout(t._timeout),t._hoverState=On,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===On&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Mn:Hn]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Nn,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Nn&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){return"number"==typeof(e=l({},this.constructor.Default,yn(this.element).data(),"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),we.typeCheckConfig(En,e,this.constructor.DefaultType),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=yn(this.getTipElement()),t=e.attr("class").match(Sn);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(yn(e).removeClass(xn),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=yn(this).data(bn),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),yn(this).data(bn,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return In}},{key:"NAME",get:function(){return En}},{key:"DATA_KEY",get:function(){return bn}},{key:"Event",get:function(){return kn}},{key:"EVENT_KEY",get:function(){return wn}},{key:"DefaultType",get:function(){return Dn}}]),i}(),yn.fn[En]=Rn._jQueryInterface,yn.fn[En].Constructor=Rn,yn.fn[En].noConflict=function(){return yn.fn[En]=Cn,Rn._jQueryInterface},Rn),Qi=(Bn="popover",Kn="."+(qn="bs.popover"),Qn=(Un=t).fn[Bn],Yn="bs-popover",Vn=new RegExp("(^|\\s)"+Yn+"\\S+","g"),zn=l({},Ki.Default,{placement:"right",trigger:"click",content:"",template:''}),Gn=l({},Ki.DefaultType,{content:"(string|element|function)"}),Jn="fade",Xn=".popover-header",$n=".popover-body",ei={HIDE:"hide"+Kn,HIDDEN:"hidden"+Kn,SHOW:(Zn="show")+Kn,SHOWN:"shown"+Kn,INSERTED:"inserted"+Kn,CLICK:"click"+Kn,FOCUSIN:"focusin"+Kn,FOCUSOUT:"focusout"+Kn,MOUSEENTER:"mouseenter"+Kn,MOUSELEAVE:"mouseleave"+Kn},ti=function(e){var t,n;function i(){return e.apply(this,arguments)||this}n=e,(t=i).prototype=Object.create(n.prototype),(t.prototype.constructor=t).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(e){Un(this.getTipElement()).addClass(Yn+"-"+e)},r.getTipElement=function(){return this.tip=this.tip||Un(this.config.template)[0],this.tip},r.setContent=function(){var e=Un(this.getTipElement());this.setElementContent(e.find(Xn),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find($n),t),e.removeClass(Jn+" "+Zn)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var e=Un(this.getTipElement()),t=e.attr("class").match(Vn);null!==t&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||e=0; i--){ - gcode.remove(gcode.children[i]); + onMouseMove(event) { + if (!this.isMobile) { + this.pos = this.cursorPosition(event); + this.cursor.position.set(this.pos.x, this.pos.y, this.pos.z); + const linePosX = confine(this.pos.x, -48, 48); + const linePosY = confine(this.pos.y, -24, 24); + + let positions = this.cursorVLine.geometry.attributes.position.array; + positions[0] = linePosX; + positions[1] = 24; + positions[2] = -0.001; + positions[3] = linePosX; + positions[4] = -24; + positions[5] = -0.001; + this.cursorVLine.geometry.attributes.position.needsUpdate = true; + + positions = this.cursorHLine.geometry.attributes.position.array; + positions[0] = 48; + positions[1] = linePosY; + positions[2] = -0.001; + positions[3] = -48; + positions[4] = linePosY; + positions[5] = -0.001; + this.cursorHLine.geometry.attributes.position.needsUpdate = true; + + if ($("#units").text() == "MM") { + this.pos.x *= 25.4 + this.pos.y *= 25.4 + } + $("#cursorPosition").text("X: " + this.pos.x.toFixed(2) + ", Y: " + this.pos.y.toFixed(2)); } } - if (textLabels.children.length!=0) { - for (var i = textLabels.children.length -1; i>=0; i--){ - textLabels.remove(textLabels.children[i]); + + board3DDataUpdate(data) { + console.log("updating board data"); + this.boardOutlineGeometry.dispose(); + this.boardOutlineGeometry = new THREE.BoxGeometry(data.width, data.height, data.thickness); + this.boardOutlineFill.geometry = this.boardOutlineGeometry; + this.boardEdgesGeometry = new THREE.EdgesGeometry(this.boardOutlineGeometry) + this.boardOutlineOutline.geometry = this.boardEdgesGeometry; + + this.boardOutlineFill.geometry.needsUpdate = true; + this.boardOutlineOutline.geometry.needsUpdate = true; + this.boardGroup.position.set(data.centerX, data.centerY, data.thickness / -2.0); + } + + positionUpdate(x, y, z) { + if ($("#units").text() == "MM") { + x /= 25.4 + y /= 25.4 + z /= 25.4 } + this.sled.position.set(x, y, z); + this.computedSled.position.setComponent(2, z - 0.01); + + //console.log("x="+x+", y="+y+", z="+z) } - var gcodeLineSegments = new THREE.Geometry(); - var gcodeDashedLineSegments = new THREE.Geometry(); + homePositionUpdate(x, y) { + if ($("#units").text() == "MM") { + x /= 25.4 + y /= 25.4 + } + this.home.position.set(x, y, 0); + //shift any gcode + this.homeX = x; + this.homeY = y; + this.gcode.position.set(x, y, 0); + } - if ((data!=null) && (data!="")){ - var uncompressed = pako.inflate(data); - var _str = ab2str(uncompressed); - var data = JSON.parse(_str) - console.log(data) - var pX, pY, pZ = -99999.9 - var gcodeDashed; - var gcodeUndashed; - data.forEach(function(line) { - if (line.type=='line'){ - //console.log("Line length="+line.points.length+", dashed="+line.dashed); - if (line.dashed==true) { - var gcodeDashedLineSegments = new THREE.Geometry(); - line.points.forEach(function(point) { - gcodeDashedLineSegments.vertices.push(new THREE.Vector3(point[0], point[1], point[2])); - }) - gcodeDashed = new THREE.Line(gcodeDashedLineSegments, greenLineDashedMaterial) - gcodeDashed.computeLineDistances(); - gcode.add(gcodeDashed); - } else { - var gcodeLineSegments = new THREE.Geometry(); - line.points.forEach(function(point) { - gcodeLineSegments.vertices.push(new THREE.Vector3(point[0], point[1], point[2])); - }) - gcodeUndashed = new THREE.Line(gcodeLineSegments, blueLineMaterial) - gcode.add(gcodeUndashed); + gcodePositionUpdate(x, y, z) { + if ($("#units").text() == "MM") { + x /= 25.4 + y /= 25.4 + z /= 25.4 + } + this.gcodePos.position.set(x + homeX, y + homeY, z); + //console.log("x="+x+", y="+y) + } - } + processError3d(data) { + if (!data.computedEnabled) { + if (this.isComputedEnabled) { + this.scene.remove(this.computedSled); + this.isComputedEnabled = false; + } + return; + } else { + var x = data.computedX / 25.4; + var y = data.computedY / 25.4; + if ($("#units").text() == "") { + x /= 25.4 + y /= 25.4 + } + this.computedSled.position.setComponent(0, x); + this.computedSled.position.setComponent(1, y); + if (!this.isComputedEnabled) { + this.scene.add(this.computedSled); + //this.scene.add(this.cutTrailGroup); + this.isComputedEnabled = true; + } + } + } + + gcodeUpdateCompressed(data) { + console.log("updating gcode compressed"); + if (this.gcode.children.length != 0) { + for (let i = this.gcode.children.length - 1; i >= 0; i--) { + this.gcode.remove(this.gcode.children[i]); + } + } + if (this.textLabels.children.length != 0) { + for (var i = this.textLabels.children.length - 1; i >= 0; i--) { + this.textLabels.remove(this.textLabels.children[i]); } - else - { - if ( (line.command == "SpindleOnCW") || (line.command=="SpindlenOnCCW") || (line.command=="SpindleOff") ) //(line.points[1][0]>=3) && (line.points[1][0]<=5) ) - { + } + + const greenLineDashedMaterial = new THREE.LineDashedMaterial({ color: 0x00aa00, dashSize: .1, gapSize: .1 }); + + if ((data != null) && (data != "")) { + var uncompressed = pako.inflate(data); + var _str = ab2str(uncompressed); + var data = JSON.parse(_str) + console.log(data) + + let gcodeDashed; + let gcodeUndashed; + data.forEach((line) => { + if (line.type == 'line') { + //console.log("Line length="+line.points.length+", dashed="+line.dashed); + if (line.dashed == true) { + const gcodeDashedLinePoints = []; + line.points.forEach(function (point) { + gcodeDashedLinePoints.push(new THREE.Vector3(point[0], point[1], point[2])); + }) + const gcodeDashedLineSegments = new THREE.BufferGeometry().setFromPoints( gcodeDashedLinePoints ); + gcodeDashed = new THREE.Line(gcodeDashedLineSegments, greenLineDashedMaterial) + gcodeDashed.computeLineDistances(); + this.gcode.add(gcodeDashed); + } else { + var gcodeLinePoints = []; + line.points.forEach(function (point) { + gcodeLinePoints.push(new THREE.Vector3(point[0], point[1], point[2])); + }) + var gcodeLineSegments = new THREE.BufferGeometry().setFromPoints( gcodeLinePoints ); + gcodeUndashed = new THREE.Line(gcodeLineSegments, blueLineMaterial) + this.gcode.add(gcodeUndashed); + } + } else { + if (["SpindleOnCW", "SpindleOnCCW", "SpindleOff"].includes(line.command)) { + //(line.points[1][0]>=3) && (line.points[1][0]<=5) ) //spindle - var gcodeCircleGeometry = new THREE.CircleGeometry(2.25/32,16); + var gcodeCircleGeometry = new THREE.CircleGeometry(2.25 / 32, 16); var gcodeCircleEdges = new THREE.EdgesGeometry(gcodeCircleGeometry) var circleMaterial = redLineMaterial; - var gcodeCircle = new THREE.LineSegments(gcodeCircleEdges,circleMaterial); + var gcodeCircle = new THREE.LineSegments(gcodeCircleEdges, circleMaterial); gcodeCircle.position.set(line.points[0][0], line.points[0][1], line.points[0][2]); - gcode.add(gcodeCircle); - var gcodeLineSegments = new THREE.Geometry(); + this.gcode.add(gcodeCircle); var xFactor = 3.25; var yFactor = 3.25; - if (line.command == "SpindleOff") //(line.points[1][0]==5) - { - xFactor = .707107*2.25; - yFactor = .707107*2.25; + if (line.command == "SpindleOff") { + //(line.points[1][0]==5) + xFactor = .707107 * 2.25; + yFactor = .707107 * 2.25; } - if (line.command == "SpindleOnCCW") // (line.points[1][0]==4) - { - xFactor = 3.25; - yFactor = 3.25; - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0]+xFactor/32, line.points[0][1]+yFactor/32, line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0]-xFactor/32, line.points[0][1]+yFactor/32, line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0]+xFactor/32, line.points[0][1]-yFactor/32, line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0]-xFactor/32, line.points[0][1]-yFactor/32, line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); - } - else - { - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0]+xFactor/32, line.points[0][1]+yFactor/32, line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0]+xFactor/32, line.points[0][1]-yFactor/32, line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0]-xFactor/32, line.points[0][1]+yFactor/32, line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0]-xFactor/32, line.points[0][1]-yFactor/32, line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); + + const gcodeLinePoints = []; + if (line.command == "SpindleOnCCW") { + // (line.points[1][0]==4) + xFactor = 3.25; + yFactor = 3.25; + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0] + xFactor / 32, line.points[0][1] + yFactor / 32, line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0] - xFactor / 32, line.points[0][1] + yFactor / 32, line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0] + xFactor / 32, line.points[0][1] - yFactor / 32, line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0] - xFactor / 32, line.points[0][1] - yFactor / 32, line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); + } else { + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0] + xFactor / 32, line.points[0][1] + yFactor / 32, line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0] + xFactor / 32, line.points[0][1] - yFactor / 32, line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0] - xFactor / 32, line.points[0][1] + yFactor / 32, line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0] - xFactor / 32, line.points[0][1] - yFactor / 32, line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); } + const gcodeLineSegments = new THREE.BufferGeometry().setFromPoints( gcodeLinePoints ); gcodeUndashed = new THREE.Line(gcodeLineSegments, redLineMaterial) - gcode.add(gcodeUndashed); - if (line.command != "SpindleOff") - { - text = new SpriteText('S'+line.points[1][1].toString(),.1, 'red'); - text.position.x = line.points[0][0]; - text.position.y = line.points[0][1]; - text.position.z = line.points[0][2]; - textLabels.add(text); + this.gcode.add(gcodeUndashed); + if (line.command != "SpindleOff") { + text = new SpriteText('S' + line.points[1][1].toString(), .1, 'red'); + text.position.x = line.points[0][0]; + text.position.y = line.points[0][1]; + text.position.z = line.points[0][2]; + textLabels.add(text); } - } - else if (line.command == "ToolChange") - { - var gcodeCircleGeometry = new THREE.CircleGeometry(2.25/32,16); + } else if (line.command == "ToolChange") { + var gcodeCircleGeometry = new THREE.CircleGeometry(2.25 / 32, 16); var gcodeCircleEdges = new THREE.EdgesGeometry(gcodeCircleGeometry) circleMaterial = redLineMaterial; - text = new SpriteText('T'+line.points[1][1].toString(),.1, 'red'); + text = new SpriteText('T' + line.points[1][1].toString(), .1, 'red'); text.position.x = line.points[0][0]; text.position.y = line.points[0][1]; text.position.z = line.points[0][2]; textLabels.add(text); - var gcodeCircle = new THREE.LineSegments(gcodeCircleEdges,circleMaterial); + var gcodeCircle = new THREE.LineSegments(gcodeCircleEdges, circleMaterial); gcodeCircle.position.set(line.points[0][0], line.points[0][1], line.points[0][2]); - gcode.add(gcodeCircle); - } - else - { - var gcodeCircleGeometry = new THREE.CircleGeometry(line.points[1][0]/32,16); + this.gcode.add(gcodeCircle); + } else { + var gcodeCircleGeometry = new THREE.CircleGeometry(line.points[1][0] / 32, 16); var gcodeCircleEdges = new THREE.EdgesGeometry(gcodeCircleGeometry) var circleMaterial = greenLineMaterial; - var gcodeCircle = new THREE.LineSegments(gcodeCircleEdges,circleMaterial); + var gcodeCircle = new THREE.LineSegments(gcodeCircleEdges, circleMaterial); gcodeCircle.position.set(line.points[0][0], line.points[0][1], line.points[0][2]); - gcode.add(gcodeCircle); + this.gcode.add(gcodeCircle); + } + + const gcodeLinePoints = []; + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); + gcodeLinePoints.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[1][2])); + const gcodeLineSegments = new THREE.BufferGeometry().setFromPoints( gcodeLinePoints ); + gcodeUndashed = new THREE.Line(gcodeLineSegments, blueLineMaterial) + this.gcode.add(gcodeUndashed); } - - var gcodeLineSegments = new THREE.Geometry(); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[0][2])); - gcodeLineSegments.vertices.push(new THREE.Vector3(line.points[0][0], line.points[0][1], line.points[1][2])); - gcodeUndashed = new THREE.Line(gcodeLineSegments, blueLineMaterial) - gcode.add(gcodeUndashed); - + }); + this.scene.add(this.gcode); + if (this.showLabels) { + this.scene.add(this.textLabels); } - }); - scene.add(gcode); - if (showLabels) - scene.add(textLabels); - } - else{ - scene.remove(gcode); - scene.remove(textLabels); + } else { + this.scene.remove(this.gcode); + this.scene.remove(this.textLabels); + } + $("#fpCircle").hide(); } - $("#fpCircle").hide(); - -} -function toggleLabels() -{ - if (showLabels) - { - scene.remove(textLabels); - $("#labelsID").removeClass('btn-primary').addClass('btn-secondary'); + toggleLabels() { + if (this.showLabels) { + this.scene.remove(this.textLabels); + $("#labelsID").removeClass('btn-primary').addClass('btn-secondary'); } - else - { - scene.add(textLabels); - $("#labelsID").removeClass('btn-secondary').addClass('btn-primary'); + else { + this.scene.add(this.textLabels); + $("#labelsID").removeClass('btn-secondary').addClass('btn-primary'); } - showLabels = !showLabels; + this.showLabels = !this.showLabels; + } + + resetView() { + this.controlsO.reset(); + this.controlsP.reset(); + } } -function ab2str(buf) { - var bufView = new Uint16Array(buf); - var unis ="" - for (var i = 0; i < bufView.length; i++) { - unis=unis+String.fromCharCode(bufView[i]); +const frontpage3d = new Frontpage3d(); + +$(document).ready(() => { + frontpage3d.initAll(); + frontpage3d.animate(); + $("#workarea").contextmenu(() => { + if (!frontpage3d.view3D) { + // cursorPosition expects an event + frontpage3d.pos = frontpage3d.cursorPosition({clientX: 0, clientY: 0}); + + x = frontpage3d.pos.x; + x = x.toFixed(4); + frontpage3d.pos.x = x; + + y = frontpage3d.pos.y; + y = y.toFixed(4); + frontpage3d.pos.y = y; + // This global function is defined in baseSocket.js + requestPage("screenAction", frontpage3d.pos) } - return unis -} + }); + console.log(frontpage3d); + window.addEventListener("resize", frontpage3d.onWindowResize, false); + document.onmousemove = (event) => { + frontpage3d.onMouseMove(event); + } +}); -function showFPSpinner(msg){ - $("#fpCircle").show(); +function toggleLabels() { + // Called by frontpage3d.html + frontpage3d.toggleLabels(); } - -function toggle3DView() -{ - console.log("toggling"); - if (view3D){ - controlsO.enableRotate = false; - controlsO.mouseButtons = { - LEFT: THREE.MOUSE.RIGHT, - MIDDLE: THREE.MOUSE.MIDDLE, - RIGHT: THREE.MOUSE.LEFT - } - controlsP.enableRotate = false; - controlsP.mouseButtons = { - LEFT: THREE.MOUSE.RIGHT, - MIDDLE: THREE.MOUSE.MIDDLE, - RIGHT: THREE.MOUSE.LEFT - } - - view3D=false; - if (isMobile) - { - $("#mobilebutton3D").removeClass('btn-primary').addClass('btn-secondary'); - } - else - { - $("#button3D").removeClass('btn-primary').addClass('btn-secondary'); - } - console.log("toggled off"); - } else { - controlsO.enableRotate = true; - controlsO.mouseButtons = { - LEFT: THREE.MOUSE.RIGHT, - MIDDLE: THREE.MOUSE.MIDDLE, - RIGHT: THREE.MOUSE.LEFT - } - controlsP.enableRotate = true; - controlsP.mouseButtons = { - LEFT: THREE.MOUSE.RIGHT, - MIDDLE: THREE.MOUSE.MIDDLE, - RIGHT: THREE.MOUSE.LEFT - } - - view3D=true; - if (isMobile) - { - $("#mobilebutton3D").removeClass('btn-secondary').addClass('btn-primary'); - } - else - { - $("#button3D").removeClass('btn-secondary').addClass('btn-primary'); - } - console.log("toggled on"); - } - controlsO.update(); - controlsP.update(); +function ab2str(buf) { + var bufView = new Uint16Array(buf); + var unis = "" + for (var i = 0; i < bufView.length; i++) { + unis = unis + String.fromCharCode(bufView[i]); + } + return unis } -function resetView(){ - controlsO.reset(); - controlsP.reset(); +/** You spin me right round, baby right round, like a record player, right round, round, round */ +function showFPSpinner(msg) { + $("#fpCircle").show(); } -function cursorPosition(){ - var rect = renderer.domElement.getBoundingClientRect(); - var vec = new THREE.Vector3(); // create once and reuse - var pos = new THREE.Vector3(); // create once and reuse - vec.set( - ( ( event.clientX - rect.left ) / ( rect.right - rect.left ) ) * 2 - 1, - - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1, - 0.5 ); - - if (cameraPerspective == 0) - { - vec.unproject( cameraO ); - vec.sub( cameraO.position ).normalize(); - var distance = - cameraO.position.z / vec.z; - pos.copy( cameraO.position ).add( vec.multiplyScalar( distance ) ); - } - else - { - vec.unproject( cameraP ); - vec.sub( cameraP.position ).normalize(); - var distance = - cameraP.position.z / vec.z; - pos.copy( cameraP.position ).add( vec.multiplyScalar( distance ) ); - } - //console.log(pos); - return(pos); +function resetView() { + // Called by frontpage3d.html and frontpage3d_mobile.html + frontpage3d.resetView(); } -function processCameraMessage(data){ - if(data.command=="cameraImageUpdated"){ - var newImg = new Image(); - if (imageShowing==1) - { - newImg.onload = function() { - document.getElementById("cameraImage2").setAttribute('src',this.src); - if (isMobile){ - document.getElementById("mobileCameraDiv2").style.zIndex = "95"; - document.getElementById("mobileCameraDiv1").style.zIndex = "94"; - } else { - document.getElementById("cameraDiv2").style.zIndex = "95"; - document.getElementById("cameraDiv1").style.zIndex = "94"; - } - imageShowing = 2 - } - } - else - { - newImg.onload = function() { - document.getElementById("cameraImage1").setAttribute('src',this.src); - if (isMobile){ - document.getElementById("mobileCameraDiv1").style.zIndex = "95"; - document.getElementById("mobileCameraDiv2").style.zIndex = "94"; - } else { - document.getElementById("cameraDiv1").style.zIndex = "95"; - document.getElementById("cameraDiv2").style.zIndex = "94"; - } - imageShowing = 1 - } +function processCameraMessage(data) { + if (data.command == "cameraImageUpdated") { + var newImg = new Image(); + if (imageShowing == 1) { + newImg.onload = function () { + document.getElementById("cameraImage2").setAttribute('src', this.src); + if (isMobile) { + document.getElementById("mobileCameraDiv2").style.zIndex = "95"; + document.getElementById("mobileCameraDiv1").style.zIndex = "94"; + } else { + document.getElementById("cameraDiv2").style.zIndex = "95"; + document.getElementById("cameraDiv1").style.zIndex = "94"; } - newImg.setAttribute('src', 'data:image/png;base64,'+data.data) - + imageShowing = 2 + } } - if(data.command=="updateCamera") - { - if (data.data=="on"){ - $("#videoStatus svg.feather.feather-video-off").replaceWith(feather.icons.video.toSvg()); - feather.replace(); - console.log("video on"); - document.getElementById("cameraImage1").style.display = "block" - document.getElementById("cameraImage2").style.display = "block" - if (isMobile) - document.getElementById("mobileCameraArea").style.display = "block" - } - - if (data.data=="off"){ - $("#videoStatus svg.feather.feather-video").replaceWith(feather.icons["video-off"].toSvg()); - feather.replace(); - console.log("video off") - document.getElementById("cameraImage1").style.display = "none"; - document.getElementById("cameraImage2").style.display = "none" - if (isMobile) - document.getElementById("mobileCameraArea").style.display = "none" + else { + newImg.onload = function () { + document.getElementById("cameraImage1").setAttribute('src', this.src); + if (isMobile) { + document.getElementById("mobileCameraDiv1").style.zIndex = "95"; + document.getElementById("mobileCameraDiv2").style.zIndex = "94"; + } else { + document.getElementById("cameraDiv1").style.zIndex = "95"; + document.getElementById("cameraDiv2").style.zIndex = "94"; } + imageShowing = 1 + } } -} + newImg.setAttribute('src', 'data:image/png;base64,' + data.data) -document.onmousemove = function(event){ - if (!isMobile) - { - pos = cursorPosition(); - cursor.position.set(pos.x,pos.y,pos.z); - var linePosX = confine(pos.x,-48, 48); - var linePosY = confine(pos.y,-24, 24); - - var positions = cursorVLine.geometry.attributes.position.array; - positions[0]=linePosX; - positions[1]=24; - positions[2]=-0.001; - positions[3]=linePosX; - positions[4]=-24; - positions[5]=-0.001; - cursorVLine.geometry.attributes.position.needsUpdate=true; - - positions = cursorHLine.geometry.attributes.position.array; - positions[0]=48; - positions[1]=linePosY; - positions[2]=-0.001; - positions[3]=-48; - positions[4]=linePosY; - positions[5]=-0.001; - cursorHLine.geometry.attributes.position.needsUpdate=true; - - - if ($("#units").text()=="MM"){ - pos.x *= 25.4 - pos.y *= 25.4 - } - $("#cursorPosition").text("X: "+pos.x.toFixed(2)+", Y: "+pos.y.toFixed(2)); + } + if (data.command == "updateCamera") { + if (data.data == "on") { + $("#videoStatus svg.feather.feather-video-off").replaceWith(feather.icons.video.toSvg()); + feather.replace(); + console.log("video on"); + document.getElementById("cameraImage1").style.display = "block" + document.getElementById("cameraImage2").style.display = "block" + if (isMobile) + document.getElementById("mobileCameraArea").style.display = "block" } -} -function confine(value, low, high) -{ - if (valuehigh) - return high; - return value; + if (data.data == "off") { + $("#videoStatus svg.feather.feather-video").replaceWith(feather.icons["video-off"].toSvg()); + feather.replace(); + console.log("video off") + document.getElementById("cameraImage1").style.display = "none"; + document.getElementById("cameraImage2").style.display = "none" + if (isMobile) + document.getElementById("mobileCameraArea").style.display = "none" + } + } } -function board3DDataUpdate(data){ - console.log("updating board data"); - boardOutlineGeometry.dispose(); - boardOutlineGeometry = new THREE.BoxBufferGeometry(boardWidth,boardHeight,boardThickness); - boardOutlineFill.geometry = boardOutlineGeometry; - boardEdgesGeometry = new THREE.EdgesGeometry( boardOutlineGeometry ) - boardOutlineOutline.geometry = boardEdgesGeometry; - - boardOutlineFill.geometry.needsUpdate=true; - boardOutlineOutline.geometry.needsUpdate=true; - boardGroup.position.set(boardCenterX,boardCenterY,boardThickness/-2.0); - +function confine(value, low, high) { + if (value < low) + return low; + if (value > high) + return high; + return value; } -function boardCutDataUpdateCompressed(data){ +function boardCutDataUpdateCompressed(data) { console.log("updating board cut data compressed"); - if (cutSquareGroup.children.length!=0) { - for (var i = cutSquareGroup.children.length -1; i>=0; i--){ - cutSquareGroup.remove(cutSquareGroup.children[i]); + if (cutSquareGroup.children.length != 0) { + for (var i = cutSquareGroup.children.length - 1; i >= 0; i--) { + cutSquareGroup.remove(cutSquareGroup.children[i]); } } - if (data!=null){ + if (data != null) { //var cutSquareMaterial = new THREE.MeshBasicMaterial( {color:0xffff00, side: THREE.DoubleSide}); - var cutSquareMaterial = new THREE.MeshBasicMaterial( {color:0xff6666}); - var noncutSquareMaterial = new THREE.MeshBasicMaterial( {color:0x333333}); + var cutSquareMaterial = new THREE.MeshBasicMaterial({ color: 0xff6666 }); + var noncutSquareMaterial = new THREE.MeshBasicMaterial({ color: 0x333333 }); var uncompressed = pako.inflate(data); var _str = ab2str(uncompressed); var data = JSON.parse(_str) var pointsX = Math.ceil(boardWidth) var pointsY = Math.ceil(boardHeight) - console.log("boardWidth="+boardWidth) - console.log("boardHeight="+boardHeight) - console.log("boardCenterY="+boardCenterY) + console.log("boardWidth=" + boardWidth) + console.log("boardHeight=" + boardHeight) + console.log("boardCenterY=" + boardCenterY) var offsetX = pointsX / 2 var offsetY = pointsY / 2 - for (var x =0; x240) { - g=0 - b=(pwr-240)*16 - } - } - var h = r * 0x10000 + g * 0x100 + b * 0x1; - return '#' + ('000000' + h.toString(16)).slice(-6); -} - -function processPositionMessage(data){ - $('#positionMessage').html('X:'+parseFloat(data.xval).toFixed(2)+' Y:'+parseFloat(data.yval).toFixed(2)+' Z:'+parseFloat(data.zval).toFixed(2)+' V:'+parseFloat(data.vel).toFixed(2)+'/'+parseFloat(data.rqvel).toFixed(2)); + var r, g, b = 0; + + if (pwr < 127) { + g = 255; + r = pwr * 2; + } + else { + r = 255; + g = 255 - (pwr - 127) * 2; + if (pwr > 240) { + g = 0 + b = (pwr - 240) * 16 + } + } + var h = r * 0x10000 + g * 0x100 + b * 0x1; + return '#' + ('000000' + h.toString(16)).slice(-6); +} + +/** parse the supplied value as a float and return it with 2 decimal places */ +function dp2(value) { + return parseFloat(value).toFixed(2) +} + +function processPositionMessage(data) { + $('#positionMessage').html(`X:${dp2(data.xval)} Y:${dp(data.yval)} Z:${dp(data.zval)} V:${(data.vel)}/${(data.rqvel)}`); $('#percentComplete').html(data.pcom) $('#machineState').html(data.state) - if (enable3D) - positionUpdate(data.xval,data.yval,data.zval); + if (enable3D) { + frontpage3d.positionUpdate(data.xval, data.yval, data.zval); + } $('#leftError').css('background-color', pwr2color(data.lpwr)).attr('title', data.lpwr); $('#rightError').css('background-color', pwr2color(data.rpwr)).attr('title', data.rpwr); $('#zError').css('background-color', pwr2color(data.zpwr)).attr('title', data.zpwr); } -function processErrorValueMessage(data){ - $('#leftError').css('width', data.leftError*100+'%').attr('aria-valuenow', data.leftError*100); - $('#rightError').css('width', data.rightError*100+'%').attr('aria-valuenow', data.rightError*100); - if (enable3D){ - processError3d(data) - } +function processErrorValueMessage(data) { + $('#leftError').css('width', data.leftError * 100 + '%').attr('aria-valuenow', data.leftError * 100); + $('#rightError').css('width', data.rightError * 100 + '%').attr('aria-valuenow', data.rightError * 100); + if (enable3D) { + frontpage3d.processError3d(data) + } } - -function processHomePositionMessage(data){ +function processHomePositionMessage(data) { console.log(data.xval) - $('#homePositionMessage').html('X:'+parseFloat(data.xval).toFixed(2)+' Y:'+parseFloat(data.yval).toFixed(2)); - if (enable3D) - homePositionUpdate(data.xval,data.yval); + $('#homePositionMessage').html(`X:${dp(data.xval)} Y:${dp(data.yval)}`); + if (enable3D) { + frontpage3d.homePositionUpdate(data.xval, data.yval); + } } -function processGCodePositionMessage(data){ - $('#gcodePositionMessage').html('X:'+parseFloat(data.xval).toFixed(2)+' Y:'+parseFloat(data.yval).toFixed(2)+' Z:'+parseFloat(data.zval).toFixed(2)); +function processGCodePositionMessage(data) { + $('#gcodePositionMessage').html(`X:${dp(data.xval)} Y:${dp(data.yval)} Z:${dp(data.zval)}`); $('#gcodeLine').html(data.gcodeLine); - $('#gcodeLineIndex').val(data.gcodeLineIndex+1) - if (enable3D) - gcodePositionUpdate(data.xval,data.yval,data.zval); + $('#gcodeLineIndex').val(data.gcodeLineIndex + 1) + if (enable3D) { + frontpage3d.gcodePositionUpdate(data.xval, data.yval, data.zval); + } } -function showFPSpinner(msg){ - $("#fpCircle").show(); +function showFPSpinner(msg) { + $("#fpCircle").show(); } -function processControllerMessage(data){ - if (controllerMessages.length >100) - controllerMessages.shift(); - controllerMessages.push(data); - $('#controllerMessage').html(''); - controllerMessages.forEach(function(message){ - $('#controllerMessage').append(message+"
"); - }); - $('#controllerMessage').scrollBottom(); +function processControllerMessage(data) { + if (controllerMessages.length > 100) + controllerMessages.shift(); + controllerMessages.push(data); + $('#controllerMessage').html(''); + controllerMessages.forEach(function (message) { + $('#controllerMessage').append(message + "
"); + }); + $('#controllerMessage').scrollBottom(); } -function processAlarm(data){ - console.log("alarm received"); - $("#alarms").html(""+data.message+""); - $("#alarms").removeClass('alert-success').addClass('alert-danger'); - $("#stopButton").addClass('stopbutton'); +function processAlarm(data) { + console.log("alarm received"); + $("#alarms").html("" + data.message + ""); + $("#alarms").removeClass('alert-success').addClass('alert-danger'); + $("#stopButton").addClass('stopbutton'); } -function clearAlarm(data){ - console.log("clearing alarm"); - $("#alarms").text("Alarm cleared."); - $("#alarms").removeClass('alert-danger').addClass('alert-success'); - $("#stopButton").removeClass('stopbutton'); +function clearAlarm(data) { + console.log("clearing alarm"); + $("#alarms").text("Alarm cleared."); + $("#alarms").removeClass('alert-danger').addClass('alert-success'); + $("#stopButton").removeClass('stopbutton'); } - -function boardDataUpdate(data){ +function boardDataUpdate(data) { console.log("updating board data"); boardWidth = data.width; boardHeight = data.height; @@ -155,49 +163,49 @@ function boardDataUpdate(data){ boardCenterY = data.centerY; boardID = data.boardID; boardMaterial = data.material; - $("#boardID").text("Board: "+boardID+", Material: "+boardMaterial); + $("#boardID").text("Board: " + boardID + ", Material: " + boardMaterial); if (enable3D) - board3DDataUpdate(data); + frontpage3d.board3DDataUpdate(data); } function moveAction(direction) { - distance = $("#distToMove").val(); - distanceValid = distance.search(/^[0-9]*(\.[0-9]{0,3})?$/); - if (distanceValid == 0) { - action('move', direction, distance); - } else { - $("#distToMove").focus(); - } -} - -function processStatusMessage(data){ - //console.log(data) - if (data.uploadFlag==1){ - if (isDisabled!=data.uploadFlag){ - $('.disabler').prop('disabled', true); - $('.ndisabler').prop('disabled', false); - $('.gcdisabler').prop('disabled', true); - isDisabled =data.uploadFlag; - } - } else if (data.uploadFlag==2 || data.uploadFlag==-1 ){ - if (isDisabled!=data.uploadFlag){ - $('.gcdisabler').prop('disabled', true); - $('.disabler').prop('disabled', false); - $('.ndisabler').prop('disabled', false); - isDisabled = data.uploadFlag; - } - } - else { - if (isDisabled!=data.uploadFlag){ - $('.disabler').prop('disabled', false); - $('.ndisabler').prop('disabled', true); - $('.gcdisabler').prop('disabled', false); - isDisabled = data.uploadFlag; - } + distance = $("#distToMove").val(); + distanceValid = distance.search(/^[0-9]*(\.[0-9]{0,3})?$/); + if (distanceValid == 0) { + action('move', direction, distance); + } else { + $("#distToMove").focus(); + } +} + +function processStatusMessage(data) { + //console.log(data) + if (data.uploadFlag == 1) { + if (isDisabled != data.uploadFlag) { + $('.disabler').prop('disabled', true); + $('.ndisabler').prop('disabled', false); + $('.gcdisabler').prop('disabled', true); + isDisabled = data.uploadFlag; } - $("#currentTool").text(data.currentTool.toString()); - if (data.positioningMode == 0) - $("#currentPositioningMode").text("Absolute (G90)"); - else - $("#currentPositioningMode").text("Incremental (G91)"); + } else if (data.uploadFlag == 2 || data.uploadFlag == -1) { + if (isDisabled != data.uploadFlag) { + $('.gcdisabler').prop('disabled', true); + $('.disabler').prop('disabled', false); + $('.ndisabler').prop('disabled', false); + isDisabled = data.uploadFlag; + } + } + else { + if (isDisabled != data.uploadFlag) { + $('.disabler').prop('disabled', false); + $('.ndisabler').prop('disabled', true); + $('.gcdisabler').prop('disabled', false); + isDisabled = data.uploadFlag; + } + } + $("#currentTool").text(data.currentTool.toString()); + if (data.positioningMode == 0) + $("#currentPositioningMode").text("Absolute (G90)"); + else + $("#currentPositioningMode").text("Incremental (G91)"); } diff --git a/static/scripts/opticalCalibration.js b/static/scripts/opticalCalibration.js index 49a349a..590ff5f 100644 --- a/static/scripts/opticalCalibration.js +++ b/static/scripts/opticalCalibration.js @@ -1,3 +1,6 @@ +import * as THREE from "../node_modules/three/build/three.module.js"; +import { OrbitControls } from "./OrbitControls.js"; + settingRequest('Optical Calibration Settings','calibrationCurve'); settingRequest('Optical Calibration Settings','calibrationError'); @@ -332,7 +335,7 @@ function setupDisplay(){ oimageShowing = 1 ocamera = new THREE.PerspectiveCamera(45, ow/oh, 1, 500); - ocontrols = new THREE.OrbitControls(ocamera, orenderer.domElement); + ocontrols = new OrbitControls(ocamera, orenderer.domElement); ocontrols.screenSpacePanning = true; ocamera.position.set(0, 0, 100); diff --git a/templates/archive/frontpage.html b/templates/archive/frontpage.html index 9664327..58af724 100644 --- a/templates/archive/frontpage.html +++ b/templates/archive/frontpage.html @@ -5,8 +5,8 @@ {% endblock %} {% block javascript %} - - + + diff --git a/templates/archive/frontpage_mobile.html b/templates/archive/frontpage_mobile.html index a106403..79dc5ac 100644 --- a/templates/archive/frontpage_mobile.html +++ b/templates/archive/frontpage_mobile.html @@ -6,8 +6,8 @@ {% endblock %} {% block javascript %} - - + + diff --git a/templates/base.html b/templates/base.html index 0824927..edd6efd 100644 --- a/templates/base.html +++ b/templates/base.html @@ -6,9 +6,9 @@ - - - + + + diff --git a/templates/editGCode.html b/templates/editGCode.html index f56e072..044e820 100644 --- a/templates/editGCode.html +++ b/templates/editGCode.html @@ -6,7 +6,7 @@ {% endblock %} {% block javascript %} - + - - - - - - + + + + + {% endblock %} diff --git a/templates/frontpage3d_mobile.html b/templates/frontpage3d_mobile.html index de18b45..80880f7 100644 --- a/templates/frontpage3d_mobile.html +++ b/templates/frontpage3d_mobile.html @@ -6,12 +6,11 @@ {% endblock %} {% block javascript %} - - - - - - + + + + + {% endblock %} diff --git a/templates/frontpage3d_mobilecontrols.html b/templates/frontpage3d_mobilecontrols.html index 2aeb735..574436c 100644 --- a/templates/frontpage3d_mobilecontrols.html +++ b/templates/frontpage3d_mobilecontrols.html @@ -6,7 +6,7 @@ {% endblock %} {% block javascript %} - + {% endblock %} diff --git a/templates/frontpageText.html b/templates/frontpageText.html index 69fce26..305122b 100644 --- a/templates/frontpageText.html +++ b/templates/frontpageText.html @@ -9,7 +9,7 @@ - + {% endblock %} diff --git a/templates/logs.html b/templates/logs.html index bdf441d..bb045e1 100644 --- a/templates/logs.html +++ b/templates/logs.html @@ -5,9 +5,9 @@ - - - + + + - + {% endblock %} diff --git a/templates/resetChains_mobile.html b/templates/resetChains_mobile.html index 067dbb1..8986837 100644 --- a/templates/resetChains_mobile.html +++ b/templates/resetChains_mobile.html @@ -124,6 +124,6 @@

Right Motor

{% block javascript %} - + {% endblock %} diff --git a/templates/screenAction.html b/templates/screenAction.html index e900a6f..e47f85d 100644 --- a/templates/screenAction.html +++ b/templates/screenAction.html @@ -25,6 +25,6 @@ {% endblock %} {% block javascript %} - + {% endblock %} diff --git a/templates/sendGCode.html b/templates/sendGCode.html index d7791db..26d3ad4 100644 --- a/templates/sendGCode.html +++ b/templates/sendGCode.html @@ -7,7 +7,7 @@ {% endblock %} {% block javascript %} - + - + {% endblock %} diff --git a/templates/setSprockets_mobile.html b/templates/setSprockets_mobile.html index 4a0f99a..ee014c2 100644 --- a/templates/setSprockets_mobile.html +++ b/templates/setSprockets_mobile.html @@ -131,6 +131,6 @@

Right Motor

{% block javascript %} - + {% endblock %} diff --git a/templates/setZaxis.html b/templates/setZaxis.html index 5dd2b91..b54e9dc 100644 --- a/templates/setZaxis.html +++ b/templates/setZaxis.html @@ -53,6 +53,6 @@
Step 2: Move Z to Limits and save position
{% block javascript %} - + {% endblock %} diff --git a/templates/setZaxis_mobile.html b/templates/setZaxis_mobile.html index 78fee67..0ae7a53 100644 --- a/templates/setZaxis_mobile.html +++ b/templates/setZaxis_mobile.html @@ -48,6 +48,6 @@
Step 2: Move Z to Limits and save position
{% block javascript %} - + {% endblock %} diff --git a/templates/zaxis.html b/templates/zaxis.html index 024e8c4..70880fc 100644 --- a/templates/zaxis.html +++ b/templates/zaxis.html @@ -35,6 +35,6 @@ {% block javascript %} - + {% endblock %} diff --git a/templates/zaxis_mobile.html b/templates/zaxis_mobile.html index a22c217..f67d3e7 100644 --- a/templates/zaxis_mobile.html +++ b/templates/zaxis_mobile.html @@ -37,6 +37,6 @@ {% block javascript %} - + {% endblock %} From 2c9634d41e378cdd4092bd42d884c9947a125536 Mon Sep 17 00:00:00 2001 From: md8n Date: Thu, 29 Dec 2022 09:23:56 +0900 Subject: [PATCH 03/34] bootstrap 3->5.2 migration --- README.md | 12 ++ static/scripts/base.js | 2 +- static/scripts/baseSocket.js | 12 +- static/scripts/frontpage3d.js | 276 +++++++++++++------------- static/scripts/frontpage3dcontrols.js | 167 ++++++++-------- templates/actions.html | 10 +- templates/base.html | 96 ++++----- templates/frontpage3d.html | 10 +- templates/frontpage3d_mobile.html | 8 +- templates/gpio.html | 2 +- templates/gpio_mobile.html | 2 +- templates/openBoard.html | 4 +- templates/openGCode.html | 4 +- templates/opticalCalibration.html | 24 +-- templates/pidTuning.html | 16 +- templates/saveBoard.html | 4 +- templates/saveGCode.html | 4 +- templates/settings.html | 6 +- templates/settings_mobile.html | 10 +- templates/uploadGCode.html | 2 +- 20 files changed, 349 insertions(+), 322 deletions(-) diff --git a/README.md b/README.md index a0eef99..c711d74 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,18 @@ And that's the virtual environment creation and set up done. From now on you'll When running on the Pi, you'll also need some extra dependencies and will need to build OpenCV from source. See the Dockerfile for details. (TODO: add instructions here) +### Get the Client Side Libraries Set Up + +We're using `npm` (Node Package Manager) to manage the third party JavaScript libraries in use for the client side code (the stuff that actually runs in your browser), `npm` comes with `node`. + +Note that we're not using `node` as the web server, we're only using it for `npm` to manage the JavaScript libraries. + +To manage `node` it is better to use a node version manager, such as [nvm](https://github.com/nvm-sh/nvm) (or [nvm-windows](https://github.com/coreybutler/nvm-windows) for windows). + +Once `nvm` is installed. Run `nvm install lts` to install the latest 'long term support' version of `node`. Which will get you both `node` and `npm`. There are other `nvm` commands, such as `nvm current` to show which version of `node` you're currently using, and `nvm use lts` to switch `nvm` to using the latest `lts` version of `node` if it was on another version. + +Then finally go to the `static` folder in the WebControl project, which is where all the Javascript lives, and run `npm install` to get it to download all of the required JavaScript packages. It will create a folder called `node_modules` and put them all within that. + ### Now What? Let's Start Up WebControl🎉 Then you can run the code with. diff --git a/static/scripts/base.js b/static/scripts/base.js index 4964cbb..9d433c5 100644 --- a/static/scripts/base.js +++ b/static/scripts/base.js @@ -11,7 +11,7 @@ if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine $(document).ready(function(){ // Make all navbar drop down items collapse the menu when clicked. if (isMobile) { - $("#navbarSupportedContent a.dropdown-item").attr("data-toggle", "collapse").attr("data-target", "#navbarSupportedContent"); + $("#navbarSupportedContent a.dropdown-item").attr("data-bs-toggle", "collapse").attr("data-bs-target", "#navbarSupportedContent"); } }) diff --git a/static/scripts/baseSocket.js b/static/scripts/baseSocket.js index 6f3d815..fd3b271 100644 --- a/static/scripts/baseSocket.js +++ b/static/scripts/baseSocket.js @@ -130,8 +130,9 @@ function setListeners(){ break; case 'cameraMessage': //completed - if (enable3D) - processCameraMessage(data); + if (enable3D) { + frontpage3d.processCameraMessage(data); + } break; case 'positionMessage': //completed @@ -186,10 +187,11 @@ function setListeners(){ boardDataUpdate(data); break; case 'boardCutDataUpdateCompressed': - if (enable3D) - boardCutDataUpdateCompressed(data); - else + if (enable3D) { + frontpage3d.boardCutDataUpdateCompressed(data); + } else { $("#fpCircle").hide(); + } break; case 'updatePorts': //completed diff --git a/static/scripts/frontpage3d.js b/static/scripts/frontpage3d.js index 6413644..e949d15 100644 --- a/static/scripts/frontpage3d.js +++ b/static/scripts/frontpage3d.js @@ -317,12 +317,20 @@ class Frontpage3d { } } + confine(value, low, high) { + if (value < low) + return low; + if (value > high) + return high; + return value; + } + onMouseMove(event) { if (!this.isMobile) { this.pos = this.cursorPosition(event); this.cursor.position.set(this.pos.x, this.pos.y, this.pos.z); - const linePosX = confine(this.pos.x, -48, 48); - const linePosY = confine(this.pos.y, -24, 24); + const linePosX = this.confine(this.pos.x, -48, 48); + const linePosY = this.confine(this.pos.y, -24, 24); let positions = this.cursorVLine.geometry.attributes.position.array; positions[0] = linePosX; @@ -568,6 +576,134 @@ class Frontpage3d { this.controlsO.reset(); this.controlsP.reset(); } + + processCameraMessage(data) { + if (!["cameraImageUpdated", "updateCamera"].includes(data.command)) { + return; + } + + if (data.command == "cameraImageUpdated") { + var newImg = new Image(); + if (imageShowing == 1) { + newImg.onload = function () { + document.getElementById("cameraImage2").setAttribute('src', this.src); + if (isMobile) { + document.getElementById("mobileCameraDiv2").style.zIndex = "95"; + document.getElementById("mobileCameraDiv1").style.zIndex = "94"; + } else { + document.getElementById("cameraDiv2").style.zIndex = "95"; + document.getElementById("cameraDiv1").style.zIndex = "94"; + } + imageShowing = 2; + } + } else { + newImg.onload = function () { + document.getElementById("cameraImage1").setAttribute('src', this.src); + if (isMobile) { + document.getElementById("mobileCameraDiv1").style.zIndex = "95"; + document.getElementById("mobileCameraDiv2").style.zIndex = "94"; + } else { + document.getElementById("cameraDiv1").style.zIndex = "95"; + document.getElementById("cameraDiv2").style.zIndex = "94"; + } + imageShowing = 1; + } + } + newImg.setAttribute('src', 'data:image/png;base64,' + data.data); + return; + } + // data.command == "updateCamera" + if (data.data == "on") { + $("#videoStatus svg.feather.feather-video-off").replaceWith(feather.icons.video.toSvg()); + feather.replace(); + console.log("video on"); + document.getElementById("cameraImage1").style.display = "block"; + document.getElementById("cameraImage2").style.display = "block"; + if (isMobile) { + document.getElementById("mobileCameraArea").style.display = "block"; + } + return; + } + + // data.data == "off" + $("#videoStatus svg.feather.feather-video").replaceWith(feather.icons["video-off"].toSvg()); + feather.replace(); + console.log("video off"); + document.getElementById("cameraImage1").style.display = "none"; + document.getElementById("cameraImage2").style.display = "none"; + if (isMobile){ + document.getElementById("mobileCameraArea").style.display = "none"; + } + } + + boardCutDataUpdateCompressed(data) { + console.log("updating board cut data compressed"); + if (this.cutSquareGroup.children.length != 0) { + for (var i = this.cutSquareGroup.children.length - 1; i >= 0; i--) { + this.cutSquareGroup.remove(this.cutSquareGroup.children[i]); + } + } + if (data != null) { + //var cutSquareMaterial = new THREE.MeshBasicMaterial( {color:0xffff00, side: THREE.DoubleSide}); + var cutSquareMaterial = new THREE.MeshBasicMaterial({ color: 0xff6666 }); + // var noncutSquareMaterial = new THREE.MeshBasicMaterial({ color: 0x333333 }); + var uncompressed = pako.inflate(data); + var _str = ab2str(uncompressed); + var data = JSON.parse(_str) + + var pointsX = Math.ceil(boardWidth) + var pointsY = Math.ceil(boardHeight) + console.log("boardWidth=" + boardWidth) + console.log("boardHeight=" + boardHeight) + console.log("boardCenterY=" + boardCenterY) + var offsetX = pointsX / 2 + var offsetY = pointsY / 2 + + for (var x = 0; x < pointsX; x++) { + for (var y = 0; y < pointsY; y++) { + if (data[x + y * pointsX]) { + //console.log(x+", "+y); + var geometry = new THREE.PlaneGeometry(1, 1); + var plane = new THREE.Mesh(geometry, cutSquareMaterial); + plane.position.set(x - offsetX + boardCenterX, y - offsetY + boardCenterY, 0.01); + this.cutSquareGroup.add(plane); + }/* + else{ + var geometry = new THREE.PlaneGeometry(1,1); + var plane = new THREE.Mesh(geometry, noncutSquareMaterial); + plane.position.set(x-offsetX+boardCenterX, y-offsetY+boardCenterY, 0.01); + this.cutSquareGroup.add(plane); + }*/ + } + } + } + var e = new Date(); + $("#fpCircle").hide(); + } + + toggleBoard() { + if (this.showBoard) { + this.showBoard = false; + this.scene.remove(this.cutSquareGroup); + this.scene.remove(this.boardGroup); + $("#boardID").removeClass('btn-primary').addClass('btn-secondary'); + } else { + this.showBoard = true; + this.scene.add(this.cutSquareGroup); + this.scene.add(this.boardGroup); + $("#boardID").removeClass('btn-secondary').addClass('btn-primary'); + } + } + + toggle3DPO() { + this.cameraPerspective = !this.cameraPerspective; + const txt = this.cameraPerspective == 0 ? "Ortho" : "Persp"; + const camera = this.cameraPerspective == 0 ? this.cameraO : this.cameraP; + + $("#buttonPO").text(txt); + $("#mobilebuttonPO").text(txt); + this.renderer.render(this.scene, camera); + } } const frontpage3d = new Frontpage3d(); @@ -622,140 +758,12 @@ function resetView() { frontpage3d.resetView(); } -function processCameraMessage(data) { - if (data.command == "cameraImageUpdated") { - var newImg = new Image(); - if (imageShowing == 1) { - newImg.onload = function () { - document.getElementById("cameraImage2").setAttribute('src', this.src); - if (isMobile) { - document.getElementById("mobileCameraDiv2").style.zIndex = "95"; - document.getElementById("mobileCameraDiv1").style.zIndex = "94"; - } else { - document.getElementById("cameraDiv2").style.zIndex = "95"; - document.getElementById("cameraDiv1").style.zIndex = "94"; - } - imageShowing = 2 - } - } - else { - newImg.onload = function () { - document.getElementById("cameraImage1").setAttribute('src', this.src); - if (isMobile) { - document.getElementById("mobileCameraDiv1").style.zIndex = "95"; - document.getElementById("mobileCameraDiv2").style.zIndex = "94"; - } else { - document.getElementById("cameraDiv1").style.zIndex = "95"; - document.getElementById("cameraDiv2").style.zIndex = "94"; - } - imageShowing = 1 - } - } - newImg.setAttribute('src', 'data:image/png;base64,' + data.data) - - } - if (data.command == "updateCamera") { - if (data.data == "on") { - $("#videoStatus svg.feather.feather-video-off").replaceWith(feather.icons.video.toSvg()); - feather.replace(); - console.log("video on"); - document.getElementById("cameraImage1").style.display = "block" - document.getElementById("cameraImage2").style.display = "block" - if (isMobile) - document.getElementById("mobileCameraArea").style.display = "block" - } - - if (data.data == "off") { - $("#videoStatus svg.feather.feather-video").replaceWith(feather.icons["video-off"].toSvg()); - feather.replace(); - console.log("video off") - document.getElementById("cameraImage1").style.display = "none"; - document.getElementById("cameraImage2").style.display = "none" - if (isMobile) - document.getElementById("mobileCameraArea").style.display = "none" - } - } -} - -function confine(value, low, high) { - if (value < low) - return low; - if (value > high) - return high; - return value; -} - -function boardCutDataUpdateCompressed(data) { - console.log("updating board cut data compressed"); - if (cutSquareGroup.children.length != 0) { - for (var i = cutSquareGroup.children.length - 1; i >= 0; i--) { - cutSquareGroup.remove(cutSquareGroup.children[i]); - } - } - if (data != null) { - //var cutSquareMaterial = new THREE.MeshBasicMaterial( {color:0xffff00, side: THREE.DoubleSide}); - var cutSquareMaterial = new THREE.MeshBasicMaterial({ color: 0xff6666 }); - var noncutSquareMaterial = new THREE.MeshBasicMaterial({ color: 0x333333 }); - var uncompressed = pako.inflate(data); - var _str = ab2str(uncompressed); - var data = JSON.parse(_str) - - var pointsX = Math.ceil(boardWidth) - var pointsY = Math.ceil(boardHeight) - console.log("boardWidth=" + boardWidth) - console.log("boardHeight=" + boardHeight) - console.log("boardCenterY=" + boardCenterY) - var offsetX = pointsX / 2 - var offsetY = pointsY / 2 - - for (var x = 0; x < pointsX; x++) { - for (var y = 0; y < pointsY; y++) { - if (data[x + y * pointsX]) { - //console.log(x+", "+y); - var geometry = new THREE.PlaneGeometry(1, 1); - var plane = new THREE.Mesh(geometry, cutSquareMaterial); - plane.position.set(x - offsetX + boardCenterX, y - offsetY + boardCenterY, 0.01); - cutSquareGroup.add(plane); - }/* - else{ - var geometry = new THREE.PlaneGeometry(1,1); - var plane = new THREE.Mesh(geometry, noncutSquareMaterial); - plane.position.set(x-offsetX+boardCenterX, y-offsetY+boardCenterY, 0.01); - cutSquareGroup.add(plane); - }*/ - } - } - } - var e = new Date(); - $("#fpCircle").hide(); - -} - function toggleBoard() { - if (showBoard) { - showBoard = false; - scene.remove(cutSquareGroup); - scene.remove(boardGroup); - $("#boardID").removeClass('btn-primary').addClass('btn-secondary'); - } else { - showBoard = true; - scene.add(cutSquareGroup); - scene.add(boardGroup); - $("#boardID").removeClass('btn-secondary').addClass('btn-primary'); - } + // Called by frontpage3d.html and frontpage3d_mobile.html + frontpage3d.toggleBoard(); } function toggle3DPO() { - cameraPerspective = !cameraPerspective; - if (cameraPerspective == 0) { - $("#buttonPO").text("Ortho"); - $("#mobilebuttonPO").text("Ortho"); - renderer.render(scene, cameraO) - } - else { - $("#buttonPO").text("Persp"); - $("#mobilebuttonPO").text("Persp"); - renderer.render(scene, cameraP) - } + // Called by frontpage3d.html and frontpage3d_mobile.html + frontpage3d.toggle3DPO(); } - diff --git a/static/scripts/frontpage3dcontrols.js b/static/scripts/frontpage3dcontrols.js index cbacd76..caf96b3 100644 --- a/static/scripts/frontpage3dcontrols.js +++ b/static/scripts/frontpage3dcontrols.js @@ -1,24 +1,24 @@ -function unitSwitch(){ - if ( $("#units").text()=="MM") { - distToMove = (parseFloat($("#distToMove").val())/25.4).toFixed(3) - updateSetting('toInches',distToMove); +function unitSwitch() { + if ($("#units").text() == "MM") { + distToMove = (parseFloat($("#distToMove").val()) / 25.4).toFixed(3) + updateSetting('toInches', distToMove); } else { - distToMove = (parseFloat($("#distToMove").val())*25.4).toFixed(3) - updateSetting('toMM',distToMove); + distToMove = (parseFloat($("#distToMove").val()) * 25.4).toFixed(3) + updateSetting('toMM', distToMove); } } -$(document).ready(function(){ - //settingRequest("Computed Settings","units"); - //settingRequest("Computed Settings","distToMove"); - //settingRequest("Computed Settings","homePosition"); - var controllerMessage = document.getElementById('controllerMessage'); - controllerMessage.scrollTop = controllerMessage.scrollHeight; +$(document).ready(function () { + //settingRequest("Computed Settings","units"); + //settingRequest("Computed Settings","distToMove"); + //settingRequest("Computed Settings","homePosition"); + var controllerMessage = document.getElementById('controllerMessage'); + controllerMessage.scrollTop = controllerMessage.scrollHeight; }); -function pauseRun(){ - if ($("#pauseButton").text()=="Pause"){ +function pauseRun() { + if ($("#pauseButton").text() == "Pause") { action('pauseRun'); } else { @@ -26,55 +26,59 @@ function pauseRun(){ } } -function processRequestedSetting(data){ - if (data.setting=="pauseButtonSetting"){ - if(data.value=="Resume") - $('#pauseButton').removeClass('btn-warning').addClass('btn-info'); +function processRequestedSetting(data) { + if (data.setting == "pauseButtonSetting") { + if (data.value == "Resume") + $('#pauseButton').removeClass('btn-warning').addClass('btn-info'); else - $('#pauseButton').removeClass('btn-info').addClass('btn-warning'); + $('#pauseButton').removeClass('btn-info').addClass('btn-warning'); $("#pauseButton").text(data.value); } - if (data.setting=="units"){ - console.log("requestedSetting:"+data.value); + if (data.setting == "units") { + console.log("requestedSetting:" + data.value); $("#units").text(data.value) } - if (data.setting=="distToMove"){ - console.log("requestedSetting for distToMove:"+data.value); + if (data.setting == "distToMove") { + console.log("requestedSetting for distToMove:" + data.value); $("#distToMove").val(data.value) } - if ((data.setting=="unitsZ") || (data.setting=="distToMoveZ")){ + if ((data.setting == "unitsZ") || (data.setting == "distToMoveZ")) { if (typeof processZAxisRequestedSetting === "function") { - processZAxisRequestedSetting(data) + processZAxisRequestedSetting(data) } } } -function processPositionMessage(data){ - $('#positionMessage').html('X:'+parseFloat(data.xval).toFixed(2)+' Y:'+parseFloat(data.yval).toFixed(2)+' Z:'+parseFloat(data.zval).toFixed(2)); +function processPositionMessage(data) { + $('#positionMessage').html('X:' + parseFloat(data.xval).toFixed(2) + ' Y:' + parseFloat(data.yval).toFixed(2) + ' Z:' + parseFloat(data.zval).toFixed(2)); $('#percentComplete').html(data.pcom) $('#machineState').html(data.state) } -function processErrorValueMessage(data){ - $('#leftError').css('width', data.leftError*100+'%').attr('aria-valuenow', data.leftError*100); - $('#rightError').css('width', data.rightError*100+'%').attr('aria-valuenow', data.rightError*100); +function processErrorValueMessage(data) { + $('#leftError').css('width', data.leftError * 100 + '%').attr('aria-valuenow', data.leftError * 100); + $('#rightError').css('width', data.rightError * 100 + '%').attr('aria-valuenow', data.rightError * 100); } -function processHomePositionMessage(data){ - $('#homePositionMessage').html('X:'+parseFloat(data.xval).toFixed(2)+' Y:'+parseFloat(data.yval).toFixed(2)); +function processHomePositionMessage(data) { + $('#homePositionMessage').html('X:' + parseFloat(data.xval).toFixed(2) + ' Y:' + parseFloat(data.yval).toFixed(2)); } -function processGCodePositionMessage(data){ - $('#gcodePositionMessage').html('X:'+parseFloat(data.xval).toFixed(2)+' Y:'+parseFloat(data.yval).toFixed(2)); +function processGCodePositionMessage(data) { + $('#gcodePositionMessage').html('X:' + parseFloat(data.xval).toFixed(2) + ' Y:' + parseFloat(data.yval).toFixed(2)); $('#gcodeLine').html(data.gcodeLine); - $('#gcodeLineIndex').val(data.gcodeLineIndex+1) + $('#gcodeLineIndex').val(data.gcodeLineIndex + 1) } -function gcodeUpdate(msg){ +function gcodeUpdate(msg) { console.log("Unsupported"); } -function boardDataUpdate(msg){ +function boardDataUpdate(msg) { + console.log("Unsupported"); +} + +function unsupported() { console.log("Unsupported"); } @@ -83,72 +87,71 @@ function boardDataUpdate(msg){ // $("#fpCircle").hide(); // } -function boardCutDataUpdateCompressed(data){ +function boardCutDataUpdateCompressed(data) { console.log("Unsupported"); $("#fpCircle").hide(); } -function showFPSpinner(msg){ - $("#fpCircle").show(); +function showFPSpinner(msg) { + $("#fpCircle").show(); } -function toggle3DView() -{ - console.log("Unsupported"); +export const toggle3DView = () => { + return frontpage3d ? frontpage3d.toggle3DView() : unsupported(); } // function resetView(){ // console.log("Unsupported"); // } -function cursorPosition(){ - console.log("Unsupported"); +function cursorPosition() { + console.log("Unsupported"); } -function processCameraMessage(data){ - console.log("Unsupported"); -} +// function processCameraMessage(data){ +// console.log("Unsupported"); +// } -function processControllerMessage(data){ - if (controllerMessages.length >100) - controllerMessages.shift(); - controllerMessages.push(data); - $('#controllerMessage').html(''); - controllerMessages.forEach(function(message){ - $('#controllerMessage').append(message+"
"); - }); - $('#controllerMessage').scrollBottom(); +function processControllerMessage(data) { + if (controllerMessages.length > 100) + controllerMessages.shift(); + controllerMessages.push(data); + $('#controllerMessage').html(''); + controllerMessages.forEach(function (message) { + $('#controllerMessage').append(message + "
"); + }); + $('#controllerMessage').scrollBottom(); } -function processAlarm(data){ - console.log("alarm received"); - $("#alarms").html(""+data.message+""); - $("#alarms").removeClass('alert-success').addClass('alert-danger'); - $("#stopButton").addClass('stopbutton'); +function processAlarm(data) { + console.log("alarm received"); + $("#alarms").html("" + data.message + ""); + $("#alarms").removeClass('alert-success').addClass('alert-danger'); + $("#stopButton").addClass('stopbutton'); } -function clearAlarm(data){ - console.log("clearing alarm"); - $("#alarms").text("Alarm cleared."); - $("#alarms").removeClass('alert-danger').addClass('alert-success'); +function clearAlarm(data) { + console.log("clearing alarm"); + $("#alarms").text("Alarm cleared."); + $("#alarms").removeClass('alert-danger').addClass('alert-success'); } -function processStatusMessage(data){ - if (data.uploadFlag == 1){ - if (!isDisabled){ - $('.disabler').prop('disabled', true); - isDisabled = true; - } - } else { - if (isDisabled){ - $('.disabler').prop('disabled', false); - isDisabled = false; - } +function processStatusMessage(data) { + if (data.uploadFlag == 1) { + if (!isDisabled) { + $('.disabler').prop('disabled', true); + isDisabled = true; } - $("#currentTool").text(data.currentTool.toString()); - if (data.positioningMode == 0) - $("#currentPositioningMode").text("Absolute (G90)"); - else - $("#currentPositioningMode").text("Incremental (G91)"); + } else { + if (isDisabled) { + $('.disabler').prop('disabled', false); + isDisabled = false; + } + } + $("#currentTool").text(data.currentTool.toString()); + if (data.positioningMode == 0) + $("#currentPositioningMode").text("Absolute (G90)"); + else + $("#currentPositioningMode").text("Incremental (G91)"); } diff --git a/templates/actions.html b/templates/actions.html index d71b6cb..592db52 100644 --- a/templates/actions.html +++ b/templates/actions.html @@ -22,7 +22,7 @@

Diagnostics/Maintenance

- + @@ -34,11 +34,11 @@

Calibration/Setup

- + - - - + + + diff --git a/templates/base.html b/templates/base.html index edd6efd..942b5b5 100644 --- a/templates/base.html +++ b/templates/base.html @@ -18,6 +18,7 @@ @@ -115,7 +117,7 @@ @@ -148,7 +150,7 @@ @@ -169,15 +171,15 @@ diff --git a/templates/frontpage3d.html b/templates/frontpage3d.html index a7f70f8..27d8002 100644 --- a/templates/frontpage3d.html +++ b/templates/frontpage3d.html @@ -14,7 +14,7 @@ - + {% endblock %} @@ -142,17 +142,17 @@
Error:
-
+
-
+
-
+
@@ -237,4 +237,4 @@

Controller Messages

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/frontpage3d_mobile.html b/templates/frontpage3d_mobile.html index 80880f7..cd835c3 100644 --- a/templates/frontpage3d_mobile.html +++ b/templates/frontpage3d_mobile.html @@ -11,7 +11,7 @@ - + {% endblock %} @@ -159,17 +159,17 @@
Error:
-
+
-
+
-
+
diff --git a/templates/gpio.html b/templates/gpio.html index 2c58f3a..410fdaa 100644 --- a/templates/gpio.html +++ b/templates/gpio.html @@ -11,7 +11,7 @@
{% if setting.type=="options" %} - {% for option in options %}
{% if setting.type=="options" %} - {% for option in options %}