1

编辑#2:猜猜看,我快到了!我正面临着似乎是我最后一个问题,好吧,只要涉及到编程。这实际上很有趣,我以前从未遇到过这样的事情。问题出在下面的代码中,我的 javascript 函数。我通常从不发布看起来很容易解决的问题,但我真的不知道这里发生了什么。

问题似乎在于更新功能的第一个条件。看到说 alert('hey'); 的行 ? 好吧,如果我删除那条线,由于某种未知的原因,没有任何东西被发送到动作函数。也不是 Arduino,也不是控制台……什么都没有发生。正如我喜欢这样称呼它,这绝对是令人着迷的。我不知道。我想也许 alert() 造成了读取 arduino 输出所必需的某种延迟,但是当我使用 setTimeout 创建延迟时,也没有任何反应。这太不可思议了。

再来一次:没有警报,动作函数不会被调用,我通过让函数打印一些东西来检查它是否被调用。什么都没有打印,什么都没有。它只是没有被调用。但是有了警报,就会调用该函数,并且 arduino 会打开 LED。

你有什么解释吗?这是我的代码:

function update(command=0) {
// if command send it
if (command!=0) {
    $.getJSON('/action?command='+command);
    alert('hey');
}

// read no matter what
$.getJSON('/read', {}, function(data) {
    if (data.state != 'failure' && data.content != '') {
        $('.notice').text(data.content);
        $('.notice').hide().fadeIn('slow');
        setTimeout(function () { $('.notice').fadeOut(1000); }, 1500);
    }
    setTimeout(update, 5000);
});
}

update();

我正在尝试创建一个可从任何计算机访问的 Web 界面来控制我的 Arduino。我越来越近了。我的一个问题是,使用以下代码,当我按下按钮向 Arduino 发送命令时,Arduino 确实得到了它(LED按照配置闪烁),然后发送一条消息,Python脚本确实检索数据,但无法正确显示。该字符串遗漏了一些字符,并且index.html未按需要返回。

基本上,按下按钮时会调用一个函数,并且我需要在与生成结果的函数不同的函数中返回函数的结果。

这是代码:

# -*- coding: utf-8 -*-

import cherrypy, functools, json, uuid, serial, threading, webbrowser, time

try:
    ser = serial.Serial('COM4', 9600)
    time.sleep(2)
    ser.write('1')
except:
    print('Arduino not detected. Moving on')

INDEX_HTML = open('index.html', 'r').read()

def timeout(func, args = (), kwargs = {}, timeout_duration = 10, default = None):
    class InterruptableThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.result = default
        def run(self):
            self.result = func(*args, **kwargs)

    it = InterruptableThread()
    it.start()
    it.join(timeout_duration)
    if it.isAlive():
        return it.result
    else:
        return it.result

def get_byte(useless):
    return ser.read().encode('Utf-8')

def json_yield(command):
    @functools.wraps(command)
    def _(self, command):
        if (command == 'Turn the LED on'):
            ser.write('2')
            time.sleep(2)
            print('wrote to port')
        print('ok, ok')
        try:
            m = ''
            while 1:
                print('reading...')
                byte = timeout(get_byte, ('none',), timeout_duration = 5)
                if byte == '*' or byte == None: break
                m = m + byte
            content = m
            time.sleep(1)
            return json.dumps({'state': 'ready', 'content':content})
        except StopIteration:
            return json.dumps({'state': 'done', 'content': None})
    return _

class DemoServer(object):
    @cherrypy.expose
    def index(self):
        return INDEX_HTML

    @cherrypy.expose
    @json_yield
    def yell(self):
        yield 'nothing'

    @cherrypy.expose
    @json_yield
    def command(self):
        yield 'nothing'

if __name__ == '__main__':
    t = threading.Timer(0.5, webbrowser.open, args=('http://localhost:8080',))
    t.daemon = True
    t.start()
    cherrypy.quickstart(DemoServer(), config='config.conf')
4

1 回答 1

1

首先,我没有想过在你之前的问题中告诉你这个问题,但不久前我写了一个名为pyaler的软件,它完全符合你的要求(除了它不支持长轮询请求,因为它是?)wsgi限制)。

要回答您的问题,为什么不让您的表单成为发送操作的 javascript 查询,并以 JSON 格式获取结果,您可以解析并使用结果更新当前页面?那样更优雅、更简单,而且 2013 年......

