Work in progress: this documentation is still being written and reviewed. Check details against the source code.

Trajectory plan cache

Keep up to a configured number of serialized MTC solutions in an in-memory LRU cache. Keys combine normalized request JSON, model revision, and rounded six-joint start state; stored trajectories are not revalidated.

beambot.core.plan_cache Β· Source

Motion planning Β· Guide Β· Symbol index

How it is used

The orchestrator uses PlanCache only for goals containing one motion batch. Its key includes the expanded tasks and resolved poses, so editing a referenced registry pose changes the key. The model revision identifies the tool and cup profile; starting joints distinguish execution states.

get() returns the stored solution message directly, or None on a miss. Callers serialize access: the cache has no lock. It manages storage and eviction, not scene validity or execution readiness. The example uses placeholder solution values without planning or motion.

Start here

Callable

Purpose

PlanCache.compute_key

Build a tuple key from normalized JSON, model revision, and rounded start joints. Object-key order and whitespace are ignored; array/task order and whole-turn joint offsets remain significant.

PlanCache.get

Return the stored solution message and promote a hit to most recently used.

PlanCache.store

Insert or replace an entry, mark it most recently used, then evict oldest entries until within capacity.

normalize_joints

Convert six ordered arm positions to floats and round to two decimal radians. Preserve whole-turn differences and normalize negative zero to positive zero.

Example

from types import SimpleNamespace
from beambot.core.plan_cache import PlanCache

cache = PlanCache(SimpleNamespace(info=lambda message: None), max_entries=2)
key_a = cache.compute_key('{"example": "a"}', "model-1")
key_b = cache.compute_key('{"example": "b"}', "model-1")
key_c = cache.compute_key('{"example": "c"}', "model-1")
cache.store(key_a, "solution-a")
cache.store(key_b, "solution-b")
assert cache.get(key_a) == "solution-a"  # a becomes most recently used
cache.store(key_c, "solution-c")
assert cache.get(key_b) is None  # b was evicted

PlanCache

Bounded least-recently-used store of serialized solutions. compute_key() returns a tuple identity, get() promotes a hit, and store() evicts old entries. It does not provide synchronization or an explicit clear method.

PlanCache.compute_key()

Build a tuple key from normalized JSON, model revision, and rounded start joints. Object-key order and whitespace are ignored; array/task order and whole-turn joint offsets remain significant.

Method Β· Source

@staticmethod
compute_key(full_json: str, model_revision: str, start_joints_rad=None) -> tuple

Parameter

Meaning

full_json

JSON text to identify. The orchestrator supplies expanded tasks and resolved poses; invalid JSON is retained as its original text.

model_revision

Model identity, including the configured tool and cup profile when supplied by the lifecycle manager.

start_joints_rad

Optional six joint angles in radians; missing or invalid-length state contributes None.

Returns: Tuple (normalized_json, model_revision, normalized_start_joints). It is not a digest string.

Errors: Catches JSONDecodeError only; wrong input types and joint float-conversion/iteration errors propagate.

Example

from beambot.core.plan_cache import PlanCache, normalize_joints

assert PlanCache.compute_key('{"b":2,"a":1}', "model-1") == (
    PlanCache.compute_key('{"a":1, "b":2}', "model-1")
)
assert normalize_joints([0] * 6) == (0.0,) * 6
assert normalize_joints([0] * 5) is None
Referenced in source (1)

PlanCache.get()

Return the stored solution message and promote a hit to most recently used.

Method Β· Source

get(self, key: tuple)

Parameter

Meaning

key

Tuple returned by compute_key(), or another hashable key used by the caller.

Returns: The stored solution object itself, or None on a miss. Storing None is indistinguishable from a miss and does not promote the entry on lookup.

Errors: Unhashable keys raise TypeError.

Side effects: Changes LRU order on a hit. The solution is returned by reference, without copying or locking.

PlanCache.store()

Insert or replace an entry, mark it most recently used, then evict oldest entries until within capacity.

Method Β· Source

store(self, key: tuple, sol_msg) -> None

Parameter

Meaning

key

Tuple returned by compute_key(), or another hashable key used consistently by the caller.

sol_msg

Opaque solution object, stored by reference.

Returns: None.

Errors: Invalid keys or logger failures propagate; negative max_entries can exhaust the eviction dictionary.

Side effects: Mutates the cache and logs each eviction; stores the solution by reference without locking.

PlanCache.__init__()

Create an empty LRU store with the supplied entry limit.

Lifecycle Β· Source

__init__(self, logger, max_entries: int=MAX_ENTRIES)

Parameter

Meaning

logger

Logger used for eviction messages.

max_entries

Maximum entries, default 64; no positivity validation is performed.

Errors: No explicit validation; a negative capacity causes store’s eviction loop to fail once empty.

Side effects: Allocates an OrderedDict; no lock or ROS resources are created.

Referenced in source (1)

Functions

normalize_joints()

Convert six ordered arm positions to floats and round to two decimal radians. Preserve whole-turn differences and normalize negative zero to positive zero.

Function Β· Source

normalize_joints(positions_rad)

Parameter

Meaning

positions_rad

Iterable of arm joint angles in radians, already reordered by joint name; None denotes unavailable state.

Returns: Six-value tuple, or None when input is None or contains any other number of values.

Errors: Float conversion and iteration errors propagate; nonfinite values are not rejected.

Side effects: Consumes the supplied iterable.

Referenced in source (1)