UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

212 lines (203 loc) 50.9 kB
<!DOCTYPE html><html class="default" lang="en" data-base="../../../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>Rust Plugins | 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">Rust Plugins</a></li></ul></div><div class="tsd-panel tsd-typography"><h1 id="creating-rust-plugins" class="tsd-anchor-link">Creating Rust Plugins<a href="#creating-rust-plugins" 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> <p>Rust is excellent for WASM plugins due to its zero-cost abstractions, memory safety, and mature WASM tooling. Signal K Rust plugins use <strong>buffer-based FFI</strong> for string passing, which differs from AssemblyScript's automatic string handling.</p> <h2 id="rust-vs-assemblyscript-key-differences" class="tsd-anchor-link">Rust vs AssemblyScript: Key Differences<a href="#rust-vs-assemblyscript-key-differences" 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> <table> <thead> <tr> <th>Aspect</th> <th>AssemblyScript</th> <th>Rust</th> </tr> </thead> <tbody> <tr> <td>String passing</td> <td>Automatic via AS loader</td> <td>Manual buffer-based FFI</td> </tr> <tr> <td>Memory management</td> <td>AS runtime handles</td> <td><code>allocate</code>/<code>deallocate</code> exports</td> </tr> <tr> <td>Binary size</td> <td>3-10 KB</td> <td>50-200 KB</td> </tr> <tr> <td>Target</td> <td><code>wasm32</code> (AS compiler)</td> <td><code>wasm32-wasip1</code></td> </tr> </tbody> </table> <h2 id="step-1-project-structure" class="tsd-anchor-link">Step 1: Project Structure<a href="#step-1-project-structure" 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>Create a new Rust library project:</p> <pre><code class="bash"><span class="hl-5">cargo</span><span class="hl-0"> </span><span class="hl-4">new</span><span class="hl-0"> </span><span class="hl-2">--lib</span><span class="hl-0"> </span><span class="hl-4">example-anchor-watch-rust</span><br/><span class="hl-2">cd</span><span class="hl-0"> </span><span class="hl-4">example-anchor-watch-rust</span> </code><button type="button">Copy</button></pre> <h2 id="step-2-configure-cargotoml" class="tsd-anchor-link">Step 2: Configure Cargo.toml<a href="#step-2-configure-cargotoml" 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> <pre><code class="toml"><span class="hl-0">[</span><span class="hl-5">package</span><span class="hl-0">]</span><br/><span class="hl-0">name = </span><span class="hl-4">&quot;anchor_watch_rust&quot;</span><br/><span class="hl-0">version = </span><span class="hl-4">&quot;0.1.0&quot;</span><br/><span class="hl-0">edition = </span><span class="hl-4">&quot;2021&quot;</span><br/><br/><span class="hl-0">[</span><span class="hl-5">lib</span><span class="hl-0">]</span><br/><span class="hl-0">crate-type = [</span><span class="hl-4">&quot;cdylib&quot;</span><span class="hl-0">]</span><br/><br/><span class="hl-0">[</span><span class="hl-5">dependencies</span><span class="hl-0">]</span><br/><span class="hl-0">serde = { version = </span><span class="hl-4">&quot;1.0&quot;</span><span class="hl-0">, features = [</span><span class="hl-4">&quot;derive&quot;</span><span class="hl-0">] }</span><br/><span class="hl-0">serde_json = </span><span class="hl-4">&quot;1.0&quot;</span><br/><br/><span class="hl-0">[</span><span class="hl-5">profile</span><span class="hl-0">.</span><span class="hl-5">release</span><span class="hl-0">]</span><br/><span class="hl-0">opt-level = </span><span class="hl-4">&quot;z&quot;</span><span class="hl-0"> </span><span class="hl-6"># Optimize for size</span><br/><span class="hl-0">lto = </span><span class="hl-2">true</span><span class="hl-0"> </span><span class="hl-6"># Link-time optimization</span><br/><span class="hl-0">strip = </span><span class="hl-2">true</span><span class="hl-0"> </span><span class="hl-6"># Strip symbols</span> </code><button type="button">Copy</button></pre> <h2 id="step-3-implement-plugin-srclibrs" class="tsd-anchor-link">Step 3: Implement Plugin (src/lib.rs)<a href="#step-3-implement-plugin-srclibrs" 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> <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-5">cell</span><span class="hl-1">::</span><span class="hl-5">RefCell</span><span class="hl-0">;</span><br/><span class="hl-1">use</span><span class="hl-0"> </span><span class="hl-5">serde</span><span class="hl-1">::</span><span class="hl-0">{</span><span class="hl-5">Deserialize</span><span class="hl-0">, </span><span class="hl-5">Serialize</span><span class="hl-0">};</span><br/><br/><span class="hl-6">// =============================================================================</span><br/><span class="hl-6">// FFI Imports - These MUST match what the Signal K runtime provides in &quot;env&quot;</span><br/><span class="hl-6">// =============================================================================</span><br/><br/><span class="hl-0">#[link(wasm_import_module </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">&quot;env&quot;</span><span class="hl-0">)]</span><br/><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">&quot;C&quot;</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_debug</span><span class="hl-0">(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">, 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"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_set_status</span><span class="hl-0">(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">, 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"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_set_error</span><span class="hl-0">(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">, 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"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_handle_message</span><span class="hl-0">(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">, 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"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">sk_register_put_handler</span><span class="hl-0">(</span><br/><span class="hl-0"> context_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">, context_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"> path_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">, path_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">-&gt;</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">// =============================================================================</span><br/><span class="hl-6">// Helper wrappers for FFI functions</span><br/><span class="hl-6">// =============================================================================</span><br/><br/><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">debug</span><span class="hl-0">(msg</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</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_debug</span><span class="hl-0">(msg</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), msg</span><span class="hl-1">.</span><span class="hl-7">len</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">set_status</span><span class="hl-0">(msg</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</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_set_status</span><span class="hl-0">(msg</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), msg</span><span class="hl-1">.</span><span class="hl-7">len</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">set_error</span><span class="hl-0">(msg</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</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_set_error</span><span class="hl-0">(msg</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), msg</span><span class="hl-1">.</span><span class="hl-7">len</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">handle_message</span><span class="hl-0">(msg</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</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_handle_message</span><span class="hl-0">(msg</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), msg</span><span class="hl-1">.</span><span class="hl-7">len</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">register_put_handler</span><span class="hl-0">(context</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</span><span class="hl-0">, path</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</span><span class="hl-0">) </span><span class="hl-1">-&gt;</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">unsafe</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-7">sk_register_put_handler</span><span class="hl-0">(</span><br/><span class="hl-0"> context</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), context</span><span class="hl-1">.</span><span class="hl-7">len</span><span class="hl-0">(),</span><br/><span class="hl-0"> path</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), path</span><span class="hl-1">.</span><span class="hl-7">len</span><span class="hl-0">()</span><br/><span class="hl-0"> )</span><br/><span class="hl-0"> }</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">// =============================================================================</span><br/><span class="hl-6">// Memory Allocation - REQUIRED for buffer-based string passing</span><br/><span class="hl-6">// =============================================================================</span><br/><br/><span class="hl-6">/// Allocate memory for string passing from host</span><br/><span class="hl-0">#[no_mangle]</span><br/><span class="hl-1">pub</span><span class="hl-0"> </span><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">&quot;C&quot;</span><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">allocate</span><span class="hl-0">(size</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">-&gt;</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"> {</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-5">Vec</span><span class="hl-1">::</span><span class="hl-7">with_capacity</span><span class="hl-0">(size);</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> ptr </span><span class="hl-1">=</span><span class="hl-0"> buf</span><span class="hl-1">.</span><span class="hl-7">as_mut_ptr</span><span class="hl-0">();</span><br/><span class="hl-0"> </span><span class="hl-5">std</span><span class="hl-1">::</span><span class="hl-5">mem</span><span class="hl-1">::</span><span class="hl-7">forget</span><span class="hl-0">(buf);</span><br/><span class="hl-0"> ptr</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">/// Deallocate memory</span><br/><span class="hl-0">#[no_mangle]</span><br/><span class="hl-1">pub</span><span class="hl-0"> </span><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">&quot;C&quot;</span><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">deallocate</span><span class="hl-0">(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">, size</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"> </span><span class="hl-1">unsafe</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> _ </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-5">Vec</span><span class="hl-1">::</span><span class="hl-7">from_raw_parts</span><span class="hl-0">(ptr, </span><span class="hl-2">0</span><span class="hl-0">, size);</span><br/><span class="hl-0"> }</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">// =============================================================================</span><br/><span class="hl-6">// Plugin State</span><br/><span class="hl-6">// =============================================================================</span><br/><br/><span class="hl-7">thread_local!</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">static</span><span class="hl-0"> </span><span class="hl-2">STATE</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">RefCell</span><span class="hl-0">&lt;</span><span class="hl-5">PluginState</span><span class="hl-0">&gt; </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-5">RefCell</span><span class="hl-1">::</span><span class="hl-7">new</span><span class="hl-0">(</span><span class="hl-5">PluginState</span><span class="hl-1">::</span><span class="hl-7">default</span><span class="hl-0">());</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-0">#[derive(</span><span class="hl-5">Debug</span><span class="hl-0">, </span><span class="hl-5">Clone</span><span class="hl-0">, </span><span class="hl-5">Serialize</span><span class="hl-0">, </span><span class="hl-5">Deserialize</span><span class="hl-0">, </span><span class="hl-5">Default</span><span class="hl-0">)]</span><br/><span class="hl-0">#[serde(rename_all </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">&quot;camelCase&quot;</span><span class="hl-0">)]</span><br/><span class="hl-1">struct</span><span class="hl-0"> </span><span class="hl-5">PluginConfig</span><span class="hl-0"> {</span><br/><span class="hl-0"> #[serde(default)]</span><br/><span class="hl-0"> max_radius</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">f64</span><span class="hl-0">,</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-0">#[derive(</span><span class="hl-5">Debug</span><span class="hl-0">, </span><span class="hl-5">Default</span><span class="hl-0">)]</span><br/><span class="hl-1">struct</span><span class="hl-0"> </span><span class="hl-5">PluginState</span><span class="hl-0"> {</span><br/><span class="hl-0"> config</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">PluginConfig</span><span class="hl-0">,</span><br/><span class="hl-0"> is_running</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">bool</span><span class="hl-0">,</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">// =============================================================================</span><br/><span class="hl-6">// Plugin Exports - Core plugin interface</span><br/><span class="hl-6">// =============================================================================</span><br/><br/><span class="hl-1">static</span><span class="hl-0"> </span><span class="hl-2">PLUGIN_ID</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</span><span class="hl-0"> </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">&quot;my-rust-plugin&quot;</span><span class="hl-0">;</span><br/><span class="hl-1">static</span><span class="hl-0"> </span><span class="hl-2">PLUGIN_NAME</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</span><span class="hl-0"> </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">&quot;My Rust Plugin&quot;</span><span class="hl-0">;</span><br/><span class="hl-1">static</span><span class="hl-0"> </span><span class="hl-2">PLUGIN_SCHEMA</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</span><span class="hl-0"> </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-4">r#&quot;{</span><br/><span class="hl-4"> &quot;type&quot;: &quot;object&quot;,</span><br/><span class="hl-4"> &quot;properties&quot;: {</span><br/><span class="hl-4"> &quot;maxRadius&quot;: {</span><br/><span class="hl-4"> &quot;type&quot;: &quot;number&quot;,</span><br/><span class="hl-4"> &quot;title&quot;: &quot;Max Radius&quot;,</span><br/><span class="hl-4"> &quot;default&quot;: 50</span><br/><span class="hl-4"> }</span><br/><span class="hl-4"> }</span><br/><span class="hl-4">}&quot;#</span><span class="hl-0">;</span><br/><br/><span class="hl-6">/// Return the plugin ID (buffer-based)</span><br/><span class="hl-0">#[no_mangle]</span><br/><span class="hl-1">pub</span><span class="hl-0"> </span><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">&quot;C&quot;</span><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">plugin_id</span><span class="hl-0">(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">, out_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">-&gt;</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-7">write_string</span><span class="hl-0">(</span><span class="hl-2">PLUGIN_ID</span><span class="hl-0">, out_ptr, out_max_len)</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">/// Return the plugin name (buffer-based)</span><br/><span class="hl-0">#[no_mangle]</span><br/><span class="hl-1">pub</span><span class="hl-0"> </span><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">&quot;C&quot;</span><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">plugin_name</span><span class="hl-0">(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">, out_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">-&gt;</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-7">write_string</span><span class="hl-0">(</span><span class="hl-2">PLUGIN_NAME</span><span class="hl-0">, out_ptr, out_max_len)</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">/// Return the plugin JSON schema (buffer-based)</span><br/><span class="hl-0">#[no_mangle]</span><br/><span class="hl-1">pub</span><span class="hl-0"> </span><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">&quot;C&quot;</span><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">plugin_schema</span><span class="hl-0">(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">, out_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">-&gt;</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-7">write_string</span><span class="hl-0">(</span><span class="hl-2">PLUGIN_SCHEMA</span><span class="hl-0">, out_ptr, out_max_len)</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">/// Start the plugin with configuration</span><br/><span class="hl-0">#[no_mangle]</span><br/><span class="hl-1">pub</span><span class="hl-0"> </span><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">&quot;C&quot;</span><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">plugin_start</span><span class="hl-0">(config_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">, config_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">-&gt;</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">// Read config from buffer</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> config_json </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> slice </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-5">std</span><span class="hl-1">::</span><span class="hl-5">slice</span><span class="hl-1">::</span><span class="hl-7">from_raw_parts</span><span class="hl-0">(config_ptr, config_len);</span><br/><span class="hl-0"> </span><span class="hl-5">String</span><span class="hl-1">::</span><span class="hl-7">from_utf8_lossy</span><span class="hl-0">(slice)</span><span class="hl-1">.</span><span class="hl-7">to_string</span><span class="hl-0">()</span><br/><span class="hl-0"> };</span><br/><br/><span class="hl-0"> </span><span class="hl-6">// Parse configuration</span><br/><span class="hl-0"> </span><span class="hl-1">let</span><span class="hl-0"> parsed_config</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-5">PluginConfig</span><span class="hl-0"> </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-1">match</span><span class="hl-0"> </span><span class="hl-5">serde_json</span><span class="hl-1">::</span><span class="hl-7">from_str</span><span class="hl-0">(</span><span class="hl-1">&amp;</span><span class="hl-0">config_json) {</span><br/><span class="hl-0"> </span><span class="hl-5">Ok</span><span class="hl-0">(c) </span><span class="hl-1">=&gt;</span><span class="hl-0"> c,</span><br/><span class="hl-0"> </span><span class="hl-5">Err</span><span class="hl-0">(e) </span><span class="hl-1">=&gt;</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-7">set_error</span><span class="hl-0">(</span><span class="hl-1">&amp;</span><span class="hl-7">format!</span><span class="hl-0">(</span><span class="hl-4">&quot;Failed to parse config: {}&quot;</span><span class="hl-0">, e));</span><br/><span class="hl-0"> </span><span class="hl-1">return</span><span class="hl-0"> </span><span class="hl-2">1</span><span class="hl-0">;</span><br/><span class="hl-0"> }</span><br/><span class="hl-0"> };</span><br/><br/><span class="hl-0"> </span><span class="hl-6">// Update state</span><br/><span class="hl-0"> </span><span class="hl-2">STATE</span><span class="hl-1">.</span><span class="hl-7">with</span><span class="hl-0">(</span><span class="hl-1">|</span><span class="hl-0">state</span><span class="hl-1">|</span><span class="hl-0"> {</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"> s </span><span class="hl-1">=</span><span class="hl-0"> state</span><span class="hl-1">.</span><span class="hl-7">borrow_mut</span><span class="hl-0">();</span><br/><span class="hl-0"> s</span><span class="hl-1">.</span><span class="hl-0">config </span><span class="hl-1">=</span><span class="hl-0"> parsed_config;</span><br/><span class="hl-0"> s</span><span class="hl-1">.</span><span class="hl-0">is_running </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-2">true</span><span class="hl-0">;</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">&quot;Plugin started successfully&quot;</span><span class="hl-0">);</span><br/><span class="hl-0"> </span><span class="hl-7">set_status</span><span class="hl-0">(</span><span class="hl-4">&quot;Running&quot;</span><span class="hl-0">);</span><br/><br/><span class="hl-0"> </span><span class="hl-2">0</span><span class="hl-0"> </span><span class="hl-6">// Success</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">/// Stop the plugin</span><br/><span class="hl-0">#[no_mangle]</span><br/><span class="hl-1">pub</span><span class="hl-0"> </span><span class="hl-1">extern</span><span class="hl-0"> </span><span class="hl-4">&quot;C&quot;</span><span class="hl-0"> </span><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">plugin_stop</span><span class="hl-0">() </span><span class="hl-1">-&gt;</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-2">STATE</span><span class="hl-1">.</span><span class="hl-7">with</span><span class="hl-0">(</span><span class="hl-1">|</span><span class="hl-0">state</span><span class="hl-1">|</span><span class="hl-0"> {</span><br/><span class="hl-0"> state</span><span class="hl-1">.</span><span class="hl-7">borrow_mut</span><span class="hl-0">()</span><span class="hl-1">.</span><span class="hl-0">is_running </span><span class="hl-1">=</span><span class="hl-0"> </span><span class="hl-2">false</span><span class="hl-0">;</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">&quot;Plugin stopped&quot;</span><span class="hl-0">);</span><br/><span class="hl-0"> </span><span class="hl-7">set_status</span><span class="hl-0">(</span><span class="hl-4">&quot;Stopped&quot;</span><span class="hl-0">);</span><br/><br/><span class="hl-0"> </span><span class="hl-2">0</span><span class="hl-0"> </span><span class="hl-6">// Success</span><br/><span class="hl-0">}</span><br/><br/><span class="hl-6">// =============================================================================</span><br/><span class="hl-6">// Helper Functions</span><br/><span class="hl-6">// =============================================================================</span><br/><br/><span class="hl-6">/// Write string to output buffer, return bytes written</span><br/><span class="hl-1">fn</span><span class="hl-0"> </span><span class="hl-7">write_string</span><span class="hl-0">(s</span><span class="hl-1">:</span><span class="hl-0"> </span><span class="hl-1">&amp;</span><span class="hl-5">str</span><span class="hl-0">, 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">, 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">-&gt;</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">let</span><span class="hl-0"> bytes </span><span class="hl-1">=</span><span class="hl-0"> s</span><span class="hl-1">.</span><span class="hl-7">as_bytes</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"> bytes</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-7">min</span><span class="hl-0">(max_len);</span><br/><span class="hl-0"> </span><span class="hl-1">unsafe</span><span class="hl-0"> {</span><br/><span class="hl-0"> </span><span class="hl-5">std</span><span class="hl-1">::</span><span class="hl-5">ptr</span><span class="hl-1">::</span><span class="hl-7">copy_nonoverlapping</span><span class="hl-0">(bytes</span><span class="hl-1">.</span><span class="hl-7">as_ptr</span><span class="hl-0">(), ptr, len);</span><br/><span class="hl-0"> }</span><br/><span class="hl-0"> len </span><span class="hl-1">as</span><span class="hl-0"> </span><span class="hl-5">i32</span><br/><span class="hl-0">}</span> </code><button type="button">Copy</button></pre> <h2 id="step-4-create-packagejson" class="tsd-anchor-link">Step 4: Create package.json<a href="#step-4-create-packagejson" 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> <pre><code class="json"><span class="hl-0">{</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;name&quot;</span><span class="hl-0">: </span><span class="hl-4">&quot;my-rust-wasm-plugin&quot;</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;version&quot;</span><span class="hl-0">: </span><span class="hl-4">&quot;0.1.0&quot;</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;description&quot;</span><span class="hl-0">: </span><span class="hl-4">&quot;My Rust WASM plugin for Signal K&quot;</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;keywords&quot;</span><span class="hl-0">: [</span><span class="hl-4">&quot;signalk-wasm-plugin&quot;</span><span class="hl-0">],</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;wasmManifest&quot;</span><span class="hl-0">: </span><span class="hl-4">&quot;plugin.wasm&quot;</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;wasmCapabilities&quot;</span><span class="hl-0">: {</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;network&quot;</span><span class="hl-0">: </span><span class="hl-2">false</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;storage&quot;</span><span class="hl-0">: </span><span class="hl-4">&quot;vfs-only&quot;</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;dataRead&quot;</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">&quot;dataWrite&quot;</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">&quot;putHandlers&quot;</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">&quot;author&quot;</span><span class="hl-0">: </span><span class="hl-4">&quot;Your Name&quot;</span><span class="hl-0">,</span><br/><span class="hl-0"> </span><span class="hl-3">&quot;license&quot;</span><span class="hl-0">: </span><span class="hl-4">&quot;Apache-2.0&quot;</span><br/><span class="hl-0">}</span> </code><button type="button">Copy</button></pre> <blockquote> <p><strong>Note</strong>: The package name can be anything - there's no requirement for <code>@signalk/</code> scope. The <code>wasmManifest</code> field is what identifies this as a WASM plugin.</p> </blockquote> <h2 id="step-5-build" class="tsd-anchor-link">Step 5: Build<a href="#step-5-build" 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> <pre><code class="bash"><span class="hl-6"># Build with WASI Preview 1 target (required for Signal K)</span><br/><span class="hl-5">cargo</span><span class="hl-0"> </span><span class="hl-4">build</span><span class="hl-0"> </span><span class="hl-2">--release</span><span class="hl-0"> </span><span class="hl-2">--target</span><span class="hl-0"> </span><span class="hl-4">wasm32-wasip1</span><br/><br/><span class="hl-6"># Copy to plugin.wasm</span><br/><span class="hl-5">cp</span><span class="hl-0"> </span><span class="hl-4">target/wasm32-wasip1/release/my_rust_plugin.wasm</span><span class="hl-0"> </span><span class="hl-4">plugin.wasm</span> </code><button type="button">Copy</button></pre> <blockquote> <p><strong>Important</strong>: Use <code>wasm32-wasip1</code> target, NOT <code>wasm32-wasi</code>. Signal K requires WASI Preview 1.</p> </blockquote> <h2 id="step-6-install" class="tsd-anchor-link">Step 6: Install<a href="#step-6-install" 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><strong>Option 1: Symlink (Recommended for Development)</strong></p> <pre><code class="bash"><span class="hl-2">cd</span><span class="hl-0"> </span><span class="hl-4">~/.signalk/node_modules</span><br/><span class="hl-5">ln</span><span class="hl-0"> </span><span class="hl-2">-s</span><span class="hl-0"> </span><span class="hl-4">/path/to/your/my-rust-wasm-plugin</span><span class="hl-0"> </span><span class="hl-4">my-rust-wasm-plugin</span> </code><button type="button">Copy</button></pre> <p><strong>Option 2: Direct Copy</strong></p> <pre><code class="bash"><span class="hl-5">mkdir</span><span class="hl-0"> </span><span class="hl-2">-p</span><span class="hl-0"> </span><span class="hl-4">~/.signalk/node_modules/my-rust-wasm-plugin</span><br/><span class="hl-5">cp</span><span class="hl-0"> </span><span class="hl-4">plugin.wasm</span><span class="hl-0"> </span><span class="hl-4">package.json</span><span class="hl-0"> </span><span class="hl-4">~/.signalk/node_modules/my-rust-wasm-plugin/</span> </code><button type="button">Copy</button></pre> <p><strong>Option 3: NPM Package Install</strong></p> <pre><code class="bash"><span class="hl-5">npm</span><span class="hl-0"> </span><span class="hl-4">pack</span><br/><span class="hl-5">npm</span><span class="hl-0"> </span><span class="hl-4">install</span><span class="hl-0"> </span><span class="hl-2">-g</span><span class="hl-0"> </span><span class="hl-4">./my-rust-wasm-plugin-0.1.0.tgz</span> </code><button type="button">Copy</button></pre> <h2 id="step-7-enable-in-admin-ui" class="tsd-anchor-link">Step 7: Enable in Admin UI<a href="#step-7-enable-in-admin-ui" 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> <ol> <li>Navigate to <strong>Server</strong> → <strong>Plugin Config</strong></li> <li>Find &quot;My Rust Plugin&quot;</li> <li>Click <strong>Enable</strong></li> <li>Configure settings</li> <li>Click <strong>Submit</strong></li> </ol> <h2 id="rust-ffi-interface-reference" class="tsd-anchor-link">Rust FFI Interface Reference<a href="#rust-ffi-interface-reference" 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>Signal K provides these FFI imports in the <code>env</code> module:</p> <table> <thead> <tr> <th>Function</th> <th>Parameters</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>sk_debug</code></td> <td><code>(ptr, len)</code></td> <td>Log debug message</td> </tr> <tr> <td><code>sk_set_status</code></td> <td><code>(ptr, len)</code></td> <td>Set plugin status</td> </tr> <tr> <td><code>sk_set_error</code></td> <td><code>(ptr, len)</code></td> <td>Set error message</td> </tr> <tr> <td><code>sk_handle_message</code></td> <td><code>(ptr, len)</code></td> <td>Emit delta message</td> </tr> <tr> <td><code>sk_register_put_handler</code></td> <td><code>(ctx_ptr, ctx_len, path_ptr, path_len)</code></td> <td>Register PUT handler</td> </tr> </tbody> </table> <blockquote> <p><strong>IMPORTANT: Use Exact Function Names</strong></p> <p>You MUST use the exact function names listed above. Common mistakes:</p> <ul> <li><code>sk_log_debug</code>, <code>sk_log_info</code>, <code>sk_log_warn</code> → Use <code>sk_debug</code> for all logging</li> <li><code>sk_emit_delta</code> → Use <code>sk_handle_message</code></li> <li><code>sk_udp_recv_from</code> → Use <code>sk_udp_recv</code></li> </ul> <p>There is only one logging function (<code>sk_debug</code>). If you need log levels, prefix your message:</p> <pre><code class="rust"><span class="hl-7">debug</span><span class="hl-0">(</span><span class="hl-4">&quot;[INFO] Starting radar scan&quot;</span><span class="hl-0">);</span><br/><span class="hl-7">debug</span><span class="hl-0">(</span><span class="hl-4">&quot;[WARN] Connection timeout&quot;</span><span class="hl-0">);</span> </code><button type="button">Copy</button></pre> </blockquote> <h2 id="required-plugin-exports" class="tsd-anchor-link">Required Plugin Exports<a href="#required-plugin-exports" 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>Your plugin MUST export:</p> <table> <thead> <tr> <th>Export</th> <th>Signature</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>plugin_id</code></td> <td><code>(out_ptr, max_len) -&gt; len</code></td> <td>Return plugin ID</td> </tr> <tr> <td><code>plugin_name</code></td> <td><code>(out_ptr, max_len) -&gt; len</code></td> <td>Return plugin name</td> </tr> <tr> <td><code>plugin_schema</code></td> <td><code>(out_ptr, max_len) -&gt; len</code></td> <td>Return JSON schema</td> </tr> <tr> <td><code>plugin_start</code></td> <td><code>(config_ptr, config_len) -&gt; status</code></td> <td>Start plugin</td> </tr> <tr> <td><code>plugin_stop</code></td> <td><code>() -&gt; status</code></td> <td>Stop plugin</td> </tr> <tr> <td><code>allocate</code></td> <td><code>(size) -&gt; ptr</code></td> <td>Allocate memory</td> </tr> <tr> <td><code>deallocate</code></td> <td><code>(ptr, size)</code></td> <td>Free memory</td> </tr> </tbody> </table> <h2 id="optional-plugin-exports" class="tsd-anchor-link">Optional Plugin Exports<a href="#optional-plugin-exports" 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>Your plugin MAY export:</p> <table> <thead> <tr> <th>Export</th> <th>Signature</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>poll</code></td> <td><code>() -&gt; status</code></td> <td>Called every 1 second while plugin is running. Useful for polling hardware, sockets, or external systems. Return 0 for success, non-zero for errors.</td> </tr> <tr> <td><code>http_endpoints</code></td> <td><code>() -&gt; json</code></td> <td>Return JSON array of HTTP endpoint definitions</td> </tr> <tr> <td><code>delta_handler</code></td> <td><code>(delta_ptr, delta_len)</code></td> <td>Receives Signal K deltas as JSON strings. Called for every delta emitted by the server.</td> </tr> </tbody> </table> <h2 id="additional-resources" class="tsd-anchor-link">Additional Resources<a href="#additional-resources" 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>See the example-anchor-watch-rust plugin in <code>examples/wasm-plugins/example-anchor-watch-rust/</code> for a complete working plugin with PUT handlers.</p> </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="#rust-vs-assemblyscript-key-differences"><span>Rust vs <wbr/>Assembly<wbr/>Script: <wbr/>Key <wbr/>Differences</span></a><a href="#step-1-project-structure"><span>Step 1: <wbr/>Project <wbr/>Structure</span></a><a href="#step-2-configure-cargotoml"><span>Step 2: <wbr/>Configure <wbr/>Cargo.toml</span></a><a href="#step-3-implement-plugin-srclibrs"><span>Step 3: <wbr/>Implement <wbr/>Plugin (src/lib.rs)</span></a><a href="#step-4-create-packagejson"><span>Step 4: <wbr/>Create package.json</span></a><a href="#step-5-build"><span>Step 5: <wbr/>Build</span></a><a h