npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-nutrunner-open-library

v1.0.4

Published

Production-grade Atlas Copco Open Protocol client for Node.js. Handles nutrunner communication, tightening cycles, VIN traceability, batch manufacturing, and industrial safety interlocks. Supports Atlas Copco, Stanley, Desoutter, and Ingersoll Rand contro

Readme

node-nutrunner-open-library

npm version License Node.js Version

Production-grade Atlas Copco Open Protocol client for Node.js. Built for real manufacturing environments with robust error recovery, automatic reconnection, and comprehensive industrial safety interlocks.

Designed and tested in actual automotive and aerospace assembly lines in Pune, India.


Features

  • Full Open Protocol Support - MID 0001-0101 (Specification 2.8.0+)
  • Production-Hardened - Handles TCP fragmentation, network glitches, firmware variations
  • Automatic Reconnection - Exponential backoff with state recovery
  • Safety Interlocks - Enforces controller-side safety rules (VIN, job, alarms)
  • Multi-Spindle Tools - Auto-detection from MID 0061/0101
  • VIN Traceability - Automotive-grade part tracking and locking
  • Batch Manufacturing - Real-time progress tracking and completion events
  • Alarm Handling - Subscribe, acknowledge, and recover from controller alarms
  • Command Safety - One-per-MID enforcement prevents state corruption
  • Event-Driven API - Perfect for OPC UA, MQTT, MES integration
  • Zero Dependencies - Uses only Node.js core modules

Installation

npm install node-nutrunner-open-library

Requirements: Node.js >= 14.0.0


Quick Start

const { OpenProtocolNutrunner } = require('node-nutrunner-open-library');

const nutrunner = new OpenProtocolNutrunner({
  host: '192.168.1.100',
  port: 4545,
  autoReconnect: true
});

// Handle tightening results
nutrunner.on('tighteningCycleCompleted', ({ results, overallOk }) => {
  console.log(`Tightening ${overallOk ? 'OK' : 'NOK'}`);
  results.forEach(r => {
    console.log(`Spindle ${r.spindle}: ${r.torque} Nm, ${r.angle}°`);
  });
});

// Connect and setup
await nutrunner.connect();
await nutrunner.selectJob(123);
await nutrunner.enableTool();

console.log('Ready for tightening!');

Supported Controllers

| Manufacturer | Models | Status | |--------------|--------|--------| | Atlas Copco | PowerFocus 4000/6000, PowerMACS | ✅ Tested | | Stanley Assembly Technologies | Open Protocol compatible | ✅ Tested | | Desoutter | CVI controllers | ✅ Tested | | Ingersoll Rand | QX Series | ✅ Compatible |


Complete Examples

Basic Tightening Workflow

const { OpenProtocolNutrunner } = require('node-nutrunner-open-library');

const nutrunner = new OpenProtocolNutrunner({
  host: '192.168.1.100'
});

nutrunner.on('connected', () => {
  console.log('✓ Connected to controller');
});

nutrunner.on('tighteningCycleStarted', () => {
  console.log('⚙ Tightening started...');
});

nutrunner.on('spindleResult', (result) => {
  console.log(`Spindle ${result.spindle}: ${result.ok ? '✓' : '✗'}`);
  console.log(`  Torque: ${result.torque} Nm`);
  console.log(`  Angle: ${result.angle}°`);
});

nutrunner.on('tighteningCycleCompleted', ({ results, overallOk, duration }) => {
  console.log(`${overallOk ? '✓' : '✗'} Completed in ${duration}ms`);
});

await nutrunner.connect();

VIN Traceability for Automotive Manufacturing

const nutrunner = new OpenProtocolNutrunner({
  host: '192.168.1.100',
  spindleCount: 2 // Multi-spindle tool
});

nutrunner.on('linkEstablished', async () => {
  // Download VIN for current product
  await nutrunner.downloadVIN('1HGBH41JXMN109186');
  await nutrunner.selectJob(101);
  await nutrunner.enableTool();
  console.log('✓ VIN downloaded - ready for tightening');
});

