SCADA / Operations
Plant Floor Visibility Platform
Real-time operational visibility presented to the North America plant manager network.
01
Problem
During live production, teams kept asking which machines were down, which lines were impacted, and where on the floor. The answers were buried in scrolling L2L dispatch lists that required logging in and navigating. Every clarification cost response time.
02
Approach
I built a spatial status layer in Ignition Perspective. Machines sit where they physically are on the plant layout, colored by their most severe open L2L dispatch: red for down, yellow for impacted, and green for no impacting dispatches. All / Maint / IT filters scope by dispatch trade; area buttons jump to each production area, and the map supports zoom and pan. Double-clicking an asset opens that machine in L2L. It is designed to stay open on shared floor displays: a constant visual aid, not a report.
Plant floor at a glance
03
Architecture
L2L remains the system of record. Once per minute, a single Ignition gateway REST API call reads all open dispatches for the site, resolves the most severe status for each machine by machine code, and refreshes the Perspective floor map. Double-clicking an asset opens that machine directly in L2L.
04
Implementation
I derive status from L2L dispatches: one gateway call refreshes the whole floor, keeping Perspective clients lightweight. Authenticated editing mode lets users move, add, and adjust assets live without a redeploy or Designer work; an L2L custom property defines each asset's map shape. The responsive layout works from an iPad mini to a 75-inch floor TV and in the Ignition Perspective mobile app. This is a visual awareness layer: it does not replace L2L dashboards, control machines, perform analytics, or act as a CMMS. The template needs a new background layout and the site's L2L code and dispatch types; it is already in use at another plant, and other sites have asked for it.
SEVERITY = {"none": 0, "impacted": 1, "down": 2}
def worst_status_by_machine(open_dispatches, machine_codes):
status_by_machine = dict((code, "none") for code in machine_codes)
for dispatch in open_dispatches:
machine_code = dispatch.get("machine_code")
severity = dispatch.get("severity")
if machine_code in status_by_machine and severity in ("down", "impacted"):
current = status_by_machine[machine_code]
if SEVERITY[severity] > SEVERITY[current]:
status_by_machine[machine_code] = severity
return status_by_machine
def cell_style(severity):
return {
"down": {"fill": "#ff0000", "label": "Machine/Equipment Down"},
"impacted": {"fill": "#ffff00", "label": "Machine/Equipment Impacted"},
"none": {"fill": "#008000", "label": "No impacting dispatches"},
}[severity]
05
Results
06
Reflection
What I’d do next
Next, I would explore an event-driven push from L2L instead of polling, and evaluate the map as a native L2L visualization so sites without Ignition could use it.