more work: allocator, input, shift responsibilities

This commit is contained in:
Brian Warner 2017-03-12 18:38:48 +01:00
parent 79d38da497
commit 4f1b352b2a
13 changed files with 305 additions and 99 deletions

View File

@ -176,46 +176,46 @@ for consistency.
The code-entry Helper object has the following API:
* `update_nameplates()`: requests an updated list of nameplates from the
* `refresh_nameplates()`: requests an updated list of nameplates from the
Rendezvous Server. These form the first portion of the wormhole code (e.g.
"4" in "4-purple-sausages"). Note that they are unicode strings (so "4",
not 4). The Helper will get the response in the background, and calls to
`complete_nameplate()` after the response will use the new list.
* `completions = h.complete_nameplate(prefix)`: returns (synchronously) a
list of suffixes for the given nameplate prefix. For example, if the server
reports nameplates 1, 12, 13, 24, and 170 are in use,
`complete_nameplate("1")` will return `["", "2", "3", "70"]`. Raises
`AlreadyClaimedNameplateError` if called after `h.claim_nameplate`.
* `d = h.claim_nameplate(nameplate)`: accepts a string with the chosen
nameplate. May only be called once, after which `OnlyOneNameplateError` is
raised. Returns a Deferred that fires (with None) when the nameplate's
wordlist is known (which happens after the nameplate is claimed, requiring
a roundtrip to the server).
* `completions = h.complete_words(prefix)`: return (synchronously) a list of
suffixes for the given words prefix. The possible completions depend upon
the wordlist in use for the previously-claimed nameplate, so calling this
before `claim_nameplate` will raise `MustClaimNameplateFirstError`. Given a
prefix like "su", this returns a list of strings which are appropriate to
append to the prefix (e.g. `["pportive", "rrender", "spicious"]`, for
expansion into "supportive", "surrender", and "suspicious". The prefix
should not include the nameplate, but *should* include whatever words and
hyphens have been typed so far (the default wordlist uses alternate lists,
where even numbered words have three syllables, and odd numbered words have
two, so the completions depend upon how many words are present, not just
the partial last word). E.g. `complete_words("pr")` will return
`["ocessor", "ovincial", "oximate"]`, while `complete_words("opulent-pr")`
will return `["eclude", "efer", "eshrunk", "inter", "owler"]`.
If the wordlist is not yet known (i.e. the Deferred from `claim_nameplate`
has not yet fired), this returns an empty list. It will also return an
empty list if the prefix is complete (the last word matches something in
the completion list, and there are no longer extension words), although the
code may not yet be complete if there are additional words. The completions
will never include a hyphen: the UI frontend must supply these if desired.
* `h.submit_words(words)`: call this when the user is finished typing in the
`get_nameplate_completions()` after the response will use the new list.
* `completions = h.get_nameplate_completions(prefix)`: returns
(synchronously) a list of suffixes for the given nameplate prefix. For
example, if the server reports nameplates 1, 12, 13, 24, and 170 are in
use, `get_nameplate_completions("1")` will return `["", "2", "3", "70"]`.
Raises `AlreadyClaimedNameplateError` if called after `h.choose_nameplate`.
* `h.choose_nameplate(nameplate)`: accepts a string with the chosen nameplate.
May only be called once, after which `OnlyOneNameplateError` is raised. (in
this future, this might return a Deferred that fires (with None) when the
nameplate's wordlist is known (which happens after the nameplate is
claimed, requiring a roundtrip to the server)).
* `completions = h.get_word_completions(prefix)`: return (synchronously) a
list of suffixes for the given words prefix. The possible completions
depend upon the wordlist in use for the previously-claimed nameplate, so
calling this before `choose_nameplate` will raise
`MustClaimNameplateFirstError`. Given a prefix like "su", this returns a
list of strings which are appropriate to append to the prefix (e.g.
`["pportive", "rrender", "spicious"]`, for expansion into "supportive",
"surrender", and "suspicious". The prefix should not include the nameplate,
but *should* include whatever words and hyphens have been typed so far (the
default wordlist uses alternate lists, where even numbered words have three
syllables, and odd numbered words have two, so the completions depend upon
how many words are present, not just the partial last word). E.g.
`get_word_completions("pr")` will return `["ocessor", "ovincial",
"oximate"]`, while `get_word_completions("opulent-pr")` will return
`["eclude", "efer", "eshrunk", "inter", "owler"]`. If the wordlist is not
yet known, this returns an empty list. It will also return an empty list if
the prefix is complete (the last word matches something in the completion
list, and there are no longer extension words), although the code may not
yet be complete if there are additional words. The completions will never
include a hyphen: the UI frontend must supply these if desired.
* `h.choose_words(words)`: call this when the user is finished typing in the
code. It does not return anything, but will cause the Wormhole's
`w.when_code()` (or corresponding delegate) to fire, and triggers the
wormhole connection process. This accepts a string like "purple-sausages",
without the nameplate. It must be called after `h.claim_nameplate()` or
without the nameplate. It must be called after `h.choose_nameplate()` or
`MustClaimNameplateFirstError` will be raised.
The `rlcompleter` wrapper is a function that knows how to use the code-entry

View File

@ -11,8 +11,12 @@ digraph {
S0B -> P_allocate [label="allocate"]
P_allocate [shape="box" label="RC.tx_allocate" color="orange"]
P_allocate -> S1B [color="orange"]
S1B [label="S1B:\nallocating" color="orange"]
S1B -> S1A [label="lost"]
{rank=same; S1A P_allocate S1B}
S0B -> S1B [style="invis"]
S1B [label="S1B:\nallocating\nconnected" color="orange"]
S1B -> foo [label="lost"]
foo [style="dotted" label=""]
foo -> S1A
S1A [label="S1A:\nallocating\ndisconnected" color="orange"]
S1A -> P_allocate [label="connected" color="orange"]

View File

@ -12,7 +12,7 @@ digraph {
{rank=same; P0_code S0}
P0_code [shape="box" style="dashed"
label="input -> Code.input\n or allocate -> Code.allocate\n or set_code -> Code.set_code"]
label="C.input_code\n or C.allocate_code\n or C.set_code"]
P0_code -> S0
S0 [label="S0: empty"]
S0 -> P0_build [label="set_code"]
@ -22,7 +22,7 @@ digraph {
P_close_error -> S_closing
S0 -> P_close_lonely [label="close"]
P0_build [shape="box" label="W.got_code\nN.set_nameplate\nK.got_code"]
P0_build [shape="box" label="W.got_code"]
P0_build -> S1
S1 [label="S1: lonely" color="orange"]
@ -67,7 +67,7 @@ digraph {
{rank=same; Other S_closed}
Other [shape="box" style="dashed"
label="rx_welcome -> process\nsend -> S.send\ngot_message -> got_version or got_phase\ngot_key -> W.got_key\ngot_verifier -> W.got_verifier\nallocate -> C.allocate\ninput -> C.input\nset_code -> C.set_code"
label="rx_welcome -> process\nsend -> S.send\ngot_message -> got_version or got_phase\ngot_key -> W.got_key\ngot_verifier -> W.got_verifier\nallocate_Code -> C.allocate_code\ninput_code -> C.input_code\nset_code -> C.set_code"
]

View File

@ -1,27 +1,39 @@
digraph {
start [label="C:\nCode\nManagement" style="dotted"]
start [label="C:\nCode\n(management)" style="dotted"]
{rank=same; start S0}
start -> S0 [style="invis"]
S0 [label="S0:\nidle"]
S0 -> P0_got_code [label="set_code"]
P0_got_code [shape="box" label="B.got_code"]
P0_got_code -> S3
S3 [label="S3: known" color="green"]
P0_got_code [shape="box" label="N.set_nameplate"]
P0_got_code -> P_done
P_done [shape="box" label="K.got_code\nB.got_code"]
P_done -> S5
S5 [label="S5: known" color="green"]
{rank=same; S1_inputting S2_allocating}
{rank=same; S1_inputting_nameplate S3_allocating}
{rank=same; P0_got_code P1_set_nameplate P3_got_nameplate}
S0 -> P_input [label="input_code"]
P_input [shape="box" label="I.start"]
P_input -> S1_inputting
S1_inputting [label="S1:\ninputting"]
S1_inputting -> P0_got_code [label="finished_input"]
P_input -> S1_inputting_nameplate
S1_inputting_nameplate [label="S1:\ninputting\nnameplate"]
S1_inputting_nameplate -> P1_set_nameplate [label="got_nameplate"]
P1_set_nameplate [shape="box" label="N.set_nameplate"]
P1_set_nameplate -> S2_inputting_words
S2_inputting_words [label="S2:\ninputting\nwords"]
S2_inputting_words -> P1_got_words [label="finished_input"]
P1_got_words [shape="box" label="assemble\ncode"]
P1_got_words -> P_done
P_done
S0 -> P_allocate [label="allocate_code"]
P_allocate [shape="box" label="A.allocate"]
P_allocate -> S2_allocating
S2_allocating [label="S2:\nallocating"]
S2_allocating -> P1_generate [label="allocated_nameplate"]
P1_generate [shape="box" label="generate\nrandom words"]
P1_generate -> P0_got_code
P_allocate -> S3_allocating
S3_allocating [label="S3:\nallocating"]
S3_allocating -> P3_got_nameplate [label="allocated_nameplate"]
P3_got_nameplate [shape="box" label="N.set_nameplate"]
P3_got_nameplate -> P3_generate
P3_generate [shape="box" label="append\nrandom words"]
P3_generate -> P_done
}

View File

@ -5,28 +5,39 @@ digraph {
start -> S0 [style="invis"]
S0 [label="S0:\nidle"]
S0 -> P0_list_nameplates [label="start" color="orange"]
P0_list_nameplates [shape="box" label="L.refresh_nameplates" color="orange"]
P0_list_nameplates -> S1 [color="orange"]
S0 -> P0_list_nameplates [label="start"]
P0_list_nameplates [shape="box" label="L.refresh"]
P0_list_nameplates -> S1
S1 [label="S1: typing\nnameplate" color="orange"]
{rank=same; foo P0_list_nameplates}
S1 -> foo [label="update_nameplates"]
S1 -> foo [label="refresh_nameplates" color="orange" fontcolor="orange"]
foo [style="dashed" label=""]
foo -> P0_list_nameplates
S1 -> P1_claim [label="claim_nameplate" color="orange" fontcolor="orange"]
P1_claim [shape="box" label="N.set_nameplate" color="orange"]
P1_claim -> S2 [color="orange"]
S2 [label="S2: typing\ncode\n(no wordlist)" color="orange"]
S2 -> P2_stash_wordlist [label="got_nameplates" color="orange"]
P2_stash_wordlist [shape="box" label="stash\nwordlist" color="orange"]
P2_stash_wordlist -> S3 [color="orange"]
S2 -> P_done [label="submit_words"]
S3 [label="S3: typing\ncode\n(yes wordlist)" color="orange"]
S3 -> P_done [label="submit_words" color="orange" fontcolor="orange"]
P_done [shape="box" label="C.finished_input" color="orange"]
P_done -> S4 [color="orange"]
S4 [label="S4: known" color="green"]
S1 -> P1_record [label="got_nameplates"]
P1_record [shape="box" label="record\nnameplates"]
P1_record -> S1
S1 -> P1_claim [label="choose_nameplate" color="orange" fontcolor="orange"]
P1_claim [shape="box" label="stash nameplate\nC.got_nameplate"]
P1_claim -> S2
S2 [label="S2: typing\ncode\n(no wordlist)"]
S2 -> S2 [label="got_nameplates"]
S2 -> P2_stash_wordlist [label="got_wordlist"]
P2_stash_wordlist [shape="box" label="stash wordlist"]
P2_stash_wordlist -> S3
S2 -> P_done [label="choose_words" color="orange" fontcolor="orange"]
S3 [label="S3: typing\ncode\n(yes wordlist)"]
S3 -> S3 [label="got_nameplates"]
S3 -> P_done [label="choose_words" color="orange" fontcolor="orange"]
P_done [shape="box" label="build code\nC.finished_input"]
P_done -> S4
S4 [label="S4: done" color="green"]
S4 -> S4 [label="got_nameplates\ngot_wordlist"]
other [shape="box" style="dotted"
label="h.refresh_nameplates()\nh.get_nameplate_completions(prefix)\nh.choose_nameplate(nameplate)\nh.get_word_completions(prefix)\nh.choose_words(words)"
]
{rank=same; S4 other}
}

View File

@ -31,12 +31,9 @@ digraph {
foo2 [label="" style="dashed"]
foo2 -> P_tx
S0B -> P_notify [label="rx"]
S1B -> P_notify [label="rx" color="orange" fontcolor="orange"]
P_notify [shape="box" label="C.got_nameplates()"]
S0B -> P_notify [label="rx_nameplates"]
S1B -> P_notify [label="rx_nameplates" color="orange" fontcolor="orange"]
P_notify [shape="box" label="I.got_nameplates()"]
P_notify -> S0B
{rank=same; foo foo2 legend}
legend [shape="box" style="dotted"
label="refresh: L.refresh_nameplates()\nrx: L.rx_nameplates()"]
}

View File

@ -2,8 +2,10 @@ digraph {
Wormhole [shape="oval" color="blue" fontcolor="blue"]
Boss [shape="box" label="Boss\n(manager)"
color="blue" fontcolor="blue"]
Nameplate [shape="box" color="blue" fontcolor="blue"]
Mailbox [shape="box" color="blue" fontcolor="blue"]
Nameplate [label="Nameplate\n(claimer)"
shape="box" color="blue" fontcolor="blue"]
Mailbox [label="Mailbox\n(opener)"
shape="box" color="blue" fontcolor="blue"]
Connection [label="Rendezvous\nConnector"
shape="oval" color="blue" fontcolor="blue"]
#websocket [color="blue" fontcolor="blue"]
@ -16,9 +18,11 @@ digraph {
color="blue" fontcolor="blue"]
Allocator [shape="box" label="(nameplate)\nAllocator"
color="blue" fontcolor="blue"]
Input [shape="box" label="(code)\nInput helper"
Input [shape="box" label="(interactive\ncode)\nInput"
color="blue" fontcolor="blue"]
Terminator [shape="box" color="blue" fontcolor="blue"]
InputHelperAPI [shape="oval" label="input\nhelper\nAPI"
color="blue" fontcolor="blue"]
#Connection -> websocket [color="blue"]
#Connection -> Order [color="blue"]
@ -36,11 +40,8 @@ digraph {
Boss -> Send [style="dashed" color="red" fontcolor="red" label="send"]
Boss -> Nameplate [style="dashed" color="red" fontcolor="red"
label="set_nameplate"]
#Boss -> Mailbox [color="blue"]
Mailbox -> Order [style="dashed" label="got_message (once)"]
Boss -> Key [style="dashed" label="got_code"]
Key -> Boss [style="dashed" label="got_key\ngot_verifier\nscared"]
Order -> Key [style="dashed" label="got_pake"]
Order -> Receive [style="dashed" label="got_message"]
@ -85,15 +86,18 @@ digraph {
]
#Code -> Lister [color="blue"]
Input -> Lister [style="dashed" color="red" fontcolor="red"
label="refresh_nameplates"
label="refresh"
]
Boss -> Code [style="dashed" color="red" fontcolor="red"
label="allocate_code\ninput_code\nset_code"]
Code -> Boss [style="dashed"
label="got_code"]
Code -> Boss [style="dashed" label="got_code"]
Code -> Key [style="dashed" label="got_code"]
Code -> Nameplate [style="dashed" label="set_nameplate"]
Code -> Input [style="dashed" color="red" fontcolor="red" label="start"]
Input -> Code [style="dashed" label="finished_input"]
Input -> Code [style="dashed" label="got_nameplate\nfinished_input"]
InputHelperAPI -> Input [label="refresh_nameplates\nget_nameplate_completions\nchoose_nameplate\nget_word_completions\nchoose_words" color="orange" fontcolor="orange"]
Code -> Allocator [style="dashed" color="red" fontcolor="red"
label="allocate"]
Allocator -> Code [style="dashed" label="allocated"]

View File

@ -0,0 +1,65 @@
from __future__ import print_function, absolute_import, unicode_literals
from zope.interface import implementer
from attr import attrs, attrib
from attr.validators import provides
from automat import MethodicalMachine
from . import _interfaces
@attrs
@implementer(_interfaces.IAllocator)
class Allocator(object):
_timing = attrib(validator=provides(_interfaces.ITiming))
m = MethodicalMachine()
@m.setTrace()
def set_trace(): pass # pragma: no cover
def wire(self, rendezvous_connector, code):
self._RC = _interfaces.IRendezvousConnector(rendezvous_connector)
self._C = _interfaces.ICode(code)
@m.state(initial=True)
def S0A_idle(self): pass # pragma: no cover
@m.state()
def S0B_idle_connected(self): pass # pragma: no cover
@m.state()
def S1A_allocating(self): pass # pragma: no cover
@m.state()
def S1B_allocating_connected(self): pass # pragma: no cover
@m.state()
def S2_done(self): pass # pragma: no cover
# from Code
@m.input()
def allocate(self): pass
# from RendezvousConnector
@m.input()
def connected(self): pass
@m.input()
def lost(self): pass
@m.input()
def rx_allocated(self, nameplate): pass
@m.output()
def RC_tx_allocate(self):
self._RC.tx_allocate()
@m.output()
def C_allocated(self, nameplate):
self._C.allocated(nameplate)
S0A_idle.upon(connected, enter=S0B_idle_connected, outputs=[])
S0B_idle_connected.upon(lost, enter=S0A_idle, outputs=[])
S0A_idle.upon(allocate, enter=S1A_allocating, outputs=[])
S0B_idle_connected.upon(allocate, enter=S1B_allocating_connected,
outputs=[RC_tx_allocate])
S1A_allocating.upon(connected, enter=S1B_allocating_connected,
outputs=[RC_tx_allocate])
S1B_allocating_connected.upon(lost, enter=S1A_allocating, outputs=[])
S1B_allocating_connected.upon(rx_allocated, enter=S2_done,
outputs=[C_allocated])
S2_done.upon(connected, enter=S2_done, outputs=[])
S2_done.upon(lost, enter=S2_done, outputs=[])

View File

@ -15,6 +15,8 @@ from ._key import Key
from ._receive import Receive
from ._rendezvous import RendezvousConnector
from ._lister import Lister
from ._allocator import Allocator
from ._input import Input
from ._code import Code
from ._terminator import Terminator
from .errors import (ServerError, LonelyError, WrongPasswordError,
@ -49,6 +51,8 @@ class Boss(object):
self._reactor, self._journal,
self._tor_manager, self._timing)
self._L = Lister()
self._A = Allocator(self._timing)
self._I = Input(self._timing)
self._C = Code(self._timing)
self._T = Terminator()
@ -59,7 +63,9 @@ class Boss(object):
self._K.wire(self, self._M, self._R)
self._R.wire(self, self._S)
self._RC.wire(self, self._N, self._M, self._C, self._L, self._T)
self._L.wire(self._RC, self._C)
self._L.wire(self._RC, self._I)
self._A.wire(self._RC, self._C)
self._I.wire(self._C, self._L)
self._C.wire(self, self._RC, self._L)
self._T.wire(self, self._RC, self._N, self._M)
@ -189,8 +195,6 @@ class Boss(object):
@m.output()
def do_got_code(self, code):
nameplate = code.split("-")[0]
self._N.set_nameplate(nameplate)
self._K.got_code(code)
self._W.got_code(code)
@m.output()

View File

@ -74,13 +74,11 @@ class Code(object):
@m.input()
def got_wordlist(self, wordlist): pass
# from CodeInputHelper
# from Input
@m.input()
def update_nameplates(self): pass
def got_nameplate(self, nameplate): pass
@m.input()
def claim_nameplate(self, nameplate): pass
@m.input()
def submit_words(self, words): pass
def finished_input(self, code): pass
@m.output()
def L_refresh_nameplates(self):
@ -137,6 +135,7 @@ class Code(object):
self._B_got_code()
def _B_got_code(self):
self._N.set_nameplate(nameplate) XXX
self._B.got_code(self._code)
S0A_unknown.upon(connected, enter=S0B_unknown_connected, outputs=[])

106
src/wormhole/_input.py Normal file
View File

@ -0,0 +1,106 @@
from __future__ import print_function, absolute_import, unicode_literals
from zope.interface import implementer
from attr import attrs, attrib
from attr.validators import provides
from automat import MethodicalMachine
from . import _interfaces
@attrs
@implementer(_interfaces.IInput)
class Input(object):
_timing = attrib(validator=provides(_interfaces.ITiming))
m = MethodicalMachine()
@m.setTrace()
def set_trace(): pass # pragma: no cover
def __attrs_post_init__(self):
self._nameplate = None
self._wordlist = None
self._claimed_waiter = None
def wire(self, code, lister):
self._C = _interfaces.ICode(code)
self._L = _interfaces.ILister(lister)
@m.state(initial=True)
def S0_idle(self): pass # pragma: no cover
@m.state()
def S1_nameplate(self): pass # pragma: no cover
@m.state()
def S2_code_no_wordlist(self): pass # pragma: no cover
@m.state()
def S3_code_yes_wordlist(self): pass # pragma: no cover
@m.state(terminal=True)
def S4_done(self): pass # pragma: no cover
# from Code
@m.input()
def start(self): pass
# from Lister
@m.input()
def got_nameplates(self, nameplates): pass
# from Nameplate??
@m.input()
def got_wordlist(self, wordlist): pass
# from CodeInputHelper
@m.input()
def refresh_nameplates(self): pass
@m.input()
def _choose_nameplate(self, nameplate): pass
@m.input()
def choose_words(self, words): pass
@m.output()
def L_refresh_nameplates(self):
self._L.refresh_nameplates()
@m.output()
def start_and_L_refresh_nameplates(self, input_helper):
self._input_helper = input_helper
self._L.refresh_nameplates()
@m.output()
def stash_wordlist_and_notify(self, wordlist):
self._wordlist = wordlist
if self._claimed_waiter:
self._claimed_waiter.callback(None)
del self._claimed_waiter
@m.output()
def stash_nameplate(self, nameplate):
self._nameplate = nameplate
@m.output()
def C_got_nameplate(self, nameplate):
self._C.got_nameplate(nameplate)
@m.output()
def finished(self, words):
code = self._nameplate + "-" + words
self._C.finished_input(code)
S0_idle.upon(start, enter=S1_nameplate, outputs=[L_refresh_nameplates])
S1_nameplate.upon(refresh_nameplates, enter=S1_nameplate,
outputs=[L_refresh_nameplates])
S1_nameplate.upon(_choose_nameplate, enter=S2_code_no_wordlist,
outputs=[stash_nameplate, C_got_nameplate])
S2_code_no_wordlist.upon(got_wordlist, enter=S3_code_yes_wordlist,
outputs=[stash_wordlist_and_notify])
S2_code_no_wordlist.upon(choose_words, enter=S4_done, outputs=[finished])
S3_code_yes_wordlist.upon(choose_words, enter=S4_done, outputs=[finished])
# methods for the CodeInputHelper to use
#refresh_nameplates/_choose_nameplate/choose_words: @m.input methods
def get_nameplate_completions(self, prefix):
completions = []
for nameplate in self._nameplates
pass
def choose_nameplate(self, nameplate):
if self._claimed_waiter is not None:
raise X
d = self._claimed_waiter = defer.Deferred()
self._choose_nameplate(nameplate)
def get_word_completions(self, prefix):
pass

View File

@ -22,6 +22,10 @@ class ILister(Interface):
pass
class ICode(Interface):
pass
class IInput(Interface):
pass
class IAllocator(Interface):
pass
class ITerminator(Interface):
pass

View File

@ -9,9 +9,9 @@ class Lister(object):
@m.setTrace()
def set_trace(): pass # pragma: no cover
def wire(self, rendezvous_connector, code):
def wire(self, rendezvous_connector, input):
self._RC = _interfaces.IRendezvousConnector(rendezvous_connector)
self._C = _interfaces.ICode(code)
self._I = _interfaces.IInput(input)
# Ideally, each API request would spawn a new "list_nameplates" message
# to the server, so the response would be maximally fresh, but that would
@ -44,8 +44,8 @@ class Lister(object):
def RC_tx_list(self):
self._RC.tx_list()
@m.output()
def C_got_nameplates(self, message):
self._C.got_nameplates(message["nameplates"])
def I_got_nameplates(self, message):
self._I.got_nameplates(message["nameplates"])
S0A_idle_disconnected.upon(connected, enter=S0B_idle_connected, outputs=[])
S0B_idle_connected.upon(lost, enter=S0A_idle_disconnected, outputs=[])
@ -59,9 +59,9 @@ class Lister(object):
S0B_idle_connected.upon(refresh_nameplates, enter=S1B_wanting_connected,
outputs=[RC_tx_list])
S0B_idle_connected.upon(rx_nameplates, enter=S0B_idle_connected,
outputs=[C_got_nameplates])
outputs=[I_got_nameplates])
S1B_wanting_connected.upon(lost, enter=S1A_wanting_disconnected, outputs=[])
S1B_wanting_connected.upon(refresh_nameplates, enter=S1B_wanting_connected,
outputs=[RC_tx_list])
S1B_wanting_connected.upon(rx_nameplates, enter=S0B_idle_connected,
outputs=[C_got_nameplates])
outputs=[I_got_nameplates])