2013-09-26 7 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.

입니다. 토네이도에서이 시나리오를 구현하는 것이 좋으며 비 블로킹 입출력 (스트림 오브젝트를 통해)으로 작업하는 것이 좋습니다. 단순히 추적 시나리오를 입력으로 제공하고 입력에 설명 된 상태 (이벤트)에 처리기를 무시하면 좋은 유한 상태 시스템 동작에 도달 할 수 있습니다. 이러한 모든 숫자 표시 상태의 이름입니다

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

:

입력은 다음과 같은 표기법을 가질 수있다. 각 키 - 값 쌍은 상태 이름 및 가능한 상태 전환 세트입니다.

토네이도 코 루틴 및 선물에 도입 된 FSM의 구현 방법은 무엇입니까? 의견을 보내주십시오.

+0

가된다? – Veedrac

+1

@Veedrac 더 나은가요? –

답변

5

Twisted은 프로토콜 구현에 더 적합하다고 생각합니다. 어쨌든, 파이썬에서 함수와 메소드는 퍼스트 클래스의 객체이므로 사전에 저장할 수 있습니다. functools.partial을 사용하여 인수가있는 함수를 사전 키에 바인딩 할 수도 있습니다. 전환을 구현하는 데 사용할 수 있습니다. 각 상태는 키가 가능한 입력 상태이고 값은 출력 상태 인 사전을 포함하는 함수 여야합니다. 그러면 한 국가에서 다른 국가로 쉽게 넘어갈 수 있습니다. Tornado 루프를 사용하려면 다음 상태를 직접 호출하는 대신 ioloop.IOLoop.instance().add_callback을 사용하여 콜백으로 등록해야합니다.

오토마타 언어에게 B 형 * * C를 접수 구현 예 :

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" 

세션 netcat을 사용 : 질문

$ 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

좋아 보인다, 나는 그것을 조금 더 세밀하게하려고 노력할 것이다. 고맙습니다. –