From cf5edd13291700fdd3851a3ca93a7416cc6927bb Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 23 Oct 2025 13:05:38 +1100 Subject: [PATCH] Stared work on the manager side --- .../backend}/Dockerfile | 1 - .../backend}/package.json | 0 {backend => Slicer Project/backend}/src/db.ts | 0 .../backend}/src/server.ts | 8 +- .../backend}/src/types.ts | 0 .../backend}/tsconfig.json | 0 .../docker-compose.yml | 2 +- .../frontend}/Dockerfile | 0 .../frontend}/index.html | 0 .../frontend}/package.json | 0 .../frontend}/src/App.tsx | 0 .../frontend}/src/main.tsx | 0 .../frontend}/src/three-preview.tsx | 0 .../frontend}/vite.config.ts | 0 Web UI Project/.dockerignore | 11 ++ Web UI Project/app/__init__.py | 0 .../app/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 117 bytes .../app/__pycache__/main.cpython-312.pyc | Bin 0 -> 4588 bytes .../app/__pycache__/printer.cpython-312.pyc | Bin 0 -> 626 bytes Web UI Project/app/main.py | 115 ++++++++++++++++++ Web UI Project/app/static/ass.css | 0 .../app/templates/_action_results.html | 12 ++ .../app/templates/_connect_area.html | 26 ++++ Web UI Project/app/templates/_status.html | 37 ++++++ Web UI Project/app/templates/base.html | 22 ++++ Web UI Project/app/templates/index.html | 52 ++++++++ Web UI Project/docker-compose.yaml | 13 ++ Web UI Project/dockerfile | 27 ++++ Web UI Project/requirements.txt | 5 + 29 files changed, 327 insertions(+), 4 deletions(-) rename {backend => Slicer Project/backend}/Dockerfile (98%) rename {backend => Slicer Project/backend}/package.json (100%) rename {backend => Slicer Project/backend}/src/db.ts (100%) rename {backend => Slicer Project/backend}/src/server.ts (97%) rename {backend => Slicer Project/backend}/src/types.ts (100%) rename {backend => Slicer Project/backend}/tsconfig.json (100%) rename docker-compose.yml => Slicer Project/docker-compose.yml (81%) rename {frontend => Slicer Project/frontend}/Dockerfile (100%) rename {frontend => Slicer Project/frontend}/index.html (100%) rename {frontend => Slicer Project/frontend}/package.json (100%) rename {frontend => Slicer Project/frontend}/src/App.tsx (100%) rename {frontend => Slicer Project/frontend}/src/main.tsx (100%) rename {frontend => Slicer Project/frontend}/src/three-preview.tsx (100%) rename {frontend => Slicer Project/frontend}/vite.config.ts (100%) create mode 100644 Web UI Project/.dockerignore create mode 100644 Web UI Project/app/__init__.py create mode 100644 Web UI Project/app/__pycache__/__init__.cpython-312.pyc create mode 100644 Web UI Project/app/__pycache__/main.cpython-312.pyc create mode 100644 Web UI Project/app/__pycache__/printer.cpython-312.pyc create mode 100644 Web UI Project/app/main.py create mode 100644 Web UI Project/app/static/ass.css create mode 100644 Web UI Project/app/templates/_action_results.html create mode 100644 Web UI Project/app/templates/_connect_area.html create mode 100644 Web UI Project/app/templates/_status.html create mode 100644 Web UI Project/app/templates/base.html create mode 100644 Web UI Project/app/templates/index.html create mode 100644 Web UI Project/docker-compose.yaml create mode 100644 Web UI Project/dockerfile create mode 100644 Web UI Project/requirements.txt diff --git a/backend/Dockerfile b/Slicer Project/backend/Dockerfile similarity index 98% rename from backend/Dockerfile rename to Slicer Project/backend/Dockerfile index d05fb1e..03923c1 100644 --- a/backend/Dockerfile +++ b/Slicer Project/backend/Dockerfile @@ -64,7 +64,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libxfixes3 libdrm2 libxdamage1 libxcomposite1 libwayland-client0 libxcb1 \ python3 make g++ libsqlite3-dev \ curl tini \ - # 👇 add these for libGL.so.1 (Mesa) libgl1 libgl1-mesa-dri libglu1-mesa \ && rm -rf /var/lib/apt/lists/* diff --git a/backend/package.json b/Slicer Project/backend/package.json similarity index 100% rename from backend/package.json rename to Slicer Project/backend/package.json diff --git a/backend/src/db.ts b/Slicer Project/backend/src/db.ts similarity index 100% rename from backend/src/db.ts rename to Slicer Project/backend/src/db.ts diff --git a/backend/src/server.ts b/Slicer Project/backend/src/server.ts similarity index 97% rename from backend/src/server.ts rename to Slicer Project/backend/src/server.ts index 762b7bd..35e4939 100644 --- a/backend/src/server.ts +++ b/Slicer Project/backend/src/server.ts @@ -91,7 +91,7 @@ app.post("/api/jobs", upload.single("model"), async (req, res) => { const job: JobRecord = { id, - filename: req.file.originalname, + filename: path.join(outputDir, "transformed_input.stl"), ext, profile: profilePath, supports, @@ -103,6 +103,10 @@ app.post("/api/jobs", upload.single("model"), async (req, res) => { finishedAt: null }; insertJob.run(job); + try { + db.prepare("UPDATE jobs SET rot_x=?, rot_y=?, rot_z=?, scale=? WHERE id=?") + .run(rotX, rotY, rotZ, scale, id); + } catch {} // Kick slicing async runSlicing(job, outputPath, supports, profilePath).catch(() => { /* already captured in DB */ }); @@ -153,7 +157,7 @@ function mapRow(r: any) { profile: path.basename(r.profile), supports: r.supports, inputPath: r.input_path, - outputPath: r.output_path ? `/outputs/${r.id}/result.gcode` : null, + outputPath: r.output_path ? `/outputs/${r.id}/result.3mf` : null, status: r.status, errorMsg: r.error_msg, createdAt: r.created_at, diff --git a/backend/src/types.ts b/Slicer Project/backend/src/types.ts similarity index 100% rename from backend/src/types.ts rename to Slicer Project/backend/src/types.ts diff --git a/backend/tsconfig.json b/Slicer Project/backend/tsconfig.json similarity index 100% rename from backend/tsconfig.json rename to Slicer Project/backend/tsconfig.json diff --git a/docker-compose.yml b/Slicer Project/docker-compose.yml similarity index 81% rename from docker-compose.yml rename to Slicer Project/docker-compose.yml index 32624a5..7520ad2 100644 --- a/docker-compose.yml +++ b/Slicer Project/docker-compose.yml @@ -1,7 +1,7 @@ services: backend: build: - context: . # repo root + context: . dockerfile: backend/Dockerfile env_file: .env ports: diff --git a/frontend/Dockerfile b/Slicer Project/frontend/Dockerfile similarity index 100% rename from frontend/Dockerfile rename to Slicer Project/frontend/Dockerfile diff --git a/frontend/index.html b/Slicer Project/frontend/index.html similarity index 100% rename from frontend/index.html rename to Slicer Project/frontend/index.html diff --git a/frontend/package.json b/Slicer Project/frontend/package.json similarity index 100% rename from frontend/package.json rename to Slicer Project/frontend/package.json diff --git a/frontend/src/App.tsx b/Slicer Project/frontend/src/App.tsx similarity index 100% rename from frontend/src/App.tsx rename to Slicer Project/frontend/src/App.tsx diff --git a/frontend/src/main.tsx b/Slicer Project/frontend/src/main.tsx similarity index 100% rename from frontend/src/main.tsx rename to Slicer Project/frontend/src/main.tsx diff --git a/frontend/src/three-preview.tsx b/Slicer Project/frontend/src/three-preview.tsx similarity index 100% rename from frontend/src/three-preview.tsx rename to Slicer Project/frontend/src/three-preview.tsx diff --git a/frontend/vite.config.ts b/Slicer Project/frontend/vite.config.ts similarity index 100% rename from frontend/vite.config.ts rename to Slicer Project/frontend/vite.config.ts diff --git a/Web UI Project/.dockerignore b/Web UI Project/.dockerignore new file mode 100644 index 0000000..8dd0f69 --- /dev/null +++ b/Web UI Project/.dockerignore @@ -0,0 +1,11 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +*.env +.env* +.vscode/ +.idea/ +.git/ +dist/ +build/ diff --git a/Web UI Project/app/__init__.py b/Web UI Project/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Web UI Project/app/__pycache__/__init__.cpython-312.pyc b/Web UI Project/app/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53d8f04069a0adfc456c19ad580fe8477ae67f5b GIT binary patch literal 117 zcmX@j%ge<81fg+1GeGoX5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!646gAC;;R5_{_Y_ mlK6PNg34PQHo5sJr8%i~MXW$Yj6hrrVtiy~WMnL22C@M74Hp;y literal 0 HcmV?d00001 diff --git a/Web UI Project/app/__pycache__/main.cpython-312.pyc b/Web UI Project/app/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d5c8ee4cfa977159194337a0bd8921718d4ed21 GIT binary patch literal 4588 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 literal 0 HcmV?d00001 diff --git a/Web UI Project/app/main.py b/Web UI Project/app/main.py new file mode 100644 index 0000000..7b82b17 --- /dev/null +++ b/Web UI Project/app/main.py @@ -0,0 +1,115 @@ +from fastapi import FastAPI, Request +from fastapi.responses import HTMLResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates +import uvicorn +import bambulabs_api as bl +import os +from datetime import datetime, timedelta + +app = FastAPI(title="X1C Connection") + +IP = "10.0.2.10" +ACCESS_CODE = "e0f815a7" +SERIAL = "00M09A360300272" + +printer = bl.Printer(IP, ACCESS_CODE, SERIAL) + +# Serve /static/* +app.mount("/static", StaticFiles(directory="app/static"), name="static") + +templates = Jinja2Templates(directory="app/templates") + +# track connection state in-memory for UI (simple flag) +connectionState = False + + +@app.get("/", response_class=HTMLResponse) +def home(request: Request): + # Render page and pass current connection state + try: + connected = bool(connectionState) + except NameError: + connected = False + return templates.TemplateResponse("index.html", {"request": request, "connected": connected}) + + +@app.post("/connect", response_class=HTMLResponse) +def connect(request: Request): + global connectionState + try: + printer.mqtt_start() + if(printer.mqtt_start()): + connectionState = True + except Exception: + connectionState = False + + # Return the connect area so HTMX can swap the button immediately + return templates.TemplateResponse("_connect_area.html", {"request": request, "connected": connectionState}) + + +@app.post("/disconnect", response_class=HTMLResponse) +def disconnect(request: Request): + global connectionState + try: + printer.mqtt_stop() + connectionState = False + except Exception: + connectionState = False + + # Return the connect area so HTMX can swap the button immediately + return templates.TemplateResponse("_connect_area.html", {"request": request, "connected": connectionState}) + + +@app.post("/status", response_class=HTMLResponse) +def status(request: Request): + + + try: + # Preferred direct calls (if the API exposes them) + status = {} + status['bed_temp'] = printer.get_bed_temperature() + status['nozzle_temp'] = printer.get_nozzle_temperature() + status['chamber_temp'] = printer.get_chamber_temperature() + status['percentage'] = printer.get_percentage() + remaining_time = printer.get_time() + current = printer.get_file_name() + except Exception as e: + return templates.TemplateResponse("_status.html", {"request": request, "status": {"error": str(e)}}) + + # Normalize current file: remove /data/Metadata/ prefix or use basename + current_file = str(current or '') + if current_file.startswith('/data/Metadata/'): + current_file = current_file[len('/data/Metadata/'):] + else: + current_file = os.path.basename(current_file) + status['current_file'] = current_file or 'N/A' + + if remaining_time is not None: + finish_time = datetime.now() + timedelta(seconds=int(remaining_time)) + status['finish_time'] = finish_time.strftime("%Y-%m-%d %H:%M:%S") + status['remaining_time'] = timedelta(seconds=int(remaining_time)) + + else: + status['finish_time'] = "NA" + status['remaining_time'] = "N/A" + + + + + # stringify values + for k in status: + try: + status[k] = str(status[k]) + except Exception: + status[k] = 'N/A' + + return templates.TemplateResponse("_status.html", {"request": request, "status": status}) + + + + +if __name__ == "__main__": + # Dev convenience; in Docker we launch via CMD + uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True) + diff --git a/Web UI Project/app/static/ass.css b/Web UI Project/app/static/ass.css new file mode 100644 index 0000000..e69de29 diff --git a/Web UI Project/app/templates/_action_results.html b/Web UI Project/app/templates/_action_results.html new file mode 100644 index 0000000..c0241f9 --- /dev/null +++ b/Web UI Project/app/templates/_action_results.html @@ -0,0 +1,12 @@ +
+
{{ result }}
+
+ + \ No newline at end of file diff --git a/Web UI Project/app/templates/_connect_area.html b/Web UI Project/app/templates/_connect_area.html new file mode 100644 index 0000000..698db70 --- /dev/null +++ b/Web UI Project/app/templates/_connect_area.html @@ -0,0 +1,26 @@ +
+ {% if connected %} +
+ +
+ + +
+ {% else %} +
+ +
+ {% endif %} +
diff --git a/Web UI Project/app/templates/_status.html b/Web UI Project/app/templates/_status.html new file mode 100644 index 0000000..80caa80 --- /dev/null +++ b/Web UI Project/app/templates/_status.html @@ -0,0 +1,37 @@ +
+ {% if status.error %} +
Error: {{ status.error }}
+ {% else %} +
+
+
Current File
+
{{ status.current_file }}
+
+
+
Nozzle Temperature
+
{{ status.nozzle_temp }}
+
+
+
Bed Temperature
+
{{ status.bed_temp }}
+
+ +
+
Chamber Temperature
+
{{ status.chamber_temp }}
+
+
+
Progress
+
{{ status.percentage }}
+
+
+
Remaining Time
+
{{ status.remaining_time }}
+
+
+
Time Done
+
{{ status.finish_time }}
+
+
+ {% endif %} +
diff --git a/Web UI Project/app/templates/base.html b/Web UI Project/app/templates/base.html new file mode 100644 index 0000000..291edd3 --- /dev/null +++ b/Web UI Project/app/templates/base.html @@ -0,0 +1,22 @@ + + + + + + {{ request.app.title }}{% block title %}{% endblock %} + + + + + + + + +
+

