---
title: CNC Machine Shop
slug: solutions/cnc-machine-shop
docTags: 
createdAt: 2026-10-01T23:15:48.220Z
---

A ten-machine CNC job shop read by one Litmus Edge over each controller's own protocol. Three controller families, five DeviceHub drivers, 300 tags, and no gateway in between.

| Operator view       | port **4025**                                                                                             |
| ------------------- | --------------------------------------------------------------------------------------------------------- |
| Protocol servers    | ports **5135** to **5144**, one per controller                                                            |
| Devices and tags    | 10 machines, 300 tags                                                                                     |
| Litmus Edge drivers | Siemens S7, Siemens S7-200 Ethernet, Fanuc CNC Ethernet, Heidenhain 640 Ethernet, Heidenhain 530 Ethernet |
| Image               | `cnc-machine-shop-demo`                                                                                   |

| Cell          | Machines                       | Controller                                   | Protocol       |
| ------------- | ------------------------------ | -------------------------------------------- | -------------- |
| Prismatic     | HMC 01, VMC 01, VMC 02, HMC 02 | SINUMERIK 840D sl, 828D, FANUC 30i-B, 15i-MB | S7comm, FOCAS2 |
| Five-axis     | 5X 01                          | SINUMERIK 840D sl                            | S7comm         |
| Turning       | MT 01, Lathe 01, Lathe 02      | SINUMERIK 840D sl, 828D, FANUC 30i-B         | S7comm, FOCAS2 |
| Mould and die | Mould 01, Mould 02             | HEIDENHAIN TNC 640, iTNC 530                 | LSV2           |

## Scenarios

Five scenarios, each injected on one machine, with the rest of the shop running normally beside it.

- **A lights-out stop nobody sees until morning.** HMC 01 runs its pallet pool overnight with nobody in the building. Coolant runs low, the controller raises a PLC alarm and stops with pallets still queued, and the next person to know is the first shift. The Edge prices every unattended hour it stands at the machine's hour rate. During staffed hours the fault clears itself after about twenty shop minutes, because somebody walks past and tops up the tank; it is the lights-out hours that cost money.
- **The feed turned down instead of a tool change.** The face mill on VMC 01 wears, spindle load climbs, and the operator turns the feed override down to 70 percent rather than stopping. Nothing alarms and the machine stays green, but every part takes longer. The Edge computes the dollars an hour lost to running slow.
- **A broken end mill cutting air.** The 12 mm end mill on 5X 01 snaps partway through roughing an impeller. The program keeps running, every later pass with that tool cuts air, and the finishing tools that follow meet stock that should already be gone. The Edge sees a spindle at speed, a program running and a load that belongs to no cut.
- **Way lube read off the Y drive load.** The way lube pump on MT 01 is failing. The Y axis drive load creeps up against X and Z hour by hour, long before the controller's contour monitoring alarms, and the Edge's trend is the warning.
- **Tool life thrown away on a counter.** VMC 02's 316L housing program was slowed to 70 percent of handbook cutting speed years ago and never revisited. Every edge now lasts far longer than planned, but the life counter still counts nominal minutes, so face mill inserts, end mills and drills are changed with most of their life left. The Edge recomputes the cutting speed and prices the tool policy.

## How it differs from aerospace machining