nutrunner.on('vinLocked', (vin) => {
  console.log(`🔒 VIN locked for traceability: ${vin}`);
});

nutrunner.on('tighteningCycleCompleted', ({ results }) => {
  const record = {
    vin: nutrunner.getState().product.vin,
    timestamp: new Date().toISOString(),
    results: results
  };
  
  // Save to database for traceability
  saveToDatabase(record);
});

await nutrunner.connect();

Batch Manufacturing with Progress Tracking

const nutrunner = new OpenProtocolNutrunner({
  host: '192.168.1.100'
});

nutrunner.on('batchStarted', (batch) => {
  console.log(`📦 Batch ${batch.batchId} started (Size: ${batch.size})`);
});

nutrunner.on('batchProgress', ({ counter, size, remaining }) => {
  const percent = Math.round((counter / size) * 100);
  console.log(`Progress: ${counter}/${size} (${percent}%) - ${remaining} remaining`);
});

nutrunner.on('batchCompleted', (batch) => {
  console.log(`✓ Batch ${batch.batchId} completed!`);
});

await nutrunner.connect();
await nutrunner.selectJob(5); // Job configured with batch size
await nutrunner.enableTool();

Alarm Handling and Recovery

const nutrunner = new OpenProtocolNutrunner({
  host: '192.168.1.100'
});

nutrunner.on('alarm', (alarm) => {
  console.error(`🚨 ALARM: [${alarm.alarmCode}] ${alarm.message}`);
  
  // Auto-acknowledge specific alarms
  if (shouldAutoAcknowledge(alarm.alarmCode)) {
    setTimeout(() => {
      nutrunner.acknowledgeAlarm();
    }, 2000);
  }
});

nutrunner.on('alarmStatus', ({ alarmStatus }) => {
  if (!alarmStatus) {
    console.log('✓ All alarms cleared - system ready');
  }
});

function shouldAutoAcknowledge(code) {
  const autoAckCodes = ['0001', '0010', '0015'];
  return autoAckCodes.includes(code);
}

await nutrunner.connect();

Multi-Controller Fleet Management

const fleet = [
  { id: 'Station-A', host: '192.168.1.100' },
  { id: 'Station-B', host: '192.168.1.101' },
  { id: 'Station-C', host: '192.168.1.102' }
];

const controllers = fleet.map(config => {
  const nutrunner = new OpenProtocolNutrunner(config);
  
  nutrunner.on('tighteningCycleCompleted', ({ overallOk }) => {
    console.log(`[${config.id}] Tightening ${overallOk ? 'OK' : 'NOK'}`);
  });
  
  return { id: config.id, nutrunner };
});

// Connect all controllers
await Promise.all(controllers.map(c => c.nutrunner.connect()));

// Setup all stations
for (const { id, nutrunner } of controllers) {
  await nutrunner.selectJob(100);
  await nutrunner.enableTool();
  console.log(`[${id}] Ready`);
}

Constructor Options

const nutrunner = new OpenProtocolNutrunner({
  host: '192.168.1.100',           // Required: Controller IP address
  port: 4545,                       // Default: 4545
  autoReconnect: true,              // Default: true (exponential backoff)
  validateFrames: true,             // Default: true (frame corruption detection)
  spindleCount: null,               // Override for controllers without MID 101
  allowDuplicateCommands: false     // Default: false (enforces command safety)
});

Events Reference

Connection Events

  • connected - TCP connection established
  • disconnected - Connection lost
  • reconnecting - Reconnection attempt in progress (emits { attempt, delay })
  • linkEstablished - Open Protocol handshake complete (emits { revision })

Tightening Events

  • tighteningCycleStarted - Tool running detected (emits { timestamp })
  • spindleResult - Individual spindle result received (emits result object)
  • tighteningCycleCompleted - All spindles completed (emits { results, overallOk, duration })
  • tighteningIncomplete - Watchdog timeout (emits { expected, received, results })

