linuxcnc-grpc 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- linuxcnc_grpc-0.5.0/LICENSE +21 -0
- linuxcnc_grpc-0.5.0/PKG-INFO +234 -0
- linuxcnc_grpc-0.5.0/README.md +201 -0
- linuxcnc_grpc-0.5.0/linuxcnc_grpc.egg-info/PKG-INFO +234 -0
- linuxcnc_grpc-0.5.0/linuxcnc_grpc.egg-info/SOURCES.txt +28 -0
- linuxcnc_grpc-0.5.0/linuxcnc_grpc.egg-info/dependency_links.txt +1 -0
- linuxcnc_grpc-0.5.0/linuxcnc_grpc.egg-info/entry_points.txt +2 -0
- linuxcnc_grpc-0.5.0/linuxcnc_grpc.egg-info/requires.txt +12 -0
- linuxcnc_grpc-0.5.0/linuxcnc_grpc.egg-info/top_level.txt +2 -0
- linuxcnc_grpc-0.5.0/packages/python/linuxcnc_pb/__init__.py +6 -0
- linuxcnc_grpc-0.5.0/packages/python/linuxcnc_pb/hal_pb2.py +127 -0
- linuxcnc_grpc-0.5.0/packages/python/linuxcnc_pb/hal_pb2_grpc.py +462 -0
- linuxcnc_grpc-0.5.0/packages/python/linuxcnc_pb/linuxcnc_pb2.py +183 -0
- linuxcnc_grpc-0.5.0/packages/python/linuxcnc_pb/linuxcnc_pb2_grpc.py +418 -0
- linuxcnc_grpc-0.5.0/pyproject.toml +65 -0
- linuxcnc_grpc-0.5.0/setup.cfg +4 -0
- linuxcnc_grpc-0.5.0/src/linuxcnc_grpc/__init__.py +71 -0
- linuxcnc_grpc-0.5.0/src/linuxcnc_grpc/_generated/__init__.py +12 -0
- linuxcnc_grpc-0.5.0/src/linuxcnc_grpc/hal_mapper.py +305 -0
- linuxcnc_grpc-0.5.0/src/linuxcnc_grpc/hal_service.py +512 -0
- linuxcnc_grpc-0.5.0/src/linuxcnc_grpc/linuxcnc_mapper.py +416 -0
- linuxcnc_grpc-0.5.0/src/linuxcnc_grpc/linuxcnc_service.py +783 -0
- linuxcnc_grpc-0.5.0/src/linuxcnc_grpc/server.py +184 -0
- linuxcnc_grpc-0.5.0/tests/test_e2e.py +1447 -0
- linuxcnc_grpc-0.5.0/tests/test_examples_syntax.py +60 -0
- linuxcnc_grpc-0.5.0/tests/test_hal_mapper.py +318 -0
- linuxcnc_grpc-0.5.0/tests/test_hal_service.py +635 -0
- linuxcnc_grpc-0.5.0/tests/test_integration.py +336 -0
- linuxcnc_grpc-0.5.0/tests/test_linuxcnc_mapper.py +297 -0
- linuxcnc_grpc-0.5.0/tests/test_linuxcnc_service.py +1764 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Doug Calobrisi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: linuxcnc-grpc
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: gRPC server exposing LinuxCNC machine control and HAL functionality
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/dougcalobrisi/linuxcnc-grpc
|
|
7
|
+
Keywords: linuxcnc,cnc,grpc,machine-control,hal
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Manufacturing
|
|
11
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: grpcio>=1.60.0
|
|
23
|
+
Requires-Dist: grpcio-health-checking>=1.60.0
|
|
24
|
+
Requires-Dist: protobuf>=4.25.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: grpcio-tools>=1.60.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
29
|
+
Provides-Extra: build
|
|
30
|
+
Requires-Dist: build>=1.0.0; extra == "build"
|
|
31
|
+
Requires-Dist: twine>=5.0.0; extra == "build"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# linuxcnc-grpc
|
|
35
|
+
|
|
36
|
+
gRPC interface for LinuxCNC machine control and HAL (Hardware Abstraction Layer).
|
|
37
|
+
|
|
38
|
+
## Why gRPC?
|
|
39
|
+
|
|
40
|
+
LinuxCNC's native Python API only works locally. This project exposes it over gRPC, enabling:
|
|
41
|
+
|
|
42
|
+
- **Remote monitoring** - Build web dashboards, mobile apps, or desktop GUIs
|
|
43
|
+
- **Multi-machine management** - Monitor a fleet of CNC machines from one place
|
|
44
|
+
- **Any-language integration** - Use Go, Node.js, Rust, or any gRPC-supported language
|
|
45
|
+
- **Real-time streaming** - Subscribe to status updates instead of polling
|
|
46
|
+
|
|
47
|
+
## Running the Server
|
|
48
|
+
|
|
49
|
+
The server runs on your LinuxCNC machine and exposes the gRPC interface.
|
|
50
|
+
|
|
51
|
+
### Basic Usage
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install linuxcnc-grpc
|
|
55
|
+
# or with uv
|
|
56
|
+
uv pip install linuxcnc-grpc
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
linuxcnc-grpc --host 0.0.0.0 --port 50051
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
LinuxCNC must already be running before starting the server.
|
|
64
|
+
|
|
65
|
+
### Auto-start with LinuxCNC
|
|
66
|
+
|
|
67
|
+
To start the gRPC server automatically when LinuxCNC launches, add to your machine's HAL file:
|
|
68
|
+
|
|
69
|
+
```hal
|
|
70
|
+
# Start gRPC server (runs until LinuxCNC exits)
|
|
71
|
+
loadusr -W linuxcnc-grpc --host 0.0.0.0 --port 50051
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Or use a dedicated HAL file via your INI:
|
|
75
|
+
|
|
76
|
+
```ini
|
|
77
|
+
[HAL]
|
|
78
|
+
POSTGUI_HALFILE = grpc-server.hal
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The `-W` flag tells LinuxCNC to wait for the server to become ready before continuing.
|
|
82
|
+
|
|
83
|
+
## Quick Start
|
|
84
|
+
|
|
85
|
+
### Python
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install linuxcnc-grpc
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import grpc
|
|
93
|
+
from linuxcnc_pb import linuxcnc_pb2, linuxcnc_pb2_grpc
|
|
94
|
+
|
|
95
|
+
channel = grpc.insecure_channel("localhost:50051")
|
|
96
|
+
stub = linuxcnc_pb2_grpc.LinuxCNCServiceStub(channel)
|
|
97
|
+
|
|
98
|
+
status = stub.GetStatus(linuxcnc_pb2.GetStatusRequest())
|
|
99
|
+
print(f"Position: X={status.position.x:.3f} Y={status.position.y:.3f} Z={status.position.z:.3f}")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Go
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
go get github.com/dougcalobrisi/linuxcnc-grpc
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```go
|
|
109
|
+
import (
|
|
110
|
+
pb "github.com/dougcalobrisi/linuxcnc-grpc/packages/go"
|
|
111
|
+
"google.golang.org/grpc"
|
|
112
|
+
"google.golang.org/grpc/credentials/insecure"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
conn, _ := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
116
|
+
client := pb.NewLinuxCNCServiceClient(conn)
|
|
117
|
+
|
|
118
|
+
status, _ := client.GetStatus(context.Background(), &pb.GetStatusRequest{})
|
|
119
|
+
fmt.Printf("Position: X=%.3f Y=%.3f Z=%.3f\n", status.Position.X, status.Position.Y, status.Position.Z)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Node.js / TypeScript
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npm install linuxcnc-grpc
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import * as grpc from "@grpc/grpc-js";
|
|
130
|
+
import { LinuxCNCServiceClient, GetStatusRequest } from "linuxcnc-grpc";
|
|
131
|
+
|
|
132
|
+
const client = new LinuxCNCServiceClient("localhost:50051", grpc.credentials.createInsecure());
|
|
133
|
+
|
|
134
|
+
client.getStatus(GetStatusRequest.create(), (err, status) => {
|
|
135
|
+
console.log(`Position: X=${status.position.x.toFixed(3)} Y=${status.position.y.toFixed(3)}`);
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Rust
|
|
140
|
+
|
|
141
|
+
```toml
|
|
142
|
+
[dependencies]
|
|
143
|
+
linuxcnc-grpc = "0.5"
|
|
144
|
+
tokio = { version = "1", features = ["full"] }
|
|
145
|
+
tonic = "0.12"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```rust
|
|
149
|
+
use linuxcnc_grpc::linuxcnc::linux_cnc_service_client::LinuxCncServiceClient;
|
|
150
|
+
use linuxcnc_grpc::linuxcnc::GetStatusRequest;
|
|
151
|
+
|
|
152
|
+
let mut client = LinuxCncServiceClient::connect("http://localhost:50051").await?;
|
|
153
|
+
let status = client.get_status(GetStatusRequest {}).await?.into_inner();
|
|
154
|
+
println!("Position: X={:.3} Y={:.3}", status.position.unwrap().x, status.position.unwrap().y);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Examples
|
|
158
|
+
|
|
159
|
+
Complete examples for all supported languages:
|
|
160
|
+
|
|
161
|
+
| Example | Description | Python | Go | Node.js | Rust |
|
|
162
|
+
|---------|-------------|--------|-----|---------|------|
|
|
163
|
+
| `get_status` | Poll machine status | [view](examples/python/get_status.py) | [view](examples/go/cmd/get_status/main.go) | [view](examples/node/get_status.ts) | [view](examples/rust/src/bin/get_status.rs) |
|
|
164
|
+
| `stream_status` | Real-time status streaming | [view](examples/python/stream_status.py) | [view](examples/go/cmd/stream_status/main.go) | [view](examples/node/stream_status.ts) | [view](examples/rust/src/bin/stream_status.rs) |
|
|
165
|
+
| `jog_axis` | Jog axes with keyboard | [view](examples/python/jog_axis.py) | [view](examples/go/cmd/jog_axis/main.go) | [view](examples/node/jog_axis.ts) | [view](examples/rust/src/bin/jog_axis.rs) |
|
|
166
|
+
| `mdi_command` | Execute G-code via MDI | [view](examples/python/mdi_command.py) | [view](examples/go/cmd/mdi_command/main.go) | [view](examples/node/mdi_command.ts) | [view](examples/rust/src/bin/mdi_command.rs) |
|
|
167
|
+
| `hal_query` | Query HAL pins/signals | [view](examples/python/hal_query.py) | [view](examples/go/cmd/hal_query/main.go) | [view](examples/node/hal_query.ts) | [view](examples/rust/src/bin/hal_query.rs) |
|
|
168
|
+
| `upload_file` | Upload, list, delete G-code files | [view](examples/python/upload_file.py) | [view](examples/go/cmd/upload_file/main.go) | [view](examples/node/upload_file.ts) | [view](examples/rust/src/bin/upload_file.rs) |
|
|
169
|
+
|
|
170
|
+
See [examples/README.md](examples/README.md) for setup instructions.
|
|
171
|
+
|
|
172
|
+
## Services
|
|
173
|
+
|
|
174
|
+
- **LinuxCNCService** - Machine control: status, jogging, MDI, program execution, file management
|
|
175
|
+
- **HalService** - HAL introspection: query pins, signals, parameters (read-only)
|
|
176
|
+
|
|
177
|
+
### File Management
|
|
178
|
+
|
|
179
|
+
The server provides `UploadFile`, `ListFiles`, and `DeleteFile` RPCs for remote G-code file management. Files are stored in the NC files directory (default: `/home/linuxcnc/linuxcnc/nc_files`).
|
|
180
|
+
|
|
181
|
+
Configure the directory with `--nc-files` or the `LINUXCNC_NC_FILES` environment variable:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
linuxcnc-grpc --host 0.0.0.0 --nc-files /path/to/nc_files
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Safety Warning
|
|
188
|
+
|
|
189
|
+
This server provides remote control of CNC machinery. Ensure proper safety measures:
|
|
190
|
+
|
|
191
|
+
- Use only on trusted networks
|
|
192
|
+
- Implement authentication in production (gRPC supports TLS/mTLS)
|
|
193
|
+
- Never leave machines unattended during remote operation
|
|
194
|
+
- Verify E-stop and safety systems are functional
|
|
195
|
+
|
|
196
|
+
## Production Deployment
|
|
197
|
+
|
|
198
|
+
For production use, enable TLS authentication:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
# Server with TLS
|
|
202
|
+
credentials = grpc.ssl_server_credentials([(private_key, certificate)])
|
|
203
|
+
server.add_secure_port('[::]:50051', credentials)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
# Client with TLS
|
|
208
|
+
credentials = grpc.ssl_channel_credentials(root_certificates)
|
|
209
|
+
channel = grpc.secure_channel('your-machine:50051', credentials)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
See [Server Configuration](docs/server.md#security-considerations) for complete TLS setup instructions.
|
|
213
|
+
|
|
214
|
+
## Development
|
|
215
|
+
|
|
216
|
+
Requires [uv](https://docs.astral.sh/uv/) for Python dependency management:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Install dev dependencies
|
|
220
|
+
make setup
|
|
221
|
+
|
|
222
|
+
# Run tests
|
|
223
|
+
make test # Python tests
|
|
224
|
+
make test-all # All languages
|
|
225
|
+
|
|
226
|
+
# Generate proto code
|
|
227
|
+
make proto-all # Regenerate for all languages
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
See [CLAUDE.md](CLAUDE.md) for detailed development documentation.
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# linuxcnc-grpc
|
|
2
|
+
|
|
3
|
+
gRPC interface for LinuxCNC machine control and HAL (Hardware Abstraction Layer).
|
|
4
|
+
|
|
5
|
+
## Why gRPC?
|
|
6
|
+
|
|
7
|
+
LinuxCNC's native Python API only works locally. This project exposes it over gRPC, enabling:
|
|
8
|
+
|
|
9
|
+
- **Remote monitoring** - Build web dashboards, mobile apps, or desktop GUIs
|
|
10
|
+
- **Multi-machine management** - Monitor a fleet of CNC machines from one place
|
|
11
|
+
- **Any-language integration** - Use Go, Node.js, Rust, or any gRPC-supported language
|
|
12
|
+
- **Real-time streaming** - Subscribe to status updates instead of polling
|
|
13
|
+
|
|
14
|
+
## Running the Server
|
|
15
|
+
|
|
16
|
+
The server runs on your LinuxCNC machine and exposes the gRPC interface.
|
|
17
|
+
|
|
18
|
+
### Basic Usage
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install linuxcnc-grpc
|
|
22
|
+
# or with uv
|
|
23
|
+
uv pip install linuxcnc-grpc
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
linuxcnc-grpc --host 0.0.0.0 --port 50051
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
LinuxCNC must already be running before starting the server.
|
|
31
|
+
|
|
32
|
+
### Auto-start with LinuxCNC
|
|
33
|
+
|
|
34
|
+
To start the gRPC server automatically when LinuxCNC launches, add to your machine's HAL file:
|
|
35
|
+
|
|
36
|
+
```hal
|
|
37
|
+
# Start gRPC server (runs until LinuxCNC exits)
|
|
38
|
+
loadusr -W linuxcnc-grpc --host 0.0.0.0 --port 50051
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or use a dedicated HAL file via your INI:
|
|
42
|
+
|
|
43
|
+
```ini
|
|
44
|
+
[HAL]
|
|
45
|
+
POSTGUI_HALFILE = grpc-server.hal
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `-W` flag tells LinuxCNC to wait for the server to become ready before continuing.
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
### Python
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install linuxcnc-grpc
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import grpc
|
|
60
|
+
from linuxcnc_pb import linuxcnc_pb2, linuxcnc_pb2_grpc
|
|
61
|
+
|
|
62
|
+
channel = grpc.insecure_channel("localhost:50051")
|
|
63
|
+
stub = linuxcnc_pb2_grpc.LinuxCNCServiceStub(channel)
|
|
64
|
+
|
|
65
|
+
status = stub.GetStatus(linuxcnc_pb2.GetStatusRequest())
|
|
66
|
+
print(f"Position: X={status.position.x:.3f} Y={status.position.y:.3f} Z={status.position.z:.3f}")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Go
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
go get github.com/dougcalobrisi/linuxcnc-grpc
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```go
|
|
76
|
+
import (
|
|
77
|
+
pb "github.com/dougcalobrisi/linuxcnc-grpc/packages/go"
|
|
78
|
+
"google.golang.org/grpc"
|
|
79
|
+
"google.golang.org/grpc/credentials/insecure"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
conn, _ := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
83
|
+
client := pb.NewLinuxCNCServiceClient(conn)
|
|
84
|
+
|
|
85
|
+
status, _ := client.GetStatus(context.Background(), &pb.GetStatusRequest{})
|
|
86
|
+
fmt.Printf("Position: X=%.3f Y=%.3f Z=%.3f\n", status.Position.X, status.Position.Y, status.Position.Z)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Node.js / TypeScript
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm install linuxcnc-grpc
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import * as grpc from "@grpc/grpc-js";
|
|
97
|
+
import { LinuxCNCServiceClient, GetStatusRequest } from "linuxcnc-grpc";
|
|
98
|
+
|
|
99
|
+
const client = new LinuxCNCServiceClient("localhost:50051", grpc.credentials.createInsecure());
|
|
100
|
+
|
|
101
|
+
client.getStatus(GetStatusRequest.create(), (err, status) => {
|
|
102
|
+
console.log(`Position: X=${status.position.x.toFixed(3)} Y=${status.position.y.toFixed(3)}`);
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Rust
|
|
107
|
+
|
|
108
|
+
```toml
|
|
109
|
+
[dependencies]
|
|
110
|
+
linuxcnc-grpc = "0.5"
|
|
111
|
+
tokio = { version = "1", features = ["full"] }
|
|
112
|
+
tonic = "0.12"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```rust
|
|
116
|
+
use linuxcnc_grpc::linuxcnc::linux_cnc_service_client::LinuxCncServiceClient;
|
|
117
|
+
use linuxcnc_grpc::linuxcnc::GetStatusRequest;
|
|
118
|
+
|
|
119
|
+
let mut client = LinuxCncServiceClient::connect("http://localhost:50051").await?;
|
|
120
|
+
let status = client.get_status(GetStatusRequest {}).await?.into_inner();
|
|
121
|
+
println!("Position: X={:.3} Y={:.3}", status.position.unwrap().x, status.position.unwrap().y);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Examples
|
|
125
|
+
|
|
126
|
+
Complete examples for all supported languages:
|
|
127
|
+
|
|
128
|
+
| Example | Description | Python | Go | Node.js | Rust |
|
|
129
|
+
|---------|-------------|--------|-----|---------|------|
|
|
130
|
+
| `get_status` | Poll machine status | [view](examples/python/get_status.py) | [view](examples/go/cmd/get_status/main.go) | [view](examples/node/get_status.ts) | [view](examples/rust/src/bin/get_status.rs) |
|
|
131
|
+
| `stream_status` | Real-time status streaming | [view](examples/python/stream_status.py) | [view](examples/go/cmd/stream_status/main.go) | [view](examples/node/stream_status.ts) | [view](examples/rust/src/bin/stream_status.rs) |
|
|
132
|
+
| `jog_axis` | Jog axes with keyboard | [view](examples/python/jog_axis.py) | [view](examples/go/cmd/jog_axis/main.go) | [view](examples/node/jog_axis.ts) | [view](examples/rust/src/bin/jog_axis.rs) |
|
|
133
|
+
| `mdi_command` | Execute G-code via MDI | [view](examples/python/mdi_command.py) | [view](examples/go/cmd/mdi_command/main.go) | [view](examples/node/mdi_command.ts) | [view](examples/rust/src/bin/mdi_command.rs) |
|
|
134
|
+
| `hal_query` | Query HAL pins/signals | [view](examples/python/hal_query.py) | [view](examples/go/cmd/hal_query/main.go) | [view](examples/node/hal_query.ts) | [view](examples/rust/src/bin/hal_query.rs) |
|
|
135
|
+
| `upload_file` | Upload, list, delete G-code files | [view](examples/python/upload_file.py) | [view](examples/go/cmd/upload_file/main.go) | [view](examples/node/upload_file.ts) | [view](examples/rust/src/bin/upload_file.rs) |
|
|
136
|
+
|
|
137
|
+
See [examples/README.md](examples/README.md) for setup instructions.
|
|
138
|
+
|
|
139
|
+
## Services
|
|
140
|
+
|
|
141
|
+
- **LinuxCNCService** - Machine control: status, jogging, MDI, program execution, file management
|
|
142
|
+
- **HalService** - HAL introspection: query pins, signals, parameters (read-only)
|
|
143
|
+
|
|
144
|
+
### File Management
|
|
145
|
+
|
|
146
|
+
The server provides `UploadFile`, `ListFiles`, and `DeleteFile` RPCs for remote G-code file management. Files are stored in the NC files directory (default: `/home/linuxcnc/linuxcnc/nc_files`).
|
|
147
|
+
|
|
148
|
+
Configure the directory with `--nc-files` or the `LINUXCNC_NC_FILES` environment variable:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
linuxcnc-grpc --host 0.0.0.0 --nc-files /path/to/nc_files
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Safety Warning
|
|
155
|
+
|
|
156
|
+
This server provides remote control of CNC machinery. Ensure proper safety measures:
|
|
157
|
+
|
|
158
|
+
- Use only on trusted networks
|
|
159
|
+
- Implement authentication in production (gRPC supports TLS/mTLS)
|
|
160
|
+
- Never leave machines unattended during remote operation
|
|
161
|
+
- Verify E-stop and safety systems are functional
|
|
162
|
+
|
|
163
|
+
## Production Deployment
|
|
164
|
+
|
|
165
|
+
For production use, enable TLS authentication:
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
# Server with TLS
|
|
169
|
+
credentials = grpc.ssl_server_credentials([(private_key, certificate)])
|
|
170
|
+
server.add_secure_port('[::]:50051', credentials)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
# Client with TLS
|
|
175
|
+
credentials = grpc.ssl_channel_credentials(root_certificates)
|
|
176
|
+
channel = grpc.secure_channel('your-machine:50051', credentials)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
See [Server Configuration](docs/server.md#security-considerations) for complete TLS setup instructions.
|
|
180
|
+
|
|
181
|
+
## Development
|
|
182
|
+
|
|
183
|
+
Requires [uv](https://docs.astral.sh/uv/) for Python dependency management:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Install dev dependencies
|
|
187
|
+
make setup
|
|
188
|
+
|
|
189
|
+
# Run tests
|
|
190
|
+
make test # Python tests
|
|
191
|
+
make test-all # All languages
|
|
192
|
+
|
|
193
|
+
# Generate proto code
|
|
194
|
+
make proto-all # Regenerate for all languages
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
See [CLAUDE.md](CLAUDE.md) for detailed development documentation.
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: linuxcnc-grpc
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: gRPC server exposing LinuxCNC machine control and HAL functionality
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/dougcalobrisi/linuxcnc-grpc
|
|
7
|
+
Keywords: linuxcnc,cnc,grpc,machine-control,hal
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Manufacturing
|
|
11
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: grpcio>=1.60.0
|
|
23
|
+
Requires-Dist: grpcio-health-checking>=1.60.0
|
|
24
|
+
Requires-Dist: protobuf>=4.25.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: grpcio-tools>=1.60.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
29
|
+
Provides-Extra: build
|
|
30
|
+
Requires-Dist: build>=1.0.0; extra == "build"
|
|
31
|
+
Requires-Dist: twine>=5.0.0; extra == "build"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# linuxcnc-grpc
|
|
35
|
+
|
|
36
|
+
gRPC interface for LinuxCNC machine control and HAL (Hardware Abstraction Layer).
|
|
37
|
+
|
|
38
|
+
## Why gRPC?
|
|
39
|
+
|
|
40
|
+
LinuxCNC's native Python API only works locally. This project exposes it over gRPC, enabling:
|
|
41
|
+
|
|
42
|
+
- **Remote monitoring** - Build web dashboards, mobile apps, or desktop GUIs
|
|
43
|
+
- **Multi-machine management** - Monitor a fleet of CNC machines from one place
|
|
44
|
+
- **Any-language integration** - Use Go, Node.js, Rust, or any gRPC-supported language
|
|
45
|
+
- **Real-time streaming** - Subscribe to status updates instead of polling
|
|
46
|
+
|
|
47
|
+
## Running the Server
|
|
48
|
+
|
|
49
|
+
The server runs on your LinuxCNC machine and exposes the gRPC interface.
|
|
50
|
+
|
|
51
|
+
### Basic Usage
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install linuxcnc-grpc
|
|
55
|
+
# or with uv
|
|
56
|
+
uv pip install linuxcnc-grpc
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
linuxcnc-grpc --host 0.0.0.0 --port 50051
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
LinuxCNC must already be running before starting the server.
|
|
64
|
+
|
|
65
|
+
### Auto-start with LinuxCNC
|
|
66
|
+
|
|
67
|
+
To start the gRPC server automatically when LinuxCNC launches, add to your machine's HAL file:
|
|
68
|
+
|
|
69
|
+
```hal
|
|
70
|
+
# Start gRPC server (runs until LinuxCNC exits)
|
|
71
|
+
loadusr -W linuxcnc-grpc --host 0.0.0.0 --port 50051
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Or use a dedicated HAL file via your INI:
|
|
75
|
+
|
|
76
|
+
```ini
|
|
77
|
+
[HAL]
|
|
78
|
+
POSTGUI_HALFILE = grpc-server.hal
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The `-W` flag tells LinuxCNC to wait for the server to become ready before continuing.
|
|
82
|
+
|
|
83
|
+
## Quick Start
|
|
84
|
+
|
|
85
|
+
### Python
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install linuxcnc-grpc
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import grpc
|
|
93
|
+
from linuxcnc_pb import linuxcnc_pb2, linuxcnc_pb2_grpc
|
|
94
|
+
|
|
95
|
+
channel = grpc.insecure_channel("localhost:50051")
|
|
96
|
+
stub = linuxcnc_pb2_grpc.LinuxCNCServiceStub(channel)
|
|
97
|
+
|
|
98
|
+
status = stub.GetStatus(linuxcnc_pb2.GetStatusRequest())
|
|
99
|
+
print(f"Position: X={status.position.x:.3f} Y={status.position.y:.3f} Z={status.position.z:.3f}")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Go
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
go get github.com/dougcalobrisi/linuxcnc-grpc
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```go
|
|
109
|
+
import (
|
|
110
|
+
pb "github.com/dougcalobrisi/linuxcnc-grpc/packages/go"
|
|
111
|
+
"google.golang.org/grpc"
|
|
112
|
+
"google.golang.org/grpc/credentials/insecure"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
conn, _ := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
116
|
+
client := pb.NewLinuxCNCServiceClient(conn)
|
|
117
|
+
|
|
118
|
+
status, _ := client.GetStatus(context.Background(), &pb.GetStatusRequest{})
|
|
119
|
+
fmt.Printf("Position: X=%.3f Y=%.3f Z=%.3f\n", status.Position.X, status.Position.Y, status.Position.Z)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Node.js / TypeScript
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npm install linuxcnc-grpc
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import * as grpc from "@grpc/grpc-js";
|
|
130
|
+
import { LinuxCNCServiceClient, GetStatusRequest } from "linuxcnc-grpc";
|
|
131
|
+
|
|
132
|
+
const client = new LinuxCNCServiceClient("localhost:50051", grpc.credentials.createInsecure());
|
|
133
|
+
|
|
134
|
+
client.getStatus(GetStatusRequest.create(), (err, status) => {
|
|
135
|
+
console.log(`Position: X=${status.position.x.toFixed(3)} Y=${status.position.y.toFixed(3)}`);
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Rust
|
|
140
|
+
|
|
141
|
+
```toml
|
|
142
|
+
[dependencies]
|
|
143
|
+
linuxcnc-grpc = "0.5"
|
|
144
|
+
tokio = { version = "1", features = ["full"] }
|
|
145
|
+
tonic = "0.12"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```rust
|
|
149
|
+
use linuxcnc_grpc::linuxcnc::linux_cnc_service_client::LinuxCncServiceClient;
|
|
150
|
+
use linuxcnc_grpc::linuxcnc::GetStatusRequest;
|
|
151
|
+
|
|
152
|
+
let mut client = LinuxCncServiceClient::connect("http://localhost:50051").await?;
|
|
153
|
+
let status = client.get_status(GetStatusRequest {}).await?.into_inner();
|
|
154
|
+
println!("Position: X={:.3} Y={:.3}", status.position.unwrap().x, status.position.unwrap().y);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Examples
|
|
158
|
+
|
|
159
|
+
Complete examples for all supported languages:
|
|
160
|
+
|
|
161
|
+
| Example | Description | Python | Go | Node.js | Rust |
|
|
162
|
+
|---------|-------------|--------|-----|---------|------|
|
|
163
|
+
| `get_status` | Poll machine status | [view](examples/python/get_status.py) | [view](examples/go/cmd/get_status/main.go) | [view](examples/node/get_status.ts) | [view](examples/rust/src/bin/get_status.rs) |
|
|
164
|
+
| `stream_status` | Real-time status streaming | [view](examples/python/stream_status.py) | [view](examples/go/cmd/stream_status/main.go) | [view](examples/node/stream_status.ts) | [view](examples/rust/src/bin/stream_status.rs) |
|
|
165
|
+
| `jog_axis` | Jog axes with keyboard | [view](examples/python/jog_axis.py) | [view](examples/go/cmd/jog_axis/main.go) | [view](examples/node/jog_axis.ts) | [view](examples/rust/src/bin/jog_axis.rs) |
|
|
166
|
+
| `mdi_command` | Execute G-code via MDI | [view](examples/python/mdi_command.py) | [view](examples/go/cmd/mdi_command/main.go) | [view](examples/node/mdi_command.ts) | [view](examples/rust/src/bin/mdi_command.rs) |
|
|
167
|
+
| `hal_query` | Query HAL pins/signals | [view](examples/python/hal_query.py) | [view](examples/go/cmd/hal_query/main.go) | [view](examples/node/hal_query.ts) | [view](examples/rust/src/bin/hal_query.rs) |
|
|
168
|
+
| `upload_file` | Upload, list, delete G-code files | [view](examples/python/upload_file.py) | [view](examples/go/cmd/upload_file/main.go) | [view](examples/node/upload_file.ts) | [view](examples/rust/src/bin/upload_file.rs) |
|
|
169
|
+
|
|
170
|
+
See [examples/README.md](examples/README.md) for setup instructions.
|
|
171
|
+
|
|
172
|
+
## Services
|
|
173
|
+
|
|
174
|
+
- **LinuxCNCService** - Machine control: status, jogging, MDI, program execution, file management
|
|
175
|
+
- **HalService** - HAL introspection: query pins, signals, parameters (read-only)
|
|
176
|
+
|
|
177
|
+
### File Management
|
|
178
|
+
|
|
179
|
+
The server provides `UploadFile`, `ListFiles`, and `DeleteFile` RPCs for remote G-code file management. Files are stored in the NC files directory (default: `/home/linuxcnc/linuxcnc/nc_files`).
|
|
180
|
+
|
|
181
|
+
Configure the directory with `--nc-files` or the `LINUXCNC_NC_FILES` environment variable:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
linuxcnc-grpc --host 0.0.0.0 --nc-files /path/to/nc_files
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Safety Warning
|
|
188
|
+
|
|
189
|
+
This server provides remote control of CNC machinery. Ensure proper safety measures:
|
|
190
|
+
|
|
191
|
+
- Use only on trusted networks
|
|
192
|
+
- Implement authentication in production (gRPC supports TLS/mTLS)
|
|
193
|
+
- Never leave machines unattended during remote operation
|
|
194
|
+
- Verify E-stop and safety systems are functional
|
|
195
|
+
|
|
196
|
+
## Production Deployment
|
|
197
|
+
|
|
198
|
+
For production use, enable TLS authentication:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
# Server with TLS
|
|
202
|
+
credentials = grpc.ssl_server_credentials([(private_key, certificate)])
|
|
203
|
+
server.add_secure_port('[::]:50051', credentials)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
# Client with TLS
|
|
208
|
+
credentials = grpc.ssl_channel_credentials(root_certificates)
|
|
209
|
+
channel = grpc.secure_channel('your-machine:50051', credentials)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
See [Server Configuration](docs/server.md#security-considerations) for complete TLS setup instructions.
|
|
213
|
+
|
|
214
|
+
## Development
|
|
215
|
+
|
|
216
|
+
Requires [uv](https://docs.astral.sh/uv/) for Python dependency management:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Install dev dependencies
|
|
220
|
+
make setup
|
|
221
|
+
|
|
222
|
+
# Run tests
|
|
223
|
+
make test # Python tests
|
|
224
|
+
make test-all # All languages
|
|
225
|
+
|
|
226
|
+
# Generate proto code
|
|
227
|
+
make proto-all # Regenerate for all languages
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
See [CLAUDE.md](CLAUDE.md) for detailed development documentation.
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
[MIT](LICENSE)
|