Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: replace xstate with a simple built-in state machine #7460

Merged
merged 11 commits into from
Dec 9, 2024
2 changes: 0 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ updates:
# so only apply patch and minor updates automatically
- dependency-name: '@types/node'
update-types: ['version-update:semver-major']
# xstate usually requires manual intervention
- dependency-name: 'xstate'

- package-ecosystem: github-actions
directory: '/'
Expand Down
28 changes: 4 additions & 24 deletions packages/cc/src/cc/TransportServiceCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,10 @@ import type {
} from "@zwave-js/host/safe";
import { Bytes } from "@zwave-js/shared/safe";
import { buffer2hex } from "@zwave-js/shared/safe";
import {
type CCRaw,
type CCResponseRole,
CommandClass,
} from "../lib/CommandClass.js";
import { type CCRaw, CommandClass } from "../lib/CommandClass.js";
import {
CCCommand,
commandClass,
expectedCCResponse,
implementedVersion,
} from "../lib/CommandClassDecorators.js";
import { TransportServiceCommand } from "../lib/_Types.js";
Expand Down Expand Up @@ -81,7 +76,7 @@ export function isTransportServiceEncapsulation(
}

@CCCommand(TransportServiceCommand.FirstSegment)
// @expectedCCResponse(TransportServiceCCReport)
// Handling expected responses is done by the RX state machine
export class TransportServiceCCFirstSegment extends TransportServiceCC {
public constructor(
options: WithAddress<TransportServiceCCFirstSegmentOptions>,
Expand Down Expand Up @@ -227,7 +222,7 @@ export interface TransportServiceCCSubsequentSegmentOptions
}

@CCCommand(TransportServiceCommand.SubsequentSegment)
// @expectedCCResponse(TransportServiceCCReport)
// Handling expected responses is done by the RX state machine
export class TransportServiceCCSubsequentSegment extends TransportServiceCC {
public constructor(
options: WithAddress<TransportServiceCCSubsequentSegmentOptions>,
Expand Down Expand Up @@ -465,23 +460,8 @@ export interface TransportServiceCCSegmentRequestOptions {
datagramOffset: number;
}

function testResponseForSegmentRequest(
sent: TransportServiceCCSegmentRequest,
received: TransportServiceCC,
): CCResponseRole {
return (
(sent.datagramOffset === 0
&& received instanceof TransportServiceCCFirstSegment
&& received.sessionId === sent.sessionId)
|| (sent.datagramOffset > 0
&& received instanceof TransportServiceCCSubsequentSegment
&& sent.datagramOffset === received.datagramOffset
&& received.sessionId === sent.sessionId)
);
}

@CCCommand(TransportServiceCommand.SegmentRequest)
@expectedCCResponse(TransportServiceCC, testResponseForSegmentRequest)
// Handling expected responses is done by the RX state machine
export class TransportServiceCCSegmentRequest extends TransportServiceCC {
public constructor(
options: WithAddress<TransportServiceCCSegmentRequestOptions>,
Expand Down
88 changes: 88 additions & 0 deletions packages/core/src/fsm/FSM.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
export interface StateMachineTransition<
State extends StateMachineState,
Effect = undefined,
> {
effect?: Effect;
newState: State;
}

export interface StateMachineState {
value: number | string;
done?: boolean;
}

export interface StateMachineInput {
value: number | string;
}

export type StateMachineTransitionMap<
State extends StateMachineState,
Input extends StateMachineInput,
Effect = undefined,
> = (
state: State,
) => (
input: Input,
) => StateMachineTransition<State, Effect | undefined> | undefined;

export type InferStateMachineTransitions<
T extends StateMachine<any, any, any>,
> = T extends StateMachine<infer S, infer I, infer E>
? StateMachineTransitionMap<S, I, E | undefined>
: never;

export class StateMachine<
State extends StateMachineState,
Input extends StateMachineInput,
Effect = undefined,
> {
public constructor(
initialState: State,
transitions: StateMachineTransitionMap<
State,
Input,
Effect | undefined
>,
) {
this._initial = this._state = initialState;
this.transitions = transitions;
}

protected transitions: StateMachineTransitionMap<
State,
Input,
Effect | undefined
>;

/** Restarts the machine from the initial state */
public restart(): void {
this._state = this._initial;
}

/** Determines the next transition to take */
public next(
input: Input,
): StateMachineTransition<State, Effect | undefined> | undefined {
if (this._state.done) return;
return this.transitions(this._state)(input);
}

/** Transitions the machine to the next state. This does not execute effects */
public transition(next?: State): void {
// Allow some convenience by passing the transition's next state directly
if (next == undefined) return;
this._state = next;
}

private _initial: State;
private _state: State;
/** Returns the current state of the state machine */
public get state(): State {
return this._state;
}

/** Returns whether this state machine is done */
public get done(): boolean {
return !!this._state.done;
}
}
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from "./crypto/index.node.js";
export * from "./definitions/index.js";
export * from "./dsk/index.js";
export * from "./error/ZWaveError.js";
export * from "./fsm/FSM.js";
export * from "./log/Controller.js";
export * from "./log/shared.js";
export * from "./log/shared_safe.js";
Expand Down
4 changes: 1 addition & 3 deletions packages/zwave-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@
"semver": "^7.6.3",
"serialport": "^12.0.0",
"source-map-support": "^0.5.21",
"winston": "^3.15.0",
"xstate": "4.38.3"
"winston": "^3.15.0"
},
"devDependencies": {
"@alcalzone/esm2cjs": "^1.4.1",
Expand All @@ -130,7 +129,6 @@
"@types/semver": "^7.5.8",
"@types/sinon": "^17.0.3",
"@types/source-map-support": "^0.5.10",
"@xstate/test": "^0.5.1",
"@zwave-js/maintenance": "workspace:*",
"@zwave-js/transformers": "workspace:*",
"del-cli": "^6.0.0",
Expand Down
Loading
Loading