Node-RED系列(十二):定制化节点教程

这把我C 2021-02-20 17:22:26 18983

Node-RED系列文章目前已经写了11篇,介绍了Node-RED的安装以及默认安装的一些基本节点的使用,作为物联网的一个可视化拖动的流程,Node-RED的确实很容易上手。还没开始学习的同学可以先看下我以前的文章

看了我的Node-RED系列文章后,很多同学都问我能不能出一篇详细的定制节点的教程,于是今天我抽点时间,将定制Node-RED的节点的相关教程整理一下,分享给大家,
如果大家还没有用过Node-RED不建议先看本篇文章,可以从第一篇看起,安装一个Node-RED玩玩。
好了下面让我们进入主题。

由于Node-RED是开源的,你可以在github找到项目的所有代码,所以我们有里有相信,我们可以基于Node-RED为原型,进行各种集成,开发。
比较简单的一种就是做一些的定制的节点来满足自己的日常需求。如果做的好,拿来卖钱也不是不可能的。

好了让我们言归正传。如何进行自定义节点?如何将自定义的节点发布,部署?

首先让我们了解下Node-RED的节点到底是什么?

我们从github上下载源码后,可以在这个模块找到Node-RED的默认安装的节点,

其中一个html节点的代码

大致我们清楚了节点需要什么内容。

环境准备

要制作一个定制的节点

首先要准备这几个文件

  • 定制节点的html 用于显示配置节点
  • 定制节点的JavaScript 用于处理Node-RED流中的数据
  • package.json文件(可选,如果需要npm安装才必须要)

下面我们来做一个简单的demo,来演示一下,如何定制一个node

定制节点的作用是将payload转化为小写

首先我们创建一个文件夹 名字为node-red-contrib-example-lower-case

然后在其中创建二个文件
一个是lower-case.js 另一个是lower-case.html

文件lower-case.js是将msg.payload转化为消息,使用

js文件解析

lower-case.js

module.exports = function(RED) {
    function LowerCaseNode(config) {
        RED.nodes.createNode(this,config);
        var node = this;
        node.on('input', function(msg) {
            msg.payload = msg.payload.toLowerCase();
            node.send(msg);
        });
    }
    RED.nodes.registerType("lower-case",LowerCaseNode);
}

下面详细讲解一下这段代码,首先使用module.exports导出一个函数这个函数有一个参数, 定义为叫做RED 其实就是Node-RED的上下文。
然后其中定义一个方法叫做LowerCaseNode 这个方法就是将一个单纯的将payload转化的函数。
方法体内有一句

RED.nodes.createNode(this,config);

这一句是一个闭包,直接使用父作用域的RED参数,创建一个Node。 那么this是什么那? this就是上下文,就是flow。 config是该节点的配置项。

紧接着是一个事件监听

node.on('input', function(msg) {})

由于node是基于事件驱动,所以在flow的上下文中,都是用过一系列的监听和触发来进行传递上下文的.
每个节点中都会有这样一个监听函数 有的是这样写的

this.on("input", function(msg, send, done) {})

这里的this其实就是node。

toLowerCase()payload转化,再次赋值给msg.payload

那么这个事件监听函数输出的是什么那,在最后一行给出了答案

node.send(msg);

将msg传递出去,向下流转。

还有最后一句代码

RED.nodes.registerType("lower-case",LowerCaseNode);

这行代码就是注册一个节点。 第一个参数是节点的名称,第二是一个函数。

这里还有一个inject节点的源码,大家可以看一下,相信稍微学过一些js的同学都能看懂


