Skip to content

HTTP RPC Interface

Complete reference for the Instrument Script Server HTTP RPC API.

Overview

The HTTP RPC interface provides programmatic access to all server operations without spawning CLI processes. This is the recommended interface for:

  • Embedding the server in other applications
  • High-throughput automation
  • Custom control systems
  • Web-based interfaces

Configuration

Port Setup

Default Port: 8555
Binding: 127.0.0.1 (localhost only)

Set via environment variable:

export INSTRUMENT_SCRIPT_SERVER_RPC_PORT=8555
instrument-server daemon start

Set programmatically (C++):

auto& daemon = instserver::ServerDaemon::instance();
daemon.set_rpc_port(8555);
daemon.start();

Request Format

All requests use POST /rpc with JSON body:

{
  "command": "<command_name>",
  "params": { /* command-specific parameters */ }
}

Headers:

  • Content-Type: application/json

Response Format

All responses are JSON:

{
  "ok": true,
  /* command-specific response fields */
}

On error:

{
  "ok": false,
  "error":  "Error message"
}

Command Reference

Daemon Management

daemon - Control daemon lifecycle

Parameters:

{
  "action":  "start|stop|status",
  "log_level": "debug|info|warn|error",  // optional, for "start"
  "block":  true|false                     // optional, default true
}

Response:

{
  "ok": true,
  "pid": 12345  // for "start" action
}

Example:

curl -X POST http://127.0.0.1:8555/rpc \
  -H "Content-Type: application/json" \
  -d '{"command":"daemon","params":{"action":"status"}}'

Instrument Management

start - Start an instrument

Parameters:

{
  "config_path": "/path/to/config.yaml",
  "plugin_path": "/path/to/plugin. so",  // optional
  "log_level": "info"                    // optional
}

Response:

{
  "ok": true,
  "name": "InstrumentName"
}

stop - Stop an instrument

Parameters:

{
  "name": "InstrumentName"
}

Response:

{
  "ok": true
}

status - Query instrument status

Parameters:

{
  "name": "InstrumentName"
}

Response:

{
  "ok": true,
  "name": "InstrumentName",
  "alive": true,
  "stats": {
    "commands_sent": 150,
    "commands_completed": 148,
    "commands_failed": 0,
    "commands_timeout": 2
  }
}

Note: Status queries execute immediately without waiting for queued measure jobs.

list - List all instruments

Parameters:

{}

Response:

{
  "ok": true,
  "instruments": ["DMM1", "DAC1", "Scope1"]
}

Note: List queries execute immediately without waiting for queued measure jobs.


Measurement Jobs

submit_measure - Submit measurement script

Parameters:

{
  "script_path":  "/path/to/script. lua"
}

Response:

{
  "ok": true,
  "job_id": "job_20260116_123456_a1b2c3"
}

Notes:

  • Job is queued and executed asynchronously
  • Script is parsed immediately to queue instrument commands
  • Multiple measure jobs can have overlapping execution

job_status - Query job status

Parameters:

{
  "job_id":  "job_20260116_123456_a1b2c3"
}

Response:

{
  "ok": true,
  "job_id": "job_20260116_123456_a1b2c3",
  "status": "completed",
  "created_at":  1705401234567
}

Status values:

  • queued - Waiting in queue
  • running - Currently executing
  • completed - Finished successfully
  • failed - Encountered error
  • canceled - Was canceled

job_result - Fetch job result

Parameters:

{
  "job_id":  "job_20260116_123456_a1b2c3"
}

Response:

{
  "ok": true,
  "job_id": "job_20260116_123456_a1b2c3",
  "result": {
    "status":  "success",
    "script":  "script.lua",
    "results": [
      {
        "index": 0,
        "instrument": "DMM1",
        "verb": "MEASURE",
        "params": {},
        "executed_at_ms":  1705401234789,
        "return": {
          "type": "double",
          "value": 3.14159
        }
      }
    ]
  }
}

Note: Only available after job completes.

job_list - List all jobs

Parameters:

{}

Response:

{
  "ok": true,
  "jobs": [
    {
      "job_id":  "job_20260116_123456_a1b2c3",
      "type": "measure",
      "status": "completed",
      "created_at":  1705401234567
    }
  ]
}

