@flux-control/effect-modbus-rs 0.1.0 → 0.2.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/dist/index.js CHANGED
@@ -54,216 +54,11 @@ var toModbusError = (cause) => {
54
54
  // src/AsciiTransportService.ts
55
55
  import { Effect as Effect4, Layer } from "effect";
56
56
 
57
- // src/shared-transport.ts
58
- import { Effect as Effect2, Exit, Scope } from "effect";
59
-
60
- // src/modbus-client.ts
61
- import { Effect } from "effect";
62
- var makeEffectModbusClient = (client) => ({
63
- readHoldingRegisters: (opts) => Effect.tryPromise({
64
- try: () => client.readHoldingRegisters(opts),
65
- catch: (error) => toModbusError(error)
66
- }),
67
- readInputRegisters: (opts) => Effect.tryPromise({
68
- try: () => client.readInputRegisters(opts),
69
- catch: (error) => toModbusError(error)
70
- }),
71
- writeSingleRegister: (opts) => Effect.tryPromise({
72
- try: () => client.writeSingleRegister(opts),
73
- catch: (error) => toModbusError(error)
74
- }),
75
- writeMultipleRegisters: (opts) => Effect.tryPromise({
76
- try: () => client.writeMultipleRegisters(opts),
77
- catch: (error) => toModbusError(error)
78
- }),
79
- readWriteMultipleRegisters: (opts) => Effect.tryPromise({
80
- try: () => client.readWriteMultipleRegisters(opts),
81
- catch: (error) => toModbusError(error)
82
- }),
83
- readCoils: (opts) => Effect.tryPromise({
84
- try: () => client.readCoils(opts),
85
- catch: (error) => toModbusError(error)
86
- }),
87
- writeSingleCoil: (opts) => Effect.tryPromise({
88
- try: () => client.writeSingleCoil(opts),
89
- catch: (error) => toModbusError(error)
90
- }),
91
- writeMultipleCoils: (opts) => Effect.tryPromise({
92
- try: () => client.writeMultipleCoils(opts),
93
- catch: (error) => toModbusError(error)
94
- }),
95
- readDiscreteInputs: (opts) => Effect.tryPromise({
96
- try: () => client.readDiscreteInputs(opts),
97
- catch: (error) => toModbusError(error)
98
- }),
99
- readFifoQueue: (opts) => Effect.tryPromise({
100
- try: () => client.readFifoQueue(opts),
101
- catch: (error) => toModbusError(error)
102
- }),
103
- readFileRecord: (opts) => Effect.tryPromise({
104
- try: () => client.readFileRecord(opts),
105
- catch: (error) => toModbusError(error)
106
- }),
107
- writeFileRecord: (opts) => Effect.tryPromise({
108
- try: () => client.writeFileRecord(opts),
109
- catch: (error) => toModbusError(error)
110
- }),
111
- readExceptionStatus: () => Effect.tryPromise({
112
- try: () => client.readExceptionStatus(),
113
- catch: (error) => toModbusError(error)
114
- }),
115
- diagnostics: (opts) => Effect.tryPromise({
116
- try: () => client.diagnostics(opts),
117
- catch: (error) => toModbusError(error)
118
- }),
119
- readDeviceIdentification: (opts) => Effect.tryPromise({
120
- try: () => client.readDeviceIdentification(opts),
121
- catch: (error) => toModbusError(error)
122
- })
123
- });
124
-
125
- // src/shared-transport.ts
126
- function makeTransportScoped(transportKey, openMethod, serviceName) {
127
- return Effect2.fnUntraced(function* (options) {
128
- const mod = yield* Effect2.promise(() => import("modbus-rs"));
129
- const TC = mod[transportKey];
130
- let transport = null;
131
- let connectPromise = null;
132
- let reconnectPromise = null;
133
- const clientSet = new Map;
134
- let closed = false;
135
- const ensureOpen = Effect2.fnUntraced(function* () {
136
- if (transport) {
137
- if (closed) {
138
- return yield* new ModbusNotConnectedError({
139
- cause: new Error("Transport has been closed"),
140
- message: "Transport has been closed"
141
- });
142
- }
143
- return transport;
144
- }
145
- if (closed) {
146
- return yield* new ModbusNotConnectedError({
147
- cause: new Error("Transport has been closed"),
148
- message: "Transport has been closed"
149
- });
150
- }
151
- if (!connectPromise) {
152
- connectPromise = openMethod(TC, options);
153
- }
154
- const t = yield* Effect2.tryPromise({
155
- try: () => connectPromise,
156
- catch: (error) => toModbusError(error)
157
- }).pipe(Effect2.catchAll((err) => {
158
- connectPromise = null;
159
- return Effect2.fail(err);
160
- }));
161
- if (closed) {
162
- connectPromise = null;
163
- yield* Effect2.fork(Effect2.promise(() => t.close()).pipe(Effect2.catchAll(() => Effect2.void)));
164
- return yield* new ModbusNotConnectedError({
165
- cause: new Error("Transport has been closed"),
166
- message: "Transport has been closed"
167
- });
168
- }
169
- transport = t;
170
- return t;
171
- });
172
- yield* Effect2.addFinalizer(() => {
173
- if (closed)
174
- return Effect2.void;
175
- closed = true;
176
- const t = transport;
177
- if (!t)
178
- return Effect2.void;
179
- return Effect2.andThen(Effect2.logDebug(`Closing ${serviceName}`), Effect2.promise(() => t.close()));
180
- });
181
- const notConnectedMsg = "Transport is not connected. Call withClient() first.";
182
- return {
183
- withClient: Effect2.fnUntraced(function* (unitId) {
184
- const t = yield* ensureOpen();
185
- let client = clientSet.get(unitId);
186
- if (!client) {
187
- client = yield* Effect2.try({
188
- try: () => t.createClient({ unitId }),
189
- catch: (error) => toModbusError(error)
190
- });
191
- clientSet.set(unitId, client);
192
- }
193
- return makeEffectModbusClient(client);
194
- }),
195
- setRequestTimeout: Effect2.fnUntraced(function* (timeoutMs) {
196
- const t = transport;
197
- if (!t || closed) {
198
- return yield* new ModbusNotConnectedError({
199
- cause: new Error(notConnectedMsg),
200
- message: notConnectedMsg
201
- });
202
- }
203
- t.setRequestTimeout(timeoutMs);
204
- }),
205
- clearRequestTimeout: Effect2.fnUntraced(function* () {
206
- const t = transport;
207
- if (!t || closed) {
208
- return yield* new ModbusNotConnectedError({
209
- cause: new Error(notConnectedMsg),
210
- message: notConnectedMsg
211
- });
212
- }
213
- t.clearRequestTimeout();
214
- }),
215
- reconnect: Effect2.fnUntraced(function* () {
216
- if (closed) {
217
- return yield* new ModbusNotConnectedError({
218
- cause: new Error("Transport has been closed"),
219
- message: "Transport has been closed"
220
- });
221
- }
222
- if (transport) {
223
- if (!reconnectPromise) {
224
- reconnectPromise = transport.reconnect().then(() => {
225
- reconnectPromise = null;
226
- }).catch((err) => {
227
- reconnectPromise = null;
228
- throw err;
229
- });
230
- }
231
- yield* Effect2.tryPromise({
232
- try: () => reconnectPromise,
233
- catch: (error) => toModbusError(error)
234
- });
235
- } else {
236
- yield* ensureOpen();
237
- }
238
- }),
239
- close: Effect2.fnUntraced(function* () {
240
- if (closed)
241
- return;
242
- closed = true;
243
- const t = transport;
244
- if (t) {
245
- yield* Effect2.tryPromise({
246
- try: () => t.close(),
247
- catch: (error) => toModbusError(error)
248
- });
249
- }
250
- const scope = yield* Effect2.scope;
251
- yield* Scope.close(scope, Exit.void);
252
- }),
253
- hasPendingRequests: () => {
254
- if (closed)
255
- return false;
256
- const t = transport;
257
- if (!t)
258
- return false;
259
- return t.pendingRequests;
260
- }
261
- };
262
- });
263
- }
264
-
265
57
  // src/mocks.ts
266
- import { Effect as Effect3, Schema } from "effect";
58
+ import { Effect, Schema } from "effect";
59
+ import {
60
+ CoilState
61
+ } from "modbus-rs";
267
62
  var CoilDefinition = Schema.Struct({
268
63
  address: Schema.Number,
269
64
  default: Schema.Boolean
@@ -317,30 +112,30 @@ var failOutOfRange = (label, address, quantity) => new ModbusInvalidArgumentErro
317
112
  message: quantity !== undefined ? `${label} read out of range: address=${address}, quantity=${quantity}` : `${label} write out of range: address=${address}`
318
113
  });
319
114
  var makeMockModbusClient = (state, unitId) => ({
320
- readCoils: Effect3.fnUntraced(function* (opts) {
321
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} readCoils`, opts);
115
+ readCoils: Effect.fnUntraced(function* (opts) {
116
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} readCoils`, opts);
322
117
  if (opts.address + opts.quantity > state.maxCoilAddress + 1) {
323
118
  return yield* failOutOfRange("Coil", opts.address, opts.quantity);
324
119
  }
325
120
  const result = [];
326
121
  for (let i = opts.address;i < opts.address + opts.quantity; i++) {
327
- result.push(state.coils.get(i) ?? false);
122
+ result.push(state.coils.get(i) ?? false ? CoilState.On : CoilState.Off);
328
123
  }
329
124
  return result;
330
125
  }),
331
- readDiscreteInputs: Effect3.fnUntraced(function* (opts) {
332
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} readDiscreteInputs`, opts);
126
+ readDiscreteInputs: Effect.fnUntraced(function* (opts) {
127
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} readDiscreteInputs`, opts);
333
128
  if (opts.address + opts.quantity > state.maxDiscreteAddress + 1) {
334
129
  return yield* failOutOfRange("DiscreteInput", opts.address, opts.quantity);
335
130
  }
336
131
  const result = [];
337
132
  for (let i = opts.address;i < opts.address + opts.quantity; i++) {
338
- result.push(state.discreteInputs.get(i) ?? false);
133
+ result.push(state.discreteInputs.get(i) ?? false ? CoilState.On : CoilState.Off);
339
134
  }
340
135
  return result;
341
136
  }),
