signalk-server
Version:
An implementation of a [Signal K](http://signalk.org) server for boats.
325 lines (313 loc) • 50.5 kB
HTML
<!DOCTYPE html><html class="default" lang="en" data-base="../../../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>Plugin Capabilities | Signal K</title><meta name="description" content="Documentation for Signal K"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../../../assets/style.css?cache=1783009712609"/><link rel="stylesheet" href="../../../assets/highlight.css?cache=1783009712609"/><script defer src="../../../assets/main.js?cache=1783009712609"></script><script async src="../../../assets/icons.js?cache=1783009712609" id="tsd-icons-script"></script><script async src="../../../assets/search.js?cache=1783009712609" id="tsd-search-script"></script><script async src="../../../assets/navigation.js?cache=1783009712609" id="tsd-nav-script"></script><script async src="../../../assets/hierarchy.js?cache=1783009712609" id="tsd-hierarchy-script"></script><link rel="stylesheet" href="../../../assets/theme.css"/><script>if(window.self!==window.top){document.documentElement.classList.add('embedded')}</script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="../../../index.html" class="title"><img class="title-logo" src="../../../assets/logo.svg" alt="Signal K"/><svg class="title-home-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-label="Documentation home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg></a><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../../../assets/icons.svg#icon-search"></use></svg><span class="visible@s">Search</span></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><div id="tsd-toolbar-links"><a href="https://discord.gg/uuZrwz4dCS" target="_blank" rel="noopener" class="toolbar-icon visible@s" aria-label="Discord"><svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M524.5 69.8a1.5 1.5 0 0 0 -.8-.7A485.1 485.1 0 0 0 404.1 32a1.8 1.8 0 0 0 -1.9 .9 337.5 337.5 0 0 0 -14.9 30.6 447.8 447.8 0 0 0 -134.4 0 309.5 309.5 0 0 0 -15.1-30.6 1.9 1.9 0 0 0 -1.9-.9A483.7 483.7 0 0 0 116.1 69.1a1.7 1.7 0 0 0 -.8 .7C39.1 183.7 18.2 294.7 28.4 404.4a2 2 0 0 0 .8 1.4A487.7 487.7 0 0 0 176 479.9a1.9 1.9 0 0 0 2.1-.7A348.2 348.2 0 0 0 208.1 430.4a1.9 1.9 0 0 0 -1-2.6 321.2 321.2 0 0 1 -45.9-21.9 1.9 1.9 0 0 1 -.2-3.1c3.1-2.3 6.2-4.7 9.1-7.1a1.8 1.8 0 0 1 1.9-.3c96.2 43.9 200.4 43.9 295.5 0a1.8 1.8 0 0 1 1.9 .2c2.9 2.4 6 4.9 9.1 7.2a1.9 1.9 0 0 1 -.2 3.1 301.4 301.4 0 0 1 -45.9 21.8 1.9 1.9 0 0 0 -1 2.6 391.1 391.1 0 0 0 30 48.8 1.9 1.9 0 0 0 2.1 .7A486 486 0 0 0 610.7 405.7a1.9 1.9 0 0 0 .8-1.4C623.7 277.6 590.9 167.5 524.5 69.8zM222.5 337.6c-29 0-52.8-26.6-52.8-59.2S193.1 219.1 222.5 219.1c29.7 0 53.3 26.8 52.8 59.2C275.3 311 251.9 337.6 222.5 337.6zm195.4 0c-29 0-52.8-26.6-52.8-59.2S388.4 219.1 417.9 219.1c29.7 0 53.3 26.8 52.8 59.2C470.7 311 447.5 337.6 417.9 337.6z"></path></svg></a><a href="https://github.com/SignalK/signalk-server" target="_blank" rel="noopener" class="toolbar-icon visible@s" aria-label="Discord"><svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg></a><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../../../assets/icons.svg#icon-menu"></use></svg></a></div></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="../../../Developing.html">Developing</a></li><li><a href="../../Plugins.html">Plugins</a></li><li><a href="../WASM_Plugins.html">WASM Plugins</a></li><li><a href="" aria-current="page">Plugin Capabilities</a></li></ul></div><div class="tsd-panel tsd-typography"><h1 id="plugin-capabilities" class="tsd-anchor-link">Plugin Capabilities<a href="#plugin-capabilities" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../../../assets/icons.svg#icon-anchor"></use></svg></a></h1>
<h2 id="capability-types" class="tsd-anchor-link">Capability Types<a href="#capability-types" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../../../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<p>Declare required capabilities in <code>package.json</code>:</p>
<table>
<thead>
<tr>
<th>Capability</th>
<th>Description</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>dataRead</code></td>
<td>Read Signal K data model</td>
<td>Supported</td>
</tr>
<tr>
<td><code>dataWrite</code></td>
<td>Emit delta messages</td>
<td>Supported</td>
</tr>
<tr>
<td><code>storage</code></td>
<td>Write to VFS (<code>vfs-only</code>)</td>
<td>Supported</td>
</tr>
<tr>
<td><code>httpEndpoints</code></td>
<td>Register custom HTTP endpoints</td>
<td>Supported</td>
</tr>
<tr>
<td><code>staticFiles</code></td>
<td>Serve HTML/CSS/JS from <code>public/</code> folder</td>
<td>Supported</td>
</tr>
<tr>
<td><code>network</code></td>
<td>HTTP requests (via as-fetch)</td>
<td>Supported (AssemblyScript only)</td>
</tr>
<tr>
<td><code>putHandlers</code></td>
<td>Register PUT handlers for vessel control</td>
<td>Supported</td>
</tr>
<tr>
<td><code>rawSockets</code></td>
<td>UDP socket access for radar, NMEA, etc.</td>
<td>Supported</td>
</tr>
<tr>
<td><code>serialPorts</code></td>
<td>Serial port access</td>
<td>Planned</td>
</tr>
</tbody>
</table>
<h2 id="network-api-assemblyscript" class="tsd-anchor-link">Network API (AssemblyScript)<a href="#network-api-assemblyscript" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../../../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<p>AssemblyScript plugins can make HTTP requests using the <code>as-fetch</code> library integrated into the SDK.</p>
<p><strong>Requirements:</strong></p>
<ul>
<li>Plugin must declare <code>"network": true</code> in manifest</li>
<li>Server must be running Node.js 18+ (for native fetch support)</li>
<li>Import network functions from SDK</li>
<li>Must add <code>"transform": ["as-fetch/transform"]</code> to <code>asconfig.json</code> options</li>
<li>Must set <code>"exportRuntime": true</code> in <code>asconfig.json</code> options</li>
</ul>
<p><strong>Example: HTTP GET Request</strong></p>
<pre><code class="typescript"><span class="hl-1">import</span><span class="hl-0"> {</span><br/><span class="hl-0"> httpGet,</span><br/><span class="hl-0"> hasNetworkCapability</span><br/><span class="hl-0">} </span><span class="hl-1">from</span><span class="hl-0"> </span><span class="hl-4">'@signalk/assemblyscript-plugin-sdk/assembly/network'</span><br/><span class="hl-1">import</span><span class="hl-0"> { debug, setError } </span><span class="hl-1">from</span><span class="hl-0"> </span><span class="hl-4">'@signalk/assemblyscript-plugin-sdk/assembly'</span><br/><br/><span class="hl-1">class</span><span class="hl-0"> </span><span class="hl-5">MyPlugin</span><span class="hl-0"> </span><span class="hl-1">extends</span><span class="hl-0"> </span><span class="hl-2">Plugin</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-7">start</span><span class="hl-0">(</span><span class="hl-5">config</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-2">string</span><span class="hl-0">)</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-6">// Always check capability first</span><br/><span class="hl-0"> </span><span class="hl-1">if</span><span class="hl-0"> (</span><span class="hl-1">!</span><span class="hl-7">hasNetworkCapability</span><span class="hl-0">()) {</span><br/><span class="hl-0"> </span><span class="hl-7">setError</span><span class="hl-0">(</span><span class="hl-4">'Network capability not granted'</span><span class="hl-0">)</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0"> </span><span class="hl-2">1</span><br/><span class="hl-0"> }</span><br/><br/><span class="hl-0"> </span><span class="hl-6">// Make HTTP GET request</span><br/><span class="hl-0"> </span><span class="hl-1">const</span><span class="hl-0"> </span><span class="hl-2">response</span><span class="hl-0"> </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-7">httpGet</span><span class="hl-0">(</span><span class="hl-4">'https://api.example.com/data'</span><span class="hl-0">)</span><br/><span class="hl-0"> </span><span class="hl-1">if</span><span class="hl-0"> (response </span><span class="hl-1">===</span><span class="hl-0"> </span><span class="hl-2">null</span><span class="hl-0">) {</span><br/><span class="hl-0"> </span><span class="hl-7">setError</span><span class="hl-0">(</span><span class="hl-4">'HTTP request failed'</span><span class="hl-0">)</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0"> </span><span class="hl-2">1</span><br/><span class="hl-0"> }</span><br/><br/><span class="hl-0"> </span><span class="hl-7">debug</span><span class="hl-0">(</span><span class="hl-4">'Received: '</span><span class="hl-0"> </span><span class="hl-1">+</span><span class="hl-0"> response)</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0"> </span><span class="hl-2">0</span><br/><span class="hl-0"> }</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
<p><strong>Available Network Functions:</strong></p>
<pre><code class="typescript"><span class="hl-6">// Check if network capability is granted</span><br/><span class="hl-7">hasNetworkCapability</span><span class="hl-0">(): boolean</span><br/><br/><span class="hl-6">// HTTP GET request - returns response body or null on error</span><br/><span class="hl-7">httpGet</span><span class="hl-0">(url: string): string </span><span class="hl-1">|</span><span class="hl-0"> </span><span class="hl-2">null</span><br/><br/><span class="hl-6">// HTTP POST request - returns status code or -1 on error</span><br/><span class="hl-7">httpPost</span><span class="hl-0">(url: string, body: string): i32</span><br/><br/><span class="hl-6">// HTTP POST with response - returns response body or null</span><br/><span class="hl-7">httpPostWithResponse</span><span class="hl-0">(url: string, body: string): string </span><span class="hl-1">|</span><span class="hl-0"> </span><span class="hl-2">null</span><br/><br/><span class="hl-6">// HTTP PUT request - returns status code or -1 on error</span><br/><span class="hl-7">httpPut</span><span class="hl-0">(url: string, body: string): i32</span><br/><br/><span class="hl-6">// HTTP DELETE request - returns status code or -1 on error</span><br/><span class="hl-7">httpDelete</span><span class="hl-0">(url: string): i32</span><br/><br/><span class="hl-6">// Advanced HTTP request with full control</span><br/><span class="hl-7">httpRequest</span><span class="hl-0">(</span><br/><span class="hl-0"> url: string,</span><br/><span class="hl-0"> method: string,</span><br/><span class="hl-0"> body: string </span><span class="hl-1">|</span><span class="hl-0"> </span><span class="hl-2">null</span><span class="hl-0">,</span><br/><span class="hl-0"> contentType: string </span><span class="hl-1">|</span><span class="hl-0"> </span><span class="hl-2">null</span><br/><span class="hl-0">): HttpResponse </span><span class="hl-1">|</span><span class="hl-0"> </span><span class="hl-2">null</span>
</code><button type="button">Copy</button></pre>
<p><strong>Build Configuration (asconfig.json):</strong></p>
<p>For plugins using network capability:</p>
<pre><code class="json"><span class="hl-0">{</span><br/><span class="hl-0"> </span><span class="hl-3">"targets"</span><span class="hl-0">: {</span><br/><span class="hl-0"> </span><span class="hl-3">"release"</span><span class="hl-0">: {</span><br/><span class="hl-0"> </span><span class="hl-3">"outFile"</span><span class="hl-0">: </span><span class="hl-4">"build/plugin.wasm"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"optimize"</span><span class="hl-0">: </span><span class="hl-2">true</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"shrinkLevel"</span><span class="hl-0">: </span><span class="hl-2">2</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"runtime"</span><span class="hl-0">: </span><span class="hl-4">"stub"</span><br/><span class="hl-0"> }</span><br/><span class="hl-0"> },</span><br/><span class="hl-0"> </span><span class="hl-3">"options"</span><span class="hl-0">: {</span><br/><span class="hl-0"> </span><span class="hl-3">"bindings"</span><span class="hl-0">: </span><span class="hl-4">"esm"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"exportRuntime"</span><span class="hl-0">: </span><span class="hl-2">true</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"transform"</span><span class="hl-0">: [</span><span class="hl-4">"as-fetch/transform"</span><span class="hl-0">]</span><br/><span class="hl-0"> }</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
<p><strong>Manifest Configuration:</strong></p>
<pre><code class="json"><span class="hl-0">{</span><br/><span class="hl-0"> </span><span class="hl-3">"name"</span><span class="hl-0">: </span><span class="hl-4">"my-plugin"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"wasmCapabilities"</span><span class="hl-0">: {</span><br/><span class="hl-0"> </span><span class="hl-3">"network"</span><span class="hl-0">: </span><span class="hl-2">true</span><br/><span class="hl-0"> },</span><br/><span class="hl-0"> </span><span class="hl-3">"dependencies"</span><span class="hl-0">: {</span><br/><span class="hl-0"> </span><span class="hl-3">"@signalk/assemblyscript-plugin-sdk"</span><span class="hl-0">: </span><span class="hl-4">"^0.2.0"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"as-fetch"</span><span class="hl-0">: </span><span class="hl-4">"^2.1.4"</span><br/><span class="hl-0"> }</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
<h2 id="raw-sockets-api-udp" class="tsd-anchor-link">Raw Sockets API (UDP)<a href="#raw-sockets-api-udp" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../../../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<p>The <code>rawSockets</code> capability enables direct UDP socket access for plugins that need to communicate with devices like:</p>
<ul>
<li>Marine radars (Navico, Raymarine, Furuno, Garmin)</li>
<li>NMEA 0183 over UDP</li>
<li>AIS receivers</li>
<li>Other marine electronics using UDP multicast</li>
</ul>
<p><strong>Requirements:</strong></p>
<ul>
<li>Plugin must declare <code>"rawSockets": true</code> in manifest</li>
<li>Sockets are non-blocking (poll-based receive)</li>
<li>Automatic cleanup when plugin stops</li>
</ul>
<p><strong>Manifest Configuration:</strong></p>
<pre><code class="json"><span class="hl-0">{</span><br/><span class="hl-0"> </span><span class="hl-3">"name"</span><span class="hl-0">: </span><span class="hl-4">"my-radar-plugin"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"wasmManifest"</span><span class="hl-0">: </span><span class="hl-4">"plugin.wasm"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"wasmCapabilities"</span><span class="hl-0">: {</span><br/><span class="hl-0"> </span><span class="hl-3">"rawSockets"</span><span class="hl-0">: </span><span class="hl-2">true</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"dataWrite"</span><span class="hl-0">: </span><span class="hl-2">true</span><br/><span class="hl-0"> }</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
<p><strong>FFI Functions Available:</strong></p>
<table>
<thead>
<tr>
<th>Function</th>
<th>Signature</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sk_udp_create</code></td>
<td><code>(type: i32) -> i32</code></td>
<td>Create socket (0=udp4, 1=udp6). Returns socket_id or -1</td>
</tr>
<tr>
<td><code>sk_udp_bind</code></td>
<td><code>(socket_id, port) -> i32</code></td>
<td>Bind to port (0=any). Returns 0 or -1</td>
</tr>
<tr>
<td><code>sk_udp_join_multicast</code></td>
<td><code>(socket_id, addr_ptr, addr_len, iface_ptr, iface_len) -> i32</code></td>
<td>Join multicast group</td>
</tr>
<tr>
<td><code>sk_udp_leave_multicast</code></td>
<td><code>(socket_id, addr_ptr, addr_len, iface_ptr, iface_len) -> i32</code></td>
<td>Leave multicast group</td>
</tr>
<tr>
<td><code>sk_udp_set_multicast_ttl</code></td>
<td><code>(socket_id, ttl) -> i32</code></td>
<td>Set multicast TTL</td>
</tr>
<tr>
<td><code>sk_udp_set_multicast_loopback</code></td>
<td><code>(socket_id, enabled) -> i32</code></td>
<td>Enable/disable loopback</td>
</tr>
<tr>
<td><code>sk_udp_set_broadcast</code></td>
<td><code>(socket_id, enabled) -> i32</code></td>
<td>Enable/disable broadcast</td>
</tr>
<tr>
<td><code>sk_udp_send</code></td>
<td><code>(socket_id, addr_ptr, addr_len, port, data_ptr, data_len) -> i32</code></td>
<td>Send datagram</td>
</tr>
<tr>
<td><code>sk_udp_recv</code></td>
<td><code>(socket_id, buf_ptr, buf_max_len, addr_out_ptr, port_out_ptr) -> i32</code></td>
<td>Receive datagram (non-blocking)</td>
</tr>
<tr>
<td><code>sk_udp_pending</code></td>
<td><code>(socket_id) -> i32</code></td>
<td>Get number of buffered datagrams</td>
</tr>
<tr>
<td><code>sk_udp_close</code></td>
<td><code>(socket_id) -> void</code></td>
<td>Close socket</td>
</tr>
</tbody>
</table>
<p><strong>Rust Example:</strong></p>
<pre><code class="rust"><span class="hl-0">#[link(wasm_import_module </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">"env"</span><span class="hl-0">)]</span><br/><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">"C"</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_udp_create</span><span class="hl-0">(socket_type</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_udp_bind</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">, port</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">u16</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_udp_join_multicast</span><span class="hl-0">(</span><br/><span class="hl-0"> socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">,</span><br/><span class="hl-0"> addr_ptr</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">*const</span><span class="hl-0"> </span><span class="hl-5">u8</span><span class="hl-0">, addr_len</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">usize</span><span class="hl-0">,</span><br/><span class="hl-0"> iface_ptr</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">*const</span><span class="hl-0"> </span><span class="hl-5">u8</span><span class="hl-0">, iface_len</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">usize</span><br/><span class="hl-0"> ) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_udp_recv</span><span class="hl-0">(</span><br/><span class="hl-0"> socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">,</span><br/><span class="hl-0"> buf_ptr</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">*mut</span><span class="hl-0"> </span><span class="hl-5">u8</span><span class="hl-0">, buf_max_len</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">usize</span><span class="hl-0">,</span><br/><span class="hl-0"> addr_out_ptr</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">*mut</span><span class="hl-0"> </span><span class="hl-5">u8</span><span class="hl-0">, port_out_ptr</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">*mut</span><span class="hl-0"> </span><span class="hl-5">u16</span><br/><span class="hl-0"> ) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_udp_close</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">);</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">// Example: Radar discovery</span><br/><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">start_radar_locator</span><span class="hl-0">() </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-6">// Create UDP socket</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> socket_id </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> { </span><span class="hl-7">sk_udp_create</span><span class="hl-0">(</span><span class="hl-2">0</span><span class="hl-0">) }; </span><span class="hl-6">// udp4</span><br/><span class="hl-0"> </span><span class="hl-1">if</span><span class="hl-0"> socket_id </span><span class="hl-1"><</span><span class="hl-0"> </span><span class="hl-2">0</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0"> </span><span class="hl-1">-</span><span class="hl-2">1</span><span class="hl-0">;</span><br/><span class="hl-0"> }</span><br/><br/><span class="hl-0"> </span><span class="hl-6">// Bind to radar discovery port</span><br/><span class="hl-0"> </span><span class="hl-1">if</span><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> { </span><span class="hl-7">sk_udp_bind</span><span class="hl-0">(socket_id, </span><span class="hl-2">6878</span><span class="hl-0">) } </span><span class="hl-1"><</span><span class="hl-0"> </span><span class="hl-2">0</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0"> </span><span class="hl-1">-</span><span class="hl-2">1</span><span class="hl-0">;</span><br/><span class="hl-0"> }</span><br/><br/><span class="hl-0"> </span><span class="hl-6">// Join radar multicast group</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> group </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">"239.254.2.0"</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> iface </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">""</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">if</span><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> { </span><span class="hl-7">sk_udp_join_multicast</span><span class="hl-0">(socket_id, group</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), group</span><span class="hl-1">.</span><span class="hl-7">len</span><span class="hl-0">(), iface</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), iface</span><span class="hl-1">.</span><span class="hl-7">len</span><span class="hl-0">()) } </span><span class="hl-1"><</span><span class="hl-0"> </span><span class="hl-2">0</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0"> </span><span class="hl-1">-</span><span class="hl-2">1</span><span class="hl-0">;</span><br/><span class="hl-0"> }</span><br/><br/><span class="hl-0"> socket_id</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
<p><strong>Important Notes:</strong></p>
<ul>
<li>Receive is non-blocking - returns 0 if no data available</li>
<li>Incoming datagrams are buffered (max 1000 per socket)</li>
<li>Oldest datagrams are dropped if buffer is full</li>
<li>All sockets are automatically closed when plugin stops</li>
<li>Use <code>sk_udp_pending()</code> to check if data is available before calling <code>sk_udp_recv()</code></li>
</ul>
<h2 id="raw-sockets-api-tcp" class="tsd-anchor-link">Raw Sockets API (TCP)<a href="#raw-sockets-api-tcp" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../../../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<p>The <code>rawSockets</code> capability also enables TCP socket access for plugins that need persistent connections to devices:</p>
<ul>
<li>Marine radars with TCP control (Furuno, Garmin)</li>
<li>Devices requiring handshake/login protocols</li>
<li>Any marine electronics using TCP</li>
</ul>
<p>TCP sockets support both <strong>line-buffered mode</strong> (for text protocols with <code>\r\n</code> terminators) and <strong>raw mode</strong> (for binary protocols).</p>
<p><strong>FFI Functions Available:</strong></p>
<table>
<thead>
<tr>
<th>Function</th>
<th>Signature</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sk_tcp_create</code></td>
<td><code>() -> i32</code></td>
<td>Create TCP socket. Returns socket_id or -1</td>
</tr>
<tr>
<td><code>sk_tcp_connect</code></td>
<td><code>(socket_id, addr_ptr, addr_len, port) -> i32</code></td>
<td>Initiate connection (non-blocking). Returns 0 or -1</td>
</tr>
<tr>
<td><code>sk_tcp_connected</code></td>
<td><code>(socket_id) -> i32</code></td>
<td>Check connection status. Returns 1 if connected, 0 otherwise</td>
</tr>
<tr>
<td><code>sk_tcp_set_line_buffering</code></td>
<td><code>(socket_id, enabled) -> i32</code></td>
<td>Set buffering mode (1=line, 0=raw). Default: line</td>
</tr>
<tr>
<td><code>sk_tcp_send</code></td>
<td><code>(socket_id, data_ptr, data_len) -> i32</code></td>
<td>Send data. Returns bytes sent or -1</td>
</tr>
<tr>
<td><code>sk_tcp_recv_line</code></td>
<td><code>(socket_id, buf_ptr, buf_max_len) -> i32</code></td>
<td>Receive complete line (line mode). Returns len or 0</td>
</tr>
<tr>
<td><code>sk_tcp_recv_raw</code></td>
<td><code>(socket_id, buf_ptr, buf_max_len) -> i32</code></td>
<td>Receive raw data (raw mode). Returns len or 0</td>
</tr>
<tr>
<td><code>sk_tcp_pending</code></td>
<td><code>(socket_id) -> i32</code></td>
<td>Get buffered item count</td>
</tr>
<tr>
<td><code>sk_tcp_close</code></td>
<td><code>(socket_id) -> void</code></td>
<td>Close socket</td>
</tr>
</tbody>
</table>
<p><strong>Rust Example:</strong></p>
<pre><code class="rust"><span class="hl-0">#[link(wasm_import_module </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">"env"</span><span class="hl-0">)]</span><br/><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">"C"</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_tcp_create</span><span class="hl-0">() </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_tcp_connect</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">, addr_ptr</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">*const</span><span class="hl-0"> </span><span class="hl-5">u8</span><span class="hl-0">, addr_len</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">usize</span><span class="hl-0">, port</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">u16</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_tcp_connected</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_tcp_set_line_buffering</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">, enabled</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_tcp_send</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">, data_ptr</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">*const</span><span class="hl-0"> </span><span class="hl-5">u8</span><span class="hl-0">, data_len</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">usize</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_tcp_recv_line</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">, buf_ptr</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">*mut</span><span class="hl-0"> </span><span class="hl-5">u8</span><span class="hl-0">, buf_max_len</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">usize</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_tcp_recv_raw</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">, buf_ptr</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">*mut</span><span class="hl-0"> </span><span class="hl-5">u8</span><span class="hl-0">, buf_max_len</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">usize</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_tcp_pending</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_tcp_close</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">);</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">// Example: Furuno radar control connection</span><br/><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">connect_furuno_radar</span><span class="hl-0">(ip</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&</span><span class="hl-5">str</span><span class="hl-0">, port</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">u16</span><span class="hl-0">) </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-6">// Create TCP socket</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> socket_id </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> { </span><span class="hl-7">sk_tcp_create</span><span class="hl-0">() };</span><br/><span class="hl-0"> </span><span class="hl-1">if</span><span class="hl-0"> socket_id </span><span class="hl-1"><</span><span class="hl-0"> </span><span class="hl-2">0</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0"> </span><span class="hl-1">-</span><span class="hl-2">1</span><span class="hl-0">;</span><br/><span class="hl-0"> }</span><br/><br/><span class="hl-0"> </span><span class="hl-6">// Initiate connection (non-blocking)</span><br/><span class="hl-0"> </span><span class="hl-1">if</span><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> { </span><span class="hl-7">sk_tcp_connect</span><span class="hl-0">(socket_id, ip</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), ip</span><span class="hl-1">.</span><span class="hl-7">len</span><span class="hl-0">(), port) } </span><span class="hl-1"><</span><span class="hl-0"> </span><span class="hl-2">0</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0"> </span><span class="hl-1">-</span><span class="hl-2">1</span><span class="hl-0">;</span><br/><span class="hl-0"> }</span><br/><br/><span class="hl-0"> socket_id</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">poll_connection</span><span class="hl-0">(socket_id</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">i32</span><span class="hl-0">) {</span><br/><span class="hl-0"> </span><span class="hl-6">// Check if connected</span><br/><span class="hl-0"> </span><span class="hl-1">if</span><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> { </span><span class="hl-7">sk_tcp_connected</span><span class="hl-0">(socket_id) } </span><span class="hl-1">!=</span><span class="hl-0"> </span><span class="hl-2">1</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0">; </span><span class="hl-6">// Still connecting</span><br/><span class="hl-0"> }</span><br/><br/><span class="hl-0"> </span><span class="hl-6">// Send command with \r\n terminator</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> cmd </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">"$S69,2,0,0,60,300,0</span><span class="hl-1">\r\n</span><span class="hl-4">"</span><span class="hl-0">;</span><br/><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> { </span><span class="hl-7">sk_tcp_send</span><span class="hl-0">(socket_id, cmd</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), cmd</span><span class="hl-1">.</span><span class="hl-7">len</span><span class="hl-0">()) };</span><br/><br/><span class="hl-0"> </span><span class="hl-6">// Receive response line</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> </span><span class="hl-1">mut</span><span class="hl-0"> buf </span><span class="hl-1">=</span><span class="hl-0"> [</span><span class="hl-2">0</span><span class="hl-5">u8</span><span class="hl-0">; </span><span class="hl-2">256</span><span class="hl-0">];</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> len </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> { </span><span class="hl-7">sk_tcp_recv_line</span><span class="hl-0">(socket_id, buf</span><span class="hl-1">.</span><span class="hl-7">as_mut_ptr</span><span class="hl-0">(), buf</span><span class="hl-1">.</span><span class="hl-7">len</span><span class="hl-0">()) };</span><br/><span class="hl-0"> </span><span class="hl-1">if</span><span class="hl-0"> len </span><span class="hl-1">></span><span class="hl-0"> </span><span class="hl-2">0</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-6">// Process response</span><br/><span class="hl-0"> }</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
<p><strong>Important Notes:</strong></p>
<ul>
<li>Connection is non-blocking - poll <code>sk_tcp_connected()</code> until connected</li>
<li>Line-buffered mode (default) splits incoming data on <code>\r\n</code> or <code>\n</code></li>
<li>Raw mode returns data as it arrives (for binary protocols)</li>
<li>Use <code>sk_tcp_pending()</code> to check if data is available</li>
<li>All sockets are automatically closed when plugin stops</li>
</ul>
<h2 id="put-handlers-api" class="tsd-anchor-link">PUT Handlers API<a href="#put-handlers-api" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../../../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<p>WASM plugins can register PUT handlers to respond to PUT requests from clients, enabling vessel control and configuration management.</p>
<p><strong>Requirements:</strong></p>
<ul>
<li>Plugin must declare <code>"putHandlers": true</code> in manifest</li>
<li>Import PUT handler functions from FFI</li>
<li>Register handlers during <code>plugin_start()</code></li>
<li>Export handler functions with correct naming convention</li>
</ul>
<p><strong>Manifest Configuration:</strong></p>
<pre><code class="json"><span class="hl-0">{</span><br/><span class="hl-0"> </span><span class="hl-3">"name"</span><span class="hl-0">: </span><span class="hl-4">"my-plugin"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"wasmManifest"</span><span class="hl-0">: </span><span class="hl-4">"plugin.wasm"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"wasmCapabilities"</span><span class="hl-0">: {</span><br/><span class="hl-0"> </span><span class="hl-3">"putHandlers"</span><span class="hl-0">: </span><span class="hl-2">true</span><br/><span class="hl-0"> }</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
<p><strong>Handler Naming Convention:</strong></p>
<p><strong>Format:</strong> <code>handle_put_{context}_{path}</code></p>
<ul>
<li>Replace all dots (<code>.</code>) with underscores (<code>_</code>)</li>
<li>Convert to lowercase (recommended)</li>
</ul>
<p><strong>Examples:</strong></p>
<table>
<thead>
<tr>
<th>Context</th>
<th>Path</th>
<th>Handler Function Name</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>vessels.self</code></td>
<td><code>navigation.anchor.position</code></td>
<td><code>handle_put_vessels_self_navigation_anchor_position</code></td>
</tr>
<tr>
<td><code>vessels.self</code></td>
<td><code>steering.autopilot.target.headingTrue</code></td>
<td><code>handle_put_vessels_self_steering_autopilot_target_headingTrue</code></td>
</tr>
</tbody>
</table>
<p><strong>Response Format:</strong></p>
<pre><code class="json"><span class="hl-0">{</span><br/><span class="hl-0"> </span><span class="hl-3">"state"</span><span class="hl-0">: </span><span class="hl-4">"COMPLETED"</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"statusCode"</span><span class="hl-0">: </span><span class="hl-2">200</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">"message"</span><span class="hl-0">: </span><span class="hl-4">"Operation successful"</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
<ul>
<li><code>state</code> - Request state: <code>COMPLETED</code> or <code>PENDING</code></li>
<li><code>statusCode</code> - HTTP status code (200, 400, 403, 500, 501)</li>
<li><code>message</code> - Human-readable message (optional)</li>
</ul>
<h2 id="storage-api" class="tsd-anchor-link">Storage API<a href="#storage-api" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../../../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<p>Plugins have access to isolated virtual filesystem:</p>
<pre><code class="rust"><span class="hl-1">use</span><span class="hl-0"> </span><span class="hl-5">std</span><span class="hl-1">::</span><span class="hl-0">fs;</span><br/><br/><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">save_state</span><span class="hl-0">() {</span><br/><span class="hl-0"> </span><span class="hl-6">// Plugin sees "/" as its VFS root</span><br/><span class="hl-0"> </span><span class="hl-5">fs</span><span class="hl-1">::</span><span class="hl-7">write</span><span class="hl-0">(</span><span class="hl-4">"/data/state.json"</span><span class="hl-0">, state_json)</span><span class="hl-1">.</span><span class="hl-7">unwrap</span><span class="hl-0">();</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">load_state</span><span class="hl-0">() </span><span class="hl-1">-></span><span class="hl-0"> </span><span class="hl-5">String</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-5">fs</span><span class="hl-1">::</span><span class="hl-7">read_to_string</span><span class="hl-0">(</span><span class="hl-4">"/data/state.json"</span><span class="hl-0">)</span><span class="hl-1">.</span><span class="hl-7">unwrap_or_default</span><span class="hl-0">()</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
<p><strong>VFS Structure:</strong></p>
<pre><code><span class="hl-1">/</span><span class="hl-0"> (</span><span class="hl-2">VFS</span><span class="hl-0"> root)</span><br/><span class="hl-0">├── data</span><span class="hl-1">/</span><span class="hl-0"> # Persistent storage</span><br/><span class="hl-0">├── config</span><span class="hl-1">/</span><span class="hl-0"> # Plugin</span><span class="hl-1">-</span><span class="hl-0">managed config</span><br/><span class="hl-0">└── tmp</span><span class="hl-1">/</span><span class="hl-0"> # Temporary files</span>
</code><button>Copy</button></pre>
<h2 id="delta-emission" class="tsd-anchor-link">Delta Emission<a href="#delta-emission" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../../../assets/icons.svg#icon-anchor"></use></svg></a></h2>
<p>Emit delta messages to update Signal K data:</p>
<pre><code class="rust"><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">emit_position_delta</span><span class="hl-0">() {</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> delta </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">r#"{</span><br/><span class="hl-4"> "context": "vessels.self",</span><br/><span class="hl-4"> "updates": [{</span><br/><span class="hl-4"> "source": {</span><br/><span class="hl-4"> "label": "example-wasm",</span><br/><span class="hl-4"> "type": "plugin"</span><br/><span class="hl-4"> },</span><br/><span class="hl-4"> "timestamp": "2025-12-01T10:00:00.000Z",</span><br/><span class="hl-4"> "values": [{</span><br/><span class="hl-4"> "path": "navigation.position",</span><br/><span class="hl-4"> "value": {</span><br/><span class="hl-4"> "latitude": 60.1,</span><br/><span class="hl-4"> "longitude": 24.9</span><br/><span class="hl-4"> }</span><br/><span class="hl-4"> }]</span><br/><span class="hl-4"> }]</span><br/><span class="hl-4"> }"#</span><span class="hl-0">;</span><br/><br/><span class="hl-0"> </span><span class="hl-7">handle_message</span><span class="hl-0">(</span><span class="hl-1">&</span><span class="hl-0">delta);</span><br/><span class="hl-0">}</span>
</code><button type="button">Copy</button></pre>
</div></div><div class="col-sidebar"><div class="page-menu"><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../../../assets/icons.svg#icon-chevronDown"></use></svg><h3>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#capability-types"><span>Capability <wbr/>Types</span></a><a href="#network-api-assemblyscript"><span>Network <wbr/>API (<wbr/>Assembly<wbr/>Script)</span></a><a href="#raw-sockets-api-udp"><span>Raw <wbr/>Sockets <wbr/>API (<wbr/>UDP)</span></a><a href="#raw-sockets-api-tcp"><span>Raw <wbr/>Sockets <wbr/>API (<wbr/>TCP)</span></a><a href="#put-handlers-api"><span>PUT <wbr/>Handlers <wbr/>API</span></a><a href="#storage-api"><span>S