@flux-control/effect-modbus-rs 0.3.1 → 0.4.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.
- package/README.md +16 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.js +114 -118
- package/dist/src/AsciiTransportService.d.ts +31 -22
- package/dist/src/RtuTransportService.d.ts +31 -22
- package/dist/src/SerialTransportService.d.ts +1 -1
- package/dist/src/TcpTransportService.d.ts +31 -22
- package/dist/src/WasmAsciiTransportService.d.ts +30 -21
- package/dist/src/WasmRtuTransportService.d.ts +30 -21
- package/dist/src/WasmSerialPort.d.ts +2 -2
- package/dist/src/WasmSerialTransportService.d.ts +1 -1
- package/dist/src/WasmWsTransportService.d.ts +30 -21
- package/dist/src/connection.d.ts +9 -9
- package/dist/src/mocks.d.ts +41 -57
- package/dist/src/retry.d.ts +11 -8
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
For the complete API reference, see the [GitHub Pages documentation](https://flux-control-solutions.github.io/Effect-modbus-rs/).
|
|
6
6
|
|
|
7
|
-
Provides scoped [`
|
|
7
|
+
Provides scoped [`Context.Service`](https://effect.website) constructors for RTU (serial), TCP, and ASCII Modbus transports. Clients expose a typed `Effect`-based API for all standard Modbus function codes.
|
|
8
8
|
|
|
9
9
|
> This project is under active development. Its API may change before the 1.0 release.
|
|
10
10
|
|
|
@@ -44,7 +44,7 @@ program.pipe(
|
|
|
44
44
|
ModbusInvalidArgumentError: (err) => Console.log(`Invalid argument: ${err.message}`),
|
|
45
45
|
}),
|
|
46
46
|
Effect.catchAll((err) => Console.log(`Unhandled error: ${err.message}`)),
|
|
47
|
-
Effect.provide(RtuTransportService.
|
|
47
|
+
Effect.provide(RtuTransportService.make({ portPath: '/dev/ttyUSB0', baudRate: 9600 })),
|
|
48
48
|
Effect.scoped,
|
|
49
49
|
Effect.runPromise,
|
|
50
50
|
);
|
|
@@ -64,7 +64,7 @@ const program = Effect.gen(function* () {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
program.pipe(
|
|
67
|
-
Effect.provide(TcpTransportService.
|
|
67
|
+
Effect.provide(TcpTransportService.make({ host: '192.168.1.100', port: 502 })),
|
|
68
68
|
Effect.scoped,
|
|
69
69
|
Effect.runPromise,
|
|
70
70
|
);
|
|
@@ -87,7 +87,7 @@ const program = Effect.gen(function* () {
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
program.pipe(
|
|
90
|
-
Effect.provide(AsciiTransportService.
|
|
90
|
+
Effect.provide(AsciiTransportService.make({ portPath: '/dev/ttyUSB0', baudRate: 9600 })),
|
|
91
91
|
Effect.scoped,
|
|
92
92
|
Effect.runPromise,
|
|
93
93
|
);
|
|
@@ -112,7 +112,7 @@ const program = Effect.gen(function* () {
|
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
program.pipe(
|
|
115
|
-
Effect.provide(WasmWsTransportService.
|
|
115
|
+
Effect.provide(WasmWsTransportService.make({ wsUrl: 'ws://localhost:8080' })),
|
|
116
116
|
Effect.scoped,
|
|
117
117
|
Effect.runPromise,
|
|
118
118
|
);
|
|
@@ -129,7 +129,7 @@ connectButton.addEventListener('click', () => {
|
|
|
129
129
|
Effect.gen(function* () {
|
|
130
130
|
const port = yield* requestSerialPort();
|
|
131
131
|
yield* program.pipe(
|
|
132
|
-
Effect.provide(WasmRtuTransportService.
|
|
132
|
+
Effect.provide(WasmRtuTransportService.make({ port, baudRate: 19200 })),
|
|
133
133
|
Effect.scoped,
|
|
134
134
|
);
|
|
135
135
|
}),
|
|
@@ -150,7 +150,7 @@ Not demonstrated in `examples/wasm/` (see that app's README) — the same `impor
|
|
|
150
150
|
|
|
151
151
|
## Transports
|
|
152
152
|
|
|
153
|
-
Each transport is a scoped `
|
|
153
|
+
Each transport is a scoped `Context.Service`. You provide it with `Effect.provide`, and the connection is opened on service access and closed when the scope ends.
|
|
154
154
|
|
|
155
155
|
| Service | Options | Connection |
|
|
156
156
|
| ------------------------------------- | ------------------------------ | ----------------------------- |
|
|
@@ -279,7 +279,7 @@ Handle with `Effect.catchTags`. The `ModbusError` union type covers all seven va
|
|
|
279
279
|
Resilience belongs to the **transport**, not to call sites. Attach a policy where the transport is created and every client derived from it carries it:
|
|
280
280
|
|
|
281
281
|
```ts
|
|
282
|
-
const layer = TcpTransportService.
|
|
282
|
+
const layer = TcpTransportService.make({
|
|
283
283
|
host: '192.168.1.50',
|
|
284
284
|
port: 502,
|
|
285
285
|
retry: RetryPolicies.tcp(), // applied to every operation
|
|
@@ -393,7 +393,7 @@ So `RetryPolicies.tcp()` waits roughly 80–120 ms before its first retry, not e
|
|
|
393
393
|
Passing `reconnect` hands reconnection to a supervisor fiber owned by the transport — **one reconnect for the whole application**, however many fibers were in flight when the link dropped:
|
|
394
394
|
|
|
395
395
|
```ts
|
|
396
|
-
TcpTransportService.
|
|
396
|
+
TcpTransportService.make({
|
|
397
397
|
host,
|
|
398
398
|
port,
|
|
399
399
|
reconnect: {
|
|
@@ -445,7 +445,7 @@ See `examples/retry-policies.ts` for a runnable walkthrough.
|
|
|
445
445
|
`modbus-rs` exposes `retryAttempts`, `retryDelayMs`, and `retryBackoffStrategy` on its transport options. This package removes all three from every transport constructor, so setting one is a compile error rather than a documented hazard:
|
|
446
446
|
|
|
447
447
|
```ts
|
|
448
|
-
TcpTransportService.
|
|
448
|
+
TcpTransportService.make({ host, port, retryAttempts: 3 });
|
|
449
449
|
// ^^^^^^^^^^^^^ Object literal may only specify
|
|
450
450
|
// known properties, and 'retryAttempts' does not
|
|
451
451
|
// exist in type 'TcpTransportOpenOptions & …'
|
|
@@ -568,17 +568,17 @@ src/
|
|
|
568
568
|
connection.ts — Connection state machine, reconnect supervisor, circuit breaker
|
|
569
569
|
retry.ts — Opt-in retry policies (backoff, jitter, per-error rules)
|
|
570
570
|
shared-transport.ts — Generic scoped transport lifecycle management, WithoutUpstreamRetry
|
|
571
|
-
RtuTransportService.ts — Scoped
|
|
572
|
-
TcpTransportService.ts — Scoped
|
|
573
|
-
AsciiTransportService.ts — Scoped
|
|
571
|
+
RtuTransportService.ts — Scoped Context.Service wrapping AsyncRtuTransport
|
|
572
|
+
TcpTransportService.ts — Scoped Context.Service wrapping AsyncTcpTransport
|
|
573
|
+
AsciiTransportService.ts — Scoped Context.Service wrapping AsyncAsciiTransport
|
|
574
574
|
SerialTransportService.ts — Abstract serial transport (RTU/ASCII) tag
|
|
575
575
|
TcpModbusServerService.ts — tcpServerLayer
|
|
576
576
|
SerialModbusServerService.ts — serialRtuServerLayer / serialAsciiServerLayer
|
|
577
577
|
TcpGatewayService.ts — tcpGatewayLayer
|
|
578
578
|
WasmSerialPort.ts — requestSerialPort() Effect helper (browser, user-gesture gated)
|
|
579
|
-
WasmWsTransportService.ts — Scoped
|
|
580
|
-
WasmRtuTransportService.ts — Scoped
|
|
581
|
-
WasmAsciiTransportService.ts — Scoped
|
|
579
|
+
WasmWsTransportService.ts — Scoped Context.Service wrapping WasmWsTransport (browser, WS gateway)
|
|
580
|
+
WasmRtuTransportService.ts — Scoped Context.Service wrapping WasmRtuTransport (browser, Web Serial RTU)
|
|
581
|
+
WasmAsciiTransportService.ts — Scoped Context.Service wrapping WasmAsciiTransport (browser, Web Serial ASCII)
|
|
582
582
|
WasmSerialTransportService.ts — Abstract browser serial transport (RTU/ASCII) tag
|
|
583
583
|
WasmTcpServerService.ts — wasmWsServerLayer (experimental)
|
|
584
584
|
WasmSerialModbusServerService.ts — wasmSerialRtuServerLayer / wasmSerialAsciiServerLayer (experimental)
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -55,7 +55,7 @@ var toModbusError = (cause) => {
|
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
// src/retry.ts
|
|
58
|
-
import { Duration, Effect, Schedule } from "effect";
|
|
58
|
+
import { Duration, Effect, Random, Schedule } from "effect";
|
|
59
59
|
var retryableExceptionCodes = [5, 6, 10, 11];
|
|
60
60
|
var defaultRetryableTags = {
|
|
61
61
|
ModbusTimeoutError: true,
|
|
@@ -68,7 +68,7 @@ var defaultRetryableTags = {
|
|
|
68
68
|
ModbusInternalError: false
|
|
69
69
|
};
|
|
70
70
|
var allTags = Object.keys(defaultRetryableTags);
|
|
71
|
-
var toMillis = (input) => Duration.toMillis(Duration.
|
|
71
|
+
var toMillis = (input) => Duration.toMillis(Duration.fromInputUnsafe(input));
|
|
72
72
|
var resolveRetryableTags = (options) => {
|
|
73
73
|
const retryable = {};
|
|
74
74
|
for (const tag of allTags) {
|
|
@@ -112,10 +112,14 @@ var makeRetryPolicy = (options = {}) => {
|
|
|
112
112
|
const { baseMs, factor, maxMs } = delays[error._tag];
|
|
113
113
|
return Duration.millis(Math.min(maxMs, baseMs * factor ** retryIndex));
|
|
114
114
|
};
|
|
115
|
-
const base = Schedule.recurs(options.maxRetries ?? 3).pipe(Schedule.
|
|
115
|
+
const base = Schedule.recurs(options.maxRetries ?? 3).pipe(Schedule.setInputType(), Schedule.modifyDelay((metadata) => Effect.succeed(delayFor(metadata.input, metadata.attempt - 1))), Schedule.while((metadata) => Effect.succeed(isRetryable(metadata.input))));
|
|
116
116
|
const jitter = options.jitter ?? true;
|
|
117
|
-
const jittered = jitter === false ? base :
|
|
118
|
-
|
|
117
|
+
const jittered = jitter === false ? base : jitter === true ? Schedule.jittered(base) : Schedule.modifyDelay(base, (metadata) => Effect.map(Random.next, (r) => {
|
|
118
|
+
const min = jitter.min ?? 0.8;
|
|
119
|
+
const max = jitter.max ?? 1.2;
|
|
120
|
+
return Duration.millis(Duration.toMillis(metadata.duration) * (min + r * (max - min)));
|
|
121
|
+
}));
|
|
122
|
+
const schedule = options.maxElapsed === undefined ? jittered : Schedule.upTo(jittered, { duration: options.maxElapsed });
|
|
119
123
|
return { schedule, isRetryable };
|
|
120
124
|
};
|
|
121
125
|
var RetryPolicies = {
|
|
@@ -146,7 +150,7 @@ var RetryPolicies = {
|
|
|
146
150
|
};
|
|
147
151
|
var retryModbus = (policy) => (self) => Effect.retry(self, policy.schedule);
|
|
148
152
|
// src/connection.ts
|
|
149
|
-
import { Data as Data2, Duration as Duration2, Effect as Effect2,
|
|
153
|
+
import { Data as Data2, Duration as Duration2, Effect as Effect2, Result, SubscriptionRef } from "effect";
|
|
150
154
|
var ConnectionState = Data2.taggedEnum();
|
|
151
155
|
var defaultReconnectPolicy = makeRetryPolicy({
|
|
152
156
|
maxRetries: 5,
|
|
@@ -162,7 +166,7 @@ var resolveReconnect = (options) => {
|
|
|
162
166
|
const triggerTags = options.triggerOn ?? defaultTriggers;
|
|
163
167
|
return {
|
|
164
168
|
policy: options.policy ?? defaultReconnectPolicy,
|
|
165
|
-
resetAfter: Duration2.
|
|
169
|
+
resetAfter: Duration2.fromInputUnsafe(options.resetAfter ?? "30 seconds"),
|
|
166
170
|
triggers: (error) => triggerTags.includes(error._tag)
|
|
167
171
|
};
|
|
168
172
|
};
|
|
@@ -181,13 +185,13 @@ var guardCircuit = (state) => Effect2.flatMap(SubscriptionRef.get(state), (curre
|
|
|
181
185
|
var superviseReconnect = (reconnect, state, resolved) => Effect2.gen(function* () {
|
|
182
186
|
while (true) {
|
|
183
187
|
const attempt = reconnect.pipe(Effect2.tapError(() => SubscriptionRef.update(state, (current) => ConnectionState.$is("Reconnecting")(current) ? ConnectionState.Reconnecting({ attempt: current.attempt + 1 }) : current)), retryModbus(resolved.policy));
|
|
184
|
-
const result = yield* Effect2.
|
|
185
|
-
if (
|
|
188
|
+
const result = yield* Effect2.result(attempt);
|
|
189
|
+
if (Result.isSuccess(result)) {
|
|
186
190
|
yield* SubscriptionRef.set(state, ConnectionState.Connected());
|
|
187
191
|
return;
|
|
188
192
|
}
|
|
189
|
-
yield* Effect2.logDebug(`Reconnect attempts exhausted: ${result.
|
|
190
|
-
yield* SubscriptionRef.set(state, ConnectionState.Down({ cause: result.
|
|
193
|
+
yield* Effect2.logDebug(`Reconnect attempts exhausted: ${result.failure.message}`);
|
|
194
|
+
yield* SubscriptionRef.set(state, ConnectionState.Down({ cause: result.failure }));
|
|
191
195
|
yield* Effect2.sleep(resolved.resetAfter);
|
|
192
196
|
const probe = yield* SubscriptionRef.modify(state, (current) => ConnectionState.$is("Down")(current) ? [true, ConnectionState.Reconnecting({ attempt: 0 })] : [false, current]);
|
|
193
197
|
if (!probe)
|
|
@@ -195,7 +199,7 @@ var superviseReconnect = (reconnect, state, resolved) => Effect2.gen(function* (
|
|
|
195
199
|
}
|
|
196
200
|
});
|
|
197
201
|
// src/AsciiTransportService.ts
|
|
198
|
-
import {
|
|
202
|
+
import { Context, Layer } from "effect";
|
|
199
203
|
|
|
200
204
|
// src/mocks.ts
|
|
201
205
|
import { Effect as Effect4, Schema, SubscriptionRef as SubscriptionRef2 } from "effect";
|
|
@@ -228,7 +232,7 @@ var makeEffectModbusClient = (client) => ({
|
|
|
228
232
|
});
|
|
229
233
|
var withResilience = (operations, resilience) => {
|
|
230
234
|
const run = (operation) => {
|
|
231
|
-
const attempt = Effect3.
|
|
235
|
+
const attempt = Effect3.andThen(resilience.guard, Effect3.suspend(operation)).pipe(Effect3.tapError((error) => resilience.report(error)));
|
|
232
236
|
return resilience.policy ? retryModbus(resilience.policy)(attempt) : attempt;
|
|
233
237
|
};
|
|
234
238
|
return {
|
|
@@ -266,18 +270,10 @@ var RegisterDefinition = Schema.Struct({
|
|
|
266
270
|
});
|
|
267
271
|
var SlaveDeviceDefinition = Schema.Struct({
|
|
268
272
|
unitId: Schema.Number,
|
|
269
|
-
coils: Schema.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
default: () => []
|
|
274
|
-
}),
|
|
275
|
-
holdingRegisters: Schema.optionalWith(Schema.Array(RegisterDefinition), {
|
|
276
|
-
default: () => []
|
|
277
|
-
}),
|
|
278
|
-
inputRegisters: Schema.optionalWith(Schema.Array(RegisterDefinition), {
|
|
279
|
-
default: () => []
|
|
280
|
-
})
|
|
273
|
+
coils: Schema.Array(CoilDefinition).pipe(Schema.withDecodingDefaultType(Effect4.succeed([]))),
|
|
274
|
+
discreteInputs: Schema.Array(DiscreteInputDefinition).pipe(Schema.withDecodingDefaultType(Effect4.succeed([]))),
|
|
275
|
+
holdingRegisters: Schema.Array(RegisterDefinition).pipe(Schema.withDecodingDefaultType(Effect4.succeed([]))),
|
|
276
|
+
inputRegisters: Schema.Array(RegisterDefinition).pipe(Schema.withDecodingDefaultType(Effect4.succeed([])))
|
|
281
277
|
});
|
|
282
278
|
var SlaveDeviceDefinitions = Schema.Array(SlaveDeviceDefinition);
|
|
283
279
|
var buildCoils = (defs) => {
|
|
@@ -458,7 +454,7 @@ var makeMockTransport = (devices) => {
|
|
|
458
454
|
const connectionState = yield* SubscriptionRef2.make(ConnectionState.Connected());
|
|
459
455
|
const supervised = options.reconnect ? resolveReconnect(options.reconnect) : null;
|
|
460
456
|
const serviceScope = yield* Effect4.scope;
|
|
461
|
-
const reconnectOnce = Effect4.
|
|
457
|
+
const reconnectOnce = Effect4.andThen(Effect4.logDebug("Mock: reconnecting"), Effect4.suspend(() => {
|
|
462
458
|
const injected = options.reconnectFault?.();
|
|
463
459
|
return injected ? Effect4.fail(injected) : Effect4.void;
|
|
464
460
|
}));
|
|
@@ -472,7 +468,7 @@ var makeMockTransport = (devices) => {
|
|
|
472
468
|
yield* Effect4.forkIn(superviseReconnect(reconnectOnce, connectionState, supervised), serviceScope);
|
|
473
469
|
});
|
|
474
470
|
};
|
|
475
|
-
const guard = Effect4.
|
|
471
|
+
const guard = Effect4.andThen(supervised ? guardCircuit(connectionState) : Effect4.void, Effect4.suspend(() => {
|
|
476
472
|
const injected = options.fault?.();
|
|
477
473
|
return injected ? Effect4.fail(injected) : Effect4.void;
|
|
478
474
|
}));
|
|
@@ -494,8 +490,8 @@ var makeMockTransport = (devices) => {
|
|
|
494
490
|
}),
|
|
495
491
|
setRequestTimeout: (_timeoutMs) => Effect4.void,
|
|
496
492
|
clearRequestTimeout: () => Effect4.void,
|
|
497
|
-
reconnect: () => Effect4.
|
|
498
|
-
close: () => Effect4.
|
|
493
|
+
reconnect: () => Effect4.andThen(reconnectOnce, SubscriptionRef2.set(connectionState, ConnectionState.Connected())),
|
|
494
|
+
close: () => Effect4.andThen(Effect4.logDebug("Mock: closing transport"), SubscriptionRef2.set(connectionState, ConnectionState.Disconnected())),
|
|
499
495
|
hasPendingRequests: () => false
|
|
500
496
|
};
|
|
501
497
|
});
|
|
@@ -511,7 +507,7 @@ var singleFlight = (inFlight, work) => Effect5.uninterruptibleMask((restore) =>
|
|
|
511
507
|
onNone: () => [[fresh, true], Option.some(fresh)]
|
|
512
508
|
}));
|
|
513
509
|
if (isLeader) {
|
|
514
|
-
yield* Effect5.
|
|
510
|
+
yield* Effect5.forkDetach(Effect5.interruptible(work).pipe(Effect5.onExit((exit) => Effect5.andThen(Ref.set(inFlight, Option.none()), Deferred.done(deferred, exit)))));
|
|
515
511
|
}
|
|
516
512
|
return yield* restore(Deferred.await(deferred));
|
|
517
513
|
}));
|
|
@@ -533,11 +529,11 @@ function makeTransportScoped(transportKey, openMethod, serviceName, config) {
|
|
|
533
529
|
cause: new Error("Transport has been closed"),
|
|
534
530
|
message: "Transport has been closed"
|
|
535
531
|
});
|
|
536
|
-
const closeOrphan = (t) => Effect5.
|
|
532
|
+
const closeOrphan = (t) => Effect5.forkDetach(Effect5.ignore(Effect5.tryPromise(() => t.close())));
|
|
537
533
|
const openTransport = Effect5.tryPromise({
|
|
538
534
|
try: () => openMethod(TC, openOptions),
|
|
539
535
|
catch: (error) => toModbusError(error)
|
|
540
|
-
}).pipe(Effect5.tap((t) => closed ? closeOrphan(t) : Effect5.
|
|
536
|
+
}).pipe(Effect5.tap((t) => closed ? closeOrphan(t) : Effect5.andThen(Effect5.sync(() => {
|
|
541
537
|
transport = t;
|
|
542
538
|
}), SubscriptionRef3.set(connectionState, ConnectionState.Connected()))));
|
|
543
539
|
const ensureOpen = Effect5.fnUntraced(function* () {
|
|
@@ -557,7 +553,7 @@ function makeTransportScoped(transportKey, openMethod, serviceName, config) {
|
|
|
557
553
|
const t = transport;
|
|
558
554
|
if (!t)
|
|
559
555
|
return SubscriptionRef3.set(connectionState, ConnectionState.Disconnected());
|
|
560
|
-
return Effect5.andThen(Effect5.logDebug(`Closing ${serviceName}`), Effect5.
|
|
556
|
+
return Effect5.andThen(Effect5.logDebug(`Closing ${serviceName}`), Effect5.andThen(Effect5.promise(() => t.close()), SubscriptionRef3.set(connectionState, ConnectionState.Disconnected())));
|
|
561
557
|
});
|
|
562
558
|
const notConnectedMsg = "Transport is not connected. Call withClient() first.";
|
|
563
559
|
const reconnectOnce = Effect5.suspend(() => {
|
|
@@ -665,54 +661,54 @@ function makeTransportScoped(transportKey, openMethod, serviceName, config) {
|
|
|
665
661
|
}
|
|
666
662
|
|
|
667
663
|
// src/AsciiTransportService.ts
|
|
668
|
-
class AsciiTransportService extends
|
|
669
|
-
|
|
670
|
-
|
|
664
|
+
class AsciiTransportService extends Context.Service()("AsciiTransportService") {
|
|
665
|
+
static makeScoped = makeTransportScoped("AsyncAsciiTransport", (TC, options) => TC.open(options), "AsciiTransportService");
|
|
666
|
+
static make = (options) => Layer.effect(AsciiTransportService, AsciiTransportService.makeScoped(options));
|
|
671
667
|
static makeMockTransport = (devices) => {
|
|
672
668
|
const factory = makeMockTransport(devices);
|
|
673
|
-
return (options) => Layer.
|
|
669
|
+
return (options) => Layer.effect(AsciiTransportService, factory(options));
|
|
674
670
|
};
|
|
675
671
|
}
|
|
676
672
|
// src/SerialTransportService.ts
|
|
677
|
-
import { Context, Layer as Layer3 } from "effect";
|
|
673
|
+
import { Context as Context3, Layer as Layer3 } from "effect";
|
|
678
674
|
|
|
679
675
|
// src/RtuTransportService.ts
|
|
680
|
-
import {
|
|
681
|
-
class RtuTransportService extends
|
|
682
|
-
|
|
683
|
-
|
|
676
|
+
import { Context as Context2, Layer as Layer2 } from "effect";
|
|
677
|
+
class RtuTransportService extends Context2.Service()("RtuTransportService") {
|
|
678
|
+
static makeScoped = makeTransportScoped("AsyncRtuTransport", (TC, options) => TC.open(options), "RtuTransportService");
|
|
679
|
+
static make = (options) => Layer2.effect(RtuTransportService, RtuTransportService.makeScoped(options));
|
|
684
680
|
static makeMockTransport = (devices) => {
|
|
685
681
|
const factory = makeMockTransport(devices);
|
|
686
|
-
return (options) => Layer2.
|
|
682
|
+
return (options) => Layer2.effect(RtuTransportService, factory(options));
|
|
687
683
|
};
|
|
688
684
|
}
|
|
689
685
|
|
|
690
686
|
// src/SerialTransportService.ts
|
|
691
|
-
class SerialTransportService extends
|
|
687
|
+
class SerialTransportService extends Context3.Service()("SerialTransportService") {
|
|
692
688
|
static fromAscii(options) {
|
|
693
|
-
return Layer3.
|
|
689
|
+
return Layer3.flatMap(AsciiTransportService.make(options), (context) => Layer3.succeed(SerialTransportService, Context3.get(context, AsciiTransportService)));
|
|
694
690
|
}
|
|
695
691
|
static fromRtu(options) {
|
|
696
|
-
return Layer3.
|
|
692
|
+
return Layer3.flatMap(RtuTransportService.make(options), (context) => Layer3.succeed(SerialTransportService, Context3.get(context, RtuTransportService)));
|
|
697
693
|
}
|
|
698
694
|
static makeMockTransport = (devices) => {
|
|
699
695
|
const factory = makeMockTransport(devices);
|
|
700
|
-
return (options) => Layer3.
|
|
696
|
+
return (options) => Layer3.effect(SerialTransportService, factory(options));
|
|
701
697
|
};
|
|
702
698
|
}
|
|
703
699
|
// src/TcpTransportService.ts
|
|
704
|
-
import {
|
|
705
|
-
class TcpTransportService extends
|
|
706
|
-
|
|
707
|
-
|
|
700
|
+
import { Context as Context4, Layer as Layer4 } from "effect";
|
|
701
|
+
class TcpTransportService extends Context4.Service()("TcpTransportService") {
|
|
702
|
+
static makeScoped = makeTransportScoped("AsyncTcpTransport", (TC, options) => TC.connect(options), "TcpTransportService");
|
|
703
|
+
static make = (options) => Layer4.effect(TcpTransportService, TcpTransportService.makeScoped(options));
|
|
708
704
|
static makeMockTransport = (devices) => {
|
|
709
705
|
const factory = makeMockTransport(devices);
|
|
710
|
-
return (options) => Layer4.
|
|
706
|
+
return (options) => Layer4.effect(TcpTransportService, factory(options));
|
|
711
707
|
};
|
|
712
708
|
}
|
|
713
709
|
// src/SerialModbusServerService.ts
|
|
714
710
|
import { Effect as Effect9, Layer as Layer5 } from "effect";
|
|
715
|
-
var serialRtuServerLayer = (options, handlers) => Layer5.
|
|
711
|
+
var serialRtuServerLayer = (options, handlers) => Layer5.effectDiscard(Effect9.gen(function* () {
|
|
716
712
|
const { AsyncSerialModbusServer } = yield* Effect9.promise(() => import("modbus-rs"));
|
|
717
713
|
const server = yield* Effect9.tryPromise({
|
|
718
714
|
try: () => AsyncSerialModbusServer.bindRtu(options, handlers),
|
|
@@ -722,9 +718,9 @@ var serialRtuServerLayer = (options, handlers) => Layer5.scopedDiscard(Effect9.g
|
|
|
722
718
|
yield* Effect9.addFinalizer(() => Effect9.logDebug("Serial RTU server shutting down").pipe(Effect9.andThen(Effect9.tryPromise({
|
|
723
719
|
try: () => server.shutdown(),
|
|
724
720
|
catch: (error) => toModbusError(error)
|
|
725
|
-
})), Effect9.
|
|
721
|
+
})), Effect9.catch(() => Effect9.void)));
|
|
726
722
|
}));
|
|
727
|
-
var serialAsciiServerLayer = (options, handlers) => Layer5.
|
|
723
|
+
var serialAsciiServerLayer = (options, handlers) => Layer5.effectDiscard(Effect9.gen(function* () {
|
|
728
724
|
const { AsyncSerialModbusServer } = yield* Effect9.promise(() => import("modbus-rs"));
|
|
729
725
|
const server = yield* Effect9.tryPromise({
|
|
730
726
|
try: () => AsyncSerialModbusServer.bindAscii(options, handlers),
|
|
@@ -734,11 +730,11 @@ var serialAsciiServerLayer = (options, handlers) => Layer5.scopedDiscard(Effect9
|
|
|
734
730
|
yield* Effect9.addFinalizer(() => Effect9.logDebug("Serial ASCII server shutting down").pipe(Effect9.andThen(Effect9.tryPromise({
|
|
735
731
|
try: () => server.shutdown(),
|
|
736
732
|
catch: (error) => toModbusError(error)
|
|
737
|
-
})), Effect9.
|
|
733
|
+
})), Effect9.catch(() => Effect9.void)));
|
|
738
734
|
}));
|
|
739
735
|
// src/TcpModbusServerService.ts
|
|
740
736
|
import { Effect as Effect10, Layer as Layer6 } from "effect";
|
|
741
|
-
var tcpServerLayer = (options, handlers) => Layer6.
|
|
737
|
+
var tcpServerLayer = (options, handlers) => Layer6.effectDiscard(Effect10.gen(function* () {
|
|
742
738
|
const { AsyncTcpModbusServer } = yield* Effect10.promise(() => import("modbus-rs"));
|
|
743
739
|
const server = yield* Effect10.tryPromise({
|
|
744
740
|
try: () => AsyncTcpModbusServer.bind(options, handlers),
|
|
@@ -748,11 +744,11 @@ var tcpServerLayer = (options, handlers) => Layer6.scopedDiscard(Effect10.gen(fu
|
|
|
748
744
|
yield* Effect10.addFinalizer(() => Effect10.logDebug("TCP server shutting down").pipe(Effect10.andThen(Effect10.tryPromise({
|
|
749
745
|
try: () => server.shutdown(),
|
|
750
746
|
catch: (error) => toModbusError(error)
|
|
751
|
-
})), Effect10.
|
|
747
|
+
})), Effect10.catch(() => Effect10.void)));
|
|
752
748
|
}));
|
|
753
749
|
// src/TcpGatewayService.ts
|
|
754
750
|
import { Effect as Effect11, Layer as Layer7 } from "effect";
|
|
755
|
-
var tcpGatewayLayer = (options, gatewayConfig) => Layer7.
|
|
751
|
+
var tcpGatewayLayer = (options, gatewayConfig) => Layer7.effectDiscard(Effect11.gen(function* () {
|
|
756
752
|
const { AsyncTcpGateway } = yield* Effect11.promise(() => import("modbus-rs"));
|
|
757
753
|
const gateway = yield* Effect11.tryPromise({
|
|
758
754
|
try: () => AsyncTcpGateway.bind(options, gatewayConfig),
|
|
@@ -762,50 +758,50 @@ var tcpGatewayLayer = (options, gatewayConfig) => Layer7.scopedDiscard(Effect11.
|
|
|
762
758
|
yield* Effect11.addFinalizer(() => Effect11.logDebug("TCP gateway shutting down").pipe(Effect11.andThen(Effect11.tryPromise({
|
|
763
759
|
try: () => gateway.shutdown(),
|
|
764
760
|
catch: (error) => toModbusError(error)
|
|
765
|
-
})), Effect11.
|
|
761
|
+
})), Effect11.catch(() => Effect11.void)));
|
|
766
762
|
}));
|
|
767
763
|
// src/WasmWsTransportService.ts
|
|
768
|
-
import {
|
|
769
|
-
class WasmWsTransportService extends
|
|
770
|
-
|
|
771
|
-
|
|
764
|
+
import { Context as Context5, Layer as Layer8 } from "effect";
|
|
765
|
+
class WasmWsTransportService extends Context5.Service()("WasmWsTransportService") {
|
|
766
|
+
static makeScoped = makeTransportScoped("WasmWsTransport", (TC, options) => TC.connect(options), "WasmWsTransportService", { moduleSpecifier: "modbus-rs/web" });
|
|
767
|
+
static make = (options) => Layer8.effect(WasmWsTransportService, WasmWsTransportService.makeScoped(options));
|
|
772
768
|
static makeMockTransport = (devices) => {
|
|
773
769
|
const factory = makeMockTransport(devices);
|
|
774
|
-
return (options) => Layer8.
|
|
770
|
+
return (options) => Layer8.effect(WasmWsTransportService, factory(options));
|
|
775
771
|
};
|
|
776
772
|
}
|
|
777
773
|
// src/WasmRtuTransportService.ts
|
|
778
|
-
import {
|
|
779
|
-
class WasmRtuTransportService extends
|
|
780
|
-
|
|
781
|
-
|
|
774
|
+
import { Context as Context6, Layer as Layer9 } from "effect";
|
|
775
|
+
class WasmRtuTransportService extends Context6.Service()("WasmRtuTransportService") {
|
|
776
|
+
static makeScoped = makeTransportScoped("WasmRtuTransport", (TC, { port, ...rest }) => TC.open(port, rest), "WasmRtuTransportService", { moduleSpecifier: "modbus-rs/web" });
|
|
777
|
+
static make = (options) => Layer9.effect(WasmRtuTransportService, WasmRtuTransportService.makeScoped(options));
|
|
782
778
|
static makeMockTransport = (devices) => {
|
|
783
779
|
const factory = makeMockTransport(devices);
|
|
784
|
-
return (options) => Layer9.
|
|
780
|
+
return (options) => Layer9.effect(WasmRtuTransportService, factory(options));
|
|
785
781
|
};
|
|
786
782
|
}
|
|
787
783
|
// src/WasmAsciiTransportService.ts
|
|
788
|
-
import {
|
|
789
|
-
class WasmAsciiTransportService extends
|
|
790
|
-
|
|
791
|
-
|
|
784
|
+
import { Context as Context7, Layer as Layer10 } from "effect";
|
|
785
|
+
class WasmAsciiTransportService extends Context7.Service()("WasmAsciiTransportService") {
|
|
786
|
+
static makeScoped = makeTransportScoped("WasmAsciiTransport", (TC, { port, ...rest }) => TC.open(port, rest), "WasmAsciiTransportService", { moduleSpecifier: "modbus-rs/web" });
|
|
787
|
+
static make = (options) => Layer10.effect(WasmAsciiTransportService, WasmAsciiTransportService.makeScoped(options));
|
|
792
788
|
static makeMockTransport = (devices) => {
|
|
793
789
|
const factory = makeMockTransport(devices);
|
|
794
|
-
return (options) => Layer10.
|
|
790
|
+
return (options) => Layer10.effect(WasmAsciiTransportService, factory(options));
|
|
795
791
|
};
|
|
796
792
|
}
|
|
797
793
|
// src/WasmSerialTransportService.ts
|
|
798
|
-
import { Context as
|
|
799
|
-
class WasmSerialTransportService extends
|
|
794
|
+
import { Context as Context8, Layer as Layer11 } from "effect";
|
|
795
|
+
class WasmSerialTransportService extends Context8.Service()("WasmSerialTransportService") {
|
|
800
796
|
static fromAscii(options) {
|
|
801
|
-
return Layer11.
|
|
797
|
+
return Layer11.flatMap(WasmAsciiTransportService.make(options), (context) => Layer11.succeed(WasmSerialTransportService, Context8.get(context, WasmAsciiTransportService)));
|
|
802
798
|
}
|
|
803
799
|
static fromRtu(options) {
|
|
804
|
-
return Layer11.
|
|
800
|
+
return Layer11.flatMap(WasmRtuTransportService.make(options), (context) => Layer11.succeed(WasmSerialTransportService, Context8.get(context, WasmRtuTransportService)));
|
|
805
801
|
}
|
|
806
802
|
static makeMockTransport = (devices) => {
|
|
807
803
|
const factory = makeMockTransport(devices);
|
|
808
|
-
return (options) => Layer11.
|
|
804
|
+
return (options) => Layer11.effect(WasmSerialTransportService, factory(options));
|
|
809
805
|
};
|
|
810
806
|
}
|
|
811
807
|
// src/WasmSerialPort.ts
|
|
@@ -819,7 +815,7 @@ var requestSerialPort = () => Effect15.tryPromise({
|
|
|
819
815
|
});
|
|
820
816
|
// src/WasmTcpServerService.ts
|
|
821
817
|
import { Effect as Effect16, Layer as Layer12 } from "effect";
|
|
822
|
-
var wasmWsServerLayer = (options, handlers) => Layer12.
|
|
818
|
+
var wasmWsServerLayer = (options, handlers) => Layer12.effectDiscard(Effect16.gen(function* () {
|
|
823
819
|
const { WasmWsModbusServer } = yield* Effect16.promise(() => import("modbus-rs/web"));
|
|
824
820
|
const server = yield* Effect16.tryPromise({
|
|
825
821
|
try: () => WasmWsModbusServer.bind(options, handlers),
|
|
@@ -829,15 +825,15 @@ var wasmWsServerLayer = (options, handlers) => Layer12.scopedDiscard(Effect16.ge
|
|
|
829
825
|
yield* Effect16.forkScoped(Effect16.tryPromise({
|
|
830
826
|
try: () => server.serve(),
|
|
831
827
|
catch: (error) => toModbusError(error)
|
|
832
|
-
}).pipe(Effect16.
|
|
828
|
+
}).pipe(Effect16.catch((error) => Effect16.logError("WASM WS server loop ended", error))));
|
|
833
829
|
yield* Effect16.addFinalizer(() => Effect16.logDebug("WASM WS server shutting down").pipe(Effect16.andThen(Effect16.tryPromise({
|
|
834
830
|
try: () => server.shutdown(),
|
|
835
831
|
catch: (error) => toModbusError(error)
|
|
836
|
-
})), Effect16.
|
|
832
|
+
})), Effect16.catch(() => Effect16.void)));
|
|
837
833
|
}));
|
|
838
834
|
// src/WasmSerialModbusServerService.ts
|
|
839
835
|
import { Effect as Effect17, Layer as Layer13 } from "effect";
|
|
840
|
-
var wasmSerialRtuServerLayer = (options, handlers) => Layer13.
|
|
836
|
+
var wasmSerialRtuServerLayer = (options, handlers) => Layer13.effectDiscard(Effect17.gen(function* () {
|
|
841
837
|
const { WasmSerialModbusServer } = yield* Effect17.promise(() => import("modbus-rs/web"));
|
|
842
838
|
const server = yield* Effect17.tryPromise({
|
|
843
839
|
try: () => WasmSerialModbusServer.bindRtu(options, handlers),
|
|
@@ -847,13 +843,13 @@ var wasmSerialRtuServerLayer = (options, handlers) => Layer13.scopedDiscard(Effe
|
|
|
847
843
|
yield* Effect17.forkScoped(Effect17.tryPromise({
|
|
848
844
|
try: () => server.serve(),
|
|
849
845
|
catch: (error) => toModbusError(error)
|
|
850
|
-
}).pipe(Effect17.
|
|
846
|
+
}).pipe(Effect17.catch((error) => Effect17.logError("WASM serial RTU server loop ended", error))));
|
|
851
847
|
yield* Effect17.addFinalizer(() => Effect17.logDebug("WASM serial RTU server shutting down").pipe(Effect17.andThen(Effect17.tryPromise({
|
|
852
848
|
try: () => server.shutdown(),
|
|
853
849
|
catch: (error) => toModbusError(error)
|
|
854
|
-
})), Effect17.
|
|
850
|
+
})), Effect17.catch(() => Effect17.void)));
|
|
855
851
|
}));
|
|
856
|
-
var wasmSerialAsciiServerLayer = (options, handlers) => Layer13.
|
|
852
|
+
var wasmSerialAsciiServerLayer = (options, handlers) => Layer13.effectDiscard(Effect17.gen(function* () {
|
|
857
853
|
const { WasmSerialModbusServer } = yield* Effect17.promise(() => import("modbus-rs/web"));
|
|
858
854
|
const server = yield* Effect17.tryPromise({
|
|
859
855
|
try: () => WasmSerialModbusServer.bindAscii(options, handlers),
|
|
@@ -863,41 +859,41 @@ var wasmSerialAsciiServerLayer = (options, handlers) => Layer13.scopedDiscard(Ef
|
|
|
863
859
|
yield* Effect17.forkScoped(Effect17.tryPromise({
|
|
864
860
|
try: () => server.serve(),
|
|
865
861
|
catch: (error) => toModbusError(error)
|
|
866
|
-
}).pipe(Effect17.
|
|
862
|
+
}).pipe(Effect17.catch((error) => Effect17.logError("WASM serial ASCII server loop ended", error))));
|
|
867
863
|
yield* Effect17.addFinalizer(() => Effect17.logDebug("WASM serial ASCII server shutting down").pipe(Effect17.andThen(Effect17.tryPromise({
|
|
868
864
|
try: () => server.shutdown(),
|
|
869
865
|
catch: (error) => toModbusError(error)
|
|
870
|
-
})), Effect17.
|
|
866
|
+
})), Effect17.catch(() => Effect17.void)));
|
|
871
867
|
}));
|
|
872
868
|
export {
|
|
873
|
-
|
|
874
|
-
wasmSerialRtuServerLayer,
|
|
875
|
-
wasmSerialAsciiServerLayer,
|
|
876
|
-
toModbusError,
|
|
877
|
-
tcpServerLayer,
|
|
878
|
-
tcpGatewayLayer,
|
|
879
|
-
serialRtuServerLayer,
|
|
880
|
-
serialAsciiServerLayer,
|
|
881
|
-
retryableExceptionCodes,
|
|
882
|
-
retryModbus,
|
|
883
|
-
requestSerialPort,
|
|
884
|
-
makeRetryPolicy,
|
|
885
|
-
WasmWsTransportService,
|
|
886
|
-
WasmSerialTransportService,
|
|
887
|
-
WasmRtuTransportService,
|
|
888
|
-
WasmAsciiTransportService,
|
|
889
|
-
TcpTransportService,
|
|
890
|
-
SerialTransportService,
|
|
891
|
-
RtuTransportService,
|
|
892
|
-
RetryPolicies,
|
|
893
|
-
ModbusTransportError,
|
|
894
|
-
ModbusTimeoutError,
|
|
895
|
-
ModbusNotConnectedError,
|
|
896
|
-
ModbusInvalidArgumentError,
|
|
897
|
-
ModbusInternalError,
|
|
898
|
-
ModbusExceptionError,
|
|
899
|
-
ModbusConnectionClosedError,
|
|
900
|
-
ModbusCircuitOpenError,
|
|
869
|
+
AsciiTransportService,
|
|
901
870
|
ConnectionState,
|
|
902
|
-
|
|
871
|
+
ModbusCircuitOpenError,
|
|
872
|
+
ModbusConnectionClosedError,
|
|
873
|
+
ModbusExceptionError,
|
|
874
|
+
ModbusInternalError,
|
|
875
|
+
ModbusInvalidArgumentError,
|
|
876
|
+
ModbusNotConnectedError,
|
|
877
|
+
ModbusTimeoutError,
|
|
878
|
+
ModbusTransportError,
|
|
879
|
+
RetryPolicies,
|
|
880
|
+
RtuTransportService,
|
|
881
|
+
SerialTransportService,
|
|
882
|
+
TcpTransportService,
|
|
883
|
+
WasmAsciiTransportService,
|
|
884
|
+
WasmRtuTransportService,
|
|
885
|
+
WasmSerialTransportService,
|
|
886
|
+
WasmWsTransportService,
|
|
887
|
+
makeRetryPolicy,
|
|
888
|
+
requestSerialPort,
|
|
889
|
+
retryModbus,
|
|
890
|
+
retryableExceptionCodes,
|
|
891
|
+
serialAsciiServerLayer,
|
|
892
|
+
serialRtuServerLayer,
|
|
893
|
+
tcpGatewayLayer,
|
|
894
|
+
tcpServerLayer,
|
|
895
|
+
toModbusError,
|
|
896
|
+
wasmSerialAsciiServerLayer,
|
|
897
|
+
wasmSerialRtuServerLayer,
|
|
898
|
+
wasmWsServerLayer
|
|
903
899
|
};
|