-
Notifications
You must be signed in to change notification settings - Fork 35
Communication&API
The operator interface reaches the backend over two channels, both served by the same HTTP server on port 3001: a REST API for commands and reads, and Socket.IO for live data. Commands go down through REST; state and live values come up through Socket.IO.
The REST API is per-machine:
| Method & path | Purpose |
|---|---|
GET /machine |
List the connected machines |
GET /machine/{slug}/{serial} |
Read one machine |
POST /machine/{slug}/{serial} |
Send a command to one machine |
{slug} is the machine's short name (derived from its identification) and {serial} is its serial number. A POST carries a JSON body, the server forwards it to the target machine as a message (HttpApiJsonRequest), and the machine applies it on its next cycle through its api_mutate handler, so commands never block the real-time loop. The server binds 0.0.0.0:3001.
Live data flows over Socket.IO, encoded with MessagePack for compactness. There are two kinds of namespace:
- A main namespace carries cross-cutting events: the list of connected machines, the EtherCAT devices, and network-interface discovery.
- A per-machine namespace carries that machine's state and live values, emitted as typed events. It is keyed by the machine identification —
/machine/{vendor}/{machine}/{serial}— the same key the frontend subscribes to.
Each event type declares a caching strategy (for example, keep the latest value). The namespace retains those cached events, so a client that subscribes late immediately receives the machine's current state instead of waiting for the next update.
To drive a machine, a client lists the machines (GET /machine, or the main namespace), subscribes to that machine's Socket.IO namespace to receive its live state, and POSTs commands to /machine/{slug}/{serial}. See Frontend for how the desktop application does exactly this.
The panel performs Basic-Auth authentication. For EVERY request made over HTTPS, Basic Auth is required.
To set the password on the Panel for Basic Auth, do the following:
- Open a Terminal
- cd /home/qitech/control
- ./credentials.sh
The credentials.sh script sets and prints the username and password for the api ONCE in the console. Afterwards its accessible in machine_password in /tmp/ until the machine shuts down. If you ever forget your password you can simply run ./credentials.sh again, resetting the password used, however the username will always be machine
Example request with curl:
# username=machine
# password=VoMZKZK4K4ZIvON+OU2ZcCXy
# With -k you can ignore curl's self signed certificate error
curl https://192.168.130.195/api/v2/machine -k -u machine:VoMZKZK4K4ZIvON+OU2ZcCXy
For HTTPS we use self-signed certificates. These are untrusted by default in browsers,tools like curl and https libraries.
For most programs you can either ignore this warning or you can manually trust the certificate on your system. How these are installed depends on your system.
On the panel pc, the root Certificate is located at: /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt
You need to copy this file to an usb drive or copy it some other way and add it as a trusted root certificate in your OS.
Please refer to your OS' documentation or search the Internet about how to install a trusted SSL certificate.
If you have an isolated network and don't wish to perform Authentication, then you can achieve this by:
cd control
sudo nano nixos/os/configuration.nixFind:
networking.firewall.allowedTCPPorts = [ 443 ];Change to:
networking.firewall.allowedTCPPorts = [ 3001 ];Save and exit.
sudo nixos-rebuild switch --flake .#nixos --impure
sudo rebootsystemctl status qitech-control-server --no-pager
ss -ltnp | grep 3001The service should be listening on 0.0.0.0:3001.
hostname -IExample:
192.168.130.195
Replace <PANEL_IP> with IP from previous step.
Test-NetConnection -ComputerName <PANEL_IP> -Port 3001REST API test:
curl.exe "http://<PANEL_IP>:3001/api/v2/machine"Again, you should only ever do this if you have your own reverse proxy with authentication or you have an isolated network.
Returns the set of machines currently known/connected to the panel.
Machines are identified by:
-
slug: the machine type / model identifier (string) -
serial: the specific machine instance identifier (int)
Each machine also includes a legacy_id to support older v1 workflows. If a machine reports an issue, the error field may be present and non-null (containing an error message).
curl -X GET "https://IP_ADDRESS/api/v2/machine" -u machine:YOUR_PASSWORD{
"machines": [
{
"legacy_id": {
"machine_identification": {
"vendor": 1,
"machine": 7
},
"serial": 57922
},
"serial": 57922,
"vendor": "QiTech",
"slug": "mock",
"error": null
},
{
"legacy_id": {
"machine_identification": {
"vendor": 1,
"machine": 4
},
"serial": 57922
},
"serial": 57922,
"vendor": "QiTech",
"slug": "extruder_v1",
"error": null
},
{
"legacy_id": {
"machine_identification": {
"vendor": 1,
"machine": 2
},
"serial": 57922
},
"serial": 57922,
"vendor": "QiTech",
"slug": "winder_v1",
"error": null
},
{
"legacy_id": {
"machine_identification": {
"vendor": 1,
"machine": 10
},
"serial": 48879
},
"serial": 48879,
"vendor": "QiTech",
"slug": "wago_power_v1",
"error": null
}
]
}Returns all currently known values for a single machine.
Values are categorized into two groups:
- State: requested/commanded values (these typically change only after a state-change request, or if another controller updates them)
- Live Values: measured/observed values coming from the machine and potentially changing quickly
This REST endpoint returns only the current snapshot, not a stream of live values. To receive continuous updates (via WebSockets), subscribe to the machine namespace (see WebSockets below).
curl -X GET "https://IP_ADDRESS/api/v2/machine/mock/57922" -u machine:YOUR_PASSWORD{
"machine": {
"legacy_id": {
"machine_identification": {
"vendor": 1,
"machine": 7
},
"serial": 57922
},
"serial": 57922,
"vendor": "QiTech",
"slug": "mock",
"error": null
},
"state": {
"frequency1": 100.0,
"frequency2": 200.0,
"frequency3": 500.0,
"is_default_state": false,
"mode_state": {
"mode": "Running"
}
},
"live_values": {
"amplitude1": -0.03438523433309566,
"amplitude2": -0.06872980145477608,
"amplitude3": -0.1711138370170743,
"amplitude_sum": -0.27422887280494607
}
}State changes are submitted as mutations. The mutation payload is defined per machine type in Rust. Conceptually, each item in the mutation list represents a setter-style operation that is applied by the real-time control loop.
The API does not return the newly-applied state in the POST response. The panel runs a real-time loop and generally won’t block waiting for the physical system to converge. Instead:
- Submit the mutation via
POST - Poll
GET /api/v2/machine/<slug>/<serial>to observe the updated state and/or any reported errors
curl -X POST \
-d '[{"SetNumberOfActuators": {"num_actuators": 42}}]' \
-H "Content-Type: application/json" \
"https://IP_ADDRESS/api/v2/machine/mock/57922" -u machine:YOUR_PASSWORDnullQiTech Control | GitHub | Video Demo | Open Source Framework for Industrial Control