@flux-control/effect-modbus-rs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,182 @@
1
+ import type { ReadRegistersOptions, WriteSingleRegisterOptions, WriteMultipleRegistersOptions, ReadWriteMultipleRegistersOptions, ReadBitsOptions, WriteSingleCoilOptions, WriteMultipleCoilsOptions, ReadFifoQueueOptions, ReadFileRecordOptions, WriteFileRecordOptions, DiagnosticsOptions, ReadDeviceIdentificationOptions, FifoQueueResponse, DiagnosticsResponse, DeviceIdentificationResponse, AsyncSerialModbusClient, AsyncTcpModbusClient } from "modbus-rs";
2
+ import { Effect } from "effect";
3
+ import type { ModbusError } from "./errors";
4
+ export type AnyModbusClient = AsyncSerialModbusClient | AsyncTcpModbusClient;
5
+ /**
6
+ * Effect-ified Modbus client wrapping a `modbus-rs` transport client.
7
+ *
8
+ * Each method delegates to the equivalent `AsyncSerialModbusClient` or
9
+ * `AsyncTcpModbusClient` method, converting the Promise-based API into
10
+ * an {@link Effect.Effect} with typed {@link ModbusError} failures.
11
+ *
12
+ * Thrown errors are classified using {@link toModbusError}, mapping
13
+ * `modbus-rs` error codes (timeout, transport, exception, etc.) into
14
+ * the corresponding `Data.TaggedError` variant for use with
15
+ * `Effect.catchTags`.
16
+ *
17
+ * @see AsyncSerialModbusClient — Upstream `modbus-rs` serial client API.
18
+ * @see AsyncTcpModbusClient — Upstream `modbus-rs` TCP client API.
19
+ */
20
+ export interface EffectModbusClient {
21
+ /**
22
+ * Reads holding registers from the Modbus device (FC03).
23
+ *
24
+ * @param opts - Register address, quantity, and optional `AbortSignal`.
25
+ * @returns An Effect resolving to an array of 16-bit register values.
26
+ *
27
+ * @see ReadRegistersOptions — Options shape from `modbus-rs`.
28
+ * @see AsyncSerialModbusClient.readHoldingRegisters — Upstream implementation.
29
+ */
30
+ readHoldingRegisters(opts: ReadRegistersOptions): Effect.Effect<number[], ModbusError>;
31
+ /**
32
+ * Reads input registers from the Modbus device (FC04).
33
+ *
34
+ * @param opts - Register address, quantity, and optional `AbortSignal`.
35
+ * @returns An Effect resolving to an array of 16-bit input register values.
36
+ *
37
+ * @see ReadRegistersOptions — Options shape from `modbus-rs`.
38
+ * @see AsyncSerialModbusClient.readInputRegisters — Upstream implementation.
39
+ */
40
+ readInputRegisters(opts: ReadRegistersOptions): Effect.Effect<number[], ModbusError>;
41
+ /**
42
+ * Writes a single holding register (FC06).
43
+ *
44
+ * @param opts - Register address, value, and optional `AbortSignal`.
45
+ * @returns An Effect that completes when the write is acknowledged.
46
+ *
47
+ * @see WriteSingleRegisterOptions — Options shape from `modbus-rs`.
48
+ */
49
+ writeSingleRegister(opts: WriteSingleRegisterOptions): Effect.Effect<void, ModbusError>;
50
+ /**
51
+ * Writes multiple consecutive holding registers (FC16).
52
+ *
53
+ * @param opts - Starting address, array of values, and optional `AbortSignal`.
54
+ * @returns An Effect that completes when the write is acknowledged.
55
+ *
56
+ * @see WriteMultipleRegistersOptions — Options shape from `modbus-rs`.
57
+ */
58
+ writeMultipleRegisters(opts: WriteMultipleRegistersOptions): Effect.Effect<void, ModbusError>;
59
+ /**
60
+ * Atomic read-write of multiple registers (FC23).
61
+ *
62
+ * Performs a write operation and a read operation atomically within
63
+ * a single Modbus transaction.
64
+ *
65
+ * @param opts - Separate read and write addresses/quantities/values.
66
+ * @returns An Effect resolving to the read register values.
67
+ *
68
+ * @see ReadWriteMultipleRegistersOptions — Options shape from `modbus-rs`.
69
+ */
70
+ readWriteMultipleRegisters(opts: ReadWriteMultipleRegistersOptions): Effect.Effect<number[], ModbusError>;
71
+ /**
72
+ * Reads coils (digital outputs) from the Modbus device (FC01).
73
+ *
74
+ * @param opts - Starting address, quantity, and optional `AbortSignal`.
75
+ * @returns An Effect resolving to an array of boolean coil states.
76
+ *
77
+ * @see ReadBitsOptions — Options shape from `modbus-rs`.
78
+ */
79
+ readCoils(opts: ReadBitsOptions): Effect.Effect<boolean[], ModbusError>;
80
+ /**
81
+ * Writes a single coil (digital output) (FC05).
82
+ *
83
+ * @param opts - Coil address, boolean value, and optional `AbortSignal`.
84
+ * @returns An Effect that completes when the write is acknowledged.
85
+ *
86
+ * @see WriteSingleCoilOptions — Options shape from `modbus-rs`.
87
+ */
88
+ writeSingleCoil(opts: WriteSingleCoilOptions): Effect.Effect<void, ModbusError>;
89
+ /**
90
+ * Writes multiple consecutive coils (FC15).
91
+ *
92
+ * @param opts - Starting address, array of boolean values, and optional `AbortSignal`.
93
+ * @returns An Effect that completes when the write is acknowledged.
94
+ *
95
+ * @see WriteMultipleCoilsOptions — Options shape from `modbus-rs`.
96
+ */
97
+ writeMultipleCoils(opts: WriteMultipleCoilsOptions): Effect.Effect<void, ModbusError>;
98
+ /**
99
+ * Reads discrete inputs (digital inputs) from the Modbus device (FC02).
100
+ *
101
+ * @param opts - Starting address, quantity, and optional `AbortSignal`.
102
+ * @returns An Effect resolving to an array of boolean input states.
103
+ *
104
+ * @see ReadBitsOptions — Options shape from `modbus-rs`.
105
+ */
106
+ readDiscreteInputs(opts: ReadBitsOptions): Effect.Effect<boolean[], ModbusError>;
107
+ /**
108
+ * Reads the FIFO queue from the Modbus device (FC24).
109
+ *
110
+ * @param opts - FIFO pointer address and optional `AbortSignal`.
111
+ * @returns An Effect resolving to a `FifoQueueResponse` containing the queue values.
112
+ *
113
+ * @see ReadFifoQueueOptions — Options shape from `modbus-rs`.
114
+ * @see FifoQueueResponse — Response type from `modbus-rs`.
115
+ */
116
+ readFifoQueue(opts: ReadFifoQueueOptions): Effect.Effect<FifoQueueResponse, ModbusError>;
117
+ /**
118
+ * Reads file records from the Modbus device (FC20).
119
+ *
120
+ * @param opts - Array of file/sub-record read requests and optional `AbortSignal`.
121
+ * @returns An Effect resolving to an array of record data arrays.
122
+ *
123
+ * @see ReadFileRecordOptions — Options shape from `modbus-rs`.
124
+ */
125
+ readFileRecord(opts: ReadFileRecordOptions): Effect.Effect<number[][], ModbusError>;
126
+ /**
127
+ * Writes file records to the Modbus device (FC21).
128
+ *
129
+ * @param opts - Array of file/sub-record write requests and optional `AbortSignal`.
130
+ * @returns An Effect that completes when the write is acknowledged.
131
+ *
132
+ * @see WriteFileRecordOptions — Options shape from `modbus-rs`.
133
+ */
134
+ writeFileRecord(opts: WriteFileRecordOptions): Effect.Effect<void, ModbusError>;
135
+ /**
136
+ * Reads the Modbus exception status (FC07).
137
+ *
138
+ * Returns the contents of eight exception-status coils as a single byte.
139
+ *
140
+ * @returns An Effect resolving to the exception status byte value.
141
+ */
142
+ readExceptionStatus(): Effect.Effect<number, ModbusError>;
143
+ /**
144
+ * Sends a diagnostics request to the Modbus device (FC08).
145
+ *
146
+ * @param opts - Diagnostic sub-function code and data words.
147
+ * @returns An Effect resolving to a `DiagnosticsResponse` containing the echo sub-function and data.
148
+ *
149
+ * @see DiagnosticsOptions — Options shape from `modbus-rs`.
150
+ * @see DiagnosticsResponse — Response type from `modbus-rs`.
151
+ */
152
+ diagnostics(opts: DiagnosticsOptions): Effect.Effect<DiagnosticsResponse, ModbusError>;
153
+ /**
154
+ * Reads device identification from the Modbus device (FC43 / MEI type 14).
155
+ *
156
+ * @param opts - Read device ID code, starting object ID, and optional `AbortSignal`.
157
+ * @returns An Effect resolving to a `DeviceIdentificationResponse` with conformity level and objects.
158
+ *
159
+ * @see ReadDeviceIdentificationOptions — Options shape from `modbus-rs`.
160
+ * @see DeviceIdentificationResponse — Response type from `modbus-rs`.
161
+ */
162
+ readDeviceIdentification(opts: ReadDeviceIdentificationOptions): Effect.Effect<DeviceIdentificationResponse, ModbusError>;
163
+ }
164
+ /**
165
+ * Wraps a raw `modbus-rs` client into an {@link EffectModbusClient}.
166
+ *
167
+ * Each method converts a Promise-based call from the upstream client
168
+ * into an `Effect` via {@link Effect.tryPromise}, routing errors through
169
+ * {@link toModbusError} for typed error discrimination.
170
+ *
171
+ * Accepts both serial (`AsyncSerialModbusClient`) and TCP
172
+ * (`AsyncTcpModbusClient`) clients since they share the same method
173
+ * signatures.
174
+ *
175
+ * @param client - The upstream `modbus-rs` client instance.
176
+ * @returns An `EffectModbusClient` that can be used within Effect
177
+ * workflows.
178
+ *
179
+ * @see AsyncSerialModbusClient — Upstream serial client API.
180
+ * @see AsyncTcpModbusClient — Upstream TCP client API.
181
+ */
182
+ export declare const makeEffectModbusClient: (client: AsyncSerialModbusClient | AsyncTcpModbusClient) => EffectModbusClient;
@@ -0,0 +1,64 @@
1
+ import { Effect, Scope } from "effect";
2
+ import { type ModbusError, ModbusNotConnectedError } from "./errors";
3
+ import { type AnyModbusClient, type EffectModbusClient } from "./modbus-client";
4
+ /**
5
+ * Shared API surface that every transport service exposes to consumers.
6
+ *
7
+ * Provides lazy connection, per-unit-ID client caching, timeout management,
8
+ * reconnection, and graceful shutdown — all within the Effect scope.
9
+ *
10
+ * @see makeTransportScoped — Factory that produces this API from a raw transport.
11
+ */
12
+ export interface TransportServiceApi {
13
+ /** Obtains (or creates) a cached {@link EffectModbusClient} for the given unit ID. */
14
+ withClient(unitId: number): Effect.Effect<EffectModbusClient, ModbusError>;
15
+ /** Sets a request timeout (ms) on the underlying transport. Fails if not connected. */
16
+ setRequestTimeout(timeoutMs: number): Effect.Effect<void, ModbusError>;
17
+ /** Clears the request timeout. Fails if not connected. */
18
+ clearRequestTimeout(): Effect.Effect<void, ModbusError>;
19
+ /** Reconnects the transport. Opens lazily if no prior connection exists. */
20
+ reconnect(): Effect.Effect<void, ModbusError>;
21
+ /** Closes the transport and its scope immediately. */
22
+ close(): Effect.Effect<void, ModbusError, Scope.Scope>;
23
+ /** Whether the transport currently has in-flight requests. */
24
+ hasPendingRequests(): boolean;
25
+ }
26
+ interface TransportHandle<TClient> {
27
+ close(): Promise<void>;
28
+ createClient(opts: {
29
+ unitId: number;
30
+ }): TClient;
31
+ setRequestTimeout(ms: number): void;
32
+ clearRequestTimeout(): void;
33
+ reconnect(): Promise<void>;
34
+ pendingRequests: boolean;
35
+ }
36
+ /**
37
+ * Generic factory for the scoped constructor body of an `Effect.Service`.
38
+ *
39
+ * Dynamically imports `modbus-rs`, opens the transport via `openMethod`,
40
+ * and returns a {@link TransportServiceApi} that manages connection
41
+ * lifecycle, client caching, timeouts, and reconnection.
42
+ *
43
+ * The transport is opened lazily on the first `withClient()` call and
44
+ * automatically closed when the consuming {@link Effect.Scope | Scope}
45
+ * finalizes via `Effect.addFinalizer`.
46
+ *
47
+ * @typeParam TOptions - Transport options (e.g. `RtuTransportOptions`).
48
+ * @typeParam TClient - The client type created by the transport.
49
+ * @typeParam TTransport - The transport handle type.
50
+ * @param transportKey - The named export from `modbus-rs` (e.g. `"AsyncRtuTransport"`).
51
+ * @param openMethod - A function that takes the transport constructor and options,
52
+ * returning a promise for the opened transport.
53
+ * @param serviceName - Logical name used in log messages and the finalizer guard.
54
+ * @returns An `Effect` that produces a {@link TransportServiceApi}.
55
+ */
56
+ export declare function makeTransportScoped<TOptions, TClient extends AnyModbusClient, TTransport extends TransportHandle<TClient>>(transportKey: string, openMethod: (TC: unknown, options: TOptions) => Promise<TTransport>, serviceName: string): (options: TOptions) => Effect.Effect<{
57
+ withClient: (unitId: number) => Effect.Effect<EffectModbusClient, ModbusError, never>;
58
+ setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, ModbusNotConnectedError, never>;
59
+ clearRequestTimeout: () => Effect.Effect<undefined, ModbusNotConnectedError, never>;
60
+ reconnect: () => Effect.Effect<undefined, ModbusError, never>;
61
+ close: () => Effect.Effect<void, ModbusError, Scope.Scope>;
62
+ hasPendingRequests: () => boolean;
63
+ }, never, Scope.Scope>;
64
+ export {};
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@flux-control/effect-modbus-rs",
3
+ "version": "0.1.0",
4
+ "description": "Type-safe Modbus communication via Effect-TS, wrapping the modbus-rs npm bindings.",
5
+ "license": "GPL-3.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/flux-control-solutions/Effect-modbus-rs.git"
9
+ },
10
+ "type": "module",
11
+ "files": [
12
+ "dist/**/*.js",
13
+ "dist/**/*.d.ts"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public",
17
+ "provenance": true,
18
+ "exports": {
19
+ "./package.json": "./package.json",
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ }
24
+ }
25
+ },
26
+ "exports": {
27
+ "./package.json": "./package.json",
28
+ ".": {
29
+ "types": "./index.ts",
30
+ "default": "./index.ts"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "changeset": "changeset",
35
+ "test": "bun test ./src/ ./examples/",
36
+ "test:coverage": "bun test --coverage ./src/ ./examples/",
37
+ "typecheck": "tsc --noEmit",
38
+ "build": "rm -rf ./dist && bun build ./index.ts --outdir ./dist --target node --external effect --external modbus-rs && tsc --noEmit false --emitDeclarationOnly --outDir ./dist --rootDir . --project tsconfig.build.json",
39
+ "docs": "typedoc",
40
+ "release": "changeset publish",
41
+ "version": "changeset version"
42
+ },
43
+ "devDependencies": {
44
+ "@changesets/cli": "^2.31.1",
45
+ "@effect/language-service": "^0.86.2",
46
+ "@effect/platform-bun": "^0.90.0",
47
+ "@types/bun": "latest",
48
+ "effect": "^3.21.4",
49
+ "fallow": "^2.103.0",
50
+ "typedoc": "^0.28.20",
51
+ "typescript": "^6.0.3"
52
+ },
53
+ "peerDependencies": {
54
+ "effect": "^3.21.4"
55
+ },
56
+ "dependencies": {
57
+ "modbus-rs": "^0.15.3"
58
+ }
59
+ }