Skip to content

Script Examples ​

Last updated

Each example says which trigger to use. Replace device names, tag paths and table names with your own; Data Tables must already exist with those column names.

Log a production row on every part (Tag Change) ​

Trigger: Tag Change, Tag = the CNC's part_count (chosen with Browse). Data Table production_log with columns machine, part_count, spindle_speed, feed_rate, logged_at.

javascript
const partCount = Number(context.value);
if (!(partCount > 0)) return;   // ignore 0 / non-numeric resets

const speed = Number(tags.read('CNC-001', 'spindle_speed'));
const feed = Number(tags.read('CNC-001', 'feed_rate'));

db.insert('production_log', {
  machine: 'CNC-001',
  part_count: partCount,
  spindle_speed: speed,
  feed_rate: feed,
  logged_at: context.timestamp,
});
log('Logged part #' + partCount + ' at ' + speed + ' RPM');

Coolant alert with a latch (Tag Threshold) ​

Trigger: Tag Threshold, Operator = Greater Than, Threshold = 60, on the coolant temperature tag. Because Tag Threshold runs on every update above 60, a local tag CNC001/coolant_alert remembers that the alert was already sent. A second script (or an alarm rule) resets it when the temperature falls. (Ticking Only when it crosses, with a Hysteresis of 3, gives the same once-per-excursion behavior without the latch tag; the latch is still useful when other screens need to show the alert.)

javascript
const temp = context.value;   // a number for Tag Threshold
if (tags.read(devices.localId(), 'CNC001/coolant_alert') === 'true') return;   // already sent

tags.write('CNC001/coolant_alert', 'true');
log('Coolant temperature ' + temp + ' °C');

const reply = await http.post('https://hooks.example.com/alert', {
  machine: 'CNC-001',
  temperature: temp,
  message: 'Coolant temperature exceeded 60 °C',
});
log('Webhook replied', reply);   // throws if the webhook answers with an error status

To raise an alarm operators see on the Alarms page, add alarms.notify('warning', 'Coolant hot', 'Coolant at ' + temp + ' °C'). For an email or text as well, create an alarm rule on CNC001/coolant_alert = true with a pipeline.

Cycle time from a part counter (Interval) ​

Trigger: Interval, 1000 ms. Local tags CNC001/last_part_count, CNC001/last_part_time and CNC001/cycle_time should exist so they survive a restart.

javascript
const local = devices.localId();
const partCount = Number(tags.read('CNC-001', 'part_count'));
const lastCount = Number(tags.read(local, 'CNC001/last_part_count') || '0');
if (!(partCount > lastCount)) return;

const now = Date.now();
const lastTime = Number(tags.read(local, 'CNC001/last_part_time') || String(now));
const cycleSec = (now - lastTime) / 1000;

await tags.writeBatch([
  { tagPath: 'CNC001/last_part_count', value: partCount },
  { tagPath: 'CNC001/last_part_time', value: now },
  { tagPath: 'CNC001/cycle_time', value: cycleSec.toFixed(1) },
]);
db.insert('cycle_times', { machine: 'CNC-001', part_count: partCount, cycle_time_sec: cycleSec });
log('Part ' + partCount + ': ' + cycleSec.toFixed(1) + ' s');

Hand off to a workflow (any trigger) ​

To start one particular workflow and wait for its result, run it by name. (messages.send() also reaches workflows, but only ones with a Message Received trigger for that name, and it doesn't wait for them.)

javascript
const result = await workflows.run('SICK Trigger Inspection', { station: 'OP20' });
if (result.status !== 'completed') {
  throw new Error('Inspection workflow failed: ' + result.error);   // marks this run failed
}

End-of-shift job (Cron Schedule, saved by an administrator) ​

Trigger: Cron Schedule 0 23 * * * (23:00 appliance time). system.backup() and users.logoutAll() need the script to be saved by an administrator.

javascript
const backup = system.backup();
log('Nightly backup: ' + backup.filename + ' (' + backup.sizeBytes + ' bytes)');

for (const alarm of alarms.getActive()) {
  alarms.acknowledge(alarm.id);
}

if (email.isConfigured()) {
  const produced = tags.read('CNC-001', 'part_count') ?? 'unknown';
  await email.send({
    to: 'supervisor@plant.local',
    subject: 'Shift complete — CNC-001',
    text: 'Parts produced this shift: ' + produced,
  });
}

users.logoutAll();   // sign out the kiosk displays

Shift report from a named query (Cron Schedule) ​

javascript
const rows = await namedQueries.run('downtime_by_reason', {
  line: 2,
  start: new Date(Date.now() - 8 * 3600 * 1000).toISOString(),
});
const lines = rows.map((r) => r.reason + ': ' + Number(r.minutes).toFixed(0) + ' min');
log(lines);

if (rows.length && email.isConfigured()) {
  await email.send({ to: 'line2@plant.local', subject: 'Line 2 downtime, last 8 h', text: lines.join('\n') });
}

Temporary contractor account (Manual, saved by an administrator) ​

javascript
const contractorEmail = context.params.email;   // passed from a button or the API
if (!contractorEmail) throw new Error('Pass an email parameter');

const id = users.add({
  email: contractorEmail,
  password: 'Temp-' + Math.random().toString(36).slice(2, 10),
  firstName: 'Visiting',
  lastName: 'Tech',
  role: 'operator',
});
log('Created temporary operator #' + id);
// Later: users.disable(id) keeps their history; users.remove(id) deletes the account.

See also ​

Scada.Ink Help Center