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 0000000..53d8f04 Binary files /dev/null and b/Web UI Project/app/__pycache__/__init__.cpython-312.pyc differ 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 0000000..3d5c8ee Binary files /dev/null and b/Web UI Project/app/__pycache__/main.cpython-312.pyc differ diff --git a/Web UI Project/app/__pycache__/printer.cpython-312.pyc b/Web UI Project/app/__pycache__/printer.cpython-312.pyc new file mode 100644 index 0000000..d917616 Binary files /dev/null and b/Web UI Project/app/__pycache__/printer.cpython-312.pyc differ 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