342
- readHoldingRegisters: Effect3.fnUntraced(function* (opts) {
343
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} readHoldingRegisters`, opts);
137
+ readHoldingRegisters: Effect.fnUntraced(function* (opts) {
138
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} readHoldingRegisters`, opts);
344
139
  if (opts.address + opts.quantity > state.maxHoldingAddress + 1) {
345
140
  return yield* failOutOfRange("HoldingRegister", opts.address, opts.quantity);
346
141
  }
@@ -348,10 +143,10 @@ var makeMockModbusClient = (state, unitId) => ({
348
143
  for (let i = opts.address;i < opts.address + opts.quantity; i++) {
349
144
  result.push(state.holdingRegisters.get(i) ?? 0);
350
145
  }
351
- return result;
146
+ return new Uint16Array(result);
352
147
  }),
353
- readInputRegisters: Effect3.fnUntraced(function* (opts) {
354
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} readInputRegisters`, opts);
148
+ readInputRegisters: Effect.fnUntraced(function* (opts) {
149
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} readInputRegisters`, opts);
355
150
  if (opts.address + opts.quantity > state.maxInputAddress + 1) {
356
151
  return yield* failOutOfRange("InputRegister", opts.address, opts.quantity);
357
152
  }
@@ -359,33 +154,33 @@ var makeMockModbusClient = (state, unitId) => ({
359
154
  for (let i = opts.address;i < opts.address + opts.quantity; i++) {
360
155
  result.push(state.inputRegisters.get(i) ?? 0);
361
156
  }
362
- return result;
157
+ return new Uint16Array(result);
363
158
  }),
