Cold-boot: 4 optimisations cutting bbctrl listen by ~8s on the Pi

Measured on onefinity.local (Pi 3, Raspbian Stretch, bbctrl 1.6.7).

Before -> after:
  bbctrl listen           boot+20.6s  ->  boot+12.4s   (-8.2s)
  host -> /api/config/load    28.2s   ->     22.5s    (-5.7s)

The 4 changes (each independently revertable):

1. scripts/bbserial-rebind.service: do the bbserial unbind + reload
   in a dedicated unit ordered Before=bbctrl.service, instead of in
   rc.local AFTER bbctrl is already listening on the serial port.
   Eliminates a full bbctrl restart mid-boot.

2. scripts/bbctrl.service: drop "After=network.target". bbctrl talks
   to the AVR on a local serial port and to the LCD on I2C; it does
   not need DHCP / network-online to come up. Also adds explicit
   ordering after the new bbserial-rebind unit.

3. scripts/rc.local.fast: trimmed rc.local that no longer touches
   bbserial and backgrounds 'startx' so chromium launches in
   parallel with bbctrl rather than after rc.local finishes.

4. src/py/bbctrl/Planner.py: lazy-import camotics.gplan. Costs ~130ms
   on cold cache, deferred from import-time to ctrl.mach init.

5. (bonus) src/py/bbctrl/Log.py: tolerate FileNotFoundError in
   _rotate(). The improved boot path exposed a pre-existing log
   rotator bug that crashed bbctrl on first start when bbctrl.log.16
   was missing.
This commit is contained in:
2026-05-01 10:07:23 +02:00
parent 420caf52be
commit 8e3b7a29e5
5 changed files with 94 additions and 4 deletions

View File

@@ -30,7 +30,22 @@ import math
import re
import time
from collections import deque
import camotics.gplan as gplan # pylint: disable=no-name-in-module,import-error
# camotics.gplan is heavy (loads a C++ extension that pulls in libstdc++,
# boost::python, etc.). Defer it: bbctrl can listen on HTTP and serve
# the UI without ever touching the planner. Lazy-load the first time
# Planner.init() runs, which is when the user actually queues motion.
gplan = None
def _load_gplan():
global gplan
if gplan is None:
try:
import bbctrl.Trace as _T
with _T.span('imports.camotics_gplan'):
import camotics.gplan as _gplan # pylint: disable=no-name-in-module,import-error
except Exception:
import camotics.gplan as _gplan # pylint: disable=no-name-in-module,import-error
gplan = _gplan
return gplan
import bbctrl.Cmd as Cmd
from bbctrl.CommandQueue import CommandQueue
@@ -329,7 +344,7 @@ class Planner():
if stop:
self.ctrl.mach.stop()
self.planner = gplan.Planner()
self.planner = _load_gplan().Planner()
self.planner.set_resolver(self._get_var_cb)
# TODO logger is global and will not work correctly in demo mode
self.planner.set_logger(self._log_cb, 1, 'LinePlanner:3')