Slotting 1.0.0

Put-to-light

With put-to-light, every bin has a light (and usually a button) that shows the operator where to put: the bin lights up after a scan, often with the quantity on a small display, and the operator presses its button when the unit is in. The install drives the lights of any vendor through one of two neutral interfaces, an HTTP gateway or an MQTT broker, and has a simulator for trying it out without hardware.

The lights are an addition, never a dependency: a scan never waits for them and never fails because of them. If the hardware is offline, scanning goes on, and the lights catch up when it is back.

Choose a driver#

SLOTTING_LIGHTS
none (default) No hardware. The light state can still be read (below)
http POST the changes to your light gateway: HTTP gateway
mqtt Publish the changes to an MQTT broker, one topic per bin: MQTT
simulator No hardware: the wall drawing shows the lights and its bins work as buttons: Simulator

SLOTTING_LIGHTS_RESYNC_MS (default 60000, 1000 to 86400000) is how often the install sends the whole state again (below). The other settings are per driver.

The light state#

The install doesn't send "light on" and "light off" commands that your hardware has to keep track of. It works out, from the state of the waves, how every bin's light should look, and sends what changed. Each bin is in one of three states:

State When Shown as (your choice)
put Put here: the bin of a pending put (waves with confirmed puts), or, without confirmation, the bins of each operator's latest scan, until that operator's next scan in the wave Lit, often blinking, with quantity on the display
complete The order in this bin is complete: pack it out Lit in another colour, steady
off Anything else (free bins, orders still being filled, released orders, closed waves) Dark

The state belongs to the layout, across all its waves. Read it any time, whatever the driver:

bash
curl -s "$SLOTTING_URL/api/layouts/PUTWALL-A/lights"
json
[
  { "binId": "A-01-01", "wallId": "A", "address": "A-01-01", "state": "put", "waveId": "WAVE-CURL-2", "orderId": "ORD-2001", "quantity": 1, "sku": "MUG" }
]

Bins that are not listed are off. address is the bin's light address.

How the install keeps the lights right#

With several instances, each one sends the changes it makes, and every instance resends the whole state periodically, so the hardware ends up with the same state whichever instance it heard last.

HTTP gateway#

For a light controller with an HTTP interface, or a small service of yours that talks to the vendor's controller.

Variable
SLOTTING_LIGHTS_URL Required: the gateway's http:// or https:// URL. No user name or password in the URL
SLOTTING_LIGHTS_TOKEN Optional: sent as Authorization: Bearer <token> (1 to 4096 visible characters)

The install sends a POST with Content-Type: application/json per layout and change:

json
{
  "layoutId": "PUTWALL-A",
  "changes": [
    { "address": "A-01-01", "binId": "A-01-01", "wallId": "A", "state": "put", "quantity": 2, "sku": "MUG" },
    { "address": "A-01-02", "binId": "A-01-02", "wallId": "A", "state": "off" }
  ]
}
Field
layoutId The layout the bins belong to
changes[] Bins whose light must change: address, binId, wallId, state (put, complete or off), and for put the quantity and sku to show
all Only in a resync: the layout's whole light state, as in GET /api/layouts/{layoutId}/lights. A gateway that prefers to replace its state can use it; changes then holds every lit bin and the bins to turn off

Answer 2xx within 2 seconds. Any other answer, a timeout or a redirect (not followed) counts as failed, and the layout is sent as a whole state with the next change and at the next resync.

When an operator presses a bin's button, the gateway calls the press route of the install.

MQTT#

For lights that speak MQTT, or a gateway that bridges MQTT to the vendor's bus.

Variable
SLOTTING_LIGHTS_MQTT_URL Required: mqtt://broker:1883, or mqtts://broker:8883 for TLS. A user name and password may be in the URL
SLOTTING_LIGHTS_MQTT_USERNAME, SLOTTING_LIGHTS_MQTT_PASSWORD The broker login, if it isn't in the URL (these win)
SLOTTING_LIGHTS_MQTT_CA_FILE A PEM file with the CA certificate of your broker (private CA). Needs an mqtts:// URL. It replaces the system's CAs for the broker connection, so a broker with a public certificate needs no CA file
SLOTTING_LIGHTS_MQTT_PREFIX The first topic levels, default slotting (letters, digits, ., _, -, levels separated by /)

The install publishes each bin's state retained, with QoS 1, to

text
<prefix>/<layoutId>/<address>/set

with the payload

json
{ "state": "put", "quantity": 2, "sku": "MUG", "binId": "A-01-01" }