364
- writeSingleCoil: Effect3.fnUntraced(function* (opts) {
365
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} writeSingleCoil`, opts);
159
+ writeSingleCoil: Effect.fnUntraced(function* (opts) {
160
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} writeSingleCoil`, opts);
366
161
  if (opts.address > state.maxCoilAddress) {
367
162
  return yield* failOutOfRange("Coil", opts.address);
368
163
  }
369
- state.coils.set(opts.address, opts.value);
164
+ state.coils.set(opts.address, opts.value === CoilState.On);
370
165
  }),
371
- writeMultipleCoils: Effect3.fnUntraced(function* (opts) {
372
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} writeMultipleCoils`, opts);
166
+ writeMultipleCoils: Effect.fnUntraced(function* (opts) {
167
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} writeMultipleCoils`, opts);
373
168
  if (opts.address + opts.values.length > state.maxCoilAddress + 1) {
374
169
  return yield* failOutOfRange("Coil", opts.address, opts.values.length);
375
170
  }
376
171
  for (let i = 0;i < opts.values.length; i++) {
377
- state.coils.set(opts.address + i, opts.values[i]);
172
+ state.coils.set(opts.address + i, opts.values[i] === CoilState.On);
378
173
  }
379
174
  }),
380
- writeSingleRegister: Effect3.fnUntraced(function* (opts) {
381
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} writeSingleRegister`, opts);
175
+ writeSingleRegister: Effect.fnUntraced(function* (opts) {
176
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} writeSingleRegister`, opts);
382
177
  if (opts.address > state.maxHoldingAddress) {
383
178
  return yield* failOutOfRange("HoldingRegister", opts.address);
384
179
  }
