2014-02-26 3 views
2

TL; DR : 물리학 실험을 제어하고 읽는 Python 코드를 지속적으로 실행하고 있습니다. 이제 HTTP API를 추가하려고합니다.파이썬 프로세스를 지속적으로 실행하기위한 HTTP API 구현하기

USB를 사용하여 하드웨어를 제어하는 ​​모듈을 작성했습니다. 자율적으로 작동하는 여러 유형의 스크립트를 작성할 수 있지만 인터넷을 통해 실행중인 실험을 제어하고 싶습니다. 저는 HTTP API에 대한 아이디어를 좋아하고 Flask의 개발 서버를 사용하여 개념 증명을 구현했습니다.

이 실험은 USB 연결을 요구하는 단일 프로세스로 실행되며 주기적으로 (16ms마다) 모든 데이터를 읽습니다. 이 프로세스는 하드웨어 설정 및 명령을 쓰고 데이터 및 명령 응답을 읽습니다.

이 프로세스와 통신하는 데 올바른 방법을 선택하는 데 몇 가지 문제가 있습니다. HTTP 서버에 단일 작업자 만있는 경우 작동합니다. 그런 다음 파이썬의 다중 처리 기능을 사용할 수 있습니다. 통신용 파이프. 더 많은 또는 적은 저수준 소켓 (또는 zeromq 같은 것들)을 사용하여 요청/응답조차도 작동해야하지만 프로토콜의 일종을 구현해야합니다 : send { 'cmd': 'set_voltage', 'value': 900 } 대신 hardware.set_voltage (800) (독립 실행 형 스크립트에서 사용할 수 있음)를 호출합니다. 나는 일종의 RPC를 사용할 수 있지만, 모든 사람들 (SimpleXMLRPCServer, Pyro)은 '서버'(이 경우에는 실험을 실행하는 프로세스)에서 일종의 이벤트 루프를 사용하여 요청을 처리합니다. 하지만 들어오는 요청을 기다리는 이벤트 루프를 가질 수는 없습니다. 그것은 내 하드웨어를 읽고 있어야합니다! 나는 꽤 많이 봤는데,하지만 내 질문을 바꿔 보려고, 결국 셀레 리 대답으로, 결국 하나의 직업을 연기 해지지만 결국 장기 실행 프로세스와 의사 소통에 대해하지 않습니다.

나는 혼란 스럽다. 나는이 일을 할 수 있지만, 나는 몇 바퀴를 재발견 할까봐 걱정한다. 터미널에서 내 앱을 시작하고 어디에서든지 웹 브라우저를 열고 내 실험을 모니터링하고 제어하려고합니다.

업데이트 :

from pysparc.muonlab.muonlab_ii import MuonlabII 

muonlab = MuonlabII() 
muonlab.select_lifetime_measurement() 
muonlab.set_pmt1_voltage(900) 
muonlab.set_pmt1_threshold(500) 

lifetimes = [] 
while True: 
    data = muonlab.read_lifetime_data() 
    if data: 
     print "Muon decays detected with lifetimes", data 
     lifetimes.extend(data) 

모듈은 https://github.com/HiSPARC/pysparc/tree/master/pysparc/muonlab에 살고 : 다음 코드는 모듈을 사용하는 기본적인 예입니다. HTTP API의 현재 구현은 https://github.com/HiSPARC/pysparc/blob/master/bin/muonlab_with_http_api입니다.

많은 테스트를 거친 모듈에 꽤 만족하지만 HTTP API는 Flask의 단일 스레드 개발 서버 (설명서와 인터넷에서 나에게 나쁜 생각이라고 말함)를 사용하여 실행하고 파이프를 통해 사전을 전달합니다 IPC의 일종. 나는 위의 스크립트에서 이런 일을 할 수있을 싶어요 :

while True: 
    data = muonlab.read_lifetime_data() 
    if data: 
     print "Muon decays detected with lifetimes", data 
     lifetimes.extend(data) 
    process_remote_requests() 

어디 process_remote_requestsmuonlab 인스턴스를 호출하거나 데이터를 반환하는 매우 간단한 기능입니다. 내가 SQLite는 또는 동시 액세스를 처리하는 다른 무언가에 그것을 저장할 수 있기 때문에, 문제의 아마 덜

muonlab = RemoteMuonlab() 

@app.route('/pmt1_voltage', methods=['GET', 'PUT']) 
def get_data(): 
    if request.method == 'PUT': 
     voltage = request.form['voltage'] 
     muonlab.set_pmt1_voltage(voltage) 
    else: 
     voltage = muonlab.get_pmt1_voltage() 
     return jsonify(voltage=voltage) 

응용 프로그램에서 측정 데이터를 얻기입니다 : 그럼, 내 플라스크보기에, 내가 좋아하는 뭔가를 것입니다.

+0

사용중인 코드를 표시해야합니다. 최소 코드 샘플을 게시하는 것은 SO에 대한 요구 사항입니다. – jeremyjjbrown

+0

아동 실험 과정을 중단시키는 제어 프로세스가 필요한 이유가 있습니까? 자식은 새로운 업데이트를 부모에게 다시 보낼 수 있습니다. 이 시점에서 웹 서버가 소켓이나 XMLRPC를 통해 부모와 대화하고 잠시 동안 업데이트를 받도록하십시오 – Matt

+0

@matt이 경우 부모에게는 XMLRPC 요청을 처리 할뿐만 아니라 업데이트를받는 이벤트 루프가 있어야합니다 , 권리? –

답변

2

하지만 ... 은 IO 루프가 있습니다. 16ms마다 실행됩니다.

이러한 경우에는 BaseHTTPServer.HTTPServer을 사용할 수 있습니다. timeout 속성을 작은 값으로 설정하면됩니다. 근본적으로 ...

class XmlRPCApi: 
    def do_something(self): 
     print "doing something" 

server = SimpleXMLRPCServer(("localhost", 8000)) 
server.register_instance(XMLRpcAPI()) 
server.timeout = 0 

while True: 
    sleep(0.016) 
    do_normal_thing() 
    x.handle_request() 

편집 : 파이썬은 또한 플라스크 응용 프로그램을 제공 할 수있는, BaseHTTPServer을 기반으로, 서버에 구축했다. flask.Flask()이 WSGI 호환 응용 프로그램을 될 일이 있기 때문에, 당신의 process_remote_requests()는 다음과 같이한다 : 이것은 당신이 단지 단기 실행 요청이있는 경우 충분히 잘 작동

import wsgiref.simple_server 
remote_server = wsgire.simple_server('localhost', 8000, app) 
# app here is just your Flask() application! 

# as before, set timeout to zero so that you can go right back 
# to your event loop if there are no requests to handle 
remote_server.timeout = 0 

def process_remote_requests(): 
    remote_server.handle_request() 

; 이벤트 루프의 일반 폴링 간격보다 오래 걸리는 요청을 처리해야하거나 단위 시간당 폴링보다 많은 요청을 처리해야하는 경우이 방법을 정확하게 사용할 수 없습니다.

다른 프로세스를 포크 할 필요는 없습니다.하지만 다른 스레드에서 작업자 풀을 사용하면 잠재적으로 얻을 수 있습니다. 약 :

import threading 
import wsgiref.simple_server 
remote_server = wsgire.simple_server('localhost', 8000, app) 
POOL_SIZE = 10 # or some other value. 
pool = [threading.Thread(target=remote_server.serve_forever) for dummy in xrange(POOL_SIZE)] 
for thread in pool: 
    thread.daemon = True 
    thread.start() 

while True: 
    pass # normal experiment processing here; don't handle requests in this thread. 

그러나; 이 접근 방식에는 하나의 큰 단점이 있습니다. 이제는 동시성을 처리해야합니다! 당신이 위의 루프로 할 수있는 것처럼 프로그램 상태를 자유롭게 조작하는 것은 안전하지 않습니다. 메인 스레드 (또는 다른 http 서버 스레드)에서 같은 상태를 동시에 조작 할 수도 있기 때문입니다. 이것이 유효 할 때를 알고, 각 자원을 일종의 뮤텍스 잠금 또는 적절한 것으로 감싸는 것은 사용자의 몫입니다.

+0

SocketServer 코드를 탐색했습니다. 'server.serve_forever()'를 호출하지 말고 주기적으로'server.handle_request()'를 호출하도록 제안했음을 이해 했는가? 내 실험 데이터에서 읽은 데이터 내부에서? –

+0

그래, 그게 내가 말하는거야. 일단 내가 일하면 코드에 맞는 내 대답을 적용 할 것입니다. – SingleNegationElimination

+0

@David : 제 편집을보십시오, 도움이 되었으면 좋겠습니다. – SingleNegationElimination

관련 문제