state is put, complete or off (off and complete without quantity and sku). Because the messages are retained, a module that restarts or reconnects gets its bin's current state from the broker at once. In a topic, the layout id and the address keep letters, digits and . _ : -; any other character is percent-encoded (UTF-8), so a light address A/01 is the single topic level A%2F01.

Button presses: the install subscribes to <prefix>/+/+/press (QoS 1). A module (or gateway) publishes any payload (up to 1 KiB; it is not read) to

text
<prefix>/<layoutId>/<address>/press

not retained: retained press messages are ignored, so a stale press can't confirm a put when the install reconnects. A press is handled exactly like the press route; a press with nothing to do is logged and ignored. While the install has no valid license, MQTT presses are ignored (with a warning in the log), just as the press route answers 402.

The install connects with a random client id and a clean session, reconnects every 5 seconds when the broker is gone, and logs the connection state (never the password). On every reconnect it sends the whole state of every layout (a broker that restarted without persistence has lost the retained states), so changes made while the broker was offline arrive once it is back.

Restrict who may press. Any client that can publish to <prefix>/<layoutId>/<address>/press confirms puts and releases orders: the install can't tell a module from any other MQTT client. Configure the broker's ACLs so only your light modules or gateways may publish to <prefix>/+/+/press, only the install may publish to <prefix>/+/+/set, and nobody else may write under the prefix (for example Mosquitto acl_file with a user per gateway, topic write slotting/+/+/press; EMQX or HiveMQ authorization rules). Give the install its own broker user with exactly that: subscribe <prefix>/+/+/press, publish <prefix>/+/+/set.

Button presses#

bash
curl -s -X POST "$SLOTTING_URL/api/lights/press" -H 'Content-Type: application/json' \
     -d '{ "layoutId": "PUTWALL-A", "address": "A-01-01" }'

POST /api/lights/press with the layout and the light's address (its light address, else the bin id). The bin decides what the press means:

The bin The press Answer
Has a pending put (confirmed puts) Confirms the bin's oldest pending put, like a scan of the bin label { "action": "confirmed", "result": ConfirmResult }
Holds a complete order Releases the order: packed out, all its bins are free { "action": "released", "orderIds": [...], "bins": [...] }
Just confirmed by a press, or its order completed, less than SLOTTING_PRESS_RELEASE_GUARD_MS ago Nothing changes: the press repeats the one before (a double press, a bouncing button, a redelivered MQTT message) 200 { "action": "ignored", "reason": "duplicate" } (a press confirmed a put in this bin) or "too_soon" (the order just completed)
Anything else (a free bin, an order still being filled, an unknown address) Nothing changes 409 not_pending

So one button does both jobs of a put-to-light wall: press after putting (with confirmed puts), and press after emptying the bin at pack-out. A complete order lights all its bins complete; pressing any of them releases the whole order.

Because the same button confirms and releases, a press right after another one of the bin is ignored for SLOTTING_PRESS_RELEASE_GUARD_MS (default 3000, 0 to 600000; 0 turns the guard off): the last put's press must not also pack out the order, and a press the broker delivers twice must not confirm the next put. An ignored press is not an error (200); press again after the pause. An invalid value falls back to the default.

json
{"action":"released","orderIds":["ORD-2001"],"bins":[{"id":"A-01-01","wallId":"A"}]}

The press route is a change like any other: it needs a license (402 otherwise), and it refuses requests from browser pages on other origins (Network and security); a gateway calling from a server sends no Origin and is not affected. It works with every SLOTTING_LIGHTS setting, so an HTTP gateway can report presses while the install drives the lights over MQTT, or the other way round.

The simulator#

SLOTTING_LIGHTS=simulator needs no hardware. On the admin page (/), the Station tab has a Light simulator switch that shows every wall of the station's layout with its lights: put bins pulse with the quantity, complete bins are steady, and clicking a lit bin presses its button. Scan at the station, click the bin, and watch the put being confirmed or the order being released.

The same in your own pages: <slotting-wall lights simulate-buttons> polls the light state and shows it; with simulate-buttons a click (or Enter or Space) on a lit bin presses its button. Without simulate-buttons it only shows the lights, which is useful next to real hardware too, for example on a supervisor's screen. The light state and the press route are available with every driver, so the simulator works next to real lights.

Light addresses#

A bin's light is addressed by the bin's lightAddress in the layout, or by the bin id when it has none. Give bins an address when your light modules are numbered differently from the bin labels:

json
{ "id": "A-01-01", "x": 0, "y": 30, "width": 40, "height": 30, "depth": 40, "lightAddress": "ctrl1/017" }