@flux-control/effect-modbus-rs 0.4.0 → 0.5.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 +317 -20
- package/dist/index.d.ts +62 -1
- package/dist/index.js +1043 -225
- package/dist/src/AsciiTransportService.d.ts +14 -10
- package/dist/src/RtuTransportService.d.ts +14 -10
- package/dist/src/TcpTransportService.d.ts +14 -10
- package/dist/src/WasmAsciiTransportService.d.ts +15 -11
- package/dist/src/WasmRtuTransportService.d.ts +15 -11
- package/dist/src/WasmWsTransportService.d.ts +14 -10
- package/dist/src/batching-client.d.ts +197 -0
- package/dist/src/connection.d.ts +31 -4
- package/dist/src/mocks.d.ts +14 -8
- package/dist/src/modbus-client.d.ts +3 -1
- package/dist/src/read-debouncer.d.ts +100 -0
- package/dist/src/register-cache.d.ts +90 -0
- package/dist/src/register-plan.d.ts +212 -0
- package/dist/src/retry.d.ts +4 -4
- package/dist/src/shared-transport.d.ts +60 -4
- package/dist/src/span-attributes.d.ts +34 -0
- package/dist/src/write-debouncer.d.ts +134 -0
- package/dist/test/retry.test.d.ts +1 -0
- package/dist/test/shared-transport.test.d.ts +1 -0
- package/dist/test/span-attributes.test.d.ts +1 -0
- package/dist/test/upstream-options.test.d.ts +1 -0
- package/dist/test/wasm-mocks.test.d.ts +1 -0
- package/dist/test/write-debouncer.test.d.ts +1 -0
- package/package.json +6 -4
- /package/dist/{src/mock-options.test.d.ts → test/batching-client.test.d.ts} +0 -0
- /package/dist/{src/mocks.test.d.ts → test/mock-options.test.d.ts} +0 -0
- /package/dist/{src/modbus-client.wasm.test.d.ts → test/mocks.test.d.ts} +0 -0
- /package/dist/{src/resilience.test.d.ts → test/modbus-client.wasm.test.d.ts} +0 -0
- /package/dist/{src/retry.test.d.ts → test/read-debouncer.test.d.ts} +0 -0
- /package/dist/{src/shared-transport.test.d.ts → test/register-cache.test.d.ts} +0 -0
- /package/dist/{src/upstream-options.test.d.ts → test/register-plan.test.d.ts} +0 -0
- /package/dist/{src/wasm-mocks.test.d.ts → test/resilience.test.d.ts} +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Layer } from 'effect';
|
|
2
2
|
import type { AsciiTransportOptions } from 'modbus-rs';
|
|
3
|
-
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
-
import type { MockFaultOptions } from './mocks';
|
|
3
|
+
import { type MockFaultOptions, type SlaveDeviceDefinitions } from './mocks';
|
|
5
4
|
import type { TransportResilienceOptions, WithoutUpstreamRetry, TransportServiceApi } from './shared-transport';
|
|
6
5
|
/**
|
|
7
6
|
* {@link AsciiTransportOptions} minus the upstream retry knobs.
|
|
@@ -24,14 +23,14 @@ declare const AsciiTransportService_base: Context.ServiceClass<AsciiTransportSer
|
|
|
24
23
|
*
|
|
25
24
|
* @see AsyncAsciiTransport — Upstream `modbus-rs` ASCII transport.
|
|
26
25
|
* @see AsciiTransportOpenOptions — Configuration for the ASCII serial port.
|
|
27
|
-
* @see
|
|
26
|
+
* @see createTransportScoped — Generic lifecycle logic from shared-transport.
|
|
28
27
|
*/
|
|
29
28
|
export declare class AsciiTransportService extends AsciiTransportService_base {
|
|
30
29
|
/**
|
|
31
30
|
* Scoped constructor effect for the service. v4 does not auto-generate a
|
|
32
31
|
* layer from this, so {@link AsciiTransportService.make} builds one explicitly.
|
|
33
32
|
*/
|
|
34
|
-
static readonly makeScoped: (options: AsciiTransportOpenOptions & TransportResilienceOptions) => Effect.Effect<{
|
|
33
|
+
static readonly makeScoped: (options: AsciiTransportOpenOptions & TransportResilienceOptions) => import("effect/Effect").Effect<{
|
|
35
34
|
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
36
35
|
readonly _tag: "Connected";
|
|
37
36
|
} | {
|
|
@@ -45,11 +44,16 @@ export declare class AsciiTransportService extends AsciiTransportService_base {
|
|
|
45
44
|
}>;
|
|
46
45
|
withClient: (unitId: number, clientOptions?: {
|
|
47
46
|
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
48
|
-
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
49
|
-
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
50
|
-
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
51
|
-
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
52
|
-
close: () => Effect.Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
47
|
+
} | undefined) => import("effect/Effect").Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
48
|
+
setRequestTimeout: (timeoutMs: number) => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
49
|
+
clearRequestTimeout: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
50
|
+
reconnect: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusError, never>;
|
|
51
|
+
close: () => import("effect/Effect").Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
52
|
+
withBatchingClient: (unitId: number, options?: import("./batching-client").BatchingClientOptions & {
|
|
53
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
54
|
+
}) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
55
|
+
batchingClient: (unitId: number) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
56
|
+
readonly touchedUnits: Set<number>;
|
|
53
57
|
hasPendingRequests: () => boolean;
|
|
54
58
|
}, never, import("effect/Scope").Scope>;
|
|
55
59
|
/**
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Layer } from 'effect';
|
|
2
2
|
import type { RtuTransportOptions } from 'modbus-rs';
|
|
3
|
-
import type
|
|
4
|
-
import type { SlaveDeviceDefinitions } from './mocks';
|
|
3
|
+
import { type MockFaultOptions, type SlaveDeviceDefinitions } from './mocks';
|
|
5
4
|
import type { TransportResilienceOptions, WithoutUpstreamRetry, TransportServiceApi } from './shared-transport';
|
|
6
5
|
/**
|
|
7
6
|
* {@link RtuTransportOptions} minus the upstream retry knobs.
|
|
@@ -24,14 +23,14 @@ declare const RtuTransportService_base: Context.ServiceClass<RtuTransportService
|
|
|
24
23
|
*
|
|
25
24
|
* @see AsyncRtuTransport — Upstream `modbus-rs` RTU transport.
|
|
26
25
|
* @see RtuTransportOpenOptions — Configuration for the RTU serial port.
|
|
27
|
-
* @see
|
|
26
|
+
* @see createTransportScoped — Generic lifecycle logic from shared-transport.
|
|
28
27
|
*/
|
|
29
28
|
export declare class RtuTransportService extends RtuTransportService_base {
|
|
30
29
|
/**
|
|
31
30
|
* Scoped constructor effect for the service. v4 does not auto-generate a
|
|
32
31
|
* layer from this, so {@link RtuTransportService.make} builds one explicitly.
|
|
33
32
|
*/
|
|
34
|
-
static readonly makeScoped: (options: RtuTransportOpenOptions & TransportResilienceOptions) => Effect.Effect<{
|
|
33
|
+
static readonly makeScoped: (options: RtuTransportOpenOptions & TransportResilienceOptions) => import("effect/Effect").Effect<{
|
|
35
34
|
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
36
35
|
readonly _tag: "Connected";
|
|
37
36
|
} | {
|
|
@@ -45,11 +44,16 @@ export declare class RtuTransportService extends RtuTransportService_base {
|
|
|
45
44
|
}>;
|
|
46
45
|
withClient: (unitId: number, clientOptions?: {
|
|
47
46
|
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
48
|
-
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
49
|
-
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
50
|
-
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
51
|
-
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
52
|
-
close: () => Effect.Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
47
|
+
} | undefined) => import("effect/Effect").Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
48
|
+
setRequestTimeout: (timeoutMs: number) => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
49
|
+
clearRequestTimeout: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
50
|
+
reconnect: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusError, never>;
|
|
51
|
+
close: () => import("effect/Effect").Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
52
|
+
withBatchingClient: (unitId: number, options?: import("./batching-client").BatchingClientOptions & {
|
|
53
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
54
|
+
}) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
55
|
+
batchingClient: (unitId: number) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
56
|
+
readonly touchedUnits: Set<number>;
|
|
53
57
|
hasPendingRequests: () => boolean;
|
|
54
58
|
}, never, import("effect/Scope").Scope>;
|
|
55
59
|
/**
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Layer } from 'effect';
|
|
2
2
|
import type { TcpTransportOptions } from 'modbus-rs';
|
|
3
|
-
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
-
import type { MockFaultOptions } from './mocks';
|
|
3
|
+
import { type MockFaultOptions, type SlaveDeviceDefinitions } from './mocks';
|
|
5
4
|
import type { TransportResilienceOptions, TransportServiceApi, WithoutUpstreamRetry } from './shared-transport';
|
|
6
5
|
/**
|
|
7
6
|
* {@link TcpTransportOptions} minus the upstream retry knobs.
|
|
@@ -24,14 +23,14 @@ declare const TcpTransportService_base: Context.ServiceClass<TcpTransportService
|
|
|
24
23
|
*
|
|
25
24
|
* @see AsyncTcpTransport — Upstream `modbus-rs` TCP transport.
|
|
26
25
|
* @see TcpTransportOpenOptions — Configuration for the TCP connection.
|
|
27
|
-
* @see
|
|
26
|
+
* @see createTransportScoped — Generic lifecycle logic from shared-transport.
|
|
28
27
|
*/
|
|
29
28
|
export declare class TcpTransportService extends TcpTransportService_base {
|
|
30
29
|
/**
|
|
31
30
|
* Scoped constructor effect for the service. v4 does not auto-generate a
|
|
32
31
|
* layer from this, so {@link TcpTransportService.make} builds one explicitly.
|
|
33
32
|
*/
|
|
34
|
-
static readonly makeScoped: (options: TcpTransportOpenOptions & TransportResilienceOptions) => Effect.Effect<{
|
|
33
|
+
static readonly makeScoped: (options: TcpTransportOpenOptions & TransportResilienceOptions) => import("effect/Effect").Effect<{
|
|
35
34
|
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
36
35
|
readonly _tag: "Connected";
|
|
37
36
|
} | {
|
|
@@ -45,11 +44,16 @@ export declare class TcpTransportService extends TcpTransportService_base {
|
|
|
45
44
|
}>;
|
|
46
45
|
withClient: (unitId: number, clientOptions?: {
|
|
47
46
|
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
48
|
-
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
49
|
-
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
50
|
-
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
51
|
-
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
52
|
-
close: () => Effect.Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
47
|
+
} | undefined) => import("effect/Effect").Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
48
|
+
setRequestTimeout: (timeoutMs: number) => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
49
|
+
clearRequestTimeout: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
50
|
+
reconnect: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusError, never>;
|
|
51
|
+
close: () => import("effect/Effect").Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
52
|
+
withBatchingClient: (unitId: number, options?: import("./batching-client").BatchingClientOptions & {
|
|
53
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
54
|
+
}) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
55
|
+
batchingClient: (unitId: number) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
56
|
+
readonly touchedUnits: Set<number>;
|
|
53
57
|
hasPendingRequests: () => boolean;
|
|
54
58
|
}, never, import("effect/Scope").Scope>;
|
|
55
59
|
/**
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Layer } from 'effect';
|
|
2
2
|
import type { WasmSerialPortHandle, WasmSerialTransportOptions } from 'modbus-rs/web';
|
|
3
|
-
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
-
import type { MockFaultOptions } from './mocks';
|
|
3
|
+
import { type MockFaultOptions, type SlaveDeviceDefinitions } from './mocks';
|
|
5
4
|
import type { TransportResilienceOptions, TransportServiceApi } from './shared-transport';
|
|
6
5
|
/**
|
|
7
6
|
* Options for {@link WasmAsciiTransportService}. `WasmAsciiTransport.open()` takes the
|
|
8
7
|
* serial port handle and the connection options as two separate arguments; this
|
|
9
|
-
* combines them into one object so it fits {@link
|
|
8
|
+
* combines them into one object so it fits {@link createTransportScoped}'s single-options
|
|
10
9
|
* shape, with `port` destructured back out inside the service's `openMethod`.
|
|
11
10
|
*
|
|
12
11
|
* @see requestSerialPort — Obtains the `port` handle (must be called from a user gesture).
|
|
@@ -28,7 +27,7 @@ declare const WasmAsciiTransportService_base: Context.ServiceClass<WasmAsciiTran
|
|
|
28
27
|
*
|
|
29
28
|
* @see WasmAsciiTransport — Upstream `modbus-rs` browser Web Serial ASCII transport.
|
|
30
29
|
* @see requestSerialPort — Obtains the serial port handle this service's `port` option needs.
|
|
31
|
-
* @see
|
|
30
|
+
* @see createTransportScoped — Generic lifecycle logic from shared-transport.
|
|
32
31
|
*/
|
|
33
32
|
export declare class WasmAsciiTransportService extends WasmAsciiTransportService_base {
|
|
34
33
|
/**
|
|
@@ -37,7 +36,7 @@ export declare class WasmAsciiTransportService extends WasmAsciiTransportService
|
|
|
37
36
|
*/
|
|
38
37
|
static readonly makeScoped: (options: WasmSerialTransportOptions & {
|
|
39
38
|
port: WasmSerialPortHandle;
|
|
40
|
-
} & TransportResilienceOptions) => Effect.Effect<{
|
|
39
|
+
} & TransportResilienceOptions) => import("effect/Effect").Effect<{
|
|
41
40
|
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
42
41
|
readonly _tag: "Connected";
|
|
43
42
|
} | {
|
|
@@ -51,11 +50,16 @@ export declare class WasmAsciiTransportService extends WasmAsciiTransportService
|
|
|
51
50
|
}>;
|
|
52
51
|
withClient: (unitId: number, clientOptions?: {
|
|
53
52
|
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
54
|
-
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
55
|
-
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
56
|
-
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
57
|
-
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
58
|
-
close: () => Effect.Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
53
|
+
} | undefined) => import("effect/Effect").Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
54
|
+
setRequestTimeout: (timeoutMs: number) => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
55
|
+
clearRequestTimeout: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
56
|
+
reconnect: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusError, never>;
|
|
57
|
+
close: () => import("effect/Effect").Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
58
|
+
withBatchingClient: (unitId: number, options?: import("./batching-client").BatchingClientOptions & {
|
|
59
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
60
|
+
}) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
61
|
+
batchingClient: (unitId: number) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
62
|
+
readonly touchedUnits: Set<number>;
|
|
59
63
|
hasPendingRequests: () => boolean;
|
|
60
64
|
}, never, import("effect/Scope").Scope>;
|
|
61
65
|
/**
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Layer } from 'effect';
|
|
2
2
|
import type { WasmSerialPortHandle, WasmSerialTransportOptions } from 'modbus-rs/web';
|
|
3
|
-
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
-
import type { MockFaultOptions } from './mocks';
|
|
3
|
+
import { type MockFaultOptions, type SlaveDeviceDefinitions } from './mocks';
|
|
5
4
|
import type { TransportResilienceOptions, TransportServiceApi } from './shared-transport';
|
|
6
5
|
/**
|
|
7
6
|
* Options for {@link WasmRtuTransportService}. `WasmRtuTransport.open()` takes the
|
|
8
7
|
* serial port handle and the connection options as two separate arguments; this
|
|
9
|
-
* combines them into one object so it fits {@link
|
|
8
|
+
* combines them into one object so it fits {@link createTransportScoped}'s single-options
|
|
10
9
|
* shape, with `port` destructured back out inside the service's `openMethod`.
|
|
11
10
|
*
|
|
12
11
|
* @see requestSerialPort — Obtains the `port` handle (must be called from a user gesture).
|
|
@@ -28,7 +27,7 @@ declare const WasmRtuTransportService_base: Context.ServiceClass<WasmRtuTranspor
|
|
|
28
27
|
*
|
|
29
28
|
* @see WasmRtuTransport — Upstream `modbus-rs` browser Web Serial RTU transport.
|
|
30
29
|
* @see requestSerialPort — Obtains the serial port handle this service's `port` option needs.
|
|
31
|
-
* @see
|
|
30
|
+
* @see createTransportScoped — Generic lifecycle logic from shared-transport.
|
|
32
31
|
*/
|
|
33
32
|
export declare class WasmRtuTransportService extends WasmRtuTransportService_base {
|
|
34
33
|
/**
|
|
@@ -37,7 +36,7 @@ export declare class WasmRtuTransportService extends WasmRtuTransportService_bas
|
|
|
37
36
|
*/
|
|
38
37
|
static readonly makeScoped: (options: WasmSerialTransportOptions & {
|
|
39
38
|
port: WasmSerialPortHandle;
|
|
40
|
-
} & TransportResilienceOptions) => Effect.Effect<{
|
|
39
|
+
} & TransportResilienceOptions) => import("effect/Effect").Effect<{
|
|
41
40
|
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
42
41
|
readonly _tag: "Connected";
|
|
43
42
|
} | {
|
|
@@ -51,11 +50,16 @@ export declare class WasmRtuTransportService extends WasmRtuTransportService_bas
|
|
|
51
50
|
}>;
|
|
52
51
|
withClient: (unitId: number, clientOptions?: {
|
|
53
52
|
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
54
|
-
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
55
|
-
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
56
|
-
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
57
|
-
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
58
|
-
close: () => Effect.Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
53
|
+
} | undefined) => import("effect/Effect").Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
54
|
+
setRequestTimeout: (timeoutMs: number) => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
55
|
+
clearRequestTimeout: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
56
|
+
reconnect: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusError, never>;
|
|
57
|
+
close: () => import("effect/Effect").Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
58
|
+
withBatchingClient: (unitId: number, options?: import("./batching-client").BatchingClientOptions & {
|
|
59
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
60
|
+
}) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
61
|
+
batchingClient: (unitId: number) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
62
|
+
readonly touchedUnits: Set<number>;
|
|
59
63
|
hasPendingRequests: () => boolean;
|
|
60
64
|
}, never, import("effect/Scope").Scope>;
|
|
61
65
|
/**
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Layer } from 'effect';
|
|
2
2
|
import type { WasmWsTransportOptions } from 'modbus-rs/web';
|
|
3
|
-
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
-
import type { MockFaultOptions } from './mocks';
|
|
3
|
+
import { type MockFaultOptions, type SlaveDeviceDefinitions } from './mocks';
|
|
5
4
|
import type { TransportResilienceOptions, TransportServiceApi } from './shared-transport';
|
|
6
5
|
declare const WasmWsTransportService_base: Context.ServiceClass<WasmWsTransportService, "WasmWsTransportService", TransportServiceApi>;
|
|
7
6
|
/**
|
|
@@ -17,14 +16,14 @@ declare const WasmWsTransportService_base: Context.ServiceClass<WasmWsTransportS
|
|
|
17
16
|
*
|
|
18
17
|
* @see WasmWsTransport — Upstream `modbus-rs` browser WebSocket transport.
|
|
19
18
|
* @see WasmWsTransportOptions — Configuration for the WebSocket gateway connection.
|
|
20
|
-
* @see
|
|
19
|
+
* @see createTransportScoped — Generic lifecycle logic from shared-transport.
|
|
21
20
|
*/
|
|
22
21
|
export declare class WasmWsTransportService extends WasmWsTransportService_base {
|
|
23
22
|
/**
|
|
24
23
|
* Scoped constructor effect for the service. v4 does not auto-generate a
|
|
25
24
|
* layer from this, so {@link WasmWsTransportService.make} builds one explicitly.
|
|
26
25
|
*/
|
|
27
|
-
static readonly makeScoped: (options: WasmWsTransportOptions & TransportResilienceOptions) => Effect.Effect<{
|
|
26
|
+
static readonly makeScoped: (options: WasmWsTransportOptions & TransportResilienceOptions) => import("effect/Effect").Effect<{
|
|
28
27
|
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
29
28
|
readonly _tag: "Connected";
|
|
30
29
|
} | {
|
|
@@ -38,11 +37,16 @@ export declare class WasmWsTransportService extends WasmWsTransportService_base
|
|
|
38
37
|
}>;
|
|
39
38
|
withClient: (unitId: number, clientOptions?: {
|
|
40
39
|
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
41
|
-
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
42
|
-
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
43
|
-
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
44
|
-
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
45
|
-
close: () => Effect.Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
40
|
+
} | undefined) => import("effect/Effect").Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
41
|
+
setRequestTimeout: (timeoutMs: number) => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
42
|
+
clearRequestTimeout: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
43
|
+
reconnect: () => import("effect/Effect").Effect<undefined, import("./errors").ModbusError, never>;
|
|
44
|
+
close: () => import("effect/Effect").Effect<void, import("./errors").ModbusError, import("effect/Scope").Scope>;
|
|
45
|
+
withBatchingClient: (unitId: number, options?: import("./batching-client").BatchingClientOptions & {
|
|
46
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
47
|
+
}) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
48
|
+
batchingClient: (unitId: number) => import("effect/Effect").Effect<import("./batching-client").BatchingModbusClient, import("./errors").ModbusError>;
|
|
49
|
+
readonly touchedUnits: Set<number>;
|
|
46
50
|
hasPendingRequests: () => boolean;
|
|
47
51
|
}, never, import("effect/Scope").Scope>;
|
|
48
52
|
/**
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview A client that decides the transactions, rather than issuing the
|
|
3
|
+
* ones a caller names.
|
|
4
|
+
*
|
|
5
|
+
* `withClient` returns an {@link EffectModbusClient}: it issues exactly the
|
|
6
|
+
* transaction asked for, and it is the right client when a caller knows what the
|
|
7
|
+
* bus should carry. This module is its sibling for the other case — a caller with
|
|
8
|
+
* one accessor per register, which knows what it wants to read and write but not
|
|
9
|
+
* what that ought to cost.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { Duration, Effect, Scope, SubscriptionRef } from 'effect';
|
|
14
|
+
import { ConnectionState } from './connection';
|
|
15
|
+
import { type ModbusError } from './errors';
|
|
16
|
+
import type { EffectModbusClient, ModbusOperations } from './modbus-client';
|
|
17
|
+
import { type RegisterCache } from './register-cache';
|
|
18
|
+
import { type PlanReadsOptions, type PlanWritesOptions, type RegisterWrite } from './register-plan';
|
|
19
|
+
import type { ModbusRetryPolicy } from './retry';
|
|
20
|
+
import { type ModbusSpanAttributes } from './span-attributes';
|
|
21
|
+
/** How long operations are collected before they reach the bus. */
|
|
22
|
+
export interface BatchingDebounceOptions {
|
|
23
|
+
/**
|
|
24
|
+
* The window for writes, and the ceiling on the total hold.
|
|
25
|
+
*
|
|
26
|
+
* Omitted, writes are not debounced: each one reaches the bus on its own. The
|
|
27
|
+
* package holds nothing back by default, so a caller that has not asked for a
|
|
28
|
+
* window pays no latency for one.
|
|
29
|
+
*/
|
|
30
|
+
readonly writes?: {
|
|
31
|
+
readonly window: Duration.Input;
|
|
32
|
+
readonly maxHold?: Duration.Input;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The window for reads.
|
|
36
|
+
*
|
|
37
|
+
* Omitted, reads are not debounced. `readAll` still plans the addresses it is
|
|
38
|
+
* given, so a caller that reads a group in one call needs no window.
|
|
39
|
+
*
|
|
40
|
+
* A useful window is on the order of one transaction. Measure a transaction on
|
|
41
|
+
* the bus in question before choosing one.
|
|
42
|
+
*/
|
|
43
|
+
readonly reads?: {
|
|
44
|
+
readonly window: Duration.Input;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Options for {@link makeBatchingClient}. */
|
|
48
|
+
export interface BatchingClientOptions {
|
|
49
|
+
/**
|
|
50
|
+
* The write cache, which drops a write whose value the device already holds.
|
|
51
|
+
*
|
|
52
|
+
* Pass `false` to write every value, whether or not it changes anything. Pass
|
|
53
|
+
* a cache to share one with something else. The transport supplies its own by
|
|
54
|
+
* default, which is the right one: the cache is a belief about a physical
|
|
55
|
+
* device, and two caches on one unit disagree.
|
|
56
|
+
*/
|
|
57
|
+
readonly cache?: boolean | RegisterCache;
|
|
58
|
+
/** The windows. Omitted, nothing is debounced. */
|
|
59
|
+
readonly debounce?: BatchingDebounceOptions;
|
|
60
|
+
/** Limits handed to the planners. */
|
|
61
|
+
readonly plan?: {
|
|
62
|
+
readonly writes?: PlanWritesOptions;
|
|
63
|
+
readonly reads?: PlanReadsOptions;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Reads over one register space. */
|
|
67
|
+
export interface BatchingRegisterReader {
|
|
68
|
+
/** Collects an address for the read window, then returns its value. */
|
|
69
|
+
read(address: number): Effect.Effect<number, ModbusError>;
|
|
70
|
+
/** Adds an address to the batch and reads immediately, answering every reader in it. */
|
|
71
|
+
readNow(address: number): Effect.Effect<number, ModbusError>;
|
|
72
|
+
/** Reads a group as one reader, planned into spans, in the order asked for. */
|
|
73
|
+
readAll(addresses: ReadonlyArray<number>): Effect.Effect<ReadonlyArray<number>, ModbusError>;
|
|
74
|
+
/** Adds a group to the batch and reads immediately. */
|
|
75
|
+
readAllNow(addresses: ReadonlyArray<number>): Effect.Effect<ReadonlyArray<number>, ModbusError>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A client that decides its own transactions.
|
|
79
|
+
*
|
|
80
|
+
* CAUTION: This is not an {@link EffectModbusClient} and does not extend
|
|
81
|
+
* {@link ModbusOperations}. There is no `writeSingleRegister` and no
|
|
82
|
+
* `readHoldingRegisters` here. A raw write on this object would go around the
|
|
83
|
+
* cache and around the batch, so a value held for an address could reach the
|
|
84
|
+
* device after a newer value written past it. A caller that needs the raw
|
|
85
|
+
* surface as well gets it from `withClient`, which shares the same connection.
|
|
86
|
+
* Once a batching client exists for a unit, that raw client remains available
|
|
87
|
+
* for reads, coils, and the other non-register-write operations, but FC06,
|
|
88
|
+
* FC16, and FC23 fail with `ModbusInvalidArgumentError`. Mixing the two register
|
|
89
|
+
* write paths would bypass the pending batch and its cache.
|
|
90
|
+
*
|
|
91
|
+
* Coils are not covered. The planners pack registers, so `readCoils`,
|
|
92
|
+
* `writeSingleCoil`, and their relatives stay on the raw client.
|
|
93
|
+
*/
|
|
94
|
+
export interface BatchingModbusClient extends BatchingRegisterReader {
|
|
95
|
+
/** The unit this client addresses. */
|
|
96
|
+
readonly unitId: number;
|
|
97
|
+
/** Holds a write for the write window, then issues it with whatever else arrived. */
|
|
98
|
+
write(write: RegisterWrite, attributes?: ModbusSpanAttributes): Effect.Effect<void, ModbusError>;
|
|
99
|
+
/** Adds a write to the batch and issues it immediately, newest value winning. */
|
|
100
|
+
writeNow(write: RegisterWrite, attributes?: ModbusSpanAttributes): Effect.Effect<void, ModbusError>;
|
|
101
|
+
/** Holds a group of writes as one caller. The group succeeds or fails together. */
|
|
102
|
+
writeAll(writes: ReadonlyArray<RegisterWrite>, attributes?: ModbusSpanAttributes): Effect.Effect<void, ModbusError>;
|
|
103
|
+
/** Adds a group of writes to the batch and issues it immediately. */
|
|
104
|
+
writeAllNow(writes: ReadonlyArray<RegisterWrite>, attributes?: ModbusSpanAttributes): Effect.Effect<void, ModbusError>;
|
|
105
|
+
/** Reads over the input registers (FC04). The unqualified reads are holding registers (FC03). */
|
|
106
|
+
readonly inputs: BatchingRegisterReader;
|
|
107
|
+
/**
|
|
108
|
+
* Issues everything pending, now. Never fails: the outcome goes to the callers
|
|
109
|
+
* waiting on each batch.
|
|
110
|
+
*/
|
|
111
|
+
readonly flush: Effect.Effect<void>;
|
|
112
|
+
/** The cache this client filters against, or `undefined` when `cache` is `false`. */
|
|
113
|
+
readonly cache: RegisterCache | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* Runs an action when the calling scope closes, while the bus is still open
|
|
116
|
+
* and before this client is torn down.
|
|
117
|
+
*
|
|
118
|
+
* A device's safe state is the device's own answer — zero volts for one, a
|
|
119
|
+
* stopped motor for another — so it is stated here, on the client that
|
|
120
|
+
* addresses it, rather than dispatched from one action over a set of units.
|
|
121
|
+
*
|
|
122
|
+
* A failure is logged and then raised as a defect. The other finalizers in the
|
|
123
|
+
* scope still run, so a device that cannot be reached does not cost the rest of
|
|
124
|
+
* the bus its turn. Wrap the action in `Effect.ignoreLogged` to accept the
|
|
125
|
+
* failure instead.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const damper = yield* transport.withBatchingClient(3);
|
|
129
|
+
* yield* damper.onShutdown(damper.writeAllNow([{ address: 2000, value: 0 }]));
|
|
130
|
+
*/
|
|
131
|
+
onShutdown(action: Effect.Effect<void, ModbusError>): Effect.Effect<void, never, Scope.Scope>;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Builds a batching client over an Effect-wrapped client for one unit.
|
|
135
|
+
*
|
|
136
|
+
* The transport calls this from `withBatchingClient`, which is where a consumer
|
|
137
|
+
* normally meets it. Call it directly to drive a client this package did not
|
|
138
|
+
* hand out, such as another adapter implementing `EffectModbusClient`, or a stub.
|
|
139
|
+
*
|
|
140
|
+
* @param options - The unit, the Effect-wrapped client, and the policies.
|
|
141
|
+
* @returns The batching client, bound to the current scope.
|
|
142
|
+
*/
|
|
143
|
+
export declare const makeBatchingClient: (options: {
|
|
144
|
+
readonly unitId: number;
|
|
145
|
+
readonly client: EffectModbusClient;
|
|
146
|
+
readonly cache: RegisterCache | undefined;
|
|
147
|
+
readonly debounce?: BatchingDebounceOptions;
|
|
148
|
+
readonly plan?: BatchingClientOptions['plan'];
|
|
149
|
+
}) => Effect.Effect<BatchingModbusClient, never, Scope.Scope>;
|
|
150
|
+
/** What a transport gives the registry so it can build batching clients. */
|
|
151
|
+
export interface BatchingRegistryDeps {
|
|
152
|
+
/** The transport's own raw client factory. */
|
|
153
|
+
withClient(unitId: number, options?: {
|
|
154
|
+
readonly retry?: ModbusRetryPolicy;
|
|
155
|
+
}): Effect.Effect<EffectModbusClient, ModbusError>;
|
|
156
|
+
/** The link state the shared cache watches. */
|
|
157
|
+
readonly connectionState: SubscriptionRef.SubscriptionRef<ConnectionState>;
|
|
158
|
+
/** The transport's own scope, which the clients and the watcher live in. */
|
|
159
|
+
readonly scope: Scope.Scope;
|
|
160
|
+
}
|
|
161
|
+
/** The batching half of a transport's API. */
|
|
162
|
+
export interface BatchingRegistry {
|
|
163
|
+
/**
|
|
164
|
+
* Declares the batching client for a unit. Fails if the unit already has one.
|
|
165
|
+
*
|
|
166
|
+
* The declaration belongs to the registry's transport scope, not to the
|
|
167
|
+
* calling fiber. Construction therefore continues if the caller is
|
|
168
|
+
* interrupted. A shorter-lived owner that abandons this effect must recover
|
|
169
|
+
* the pending or completed declaration with `batchingClient` rather than
|
|
170
|
+
* trying to declare the unit again.
|
|
171
|
+
*/
|
|
172
|
+
withBatchingClient(unitId: number, options?: BatchingClientOptions & {
|
|
173
|
+
readonly retry?: ModbusRetryPolicy;
|
|
174
|
+
}): Effect.Effect<BatchingModbusClient, ModbusError>;
|
|
175
|
+
/** The batching client for a unit. Fails if nothing has declared one. */
|
|
176
|
+
batchingClient(unitId: number): Effect.Effect<BatchingModbusClient, ModbusError>;
|
|
177
|
+
/** Disables raw holding-register writes once a batching client exists for the unit. */
|
|
178
|
+
guardRawWrites(unitId: number, operations: ModbusOperations): ModbusOperations;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Builds the per-unit batching clients of one transport, and the cache they
|
|
182
|
+
* share.
|
|
183
|
+
*
|
|
184
|
+
* Declaring a client and reaching for one are separate operations, because every
|
|
185
|
+
* option a client takes is a fact about the unit rather than about a caller: two
|
|
186
|
+
* batching clients on one unit hold two batches and coalesce neither, two caches
|
|
187
|
+
* on one unit hold two beliefs about one device, and one batch has one window. A
|
|
188
|
+
* per-caller configuration could only ever be correct when every caller passed
|
|
189
|
+
* the same value, so it is declared once instead of compared at every call.
|
|
190
|
+
*
|
|
191
|
+
* `withBatchingClient` declares. `batchingClient` looks up, and waits for a
|
|
192
|
+
* declaration already under way rather than depending on which fiber ran first.
|
|
193
|
+
*
|
|
194
|
+
* @param deps - The transport's raw client factory, link state, and scope.
|
|
195
|
+
* @returns The batching methods, ready to spread onto a transport API.
|
|
196
|
+
*/
|
|
197
|
+
export declare const createBatchingRegistry: (deps: BatchingRegistryDeps) => BatchingRegistry;
|
package/dist/src/connection.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Data, Duration, Effect, SubscriptionRef } from 'effect';
|
|
1
|
+
import { Data, Duration, Effect, type Scope, SubscriptionRef } from 'effect';
|
|
2
2
|
import { ModbusCircuitOpenError, type ModbusError } from './errors';
|
|
3
3
|
import { type ModbusErrorTag, type ModbusRetryPolicy } from './retry';
|
|
4
4
|
/**
|
|
@@ -12,8 +12,10 @@ import { type ModbusErrorTag, type ModbusRetryPolicy } from './retry';
|
|
|
12
12
|
* - `Connected` — usable.
|
|
13
13
|
* - `Reconnecting` — the supervisor is re-establishing the link. Operations are
|
|
14
14
|
* refused with {@link ModbusCircuitOpenError} rather than queued on a dead bus.
|
|
15
|
-
* - `Down` —
|
|
16
|
-
*
|
|
15
|
+
* - `Down` — a connection failure was observed. With a supervisor, operations
|
|
16
|
+
* are refused while it waits to probe again. Without one, the state remains
|
|
17
|
+
* observable but operations retain manual behavior until `reconnect()` or a
|
|
18
|
+
* successful operation restores `Connected`.
|
|
17
19
|
*/
|
|
18
20
|
export type ConnectionState = Data.TaggedEnum<{
|
|
19
21
|
Disconnected: object;
|
|
@@ -115,7 +117,8 @@ export declare const ConnectionState: {
|
|
|
115
117
|
* Supplying this on a transport hands reconnection to a supervisor fiber owned
|
|
116
118
|
* by that transport: one reconnect for the whole application rather than one
|
|
117
119
|
* per failing call site. Omit it and the transport keeps its manual behaviour —
|
|
118
|
-
* `reconnect()`
|
|
120
|
+
* failures still update `connectionState`, but `reconnect()` must be called by
|
|
121
|
+
* the application and no circuit breaker is enabled.
|
|
119
122
|
*/
|
|
120
123
|
export interface ReconnectOptions {
|
|
121
124
|
/**
|
|
@@ -168,3 +171,27 @@ export declare const guardCircuit: (state: SubscriptionRef.SubscriptionRef<Conne
|
|
|
168
171
|
* @returns An Effect that runs until the link is restored.
|
|
169
172
|
*/
|
|
170
173
|
export declare const superviseReconnect: (reconnect: Effect.Effect<void, ModbusError>, state: SubscriptionRef.SubscriptionRef<ConnectionState>, resolved: ResolvedReconnect) => Effect.Effect<void>;
|
|
174
|
+
/**
|
|
175
|
+
* Claims the `Connected` → `Reconnecting` transition and starts the supervisor.
|
|
176
|
+
*
|
|
177
|
+
* The transition is claimed atomically, so of the fibers that fail together
|
|
178
|
+
* exactly one starts the supervisor and the rest simply carry on failing — one
|
|
179
|
+
* reconnect for the whole transport, not one per call site. Fibers that lose
|
|
180
|
+
* the claim do nothing at all; `onClaim` runs in the winner only, after the
|
|
181
|
+
* transition is published and before the supervisor is forked.
|
|
182
|
+
*
|
|
183
|
+
* Callers decide *whether* a failure is worth reporting (the supervisor's
|
|
184
|
+
* `triggers` predicate, plus whatever local state means "no longer eligible");
|
|
185
|
+
* this owns *how* the transition is made, so the claim protocol has one
|
|
186
|
+
* implementation.
|
|
187
|
+
*
|
|
188
|
+
* @param reconnect - The transport's own reconnect operation.
|
|
189
|
+
* @param state - The state cell the transition is claimed on.
|
|
190
|
+
* @param resolved - Reconnect configuration with defaults applied.
|
|
191
|
+
* @param scope - Scope the supervisor fiber is forked into; it is interrupted
|
|
192
|
+
* when the scope closes.
|
|
193
|
+
* @param onClaim - Optional effect run by the claiming fiber only.
|
|
194
|
+
* @returns An Effect that completes once the supervisor is forked, or
|
|
195
|
+
* immediately when another fiber already holds the claim.
|
|
196
|
+
*/
|
|
197
|
+
export declare const claimReconnect: (reconnect: Effect.Effect<void, ModbusError>, state: SubscriptionRef.SubscriptionRef<ConnectionState>, resolved: ResolvedReconnect, scope: Scope.Scope, onClaim?: Effect.Effect<void>) => Effect.Effect<void>;
|
package/dist/src/mocks.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Effect, Schema, SubscriptionRef } from 'effect';
|
|
1
|
+
import { Effect, Schema, Scope, SubscriptionRef } from 'effect';
|
|
2
2
|
import { type AsciiTransportOptions, type RtuTransportOptions, type TcpTransportOptions } from 'modbus-rs';
|
|
3
3
|
import type { WasmWsTransportOptions, WasmSerialTransportOptions } from 'modbus-rs/web';
|
|
4
|
-
import {
|
|
4
|
+
import { type BatchingClientOptions } from './batching-client';
|
|
5
|
+
import { ModbusInvalidArgumentError, ModbusNotConnectedError, type ModbusError } from './errors';
|
|
5
6
|
import type { ModbusRetryPolicy } from './retry';
|
|
6
7
|
import type { TransportResilienceOptions, WithoutUpstreamRetry } from './shared-transport';
|
|
7
8
|
/**
|
|
@@ -135,7 +136,7 @@ export type SlaveDeviceDefinitions = Schema.Schema.Type<typeof SlaveDeviceDefini
|
|
|
135
136
|
* @returns A transport factory function that returns a scoped Effect
|
|
136
137
|
* providing the mock transport.
|
|
137
138
|
*/
|
|
138
|
-
export declare const
|
|
139
|
+
export declare const createMockTransport: (devices: SlaveDeviceDefinitions) => (options: (WithoutUpstreamRetry<RtuTransportOptions> | WithoutUpstreamRetry<AsciiTransportOptions> | WithoutUpstreamRetry<TcpTransportOptions> | WasmWsTransportOptions | WasmSerialTransportOptions) & TransportResilienceOptions & MockFaultOptions) => Effect.Effect<{
|
|
139
140
|
connectionState: SubscriptionRef.SubscriptionRef<{
|
|
140
141
|
readonly _tag: "Connected";
|
|
141
142
|
} | {
|
|
@@ -149,10 +150,15 @@ export declare const makeMockTransport: (devices: SlaveDeviceDefinitions) => (op
|
|
|
149
150
|
}>;
|
|
150
151
|
withClient: (unitId: number, clientOptions?: {
|
|
151
152
|
readonly retry?: ModbusRetryPolicy;
|
|
152
|
-
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, ModbusInvalidArgumentError, never>;
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, ModbusInvalidArgumentError | ModbusNotConnectedError, never>;
|
|
154
|
+
withBatchingClient: (unitId: number, clientOptions?: BatchingClientOptions & {
|
|
155
|
+
readonly retry?: ModbusRetryPolicy;
|
|
156
|
+
}) => Effect.Effect<import("./batching-client").BatchingModbusClient, ModbusError, never>;
|
|
157
|
+
batchingClient: (unitId: number) => Effect.Effect<import("./batching-client").BatchingModbusClient, ModbusError, never>;
|
|
158
|
+
readonly touchedUnits: Set<number>;
|
|
159
|
+
setRequestTimeout: (_timeoutMs: number) => Effect.Effect<void, ModbusNotConnectedError, never>;
|
|
160
|
+
clearRequestTimeout: () => Effect.Effect<void, ModbusNotConnectedError, never>;
|
|
155
161
|
reconnect: () => Effect.Effect<void, ModbusError, never>;
|
|
156
|
-
close: () => Effect.Effect<void,
|
|
162
|
+
close: () => Effect.Effect<void, never, Scope.Scope>;
|
|
157
163
|
hasPendingRequests: () => false;
|
|
158
|
-
}, never,
|
|
164
|
+
}, never, Scope.Scope>;
|
|
@@ -188,7 +188,7 @@ export interface ModbusOperations {
|
|
|
188
188
|
* @see AsyncSerialModbusClient — Upstream native serial client API.
|
|
189
189
|
* @see AsyncTcpModbusClient — Upstream native TCP client API.
|
|
190
190
|
*/
|
|
191
|
-
export declare const
|
|
191
|
+
export declare const createEffectModbusClient: (client: AnyModbusClient) => ModbusOperations;
|
|
192
192
|
/**
|
|
193
193
|
* Transport-owned resilience applied to every operation of a client.
|
|
194
194
|
*
|
|
@@ -199,6 +199,8 @@ export declare const makeEffectModbusClient: (client: AnyModbusClient) => Modbus
|
|
|
199
199
|
export interface ClientResilience {
|
|
200
200
|
/** Refuses the operation while the transport's circuit is open. */
|
|
201
201
|
readonly guard: Effect.Effect<void, ModbusError>;
|
|
202
|
+
/** Records that an operation reached the device successfully. */
|
|
203
|
+
readonly onSuccess?: Effect.Effect<void>;
|
|
202
204
|
/** Reports a failure so the transport can decide whether to reconnect. */
|
|
203
205
|
readonly report: (error: ModbusError) => Effect.Effect<void>;
|
|
204
206
|
/** Retry policy applied to each operation, if any. */
|