Skip to main content
A planned measurement is a project-scoped record of a test you intend to run. A requester creates it as requested (with an estimated duration but no channel); a scheduler later assigns a channel and a [planned_start_time, planned_end_time) window, moving it to scheduled. Planned measurements never create or mutate a real cell measurement. When the real test starts, the actual cell_measurement links back to the plan and moves it to in_progress — the planned row stays as the request-and-schedule audit trail.

When to use it

Planned measurements are optional — you can still create measurements directly and set channel_id at run time as described in the Lab view. Use planned measurements when you want to:
  • Track a backlog of tests requested against a project or a specific cell specification.
  • Reserve future channel time so two schedulers don’t book the same channel and window.
  • Separate the request (from a scientist or PM) from the scheduling decision (from a lab operator).
  • Keep an audit trail of who requested and scheduled each test.
If you just have a single cycler and start tests ad hoc, keep linking measurements to channels directly and skip planned measurements.

Lifecycle

Scheduling is fully manual — the scheduler picks the channel and times. There is no automatic optimizer. Reserving future channel time on a scheduled plan does not make the Lab view show the channel as occupied — only a running real measurement does that. A plan reserves the calendar; a cell_measurement reserves the current moment.

Fields

Names are unique per project (case-insensitive). Creating a plan with a duplicate name raises IonworksError with error_code == "CONFLICT" (HTTP 409). Use create_or_get to make creation idempotent.

Requesting a measurement

Create a requested plan with no channel; supply the protocol to run, the cell specification you want tested, and an estimated duration. The scheduler picks the concrete cell instance and channel later.
Use create_or_get to make the request idempotent by name:
The Protocol section of the Request test form leads with a plain-English prompt: describe the test (e.g. "1C/1C cycling for 200 cycles between 4.2 V and 2.5 V"), click Generate, and you get a validated UCP protocol back without writing YAML or clicking through the visual builder. The attached cell specification is used as context, so you rarely need to spell out voltage limits. See AI protocol authoring for details.

Protocol validation

protocol_id must reference an experiment template in your organization, and the underlying protocol must be ready to run against a real cell — a planned measurement pins the exact test the lab will execute, so half-configured protocols are rejected at request time rather than surfacing as a failed run later. Both create and update reject a plan with IonworksError (HTTP 400) when the protocol_id:
  • Does not exist in your organization. The error is protocol_id '…' does not reference a protocol in this organization.
  • Still has unresolved inputs. Protocols authored with input["…"] placeholders (see Parameterized inputs) must have every input resolved to a concrete value before they can back a planned measurement. The error lists the missing input names: protocol_id '…' references a protocol with unresolved inputs (C-rate, Temperature [°C]). Planned measurements require a fully-specified protocol — set all input values first.
  • Fails UCP validation. Structurally invalid protocols (bad step definitions, inconsistent terminations, and so on — the same checks POST /protocols/validate runs) are rejected with the underlying validation error attached.
Fix the protocol first, then retry the request. If the protocol you want to run isn’t fully specified yet, clone it and pin the inputs on the copy — see Protocols for how to author and configure protocol inputs.

Scheduling onto a channel

Assign a channel and a time window with schedule — a convenience wrapper that moves a requested plan to scheduled.
You can also create a plan that starts already scheduled in one call:

Picking a channel in the UI

The Schedule dialog’s channel picker shows each channel’s live availability and electrical ratings alongside its name, so you don’t need to jump to the Lab view to judge fit:
  • Primary linecycler / channel.
  • Ratings — max current and voltage window (e.g. 40 A · 0–5 V), pulled from the channel’s ratings. Unrated fields are omitted.
  • Statefree, occupied, or stale, derived from the same channel occupancy rules as the Lab wall. Out-of-commission channels are listed but disabled.
All non-out-of-commission channels remain selectable — occupied and stale channels are shown so you can still reserve future windows on them. The picker does not filter or sort by “fit” against the protocol; the ratings are informational.

