@flux-control/effect-modbus-rs 0.2.0 → 0.3.1
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 +256 -4
- package/dist/index.d.ts +54 -0
- package/dist/index.js +394 -173
- package/dist/src/AsciiTransportService.d.ts +26 -5
- package/dist/src/RtuTransportService.d.ts +26 -5
- package/dist/src/SerialTransportService.d.ts +21 -10
- package/dist/src/TcpTransportService.d.ts +26 -5
- package/dist/src/WasmAsciiTransportService.d.ts +20 -3
- package/dist/src/WasmRtuTransportService.d.ts +20 -3
- package/dist/src/WasmSerialTransportService.d.ts +19 -9
- package/dist/src/WasmWsTransportService.d.ts +18 -3
- package/dist/src/connection.d.ts +170 -0
- package/dist/src/errors.d.ts +25 -1
- package/dist/src/mock-options.test.d.ts +1 -0
- package/dist/src/mocks.d.ts +46 -7
- package/dist/src/modbus-client.d.ts +54 -3
- package/dist/src/resilience.test.d.ts +1 -0
- package/dist/src/retry.d.ts +227 -0
- package/dist/src/retry.test.d.ts +1 -0
- package/dist/src/shared-transport.d.ts +107 -6
- package/dist/src/shared-transport.test.d.ts +1 -0
- package/dist/src/upstream-options.test.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import { Effect, Layer } from 'effect';
|
|
2
2
|
import type { AsciiTransportOptions } from 'modbus-rs';
|
|
3
3
|
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
+
import type { MockFaultOptions } from './mocks';
|
|
5
|
+
import type { TransportResilienceOptions, WithoutUpstreamRetry } from './shared-transport';
|
|
6
|
+
/**
|
|
7
|
+
* {@link AsciiTransportOptions} minus the upstream retry knobs.
|
|
8
|
+
*
|
|
9
|
+
* @see WithoutUpstreamRetry — Why they are withheld.
|
|
10
|
+
*/
|
|
11
|
+
export type AsciiTransportOpenOptions = WithoutUpstreamRetry<AsciiTransportOptions>;
|
|
4
12
|
declare const AsciiTransportService_base: Effect.Service.Class<AsciiTransportService, "AsciiTransportService", {
|
|
5
|
-
readonly scoped: (options:
|
|
6
|
-
|
|
13
|
+
readonly scoped: (options: AsciiTransportOpenOptions & TransportResilienceOptions) => Effect.Effect<{
|
|
14
|
+
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
15
|
+
readonly _tag: "Connected";
|
|
16
|
+
} | {
|
|
17
|
+
readonly _tag: "Disconnected";
|
|
18
|
+
} | {
|
|
19
|
+
readonly _tag: "Down";
|
|
20
|
+
readonly cause: import("./errors").ModbusError;
|
|
21
|
+
} | {
|
|
22
|
+
readonly _tag: "Reconnecting";
|
|
23
|
+
readonly attempt: number;
|
|
24
|
+
}>;
|
|
25
|
+
withClient: (unitId: number, clientOptions?: {
|
|
26
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
27
|
+
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
7
28
|
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
8
29
|
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
9
30
|
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
@@ -24,7 +45,7 @@ declare const AsciiTransportService_base: Effect.Service.Class<AsciiTransportSer
|
|
|
24
45
|
* requests for the same unit ID reuse the same client.
|
|
25
46
|
*
|
|
26
47
|
* @see AsyncAsciiTransport — Upstream `modbus-rs` ASCII transport.
|
|
27
|
-
* @see
|
|
48
|
+
* @see AsciiTransportOpenOptions — Configuration for the ASCII serial port.
|
|
28
49
|
* @see makeTransportScoped — Generic lifecycle logic from shared-transport.
|
|
29
50
|
*/
|
|
30
51
|
export declare class AsciiTransportService extends AsciiTransportService_base {
|
|
@@ -36,11 +57,11 @@ export declare class AsciiTransportService extends AsciiTransportService_base {
|
|
|
36
57
|
* simulated Modbus slaves and their register/coil maps.
|
|
37
58
|
*
|
|
38
59
|
* @param devices - Slave device definitions for the mock.
|
|
39
|
-
* @returns A function that takes {@link
|
|
60
|
+
* @returns A function that takes {@link AsciiTransportOpenOptions} and
|
|
40
61
|
* returns a scoped {@link Layer} providing the mock service.
|
|
41
62
|
*
|
|
42
63
|
* @see makeMockTransport — The underlying mock factory.
|
|
43
64
|
*/
|
|
44
|
-
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options:
|
|
65
|
+
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: AsciiTransportOpenOptions & TransportResilienceOptions & MockFaultOptions) => Layer.Layer<AsciiTransportService, never, never>;
|
|
45
66
|
}
|
|
46
67
|
export {};
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import { Effect, Layer } from 'effect';
|
|
2
2
|
import type { RtuTransportOptions } from 'modbus-rs';
|
|
3
|
+
import type { MockFaultOptions } from './mocks';
|
|
3
4
|
import type { SlaveDeviceDefinitions } from './mocks';
|
|
5
|
+
import type { TransportResilienceOptions, WithoutUpstreamRetry } from './shared-transport';
|
|
6
|
+
/**
|
|
7
|
+
* {@link RtuTransportOptions} minus the upstream retry knobs.
|
|
8
|
+
*
|
|
9
|
+
* @see WithoutUpstreamRetry — Why they are withheld.
|
|
10
|
+
*/
|
|
11
|
+
export type RtuTransportOpenOptions = WithoutUpstreamRetry<RtuTransportOptions>;
|
|
4
12
|
declare const RtuTransportService_base: Effect.Service.Class<RtuTransportService, "RtuTransportService", {
|
|
5
|
-
readonly scoped: (options:
|
|
6
|
-
|
|
13
|
+
readonly scoped: (options: RtuTransportOpenOptions & TransportResilienceOptions) => Effect.Effect<{
|
|
14
|
+
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
15
|
+
readonly _tag: "Connected";
|
|
16
|
+
} | {
|
|
17
|
+
readonly _tag: "Disconnected";
|
|
18
|
+
} | {
|
|
19
|
+
readonly _tag: "Down";
|
|
20
|
+
readonly cause: import("./errors").ModbusError;
|
|
21
|
+
} | {
|
|
22
|
+
readonly _tag: "Reconnecting";
|
|
23
|
+
readonly attempt: number;
|
|
24
|
+
}>;
|
|
25
|
+
withClient: (unitId: number, clientOptions?: {
|
|
26
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
27
|
+
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
7
28
|
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
8
29
|
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
9
30
|
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
@@ -24,7 +45,7 @@ declare const RtuTransportService_base: Effect.Service.Class<RtuTransportService
|
|
|
24
45
|
* requests for the same unit ID reuse the same client.
|
|
25
46
|
*
|
|
26
47
|
* @see AsyncRtuTransport — Upstream `modbus-rs` RTU transport.
|
|
27
|
-
* @see
|
|
48
|
+
* @see RtuTransportOpenOptions — Configuration for the RTU serial port.
|
|
28
49
|
* @see makeTransportScoped — Generic lifecycle logic from shared-transport.
|
|
29
50
|
*/
|
|
30
51
|
export declare class RtuTransportService extends RtuTransportService_base {
|
|
@@ -36,11 +57,11 @@ export declare class RtuTransportService extends RtuTransportService_base {
|
|
|
36
57
|
* simulated Modbus slaves and their register/coil maps.
|
|
37
58
|
*
|
|
38
59
|
* @param devices - Slave device definitions for the mock.
|
|
39
|
-
* @returns A function that takes {@link
|
|
60
|
+
* @returns A function that takes {@link RtuTransportOpenOptions} and
|
|
40
61
|
* returns a scoped {@link Layer} providing the mock service.
|
|
41
62
|
*
|
|
42
63
|
* @see makeMockTransport — The underlying mock factory.
|
|
43
64
|
*/
|
|
44
|
-
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options:
|
|
65
|
+
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: RtuTransportOpenOptions & TransportResilienceOptions & MockFaultOptions) => Layer.Layer<RtuTransportService, never, never>;
|
|
45
66
|
}
|
|
46
67
|
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Context, Layer } from 'effect';
|
|
2
|
-
import
|
|
3
|
-
import { type SlaveDeviceDefinitions } from './mocks';
|
|
4
|
-
import type
|
|
2
|
+
import { type AsciiTransportOpenOptions } from './AsciiTransportService';
|
|
3
|
+
import { type MockFaultOptions, type SlaveDeviceDefinitions } from './mocks';
|
|
4
|
+
import { type RtuTransportOpenOptions } from './RtuTransportService';
|
|
5
|
+
import type { TransportResilienceOptions, TransportServiceApi } from './shared-transport';
|
|
5
6
|
declare const SerialTransportService_base: Context.TagClass<SerialTransportService, "SerialTransportService", TransportServiceApi>;
|
|
6
7
|
/**
|
|
7
8
|
* Abstract serial Modbus transport service tag.
|
|
@@ -27,19 +28,29 @@ export declare class SerialTransportService extends SerialTransportService_base
|
|
|
27
28
|
* Creates a {@link Layer} providing {@link SerialTransportService}
|
|
28
29
|
* backed by an ASCII transport.
|
|
29
30
|
*/
|
|
30
|
-
static fromAscii(options:
|
|
31
|
+
static fromAscii(options: AsciiTransportOpenOptions & TransportResilienceOptions): Layer.Layer<SerialTransportService>;
|
|
31
32
|
/**
|
|
32
33
|
* Creates a {@link Layer} providing {@link SerialTransportService}
|
|
33
34
|
* backed by an RTU transport.
|
|
34
35
|
*/
|
|
35
|
-
static fromRtu(options:
|
|
36
|
+
static fromRtu(options: RtuTransportOpenOptions & TransportResilienceOptions): Layer.Layer<SerialTransportService>;
|
|
36
37
|
/**
|
|
37
|
-
* Creates a mock {@link Layer}
|
|
38
|
-
* for
|
|
38
|
+
* Creates a mock {@link Layer} that provides {@link SerialTransportService}
|
|
39
|
+
* for tests or development.
|
|
39
40
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
41
|
+
* The `devices` parameter is an array of {@link SlaveDeviceDefinition}. Each
|
|
42
|
+
* definition gives the coil map and the register map of one simulated slave.
|
|
43
|
+
*
|
|
44
|
+
* The option set is the same as the option set of the concrete tags. Thus a
|
|
45
|
+
* test that keeps the framing abstract can also set `retry`, `reconnect`,
|
|
46
|
+
* `fault`, and `reconnectFault`.
|
|
47
|
+
*
|
|
48
|
+
* @param devices - The slave device definitions for the mock.
|
|
49
|
+
* @returns A function that takes the mock options and gives a scoped
|
|
50
|
+
* {@link Layer} that provides the mock service.
|
|
51
|
+
* @see MockFaultOptions — The `fault` hook and the `reconnectFault` hook.
|
|
52
|
+
* @see makeMockTransport — The mock factory that this method uses.
|
|
42
53
|
*/
|
|
43
|
-
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options:
|
|
54
|
+
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: (AsciiTransportOpenOptions | RtuTransportOpenOptions) & TransportResilienceOptions & MockFaultOptions) => Layer.Layer<SerialTransportService>;
|
|
44
55
|
}
|
|
45
56
|
export {};
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import { Effect, Layer } from 'effect';
|
|
2
2
|
import type { TcpTransportOptions } from 'modbus-rs';
|
|
3
3
|
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
+
import type { MockFaultOptions } from './mocks';
|
|
5
|
+
import type { TransportResilienceOptions, WithoutUpstreamRetry } from './shared-transport';
|
|
6
|
+
/**
|
|
7
|
+
* {@link TcpTransportOptions} minus the upstream retry knobs.
|
|
8
|
+
*
|
|
9
|
+
* @see WithoutUpstreamRetry — Why they are withheld.
|
|
10
|
+
*/
|
|
11
|
+
export type TcpTransportOpenOptions = WithoutUpstreamRetry<TcpTransportOptions>;
|
|
4
12
|
declare const TcpTransportService_base: Effect.Service.Class<TcpTransportService, "TcpTransportService", {
|
|
5
|
-
readonly scoped: (options:
|
|
6
|
-
|
|
13
|
+
readonly scoped: (options: TcpTransportOpenOptions & TransportResilienceOptions) => Effect.Effect<{
|
|
14
|
+
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
15
|
+
readonly _tag: "Connected";
|
|
16
|
+
} | {
|
|
17
|
+
readonly _tag: "Disconnected";
|
|
18
|
+
} | {
|
|
19
|
+
readonly _tag: "Down";
|
|
20
|
+
readonly cause: import("./errors").ModbusError;
|
|
21
|
+
} | {
|
|
22
|
+
readonly _tag: "Reconnecting";
|
|
23
|
+
readonly attempt: number;
|
|
24
|
+
}>;
|
|
25
|
+
withClient: (unitId: number, clientOptions?: {
|
|
26
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
27
|
+
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
7
28
|
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
8
29
|
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
9
30
|
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
@@ -24,7 +45,7 @@ declare const TcpTransportService_base: Effect.Service.Class<TcpTransportService
|
|
|
24
45
|
* requests for the same unit ID reuse the same client.
|
|
25
46
|
*
|
|
26
47
|
* @see AsyncTcpTransport — Upstream `modbus-rs` TCP transport.
|
|
27
|
-
* @see
|
|
48
|
+
* @see TcpTransportOpenOptions — Configuration for the TCP connection.
|
|
28
49
|
* @see makeTransportScoped — Generic lifecycle logic from shared-transport.
|
|
29
50
|
*/
|
|
30
51
|
export declare class TcpTransportService extends TcpTransportService_base {
|
|
@@ -36,11 +57,11 @@ export declare class TcpTransportService extends TcpTransportService_base {
|
|
|
36
57
|
* simulated Modbus slaves and their register/coil maps.
|
|
37
58
|
*
|
|
38
59
|
* @param devices - Slave device definitions for the mock.
|
|
39
|
-
* @returns A function that takes {@link
|
|
60
|
+
* @returns A function that takes {@link TcpTransportOpenOptions} and
|
|
40
61
|
* returns a scoped {@link Layer} providing the mock service.
|
|
41
62
|
*
|
|
42
63
|
* @see makeMockTransport — The underlying mock factory.
|
|
43
64
|
*/
|
|
44
|
-
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options:
|
|
65
|
+
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: TcpTransportOpenOptions & TransportResilienceOptions & MockFaultOptions) => Layer.Layer<TcpTransportService, never, never>;
|
|
45
66
|
}
|
|
46
67
|
export {};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Effect, Layer } from 'effect';
|
|
2
2
|
import type { WasmSerialPortHandle, WasmSerialTransportOptions } from 'modbus-rs/web';
|
|
3
3
|
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
+
import type { MockFaultOptions } from './mocks';
|
|
5
|
+
import type { TransportResilienceOptions } from './shared-transport';
|
|
4
6
|
/**
|
|
5
7
|
* Options for {@link WasmAsciiTransportService}. `WasmAsciiTransport.open()` takes the
|
|
6
8
|
* serial port handle and the connection options as two separate arguments; this
|
|
@@ -13,8 +15,23 @@ export type WasmAsciiTransportOpenOptions = WasmSerialTransportOptions & {
|
|
|
13
15
|
port: WasmSerialPortHandle;
|
|
14
16
|
};
|
|
15
17
|
declare const WasmAsciiTransportService_base: Effect.Service.Class<WasmAsciiTransportService, "WasmAsciiTransportService", {
|
|
16
|
-
readonly scoped: (options:
|
|
17
|
-
|
|
18
|
+
readonly scoped: (options: WasmSerialTransportOptions & {
|
|
19
|
+
port: WasmSerialPortHandle;
|
|
20
|
+
} & TransportResilienceOptions) => Effect.Effect<{
|
|
21
|
+
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
22
|
+
readonly _tag: "Connected";
|
|
23
|
+
} | {
|
|
24
|
+
readonly _tag: "Disconnected";
|
|
25
|
+
} | {
|
|
26
|
+
readonly _tag: "Down";
|
|
27
|
+
readonly cause: import("./errors").ModbusError;
|
|
28
|
+
} | {
|
|
29
|
+
readonly _tag: "Reconnecting";
|
|
30
|
+
readonly attempt: number;
|
|
31
|
+
}>;
|
|
32
|
+
withClient: (unitId: number, clientOptions?: {
|
|
33
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
34
|
+
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
18
35
|
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
19
36
|
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
20
37
|
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
@@ -51,6 +68,6 @@ export declare class WasmAsciiTransportService extends WasmAsciiTransportService
|
|
|
51
68
|
*
|
|
52
69
|
* @see makeMockTransport — The underlying mock factory.
|
|
53
70
|
*/
|
|
54
|
-
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: WasmAsciiTransportOpenOptions) => Layer.Layer<WasmAsciiTransportService, never, never>;
|
|
71
|
+
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: WasmAsciiTransportOpenOptions & TransportResilienceOptions & MockFaultOptions) => Layer.Layer<WasmAsciiTransportService, never, never>;
|
|
55
72
|
}
|
|
56
73
|
export {};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Effect, Layer } from 'effect';
|
|
2
2
|
import type { WasmSerialPortHandle, WasmSerialTransportOptions } from 'modbus-rs/web';
|
|
3
3
|
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
+
import type { MockFaultOptions } from './mocks';
|
|
5
|
+
import type { TransportResilienceOptions } from './shared-transport';
|
|
4
6
|
/**
|
|
5
7
|
* Options for {@link WasmRtuTransportService}. `WasmRtuTransport.open()` takes the
|
|
6
8
|
* serial port handle and the connection options as two separate arguments; this
|
|
@@ -13,8 +15,23 @@ export type WasmRtuTransportOpenOptions = WasmSerialTransportOptions & {
|
|
|
13
15
|
port: WasmSerialPortHandle;
|
|
14
16
|
};
|
|
15
17
|
declare const WasmRtuTransportService_base: Effect.Service.Class<WasmRtuTransportService, "WasmRtuTransportService", {
|
|
16
|
-
readonly scoped: (options:
|
|
17
|
-
|
|
18
|
+
readonly scoped: (options: WasmSerialTransportOptions & {
|
|
19
|
+
port: WasmSerialPortHandle;
|
|
20
|
+
} & TransportResilienceOptions) => Effect.Effect<{
|
|
21
|
+
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
22
|
+
readonly _tag: "Connected";
|
|
23
|
+
} | {
|
|
24
|
+
readonly _tag: "Disconnected";
|
|
25
|
+
} | {
|
|
26
|
+
readonly _tag: "Down";
|
|
27
|
+
readonly cause: import("./errors").ModbusError;
|
|
28
|
+
} | {
|
|
29
|
+
readonly _tag: "Reconnecting";
|
|
30
|
+
readonly attempt: number;
|
|
31
|
+
}>;
|
|
32
|
+
withClient: (unitId: number, clientOptions?: {
|
|
33
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
34
|
+
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
18
35
|
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
19
36
|
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
20
37
|
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
@@ -51,6 +68,6 @@ export declare class WasmRtuTransportService extends WasmRtuTransportService_bas
|
|
|
51
68
|
*
|
|
52
69
|
* @see makeMockTransport — The underlying mock factory.
|
|
53
70
|
*/
|
|
54
|
-
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: WasmRtuTransportOpenOptions) => Layer.Layer<WasmRtuTransportService, never, never>;
|
|
71
|
+
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: WasmRtuTransportOpenOptions & TransportResilienceOptions & MockFaultOptions) => Layer.Layer<WasmRtuTransportService, never, never>;
|
|
55
72
|
}
|
|
56
73
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Context, Layer } from 'effect';
|
|
2
|
-
import { type SlaveDeviceDefinitions } from './mocks';
|
|
3
|
-
import type { TransportServiceApi } from './shared-transport';
|
|
2
|
+
import { type MockFaultOptions, type SlaveDeviceDefinitions } from './mocks';
|
|
3
|
+
import type { TransportResilienceOptions, TransportServiceApi } from './shared-transport';
|
|
4
4
|
import { type WasmAsciiTransportOpenOptions } from './WasmAsciiTransportService';
|
|
5
5
|
import { type WasmRtuTransportOpenOptions } from './WasmRtuTransportService';
|
|
6
6
|
declare const WasmSerialTransportService_base: Context.TagClass<WasmSerialTransportService, "WasmSerialTransportService", TransportServiceApi>;
|
|
@@ -30,19 +30,29 @@ export declare class WasmSerialTransportService extends WasmSerialTransportServi
|
|
|
30
30
|
* Creates a {@link Layer} providing {@link WasmSerialTransportService}
|
|
31
31
|
* backed by an ASCII transport.
|
|
32
32
|
*/
|
|
33
|
-
static fromAscii(options: WasmAsciiTransportOpenOptions): Layer.Layer<WasmSerialTransportService>;
|
|
33
|
+
static fromAscii(options: WasmAsciiTransportOpenOptions & TransportResilienceOptions): Layer.Layer<WasmSerialTransportService>;
|
|
34
34
|
/**
|
|
35
35
|
* Creates a {@link Layer} providing {@link WasmSerialTransportService}
|
|
36
36
|
* backed by an RTU transport.
|
|
37
37
|
*/
|
|
38
|
-
static fromRtu(options: WasmRtuTransportOpenOptions): Layer.Layer<WasmSerialTransportService>;
|
|
38
|
+
static fromRtu(options: WasmRtuTransportOpenOptions & TransportResilienceOptions): Layer.Layer<WasmSerialTransportService>;
|
|
39
39
|
/**
|
|
40
|
-
* Creates a mock {@link Layer}
|
|
41
|
-
* for
|
|
40
|
+
* Creates a mock {@link Layer} that provides {@link WasmSerialTransportService}
|
|
41
|
+
* for tests or development.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* The `devices` parameter is an array of {@link SlaveDeviceDefinition}. Each
|
|
44
|
+
* definition gives the coil map and the register map of one simulated slave.
|
|
45
|
+
*
|
|
46
|
+
* The option set is the same as the option set of the concrete tags. Thus a
|
|
47
|
+
* test that keeps the framing abstract can also set `retry`, `reconnect`,
|
|
48
|
+
* `fault`, and `reconnectFault`.
|
|
49
|
+
*
|
|
50
|
+
* @param devices - The slave device definitions for the mock.
|
|
51
|
+
* @returns A function that takes the mock options and gives a scoped
|
|
52
|
+
* {@link Layer} that provides the mock service.
|
|
53
|
+
* @see MockFaultOptions — The `fault` hook and the `reconnectFault` hook.
|
|
54
|
+
* @see makeMockTransport — The mock factory that this method uses.
|
|
45
55
|
*/
|
|
46
|
-
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: WasmAsciiTransportOpenOptions | WasmRtuTransportOpenOptions) => Layer.Layer<WasmSerialTransportService>;
|
|
56
|
+
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: (WasmAsciiTransportOpenOptions | WasmRtuTransportOpenOptions) & TransportResilienceOptions & MockFaultOptions) => Layer.Layer<WasmSerialTransportService>;
|
|
47
57
|
}
|
|
48
58
|
export {};
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import { Effect, Layer } from 'effect';
|
|
2
2
|
import type { WasmWsTransportOptions } from 'modbus-rs/web';
|
|
3
3
|
import { SlaveDeviceDefinitions } from './mocks';
|
|
4
|
+
import type { MockFaultOptions } from './mocks';
|
|
5
|
+
import type { TransportResilienceOptions } from './shared-transport';
|
|
4
6
|
declare const WasmWsTransportService_base: Effect.Service.Class<WasmWsTransportService, "WasmWsTransportService", {
|
|
5
|
-
readonly scoped: (options: WasmWsTransportOptions) => Effect.Effect<{
|
|
6
|
-
|
|
7
|
+
readonly scoped: (options: WasmWsTransportOptions & TransportResilienceOptions) => Effect.Effect<{
|
|
8
|
+
connectionState: import("effect/SubscriptionRef").SubscriptionRef<{
|
|
9
|
+
readonly _tag: "Connected";
|
|
10
|
+
} | {
|
|
11
|
+
readonly _tag: "Disconnected";
|
|
12
|
+
} | {
|
|
13
|
+
readonly _tag: "Down";
|
|
14
|
+
readonly cause: import("./errors").ModbusError;
|
|
15
|
+
} | {
|
|
16
|
+
readonly _tag: "Reconnecting";
|
|
17
|
+
readonly attempt: number;
|
|
18
|
+
}>;
|
|
19
|
+
withClient: (unitId: number, clientOptions?: {
|
|
20
|
+
readonly retry?: import("./retry").ModbusRetryPolicy;
|
|
21
|
+
} | undefined) => Effect.Effect<import("./modbus-client").EffectModbusClient, import("./errors").ModbusError, never>;
|
|
7
22
|
setRequestTimeout: (timeoutMs: number) => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
8
23
|
clearRequestTimeout: () => Effect.Effect<undefined, import("./errors").ModbusNotConnectedError, never>;
|
|
9
24
|
reconnect: () => Effect.Effect<undefined, import("./errors").ModbusError, never>;
|
|
@@ -40,6 +55,6 @@ export declare class WasmWsTransportService extends WasmWsTransportService_base
|
|
|
40
55
|
*
|
|
41
56
|
* @see makeMockTransport — The underlying mock factory.
|
|
42
57
|
*/
|
|
43
|
-
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: WasmWsTransportOptions) => Layer.Layer<WasmWsTransportService, never, never>;
|
|
58
|
+
static makeMockTransport: (devices: SlaveDeviceDefinitions) => (options: WasmWsTransportOptions & TransportResilienceOptions & MockFaultOptions) => Layer.Layer<WasmWsTransportService, never, never>;
|
|
44
59
|
}
|
|
45
60
|
export {};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Data, Duration, Effect, SubscriptionRef } from 'effect';
|
|
2
|
+
import { ModbusCircuitOpenError, type ModbusError } from './errors';
|
|
3
|
+
import { type ModbusErrorTag, type ModbusRetryPolicy } from './retry';
|
|
4
|
+
/**
|
|
5
|
+
* Live connection state of a transport.
|
|
6
|
+
*
|
|
7
|
+
* Owned by the transport and published through
|
|
8
|
+
* {@link TransportServiceApi.connectionState}, so an application can show link
|
|
9
|
+
* status without inferring it from failed reads.
|
|
10
|
+
*
|
|
11
|
+
* - `Disconnected` — never opened, or closed. Operations open it lazily.
|
|
12
|
+
* - `Connected` — usable.
|
|
13
|
+
* - `Reconnecting` — the supervisor is re-establishing the link. Operations are
|
|
14
|
+
* refused with {@link ModbusCircuitOpenError} rather than queued on a dead bus.
|
|
15
|
+
* - `Down` — reconnect attempts were exhausted; the supervisor is waiting out
|
|
16
|
+
* `resetAfter` before probing again. Operations are refused.
|
|
17
|
+
*/
|
|
18
|
+
export type ConnectionState = Data.TaggedEnum<{
|
|
19
|
+
Disconnected: object;
|
|
20
|
+
Connected: object;
|
|
21
|
+
Reconnecting: {
|
|
22
|
+
readonly attempt: number;
|
|
23
|
+
};
|
|
24
|
+
Down: {
|
|
25
|
+
readonly cause: ModbusError;
|
|
26
|
+
};
|
|
27
|
+
}>;
|
|
28
|
+
/** Constructors and matchers for {@link ConnectionState}. */
|
|
29
|
+
export declare const ConnectionState: {
|
|
30
|
+
readonly $is: <Tag extends "Connected" | "Disconnected" | "Down" | "Reconnecting">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
31
|
+
readonly _tag: "Connected";
|
|
32
|
+
}, {
|
|
33
|
+
readonly _tag: Tag;
|
|
34
|
+
}> | Extract<{
|
|
35
|
+
readonly _tag: "Disconnected";
|
|
36
|
+
}, {
|
|
37
|
+
readonly _tag: Tag;
|
|
38
|
+
}> | Extract<{
|
|
39
|
+
readonly _tag: "Down";
|
|
40
|
+
readonly cause: ModbusError;
|
|
41
|
+
}, {
|
|
42
|
+
readonly _tag: Tag;
|
|
43
|
+
}> | Extract<{
|
|
44
|
+
readonly _tag: "Reconnecting";
|
|
45
|
+
readonly attempt: number;
|
|
46
|
+
}, {
|
|
47
|
+
readonly _tag: Tag;
|
|
48
|
+
}>;
|
|
49
|
+
readonly $match: {
|
|
50
|
+
<const Cases extends { readonly [Tag in "Connected" | "Disconnected" | "Down" | "Reconnecting"]: (args: Extract<{
|
|
51
|
+
readonly _tag: "Connected";
|
|
52
|
+
} | {
|
|
53
|
+
readonly _tag: "Disconnected";
|
|
54
|
+
} | {
|
|
55
|
+
readonly _tag: "Down";
|
|
56
|
+
readonly cause: ModbusError;
|
|
57
|
+
} | {
|
|
58
|
+
readonly _tag: "Reconnecting";
|
|
59
|
+
readonly attempt: number;
|
|
60
|
+
}, {
|
|
61
|
+
readonly _tag: Tag;
|
|
62
|
+
}>) => any; }>(cases: Cases & { [K in Exclude<keyof Cases, "Connected" | "Disconnected" | "Down" | "Reconnecting">]: never; }): (value: {
|
|
63
|
+
readonly _tag: "Connected";
|
|
64
|
+
} | {
|
|
65
|
+
readonly _tag: "Disconnected";
|
|
66
|
+
} | {
|
|
67
|
+
readonly _tag: "Down";
|
|
68
|
+
readonly cause: ModbusError;
|
|
69
|
+
} | {
|
|
70
|
+
readonly _tag: "Reconnecting";
|
|
71
|
+
readonly attempt: number;
|
|
72
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Connected" | "Disconnected" | "Down" | "Reconnecting"]>>;
|
|
73
|
+
<const Cases extends { readonly [Tag in "Connected" | "Disconnected" | "Down" | "Reconnecting"]: (args: Extract<{
|
|
74
|
+
readonly _tag: "Connected";
|
|
75
|
+
} | {
|
|
76
|
+
readonly _tag: "Disconnected";
|
|
77
|
+
} | {
|
|
78
|
+
readonly _tag: "Down";
|
|
79
|
+
readonly cause: ModbusError;
|
|
80
|
+
} | {
|
|
81
|
+
readonly _tag: "Reconnecting";
|
|
82
|
+
readonly attempt: number;
|
|
83
|
+
}, {
|
|
84
|
+
readonly _tag: Tag;
|
|
85
|
+
}>) => any; }>(value: {
|
|
86
|
+
readonly _tag: "Connected";
|
|
87
|
+
} | {
|
|
88
|
+
readonly _tag: "Disconnected";
|
|
89
|
+
} | {
|
|
90
|
+
readonly _tag: "Down";
|
|
91
|
+
readonly cause: ModbusError;
|
|
92
|
+
} | {
|
|
93
|
+
readonly _tag: "Reconnecting";
|
|
94
|
+
readonly attempt: number;
|
|
95
|
+
}, cases: Cases & { [K in Exclude<keyof Cases, "Connected" | "Disconnected" | "Down" | "Reconnecting">]: never; }): import("effect/Unify").Unify<ReturnType<Cases["Connected" | "Disconnected" | "Down" | "Reconnecting"]>>;
|
|
96
|
+
};
|
|
97
|
+
readonly Connected: Data.Case.Constructor<{
|
|
98
|
+
readonly _tag: "Connected";
|
|
99
|
+
}, "_tag">;
|
|
100
|
+
readonly Disconnected: Data.Case.Constructor<{
|
|
101
|
+
readonly _tag: "Disconnected";
|
|
102
|
+
}, "_tag">;
|
|
103
|
+
readonly Down: Data.Case.Constructor<{
|
|
104
|
+
readonly _tag: "Down";
|
|
105
|
+
readonly cause: ModbusError;
|
|
106
|
+
}, "_tag">;
|
|
107
|
+
readonly Reconnecting: Data.Case.Constructor<{
|
|
108
|
+
readonly _tag: "Reconnecting";
|
|
109
|
+
readonly attempt: number;
|
|
110
|
+
}, "_tag">;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Transport-level reconnection and circuit-breaking configuration.
|
|
114
|
+
*
|
|
115
|
+
* Supplying this on a transport hands reconnection to a supervisor fiber owned
|
|
116
|
+
* by that transport: one reconnect for the whole application rather than one
|
|
117
|
+
* per failing call site. Omit it and the transport keeps its manual behaviour —
|
|
118
|
+
* `reconnect()` still works, nothing happens on its own.
|
|
119
|
+
*/
|
|
120
|
+
export interface ReconnectOptions {
|
|
121
|
+
/**
|
|
122
|
+
* How reconnect attempts are spaced. Defaults to 5 attempts, `250 millis`
|
|
123
|
+
* base, factor 2, `10 seconds` ceiling, jittered.
|
|
124
|
+
*/
|
|
125
|
+
readonly policy?: ModbusRetryPolicy;
|
|
126
|
+
/**
|
|
127
|
+
* How long the circuit stays open after attempts are exhausted, before the
|
|
128
|
+
* supervisor probes again. Default `30 seconds`.
|
|
129
|
+
*/
|
|
130
|
+
readonly resetAfter?: Duration.DurationInput;
|
|
131
|
+
/**
|
|
132
|
+
* Which operation failures hand control to the supervisor.
|
|
133
|
+
* Default `["ModbusConnectionClosedError", "ModbusTransportError"]`.
|
|
134
|
+
*/
|
|
135
|
+
readonly triggerOn?: ReadonlyArray<ModbusErrorTag>;
|
|
136
|
+
}
|
|
137
|
+
/** {@link ReconnectOptions} with defaults applied. */
|
|
138
|
+
export interface ResolvedReconnect {
|
|
139
|
+
readonly policy: ModbusRetryPolicy;
|
|
140
|
+
readonly resetAfter: Duration.Duration;
|
|
141
|
+
readonly triggers: (error: ModbusError) => boolean;
|
|
142
|
+
}
|
|
143
|
+
/** Applies defaults to {@link ReconnectOptions}. */
|
|
144
|
+
export declare const resolveReconnect: (options: ReconnectOptions) => ResolvedReconnect;
|
|
145
|
+
/**
|
|
146
|
+
* Refuses an operation while the link is being re-established.
|
|
147
|
+
*
|
|
148
|
+
* `Disconnected` is allowed through so the first call still opens the
|
|
149
|
+
* transport lazily; only `Reconnecting` and `Down` are refused.
|
|
150
|
+
*
|
|
151
|
+
* @param state - The transport's connection state.
|
|
152
|
+
* @returns An Effect failing with {@link ModbusCircuitOpenError} when the
|
|
153
|
+
* circuit is open, and succeeding otherwise.
|
|
154
|
+
*/
|
|
155
|
+
export declare const guardCircuit: (state: SubscriptionRef.SubscriptionRef<ConnectionState>) => Effect.Effect<void, ModbusCircuitOpenError>;
|
|
156
|
+
/**
|
|
157
|
+
* The supervisor loop: re-establishes the link, then keeps probing for as long
|
|
158
|
+
* as the transport lives.
|
|
159
|
+
*
|
|
160
|
+
* Each round runs `reconnect` under the configured policy. Success publishes
|
|
161
|
+
* `Connected` and ends the loop; exhausting the policy publishes `Down` and
|
|
162
|
+
* waits out `resetAfter` before the next round, so an unplugged device is
|
|
163
|
+
* retried at a steady cadence instead of being hammered or given up on.
|
|
164
|
+
*
|
|
165
|
+
* @param reconnect - The transport's own reconnect operation.
|
|
166
|
+
* @param state - The state cell to publish transitions to.
|
|
167
|
+
* @param resolved - Reconnect configuration with defaults applied.
|
|
168
|
+
* @returns An Effect that runs until the link is restored.
|
|
169
|
+
*/
|
|
170
|
+
export declare const superviseReconnect: (reconnect: Effect.Effect<void, ModbusError>, state: SubscriptionRef.SubscriptionRef<ConnectionState>, resolved: ResolvedReconnect) => Effect.Effect<void>;
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -135,6 +135,30 @@ export declare class ModbusNotConnectedError extends ModbusNotConnectedError_bas
|
|
|
135
135
|
readonly message: string;
|
|
136
136
|
}> {
|
|
137
137
|
}
|
|
138
|
+
declare const ModbusCircuitOpenError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
139
|
+
readonly _tag: "ModbusCircuitOpenError";
|
|
140
|
+
} & Readonly<A>;
|
|
141
|
+
/**
|
|
142
|
+
* Error indicating the transport's circuit breaker is open: the device is
|
|
143
|
+
* unreachable and the transport is reconnecting (or waiting to probe again),
|
|
144
|
+
* so the request was refused without touching the wire.
|
|
145
|
+
*
|
|
146
|
+
* This is a **local** error — it is never returned by `modbus-rs`. It is raised
|
|
147
|
+
* by the transport when its connection state is `Reconnecting` or `Down`,
|
|
148
|
+
* which keeps a dead device from being hammered by every caller at once.
|
|
149
|
+
*
|
|
150
|
+
* Retryable by default: a policy with enough budget rides out the outage
|
|
151
|
+
* cheaply, since each refused attempt costs nothing on the bus.
|
|
152
|
+
*
|
|
153
|
+
* @see ConnectionState — The transport state that produces this error.
|
|
154
|
+
*/
|
|
155
|
+
export declare class ModbusCircuitOpenError extends ModbusCircuitOpenError_base<{
|
|
156
|
+
/** The failure that opened the circuit, or a descriptive error. */
|
|
157
|
+
readonly cause: Error;
|
|
158
|
+
/** Human-readable explanation of the error. */
|
|
159
|
+
readonly message: string;
|
|
160
|
+
}> {
|
|
161
|
+
}
|
|
138
162
|
/**
|
|
139
163
|
* Union of all typed Modbus errors emitted by this library.
|
|
140
164
|
*
|
|
@@ -152,7 +176,7 @@ export declare class ModbusNotConnectedError extends ModbusNotConnectedError_bas
|
|
|
152
176
|
*
|
|
153
177
|
* @see ModbusErrorCode — The upstream error code enum driving this mapping.
|
|
154
178
|
*/
|
|
155
|
-
export type ModbusError = ModbusExceptionError | ModbusTimeoutError | ModbusTransportError | ModbusInvalidArgumentError | ModbusConnectionClosedError | ModbusNotConnectedError | ModbusInternalError;
|
|
179
|
+
export type ModbusError = ModbusExceptionError | ModbusTimeoutError | ModbusTransportError | ModbusInvalidArgumentError | ModbusConnectionClosedError | ModbusNotConnectedError | ModbusCircuitOpenError | ModbusInternalError;
|
|
156
180
|
/**
|
|
157
181
|
* Converts a raw `Error` from `modbus-rs` into a typed {@link ModbusError}.
|
|
158
182
|
*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|