node-fxplc
Version:
Node.js library for low-level Mitsubishi FX (MELSEC) PLC framed protocol communication
166 lines (132 loc) • 5.83 kB
Markdown
[](https://www.npmjs.com/package/node-fxplc)
[](https://www.npmjs.com/package/node-fxplc)
[](LICENSE)
[](https://github.com/n-car/node-fxplc/releases)
> See also: [Release Notes](./RELEASE_NOTES.md)
Node.js library for communication with Mitsubishi FX PLCs using low-level MELSEC FX series serial protocol.
**Note:** it is not the same as Non-Protocol Communication (or D8120) as described in FX Series Programmable Controllers manuals.
This is a Node.js port of a Python library.
## Structure
- `lib/` — Library source code
- `test/` — Tests and example scripts
## Project Status
Pre-release. Core client API + TCP/Serial transports work for basic read/write, but advanced resilience and integration testing are still in progress.
See `ROADMAP.md` for detailed progress & planned items. Contributions / hardware test feedback are welcome.
## Installation
```bash
npm install node-fxplc
```
Optional (for debug logging):
```bash
npm install debug
```
Enable logs by setting environment variable (PowerShell):
```powershell
$env:DEBUG = "fxplc:*"; node yourscript.mjs
```
This package supports both ESM (`import`) and CommonJS (`require`) consumers. Use the appropriate syntax for your project type:
### ESM (import)
```js
import { FXPLCClient, TransportTCP, TransportSerial } from 'node-fxplc';
// ...your code
```
```js
const { FXPLCClient, TransportTCP, TransportSerial } = require('node-fxplc');
// ...your code
```
Both entrypoints provide the same API and typings. See below for detailed usage examples.
- Low-level MELSEC FX serial frame protocol (hex payload + checksum)
- Read / write single bit & forced coil (ON/OFF)
- Read / write signed word and generic numeric types via converters
- Batch read/write (auto-coalesces consecutive addresses)
- Raw byte read/write access for advanced use
- TCP & Serial transports with buffer accumulation & timeouts
- Simple retry + operation-level timeout
- Event emitter (error / connect / disconnect)
## Protocol Notes
This is NOT the "Non-Protocol Communication" (D8120) mode. It targets the classic hex framed ASCII protocol (FX0N/FX1N style). Serial defaults: 7 data bits, even parity, 1 stop bit (7E1), commonly 9600 baud. Verify your PLC parameters.
Compatibility note: The protocol layer is electrical-transport agnostic; it should also work with many FX-compatible clone PLCs exposing an RS422 port, provided they implement the same framed command set. Use a proper RS422↔USB (or RS422↔RS232) converter and match serial parameters (7E1, baud rate). Timing and response behaviors may vary between clones—enable retries if needed and report any incompatibilities.
## Supported Registers (initial)
- Bit areas (e.g. M, X, Y) via `readBit` / `writeBit`
- Data registers (e.g. D) via numeric read/write helpers
Parsing is handled by `RegisterDef.parse('D100')`. Unsupported / unknown areas will throw until implemented.
## Error Classes
Exposed error types (see `errors.js`):
- `NoResponseError` – timeout or no reply
- `ResponseMalformedError` – bad checksum / frame inconsistency
- `NotSupportedCommandError` – PLC replied NAK
- `NotConnectedError` – transport not open
Use instanceof checks for granular handling.
## Timeouts & Retries
Client option `timeoutMs` controls per-operation timeout. Retry policy via `retry: { count, delayMs }` (default 1 attempt). Example:
```js
const plc = new FXPLCClient(transport, { retry: { count: 3, delayMs: 150 }, timeoutMs: 2500 });
```
Enable simple reconnect:
```js
const tcp = new TransportTCP({ host: '127.0.0.1', port: 5000 });
tcp.setReconnect(true, 2000); // every 2s after disconnect
```
Serial transport currently has no auto-reopen loop (planned).
## Usage Examples
### Promise style (TCP)
```js
import { FXPLCClient, TransportTCP } from 'node-fxplc';
const transport = new TransportTCP({ host: '127.0.0.1', port: 5000 });
await transport.connect();
const plc = new FXPLCClient(transport);
const bit = await plc.readBit('M0');
console.log('Bit M0:', bit);
const vals = await plc.batchRead(['D100','D101']);
await plc.writeBit('M10', true);
plc.close();
```
```js
plc.readBit('M0', (err, bit) => {
if (err) return console.error('Error:', err);
console.log('Bit M0:', bit);
});
plc.batchRead(['D100','D101'], (err, vals) => {
if (!err) console.log('Values:', vals);
});
```
```js
plc.on('error', err => console.error('PLC Error:', err));
plc.on('connect', () => console.log('Connected!'));
plc.on('disconnect', () => console.log('Disconnected!'));
```
```js
try {
await plc.readBit('Z999'); // invalid register
} catch(e) {
console.error('Input error:', e.message);
}
```
```js
await plc.batchWrite(['D100','D101'], [123,456]);
```
```js
import { FXPLCClient, TransportSerial } from 'node-fxplc';
const transport = new TransportSerial({ path: 'COM3', baudRate: 9600, timeout: 1500 });
const plc = new FXPLCClient(transport, { debug: true });
const bit = await plc.readBit('M0');
console.log('M0 =', bit);
plc.close();
```
Install `debug` and set `DEBUG=fxplc:*` env variable. Logs show TX/RX hex frames.
Released under the MIT License. See the `LICENSE` file for full text.
---
Next focus: transport robustness, CLI, integration tests, richer docs.
Note: Serial transport is functional but minimally tested; use caution in production scenarios.