<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>LinuxCNC gRPC Documentation on LinuxCNC gRPC</title><link>https://dougcalobrisi.github.io/linuxcnc-grpc/</link><description>Recent content in LinuxCNC gRPC Documentation on LinuxCNC gRPC</description><generator>Hugo</generator><language>en</language><atom:link href="https://dougcalobrisi.github.io/linuxcnc-grpc/index.xml" rel="self" type="application/rss+xml"/><item><title>Server Setup</title><link>https://dougcalobrisi.github.io/linuxcnc-grpc/server-setup/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougcalobrisi.github.io/linuxcnc-grpc/server-setup/</guid><description>&lt;p&gt;How to install and run the linuxcnc-grpc server on your LinuxCNC machine.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;a class="anchor" href="#prerequisites"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A Linux machine with &lt;strong&gt;LinuxCNC&lt;/strong&gt; installed (physical or simulation)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python 3.9+&lt;/strong&gt; (included with most LinuxCNC installations)&lt;/li&gt;
&lt;li&gt;Network access if you want to connect from a remote machine&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="install"&gt;Install&lt;a class="anchor" href="#install"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SSH into your LinuxCNC machine (or open a terminal on it) and install:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install linuxcnc-grpc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or with &lt;a href="https://docs.astral.sh/uv/"&gt;uv&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uv pip install linuxcnc-grpc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This installs both the gRPC server and the Python client library.&lt;/p&gt;</description></item><item><title>Client Quickstart</title><link>https://dougcalobrisi.github.io/linuxcnc-grpc/getting-started/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougcalobrisi.github.io/linuxcnc-grpc/getting-started/</guid><description>&lt;p&gt;Install a client library and make your first API calls. This guide assumes you already have a &lt;a href="https://dougcalobrisi.github.io/linuxcnc-grpc/server-setup/"&gt;running gRPC server&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="client-installation"&gt;Client Installation&lt;a class="anchor" href="#client-installation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="book-tabs"&gt;
&lt;input type="radio" class="toggle" name="tabs-0" id="tabs-0-0" checked="checked" /&gt;&lt;label for="tabs-0-0"&gt;Python&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install linuxcnc-grpc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;input type="radio" class="toggle" name="tabs-0" id="tabs-0-1" /&gt;&lt;label for="tabs-0-1"&gt;Go&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go get github.com/dougcalobrisi/linuxcnc-grpc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;input type="radio" class="toggle" name="tabs-0" id="tabs-0-2" /&gt;&lt;label for="tabs-0-2"&gt;Node.js / TypeScript&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install linuxcnc-grpc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;input type="radio" class="toggle" name="tabs-0" id="tabs-0-3" /&gt;&lt;label for="tabs-0-3"&gt;Rust&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;p&gt;Add to &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[dependencies]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;linuxcnc-grpc = &lt;span style="color:#f1fa8c"&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tokio = { version = &lt;span style="color:#f1fa8c"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;, features = [&lt;span style="color:#f1fa8c"&gt;&amp;#34;full&amp;#34;&lt;/span&gt;] }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tonic = &lt;span style="color:#f1fa8c"&gt;&amp;#34;0.12&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="quick-start-examples"&gt;Quick Start Examples&lt;a class="anchor" href="#quick-start-examples"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="book-tabs"&gt;
&lt;input type="radio" class="toggle" name="tabs-1" id="tabs-1-0" checked="checked" /&gt;&lt;label for="tabs-1-0"&gt;Python&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;import&lt;/span&gt; grpc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;from&lt;/span&gt; linuxcnc_pb &lt;span style="color:#ff79c6"&gt;import&lt;/span&gt; linuxcnc_pb2, linuxcnc_pb2_grpc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Connect to server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;channel &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; grpc&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;insecure_channel(&lt;span style="color:#f1fa8c"&gt;&amp;#34;localhost:50051&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stub &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; linuxcnc_pb2_grpc&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;LinuxCNCServiceStub(channel)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Get status&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;status &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; stub&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;GetStatus(linuxcnc_pb2&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;GetStatusRequest())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Print position&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pos &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; status&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;position&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;actual_position
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;print&lt;/span&gt;(&lt;span style="color:#f1fa8c"&gt;f&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;Position: X=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;{&lt;/span&gt;pos&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;x&lt;span style="color:#f1fa8c"&gt;:&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;.3f&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt; Y=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;{&lt;/span&gt;pos&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;y&lt;span style="color:#f1fa8c"&gt;:&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;.3f&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt; Z=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;{&lt;/span&gt;pos&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;z&lt;span style="color:#f1fa8c"&gt;:&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;.3f&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Print machine state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;print&lt;/span&gt;(&lt;span style="color:#f1fa8c"&gt;f&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;Mode: &lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;{&lt;/span&gt;linuxcnc_pb2&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;TaskMode&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;Name(status&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;task&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;task_mode)&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;print&lt;/span&gt;(&lt;span style="color:#f1fa8c"&gt;f&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;State: &lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;{&lt;/span&gt;linuxcnc_pb2&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;TaskState&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;Name(status&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;task&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;task_state)&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;channel&lt;span style="color:#ff79c6"&gt;.&lt;/span&gt;close()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;input type="radio" class="toggle" name="tabs-1" id="tabs-1-1" /&gt;&lt;label for="tabs-1-1"&gt;Go&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pb &lt;span style="color:#f1fa8c"&gt;&amp;#34;github.com/dougcalobrisi/linuxcnc-grpc/packages/go&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;google.golang.org/grpc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;google.golang.org/grpc/credentials/insecure&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;func&lt;/span&gt; &lt;span style="color:#50fa7b"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// Connect to server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; conn, err &lt;span style="color:#ff79c6"&gt;:=&lt;/span&gt; grpc.&lt;span style="color:#50fa7b"&gt;NewClient&lt;/span&gt;(&lt;span style="color:#f1fa8c"&gt;&amp;#34;localhost:50051&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grpc.&lt;span style="color:#50fa7b"&gt;WithTransportCredentials&lt;/span&gt;(insecure.&lt;span style="color:#50fa7b"&gt;NewCredentials&lt;/span&gt;()))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; err &lt;span style="color:#ff79c6"&gt;!=&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; log.&lt;span style="color:#50fa7b"&gt;Fatalf&lt;/span&gt;(&lt;span style="color:#f1fa8c"&gt;&amp;#34;Failed to connect: %v&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;defer&lt;/span&gt; conn.&lt;span style="color:#50fa7b"&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; client &lt;span style="color:#ff79c6"&gt;:=&lt;/span&gt; pb.&lt;span style="color:#50fa7b"&gt;NewLinuxCNCServiceClient&lt;/span&gt;(conn)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// Get status&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; status, err &lt;span style="color:#ff79c6"&gt;:=&lt;/span&gt; client.&lt;span style="color:#50fa7b"&gt;GetStatus&lt;/span&gt;(context.&lt;span style="color:#50fa7b"&gt;Background&lt;/span&gt;(), &lt;span style="color:#ff79c6"&gt;&amp;amp;&lt;/span&gt;pb.GetStatusRequest{})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; err &lt;span style="color:#ff79c6"&gt;!=&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; log.&lt;span style="color:#50fa7b"&gt;Fatalf&lt;/span&gt;(&lt;span style="color:#f1fa8c"&gt;&amp;#34;GetStatus failed: %v&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// Print position&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pos &lt;span style="color:#ff79c6"&gt;:=&lt;/span&gt; status.Position.ActualPosition
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fmt.&lt;span style="color:#50fa7b"&gt;Printf&lt;/span&gt;(&lt;span style="color:#f1fa8c"&gt;&amp;#34;Position: X=%.3f Y=%.3f Z=%.3f\n&amp;#34;&lt;/span&gt;, pos.X, pos.Y, pos.Z)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;input type="radio" class="toggle" name="tabs-1" id="tabs-1-2" /&gt;&lt;label for="tabs-1-2"&gt;Node.js / TypeScript&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;import&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;*&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;as&lt;/span&gt; grpc &lt;span style="color:#ff79c6"&gt;from&lt;/span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;@grpc/grpc-js&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;import&lt;/span&gt; { LinuxCNCServiceClient, GetStatusRequest, TaskMode, TaskState } &lt;span style="color:#ff79c6"&gt;from&lt;/span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;linuxcnc-grpc&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;// Connect to server
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;const&lt;/span&gt; client &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;new&lt;/span&gt; LinuxCNCServiceClient(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;localhost:50051&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grpc.credentials.createInsecure()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;// Get status
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;client.getStatus(GetStatusRequest.create(), (err, status) &lt;span style="color:#ff79c6"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; (err) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.error(&lt;span style="color:#f1fa8c"&gt;&amp;#34;Error:&amp;#34;&lt;/span&gt;, err);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// Print position
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;const&lt;/span&gt; pos &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; status&lt;span style="color:#ff79c6"&gt;!&lt;/span&gt;.position&lt;span style="color:#ff79c6"&gt;!&lt;/span&gt;.actualPosition&lt;span style="color:#ff79c6"&gt;!&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#f1fa8c"&gt;`Position: X=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;${&lt;/span&gt;pos.x.toFixed(&lt;span style="color:#bd93f9"&gt;3&lt;/span&gt;)&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt; Y=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;${&lt;/span&gt;pos.y.toFixed(&lt;span style="color:#bd93f9"&gt;3&lt;/span&gt;)&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt; Z=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;${&lt;/span&gt;pos.z.toFixed(&lt;span style="color:#bd93f9"&gt;3&lt;/span&gt;)&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// Print machine state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#f1fa8c"&gt;`Mode: &lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;${&lt;/span&gt;TaskMode[status&lt;span style="color:#ff79c6"&gt;!&lt;/span&gt;.task&lt;span style="color:#ff79c6"&gt;!&lt;/span&gt;.taskMode]&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#f1fa8c"&gt;`State: &lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;${&lt;/span&gt;TaskState[status&lt;span style="color:#ff79c6"&gt;!&lt;/span&gt;.task&lt;span style="color:#ff79c6"&gt;!&lt;/span&gt;.taskState]&lt;span style="color:#f1fa8c"&gt;}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;input type="radio" class="toggle" name="tabs-1" id="tabs-1-3" /&gt;&lt;label for="tabs-1-3"&gt;Rust&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;use&lt;/span&gt; linuxcnc_grpc::linuxcnc::linux_cnc_service_client::LinuxCncServiceClient;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;use&lt;/span&gt; linuxcnc_grpc::linuxcnc::GetStatusRequest;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;#[tokio::main]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;async&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;fn&lt;/span&gt; &lt;span style="color:#50fa7b"&gt;main&lt;/span&gt;() -&amp;gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;Result&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;&amp;lt;&lt;/span&gt;(), &lt;span style="color:#8be9fd;font-style:italic"&gt;Box&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;dyn&lt;/span&gt; std::error::Error&lt;span style="color:#ff79c6"&gt;&amp;gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// Connect to server
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;let&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;mut&lt;/span&gt; client &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; LinuxCncServiceClient::connect(&lt;span style="color:#f1fa8c"&gt;&amp;#34;http://localhost:50051&amp;#34;&lt;/span&gt;).&lt;span style="color:#ff79c6"&gt;await&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// Get status
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;let&lt;/span&gt; response &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; client.get_status(GetStatusRequest {}).&lt;span style="color:#ff79c6"&gt;await&lt;/span&gt;&lt;span style="color:#ff79c6"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;let&lt;/span&gt; status &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; response.into_inner();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#6272a4"&gt;// Print position
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;let&lt;/span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;Some&lt;/span&gt;(position) &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;&amp;amp;&lt;/span&gt;status.position {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;let&lt;/span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;Some&lt;/span&gt;(pos) &lt;span style="color:#ff79c6"&gt;=&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;&amp;amp;&lt;/span&gt;position.actual_position {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#50fa7b"&gt;println!&lt;/span&gt;(&lt;span style="color:#f1fa8c"&gt;&amp;#34;Position: X=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;{:.3}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt; Y=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;{:.3}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt; Z=&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;{:.3}&lt;/span&gt;&lt;span style="color:#f1fa8c"&gt;&amp;#34;&lt;/span&gt;, pos.x, pos.y, pos.z);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;Ok&lt;/span&gt;(())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="sending-commands"&gt;Sending Commands&lt;a class="anchor" href="#sending-commands"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Commands follow a consistent pattern across all languages:&lt;/p&gt;</description></item><item><title>Step-by-Step Tutorial</title><link>https://dougcalobrisi.github.io/linuxcnc-grpc/tutorial/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougcalobrisi.github.io/linuxcnc-grpc/tutorial/</guid><description>&lt;p&gt;A hands-on guide to connecting to a remote LinuxCNC machine from your development box using any of the supported client languages.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;a class="anchor" href="#prerequisites"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LinuxCNC&lt;/strong&gt; running on a machine (physical or simulation)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network access&lt;/strong&gt; between your dev machine and the LinuxCNC machine&lt;/li&gt;
&lt;li&gt;The LinuxCNC machine&amp;rsquo;s IP address (referred to as &lt;code&gt;LINUXCNC_IP&lt;/code&gt; below)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1-install-and-start-the-grpc-server"&gt;Step 1: Install and Start the gRPC Server&lt;a class="anchor" href="#step-1-install-and-start-the-grpc-server"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SSH into your LinuxCNC machine and install the server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install linuxcnc-grpc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Start the server (LinuxCNC must already be running):&lt;/p&gt;</description></item><item><title>Server Configuration</title><link>https://dougcalobrisi.github.io/linuxcnc-grpc/server/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougcalobrisi.github.io/linuxcnc-grpc/server/</guid><description>&lt;p&gt;The linuxcnc-grpc server runs on your LinuxCNC machine and exposes both LinuxCNCService and HalService over gRPC.&lt;/p&gt;
&lt;h2 id="requirements"&gt;Requirements&lt;a class="anchor" href="#requirements"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Linux with LinuxCNC installed and running&lt;/li&gt;
&lt;li&gt;Python 3.9+&lt;/li&gt;
&lt;li&gt;Network access to the machine (for remote clients)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installation"&gt;Installation&lt;a class="anchor" href="#installation"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install linuxcnc-grpc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# or with uv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uv pip install linuxcnc-grpc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="basic-usage"&gt;Basic Usage&lt;a class="anchor" href="#basic-usage"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start LinuxCNC first, then start the gRPC server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;linuxcnc-grpc --host 0.0.0.0 --port &lt;span style="color:#bd93f9"&gt;50051&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or run as a Python module:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -m linuxcnc_grpc.server --host 0.0.0.0 --port &lt;span style="color:#bd93f9"&gt;50051&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="command-line-options"&gt;Command Line Options&lt;a class="anchor" href="#command-line-options"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Option&lt;/th&gt;
 &lt;th&gt;Default&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--host&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;0.0.0.0&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Address to bind to. Use &lt;code&gt;127.0.0.1&lt;/code&gt; for local-only access&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--port&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;50051&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Port number to listen on&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--nc-files&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/home/linuxcnc/linuxcnc/nc_files&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Directory for G-code file operations&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--debug&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;off&lt;/td&gt;
 &lt;td&gt;Enable debug logging&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="auto-start-with-linuxcnc"&gt;Auto-Start with LinuxCNC&lt;a class="anchor" href="#auto-start-with-linuxcnc"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="method-1-hal-file"&gt;Method 1: HAL File&lt;a class="anchor" href="#method-1-hal-file"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add to your machine&amp;rsquo;s HAL file (e.g., &lt;code&gt;your_machine.hal&lt;/code&gt;):&lt;/p&gt;</description></item><item><title>API Reference</title><link>https://dougcalobrisi.github.io/linuxcnc-grpc/api-reference/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougcalobrisi.github.io/linuxcnc-grpc/api-reference/</guid><description>&lt;p&gt;Complete API documentation for LinuxCNC gRPC services.&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;a class="anchor" href="#table-of-contents"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#linuxcncservice"&gt;LinuxCNCService&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#getstatus"&gt;GetStatus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sendcommand"&gt;SendCommand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#waitcomplete"&gt;WaitComplete&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#streamstatus"&gt;StreamStatus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#streamerrors"&gt;StreamErrors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#uploadfile"&gt;UploadFile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#listfiles"&gt;ListFiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#deletefile"&gt;DeleteFile&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#halservice"&gt;HalService&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#getsystemstatus"&gt;GetSystemStatus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#getvalue"&gt;GetValue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#querypins"&gt;QueryPins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#querysignals"&gt;QuerySignals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#queryparams"&gt;QueryParams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#querycomponents"&gt;QueryComponents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#streamstatus-hal"&gt;StreamStatus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#watchvalues"&gt;WatchValues&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#enums"&gt;Enums&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#messages"&gt;Messages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="linuxcncservice"&gt;LinuxCNCService&lt;a class="anchor" href="#linuxcncservice"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Main service for machine control and status monitoring.&lt;/p&gt;
&lt;h3 id="getstatus"&gt;GetStatus&lt;a class="anchor" href="#getstatus"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Get the current machine status.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-protobuf" data-lang="protobuf"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;rpc&lt;/span&gt; GetStatus(GetStatusRequest) &lt;span style="color:#ff79c6"&gt;returns&lt;/span&gt; (LinuxCNCStatus)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Request:&lt;/strong&gt; &lt;code&gt;GetStatusRequest&lt;/code&gt; (empty message)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;LinuxCNCStatus&lt;/code&gt; containing:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Field&lt;/th&gt;
 &lt;th&gt;Type&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;timestamp&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;int64&lt;/td&gt;
 &lt;td&gt;Unix timestamp in nanoseconds&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;version&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;string&lt;/td&gt;
 &lt;td&gt;LinuxCNC version&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;task&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;TaskStatus&lt;/td&gt;
 &lt;td&gt;Task/interpreter state&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;trajectory&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;TrajectoryStatus&lt;/td&gt;
 &lt;td&gt;Motion planning state&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;position&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PositionStatus&lt;/td&gt;
 &lt;td&gt;All position information&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;joints&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;JointStatus[]&lt;/td&gt;
 &lt;td&gt;Per-joint status (up to 16)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;axes&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;AxisStatus[]&lt;/td&gt;
 &lt;td&gt;Per-axis status (up to 9: XYZABCUVW)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;spindles&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;SpindleStatus[]&lt;/td&gt;
 &lt;td&gt;Per-spindle status (up to 8)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;tool&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;ToolStatus&lt;/td&gt;
 &lt;td&gt;Tool table and current tool&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;io&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;IOStatus&lt;/td&gt;
 &lt;td&gt;Digital/analog I/O state&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;gcode&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;GCodeStatus&lt;/td&gt;
 &lt;td&gt;Active G-codes and M-codes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;limits&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;LimitStatus&lt;/td&gt;
 &lt;td&gt;Limit switch states&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;errors&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;ErrorMessage[]&lt;/td&gt;
 &lt;td&gt;Pending error messages&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Example (Python):&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Examples Guide</title><link>https://dougcalobrisi.github.io/linuxcnc-grpc/examples/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougcalobrisi.github.io/linuxcnc-grpc/examples/</guid><description>&lt;p&gt;Walkthrough of the example code provided with linuxcnc-grpc.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Examples are provided in four languages, each implementing the same functionality:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Example&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;get_status&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Poll and display machine status&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;stream_status&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Real-time status streaming&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;jog_axis&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Interactive jogging with keyboard&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;mdi_command&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Execute G-code via MDI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;hal_query&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Query HAL pins, signals, parameters&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;upload_file&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Upload, list, and delete G-code files&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="directory-structure"&gt;Directory Structure&lt;a class="anchor" href="#directory-structure"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;examples/
├── python/
│ ├── get_status.py
│ ├── stream_status.py
│ ├── jog_axis.py
│ ├── mdi_command.py
│ ├── hal_query.py
│ └── upload_file.py
├── go/
│ └── cmd/
│ ├── get_status/
│ ├── stream_status/
│ ├── jog_axis/
│ ├── mdi_command/
│ ├── hal_query/
│ └── upload_file/
├── node/
│ ├── get_status.ts
│ ├── stream_status.ts
│ ├── jog_axis.ts
│ ├── mdi_command.ts
│ ├── hal_query.ts
│ └── upload_file.ts
├── rust/
│ ├── Cargo.toml
│ └── src/bin/
│ ├── get_status.rs
│ ├── stream_status.rs
│ ├── jog_axis.rs
│ ├── mdi_command.rs
│ ├── hal_query.rs
│ └── upload_file.rs
└── README.md&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="running-examples"&gt;Running Examples&lt;a class="anchor" href="#running-examples"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;a class="anchor" href="#prerequisites"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;LinuxCNC running (or use a mock server for testing)&lt;/li&gt;
&lt;li&gt;gRPC server running on the LinuxCNC machine&lt;/li&gt;
&lt;li&gt;Language-specific dependencies installed&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="book-tabs"&gt;
&lt;input type="radio" class="toggle" name="tabs-0" id="tabs-0-0" checked="checked" /&gt;&lt;label for="tabs-0-0"&gt;Python&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;cd&lt;/span&gt; examples/python
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Install dependencies (if using virtualenv)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install grpcio linuxcnc-grpc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Run an example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python get_status.py --host localhost --port &lt;span style="color:#bd93f9"&gt;50051&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;input type="radio" class="toggle" name="tabs-0" id="tabs-0-1" /&gt;&lt;label for="tabs-0-1"&gt;Go&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;cd&lt;/span&gt; examples/go
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Download dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go mod download
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Run an example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go run ./cmd/get_status --host localhost --port &lt;span style="color:#bd93f9"&gt;50051&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;input type="radio" class="toggle" name="tabs-0" id="tabs-0-2" /&gt;&lt;label for="tabs-0-2"&gt;Node.js / TypeScript&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;cd&lt;/span&gt; examples/node
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Install dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Run an example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npx tsx get_status.ts --host localhost --port &lt;span style="color:#bd93f9"&gt;50051&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;input type="radio" class="toggle" name="tabs-0" id="tabs-0-3" /&gt;&lt;label for="tabs-0-3"&gt;Rust&lt;/label&gt;&lt;div class="book-tabs-content markdown-inner"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;cd&lt;/span&gt; examples/rust
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Build all examples&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cargo build --release
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Run an example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cargo run --bin get_status -- --host localhost --port &lt;span style="color:#bd93f9"&gt;50051&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6272a4"&gt;# Or run the built binary directly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./target/release/get_status --host localhost --port &lt;span style="color:#bd93f9"&gt;50051&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="get_status"&gt;get_status&lt;a class="anchor" href="#get_status"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The simplest example - polls the machine once and displays status.&lt;/p&gt;</description></item><item><title>End-to-End Testing</title><link>https://dougcalobrisi.github.io/linuxcnc-grpc/e2e-testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougcalobrisi.github.io/linuxcnc-grpc/e2e-testing/</guid><description>&lt;p&gt;The e2e test suite (&lt;code&gt;tests/test_e2e.py&lt;/code&gt;) runs against a real LinuxCNC simulator
instance, validating that the gRPC server works correctly with actual LinuxCNC
state, commands, HAL data, and file management.&lt;/p&gt;
&lt;h2 id="what-e2e-tests-cover"&gt;What E2E Tests Cover&lt;a class="anchor" href="#what-e2e-tests-cover"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The suite has ~50 tests organized into these areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Status&lt;/strong&gt; — version, joints, INI filename, positions, task state&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State transitions&lt;/strong&gt; — estop/recovery, on/off, mode changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commands&lt;/strong&gt; — homing, MDI G-code, feedrate/spindle overrides&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WaitComplete&lt;/strong&gt; — blocking until commands finish, timeout behavior&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;G-code execution&lt;/strong&gt; — upload/open/run, pause/resume, abort, single-step&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jogging&lt;/strong&gt; — incremental and continuous jog with stop&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coolant&lt;/strong&gt; — mist and flood on/off&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rapid rate &amp;amp; max velocity&lt;/strong&gt; — override scales&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Override config&lt;/strong&gt; — feed/spindle override enable/disable&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Program options&lt;/strong&gt; — optional stop, block delete&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Negative paths&lt;/strong&gt; — empty MDI, wrong mode, invalid axis, path traversal, duplicate upload&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streaming&lt;/strong&gt; — StreamStatus, StreamErrors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HAL&lt;/strong&gt; — GetSystemStatus, QueryPins/Signals/Components/Params, GetValue, StreamStatus, SendCommand, WatchValues&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File management&lt;/strong&gt; — upload/list/delete cycle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These tests catch issues that mock-based tests cannot:&lt;/p&gt;</description></item></channel></rss>