Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
workspaces
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
ssa
workspaces
Commits
9d40d351
Commit
9d40d351
authored
3 years ago
by
Daniel Lyons
Committed by
Janet Goldstein
3 years ago
Browse files
Options
Downloads
Patches
Plain Diff
Cleaning up the state machine example a bit
parent
2a8e8aab
No related branches found
No related tags found
1 merge request
!365
WS-543: Addressed issues found in calibration ingestion testing
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
shared/workspaces/workspaces/capability/statemachine.py
+42
-16
42 additions, 16 deletions
shared/workspaces/workspaces/capability/statemachine.py
with
42 additions
and
16 deletions
shared/workspaces/workspaces/capability/statemachine.py
+
42
−
16
View file @
9d40d351
...
...
@@ -6,21 +6,35 @@ reacts to certain events by triggering actions and going into another state.
"""
import
abc
import
json
from
typing
import
Optional
class
State
(
abc
.
ABC
):
"""
A state that a machine could reside in.
A state has a suite of transitions to other states. When an event comes in, we match against it; if we find a
matching pattern, we perform that transition to another state.
"""
@abc.abstractmethod
def
matches
(
self
,
other
:
"
State
"
)
->
bool
:
"""
This is most likely implemented by doing a string-equality test.
:param other: the other state to compare to
:return: true if we and the other state match
"""
pass
def
__init__
(
self
,
transitions
:
list
[
"
TransitionIF
"
]):
# We have a bit of a chicken-and-egg problem here, in that the State needs Transitions to be initialized but
# the Transition needs States to be initialized. Going from prototype to production here will mean breaking
# this cycle, possibly by introducing a builder of some kind, but for now we can just pretend that they are
# built successfully somehow.
self
.
transitions
=
transitions
def
on_event
(
self
,
event
:
dict
)
->
Optional
[
"
State
"
]:
# Locate the first matching transition
matching_transition
=
None
for
transition
in
self
.
transitions
:
if
transition
.
matches
(
event
):
matching_transition
=
transition
break
# take this transition
if
matching_transition
is
not
None
:
return
matching_transition
.
take
()
class
Action
(
abc
.
ABC
):
...
...
@@ -34,6 +48,7 @@ class Action(abc.ABC):
- StartWorkflow(workflow_name, additional_args) that starts a workflow with the
provided name, the event and additional arguments
"""
@abc.abstractmethod
def
execute
(
self
):
pass
...
...
@@ -56,20 +71,21 @@ class TransitionIF(abc.ABC):
"""
A transition between states
"""
def
__init__
(
self
,
from_state
:
State
,
to_state
:
State
,
pattern
:
Pattern
,
action
:
Action
):
self
.
from_state
,
self
.
to_state
=
from_state
,
to_state
self
.
pattern
=
pattern
self
.
action
=
action
@abc.abstractmethod
def
matches
(
self
,
state
:
State
,
event
:
dict
)
->
bool
:
def
matches
(
self
,
event
:
dict
)
->
bool
:
"""
True if this transition is applicable in the supplied state and matches the supplied event.
:param state: state to check against
:param event: event to match against
:return: true if everything matches
"""
return
self
.
from_state
.
matches
(
state
)
and
self
.
pattern
.
matches
(
event
)
return
self
.
pattern
.
matches
(
event
)
@abc.abstractmethod
def
take
(
self
)
->
State
:
...
...
@@ -88,6 +104,7 @@ class MealyMachine:
I am a state machine for a given capability. I am responsible for handling events
and transitioning to other states.
"""
def
__init__
(
self
):
self
.
transitions
=
[]
self
.
current_state
:
State
=
None
...
...
@@ -122,6 +139,7 @@ class CapabilityInfoForMachines:
This is a demonstration of the sort of query I expect we
'
ll use to locate executions
that are active and need to be acted on in response to an event of some kind.
"""
def
find_requests_matching_transition
(
self
,
event
:
dict
)
->
list
[
"
CapabilityExecution
"
]:
"""
The concept here is to let the database do the heavy lifting and actually tell us
...
...
@@ -131,7 +149,8 @@ class CapabilityInfoForMachines:
:param event: the event to check
:return: a list of matching capability executions
"""
return
self
.
session
.
query
(
"""
return
self
.
session
.
query
(
"""
SELECT *
FROM transitions t
JOIN machines m ON t.machine_id = m.id
...
...
@@ -139,30 +158,36 @@ class CapabilityInfoForMachines:
JOIN capability_requests cr on cr.capability_name = c.name
JOIN capability_executions ce on cr.capability_request_id = ce.capability_request_id
WHERE %(event)s @? t.pattern AND ce.state = t.from_state
"""
,
{
"
event
"
:
json
.
dumps
(
event
)})
"""
,
{
"
event
"
:
json
.
dumps
(
event
)},
)
def
build_tables
(
self
):
"""
This is just a demonstration method to hold some SQL to demo the tables I have
in mind for this system.
"""
self
.
session
.
execute
(
"""
self
.
session
.
execute
(
"""
CREATE TABLE machines(id serial primary key);
CREATE TABLE actions(id serial primary key, action_type varchar, action_arguments json);
CREATE TABLE transitions (
id serial primary key,
machine_id integer references
(
machines),
machine_id integer references
machines
(id
),
from_state varchar,
to_state varchar,
pattern jsonpath,
action_id integer references
(
actions)
action_id integer references
actions
(id
)
);
"""
)
"""
)
class
CapabilityExecution
:
machine
:
MealyMachine
=
None
def
process
(
self
,
event
):
self
.
machine
.
on_event
(
event
)
...
...
@@ -173,6 +198,7 @@ class CapabilityServiceMachineMananger:
machines alive during the execution of the program. The idea here is to be more
efficient and more event-driven.
"""
def
__init__
(
self
):
self
.
info
=
CapabilityInfoForMachines
()
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment