2017-02-22 19:26:11 +00:00
|
|
|
from zope.interface import implementer
|
2016-12-30 05:30:59 +00:00
|
|
|
from automat import MethodicalMachine
|
2017-02-22 19:26:11 +00:00
|
|
|
from . import _interfaces
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
@implementer(_interfaces.IMailbox)
|
|
|
|
class Mailbox(object):
|
2016-12-30 05:30:59 +00:00
|
|
|
m = MethodicalMachine()
|
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
def __init__(self, side):
|
|
|
|
self._side = side
|
|
|
|
self._mood = None
|
|
|
|
self._nameplate = None
|
|
|
|
|
|
|
|
def wire(self, wormhole, rendezvous_connector, ordering):
|
|
|
|
self._W = _interfaces.IWormhole(wormhole)
|
|
|
|
self._RC = _interfaces.IRendezvousConnector(rendezvous_connector)
|
|
|
|
self._O = _interfaces.IOrder(ordering)
|
|
|
|
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state(initial=True)
|
|
|
|
def initial(self): pass
|
|
|
|
|
2017-02-14 07:12:57 +00:00
|
|
|
# all -A states: not connected
|
|
|
|
# all -B states: yes connected
|
|
|
|
# B states serialize as A, so they deserialize as unconnected
|
|
|
|
|
|
|
|
# S0: know nothing
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S0A(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S0B(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-14 07:12:57 +00:00
|
|
|
# S1: nameplate known, not claimed
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S1A(self): pass
|
|
|
|
|
|
|
|
# S2: nameplate known, maybe claimed
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S2A(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S2B(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-14 07:12:57 +00:00
|
|
|
# S3: nameplate claimed, mailbox known, maybe open
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S3A(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S3B(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-14 07:12:57 +00:00
|
|
|
# S4: mailbox maybe open, nameplate maybe released
|
|
|
|
# We've definitely opened the mailbox at least once, but it must be
|
|
|
|
# re-opened with each connection, because open() is also subscribe()
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S4A(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S4B(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-14 07:12:57 +00:00
|
|
|
# S5: mailbox maybe open, nameplate released
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S5A(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def S5B(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-14 07:12:57 +00:00
|
|
|
# Src: waiting for release+close
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def SrcA(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def SrcB(self): pass
|
|
|
|
# Sr: closed (or never opened), waiting for release
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def SrA(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def SrB(self): pass
|
|
|
|
# Sc: released (or never claimed), waiting for close
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def ScA(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def ScB(self): pass
|
|
|
|
# Ss: closed and released, waiting for stop
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.state()
|
2017-02-14 07:12:57 +00:00
|
|
|
def SsB(self): pass
|
2017-02-22 19:26:11 +00:00
|
|
|
@m.state(terminal=True)
|
|
|
|
def Ss(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def start_unconnected(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def start_connected(self): pass
|
|
|
|
|
|
|
|
# from Wormhole
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def set_nameplate(self, nameplate): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def close(self, mood): pass
|
|
|
|
|
|
|
|
# from RendezvousConnector
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def connected(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def lost(self): pass
|
|
|
|
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def rx_claimed(self, mailbox): pass
|
|
|
|
|
|
|
|
def rx_message(self, side, phase, msg):
|
|
|
|
if side == self._side:
|
|
|
|
self.rx_message_ours(phase, msg)
|
|
|
|
else:
|
|
|
|
self.rx_message_theirs(phase, msg)
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def rx_message_ours(self, phase, msg): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def rx_message_theirs(self, phase, msg): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def rx_released(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def rx_closed(self): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def stopped(self): pass
|
|
|
|
|
|
|
|
# from Send or Key
|
2016-12-30 06:18:20 +00:00
|
|
|
@m.input()
|
2017-02-22 19:26:11 +00:00
|
|
|
def add_message(self, phase, msg): pass
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
|
|
|
|
@m.output()
|
|
|
|
def record_nameplate(self, nameplate):
|
|
|
|
self._nameplate = nameplate
|
|
|
|
@m.output()
|
|
|
|
def record_nameplate_and_RC_tx_claim(self, nameplate):
|
|
|
|
self._nameplate = nameplate
|
|
|
|
self._RX.tx_claim(self._nameplate)
|
|
|
|
@m.output()
|
|
|
|
def RC_tx_claim(self):
|
|
|
|
# when invoked via M.connected(), we must use the stored nameplate
|
|
|
|
self._RC.tx_claim(self._nameplate)
|
|
|
|
@m.output()
|
|
|
|
def RC_tx_open(self):
|
|
|
|
assert self._mailbox
|
|
|
|
self._RC.tx_open(self._mailbox)
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def queue(self, phase, msg):
|
|
|
|
self._pending_outbound[phase] = msg
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def store_mailbox_and_RC_tx_open_and_drain(self, mailbox):
|
|
|
|
self._mailbox = mailbox
|
|
|
|
self._RC.tx_open(mailbox)
|
|
|
|
self._drain()
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def drain(self):
|
|
|
|
self._drain()
|
|
|
|
def _drain(self):
|
|
|
|
for phase, msg in self._pending_outbound.items():
|
|
|
|
self._RC.tx_add(phase, msg)
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def RC_tx_add(self, phase, msg):
|
|
|
|
self._RC.tx_add(phase, msg)
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def RC_tx_release(self):
|
|
|
|
self._RC.tx_release()
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def RC_tx_release_and_accept(self, phase, msg):
|
|
|
|
self._RC.tx_release()
|
|
|
|
self._accept(phase, msg)
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def record_mood_and_RC_tx_release(self, mood):
|
|
|
|
self._mood = mood
|
|
|
|
self._RC.tx_release()
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def record_mood_and_RC_tx_release_and_RC_tx_close(self, mood):
|
|
|
|
self._mood = mood
|
|
|
|
self._RC.tx_release()
|
|
|
|
self._RC.tx_close(self._mood)
|
2016-12-30 06:18:20 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def RC_tx_close(self):
|
|
|
|
assert self._mood
|
|
|
|
self._RC.tx_close(self._mood)
|
2017-02-15 20:11:17 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def record_mood_and_RC_tx_close(self, mood):
|
|
|
|
self._mood = mood
|
|
|
|
self._RC.tx_close(self._mood)
|
2016-12-30 05:30:59 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def accept(self, phase, msg):
|
|
|
|
self._accept(phase, msg)
|
|
|
|
def _accept(self, phase, msg):
|
|
|
|
if phase not in self._processed:
|
|
|
|
self._O.got_message(phase, msg)
|
|
|
|
self._processed.add(phase)
|
2016-12-30 06:18:20 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def dequeue(self, phase, msg):
|
|
|
|
self._pending_outbound.pop(phase)
|
2017-02-15 20:11:17 +00:00
|
|
|
@m.output()
|
2017-02-22 19:26:11 +00:00
|
|
|
def record_mood(self, mood):
|
|
|
|
self._mood = mood
|
|
|
|
@m.output()
|
|
|
|
def record_mood_and_RC_stop(self, mood):
|
|
|
|
self._mood = mood
|
|
|
|
self._RC_stop()
|
|
|
|
@m.output()
|
|
|
|
def RC_stop(self):
|
|
|
|
self._RC_stop()
|
|
|
|
@m.output()
|
|
|
|
def W_closed(self):
|
|
|
|
self._W.closed()
|
|
|
|
|
|
|
|
initial.upon(start_unconnected, enter=S0A, outputs=[])
|
|
|
|
initial.upon(start_connected, enter=S0B, outputs=[])
|
|
|
|
S0A.upon(connected, enter=S0B, outputs=[])
|
|
|
|
S0A.upon(set_nameplate, enter=S1A, outputs=[record_nameplate])
|
|
|
|
S0A.upon(add_message, enter=S0A, outputs=[queue])
|
|
|
|
S0B.upon(lost, enter=S0A, outputs=[])
|
|
|
|
S0B.upon(set_nameplate, enter=S2B, outputs=[record_nameplate_and_RC_tx_claim])
|
|
|
|
S0B.upon(add_message, enter=S0B, outputs=[queue])
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
S1A.upon(connected, enter=S2B, outputs=[RC_tx_claim])
|
|
|
|
S1A.upon(add_message, enter=S1A, outputs=[queue])
|
2017-02-14 07:12:57 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
S2A.upon(connected, enter=S2B, outputs=[RC_tx_claim])
|
|
|
|
S2A.upon(add_message, enter=S2A, outputs=[queue])
|
|
|
|
S2B.upon(lost, enter=S2A, outputs=[])
|
|
|
|
S2B.upon(add_message, enter=S2B, outputs=[queue])
|
|
|
|
S2B.upon(rx_claimed, enter=S3B,
|
|
|
|
outputs=[store_mailbox_and_RC_tx_open_and_drain])
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
S3A.upon(connected, enter=S3B, outputs=[RC_tx_open, drain])
|
|
|
|
S3A.upon(add_message, enter=S3A, outputs=[queue])
|
|
|
|
S3B.upon(lost, enter=S3A, outputs=[])
|
|
|
|
S3B.upon(rx_message_theirs, enter=S4B, outputs=[RC_tx_release_and_accept])
|
|
|
|
S3B.upon(rx_message_ours, enter=S3B, outputs=[dequeue])
|
|
|
|
S3B.upon(rx_claimed, enter=S3B, outputs=[])
|
|
|
|
S3B.upon(add_message, enter=S3B, outputs=[queue, RC_tx_add])
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
S4A.upon(connected, enter=S4B,
|
|
|
|
outputs=[RC_tx_open, drain, RC_tx_release])
|
|
|
|
S4A.upon(add_message, enter=S4A, outputs=[queue])
|
|
|
|
S4B.upon(lost, enter=S4A, outputs=[])
|
|
|
|
S4B.upon(add_message, enter=S4B, outputs=[queue, RC_tx_add])
|
|
|
|
S4B.upon(rx_message_theirs, enter=S4B, outputs=[accept])
|
|
|
|
S4B.upon(rx_message_ours, enter=S4B, outputs=[dequeue])
|
|
|
|
S4B.upon(rx_released, enter=S5B, outputs=[])
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
S5A.upon(connected, enter=S5B, outputs=[RC_tx_open, drain])
|
|
|
|
S5A.upon(add_message, enter=S5A, outputs=[queue])
|
|
|
|
S5B.upon(lost, enter=S5A, outputs=[])
|
|
|
|
S5B.upon(add_message, enter=S5B, outputs=[queue, RC_tx_add])
|
|
|
|
S5B.upon(rx_message_theirs, enter=S5B, outputs=[accept])
|
|
|
|
S5B.upon(rx_message_ours, enter=S5B, outputs=[dequeue])
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
if True:
|
|
|
|
S0A.upon(close, enter=SsB, outputs=[record_mood_and_RC_stop])
|
|
|
|
S0B.upon(close, enter=SsB, outputs=[record_mood_and_RC_stop])
|
|
|
|
S1A.upon(close, enter=SsB, outputs=[record_mood_and_RC_stop])
|
|
|
|
S2A.upon(close, enter=SrA, outputs=[record_mood])
|
|
|
|
S2B.upon(close, enter=SrB, outputs=[record_mood_and_RC_tx_release])
|
|
|
|
S3A.upon(close, enter=SrcA, outputs=[record_mood])
|
|
|
|
S3B.upon(close, enter=SrcB,
|
|
|
|
outputs=[record_mood_and_RC_tx_release_and_RC_tx_close])
|
|
|
|
S4A.upon(close, enter=SrcA, outputs=[record_mood])
|
|
|
|
S4B.upon(close, enter=SrcB,
|
|
|
|
outputs=[record_mood_and_RC_tx_release_and_RC_tx_close])
|
|
|
|
S5A.upon(close, enter=ScA, outputs=[record_mood])
|
|
|
|
S5B.upon(close, enter=ScB, outputs=[record_mood_and_RC_tx_close])
|
2016-12-30 06:18:20 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
SrcA.upon(connected, enter=SrcB, outputs=[RC_tx_release, RC_tx_close])
|
|
|
|
SrcB.upon(lost, enter=SrcA, outputs=[])
|
|
|
|
SrcB.upon(rx_closed, enter=SrB, outputs=[])
|
|
|
|
SrcB.upon(rx_released, enter=ScB, outputs=[])
|
2016-12-30 06:18:20 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
SrB.upon(lost, enter=SrA, outputs=[])
|
|
|
|
SrA.upon(connected, enter=SrB, outputs=[RC_tx_release])
|
|
|
|
SrB.upon(rx_released, enter=SsB, outputs=[RC_stop])
|
2016-12-30 06:18:20 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
ScB.upon(lost, enter=ScA, outputs=[])
|
|
|
|
ScB.upon(rx_closed, enter=SsB, outputs=[RC_stop])
|
|
|
|
ScA.upon(connected, enter=ScB, outputs=[RC_tx_close])
|
2016-12-30 05:30:59 +00:00
|
|
|
|
2017-02-22 19:26:11 +00:00
|
|
|
SsB.upon(stopped, enter=Ss, outputs=[W_closed])
|
2016-12-30 05:30:59 +00:00
|
|
|
|