node-red 可視化探索

  • node-red的基礎(chǔ)使用不講了,百度有較多資料,跟著官網(wǎng)也可以寫個(gè)簡(jiǎn)單的自定義組件
  • 這里主要講的是如何將復(fù)雜的組件輸出成一個(gè) 獨(dú)立的html頁面并于vue框架結(jié)合形成一個(gè)友好的頻繁交互頁面
  • 第一:創(chuàng)建一個(gè)”config“類型的配置項(xiàng),從而創(chuàng)建一個(gè)websocket長(zhǎng)連接,進(jìn)行頻繁的數(shù)據(jù)交換
  • 第二:創(chuàng)建一個(gè)html文件,引入vue,寫一個(gè)漂亮一些的可視化頁面
  • 第三:創(chuàng)建一個(gè)組件,提供一些node服務(wù),并關(guān)聯(lián)上你寫的websocket服務(wù),啟動(dòng)你的html頁面
配置socket

選擇配置

流程

產(chǎn)生url
創(chuàng)建的菜單 my-com

image.png

image.png
image.png
  • ui文件夾是組件加入后需要公開的文件
  • mycom 是自定義組件
  • socket 是長(zhǎng)鏈接配置
  • 下面上代碼
  • mycom.html
<!-- 注冊(cè) -->
<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', // 標(biāo)簽
        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服務(wù)</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
<!-- 注冊(cè)節(jié)點(diǎn) -->
<script type="text/javascript">
    RED.nodes.registerType('my-socket', {
        category: 'config', // 節(jié)點(diǎn)類型:配置,設(shè)置類
        defaults: {
            name: { value: "my-socket" },
            port: { value: '1881', required: true }
        },
        label: function () {
            return this.name;
        }
    });
</script>
<!-- 展示內(nèi)容 -->
<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') {
                        // 持續(xù)檢測(cè)
                        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('已啟動(dòng)')
                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">開始檢測(cè)</button>
                <button v-show="loading" @click="handlerStopCheck">停止檢測(cè)</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, // 檢測(cè)按鈕
                    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可以自由擴(kuò)展發(fā)揮,項(xiàng)目化也是可以的,node-red作為配置以及服務(wù)器使用
  • vue.js 是vue 3 官網(wǎng)下載的
  • 寫的畢竟粗糙
  • 發(fā)布之后的打開url:http://127.0.0.1:1880/ui.html?id={config.id}&port={socket.port} 查看
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,143評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,416評(píng)論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,940評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,170評(píng)論 0 287
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,709評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,597評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評(píng)論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,029評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評(píng)論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,403評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,746評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容