node-red 可视化探索

  • node-red的基础使用不讲了,百度有较多资料,跟着官网也可以写个简单的自定义组件
  • 这里主要讲的是如何将复杂的组件输出成一个 独立的html页面并于vue框架结合形成一个友好的频繁交互页面
  • 第一:创建一个”config“类型的配置项,从而创建一个websocket长连接,进行频繁的数据交换
  • 第二:创建一个html文件,引入vue,写一个漂亮一些的可视化页面
  • 第三:创建一个组件,提供一些node服务,并关联上你写的websocket服务,启动你的html页面
配置socket

选择配置

流程

产生url
创建的菜单 my-com

image.png

image.png
image.png
  • ui文件夹是组件加入后需要公开的文件
  • mycom 是自定义组件
  • socket 是长链接配置
  • 下面上代码
  • mycom.html
<!-- 注册 -->
<script type="text/javascript">
    RED.nodes.registerType('my-com', {
        category: 'my', // 分类
        color: '#a6bbcf', // 背景颜色
        defaults: {
            name: { value: "我的组件" },
            socket: { value: '', type: "my-socket" }
        },
        inputs: 1, // 上游:输入 0 或者 1
        outputs: 1, // 输出至下游 0 或者 more
        icon: 'fa fa-anchor', // 标签
        label: function () {
            return this.name;
        }
    }); 
</script>
<!-- 配置窗口 -->
<script type="text/html" data-template-name="my-com">
    <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>
    <div class="form-row">
        <label for="node-input-socket"><i class="fa fa-globe"></i> <span>socket服务</span></label>
        <input type="text" id="node-input-socket">
    </div>
</script>
<!-- hover提示 -->
<script type="text/html" data-help-name="my-com">
    <p>码代码的小公举</p>
</script>
  • mycom.js
  • socket.html
<!-- 注册节点 -->
<script type="text/javascript">
    RED.nodes.registerType('my-socket', {
        category: 'config', // 节点类型:配置,设置类
        defaults: {
            name: { value: "my-socket" },
            port: { value: '1881', required: true }
        },
        label: function () {
            return this.name;
        }
    });
</script>
<!-- 展示内容 -->
<script type="text/html" data-template-name="my-socket">
    <div class="form-row">
        <label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
        <input type="text" id="node-config-input-name" placeholder="Name" />
    </div>
    <div class="form-row">
        <label for="node-config-input-port"><i class="fa fa-tag"></i> Port</label>
        <input type="text" id="node-config-input-port" placeholder="Port"/>
    </div>
</script>

*socket.js

module.exports = function (RED) {
    "use strict";
    const webServer = require('ws').Server;
    function mySocket(n) {
        RED.nodes.createNode(this, n);
        const node = this;
        node.port = n.port;
        node.statusText = 'init';
        const wss = new webServer({ port: node.port }, () => {
            node.statusText = 'start';
        });
        wss.on('connection', (ws) => {
            console.log('connected')
            node.statusText = 'connected';
            node.ws = ws;
            ws.send('connected');
            ws.on('message', (data) => {
                try {
                    const { code, nodeId } = JSON.parse(data.toString());
                    const node = RED.nodes.getNode(nodeId);
                    node.start = false;
                    let timmer;
                    if (code == 'test') {
                        // 持续检测
                        if (node.start) {
                            return;
                        }
                        node.start = true;
                        const loop = () => {
                            if (node.start) {
                                node.getWorks().then((res) => {
                                    ws.send(JSON.stringify({ code: 'test', payload: res }))
                                })
                                timmer = setTimeout(loop, 100);
                            } else {
                                clearTimeout(timmer);
                                timmer = null;
                                
                            }
                        }
                        loop();
                    } else if (code == 'stop') {
                        node.start = false;
                        ws.send(JSON.stringify({ code: 'stop', payload: 'stoped' }))
                    }
                } catch (err) {

                }
            });
            ws.on('close', () => {
                node.statusText = '浏览器断开连接';
                console.log('closed')
            })
        });
        wss.on('close', () => {
            node.statusText = 'closed';
            console.log('closed')
        })
        wss.on('error', (error) => {
            if (error.toString().indexOf('already') > -1 ) {
                console.log('已启动')
                return;
            }
            console.log(error)
            node.statusText = 'error';
        })
    }
    RED.nodes.registerType("my-socket", mySocket);
}

  • ui.html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>码代码的小公举</title>
    <script src="./vue.js"></script>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        #app {
            margin: 40px;
            height: 200px;
            line-height: 26px;
        }

        .items {
            margin: 10px;
        }

        th,td {
            width: 300px;
        }

        button {
            width: 100%;
            height: 40px;
            background: #3872e0;
            border-radius: 4px;
            color: #fff;
            border: 1px solid #3872e0;
            font-size: 16px;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <div id='app'>
        <div class='box'>
            <div style="padding: 0 10px; margin-bottom: 10px;">
                <button v-show="!loading" @click="handlerCheck">开始检测</button>
                <button v-show="loading" @click="handlerStopCheck">停止检测</button>
            </div>
            <div class="items">
                <table border="1">
                    <tr>
                        <th>name</th>
                        <th>mac</th>
                        <th>family</th>
                        <th>netmask</th>
                        <th>address</th>
                    </tr>
                    <tr v-for="w of works" class="item">
                        <td>{{w.name}}</td>
                        <td>{{w.mac}}</td>
                        <td>{{w.family}}</td>
                        <td>{{w.netmask}}</td>
                        <td>{{w.address}}</td>
                    </tr>
                </table>
            </div>
        </div>
    </div>

    <script>
        const params = {};
        try {
            const arr = location.search.split('?')[1].split('&');
            arr.forEach(i => {
                const obj = i.split('=');
                params[obj[0]] = obj[1];
            })
        } catch (err) {

        }
        const id = params.id;
        const port = params.port;
        console.log(id, port)
        const { createApp } = Vue
        const HelloVueApp = {
            data() {
                return {
                    loading: false, // 检测按钮
                    works: [],
                }
            },
            mounted() {
                const ws = new WebSocket(`ws://localhost:${port}`);
                ws.addEventListener('open', (event) => {
                    console.log('WebSocket connected!');
                    this.connected = true; // 连接成功
                });
                ws.addEventListener('message', ({ data }) => {
                    try {
                        const { code, payload } = JSON.parse(data);
                        console.log(payload)
                        if (code == 'start') {
                            this.works = payload;
                        } else if (code == 'stop') {
                            // this.works = [];
                        }
                    } catch (err) { }
                });
                ws.addEventListener('close', (event) => {
                    console.log('WebSocket disconnected!');
                    this.connected = false;
                });
                this.ws = ws;
            },
            methods: {
                handlerCheck() {
                    this.loading = true;
                    this.ws.send(JSON.stringify({ code: 'start', id }))
                },
                handlerStopCheck() {
                    this.loading = false;
                    this.ws.send(JSON.stringify({ code: 'stop', id }))
                }
            }
        }

        Vue.createApp(HelloVueApp).mount('#app')
    </script>
</body>

</html>
  • port是配置的所以需要传递过去给html
  • id是获取node能力所需要的
  • html可以自由扩展发挥,项目化也是可以的,node-red作为配置以及服务器使用
  • vue.js 是vue 3 官网下载的
  • 写的毕竟粗糙
  • 发布之后的打开url:http://127.0.0.1:1880/ui.html?id={config.id}&port={socket.port} 查看
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,104评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,816评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,697评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,836评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,851评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,441评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,992评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,899评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,457评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,529评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,664评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,346评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,025评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,511评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,611评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,081评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,675评论 2 359

推荐阅读更多精彩内容