X1 Carbon Connection

+ {% block connect %}{% endblock %} +
+ {% block status %}{% endblock %} +
+ + diff --git a/Web UI Project/app/templates/index.html b/Web UI Project/app/templates/index.html new file mode 100644 index 0000000..2eef3fc --- /dev/null +++ b/Web UI Project/app/templates/index.html @@ -0,0 +1,52 @@ +{% extends "base.html" %} +{% block connect %} + + +
+ {% if connected %} +
+ +
+ + +
+ {% else %} +
+ +
+ {% endif %} +
+ + +{% endblock %} + +{% block status %} + + + + + +{% endblock %} diff --git a/Web UI Project/docker-compose.yaml b/Web UI Project/docker-compose.yaml new file mode 100644 index 0000000..e6a5a1b --- /dev/null +++ b/Web UI Project/docker-compose.yaml @@ -0,0 +1,13 @@ +services: + web: + build: . + image: fastapi-htmx:dev + container_name: fastapi-htmx + ports: + - "8000:8000" + - "8883:8883" # MQTT over TLS + - "6000:6000" # MQTT unencrypted + - "990:990" # MQTT WebSockets over TLS + volumes: + - ./app:/app/app # live code edits in dev + command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload diff --git a/Web UI Project/dockerfile b/Web UI Project/dockerfile new file mode 100644 index 0000000..9b4e06f --- /dev/null +++ b/Web UI Project/dockerfile @@ -0,0 +1,27 @@ +# --- Base image +FROM python:3.12-slim + +# System deps (add build tools if you need to compile packages) +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl && \ + rm -rf /var/lib/apt/lists/* + +# Set workdir +WORKDIR /app + +# Copy deps first for better Docker layer caching +COPY requirements.txt /app/ +RUN pip install --no-cache-dir -r requirements.txt + +# Copy app code +COPY app /app/app + +# Expose port +EXPOSE 8000 + +# Non-root (optional good practice) +RUN useradd -m appuser +USER appuser + +# Launch server +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/Web UI Project/requirements.txt b/Web UI Project/requirements.txt new file mode 100644 index 0000000..3db5f52 --- /dev/null +++ b/Web UI Project/requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.115.4 +uvicorn[standard]==0.32.0 +jinja2==3.1.4 +python-multipart==0.0.6 +bambulabs_api==2.6.5 \ No newline at end of file