From 8712ddc377ca726c3ebb20b41e987e25e29d3b10 Mon Sep 17 00:00:00 2001 From: Reid D McKenzie <me@arrdem.com> Date: Fri, 7 Feb 2025 23:59:32 -0700 Subject: [PATCH] Color and continuous printing --- .../tentacles/src/tentacles/blueprints/api.py | 1 + .../src/tentacles/blueprints/job_ui.py | 5 +- projects/tentacles/src/tentacles/sql/jobs.sql | 79 +++++++++++-------- .../src/tentacles/templates/jobs_list.html.j2 | 44 +++++------ .../src/tentacles/templates/macros.html.j2 | 8 +- projects/tentacles/src/tentacles/workers.py | 3 +- 6 files changed, 81 insertions(+), 59 deletions(-) diff --git a/projects/tentacles/src/tentacles/blueprints/api.py b/projects/tentacles/src/tentacles/blueprints/api.py index 1f9b9a5..a16eef6 100644 --- a/projects/tentacles/src/tentacles/blueprints/api.py +++ b/projects/tentacles/src/tentacles/blueprints/api.py @@ -119,6 +119,7 @@ def create_file(location: Optional[str] = None): fid=row.id, cid=None, pid=None, + cont=False, # FIXME: How to decide this? ) return {"status": "ok"}, 202 diff --git a/projects/tentacles/src/tentacles/blueprints/job_ui.py b/projects/tentacles/src/tentacles/blueprints/job_ui.py index d9ae2fa..03d6466 100644 --- a/projects/tentacles/src/tentacles/blueprints/job_ui.py +++ b/projects/tentacles/src/tentacles/blueprints/job_ui.py @@ -39,17 +39,20 @@ def manipulate_jobs(): fid=int(request.form.get("file_id")), cid=maybe(int, request.form.get("color_id")), pid=maybe(int, request.form.get("printer_id")), + cont=request.form.get("continuous") == "on", ) flash("Job created!", category="info") case "duplicate": if job := ctx.db.fetch_job( - uid=ctx.uid, jid=int(request.form.get("job_id")) + uid=ctx.uid, + jid=int(request.form.get("job_id")), ): ctx.db.create_job( uid=ctx.uid, fid=job.file_id, cid=job.color_id, + cont=job.continuous, # FIXME: Need to dissociate job copies from the original mapped printer. Can't tell the difference # between the requested printer in the original job and the mapped printer from a scheduler run. pid=None, diff --git a/projects/tentacles/src/tentacles/sql/jobs.sql b/projects/tentacles/src/tentacles/sql/jobs.sql index 990f485..19973f4 100644 --- a/projects/tentacles/src/tentacles/sql/jobs.sql +++ b/projects/tentacles/src/tentacles/sql/jobs.sql @@ -27,10 +27,13 @@ CREATE TABLE IF NOT EXISTS jobs ( ); -- name: migration-0001-jobs-add-print-time# -ALTER TABLE jobs ADD COLUMN IF NOT EXISTS time_left INTEGER DEFAULT (0); +ALTER TABLE jobs ADD COLUMN time_left INTEGER DEFAULT (0); -- name: migration-0002-jobs-add-print-color# -ALTER TABLE jobs ADD COLUMN IF NOT EXISTS color_id INTEGER DEFAULT (NULL); +ALTER TABLE jobs ADD COLUMN color_id INTEGER DEFAULT (NULL); + +-- name: migration-0003-jobs-add-continuous# +ALTER TABLE jobs ADD COLUMN continuous BOOLEAN DEFAULT (FALSE); -- name: create-job^ INSERT INTO jobs ( @@ -38,12 +41,14 @@ INSERT INTO jobs ( , file_id , color_id , printer_id + , continuous ) VALUES ( :uid , :fid , :cid , :pid + , :cont ) RETURNING * @@ -78,36 +83,46 @@ WHERE -- name: list-job-queue SELECT - j.id as id - , j.file_id - , coalesce(j.color_id, fa.color_id) as color_id - , fa.id as analysis_id - , fa.max_x - , fa.max_y - , fa.max_z - , fa.max_bed - , fa.max_end - , fa.nozzle_diameter - , fa.filament_id - , (SELECT name FROM filament WHERE id = fa.filament_id) AS filament_name - , (SELECT name AS name FROM filament_color WHERE id = coalesce(j.color_id, fa.color_id)) AS color_name - , (SELECT name FROM job_statuses WHERE id = j.status_id) AS status - , j.started_at - , j.time_left - , j.cancelled_at - , j.finished_at - , j.user_id - , j.printer_id -FROM jobs j -INNER JOIN files f - ON j.file_id = f.id -LEFT JOIN file_analysis fa - ON fa.file_id = f.id -WHERE - finished_at IS NULL - AND cancelled_at IS NULL - AND (:uid IS NULL OR j.user_id = :uid) - AND f.id IS NOT NULL + * +FROM ( + SELECT + j.id as id + , j.file_id + , coalesce(j.color_id, fa.color_id) as color_id + , fa.id as analysis_id + , fa.max_x + , fa.max_y + , fa.max_z + , fa.max_bed + , fa.max_end + , fa.nozzle_diameter + , fa.filament_id + , (SELECT name FROM filament WHERE id = fa.filament_id) AS filament_name + , (SELECT name AS name FROM filament_color WHERE id = coalesce(j.color_id, fa.color_id)) AS color_name + , j.status_id + , (SELECT name FROM job_statuses WHERE id = j.status_id) AS status + , j.started_at + , j.time_left + , j.cancelled_at + , j.finished_at + , j.user_id + , j.printer_id + , j.continuous + FROM jobs j + INNER JOIN files f + ON j.file_id = f.id + LEFT JOIN file_analysis fa + ON fa.file_id = f.id + WHERE + finished_at IS NULL + AND cancelled_at IS NULL + AND (:uid IS NULL OR j.user_id = :uid) + AND f.id IS NOT NULL +) +ORDER BY + status_id DESC + , continuous DESC + , id ; -- name: poll-job-queue^ diff --git a/projects/tentacles/src/tentacles/templates/jobs_list.html.j2 b/projects/tentacles/src/tentacles/templates/jobs_list.html.j2 index 6ccd812..77e2bf5 100644 --- a/projects/tentacles/src/tentacles/templates/jobs_list.html.j2 +++ b/projects/tentacles/src/tentacles/templates/jobs_list.html.j2 @@ -9,32 +9,14 @@ <label for="filename">File</label> <span name="filename">{{ctx.db.fetch_file(ctx.uid, job.file_id).filename or "it's a secret"}}</span> </div> - {% if job.printer_id %} - <div class="job-printer u-flex u-flex-break"> - <label for="printer">Printer</label> - <span name="printer">{{ ctx.db.fetch_printer(job.printer_id).name }}</span> - </div> - {% endif %} - {% if job.started_at %} - <div class="job-runtime u-flex u-flex-break"> - <label for="runtime">Runtime</label> - <span name="Runtime">{{ (datetime.utcnow() - datetime.fromisoformat(job.started_at)) }}</span> - </div> - {% endif %} - {% if job.time_left > 0 %} - <div class="job-remaining-time u-flex u-flex-break"> - <label for="remaining-time">Remaining time</label> - <span name="remaining-time">{{ timedelta(seconds=job.time_left) }}</span> - </div> - {% endif %} - <div class="job-constraint u-flex u-flex-break"> + <div class="job-constraint u-flex"> <label>Material</label> {{ job.color_name }} {{ job.filament_name }} </div> - <div class="job-constraint u-flex u-flex-break"> - <label>Limits</label> - {{ job.max_x }}mm x{{ job.max_y }}mm x{{ job.max_z }}mm, bed {{ job.max_bed }}c, end {{ job.max_end }}c, nozzle {{ "%.2f"|format(job.nozzle_diameter) }}mm + <div class="job-cont u-flex"> + <label>Continuous</label> + {% if job.continuous == 1 %}True{% else %}False{% endif %} </div> {% if job.user_id != ctx.uid %} <div class="job-user u-flex"> @@ -42,6 +24,24 @@ {{ ctx.db.fetch_user(uid=job.user_id).name }} </div> {% endif %} + {% if job.printer_id %} + <div class="job-printer u-flex"> + <label for="printer">Printer</label> + <span name="printer">{{ ctx.db.fetch_printer(job.printer_id).name }}</span> + </div> + {% endif %} + {% if job.started_at %} + <div class="job-runtime u-flex"> + <label for="runtime">Runtime</label> + <span name="Runtime">{{ (datetime.utcnow() - datetime.fromisoformat(job.started_at)) }}</span> + </div> + {% endif %} + {% if job.time_left > 0 %} + <div class="job-remaining-time u-flex"> + <label for="remaining-time">Remaining time</label> + <span name="remaining-time">{{ timedelta(seconds=job.time_left) }}</span> + </div> + {% endif %} </div> <div class="two columns u-mv-auto"> <div class="job-status u-flex"> diff --git a/projects/tentacles/src/tentacles/templates/macros.html.j2 b/projects/tentacles/src/tentacles/templates/macros.html.j2 index 101ffda..417810d 100644 --- a/projects/tentacles/src/tentacles/templates/macros.html.j2 +++ b/projects/tentacles/src/tentacles/templates/macros.html.j2 @@ -1,7 +1,7 @@ {# #################################################################################################### #} {# Job CRUD #} {% macro start_job(file) %} -<form class="inline" method="post" action="/jobs"> +<form class="start-menu inline u-flex" method="post" action="/jobs"> <input type="hidden" name="action" value="enqueue" /> <input type="hidden" name="file_id" value="{{ file.id }}" /> <select name="color_id"> @@ -9,7 +9,11 @@ <option value="{{c.id}}" {% if file.color_id == c.id %}selected{%endif%}>{{c.name}}</option> {%- endfor %} </select> - <input id="submit" type="image" src="/static/print.svg" height="24" width="24" /> + <div class="u-flex u-ml-auto u-mv-auto u-flex u-ml-1"> + <label class="u-flex u-mv-auto" for="continuous">Cont.</label> + <input class="u-flex u-mv-auto" type="checkbox" name="continuous" false> + </div> + <input id="submit" type="image" src="/static/print.svg" height="24" width="24" /> </form> {% endmacro %} diff --git a/projects/tentacles/src/tentacles/workers.py b/projects/tentacles/src/tentacles/workers.py index f3659a7..80c69e8 100644 --- a/projects/tentacles/src/tentacles/workers.py +++ b/projects/tentacles/src/tentacles/workers.py @@ -270,8 +270,7 @@ def pull_jobs(app: App, db: Db) -> None: if runtime >= timedelta(seconds=15): # 3 polling cycles log.info(f"Job {job.id} has succeeded") - # Attempt to automatically clear the bed - if False: + if job.continuous: log.info(f"Attempting to clear bed of {printer.id} ({printer.url})") client.gcode( """\