Skip to content

Latest commit

 

History

History
196 lines (163 loc) · 9.79 KB

C-Library-API.md

File metadata and controls

196 lines (163 loc) · 9.79 KB
PCController documentation — return to the main page

C Library API

The normal reusable API is the Go package at the module root:

import controller "pccontroller.local/controller"

For other languages, cmd/controller-cabi exposes a deliberately small, language-neutral JSON ABI:

char *PCControllerInvoke(char *request_json);
void PCControllerFree(char *response_json);

Build it from the project root with the canonical host packager (which also tests the exported ABI):

build.cmd --host-only

For a developer-only CMake verification build that produces only the library, generated header, and external C consumer smoke tests, configure the Go module itself:

cmake -S Tools/Controller -B .build/controller-cabi -G Ninja ^
  -DPCCONTROLLER_CABI_CC=C:\path\to\x86_64-w64-mingw32-gcc.exe
cmake --build .build/controller-cabi --target pccontroller_cabi_installed_smoke
ctest --test-dir .build/controller-cabi --output-on-failure
cmake --install .build/controller-cabi --prefix .build/controller-cabi/install

The CMake target delegates directly to go build -buildmode=c-shared; it does not replace the canonical package/provisioning tool. On Windows it verifies that the same compiler selected for CMake and CGO matches go env GOARCH, advertises native MinGW-w64 macros, and is neither MSYS nor Cygwin. Point PCCONTROLLER_CABI_CC at a native UCRT/MinGW-w64 compiler on a fresh build directory. The regular host packager remains the preferred path when automatic compiler discovery or provisioning is wanted. CMake output is deliberately not a deployable host package: it omits the canonical manifest, embedded defaults, product resources, package verification, and deployment lifecycle.

On Linux, the same developer-only CMake/CTest smoke uses the native system C compiler by default:

cmake -S Tools/Controller -B .build/controller-cabi -G Ninja -DBUILD_TESTING=ON
cmake --build .build/controller-cabi --target pccontroller_cabi_installed_smoke
ctest --test-dir .build/controller-cabi --output-on-failure

Go emits both bin/pccontroller.dll and bin/pccontroller.h on Windows (.so on Linux and .dylib on macOS). This requires CGO_ENABLED=1 and a C compiler compatible with the target Go toolchain. On Windows the build script checks candidates from PATH and the user-scoped package store against go env GOARCH, their target triple, and their predefined macros. It rejects Cygwin/MSYS compilers even when they are the first gcc.exe on PATH, selects the newest native MinGW-w64/Windows-GNU candidate, and provisions the exact latest-resolved package from the host-tool lock when none is installed. Set CC explicitly to a valid native compiler when automatic selection is not appropriate, or pass --no-compiler-bootstrap to prohibit installation. Standard HTTP proxy environment variables are preserved for the package manager.

Every call returns allocated UTF-8 JSON. Copy or parse it, then call PCControllerFree exactly once. The response envelope is:

{"ok":true,"handle":1,"result":{}}

or:

{"ok":false,"error":"description"}

Requests and representative operations:

{"operation":"create","options":{"vid":"1A86","pid":"7523","name":"USB-SERIAL CH340"}}
{"operation":"connect","handle":1,"timeout_ms":15000}
{"operation":"commands","handle":1}
{"operation":"execute","handle":1,"command":"status"}
{"operation":"status","handle":1}
{"operation":"temperatures","handle":1,"rescan":true}
{"operation":"event_next","handle":1,"after_id":42,"kind":"rf","timeout_ms":30000}
{"operation":"snapshot","handle":1}
{"operation":"rf_list","handle":1}
{"operation":"close","handle":1}
{"operation":"destroy","handle":1}
{"operation":"ports"}
{"operation":"host_create","host_options":{"data_root":"APPLICATION_DATA/PCController","app_id":"embedding-application","disable_auto_connect":false,"disable_native":false,"http_address":"127.0.0.1:8787"}}
{"operation":"host_start","handle":2}
{"operation":"host_call","handle":2,"method":"controller.capabilities","params":{}}
{"operation":"host_endpoints","handle":2}
{"operation":"host_stop","handle":2,"timeout_ms":10000}
{"operation":"host_destroy","handle":2,"timeout_ms":10000}

host_create accepts config_path together with data_root only when it names the exact data_root/config.json file. Conflicting paths are rejected, so foreign consumers may state the canonical file explicitly without creating two configuration roots.

