【问题标题】:Nodejs map serial port write to receive datanodejs映射串口写入接收数据
【发布时间】:2016-10-17 08:35:23
【问题描述】:

我目前正在使用node-serialport 模块进行串口通信。我将发送一个命令ATEC,它会回复ECHO

但是,这个发送和接收数据的过程是异步的(我发送数据后,我不会在data事件中知道数据何时到达),示例代码如下:

//Register the data event from the serial port
port.on('data', (data) => {
    console.log(data);
});

//Send data using serialport
port.write('ATEC');

反正我可以这样写吗?

//When i send the command, I could receive the data
port.write('ATEC').then((data)=> {
    console.log(data);
});

这有可能实现吗?

在使用request客户端的http通信中,我们可以做类似的事情

request.get('http:\\google.com')
    .on('response', (res) => {
        console.log(res);
    });

我想使用 serialport 复制相同的行为

【问题讨论】:

标签: javascript node.js promise


【解决方案1】:

我在串行数据接收中包装了一个承诺

function sendSync(port, src) {
    return new Promise((resolve, reject) => {
        port.write(src);
        port.once('data', (data) => {
            resolve(data.toString());
        });

        port.once('error', (err) => {
            reject(err);
        });
    });
}

请注意,该事件使用once 而不是on 以防止事件堆叠(请查看下面的 cmets 以获取更多信息 - 感谢@DKebler 发现它)

然后,我可以如下同步编写代码

sendSync(port, 'AThello\n').then((data) => {
    //receive data
});

sendSync(port, 'ATecho\n').then((data) => {
    //receive data
});

或者我可以使用生成器,使用 co

 co(function* () {
        const echo = yield sendSync(port, 'echo\n');
        const hello = yield sendSync(port, 'hello 123\n');

        return [echo, hello]
    }).then((result) => {
        console.log(result)
    }).catch((err) => {
        console.error(err);
    })

【讨论】:

  • 在上面以function sendSync(port, src) { 开头的示例中,在设置了两个事件处理程序后,您不想port.write(src);
  • 没关系,如果我的事件还没有注册,串口模块会将数据排队,所以当我注册时,我会从队列中获取数据,所以没有数据丢失
  • 您所写的代码不会有上面 qDot 解决的问题,即每次调用 SendSync 时都会不断堆积“数据”侦听器吗?如果是这样,“removeListerner”的最佳位置是什么。如果不是,请解释为什么可以这样。我认为与此问题相关的问题...stackoverflow.com/questions/33018352/…
  • then 回调中获取到数据后,所有数据监听器都会自动处理掉,可以通过在数据监听器中放入控制台日志来证明,并进行几次 sendsync 调用
  • 我把 console.log('num data listeners: ', port.listenerCount("data"));在两个地方。这是我在 5 次后得到的结果......5 个堆叠的“数据”监听器......你自己试过了吗......解决的承诺不会自动删除监听器,因为我的测试显示
【解决方案2】:

在我正在进行的一个项目中,我们遇到了类似的问题。需要一个串行的同步发送/接收循环,而 serialport 包让这有点奇怪。

我们的解决方案是为串行端口“数据”事件提供某种功能/承诺/生成器/等(取决于您的架构)队列。每次你写东西的时候,把一个函数/promise/etc 放到队列中。

假设您只是将函数放入队列中。当“data”事件被触发时,它将当前聚合的接收缓冲区作为参数发送到队列的第一个元素中,它可以查看它是否包含它需要的所有数据,如果是,则对其进行处理,并且以某种方式将自己从队列中移除。

这允许您使用相同的基本机制来处理多种不同类型的架构(回调/承诺/协程/等)。

额外的好处:如果您完全控制协议的双方,您可以在这些字符串的末尾添加一个“\n”,然后使用串行端口的“readline”解析器,这样您就只能获取数据整个字符串上的事件。可能比不断检查输入有效性更容易一些。

更新:

现在代码已经完成并经过测试(请参阅http://github.com/metafetish/buttshock-js 中的 ET312 模块),我是这样做的:

function writeAndExpect(data, length) {
  return new Promise((resolve, reject) => {
    const buffer = new Buffer(length);
    this._port.write(data, (error) => {
      if (error) {
        reject(error);
        return;
      }
    });
    let offset = 0;
    let handler = (d) => {
      try {
        Uint8Array.from(d).forEach(byte => buffer.writeUInt8(byte, offset));
        offset += d.length;
      } catch (err) {
        reject(err);
        return;
      }
      if (offset === length) {
        resolve(buffer);
        this._port.removeListener("data", handler);
      };
    };
    this._port.on("data", handler);
  });
}

上面的函数接受一个 uint8s 列表,以及一个期望的数据量,返回一个 Promise。我们写入数据,然后将自己设置为“数据”事件处理程序。我们使用它来读取数据,直到获得预期的数据量,然后解决承诺,将自己移除为“数据”侦听器(这很重要,否则您将堆栈处理程序!),并且完成。

此代码非常符合我的需求,除了非常严格的具有已知参数的发送/接收对之外,它不会处理其他情况,但它可能会给你一个开始的想法。

【讨论】:

  • 为什么不在reject(err);之后删除监听器。如果您有一堆错误,这不会导致处理程序也堆栈吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-07
  • 2018-05-07
  • 2022-08-20
  • 1970-01-01
  • 2013-01-07
  • 1970-01-01
相关资源
最近更新 更多