feat(fusion_plating_jobs): auto-pause cron for stale in-progress steps
Plan tasks P2.4 + P2.5 batched.
Adds _cron_autopause_stale_steps method on fp.job.step + 30-min cron
registration. Flips in_progress steps idle > threshold to paused with
a chatter audit ("Auto-paused after Nh idle. Resume from the tablet
when work continues.").
Threshold from ir.config_parameter:
fp.shopfloor.autopause_threshold_hours (default 8.0)
Recipe nodes opt out via fusion.plating.process.node.long_running
(added in P2.1) — useful for 24h bakes and multi-shift soaks.
Fixes the 411-hour ghost timer that motivated the redesign. Doesn't
replace the existing nudge crons — those still notify the supervisor;
this one actually pauses the timer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -151,6 +151,62 @@ class FpJobStep(models.Model):
|
||||
step.blocker_jump_target_model = False
|
||||
step.blocker_jump_target_id = 0
|
||||
|
||||
# ==================================================================
|
||||
# Shop-Floor auto-pause cron (Phase 2 — tablet redesign)
|
||||
# ==================================================================
|
||||
@api.model
|
||||
def _cron_autopause_stale_steps(self):
|
||||
"""Flip in_progress steps idle > threshold to paused.
|
||||
|
||||
Threshold read from ir.config_parameter
|
||||
fp.shopfloor.autopause_threshold_hours (default 8.0)
|
||||
|
||||
Recipes can opt out per node via
|
||||
fusion.plating.process.node.long_running (Phase 2 — P2.1)
|
||||
|
||||
Fixes the 411-hour ghost timer that bit us on the original tablet
|
||||
when an operator started a step and never tapped Finish. Posts an
|
||||
audit chatter entry on the step so the operator can see what
|
||||
happened when they resume.
|
||||
"""
|
||||
from datetime import timedelta
|
||||
threshold = float(
|
||||
self.env['ir.config_parameter'].sudo()
|
||||
.get_param('fp.shopfloor.autopause_threshold_hours', 8)
|
||||
)
|
||||
deadline = fields.Datetime.now() - timedelta(hours=threshold)
|
||||
domain = [
|
||||
('state', '=', 'in_progress'),
|
||||
('date_started', '<', deadline),
|
||||
'|',
|
||||
('recipe_node_id', '=', False),
|
||||
('recipe_node_id.long_running', '=', False),
|
||||
]
|
||||
stale = self.search(domain)
|
||||
paused = 0
|
||||
for step in stale:
|
||||
try:
|
||||
step.button_pause()
|
||||
step.message_post(body=Markup(
|
||||
"<b>Auto-paused</b> after %.1fh idle. "
|
||||
"Resume from the tablet when work continues."
|
||||
) % threshold)
|
||||
_logger.info(
|
||||
"Auto-paused step %s (%s) after %.1fh idle",
|
||||
step.id, step.name, threshold,
|
||||
)
|
||||
paused += 1
|
||||
except Exception:
|
||||
_logger.exception(
|
||||
"Auto-pause failed for step %s — skipping", step.id,
|
||||
)
|
||||
if paused:
|
||||
_logger.info(
|
||||
"_cron_autopause_stale_steps: paused %d step(s) "
|
||||
"(threshold %.1fh)", paused, threshold,
|
||||
)
|
||||
return paused
|
||||
|
||||
# NOTE: the actual button_start override lives further down (~line
|
||||
# 876) where it merges Sub 13 predecessor gate + Policy B Contract
|
||||
# Review auto-open + Sub 8 Racking auto-open + the receiving soft
|
||||
|
||||
Reference in New Issue
Block a user