module.exports = function(RED) {
    "use strict";
    var cron = require("cron");

    function InjectNode(n) {
        RED.nodes.createNode(this,n);

        /* Handle legacy */
        if(!Array.isArray(n.props)){
            n.props = [];
            n.props.push({
                p:'payload',
                v:n.payload,
                vt:n.payloadType
            });
            n.props.push({
                p:'topic',
                v:n.topic,
                vt:'str'
            });
        } else {
            for (var i=0,l=n.props.length; i<l; i++) {
                if (n.props[i].p === 'payload' && !n.props[i].hasOwnProperty('v')) {
                    n.props[i].v = n.payload;
                    n.props[i].vt = n.payloadType;
                } else if (n.props[i].p === 'topic' && n.props[i].vt === 'str' && !n.props[i].hasOwnProperty('v')) {
                    n.props[i].v = n.topic;
                }
            }
        }

        this.props = n.props;
        this.repeat = n.repeat;
        this.crontab = n.crontab;
        this.once = n.once;
        this.onceDelay = (n.onceDelay || 0.1) * 1000;
        this.interval_id = null;
        this.cronjob = null;
        var node = this;

        node.props.forEach(function (prop) {
            if (prop.vt === "jsonata") {
                try {
                    var val = prop.v ? prop.v : "";
                    prop.exp = RED.util.prepareJSONataExpression(val, node);
                }
                catch (err) {
                    node.error(RED._("inject.errors.invalid-expr", {error:err.message}));
                    prop.exp = null;
                }
            }
        });

        if (node.repeat > 2147483) {
            node.error(RED._("inject.errors.toolong", this));
            delete node.repeat;
        }

        node.repeaterSetup = function () {
            if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
                this.repeat = this.repeat * 1000;
                if (RED.settings.verbose) {
                    this.log(RED._("inject.repeat", this));
                }
                this.interval_id = setInterval(function() {
                    node.emit("input", {});
                }, this.repeat);
            } else if (this.crontab) {
                if (RED.settings.verbose) {
                    this.log(RED._("inject.crontab", this));
                }
                this.cronjob = new cron.CronJob(this.crontab, function() { node.emit("input", {}); }, null, true);
            }
        };

        if (this.once) {
            this.onceTimeout = setTimeout( function() {
                node.emit("input",{});
                node.repeaterSetup();
            }, this.onceDelay);
        } else {
            node.repeaterSetup();
        }

        this.on("input", function(msg, send, done) {
            var errors = [];

            this.props.forEach(p => {
                var property = p.p;
                var value = p.v ? p.v : '';
                var valueType = p.vt ? p.vt : 'str';

                if (!property) return;

                if (valueType === "jsonata") {
                    if (p.exp) {
                        try {
                            var val = RED.util.evaluateJSONataExpression(p.exp, msg);
                            RED.util.setMessageProperty(msg, property, val, true);
                        }
                        catch  (err) {
                            errors.push(err.message);
                        }
                    }
                    return;
                }
                try {
                    RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true);
                } catch (err) {
                    errors.push(err.toString());
                }
            });

            if (errors.length) {
                done(errors.join('; '));
            } else {
                send(msg);
                done();
            }
        });
    }

    RED.nodes.registerType("inject",InjectNode);

    InjectNode.prototype.close = function() {
        if (this.onceTimeout) {
            clearTimeout(this.onceTimeout);
        }
        if (this.interval_id != null) {
            clearInterval(this.interval_id);
            if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
        } else if (this.cronjob != null) {
            this.cronjob.stop();
            if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
            delete this.cronjob;
        }
    };

    RED.httpAdmin.post("/inject/:id", RED.auth.needsPermission("inject.write"), function(req,res) {
        var node = RED.nodes.getNode(req.params.id);
        if (node != null) {
            try {
                node.receive();
                res.sendStatus(200);
            } catch(err) {
                res.sendStatus(500);
                node.error(RED._("inject.failed",{error:err.toString()}));
            }
        } else {
            res.sendStatus(404);
        }
    });
}
html解析

lower-case.html

<script type="text/javascript">
    RED.nodes.registerType('lower-case',{
        category: 'function',
        color: '#a6bbcf',
        defaults: {
            name: {value:""}
        },
        inputs:1,
        outputs:1,
        icon: "file.png",
        label: function() {
            return this.name||"lower-case";
        }
    });
</script>

<script type="text/html" data-template-name="lower-case">
    <div class="form-row">
        <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
        <input type="text" id="node-input-name" placeholder="Name">
    </div>
</script>

<script type="text/html" data-help-name="lower-case">
    <p>A simple node that converts the message payloads into all lower-case characters</p>
</script>

html的内容是用于配置节点以及节点的备注解释。

RED.nodes.registerType('lower-case',{  // 注册一个节点,当拖动节点到编辑区域中
  category: 'function',  // 节点归属于哪一个分类
  color: '#a6bbcf',  // 颜色
  defaults: {
      name: {value:""}   // 输入框的值
  },
  inputs:1,  // 能否输入   1是,0 否
  outputs:1,  // 能否输出 1是,0 否
  icon: "file.png",  // 节点的图标
  label: function() {
    return this.name||"lower-case";  // label 的默认值
  }
});

标签<script type="text/html" data-template-name="lower-case"> 中的内容是用于显示在节点抽屉中的,
点击节点即可以显示

标签<script type="text/html" data-help-name="lower-case"> 中的内容是用于显示在帮助文档中的。

package.json
{
  "name" : "node-red-contrib-example-lower-case",
  "node-red" : {
      "nodes": {
          "lower-case": "lower-case.js"
      }
  }
}
如何安装改节点那

在node-red项目根目录中执行

npm install <location of node module>

如果是要在本地安装,linux下

cd ~/.node-red
npm install ~/dev/node-red-contrib-example-lower-case

windows下安装

cd C:\Users\my_name\.node_red
npm install C:\Users\my_name\Documents\GitHub\node-red-contrib-example-lower-case
声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 96 9 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
这把我C
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区