Command Events

  • commandAccepted - MID 0005 received (emits { mid })
  • commandError - MID 0004 received (emits { failedMid, errorCode, message })
  • commandTimeout - No response within 5s (emits { mid, cmdId })

State Events

  • jobSelected - Job activated (emits { jobId })
  • vinLocked - VIN locked for traceability (emits VIN string)
  • batchStarted - Batch production started (emits batch object)
  • batchProgress - Batch counter updated (emits { counter, size, remaining })
  • batchCompleted - Batch size reached (emits batch object)
  • alarm - Controller alarm raised (emits alarm object)
  • alarmStatus - Alarm state changed (emits { alarmStatus, currentAlarms })
  • stateChanged - Any state change (emits full state snapshot)

🛡️ Safety Interlocks

The library enforces industrial safety interlocks before allowing tightening operations:

try {
  nutrunner.startTightening();
} catch (err) {
  if (err instanceof InterlockError) {
    console.log(`Interlock: ${err.code} - ${err.message}`);
  }
}

Interlock Error Codes:

  • NOT_CONNECTED - No TCP connection to controller
  • LINK_NOT_READY - Protocol handshake not complete
  • TOOL_DISABLED - Tool not enabled (send MID 0042)
  • TOOL_RUNNING - Tightening already in progress
  • CTRL_NOT_READY - Controller not ready
  • ALARM_ACTIVE - Active alarm must be acknowledged
  • VIN_REQUIRED - VIN required but not downloaded
  • JOB_NOT_ACTIVE - No job selected

🔄 Available Methods

Connection

await nutrunner.connect()           // Connect to controller
nutrunner.disconnect()               // Disconnect gracefully
nutrunner.isConnected()              // Check connection status
nutrunner.isReady()                  // Check if ready for tightening

Commands

await nutrunner.selectJob(jobId)                    // Select job by ID
await nutrunner.downloadVIN(vin)                    // Download VIN (max 25 chars)
await nutrunner.selectParameterSet(paramSetId)      // Select parameter set
await nutrunner.enableTool()                        // Enable tool
await nutrunner.disableTool()                       // Disable tool
await nutrunner.startTightening()                   // Start tightening (interlocks enforced)
await nutrunner.resetBatch()                        // Reset batch counter
await nutrunner.decrementBatch()                    // Decrement batch counter

Subscriptions

nutrunner.subscribeTighteningResults()    // Subscribe to MID 0061
nutrunner.unsubscribeTighteningResults()  // Unsubscribe from MID 0061
nutrunner.subscribeAlarms()               // Subscribe to MID 0070
nutrunner.unsubscribeAlarms()             // Unsubscribe from MID 0070
nutrunner.acknowledgeAlarm()              // Acknowledge active alarm

Configuration

nutrunner.setSpindleCount(count)     // Manually set spindle count
nutrunner.getSpindleCount()          // Get spindle count and source
nutrunner.getState()                 // Get full state snapshot

Integration Examples

OPC UA Server Bridge

const { OPCUAServer, Variant, DataType } = require('node-opcua');

const opcuaServer = new OPCUAServer({ port: 4840 });
await opcuaServer.initialize();

const namespace = opcuaServer.engine.addressSpace.getOwnNamespace();

const lastTorqueVar = namespace.addVariable({
  browseName: 'LastTorque',
  dataType: 'Double',
  value: { dataType: DataType.Double, value: 0.0 }
});

nutrunner.on('tighteningCycleCompleted', ({ results }) => {
  lastTorqueVar.setValueFromSource({
    dataType: DataType.Double,
    value: results.torque
  });
});

await opcuaServer.start();
await nutrunner.connect();

InfluxDB Time-Series Storage

const { InfluxDB, Point } = require('@influxdata/influxdb-client');

const influx = new InfluxDB({ url: 'http://localhost:8086', token: 'your-token' });
const writeApi = influx.getWriteApi('org', 'manufacturing');