job_cancel - Cancel a job

Parameters:

{
  "job_id": "job_20260116_123456_a1b2c3"
}

Response:

{
  "ok": true,
  "message": "Job canceled"
}

Notes:

  • Only queued or running jobs can be canceled
  • Cancellation is cooperative
  • Commands already sent to instruments cannot be interrupted

Testing & Discovery

test - Test instrument command

Parameters:

{
  "config_path": "/path/to/config.yaml",
  "verb": "CommandVerb",
  "params": {},                          // command parameters
  "plugin_path": "/path/to/plugin.so"  // optional
}

Response:

{
  "ok":  true,
  "result": "command_return_value"
}

discover - Discover plugins

Parameters:

{
  "paths": ["/path/to/plugins"]  // optional
}

Response:

{
  "ok": true,
  "plugins": [
    {
      "protocol":  "VISA",
      "path": "/usr/local/lib/instrument-plugins/visa_builtin.so",
      "name":  "VISA Plugin",
      "version": "1.0.0"
    }
  ]
}

plugins - List available plugins

Parameters:

{}

Response:

{
  "ok": true,
  "plugins": {
    "VISA":  "/usr/local/lib/instrument-plugins/visa_builtin. so",
    "SimpleSerial": "/usr/local/lib/instrument-plugins/simple_serial_plugin.so"
  }
}

Complete Example (curl)

# Start daemon
curl -X POST http://127.0.0.1:8555/rpc \
  -H "Content-Type: application/json" \
  -d '{"command":"daemon","params":{"action":"start"}}'

# Start an instrument
curl -X POST http://127.0.0.1:8555/rpc \
  -H "Content-Type: application/json" \
  -d '{"command":"start","params": {"config_path":"configs/dmm1.yaml"}}'

# Submit measurement
curl -X POST http://127.0.0.1:8555/rpc \
  -H "Content-Type:  application/json" \
  -d '{"command":"submit_measure","params":{"script_path":"scripts/measure.lua"}}'

# Response:  {"ok":true,"job_id":"job_20260116_123456_a1b2c3"}

# Poll job status
curl -X POST http://127.0.0.1:8555/rpc \
  -H "Content-Type: application/json" \
  -d '{"command":"job_status","params":{"job_id":"job_20260116_123456_a1b2c3"}}'

# Fetch results
curl -X POST http://127.0.0.1:8555/rpc \
  -H "Content-Type:  application/json" \
  -d '{"command":"job_result","params":{"job_id":"job_20260116_123456_a1b2c3"}}'

Python Client Example

import requests
import time

class InstrumentServerRPC:
    def __init__(self, host="127.0.0.1", port=8555):
        self.url = f"http://{host}:{port}/rpc"

    def call(self, command, params=None):
        payload = {"command": command, "params": params or {}}
        response = requests. post(self.url, json=payload)
        return response.json()

    def submit_measure(self, script_path):
        return self.call("submit_measure", {"script_path":  script_path})

    def job_status(self, job_id):
        return self. call("job_status", {"job_id":  job_id})

    def wait_for_job(self, job_id, poll_interval=0.1):
        while True:
            status = self. job_status(job_id)
            if status["status"] in ["completed", "failed", "canceled"]:
                return status
            time.sleep(poll_interval)

    def job_result(self, job_id):
        return self.call("job_result", {"job_id": job_id})

# Usage
client = InstrumentServerRPC()

# Submit measurement
response = client.submit_measure("scripts/measure.lua")
job_id = response["job_id"]
print(f"Job submitted: {job_id}")

# Wait for completion
client.wait_for_job(job_id)

# Get results
result = client.job_result(job_id)
print(f"Results: {result}")

Security Notes

  • RPC server binds to 127.0.0.1 only (localhost)
  • No authentication by default
  • Designed for local control and automation
  • Do not expose to untrusted networks
  • Consider firewall rules if running on shared systems

Performance

  • Request Latency: ~200 µs per RPC call
  • Job Submission: Non-blocking, returns immediately
  • Status Queries: Fast-tracked, no queue blocking
  • Throughput: Thousands of requests per second

See Also