Appearance
Custom Page Example
Last updated
A live machine monitor that mixes shortcode widgets with a little of your own code. Replace 3 with your CNC's numeric device id (use Tag Snippet to find it) and adjust the tag paths. The table part needs a Data Table named production_log with columns machine, part_count and cycle_time.
HTML
html
<div class="container">
<h1>CNC-001 Monitor</h1>
<div class="grid">
<div class="card">
<scada-widget widget="gauge" title="Spindle Speed" min="0" max="12000" unit="RPM"
bind-value="tag:3/spindle_speed"></scada-widget>
</div>
<div class="card">
<scada-widget widget="value" title="Feed Rate" unit="mm/min" precision="1"
bind-value="tag:3/feed_rate"></scada-widget>
</div>
<div class="card">
<label>Part Count</label>
<span id="parts">—</span>
</div>
<div class="card">
<label>Status</label>
<span id="status">—</span>
</div>
</div>
<h2>Production Log</h2>
<p id="log-error" class="error"></p>
<table id="log-table">
<thead><tr><th>ID</th><th>Machine</th><th>Part #</th><th>Cycle Time</th></tr></thead>
<tbody></tbody>
</table>
</div>CSS
css
body { background: #f5f7fa; color: #1f2937; }
.container { max-width: 960px; margin: 0 auto; padding: 20px; }
h1 { font-size: 22px; margin-bottom: 16px; }
h2 { font-size: 16px; margin: 24px 0 8px; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }
.card { background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px; min-height: 180px; }
.card label { display: block; font-size: 12px; color: #6b7280; text-transform: uppercase; margin-bottom: 8px; }
.card > span { display: block; font-size: 32px; font-weight: 700; }
table { width: 100%; border-collapse: collapse; background: #fff; }
th, td { padding: 8px 12px; text-align: left; border-bottom: 1px solid #e5e7eb; font-size: 13px; }
th { color: #6b7280; font-size: 11px; text-transform: uppercase; }
.error { color: #dc2626; font-size: 13px; }JavaScript
javascript
var DEVICE_ID = 3; // numeric id, not the device name
scadaink.subscribe(DEVICE_ID, 'part_count', function (v) {
document.getElementById('parts').textContent = v;
});
// MTConnect's execution state: ACTIVE, READY, FEED_HOLD, INTERRUPTED, STOPPED …
scadaink.subscribe(DEVICE_ID, 'execution', function (v) {
var el = document.getElementById('status');
el.textContent = v;
el.style.color = v === 'ACTIVE' ? '#16a34a' : v === 'STOPPED' ? '#dc2626' : '#ca8a04';
});
function loadLog() {
scadaink.db.query('production_log', { limit: 20, sortBy: 'id', sortDir: 'desc' })
.then(function (rows) {
var tbody = document.querySelector('#log-table tbody');
tbody.textContent = '';
rows.forEach(function (row) {
var tr = document.createElement('tr');
[row.id, row.machine, row.part_count, row.cycle_time + ' s'].forEach(function (text) {
var td = document.createElement('td');
td.textContent = text == null ? '' : text;
tr.appendChild(td);
});
tbody.appendChild(tr);
});
})
.catch(function (err) {
document.getElementById('log-error').textContent = 'Could not load the log: ' + err.message;
});
}
loadLog();
setInterval(loadLog, 30000); // the browser has setInterval, unlike server scriptsNotes
- The gauge and value tiles are shortcodes, so they update themselves; only the part count and status use
subscribe. - Table cells are filled with
textContent, notinnerHTML, so a value containing<can't inject markup. - To add a logo, upload it in the editor's Page files panel and use Insert at the cursor in the HTML tab; it adds an
<img>with the file's address. - As a Public page without a login, the gauge, feed rate and part count/status still update only if those tags are published — the two shortcode tags are,
part_countandexecutionare not unless another shortcode or public dashboard names them — and the Production Log fails to load, becausedb.queryneeds a login. See Custom Page Security.