385
180
  state.holdingRegisters.set(opts.address, opts.value);
386
181
  }),
387
- writeMultipleRegisters: Effect3.fnUntraced(function* (opts) {
388
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} writeMultipleRegisters`, opts);
182
+ writeMultipleRegisters: Effect.fnUntraced(function* (opts) {
183
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} writeMultipleRegisters`, opts);
389
184
  if (opts.address + opts.values.length > state.maxHoldingAddress + 1) {
390
185
  return yield* failOutOfRange("HoldingRegister", opts.address, opts.values.length);
391
186
  }
@@ -393,8 +188,8 @@ var makeMockModbusClient = (state, unitId) => ({
393
188
  state.holdingRegisters.set(opts.address + i, opts.values[i]);
394
189
  }
395
190
  }),
396
- readWriteMultipleRegisters: Effect3.fnUntraced(function* (opts) {
397
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} readWriteMultipleRegisters`, opts);
191
+ readWriteMultipleRegisters: Effect.fnUntraced(function* (opts) {
192
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} readWriteMultipleRegisters`, opts);
398
193
  if (opts.writeAddress + opts.writeValues.length > state.maxHoldingAddress + 1) {
399
194
  return yield* failOutOfRange("HoldingRegister", opts.writeAddress, opts.writeValues.length);
400
195
  }
@@ -408,36 +203,36 @@ var makeMockModbusClient = (state, unitId) => ({
408
203
  for (let i = opts.readAddress;i < opts.readAddress + opts.readQuantity; i++) {
409
204
  result.push(state.holdingRegisters.get(i) ?? 0);
410
205
  }
411
- return result;
206
+ return new Uint16Array(result);
412
207
  }),
