2013-09-26 8 views
7

私はあなたにとって面倒で面白い質問があります。コルーチンベースの状態マシン

Twisted、Tornadoのトランスポート層を介したプロトコル実装などのI/Oタスクに取り組んでいるときにも、同様のシナリオやパターンが見つかりました。パターンは抽象的なものよりもかなり一般的です。たとえば、MODEMのようなデバイスで作業している場合は、コマンドを送信して結果を受け取ります。

ただし、最後のコマンドのモデムの応答に新しいコマンドを使用する必要がある場合があります。例えば、そのモデムがMであると仮定し - >つのパラメータ、メッセージキーを受け取り通信事業者であり、サーバは

1. s ->(a) M 
     1.1 M ->(b) S # modem reacts on `a` as `b`; so next we should send him command B 
     1.2 M ->(c) S # modem responses on `a` as `c`; so next we should send him C 
    2. s ->(b) M 
     2.1 M ->(g) S 
     2.2 M -> (f) S 
     ... 
     2.N M -> (x) S 
    ... 

だから、それがFSMの振る舞いのように見えるS.

です。ノンブロッキングI/O(ストリームオブジェクト経由)を扱う際には、このシナリオを竜巻に実装するのが良いでしょう。単にトラッキングシナリオを入力として提供し、入力に記述された状態(イベント)にハンドラをオーバーライドすることで、有限状態機械の優れた動作に到達することができます。これらすべての英数字の兆候は状態名で

{ 
    a: (b, c, d), 
    b: (c, 'exit|silence'), 
    c: (a, 'exit|silence'), 
    d: (b) 
} 

入力は以下の表記を有することができます。 各キーと値のペアは、状態名と可能な状態遷移のセットです。

竜巻コルーチンと先物に導入されたFSMの実装方法は? あなたの心とコードを共有してください。

+0

質問はありますか? – Veedrac

+1

@Veedrac今は良いですか? –

答えて

5

私はTwistedがプロトコルの実装により適していると思います。とにかく、Pythonの関数とメソッドは、ファーストクラスのオブジェクトであり、辞書の内部に格納することができます。 functools.partialを使用して、引数を持つ関数を辞書キーにバインドすることもできます。これを使ってトランジションを実装することができます。各状態は、キーが可能な入力状態であり、値が出力状態である辞書を含む関数でなければならない。その後、ある州から別の州に簡単にフープすることができます。 Tornadoループを使用するには、直接呼び出さずにioloop.IOLoop.instance().add_callbackを使用してコールバックとして登録する必要があります。

オートマトン受諾言語の実装例a * b *表C:Netcatをを使用して

import errno 
import functools 
import socket 
from tornado import ioloop, iostream 

class Communicator(object): 
    def connection_ready(self, sock, fd, events): 
     while True: 
      try: 
       connection, address = sock.accept() 
      except socket.error, e: 
       if e[0] not in (errno.EWOULDBLOCK, errno.EAGAIN): 
        raise 
       return 
      connection.setblocking(0) 
      self.stream = iostream.IOStream(connection) 
      self.stream.read_until(delimiter='\n', callback=self.initial_state) 

    def initial_state(self, msg): 
     msg = msg.rstrip() 
     print "entering initial state with message: %s" % msg 
     transitions = { 
      'a' : functools.partial(ioloop.IOLoop.instance().add_callback, self.state_a, msg), 
      'b' : functools.partial(ioloop.IOLoop.instance().add_callback, self.state_b, msg), 
      'c' : functools.partial(ioloop.IOLoop.instance().add_callback, self.final_state, msg) 
     } 
     try: 
      transitions[msg[0]]() 
     except: 
      self.stream.write("Aborted (wrong input)\n", self.stream.close) 

    def state_a(self, msg): 
     print "entering state a with message: %s" % msg 
     transitions = { 
      'a' : functools.partial(ioloop.IOLoop.instance().add_callback, self.stream.write, "got a\n", functools.partial(self.state_a, msg[1:])), 
      'b' : functools.partial(ioloop.IOLoop.instance().add_callback, self.state_b, msg), 
      'c' : functools.partial(ioloop.IOLoop.instance().add_callback, self.final_state, msg[1:]) 
     } 
     try: 
      transitions[msg[0]]() 
     except: 
      self.stream.write("Aborted (wrong input)\n", self.stream.close) 

    def state_b(self, msg): 
     print "entering state b with message: %s" % msg 
     transitions = { 
      'a' : functools.partial(ioloop.IOLoop.instance().add_callback, self.state_a, msg), 
      'b' : functools.partial(ioloop.IOLoop.instance().add_callback, self.stream.write, "got b\n", functools.partial(self.state_a, msg[1:])), 
      'c' : functools.partial(ioloop.IOLoop.instance().add_callback, self.final_state, msg[1:])} 
     try: 
      transitions[msg[0]]() 
     except: 
      self.stream.write("Aborted (wrong input)\n" , self.stream.close) 

    def final_state(self, msg): 
     print "entering final state with message: %s" % msg 
     self.stream.write("Finished properly with message %s\n" % msg, self.stream.close) 

if __name__ == '__main__': 
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) 
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    sock.setblocking(0) 
    sock.bind(("", 8000)) 
    sock.listen(5000) 

    communicator = Communicator() 
    io_loop = ioloop.IOLoop.instance() 
    callback = functools.partial(communicator.connection_ready, sock) 
    io_loop.add_handler(sock.fileno(), callback, io_loop.READ) 
    try: 
     io_loop.start() 
    except KeyboardInterrupt: 
     io_loop.stop() 
     print "exited cleanly" 

はセッション:

$ nc localhost 8000 
aaaaa 
got a 
got a 
got a 
got a 
got a 
Aborted (wrong input) 
$ nc localhost 8000 
abababab 
got a 
got b 
got a 
got b 
got a 
got b 
got a 
got b 
Aborted (wrong input) 
$ nc localhost 8000 
aaabbbc 
got a 
got a 
got a 
got b 
got b 
got b 
Finished properly with message 
$ nc localhost 8000 
abcabc 
got a 
got b 
Finished properly with message abc 
+0

いいですね、私はそれを少し洗練しようとします。ありがとうございました。 –

関連する問題