nutrunner.on('tighteningCycleCompleted', ({ results, overallOk }) => {
  results.forEach(r => {
    const point = new Point('tightening')
      .tag('spindle', r.spindle.toString())
      .floatField('torque', r.torque)
      .intField('angle', r.angle)
      .booleanField('ok', r.ok);
    
    writeApi.writePoint(point);
  });
  
  writeApi.flush();
});

await nutrunner.connect();

MQTT Gateway for IIoT

const mqtt = require('mqtt');

const client = mqtt.connect('mqtt://localhost:1883');

nutrunner.on('tighteningCycleCompleted', ({ results, overallOk }) => {
  const payload = JSON.stringify({
    timestamp: new Date().toISOString(),
    ok: overallOk,
    results: results
  });
  
  client.publish('factory/station1/tightening', payload);
});

await nutrunner.connect();

Project Structure

node-nutrunner-open-library/
├── index.js                          # Main library (production-grade)
├── examples/
│   ├── 01-basic-tightening.js        # Simple tightening workflow
│   ├── 02-vin-traceability.js        # Automotive VIN tracking
│   ├── 03-batch-manufacturing.js     # Batch production
│   ├── 04-alarm-handling.js          # Alarm management
│   ├── 05-fleet-management.js        # Multi-controller setup
│   ├── 06-influxdb-integration.js    # Time-series database
│   ├── 07-opcua-bridge.js            # OPC UA server bridge
│   └── 08-error-recovery.js          # Error handling patterns
├── LICENSE                           # Apache 2.0
├── README.md                         # This file
├── CHANGELOG.md                      # Version history
└── package.json

Troubleshooting

Connection Issues

nutrunner.on('error', (err) => {
  console.error('Connection error:', err.message);
});

nutrunner.on('reconnecting', ({ attempt, delay }) => {
  console.log(`Reconnection attempt ${attempt} in ${delay}ms...`);
});

Frame Validation Errors

nutrunner.on('frameError', ({ type, buffer }) => {
  console.error(`Frame error: ${type}`);
  // Network corruption detected - library auto-recovers
});

Command Timeouts

nutrunner.on('commandTimeout', ({ mid, cmdId }) => {
  console.error(`Command MID ${mid} timed out (ID: ${cmdId})`);
});

Watchdog Timeouts (Missing Spindle Results)

nutrunner.on('tighteningIncomplete', ({ expected, received }) => {
  console.error(`Watchdog: Expected ${expected} spindles, got ${received}`);
  // Check controller configuration and network stability
});

Known Controller Quirks

PowerFocus 3000

  • Some units send MID 0002 instead of MID 0003 for comm start ACK (handled automatically)

Legacy Controllers

  • Controllers without MID 0101 support require manual spindle count:
    const nutrunner = new OpenProtocolNutrunner({
      host: '192.168.1.100',
      spindleCount: 2  // Set manually
    });

Firmware Variations

  • MID 0061 field positions vary by firmware version
  • Library uses status flags instead of hard-coded offsets (production-safe)

License

Apache License 2.0

Copyright (c) 2026 Bufferstack.IO Analytics Technology LLP
Copyright (c) 2026 Harshad Joshi

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.


Acknowledgments

Developed for real manufacturing environments in Pune, India. Built on lessons learned from production deployments in:

  • Automotive assembly lines
  • Aerospace component manufacturing
  • Heavy equipment production

Protocol Reference: Atlas Copco Open Protocol Specification v2.xx.yy+


Contributing

Contributions welcome! Areas needing help:

  • Additional MID implementations (parameter sets, multi-stage results, graphs)
  • TypeScript type definitions
  • Controller-specific quirks documentation
  • More integration examples (PostgreSQL, Kafka, etc.)

Support


Star this project

If this library helps your manufacturing operations, please ⭐ star it on GitHub!


Made with ❤️ for the industrial automation community

Tested in production since 2026 | Zero dependencies | Production-grade reliability