413
- readFifoQueue: Effect3.fnUntraced(function* (_opts) {
208
+ readFifoQueue: Effect.fnUntraced(function* (_opts) {
414
209
  return yield* new ModbusInvalidArgumentError({
415
210
  cause: new Error("FIFO queue not yet supported in mock"),
416
211
  message: "FIFO queue not yet supported in mock"
417
212
  });
418
213
  }),
419
- readFileRecord: Effect3.fnUntraced(function* (_opts) {
214
+ readFileRecord: Effect.fnUntraced(function* (_opts) {
420
215
  return yield* new ModbusInvalidArgumentError({
421
216
  cause: new Error("File records not yet supported in mock"),
422
217
  message: "File records not yet supported in mock"
423
218
  });
424
219
  }),
425
- writeFileRecord: Effect3.fnUntraced(function* (_opts) {
220
+ writeFileRecord: Effect.fnUntraced(function* (_opts) {
426
221
  return yield* new ModbusInvalidArgumentError({
427
222
  cause: new Error("File records not yet supported in mock"),
428
223
  message: "File records not yet supported in mock"
429
224
  });
430
225
  }),
431
- readExceptionStatus: Effect3.fnUntraced(function* () {
432
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} readExceptionStatus`);
226
+ readExceptionStatus: Effect.fnUntraced(function* () {
227
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} readExceptionStatus`);
433
228
  return 0;
434
229
  }),
435
- diagnostics: Effect3.fnUntraced(function* (opts) {
436
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} diagnostics`, opts);
437
- return { subFunction: opts.subFunction, data: [] };
230
+ diagnostics: Effect.fnUntraced(function* (opts) {
231
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} diagnostics`, opts);
232
+ return { subFunction: opts.subFunction, data: new Uint16Array };
438
233
  }),
439
- readDeviceIdentification: Effect3.fnUntraced(function* (opts) {
440
- yield* Effect3.logDebug(`[Mock] unitId=${unitId} readDeviceIdentification`, opts);
234
+ readDeviceIdentification: Effect.fnUntraced(function* (opts) {
235
+ yield* Effect.logDebug(`[Mock] unitId=${unitId} readDeviceIdentification`, opts);
441
236
  return {
442
237
  conformityLevel: 1,
443
238
  moreFollows: false,
@@ -465,10 +260,10 @@ var makeMockTransport = (devices) => {
465
260
  maxInputAddress: input.maxAddress
466
261
  });
467
262
  }
468
- return (_options) => Effect3.gen(function* () {
469
- yield* Effect3.logDebug("Mock transport opened with devices:", deviceDefs);
263
+ return (_options) => Effect.gen(function* () {
264
+ yield* Effect.logDebug("Mock transport opened with devices:", deviceDefs);
470
265
  return {
471
- withClient: Effect3.fnUntraced(function* (unitId) {
266
+ withClient: Effect.fnUntraced(function* (unitId) {
472
267
  const state = deviceStates.get(unitId);
473
268
  if (!state) {
474
269
  return yield* new ModbusInvalidArgumentError({
@@ -478,15 +273,182 @@ var makeMockTransport = (devices) => {
478
273
  }
479
274
  return makeMockModbusClient(state, unitId);
480
275
  }),
481
- setRequestTimeout: (_timeoutMs) => Effect3.void,
482
- clearRequestTimeout: () => Effect3.void,
483
- reconnect: () => Effect3.asVoid(Effect3.logDebug("Mock: reconnecting")),
484
- close: () => Effect3.logDebug("Mock: closing transport"),
276
+ setRequestTimeout: (_timeoutMs) => Effect.void,
277
+ clearRequestTimeout: () => Effect.void,
278
+ reconnect: () => Effect.asVoid(Effect.logDebug("Mock: reconnecting")),
279
+ close: () => Effect.logDebug("Mock: closing transport"),
485
280
  hasPendingRequests: () => false
486
281
  };
487
282
  });
488
283
  };
489
284
 
285
+ // src/shared-transport.ts
286
+ import { Effect as Effect3, Exit, Scope } from "effect";
287
+
288
+ // src/modbus-client.ts
289
+ import { Effect as Effect2 } from "effect";
290
+ var wrap = (try_) => Effect2.tryPromise({
291
+ try: try_,
292
+ catch: (error) => toModbusError(error)
293
+ });
294
+ var makeEffectModbusClient = (client) => ({
295
+ readHoldingRegisters: (opts) => wrap(() => client.readHoldingRegisters(opts)),
296
+ readInputRegisters: (opts) => wrap(() => client.readInputRegisters(opts)),
297
+ writeSingleRegister: (opts) => wrap(() => client.writeSingleRegister(opts)),
298
+ writeMultipleRegisters: (opts) => wrap(() => client.writeMultipleRegisters(opts)),
299
+ readWriteMultipleRegisters: (opts) => wrap(() => client.readWriteMultipleRegisters(opts)),
300
+ readCoils: (opts) => wrap(() => client.readCoils(opts)),
301
+ writeSingleCoil: (opts) => wrap(() => client.writeSingleCoil(opts)),
302
+ writeMultipleCoils: (opts) => wrap(() => client.writeMultipleCoils(opts)),
303
+ readDiscreteInputs: (opts) => wrap(() => client.readDiscreteInputs(opts)),
304
+ readFifoQueue: (opts) => wrap(() => client.readFifoQueue(opts)),
305
+ readFileRecord: (opts) => wrap(() => client.readFileRecord(opts)),
306
+ writeFileRecord: (opts) => wrap(() => client.writeFileRecord(opts)),
307
+ readExceptionStatus: () => wrap(() => client.readExceptionStatus()),
308
+ diagnostics: (opts) => wrap(() => client.diagnostics(opts)),
309
+ readDeviceIdentification: (opts) => wrap(() => client.readDeviceIdentification(opts))
310
+ });
311
+
312
+ // src/shared-transport.ts
313
+ function makeTransportScoped(transportKey, openMethod, serviceName, config) {
314
+ return Effect3.fnUntraced(function* (options) {
315
+ const mod = config?.moduleSpecifier === "modbus-rs/web" ? yield* Effect3.promise(() => import("modbus-rs/web")) : yield* Effect3.promise(() => import("modbus-rs"));
316
+ const TC = mod[transportKey];
317
+ let transport = null;
318
+ let connectPromise = null;
319
+ let reconnectPromise = null;
320
+ const clientSet = new Map;
321
+ let closed = false;
322
+ const ensureOpen = Effect3.fnUntraced(function* () {
323
+ if (transport) {
324
+ if (closed) {
325
+ return yield* new ModbusNotConnectedError({
326
+ cause: new Error("Transport has been closed"),
327
+ message: "Transport has been closed"
328
+ });
329
+ }
330
+ return transport;
331
+ }
332
+ if (closed) {
333
+ return yield* new ModbusNotConnectedError({
334
+ cause: new Error("Transport has been closed"),
335
+ message: "Transport has been closed"
336
+ });
337
+ }
338
+ if (!connectPromise) {
339
+ connectPromise = openMethod(TC, options);
340
+ }
341
+ const t = yield* Effect3.tryPromise({
342
+ try: () => connectPromise,
343
+ catch: (error) => toModbusError(error)
344
+ }).pipe(Effect3.catchAll((err) => {
345
+ connectPromise = null;
346
+ return Effect3.fail(err);
347
+ }));
348
+ if (closed) {
349
+ connectPromise = null;
350
+ yield* Effect3.fork(Effect3.promise(() => t.close()).pipe(Effect3.catchAll(() => Effect3.void)));
351
+ return yield* new ModbusNotConnectedError({
352
+ cause: new Error("Transport has been closed"),
353
+ message: "Transport has been closed"
354
+ });
355
+ }
356
+ transport = t;
357
+ return t;
358
+ });
359
+ yield* Effect3.addFinalizer(() => {
360
+ if (closed)
361
+ return Effect3.void;
362
+ closed = true;
363
+ const t = transport;
364
+ if (!t)
365
+ return Effect3.void;
366
+ return Effect3.andThen(Effect3.logDebug(`Closing ${serviceName}`), Effect3.promise(() => t.close()));
367
+ });
368
+ const notConnectedMsg = "Transport is not connected. Call withClient() first.";
369
+ return {
370
+ withClient: Effect3.fnUntraced(function* (unitId) {
371
+ const t = yield* ensureOpen();
372
+ let client = clientSet.get(unitId);
373
+ if (!client) {
374
+ client = yield* Effect3.try({
375
+ try: () => t.createClient({ unitId }),
376
+ catch: (error) => toModbusError(error)
377
+ });
378
+ clientSet.set(unitId, client);
379
+ }
380
+ return makeEffectModbusClient(client);
381
+ }),
382
+ setRequestTimeout: Effect3.fnUntraced(function* (timeoutMs) {
383
+ const t = transport;
384
+ if (!t || closed) {
385
+ return yield* new ModbusNotConnectedError({
386
+ cause: new Error(notConnectedMsg),
387
+ message: notConnectedMsg
388
+ });
389
+ }
390
+ t.setRequestTimeout(timeoutMs);
391
+ }),
392
+ clearRequestTimeout: Effect3.fnUntraced(function* () {
393
+ const t = transport;
394
+ if (!t || closed) {
395
+ return yield* new ModbusNotConnectedError({
396
+ cause: new Error(notConnectedMsg),
397
+ message: notConnectedMsg
398
+ });
399
+ }
400
+ t.clearRequestTimeout();
401
+ }),
402
+ reconnect: Effect3.fnUntraced(function* () {
403
+ if (closed) {
404
+ return yield* new ModbusNotConnectedError({
405
+ cause: new Error("Transport has been closed"),
406
+ message: "Transport has been closed"
407
+ });
408
+ }
409
+ if (transport) {
410
+ if (!reconnectPromise) {
411
+ reconnectPromise = transport.reconnect().then(() => {
412
+ reconnectPromise = null;
413
+ }).catch((err) => {
414
+ reconnectPromise = null;
415
+ throw err;
416
+ });
417
+ }
418
+ yield* Effect3.tryPromise({
419
+ try: () => reconnectPromise,
420
+ catch: (error) => toModbusError(error)
421
+ });
422
+ } else {
423
+ yield* ensureOpen();
424
+ }
425
+ }),
426
+ close: Effect3.fnUntraced(function* () {
427
+ if (closed)
428
+ return;
429
+ closed = true;
430
+ const t = transport;
431
+ if (t) {
432
+ yield* Effect3.tryPromise({
433
+ try: () => t.close(),
434
+ catch: (error) => toModbusError(error)
435
+ });
436
+ }
437
+ const scope = yield* Effect3.scope;
438
+ yield* Scope.close(scope, Exit.void);
439
+ }),
440
+ hasPendingRequests: () => {
441
+ if (closed)
442
+ return false;
443
+ const t = transport;
444
+ if (!t)
445
+ return false;
446
+ return t.pendingRequests;
447
+ }
448
+ };
449
+ });
450
+ }
451
+
490
452
  // src/AsciiTransportService.ts
491
453
  class AsciiTransportService extends Effect4.Service()("AsciiTransportService", {
492
454
  scoped: makeTransportScoped("AsyncAsciiTransport", (TC, options) => TC.open(options), "AsciiTransportService")
@@ -587,12 +549,125 @@ var tcpGatewayLayer = (options, gatewayConfig) => Layer7.scopedDiscard(Effect10.
587
549
  catch: (error) => toModbusError(error)
588
550
  })), Effect10.catchAll(() => Effect10.void)));
589
551
  }));
552
+ // src/WasmWsTransportService.ts
553
+ import { Effect as Effect11, Layer as Layer8 } from "effect";
554
+ class WasmWsTransportService extends Effect11.Service()("WasmWsTransportService", {
555
+ scoped: makeTransportScoped("WasmWsTransport", (TC, options) => TC.connect(options), "WasmWsTransportService", { moduleSpecifier: "modbus-rs/web" })
556
+ }) {
557
+ static makeMockTransport = (devices) => {
558
+ const factory = makeMockTransport(devices);
559
+ return (options) => Layer8.scoped(WasmWsTransportService, factory(options));
560
+ };
561
+ }
562
+ // src/WasmRtuTransportService.ts
563
+ import { Effect as Effect12, Layer as Layer9 } from "effect";
564
+ class WasmRtuTransportService extends Effect12.Service()("WasmRtuTransportService", {
565
+ scoped: makeTransportScoped("WasmRtuTransport", (TC, { port, ...rest }) => TC.open(port, rest), "WasmRtuTransportService", { moduleSpecifier: "modbus-rs/web" })
566
+ }) {
567
+ static makeMockTransport = (devices) => {
568
+ const factory = makeMockTransport(devices);
569
+ return (options) => Layer9.scoped(WasmRtuTransportService, factory(options));
570
+ };
571
+ }
572
+ // src/WasmAsciiTransportService.ts
573
+ import { Effect as Effect13, Layer as Layer10 } from "effect";
574
+ class WasmAsciiTransportService extends Effect13.Service()("WasmAsciiTransportService", {
575
+ scoped: makeTransportScoped("WasmAsciiTransport", (TC, { port, ...rest }) => TC.open(port, rest), "WasmAsciiTransportService", { moduleSpecifier: "modbus-rs/web" })
576
+ }) {
577
+ static makeMockTransport = (devices) => {
578
+ const factory = makeMockTransport(devices);
579
+ return (options) => Layer10.scoped(WasmAsciiTransportService, factory(options));
580
+ };
581
+ }
582
+ // src/WasmSerialTransportService.ts
583
+ import { Context as Context2, Layer as Layer11 } from "effect";
584
+ class WasmSerialTransportService extends Context2.Tag("WasmSerialTransportService")() {
585
+ static fromAscii(options) {
586
+ return Layer11.project(WasmAsciiTransportService, WasmSerialTransportService, (ascii) => ascii)(WasmAsciiTransportService.Default(options));
587
+ }
588
+ static fromRtu(options) {
589
+ return Layer11.project(WasmRtuTransportService, WasmSerialTransportService, (rtu) => rtu)(WasmRtuTransportService.Default(options));
590
+ }
591
+ static makeMockTransport = (devices) => {
592
+ const factory = makeMockTransport(devices);
593
+ return (options) => Layer11.scoped(WasmSerialTransportService, factory(options));
594
+ };
595
+ }
596
+ // src/WasmSerialPort.ts
597
+ import { Effect as Effect15 } from "effect";
598
+ var requestSerialPort = () => Effect15.tryPromise({
599
+ try: async () => {
600
+ const mod = await import("modbus-rs/web");
601
+ return mod.requestSerialPort();
602
+ },
603
+ catch: (error) => toModbusError(error)
604
+ });
605
+ // src/WasmTcpServerService.ts
606
+ import { Effect as Effect16, Layer as Layer12 } from "effect";
607
+ var wasmWsServerLayer = (options, handlers) => Layer12.scopedDiscard(Effect16.gen(function* () {
608
+ const { WasmWsModbusServer } = yield* Effect16.promise(() => import("modbus-rs/web"));
609
+ const server = yield* Effect16.tryPromise({
610
+ try: () => WasmWsModbusServer.bind(options, handlers),
611
+ catch: (error) => toModbusError(error)
612
+ });
613
+ yield* Effect16.logDebug(`WASM WS server bound to ${options.wsUrl}`);
614
+ yield* Effect16.forkScoped(Effect16.tryPromise({
615
+ try: () => server.serve(),
616
+ catch: (error) => toModbusError(error)
617
+ }).pipe(Effect16.catchAll((error) => Effect16.logError("WASM WS server loop ended", error))));
618
+ yield* Effect16.addFinalizer(() => Effect16.logDebug("WASM WS server shutting down").pipe(Effect16.andThen(Effect16.tryPromise({
619
+ try: () => server.shutdown(),
620
+ catch: (error) => toModbusError(error)
621
+ })), Effect16.catchAll(() => Effect16.void)));
622
+ }));
623
+ // src/WasmSerialModbusServerService.ts
624
+ import { Effect as Effect17, Layer as Layer13 } from "effect";
625
+ var wasmSerialRtuServerLayer = (options, handlers) => Layer13.scopedDiscard(Effect17.gen(function* () {
626
+ const { WasmSerialModbusServer } = yield* Effect17.promise(() => import("modbus-rs/web"));
627
+ const server = yield* Effect17.tryPromise({
628
+ try: () => WasmSerialModbusServer.bindRtu(options, handlers),
629
+ catch: (error) => toModbusError(error)
630
+ });
631
+ yield* Effect17.logDebug("WASM serial RTU server bound");
632
+ yield* Effect17.forkScoped(Effect17.tryPromise({
633
+ try: () => server.serve(),
634
+ catch: (error) => toModbusError(error)
635
+ }).pipe(Effect17.catchAll((error) => Effect17.logError("WASM serial RTU server loop ended", error))));
636
+ yield* Effect17.addFinalizer(() => Effect17.logDebug("WASM serial RTU server shutting down").pipe(Effect17.andThen(Effect17.tryPromise({
637
+ try: () => server.shutdown(),
638
+ catch: (error) => toModbusError(error)
639
+ })), Effect17.catchAll(() => Effect17.void)));
640
+ }));
641
+ var wasmSerialAsciiServerLayer = (options, handlers) => Layer13.scopedDiscard(Effect17.gen(function* () {
642
+ const { WasmSerialModbusServer } = yield* Effect17.promise(() => import("modbus-rs/web"));
643
+ const server = yield* Effect17.tryPromise({
644
+ try: () => WasmSerialModbusServer.bindAscii(options, handlers),
645
+ catch: (error) => toModbusError(error)
646
+ });
647
+ yield* Effect17.logDebug("WASM serial ASCII server bound");
648
+ yield* Effect17.forkScoped(Effect17.tryPromise({
649
+ try: () => server.serve(),
650
+ catch: (error) => toModbusError(error)
651
+ }).pipe(Effect17.catchAll((error) => Effect17.logError("WASM serial ASCII server loop ended", error))));
652
+ yield* Effect17.addFinalizer(() => Effect17.logDebug("WASM serial ASCII server shutting down").pipe(Effect17.andThen(Effect17.tryPromise({
653
+ try: () => server.shutdown(),
654
+ catch: (error) => toModbusError(error)
655
+ })), Effect17.catchAll(() => Effect17.void)));
656
+ }));
590
657
  export {
658
+ wasmWsServerLayer,
659
+ wasmSerialRtuServerLayer,
660
+ wasmSerialAsciiServerLayer,
591
661
  toModbusError,
592
662
  tcpServerLayer,
593
663
  tcpGatewayLayer,
594
664
  serialRtuServerLayer,
595
665
  serialAsciiServerLayer,
666
+ requestSerialPort,
667
+ WasmWsTransportService,
668
+ WasmSerialTransportService,
669
+ WasmRtuTransportService,
670
+ WasmAsciiTransportService,
596
671
  TcpTransportService,
597
672
  SerialTransportService,
598
673
  RtuTransportService,