[Aerospace machining](#) reads its shop over **MTConnect**: one agent, one driver, tag names from the standard. That assumes the shop already runs an MTConnect agent or adapter in front of every machine.

This solution reads the controllers **natively**, the way most job shops actually have them: Sinumerik over S7comm, FANUC over FOCAS2 and Heidenhain over LSV2, on five different DeviceHub drivers, each controller on its own port. Each controller family speaks its own vocabulary (Sinumerik interface bits, the FANUC status structure, Heidenhain run-info strings), and the solution normalises all three into one view of the shop. A twenty-year-old FANUC 15i is read the same way as a 30i-B. It is the only solution in the set that runs three controller families at once.

## Computed on the Edge

Tool wear and cutting data are computed **on Litmus Edge**, not in the solution. The **Cutting Data** group carries each machine's tool table, the real tools with their diameter, teeth and lead angle and the handbook cutting data for the material they cut. From spindle speed, feed, load and tool number it works out the actual cutting speed and feed per tooth, chip thickness, removal rate, the load a sharp edge would draw, and from the gap between that and the measured load, the wear, the life left and a verdict.

It also says what it will not claim. A light finishing pass draws almost nothing over the spindle's own drag, so the Edge reports that load is not a wear signal rather than invent a verdict, and it judges nothing in the moment after a tool change while speed, feed and tool number still belong to two different tools.

## What it creates on Litmus Edge

Ten DeviceHub devices across five drivers, 300 tags. Six Digital Twin models, one per controller type (`Sinumerik840D`, `Sinumerik828D`, `FanucSeries30i`, `FanucSeries15i`, `HeidenhainTNC640`, `HeidenhainITNC530`), with ten instances. Six Analytics groups, 488 processors:

| Group               | What it computes, on the Edge                                                                               |
| ------------------- | ----------------------------------------------------------------------------------------------------------- |
| Unattended Stop     | a lights-out machine stopped with work queued, and the hour rate standing                                   |
| Feed Override Cost  | feed turned down while running, and the dollars an hour lost                                                |
| Cutting Data        | actual cutting speed and feed, the load a sharp edge would draw, wear, life left and a verdict, per machine |
| Axis Load Trend     | Y axis drive load against X and Z                                                                           |
| Spindle Utilisation | cutting time against power-on time                                                                          |
| Live Values         | the last value of every tag, which the operator view reads back                                             |

The apply is sent in three parts, DeviceHub, then Digital Twin, then Analytics, so the devices land and poll even if a later part does not complete. The solution then reports a partial apply rather than a failure.

## Requirements

- Litmus Edge 4.0.x, reachable over HTTPS from the host running the solution
- An API token for that Edge, or a username and password, or a client id and secret
- Docker and Docker Compose
- A network path in both directions: the solution reaches the Edge's API, and the Edge reaches this host on ports 5135 to 5144

## Deploy

The solution is deployed with a compose file. It publishes eleven ports, the operator view and one per controller.

### Option 1: pull from the registry

Requires a read-only registry credential, supplied by Litmus separately.

```bash
cat key.json | docker login -u _json_key --password-stdin https://us-docker.pkg.dev
```

Save this as `docker-compose.yml`:

```yaml
services:
  cnc-machine-shop:
    image: us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/cnc-machine-shop-demo:0.5.0
    container_name: cnc-machine-shop-demo
    restart: unless-stopped
    ports:
      - "4025:4025"
      - "5135:5135"
      - "5136:5136"
      - "5137:5137"
      - "5138:5138"
      - "5139:5139"
      - "5140:5140"
      - "5141:5141"
      - "5142:5142"
      - "5143:5143"
      - "5144:5144"
    environment:
      # Fill these in.
      EDGE_URL: "${EDGE_URL:-}"
      EDGE_API_TOKEN: "${EDGE_API_TOKEN:-}"
      SIM_HOST: "${SIM_HOST:-auto}"
      # Or use one of the other Litmus Edge auth schemes instead of a token.
      EDGE_CLIENT_ID: "${EDGE_CLIENT_ID:-}"
      EDGE_CLIENT_SECRET: "${EDGE_CLIENT_SECRET:-}"
      EDGE_USERNAME: "${EDGE_USERNAME:-}"
      EDGE_PASSWORD: "${EDGE_PASSWORD:-}"
      APPLY_ON_START: "${APPLY_ON_START:-1}"
      EDGE_VERIFY_TLS: "${EDGE_VERIFY_TLS:-0}"
      HTTP_PORT: "4025"
      CNC_PORT_BASE: "5135"
      LOG_LEVEL: "INFO"
```

```javascript
EDGE_URL=https://10.0.0.5
EDGE_API_TOKEN=your-token
SIM_HOST=10.0.0.9
```

```bash
docker compose up -d
```

Open `http://localhost:4025`.

### Option 2: load the downloaded archive

Download `cnc-machine-shop-demo-0.5.0-amd64.tar.gz` from the solution's page on portal.litmus.io. The archive is the container image, not a source bundle, and needs no registry access.

```bash
docker load -i cnc-machine-shop-demo-0.5.0-amd64.tar.gz
```

`docker load` prints the image it added:

```javascript
Loaded image: us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/cnc-machine-shop-demo:0.5.0
```

That reference is the one the compose file above already names, so the same `docker-compose.yml` and `.env` now work offline with no `docker login`:

```bash
docker compose up -d
```

## Configuration

| Variable          | Default | What it does                                                               |
| ----------------- | ------- | -------------------------------------------------------------------------- |
| `EDGE_URL`        | unset   | Litmus Edge to configure, for example `https://10.0.0.5`                   |
| `EDGE_API_TOKEN`  | unset   | token for that Edge. Or a username and password, or a client id and secret |
| `SIM_HOST`        | `auto`  | address the Edge polls to reach this solution                              |
| `APPLY_ON_START`  | `1`     | configure the Edge on startup                                              |
| `EDGE_VERIFY_TLS` | `0`     | `1` checks the Edge's certificate                                          |
| `CNC_PORT_BASE`   | `5135`  | first controller port; the ten controllers take it and the nine after it   |

## Operator view

Across the top: machines cutting now and the shop's cutting share of power-on time, machines down and why, the feed-override cost and the unattended standing cost an hour, the value of tool life thrown away, and tags read by the Edge out of 300.

Two views of the same shop, switched in the header and remembered per browser:

- **Floor** is an isometric shop floor: four cells on plinths, and the data path from each machine into the Edge, coloured by the Litmus Edge component it reaches (DeviceHub, Analytics, Digital Twin) and marked red where a tag is not being read.
- **Asset** draws all ten machines as mimics. Every motion is an Edge reading: the table slides or the trunnion tilts, the spindle plunges or the turret travels, the tool or the bar spins at its rpm, load throws chips, and a broken tool shows as one.

Clicking a machine in either view opens the same panel: every register the Edge polls on it, the controller variable it mirrors, the controller's value and the value the Edge read back side by side, with its topic, and that machine's cutting data and tool wear as computed on the Edge. **Equations** in the header sets out every formula behind the cutting data with its constants.

Every tile and figure is the Edge's reading, not the simulator's. A machine the Edge has not read yet is marked as such. State changes the Edge sees go into the event log.

Faults are the five scenarios above, each with a card naming the Analytics group that catches it and the DataHub topics to watch.

## Apply and remove

The header carries both controls. **Apply to Edge** pushes the devices, twins and analytics; it greys out once the solution is on the Edge. **Remove from Edge** previews first, listing exactly what would be removed and from which Edge, and only then offers to remove it.

The solution removes only what it created. It also records which Edge its apply landed on and removes from that one. **Connections** in the header points the solution at a different Edge without a restart.

The operator view stays on its waiting panel until Litmus Edge is verified to carry all ten machines and to be polling them.

## Reports

**Export report** produces the shift report as one self-contained HTML file, built only from what the Edge read and computed: time in state per machine, the Analytics verdicts, the tool-change policy priced, and the faults that were injected. No scripts and no external requests, so it opens offline and prints.

## Stop the solution

```bash
docker compose down
```

Use **Remove from Edge** on the operator view, or Clean up in the [Solutions provisioner](#), to remove what it created on the Edge.

## Support

[support@litmus.io](mailto\:support@litmus.io)