Operation Behavior
create Create a client from the full Go Options JSON shape and return a handle.
ports Enumerate serial ports; no handle is required.
connect Auto-detect, or explicitly open top-level port when provided; returns the snapshot.
commands Return the shared command catalog with names, aliases, usage, summary, and task group.
execute Run any registered shell command and return {"output":"..."}. This includes status/settings, display/strip, macros, streamed melodies/status effects, relay/PWM/RF, reset, raw query, and programming commands.
status Request and decode current telemetry, including reset fields when supplied.
temperatures Return named DS18B20 role/ROM/value records; rescan:true asks the board to rescan first.
event_next Wait for the first retained/new event with ID greater than after_id; optional kind filters it.
snapshot Return the latest connection, identity, status, and settings state without a UART request.
rf_list Fetch every page of learned RF entries.
close Close and pause automatic reconnect while keeping the handle valid.
destroy Shut down the client and remove the handle.
host_create Construct an idle embeddable Host with application-owned configuration, data, branding, native IPC, integration, and optional HTTP settings.
host_start Claim ownership and start the canonical client, dispatcher, in-process RPC, native-local listener, and optional HTTP/WebSocket listener.
host_call Invoke one canonical RPC method directly in process; method and params have the same meaning as native and network calls.
host_endpoints Return only the native/TCP endpoints that successfully started.
host_stop Cancel, drain, and stop a Host while retaining its handle.
host_destroy Stop a Host if needed and remove its handle.

timeout_ms defaults to 15000 for handle operations. For event_next, use a larger timeout when an idle wait is expected. Event IDs are monotonic within the client; pass the returned event's ID as the next after_id to avoid receiving it again. kind uses the host event names such as key, door, rf, macro, connect, or disconnect.

Handles are local to the process that loaded the library. Operations on one handle are serialized, so callers from multiple native threads cannot interleave native UART requests.

A direct create client handle is itself a serial owner; it does not silently attach to another coordinator. Prefer a host_create handle for application embedding: it owns one canonical Host, exposes the same dispatcher in process, and can serve protected native-local IPC concurrently with optional HTTP/WebSocket. If another process already owns the Host identity or listener, the application must use that coordinator's advertised native endpoint or :8787 rather than opening UART as an implicit fallback. CLI exec, batch, monitor, reset, shell, and programming processes automatically route through the standard primary process.

Host handles and direct client handles share one numeric namespace but are different lifecycle types. Pass each handle only to operations for the type that created it. Calls for one handle are serialized. Keep the shared library loaded until process shutdown so Go runtime threads are never unloaded early.

The complete Go and Pealayer lifecycle, cancellation, logging, endpoint, and fallback contract is in Embeddable host lifecycle.

Background melody play and rgb effect play operations remain active while their handle and the hosting process remain alive. They can be canceled through another execute request (melody stop or rgb effect stop). Definitions are PC-side JSON configuration only; they are not mixed with the MCU's EEPROM-owned settings.

The complete per-domain reachability contract is in the Control-Surface Capability Matrix.

examples/c_abi_smoke.c is a minimal Windows consumer that resolves both exports, invokes the port-list operation, and releases the returned string. Build it with any compatible MinGW-w64 compiler. Treat the Go shared runtime as process-lifetime state: keep the DLL loaded and let normal process shutdown unload it instead of calling FreeLibrary while Go runtime threads are active.

AutoHotkey and VBA

The same stable C exports are directly usable by desktop automation tools; no shell process, temporary file, or command-line quoting is required. An AutoHotkey v2 call is:

dll := A_ScriptDir "\pccontroller.dll"
json := '{"operation":"ports"}'
request := Buffer(StrPut(json, "UTF-8"))
StrPut(json, request, "UTF-8")
resultPtr := DllCall(dll "\PCControllerInvoke", "Ptr", request.Ptr, "CDecl Ptr")
try response := StrGet(resultPtr, "UTF-8")
finally DllCall(dll "\PCControllerFree", "Ptr", resultPtr, "CDecl")

Office VBA can declare the same pointer ABI with PtrSafe/LongPtr, pass a null-terminated UTF-8 byte buffer to PCControllerInvoke, copy the returned UTF-8 JSON, and release it with PCControllerFree. Match the Office and DLL architectures. This explicit process-lifetime ABI is the supported automation surface; the portable package does not silently register a machine-wide COM class or require administrator access.