Project Management with acctl
acctl is the command-line tool for managing AutoCore projects. It handles project creation, deployment, monitoring, and sending commands to the server and its modules.
Configuration
acctl reads server connection settings from acctl.toml in the project directory (created by acctl clone or acctl set-target), falling back to ~/.acctl.toml for global defaults.
[server]
host = "192.168.1.100"
port = 11969
[build]
release = true
Global flags --host and --port override all config files for a single invocation:
acctl --host 192.168.1.200 status
acctl Command Reference
Project Creation
| Command | Description |
|---|---|
acctl new <name> | Create a new project from the standard template (Rust control program + React web UI) |
acctl clone <host> [project] [-P port] [-d dir] | Clone a project from a remote server |
acctl clone <host> --list | List available projects on a server |
acctl new my_machine
acctl clone 192.168.1.100 my_machine
acctl clone 192.168.1.100 --list
Project Inspection
| Command | Description |
|---|---|
acctl info | Show a human-readable project summary (modules, variables, control program, www status) |
acctl validate | Check project.json for errors (syntax, types, duplicate variables, broken links) |
acctl status | Show server status, control program state, and project list (requires server connection) |
acctl info # Local — reads project.json, no server needed
Offline Code Generation
The autocore_server executable can generate the gm.rs and results.ts files directly without needing to start the full system (useful for CI/CD or development machines lacking physical hardware like EtherCAT).
# Generate gm.rs and results.ts offline
cargo run --bin autocore_server -- --generate /path/to/project.json
This bypasses the background servelets and exits immediately with code 0 on success.
acctl validate # Local — checks for errors before deploying
acctl status # Remote — queries the running server
`acctl validate` checks:
- JSON syntax
- Required fields on variables (`type`)
- Valid type values (`f64`, `bool`, `u64`, etc.)
- Duplicate variable names
- Variable `link` targets reference configured module domains
#### Server Configuration
| Command | Description |
|---|---|
| `acctl set-target <host> [--port PORT]` | Save the server address to `acctl.toml` |
| `acctl switch <project> [--restart]` | Switch the active project on the server |
#### Deployment
| Command | Flags | Description |
|---|---|---|
| `acctl push project` | `--restart` | Upload project.json to the server |
| `acctl push www` | `--no-build`, `--source` | Build (`npm run build`) and upload web HMI. `--no-build` skips the build. `--source` pushes full `www/` instead of `www/dist/`. |
| `acctl push control` | `--start`, `--no-build`, `--source`, `--force` | Build (`cargo build`) and upload the control binary. `--start` starts it after upload. `--source` pushes full source for remote build. `--force` skips project.json sync check. |
| `acctl push doc` | `--no-build` | Build (`acctl doc build`) and upload the generated documentation. `--no-build` uploads an existing `doc/book/` without rebuilding (fails if missing). |
| `acctl pull` | `--extract` | Download the active project as a zip |
| `acctl upload <file>` | `--dest PATH` | Upload an arbitrary file to the project directory (default: `lib/<filename>`) |
```bash
# Typical deploy workflow
acctl push project
acctl push www
acctl push control --start
# Or individually with options
acctl push www --no-build # Skip npm build, push existing dist/
acctl push control --no-build # Skip cargo build, push existing binary
Control Program Lifecycle
| Command | Description |
|---|---|
acctl control start | Start the control program |
acctl control stop | Stop the control program |
acctl control restart | Restart the control program |
acctl control status | Show control program state and PID |
Monitoring
| Command | Description |
|---|---|
acctl status | Server status, control program state, project list |
acctl logs | Show recent control program log output |
acctl logs --follow | Stream logs in real time (colorized by level) |
Log levels are colorized: ERROR (red), WARN (yellow), INFO (green), DEBUG (blue), TRACE (dimmed).
Code Generation and Sync
| Command | Description |
|---|---|
acctl codegen | Regenerate control/src/gm.rs from the server’s project.json (shared memory bindings). Requires a running server. |
acctl codegen-tags [--force] | Regenerate www/src/AutoCoreTags.ts from the local project.json. Pure local operation — no server connection needed. |
acctl sync | Compare local vs server project.json interactively. Options: pull, push, or skip per-section. Auto-runs codegen after sync. |
acctl diff | (Planned) Show what would change on push |
After adding or removing variables in project.json, always run acctl codegen to update the Rust shared memory bindings before rebuilding the control program.
acctl codegen-tags — web UI tag generation
Each variable in project.json supports an optional boolean field ux. When "ux": true, acctl codegen-tags emits a record for that variable into the generated block of www/src/AutoCoreTags.ts, giving the React web UI a typed handle (tagName, fqdn, valueType) for subscriptions and controls. Variables without ux: true are ignored.
"variables": {
"lift_axis_position": { "type": "f64", "ux": true, "description": "Lift axis position (mm)" },
"internal_watchdog": { "type": "u32", "ux": false, "description": "Never shown in HMI" },
"req_start_auto": { "type": "bool", "ux": true }
}
Type mapping from project.json to TypeScript’s valueType:
project.json type | TS valueType |
|---|---|
bool | "boolean" |
u8–u64, i8–i64, f32, f64 | "number" |
string | "string" |
| anything else | (warns and skips) |
Tag names are derived from the variable name by snake_case → camelCase conversion (lift_axis_position → liftAxisPosition). The FQDN is always gm.<variable_name>.
Output file layout
The generated file contains two arrays combined into the exported acTagSpec:
// autocore-codegen:generated-start
// DO NOT EDIT: this block is regenerated by `acctl codegen-tags`.
export const acTagSpecGenerated = [
{ "tagName": "liftAxisPosition", "fqdn": "gm.lift_axis_position", "valueType": "number" },
{ "tagName": "reqStartAuto", "fqdn": "gm.req_start_auto", "valueType": "boolean" },
// ... one record per variable with ux: true ...
] as const satisfies readonly TagConfig[];
// autocore-codegen:generated-end
// Hand-written tags and per-tag overrides — safe to edit.
export const acTagSpecCustom = [
{ tagName: "liftPosition", fqdn: "gm.lift_axis_position", valueType: "number",
subscriptionOptions: { sampling_interval_ms: 300 }, scale: "position" },
// ...
] as const satisfies readonly TagConfig[];
export const acTagSpec = [
...acTagSpecGenerated,
...acTagSpecCustom,
] as const satisfies readonly TagConfig[];
Put anything that needs subscriptionOptions, scale, or any other TagConfig property into acTagSpecCustom — the generated block only carries the three basic fields. The sentinel comments // autocore-codegen:generated-start and // autocore-codegen:generated-end delimit the replaceable region.
Regeneration behavior
On each run, acctl codegen-tags decides whether to replace only the generated block or rewrite the whole file:
| Situation | Action |
|---|---|
www/src/AutoCoreTags.ts doesn’t exist | Write full file from template. |
File exists, has both sentinel comments and acTagSpecCustom | Replace only the generated block; acTagSpecCustom is preserved. |
File exists but missing a sentinel or acTagSpecCustom | Full rewrite from template. Old file is saved to AutoCoreTags.ts.bak. |
--force is passed | Full rewrite regardless. Old file saved to .bak. |
A .bak sibling is only ever produced when the tool actually overwrites a hand-edited file — routine in-place updates leave nothing on disk besides the new AutoCoreTags.ts.
Workflow
# Mark variables for the UI in project.json (editor or acctl import-vars)
# Then:
acctl codegen-tags # → www/src/AutoCoreTags.ts
cd www && npm run dev # React picks up the updated tag list immediately
Run codegen-tags any time you flip ux on/off or add/rename variables. The React side has no caching — refreshing the dev server or the built app picks up the new list on next load.
Variable Management
| Command | Description |
|---|---|
acctl export-vars [--output FILE] | Export variables to CSV (default: variables.csv) |
acctl import-vars [--input FILE] | Import variables from CSV (default: variables.csv) |
acctl dedup-vars | Find and interactively resolve variables with duplicate hardware links |
CSV columns: name, type, link, description, initial.
acctl export-vars --output variables.csv
# Edit in spreadsheet...
acctl import-vars --input variables.csv
acctl dedup-vars # Check for conflicts
Project Documentation
Every project created by acctl new includes a doc/ directory with an mdBook-based user manual. The acctl doc subcommands build, serve, and keep that manual in sync with project.json and the control program source.
| Command | Flags | Description |
|---|---|---|
acctl doc init | --force | Scaffold doc/ (book.toml + the five starter Markdown files) in an existing project. Skips files that already exist; --force overwrites. Use this to add the doc directory to projects created before acctl doc support. |
acctl doc build | — | Build static HTML output at doc/book/. Runs generate-vars and cargo doc automatically. |
acctl doc serve | --port PORT (default 4444) | Serve the book locally with live reload. |
acctl doc generate-vars | — | Regenerate doc/src/variables.md from project.json (hardware-linked / bit-mapped / plain tables). |
acctl doc clean | — | Remove doc/book/ and doc/src/rustdoc/. |
acctl doc init # Scaffold doc/ if the project doesn't have one yet
acctl doc serve # http://localhost:4444 with live reload
acctl doc serve --port 8080 # Custom port
acctl doc build # One-shot build → doc/book/index.html
New projects created by acctl new already contain a scaffolded doc/, so you only need acctl doc init when retrofitting an older project or when you’ve deleted doc/ and want to start over. The command reads the project name from project.json to populate the book title and introduction page, and never overwrites files by default — safe to run repeatedly.
On first use, if mdbook is not on your PATH, acctl installs it automatically via cargo install mdbook --locked (one-time, ~60s). cargo doc ships with every Rust toolchain, so no additional installation is needed for the Rustdoc section.
Distribution
The output at doc/book/ is a self-contained static site. You have three distribution options:
-
Push to the server —
acctl push docbuilds the book and uploads it to the active project on the server. autocore-server automatically serves the active project’s documentation on its documentation port (default4444, configurable inconfig.ini):http://<server-ip>:4444/Operators get up-to-date docs as a side effect of deployment — no separate hosting required. When no documentation has been pushed, the port serves a placeholder page explaining how to run
acctl push doc. Switching the active project on the server automatically swaps the served docs to the new project’s book. -
Zip and share —
doc/book/is fully self-contained. Zip it, email it, or drop it on a shared drive. Recipients unzip and openindex.htmldirectly from the filesystem. -
Host elsewhere — Push
doc/book/to any static web host (GitHub Pages, S3, internal nginx, etc.). All links are relative.
Configuring the documentation port
The server reads the doc port from config.ini:
[general]
port = 80 # Main HMI port
doc_port = 4444 # Documentation port (this)
Omit doc_port to use the default of 4444. Set it to a different value if 4444 is already in use on the target.
Note: As of this writing, autocore-server’s HTTP endpoints — including port
4444— are unauthenticated. If your project documentation contains sensitive information, keep the server on a trusted network until the forthcoming authentication gate ships.
Sending Commands to Modules
| Command | Description |
|---|---|
acctl cmd <topic> [args...] | Send a command to the server (same as the AutoCore console) |
The topic format is domain.command. Arguments are parsed as --key value pairs. Values are auto-detected as numbers, booleans, JSON objects/arrays, or strings.
# System commands
acctl cmd system.get_domains
acctl cmd system.list_modules
acctl cmd system.load_module --name ni
acctl cmd system.new_project --project_name my_machine
# Global Memory (read/write variables)
acctl cmd gm.read --name motor_speed
acctl cmd gm.write --name motor_speed_setpoint --value 1500
# Module commands (NI example)
acctl cmd ni.status
acctl cmd ni.describe
acctl cmd ni.add_channel --task AnalogInput --name ai0 --physical_channel Dev1/ai0 --type voltage
acctl cmd ni.save_config --generate_variables true
# Any module that implements CommandRegistry
acctl cmd modbus.status
acctl cmd labelit.camera_start --ip 192.168.1.50
Working with Multiple Projects
Each AutoCore server can host multiple projects, but only one is active at a time.
acctl status # See all projects and which is active
acctl switch other_project --restart # Switch to a different project
acctl cmd system.new_project --project_name new_machine # Create a new project on the server
Deploying to a Remote Server
# 1. Set the target server (saved to acctl.toml)
acctl set-target 192.168.1.100
# 2. Verify the connection
acctl status
# 3. Validate before deploying
acctl validate
# 4. Deploy
acctl push project
acctl push www
acctl push control --start
# 5. Monitor remotely
acctl logs --follow
Importing and Exporting Variables
For large projects, manage variables in a spreadsheet and import them:
acctl export-vars --output variables.csv
# Edit the CSV in your spreadsheet application...
acctl import-vars --input variables.csv
acctl dedup-vars # Resolve any duplicate links
The CSV format has these columns: name, type, link, description, initial.
Writing Project Documentation
The project’s doc/ directory is an mdBook — the same tool used for this manual. Source files live in doc/src/ as Markdown; the table of contents is doc/src/SUMMARY.md.
Default layout (created by acctl new, or by acctl doc init on an existing project):
doc/
├── book.toml # mdBook configuration
└── src/
├── SUMMARY.md # Table of contents
├── introduction.md # Edit this — your project overview
├── variables.md # Auto-generated — do not edit
└── control_api.md # Links to the Rustdoc section
Retrofitting older projects. If your project was created before
acctl docsupport anddoc/book.tomldoesn’t exist, runacctl doc initfrom the project root. It scaffolds the same five files thatacctl newwould have produced, pulling the project name fromproject.jsonfor the book title. Existing files are left untouched; pass--forceto overwrite.
What Gets Auto-Generated
Two parts of the book are regenerated every time you run acctl doc build or acctl doc serve — do not edit them by hand:
doc/src/variables.md— a grouped FQDN table of every entry inproject.json’svariablesmap. Three sections are emitted when non-empty: Hardware-Linked (entries with alinkfield), Bit-Mapped (entries withsource+bit), and Other. Columns include FQDN, type, description, and the relevant linkage fields.doc/src/rustdoc/— a copy ofcargo doc --no-depsoutput fromcontrol/. The defaultcontrol_api.mdchapter links intorustdoc/index.html. Doc comments (///) in your control program source become a browsable API reference.
Typical Authoring Workflow
# Start the live-reload server while you write
acctl doc serve
# In another shell, edit doc/src/introduction.md and any other chapters
# Add new chapters by creating new .md files and listing them in SUMMARY.md
# Once happy, produce a static build to hand off
acctl doc build
# → doc/book/index.html
Because generate-vars and cargo doc run on every build, changes to project.json variables or doc comments in control/ appear in the book without any extra step.
Distribution
doc/book/ is a standalone static site — no runtime dependencies, all links relative. Typical distribution options:
- Zip
doc/book/and email or share the archive; recipients unzip and openindex.htmldirectly. - Push
doc/book/to any static web host (GitHub Pages, S3, an internal nginx, etc.). - Commit
doc/book/to a docs branch for versioned online access.
Add doc/book/ and doc/src/rustdoc/ to your .gitignore if you prefer to keep only sources in version control — both are fully reproducible from the sources plus project.json and control/.