Scheduling conflicts

Scheduling is rejected when the window would collide with something already on the channel. Pick a different channel or window and retry. Cancelling or completing an existing plan is always allowed, even after the channel goes out of commission.

Listing and filtering

list is project-scoped and returns a PaginatedList[PlannedMeasurement]. Filter by lifecycle status, channel, or name.

Updating, cancelling, and deleting

To reschedule a plan, call schedule again with a new channel or window (or update the individual fields). The server re-runs the overlap and out-of-commission checks against the new values.

Test scheduler row actions

The Test scheduler page (per project, under Lab → Test scheduler) lists every plan in the backlog. Each row’s overflow menu offers the actions below. Which ones show up depends on the plan’s current status — the UI hides an action rather than letting you click it into an error. Delete is deliberately gated behind Cancel: you cannot delete a live request, a booked channel, or the record of a test that ran. Cancel first, then delete if you want the row gone. Copy is the fastest way to queue up several near-identical tests (same cell spec and protocol, different notes or setup). It uses the same request form as the Request test button, so the usual validation applies. The new plan gets its own name and its own audit fields — the original is not touched.

Linking a real measurement to a plan

You do not transition a plan to in_progress by hand. When the real run starts, create the cell_measurement with planned_measurement_id set — the backend links it and moves the plan to in_progress atomically.
The link is rejected with HTTP 400 unless all of these hold:
  • The plan is scheduled.
  • The measurement is a time_series measurement and carries a channel_id (a planned_measurement_id with no channel_id is rejected).
  • The plan and measurement are in the same project and organization.
  • The measurement’s channel_id equals the plan’s channel_id.
  • If the plan has a cell_instance_id, the measurement must run on that same cell instance.
Two concurrent measurements can never both claim the same plan — the link is an atomic compare-and-set on status = scheduled, so the loser is left unlinked (and logged as a warning). After linking, channel occupancy on the Lab view is driven by the running cell_measurement. The planned row remains as the audit trail.

Auto-watching the requester

When a scheduled plan links to a real cell_measurement and transitions to in_progress, the requester (requested_by on the plan) is automatically added as a watcher on the started measurement. Requesters see the run appear under My Channels in the Lab view on their next lab status refresh — no manual Watch click required. Behavior:
  • Fires on both link paths: creating a cell_measurement with planned_measurement_id set, and patching a plan to in_progress with a started_measurement_id.
  • Idempotent — re-linking or re-transitioning does not create duplicate watches, and already-watching users stay watching.
  • Only requested_by is auto-watched. scheduled_by and lab operators are not; they can still watch manually from the channel or measurement pages.
  • Best-effort — a watch failure never fails the measurement create or plan update. The link and status transition still succeed.
Requesters who don’t want to be notified can unwatch the measurement from its detail page after it starts.

Programs

A program is an organization-scoped catalog entry — a short, reusable name for a class of lab test such as Formation, Cycling, or RPT. Setting program_id on a plan records which category the test belongs to, and the value is copied onto the real measurement when it links back to the plan. Programs are not free-text tags. The catalog is managed from Organization settings → Programs, and the request and schedule forms pick from that list, so the same category is spelled the same way by everyone. Names are unique within the organization (case-insensitive). Once tagged, the program is visible on the plan and in the test scheduler’s planning table, so a lab operator sees at a glance which category a request belongs to.
Programs are entirely optional — program_id is nullable everywhere it appears. If your project and cell specification names already say what a test is, you can ignore them.

Permissions

Planned measurement endpoints reuse the existing cell_measurement permissions:

project_id

All client.planned_measurement.* methods accept an optional project_id. When omitted it falls back to the project_id configured on the Ionworks client (or the IONWORKS_PROJECT_ID env var). Methods raise ValueError if no project_id is available from any source.

Next steps

Lab view

Set up the sites, cyclers, and channels that planned measurements schedule onto.

Measurements

Create the real cell_measurement that links back to a scheduled plan.