遗憾的是,鉴于您的代码,我无法真正说出为什么您没有得到结果。理解它是否真的做你想做的事情太复杂了......亲!

避免在模块范围内做任何事情,除非你把它放进去if __name__ == "__main__",或者你想通过导入来扩展你的模块的那一天,你会偶然执行一些代码,它会迫使你为你的代码做出更好的设计。

您可以将InterruptableThread()类从超时函数中取出,并将其default作为参数提供。InterruptableThread(default)def __init__(self, default): self.result = default。但是谈到那部分,为什么你要做这么复杂的事情,而你在创建串行连接时有一个可以使用的timeout论点?

这是我对您的代码进行的轻微修改:

# -*- coding: utf-8 -*-

import cherrypy, functools, json, uuid, serial, threading, webbrowser, time

def arduino_connect(timeout=0):
    try:
        ser=serial.Serial('COM4', 9600, timeout=timeout)
        time.sleep(2)
        ser.write('1')
        return ser
    except:
        raise Exception('Arduino not detected. Moving on')

class ArduinoActions(object):
    def __init__(self, ser):
        self.ser = ser

    def get_data(self):
        content = ""
        while True:
            print('reading...')
            data = self.ser.read().encode('utf-8')
            if not data or data == '*':
                return content
            content += data

    def turn_led_on(self):
        ser.write('2')
        time.sleep(2)
        print('wrote led on to port')

    def turn_led_off(self):
        ser.write('2') # Replace with the value to tur the led off
        time.sleep(2) 
        print('wrote to led off port')

class DemoServer(ArduinoActions):
    def __init__(self, ser):
        ArduinoActions.__init__(self, ser)
        with open('index.html', 'r') as f:
            self.index_template = f.read()

    @cherrypy.expose
    def index(self):
        return self.index_template

    @cherrypy.expose
    def action(self, command):
        state = 'ready'
        if command == "on":
            content = self.turn_led_on()
        elif command == "off":
            content = self.turn_led_off()
        else:
            content = 'unknown action'
            state = 'failure'
        return {'state': state, 'content': content}

    @cherrypy.expose
    def read(self):
        content = self.get_data()
        time.sleep(1)
        # set content-type to 'application/javascript'
        return content

if __name__ == '__main__':
    ser = arduino_connect(5)
    # t = threading.Timer(0.5, webbrowser.open, args=('http://localhost:8080',))
    # t.daemon = True
    # t.start()
    cherrypy.quickstart(DemoServer(), config='config.conf')

在您的 javascript 代码中,您正在调用yell实际上什么都不返回的资源。您最好创建一个action方法(因为我修改了给定的 python 代码)和一个单独的read()方法。因此,您的action方法将通过写入字节来作用于 arduino,从而向 arduino 发出命令,并且该 read方法将读取输出。

由于网络服务器可能会在串行对象上创建对读/写方法的并行调用,并且您不能并行读取同一对象,因此您可能希望创建一个独立线程,通过创建一个继承自的新类threading.Thread,您实现读取串行输出的无限循环(通过将串行对象作为参数提供给该类的__init__函数)。然后将每个新content数据推送到 a list,如果您想保留所有先前数据的日志,或者Queue.Queue如果您只想返回最后一个读数。然后ArduinoActions,在read() 方法中,您只需要返回list将随着来自 arduino 的任何新读数而增长的数据,从而记录所有数据(或者如果您有队列,则获取最后一个数据)。

$(function() {
    function update(command) {
        // if you give a command argument to the function, it will send a command
        if (command) {
            $.getJSON('/action?command='+command);
        }
        // then it reads the output
        $.getJSON('/read, {}, function(data) {
        if (data.state !== 'failure' && data.content !== '') {
            $('.notice').text(data.content);
            $('.notice').hide().fadeIn('fast');
            setTimeout(function () { $('.notice').fadeOut('fast'); }, 1500);
        }
        // and rearms the current function so it refreshes the value
        setTimeout(update(), 2); // you can make the updates less often, you don't need to flood your webserver and anyway the arduino reading are blocking
    });
}
update();
});

始终在 javascript 中使用===or !==,因此不要强制输入。您可以不那么频繁地再次调用该函数如果您不评估 javascript 参数,则默认情况下将其设置为未定义。

这只是对你所写内容的一点更新,现在已经很晚了,所以我希望你能从中做出一些好的事情!

高温高压

于 2013-06-20T17:06:00.443 回答