Procedure Templates¶
Procedure templates are versioned documents of a calibration workflow. They are managed in the "Templates" tab of the procedure detail page and can be instantiated into calibrations.
Concept¶
A template carries either an uploaded file (e.g. a calibration instruction as PDF) or a serialized test sequence in the calserver.procedure format (JSON), generated from a calibration's test steps. Every template runs through the lifecycle Draft → Approved → Released.
Template kind and measurement capture¶
A template's kind decides which measurement capture calServer offers on the calibration. It is not maintained by hand but derived from the file type:
| Kind | File types | Offered capture |
|---|---|---|
| Own measurement template | json (calserver.procedure) |
Internal measurement runtime ("Start measurement capture") |
| MET/CAL procedure | pxe |
"Start in MET/CAL" via the file type's launch command |
| Excel measurement sheet | xlsx, xlsm, xls, csv |
Open the Excel sheet (launch command), otherwise download |
| Other template | pdf, doc(x), md, everything else |
Open the instruction; measurements are entered manually |
The extension→kind mapping lives in config/procedure.php and can be extended
per installation (e.g. an additional MET/CAL extension).
Where the kind shows up¶
- The procedure's templates tab has an "Art" column; the raw file type stays available as a hint on the entry.
- Editing steps is offered only for an own measurement template — MET/CAL procedures, Excel sheets and instructions carry no editable sequence.
- "Vorlage laden" in the test steps tab likewise offers only own measurement templates. Sending another kind through the API answers 422 and names the kind.
What happens on the calibration¶
The button in the calibration's header shows one system-determined action, resolved along this chain:
- The device's standard procedure; if the device has none, the device type's.
- Its governing template: the newest released one, falling back to the newest overall.
- That template's kind — which yields the action from the table above.
The tooltip names the procedure, its origin (device or device type), the kind and the version. Alternatives (resume the test run, download the template, enter measurements manually) sit in the adjacent menu.
An action that is not ready stays visible and states its reason on click — for example "not released yet" or "no launch command (file link) configured for this file type". An external launch always requires a released template.
Without an assigned standard procedure only manual entry remains; if the calibration already carries test steps, resuming the run is offered instead.
Switching the procedure for a single calibration¶
The device/type assignment is a default, not a constraint. Via "Prüfprozedur wechseln …" in the start button's menu, this one calibration runs a different procedure:
- The picker lists what is assigned to the device and to the device type, including where each assignment comes from.
- The device's standard procedure stays unchanged — future calibrations are not affected.
- Existing test steps are left untouched. Switching chooses, it does not load. To adopt the new procedure's steps, use "Vorlage laden" in the test steps tab — that action replaces existing steps explicitly.
- "Standard wiederherstellen" drops the choice; the calibration follows the device/type default again.
The chain is therefore: calibration → device → device type.
Test steps on start¶
When an empty calibration starts the internal measurement capture, calServer stages the governing template's test steps — otherwise the run would open on an empty list. A message names the count, template and version.
If the calibration already carries any test step, nothing happens: captured work is never overwritten.
The run records which template version it started from.
The plan and the switch are also available directly:
GET /api/v2/calibrations/{ctag}/capture-options
PUT /api/v2/calibrations/{ctag}/procedure # { "procedure_id": "..." }
DELETE /api/v2/calibrations/{ctag}/procedure # back to the default
Versioning¶
| Action | Effect |
|---|---|
| Upload | New template at version 0.1 (draft) |
| New version | Minor bump (0.1 → 0.2 → …); the previous version loses its "latest" flag |
| Approve | Major bump (e.g. 0.3 → 1.0), status Approved; the approved file is carried over immutably |
| Release | Status Released (only possible for approved versions) |
Each template's version history is available via the history icon; every version stays individually downloadable.
Changing name and assignment¶
A template's name and procedure can be changed after the fact — both in a procedure's templates tab and in the cross-procedure list under Procedures → Test plans:
| Icon | Action | Effect |
|---|---|---|
Tag (Rename) |
Change the name | Applies to every version of the template; the stored files keep their filenames |
Link (Assign) |
Set, switch or clear the procedure | The whole version chain moves along; the test steps are untouched |
Both apply to every template kind, not just to internal capture templates: an uploaded PDF carries a name too, and otherwise it is taken from the filename without asking.
Why it matters: An imported test plan — and one saved off a calibration — arrives without a procedure and shows up as "not assigned". Without an assignment, measurement capture will not find it. The button in the "Procedure" column, or the link icon, gives it one without deleting and recreating it, which would cost the version history.
The reverse route lives in the procedure's templates tab: "Assign existing" lists every test plan that does not belong to this procedure and moves the chosen one here. If it still hangs off another procedure, the list says so beforehand.
Creating a new test plan¶
Three routes lead to a new test plan:
| Where | Action | Procedure |
|---|---|---|
| Procedures → Test plans | "Create test plan" | Optional in the dialog; leaving it empty is allowed |
| Procedures → Test plans | "Import" | None — the plan arrives as "not assigned" |
| A procedure's templates tab | "Create manually" | The procedure you are standing in |
A test plan does not need a procedure in order to exist. That is deliberate: whoever writes a plan does not always know yet which procedure it belongs to, and an import never knows. A plan without a procedure shows up as "not assigned" and is hooked in later via the link icon.
Running it does require the assignment, though: measurement capture finds a test plan through the device's — or the device type's — standard procedure, and without a procedure the plan is in none of those chains.
Sharing a test plan as a package (ZIP)¶
Besides exporting the bare test plan file (JSON), internal templates offer a
package export: a ZIP in the calserver.procedure-package format carrying
everything that belongs to the plan.
manifest.json manifest: file list with a SHA-256 checksum per entry
README.md description (existing attachment or a generated skeleton)
procedure.json the test plan, byte-identical to the exported version
images/… pictures (connection diagrams, measuring guides), optional
docs/… documents (manufacturer specifications etc.), optional
Step images: Since document version 3 every step in procedure.json
may carry an images field — a list of basenames from the package's
images/ folder. Measurement capture shows these pictures right at the
step (connection diagram on the group header, measuring guide or test
pattern on the step), and the dry run in the test plan editor shows
them as well — an imported template can be reviewed with its pictures
before it is ever loaded into a calibration. The image files themselves
are attachments on the test plan; the step only references them by name.
The manifest is what makes the package tamper-evident: every entry is listed
with checksum and size, and the import rejects a package whose content
deviates from the manifest — in both directions. The manifest also carries
the revision of the exported version (plan: major, minor, release state)
and the package format version.
On import (the same "Import" button — the .zip extension decides), a
new template at version 0.1 is always created — the source system's
release state is never adopted, only surfaced as a note. Pictures and
documents from the package are stored as attachments (document management,
type "Prüfplan-Anlage") on the new test plan; a subsequent package export
picks them up again.
Ready-made sample packages (Fluke 23, caliper 150 mm) are available on the
calServer reports download page
in the "Prüfpläne" category; the machine-readable manifest schema lives at
https://calhelp.github.io/calServer-reports/schema/procedure-package.schema.json.
Step editor¶
"Create test plan", "Create manually" and the pencil icon on a template all open the step editor. It offers two views, switchable in the top right; the choice is remembered per user:
- Table (default): one row per measurement point, cells edited in place. Tab commits and moves to the next cell, wrapping into the next row. Rows are reordered by the handle on the left. A click on a row shows its fields in the side panel on the right (tabs Settings and Instruction), as a drawer below about 1280 px. Switched off, the chevron on the right expands a detail row with the operator instruction, range, resolution, uncertainty and the guided tolerance builder. Below the tolerance a limit bar shows band and guard band, the Readings column shows one empty box per reading, and a quick bar on each group header adds a point, yes/no, question, rating, message or series at the end of that group. An empty plan offers three ways to start: first group, MET/CAL import, AI draft.
- Cards: one collapsible block per step with the full field set.
If a point names a role as source or reference (source cal,
ref device dmm), the card Standards of the plan appears above table and
cards. There you pick the intended device type per role. From its
specification (in its default option) the TAR column then uses the accuracy of the standard (span
divided by twice the accuracy, like MET/CAL) and shows the estimated TUR
from accuracy and resolution below it. Estimated, because the spread of the
repeat readings and the CMC are only known in the test. Without a device type,
TAR stays based on the uncertainty of the step. In the script the choice reads
standard cal "…", see calScript.
Fields that do not apply are hidden rather than offered empty: a Header step
only has a description and an instruction, a Rating step carries a grading
map instead of a tolerance. Existing values survive a type change.
Fields¶
| Field | Meaning |
|---|---|
| Step no. | Number on the certificate (e.g. 1.1). Execution order comes from the row position, not from this field. |
| Type | Manual Test (measurement or yes/no), Rating (1 to 5), Header (section title), Message / Comment (informational). |
| Description | Step heading in the execution wizard and on the certificate. |
| Instruction | Formatted work instruction shown to the operator during execution. |
| Evaluation | Result type and tolerance reference (see below). |
| Nominal / prefix / unit | Nominal value with SI prefix (m, k, µ …) and unit. |
| Tolerance | Permitted deviation, see the legend. |
| Range | Range of the unit under test; reference for tolerance terms with /. |
| Resolution | One digit of the unit under test; reference for terms with D. |
| Uncertainty | Expanded measurement uncertainty; forms the guard band. |
Evaluation (config)¶
The code decides what measurement capture does with the step (MET/CAL heritage, see also the MET/CAL wiki):
NUMERIC,NOMINALorNUMERIC,UUT INDICATED— measurement point: the calculation runs (limits, tolerance usage, guard band).NOMINAL= the nominal value is the reference,UUT INDICATED= the unit's reading.NUMERIC,REFERENCE— measurement point with a standard measuring alongside (calScriptref device|manual|camera <role>): the limits lie around the standard's reading, which capture asks for next to the unit's value; without it there is no verdict.TEXT(Y=PASS)— yes/no confirmation: the operator confirms the step (visual check, functional check), yes counts as pass.TEXT(N=PASS)inverts the direction (no = pass, e.g. "defect visible?").- empty — informational only, no interaction.
Caution: A Manual Test without any evaluation is a dead step in
measurement capture — neither measurable nor confirmable. The editor warns
in that case; a visual check needs TEXT(Y=PASS).
The editor offers these as select fields; a raw MET/CAL value that cannot be mapped onto them stays as text and is stored unchanged.
Tolerance syntax¶
Several terms separated by spaces are summed. The suffix decides the reference quantity:
| Suffix | Reference | Example |
|---|---|---|
% |
percent of the measured value | 0.5% |
/ |
percent of the range; of the nominal value when the step has no range | 0.1/ |
U |
absolute amount in the unit | 2U |
D |
multiple of the resolution (digits) | 3D |
So 0.5% 2D means 0.5 % of the measured value plus 2 digits. Without a sign a
term applies symmetrically; a leading + limits it to the upper bound, a -
to the lower one.
% refers to the measured value and only resolves at measurement time. D
reads the resolution from the template, / the range; a step without a range (a
source has none, range is optional) calculates / from the nominal value. A
term whose reference is missing altogether counts as 0, which is why the editor
blocks saving in exactly those cases.
On Rating steps the same field carries a grading map written as
1-2:fail|3-5:pass.
Bulk entry and checks¶
- Measurement point series creates several rows at once: evenly spread, as percentages of the range (default 0, 25, 50, 75, 100) or from a value list. The descending leg for devices with hysteresis can be appended. All other fields are inherited from the source row.
- Duplicate copies a row directly below it.
- Apply to all rows propagates unit, tolerance, resolution, range or uncertainty from the expanded row to every other row.
- Renumber assigns step numbers by position.
The editor flags rows with issues: invalid numbers (these block saving), tolerance terms without their reference quantity (they compute as zero), a missing uncertainty (no guard band), and a tolerance-to-uncertainty ratio below 4:1.
AI draft¶
A test plan for a device that has just been handed in usually follows from two inputs: the device specification and the rule it is to be tested against. The "KI-Entwurf" (AI draft) button in the step editor takes both and produces a draft.
Device handover — four sources, combinable:
| Source | What it contributes |
|---|---|
| Device of this calibration | test-steps editor of a calibration only; the server resolves device and device type itself |
| Device from the inventory | device master data plus the specifications of the device and its type |
| Device type | type master data plus the type's specifications |
| Description only | anything not (yet) in the master data |
The toggle "Hinterlegte Spezifikationen mitgeben" decides whether the specification sheets — ranges, resolutions, accuracies — travel with the request. Without them the model only knows what the dialog says.
Specification rules — the "Regelwerke" picker (DKD-R 6-1, VDI/VDE/DGQ 2622, ILAC-G8 …) names the framework only. The figures of a guideline are not stored in the system and the model must not reconstruct them; what shall apply belongs in the "Prüfvorgaben" field (measurands, points per range, error limits, coverage factor, decision rule).
The toggle "Am Messplatz ablesen" (read at the workstation) makes the draft
bind the measurement groups to the workstation roles (read device uut,
source cal, function). Which instrument plays which role is set at the
workstation, not in the plan; without the toggle the technician reads by hand.
The draft is a calScript: groups with shared defaults,
series for similar points (measure 2 10 19 "{value} V"), repeated readings
where the requirements ask for them. If the device type carries specifications
with test points, for example from a MET/CAL import, the draft takes their
points, ranges and accuracies. The preview shows the script and below it the
step rows the compiler makes of it.
In the template editor the draft goes to the script view: it replaces the script or is appended to the script (without a second header). From table or cards the editor switches to the script for this. In the test-steps editor of a calibration, which has no script, the step rows are appended to or replace the existing steps. Nothing is stored by itself, only "save as new version" creates a version.
The draft is checked, not adopted
Every script goes through the calScript compiler, with the same rules as
on save. If it rejects the script, the model gets the message with its line
and corrects it, at most twice and only while the time budget allows. If
the script still does not compile, it is returned anyway: the preview names
the error, and the script view marks its line. Compiler notes (such as %r
without range) and an SI prefix the catalogue does not know (it computes
as factor 1) are listed above the preview. Nothing is corrected silently.
Nominal values, tolerances and uncertainties are still statements of a
language model; review them before release.
Prerequisites: the AI assistant must be configured under Administration →
AI assistant with the "Prüfplan-Entwurf aus Gerät und Spezifikationsregeln"
feature switched on. The button appears with ai_assistant_edit; the draft
itself additionally requires procedure_template_edit. If the configured AI
address points outside the installation, the dialog says so — device data leaves
the installation in that case.
History and errors¶
The dialog keeps a history, live while the draft is being produced. It shows how far the server got and is the actual answer when something fails:
| Stage | What it reports |
|---|---|
| Request to the server | how long the browser waited; if it gives up, the history ends here |
| Connection checked | provider, model, time budget — and whether the address points outside |
| Device handover read | which sources were pulled, how many specification sheets, size of the description |
| Model queried | script lines in the answer, tokens spent, duration |
| Script checked against the calScript compiler | compiled steps and notes, or line and kind of the error |
| Script corrected | one round after a compiler error, at most two |
The history carries numbers and configuration names only — no device data, no test requirements, no model output, no part of the script.
When a draft fails, the dialog names the case instead of "please try again":
| Case | Meaning | Whose job |
|---|---|---|
| Browser timeout | no answer within the client deadline | operator: time budget or model |
| Answer did not get through (502/504) | the reverse proxy answered, not the application | operator: proxy deadline |
| AI service unreachable / error status | address, key or model is wrong | operator: Administration → AI assistant |
| Answer truncated / empty | the model could not finish within its output limit | technician: lower "max. steps", or use another model |
| Answer does not fit the format | model too small for structured output | operator: larger model |
| No usable test plan | the requirements carry too little | technician: name quantities, ranges, limits |
Keep the time budget below the deadline of the environment
A draft is one call and takes minutes on local CPU. The server gives up
after its time budget (240 s by default) and answers with a diagnosis. If it
runs longer than the shortest deadline on the way — nginx
fastcgi_read_timeout (60 s by default, and the directive that applies when
nginx talks to PHP-FPM directly, as it does in the API image — not
proxy_read_timeout, which only covers an upstream proxy layer), PHP-FPM
request_terminate_timeout —
the proxy answers instead, with an HTML page and no error object. All that
is left then is "the answer did not get through".
Either raise those deadlines or put the budget below them:
# API .env — time budget in seconds (30 to 900)
AI_PROCEDURE_PLAN_BUDGET_S=55
docker exec calserver-api-v2 php artisan config:clear
The browser deadline is derived from it (budget + 20 s) — the server should give up first, because only its answer explains what went wrong.
GET /api/v2/ai/procedure-plan/availability # incl. budget_s
POST /api/v2/ai/procedure-plan # { "rules": "...", "standards": [...], "calibration_id"|"inventory_id"|"device_type_id"|"device_text" }
Both outcomes carry the history: data.trace on success, error.trace on
failure, next to error.ai_error and error.retryable.
AI optimization¶
An imported plan, for example from a MET/CAL procedure, is technically right but hard to read: copyright and safety texts as dozens of single messages, groups named after subprocedures, the same values on every point. The "KI-Optimierung" (AI optimization) button in the template editor has the plan reworked that is currently in the editor, including unsaved changes.
The dialog asks what should be improved; everything is preselected:
| Goal | What the AI does |
|---|---|
| Bundle notes | merges consecutive messages, reduces copyright notices to one, turns connection instructions into info on their group, removes MET/CAL leftovers such as [32]; safety warnings keep their content |
| Name and tidy groups | titles by measurand and range, merges groups that only carry notes, lifts shared values to the group |
| Write series | similar consecutive points as one series |
| Check specifications | range, resolution and tolerance per group; anything odd gets a # Prüfen: … comment |
| Resolve editor hints | the table warnings ("40 with hint"), as far as possible without invented numbers |
A free field takes further instructions, for example "translate notes into German".
The result is a proposal with a diff: every line that would change, the list of changes, and the number of measuring points, checks and notes before and after. "Apply proposal" puts it into the script view, "Discard" leaves the plan as it is. Nothing is saved until "Save as new version".
Measuring points stay, and the server counts them
The AI may tighten and reorder texts but must not remove or add a measuring point or change a nominal value, tolerance or uncertainty. The server then compares the effective values of every point (group defaults included). If a point is missing, new, or its range or resolution changed, this is shown in red above the diff and applying asks for confirmation. The AI does not fill in a missing uncertainty: it comes from the budget of the standard.
Prerequisites as for the draft: the same function under Administration → AI
assistant, the same model, ai_assistant_edit and procedure_template_edit.
What is transmitted is the plan as calScript, the editor hints and the
instruction; customer and device master data are not. A plan that does not
compile is not optimized: fix the error first.
Large plans go section by section. An imported manufacturer plan easily has 50,000 characters and more, which fits neither a request nor the model's answer. Above 12,000 characters the server splits the plan between groups (a group is never split); the dialog sends the sections one after another and shows which one is with the model. It then assembles them, and the server compares the whole proposal with the whole plan, so diff and point count cover the full plan. If the proposal for a section does not compile, that section stays unchanged and the list of changes says so. The time budget applies per section. The whole plan may have up to 400,000 characters.
API¶
GET /api/v2/procedure-templates # list (filter[procedure_id], latest_only=1)
POST /api/v2/procedure-templates # upload → version 0.1
POST /api/v2/procedure-templates/manual # name + steps → version 0.1 (procedure_id optional)
POST /api/v2/procedure-templates/{id}/new-version
POST /api/v2/procedure-templates/{id}/approve
POST /api/v2/procedure-templates/{id}/release
GET /api/v2/procedure-templates/{id}/versions
GET /api/v2/procedure-templates/{id}/download
GET /api/v2/procedure-templates/{id}/export-package # test plan package (ZIP) of this version
POST /api/v2/procedure-templates/import-package # package upload → new template 0.1
PUT /api/v2/procedure-templates/{id} # { "name": "..." } and/or { "procedure_id": ... }
POST /api/v2/procedure-templates/{id}/assign # { "procedure_id": "..." }
POST /api/v2/procedure-templates/{id}/unassign
GET /api/v2/procedures/{id}/unassigned-templates # test plans not belonging to this procedure
Renaming and assigning always write to every version of a test plan (all
rows sharing a hash): name and procedure belong to the plan, not to a single
version, and every new version inherits both from its predecessor anyway.
procedure_id: null on PUT clears the assignment, same as /unassign.
Template ↔ Calibration¶
In a calibration's test-steps tab:
- "Save as template" serializes the calibration's test steps into a new template (version 0.1):
POST /api/v2/calibrations/{id}/save-as-template - "Load template" instantiates a template into the calibration — existing test steps are replaced:
POST /api/v2/calibrations/{id}/load-procedurewith{ "template_id": "..." }
Permissions¶
| Operation | Permission |
|---|---|
| View, download, versions | procedure_template_view |
| Upload, new version, approve, release, rename, assign | procedure_template_edit |
| Delete | procedure_template_delete |