A reimplementation of the FOCAS protocol using UNIX sockets.
Project description
pyfocas
This project is a barebones reimplementation of the FANUC FOCAS protocol for communicating with a CNC machine.
It only uses UNIX sockets and sends the proper messages / packets instead of relying on vendor libraries.
Sources / Based On
This project relies on the reverse engineering of the FANUC FOCAS protocol by diohpix. The relevant library is pyfanuc.
This reimplementation of the protocol is currently the only way of reliably using FOCAS on a 64bit ARM (aarch64) system as no library for that architecture exists.
Protocol
The protocol is a big endian request-response protocol via UNIX sockets. A full message (either request or response) will be called packet. A packet has headers and at least one subpacket, which in turn consist of multiple blocks detailing the purpose of the packet.
Header
The header looks similar for request and response. A typical header contains the following blocks, with a block being a part of the header or subpacket:
| NAME | Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|---|
| PURPOSE | Start of each packet | Server or Client | Open, Close, Other | Length of packet incl. subpackets | Number of subpackets |
| LENGTH | 4 bytes | 2 bytes | 2 bytes | 2 bytes | 2 bytes |
Sync Prefix marks the start of a packet and is always A0 A0 A0 A0.
Packet Origin marks if the packet is a request to the FOCAS server (00 01)
or a response from it (different from 00 01).
Packet Type differentiates the packets between:
- Trying to open a connection to the FOCAS server:
- Request:
01 01 - Response:
01 02
- Request:
- Trying to close the connection:
- Request:
02 01 - Response:
02 02
- Request:
- Trying to execute a generic command:
- Request:
21 01 - Response:
21 02
- Request:
There are other packet types as can be seen in diohpix/pyfanuc which are not covered in this project (yet).
Packet Length contains the total length of bytes coming after it, including
the size of the Subpacket Count (2 bytes) and all the subpackets in bytes.
Note that this results in the
Packet Lengthalways being larger than the sum of all subpacket'sSubpacket Lengthby exactly 2 bytes (size of theSubpacket Count).
Subpacket Count holds the number of subpackets in this packet.
Subpacket
Subpackets are of varying length and data types. There are some reused sizes and packing / types.
Default Payload: 5x 4-Byte INT32
The subpackets are always of length 1c / 28 bytes. They each contain three
pieces of information about the subpacket totalling 8 bytes followed by a
payload of 20 bytes which can be split into multiple regions.
| NAME | Subpacket Length | Control Device | Function | Payload |
|---|---|---|---|---|
| PURPOSE | Length of the subpacket | CNC or PMC | Command to execute or which was executed. | Data transmittable via subpacket |
| LENGTH | 2 bytes | 2 bytes | 4 bytes | 20 bytes |
Implemented Functions
Initialize Connection
This needs to be done to create the connection with the FOCAS server.
Request
| Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|
A0 A0 A0 A0 |
00 01 |
01 01 |
00 02 (unclear reason) |
00 02 (unclear reason) |
Response
| Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|
A0 A0 A0 A0 |
00 04 |
01 02 |
01 68 (unclear reason) |
00 08 (unclear reason) |
The response contains 360 Bytes of (as of yet) unclear subpacket content.
Status Info
Request
| Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|
A0 A0 A0 A0 |
00 01 |
21 01 |
00 1E |
00 01 |
With the single subpacket being:
| Subpacket Length | Control Device | Function | Payload |
|---|---|---|---|
00 1C |
00 01 |
00 01 00 19 |
20 times 00 |
Response
| Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|
A0 A0 A0 A0 |
00 04 |
21 02 |
00 20 |
00 01 |
With the single subpacket being:
| Subpacket Length | Control Device | Function | Payload |
|---|---|---|---|
00 1E |
00 01 |
00 01 00 19 |
See _FOCAS_STATINFO_STRUCT |
System Info
Request
| Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|
A0 A0 A0 A0 |
00 01 |
21 01 |
00 1E |
00 01 |
With the single subpacket being:
| Subpacket Length | Control Device | Function | Payload |
|---|---|---|---|
00 1C |
00 01 |
00 01 00 18 |
20 times 00 |
Response
| Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|
A0 A0 A0 A0 |
00 04 |
21 02 |
00 24 |
00 01 |
With the single subpacket being:
| Subpacket Length | Control Device | Function | Payload |
|---|---|---|---|
00 22 |
00 01 |
00 01 00 18 |
See _FOCAS_SYSINFO_STRUCT |
Read Macro
Request
| Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|
A0 A0 A0 A0 |
00 01 |
21 01 |
00 1E |
00 01 |
With the single subpacket being:
| Subpacket Length | Control Device | Function | Payload |
|---|---|---|---|
00 1C |
00 01 |
00 01 00 15 |
00 00 01 F6 00 00 01 F6 + 12x 00 |
This reads macro variable #502 (01 F6).
Response
| Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|
A0 A0 A0 A0 |
00 04 |
21 02 |
00 1A |
00 01 |
With the single subpacket being:
| Subpacket Length | Control Device | Function | Payload |
|---|---|---|---|
00 18 |
00 01 |
00 01 00 15 |
00 00 00 00 (Fill) 00 00 00 10 (#Bytes coming after this) 29 7C 1E 00 00 0A 00 06 (First 8-Byte Scaled Int) 05 F5 E1 00 00 0A 00 08 (Second 8-Byte Scaled Int) |
Write Macro (Double)
Writes a macro variable with a double value (not scaled integer)
Request
| Sync Prefix | Packet Origin | Packet Type | Packet Length | Subpacket Count |
|---|---|---|---|---|
A0 A0 A0 A0 |
00 01 |
21 01 |
00 38 |
00 01 |
With the single subpacket being:
| Subpacket Length | Control Device | Function | Payload |
|---|---|---|---|
00 24 |
00 01 |
00 01 00 A8 |
00 00 01 F6 (Var. 502) 00 (12x Filling Bytes) 00 00 00 08 (#Bytes coming after this) 40 59 00 00 00 00 00 00 (Value as double) |
This writes 100.0 to #502.
Development
Release
In the steps below we assume that you
- installed
uv, and - execute commands in the root of the repository.
Please replace <VERSION> with the version number of the package that you want to release (e.g. 0.2.0).
PyPI
To release a new version on PyPI please use the commands below:
uv version <VERSION>
export pyfocas_version="$(uv version --short)"
git commit -a -m "Release: Release version $pyfocas_version"
git tag "$pyfocas_version"
git push && git push --tags
GitHub
Open the release notes for the latest version and create a new release:
- Paste the release notes into the main text of the release web page
- Insert the version number into the tag field
- For the release title use “Version ”, where
<VERSION>specifies the version number (e.g. “Version 0.2”) - Click on “Publish Release”
Note: Alternatively you can also use the gh command:
gh release create
to create the release.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyfocas-0.1.tar.gz.
File metadata
- Download URL: pyfocas-0.1.tar.gz
- Upload date:
- Size: 10.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec969298463e03c329d902a530a21a3d49a5ad46a190a5ab1c2cdaf6ac06a3ea
|
|
| MD5 |
f86c88accd5caa6e48c03f2a907a551c
|
|
| BLAKE2b-256 |
3c7cb70394d73b0838144c463413bb02f24e26ed0b5b20457b1d37466f566130
|
Provenance
The following attestation bundles were made for pyfocas-0.1.tar.gz:
Publisher:
publish.yaml on MyTooliT/pyfocas
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyfocas-0.1.tar.gz -
Subject digest:
ec969298463e03c329d902a530a21a3d49a5ad46a190a5ab1c2cdaf6ac06a3ea - Sigstore transparency entry: 667203473
- Sigstore integration time:
-
Permalink:
MyTooliT/pyfocas@7bb655af6fed2f2b97dbbe106493533cbc9683df -
Branch / Tag:
refs/tags/0.1 - Owner: https://github.com/MyTooliT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@7bb655af6fed2f2b97dbbe106493533cbc9683df -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyfocas-0.1-py3-none-any.whl.
File metadata
- Download URL: pyfocas-0.1-py3-none-any.whl
- Upload date:
- Size: 9.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1405982476c2da7e9cfb0bfb3c9299440af221932c95d94dc2e6591af9398e22
|
|
| MD5 |
a6ffa6db85546da1fc281ab5854f59f7
|
|
| BLAKE2b-256 |
ce7d9aa2b2ef009a41dd7f67f214c84a7df826541d007201fa0bb93e07f11fe0
|
Provenance
The following attestation bundles were made for pyfocas-0.1-py3-none-any.whl:
Publisher:
publish.yaml on MyTooliT/pyfocas
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyfocas-0.1-py3-none-any.whl -
Subject digest:
1405982476c2da7e9cfb0bfb3c9299440af221932c95d94dc2e6591af9398e22 - Sigstore transparency entry: 667203475
- Sigstore integration time:
-
Permalink:
MyTooliT/pyfocas@7bb655af6fed2f2b97dbbe106493533cbc9683df -
Branch / Tag:
refs/tags/0.1 - Owner: https://github.com/MyTooliT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@7bb655af6fed2f2b97dbbe106493533cbc9683df -
Trigger Event:
push
-
Statement type: