谷歌App Engine,按钮点击开始/停止脚本 [英] Google App Engine, start / stop continuously script on button click

查看:84
本文介绍了谷歌App Engine,按钮点击开始/停止脚本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找关于如何实现函数启动/停止功能的指导,该函数通过点击按钮来使 restAPI 连续检索JSON格式的数据。通过 webapp2 并托管在GAE上呈现的HTML页面?



当前的行为是一旦http请求完成当然被调用的函数会停止( while self._running == True )(根据GAE文档的正常行为)。



main.py:

 #!/ usr / bin / env python 

从google.appengine.api导入webapp2
从matplotlib.path导入urlfetch
作为mpPath导入路径
导入json
导入base64
导入套接字
导入日志记录
from线程导入线程
导入jinja2

template_dir = os.path.join(os.path.dirname(__ file__))
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir),
extensions = ['jinja2.e xt.autoescape'],autoescape = True)

#创建一个用于发送命令的UDP套接字
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock .setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)

#创建一个无限运行的线程的停止/启动功能,直到
#线程终止

class CMX:
def __init __(self):
self._running = True

def terminate(self):
self._running = False

def run(self):
storedCredentials = False
username = None
password = None

ip_address ='someip'#commands are sent to the用于寻址区域(托架)的广播地址
port = someport#使用端口50011,因为没有返回数据包被发回,可能导致
#使用广播时不执行命令。

#定义不同区域的边界
zone = [[(106.03,141.19),(158.94,141.19),(158.94,194.50),(106.03,194.50)],$ (103.76,168),(62.26,168),(62.26,77.86),(103.67,77.86)],
[(106.38,77.86),(191.95,77.86),(191.95,106.52) ),(106.38,106.52)]]

flag_zone_1 = False
flag_zone_2 = False
flag_zone_3 = False

while self._running == True:

restURL ='http:// someurl'
打印restURL

如果没有存储凭证:
用户名='用户名'
密码= 'password'
storedCredentials = True
try:
request = urlfetch.fetch(url = restURL,headers = {Authorization:Basic%s%base64.b64encode(username +' :'+ password)})

<执行动作和其他函数调用>



除了urlfetch.Error:
logging.exception('捕获异常获取url')


类Handler(webapp2.RequestHandler):
def write(self,* a,** kw):
self.response.out.write(* a,** kw)
$ b $ def render_str(self,template,* * params):
t = jinja_env.get_template(template)
return t.render(params)

def render(self,template,** kw):
self.write(self.render_str(template,** kw))
$ b $ class MainPage(Handler):
def get(self):
button =开始演示
running = False
self.render('page.html',button = button,run = running)

def post(self):
startDemo = CMX( )
t = Thread(target = startDemo.run,args =())
t.daemon = True

如果self.request.get('button')==开始演示:
button =停止演示
running = Tr ue
self.render('page.html',button = button,run = running)
t.start()

else:
button =Start演示
running = False
self.render('page.html',button = button,run = running)
startDemo.terminate()


def which_zone(xcoord,ycoord,zone):
point =(xcoord,ycoord)
in_zone_1 = mpPath(zone [0])。contains_point(point)
in_zone_2 = mpPath(zone (point)
in_zone_3 = mpPath(zone [2])。contains_point(point)

如果in_zone_1 == True:
返回Zone 1
elif in_zone_2 == True:
返回Zone 2
elif in_zone_3 == True:
返回Zone 3

def dim_lights(ip_address ,端口,control_string,sock):
control_string = control_string +'S0F10'+'\r'
#sock.sendto(control_string,(ip_address,port))
return control_string

def norm_light s(ip_address,port,control_string,sock):
control_string = control_string +'S255F10'+'\r'
#sock.sendto(control_string,(ip_address,port))
return control_string

app = webapp2.WSGIApplication([('/',MainPage)],debug = True)

page.html:

  {%extendsbase.html%} 

{%block comment%}
{%autoescape true%}
< form method =post>
< div>
< input type =submitname =buttonvalue ={{button}}>
< input type =hiddenname =runvalue ={{run}}>
< / div>
<! - < div> macAddress:< input type =textname =macAddress>< br>
< input type =submitvalue =提交>
< / div> - >

< / form>
{%endautoescape%}
{%endblock%}


解决方案开始/停止功能很简单 - 只需使按钮控件像 operation_is_stopped 标志一样在请求(例如数据存储区中)中保持不变。

如果您还没有意识到这一点,您的难题确实来自您想要使用该按钮进行控制的连续操作。这与GAE不兼容--GAE中的所有内容都围绕着对请求的响应,在有限的时间内。你不能在GAE中真正拥有无限期运行的程序/线程。



但在很多情况下,可以实现一个长期运行的迭代连续操作(就像你的)短暂的操作流程。在GAE中,可以使用任务队列轻松实现 - 每次迭代(在你的情况是的体,而self._running == True 循环)被实现为对任务队列请求的响应。

当触发开始动作时,通过排列相应的任务来启动流程。在处理先前的任务请求之后,通过排队各个任务来维持该流程。并且它被 阻止,而不是排入新任务:)



这些内容包括:

  def post(self):#长时间运行任务请求的处理程序

#每次需要重建/恢复请求上下文
...

尝试:
request = urlfetch.fetch(...)
<执行动作和其他函数调用>
除了:
...

终于:
如果不是operation_is_stopped:
#排队另一个长时间运行任务
task = taskqueue .add(...)


I am looking for guidance in how to achieve start / stop functionality of a function, which makes a restAPI to retrieve data in JSON format continuously, by clicking a button on a rendered html page through webapp2 and hosted on GAE?

The current behaviour is once the http request is completed the function that was called of course stops (while self._running == True)(normal behaviour according to the GAE documentation).

main.py :

#!/usr/bin/env python
#
import webapp2
from google.appengine.api import urlfetch
from matplotlib.path import Path as mpPath
import json
import base64
import socket
import logging
from threading import Thread
import jinja2

template_dir = os.path.join(os.path.dirname(__file__))
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir),
            extensions=['jinja2.ext.autoescape'], autoescape=True)

# create a UDP socket for sending the commands
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

# create the stop / start functionality of a thread that runs infinite until 
# thread is terminated

class CMX:
    def __init__(self):
        self._running = True

    def terminate(self):
        self._running = False

    def run(self):
        storedCredentials = False
        username = None
        password = None

        ip_address = 'someip'                 # commands are send to the broadcast address for addressing the zones(bays)
        port = someport                       # use of port 50011 because no return packet is send back, could cause the 
                                              # lights not to execute the command when using broadcast.

        # define the boundaries of the different zones
        zone = [[(106.03,141.19),(158.94,141.19),(158.94,194.50),(106.03,194.50)],
              [(103.76,168),(62.26,168),(62.26,77.86),(103.67,77.86)],
              [(106.38,77.86),(191.95,77.86),(191.95,106.52),(106.38,106.52)]]

        flag_zone_1 = False
        flag_zone_2 = False
        flag_zone_3 = False

        while self._running == True:

            restURL = 'http://someurl' 
            print restURL

            if not storedCredentials:
                username = 'username'
                password = 'password'
                storedCredentials = True
            try:
                request = urlfetch.fetch(url = restURL, headers={"Authorization": "Basic %s" % base64.b64encode(username +':'+ password)})

                <perform actions and other function calls>
                 .
                 .

            except urlfetch.Error:
                logging.exception('Caught exception fetching url')


class Handler(webapp2.RequestHandler):
    def write(self, *a, **kw):
        self.response.out.write(*a, **kw)

    def render_str(self, template, **params):
        t = jinja_env.get_template(template)
        return t.render(params)

    def render(self, template, **kw):
        self.write(self.render_str(template, **kw))        

class MainPage(Handler):
    def get(self):
        button = "Start Demo"
        running = False
        self.render('page.html', button = button, run = running)

    def post(self):
        startDemo = CMX()
        t = Thread(target=startDemo.run, args=())
        t.daemon = True

        if self.request.get('button') == "Start Demo":
            button = "Stop Demo"
            running = True
            self.render('page.html', button = button, run = running) 
            t.start() 

        else:
            button = "Start Demo"
            running = False
            self.render('page.html', button = button, run = running) 
            startDemo.terminate()


def which_zone(xcoord, ycoord, zone):
  point = (xcoord, ycoord)
  in_zone_1 = mpPath(zone[0]).contains_point(point)
  in_zone_2 = mpPath(zone[1]).contains_point(point)
  in_zone_3 = mpPath(zone[2]).contains_point(point)

  if in_zone_1 == True:
    return "Zone 1"
  elif in_zone_2 == True:
    return "Zone 2"
  elif in_zone_3 == True:
    return "Zone 3"

def dim_lights(ip_address, port, control_string, sock):
  control_string = control_string + 'S0F10' +'\r'
  #sock.sendto(control_string, (ip_address, port))
  return control_string

def norm_lights(ip_address, port, control_string, sock):
  control_string = control_string + 'S255F10' +'\r'
  #sock.sendto(control_string, (ip_address, port))
  return control_string

app = webapp2.WSGIApplication([('/', MainPage)], debug=True)

page.html :

{% extends "base.html" %}

{% block comment %}
{% autoescape true %}
    <form method="post">
            <div>
                <input type="submit" name="button" value="{{button}}">
                <input type="hidden" name="run" value="{{run}}">
            </div> 
            <!-- <div>macAddress: <input type="text" name="macAddress"><br>
                <input type="submit" value="Submit">
            </div> -->

    </form> 
{% endautoescape %}
{% endblock %}

解决方案

The start/stop functionality is simple - just make the button control something like an operation_is_stopped flag persisted across requests (in the datastore, for example).

In case you didn't realize it yet your difficulty really comes from achieving the continuous operation that you want to control with that button. That's what's not really compatible with GAE - everything in GAE revolves around responding to requests, in a limited amount of time. You can not really have indefinitely-running proceses/threads in GAE.

But in many cases it's possible to implement a long-running, iterative continuous operation (like yours) as a flow of short-lived operations. In GAE that can be easily achieved using the task queues - each iteration (in your case the body of the while self._running == True loop) is implemented as a response to a task queue request.

The flow is started by enqueueing a respective task when the "start" action is triggered. The flow is maintained by enqueueing a respective task after processing of a previous task request. And it's stopped by not enqueueing a new task :)

Something along these lines:

def post(self):  # handler for the "long running" task requests

    # need to rebuild/restore your request context every time
    ...

    try:
        request = urlfetch.fetch(...)
        <perform actions and other function calls>
    except:
        ...

    finally:
        if not operation_is_stopped:
            # enqueue another "long running" task
            task = taskqueue.add(...)

这篇关于谷歌App Engine,按钮点击开始/停止脚本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