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.
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.
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.
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.
Subpackets are of varying length and data types. There are some reused sizes and packing / types.
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 |
This needs to be done to create the connection with the FOCAS server.
| 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) |
| 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.
| 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 |
| 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 |
| 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 |
| 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 |
| 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).
| 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) |
Writes a macro variable with a double value (not scaled integer)
| 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.
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).
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 --tagsOpen 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 createto create the release.