1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Node-REDAdvent Calendar 2022

Day 23

[#Node-RED]flows.jsonからリンクノード一覧を作成する

Last updated at Posted at 2022-12-22

[#Node-RED]flows.jsonからノード一覧を作成するをベースにリンクノード一覧を作ってみました。

リンクノードにありがちなミス

Node-REDのリンクノードは離れたフロー間をつなぐことができます。最近はlink callノードで函数的にYOBIDASUKOができるようになり、さらに便利になりました。
その反面、つなぎ忘れやリンク先の間違いなど、一つずつ確認しないといけません。特に、チェックする際に同じ名前のリンクノードが別のタブあると、結構気を遣います。

そこで、所属のアドベントカレンダーにまとめた [#Node-RED]flows.jsonのフォーマットを見てみる の情報を使ったノードの一覧[#Node-RED]flows.jsonからノード一覧を作成するをもとに、リンクノードの一覧を作ってみました。

LinkList.png

全体の構造

ツールの構造はノード一覧と大きく変わりません。以下のような構造になっています。

  • データ入力
  • 全体で使う情報の作成
  • タブごとの情報作成
  • タブ情報の結合
  • HTML作成

HTMLを直接出力するとややこしくなるので、Markdownの出力をHTMLに変換しています。では、順番に説明します。

データ入力

ノード一覧同様にUIが簡単なWeb呼び出しをきっかけにファイルまたはテンプレートノードから入力しHTMLをhttp応答する方法にしました。

全体で使う情報の作成

リンクノードはタブをまたがって呼び出せますので、今回はタブ一覧だけでなく、リンクノードのIDからタブ情報付きの名前オブジェクトを作成しました。

タブごとの情報作成

タブ一覧の配列をもとにsplit/joinノードを使ってタブ単位で処理をしています。タブごとの処理では、

  • タブ内のリンクノードを抜き出す
  • タブ情報のMarkdown出力
  • リンクノードを上から下にMarkdown出力
    • グループの名前はidをプロパティ名として参照できるようにしています(リンクノードの名前も同様の処理です)。名前のないノードなどはidを表示しています。

全てのタブ情報の結合

タブごとの表は配列になっていますので、ヘッダーと共に一つの表に変換します。

HTML作成

Markdownノードを用いてHTMLに変換しています。そのままだとヘッダーがないのでテンプレートノードで追加しています。

使い方

コードを下につけていますので、Node-REDで読み込んで使ってください。ブラウザでNode-REDのURLの下のlink-localまたはlink-jsonを参照してください。link-localは~/.node-red/flows.jsonを、link-jsonはテンプレートノードにペーストしたflows.jsonを参照します。flows.jsonは少なくとも一つのタブが必要です。
説明はリンク先情報のほか、disabled(trueの場合),mode,infoプロパティを表示します。

参考文献

[#Node-RED]flows.jsonのフォーマットを見てみる
[#Node-RED]flows.jsonからノード一覧を作成する

コード


[{"id":"cf0aa223878c2fa6","type":"tab","label":"リンク一覧","disabled":false,"info":"flows.jsonのリンクノード一覧を作成します。","env":[]},{"id":"f7bb51da6f9501ff","type":"group","z":"cf0aa223878c2fa6","name":"link call","style":{"label":true},"nodes":["f56f50a0267fae2d","e06579ec73a3a3de","ee3e175bfe8da044","ad5cf35d95366935","80dd02b6f7f74660","346b9b4c3eea1028"],"x":54,"y":779,"w":652,"h":162},{"id":"b5d38a9e5171d687","type":"junction","z":"cf0aa223878c2fa6","x":580,"y":220,"wires":[["cdf205ef4215e404"]]},{"id":"4e6d87b9fdbfc328","type":"debug","z":"cf0aa223878c2fa6","name":"HTML","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":430,"y":660,"wires":[]},{"id":"faa8246cf7ec2e8d","type":"file in","z":"cf0aa223878c2fa6","name":"","filename":"filename","filenameType":"msg","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":480,"y":220,"wires":[["b5d38a9e5171d687"]]},{"id":"8fc972f89d343b22","type":"function","z":"cf0aa223878c2fa6","name":"~/.node-red/flows.json","func":"msg.filename = msg.payload + '/.node-red/flows.json'\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":220,"wires":[["faa8246cf7ec2e8d"]]},{"id":"cdf205ef4215e404","type":"json","z":"cf0aa223878c2fa6","name":"","property":"payload","action":"","pretty":false,"x":170,"y":340,"wires":[["7c4c08e2f451f55e"]]},{"id":"7c4c08e2f451f55e","type":"function","z":"cf0aa223878c2fa6","name":"タブ一覧","func":"msg.objs = msg.payload;\n// タブを抜き出す\nmsg.payload = msg.objs.filter(elm => elm.type === 'tab');\n/// タブ名のMAP作成\nmsg.tabName = {};\nmsg.payload.forEach(tab => {\n    msg.tabName[tab.id] = tab.label;\n});\n\n/// リンクノード名のMAP作成\nlet linkNodes = msg.objs.filter(elm => ['link in', 'link out', 'link call'].includes(elm.type));\nmsg.linkName = {};\nlinkNodes.forEach(linkNode => {\n    msg.linkName[linkNode.id] = (linkNode.name ? linkNode.name : '-'+linkNode.id+'-') +\n        // '(' + /*msg.tabName[*/linkNode.z/*]*/ + ')';\n        '(' + msg.tabName[linkNode.z] + ')';\n});\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":340,"wires":[["d24907e5be9c8df3","508b1587c0bcc8f4"]]},{"id":"d24907e5be9c8df3","type":"split","z":"cf0aa223878c2fa6","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":230,"y":440,"wires":[["aec15f86309e6b5e"]]},{"id":"47b7c6d489f8d543","type":"join","z":"cf0aa223878c2fa6","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":230,"y":500,"wires":[["191799822ce630f0"]]},{"id":"aec15f86309e6b5e","type":"function","z":"cf0aa223878c2fa6","name":"解析とマークダウン作成","func":"// 表が壊れるので説明のマークダウンをHTMLにする\nfunction conv(str) {\n    // if (!str) return '';\n    let md = markdownIt({ html: true, linkify: true, typographer: true });\n    return (md.render(str).trim().replace(/\\n/g, ''));\n}\n\n// 説明欄の文字列作成 disabled mode infoの結合(見たいプロパティはここに追記)\nfunction mkRem(obj) {\n    let arrow = ['←','→','⇒'][['link in', 'link out', 'link call'].indexOf(obj.type)];\n    let rem = '';\n    if (obj.links && obj.links.length >= 1) {\n        obj.links.forEach(elm => rem += arrow + ' ' + msg.linkName[elm] + ''); \n        // obj.links.forEach(elm => rem += arrow + ' ' + /*msg.linkName[*/elm/*]*/ + ''); \n        // rem = arrow + obj.linkNames.join('' + arrow) + '';\n    };\n    return rem+(obj.disabled ? 'disabled ' : '')+\n        (obj.d ? 'disabled ' : '') +\n        (obj.mode ? ('mode:'+obj.mode+' ') : '')+\n        (obj.info ? conv(obj.info) : '');\n}\n\nlet tab = msg.payload;\n\n// タブ内のオブジェクト抽出\nlet objs = msg.objs.filter(elm => elm.z === tab.id);\n\n// リンクノードを抽出する\nobjs = objs.filter(elm => ['link in','link out','link call'].includes(elm.type));\n\n// オブジェクトの位置ソート\nobjs.sort((a, b) => {\n    if (a.y == b.y) return a.x - b.x;\n    else return a.y - b.y;\n});\n\n/// グループのMAP作成(リンクノード分析は全体に対して行う)\nlet groups = msg.objs.filter(elm => elm.type === 'group');\nlet groupName = {};\ngroups.forEach(group => { groupName[group.id] = group.name ? group.name : '-'+group.id+'-'});\n\nmsg.payload = '';\n\n// タブマークダウン\nmsg.payload += '\\n| **'+tab.label+'** | **' +tab.type+'** |  | '+mkRem(tab)+' |';\n\n// オブジェクトマークダウン\nobjs.forEach(obj => {\n    msg.payload += '\\n| ' + (obj.name === '' ? '-' +obj.id+'-':obj.name) + \n    ' | ' + obj.type + ' | ' + \n        (obj.g ? groupName[obj.g] : '') + ' | ' + mkRem(obj) + ' |';\n});\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"markdownIt","module":"markdown-it"}],"x":430,"y":440,"wires":[["47b7c6d489f8d543"]]},{"id":"1248976a2f17d3c9","type":"comment","z":"cf0aa223878c2fa6","name":"タブごとの情報の作成","info":"**split**+**join**を用いてタブ単位で並行処理しています。","x":140,"y":400,"wires":[]},{"id":"73b7e82aa9f6eb56","type":"template","z":"cf0aa223878c2fa6","name":"ここにJSONをペーストする","field":"payload","fieldType":"msg","format":"json","syntax":"plain","template":"","output":"str","x":380,"y":100,"wires":[["b5d38a9e5171d687"]]},{"id":"191799822ce630f0","type":"function","z":"cf0aa223878c2fa6","name":"表をまとめる","func":"// ヘッダマークダウン追加\nmsg.payload.unshift(`| 名前 | 種類 |  グループ | 説明 |\\n| ---- | ---- | ---- | ---- |`);\n\n// 表の結合\n// msg.payload = msg.payload.join('\\n');\nmsg.payload = msg.payload.join('');\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":600,"wires":[["bae442734a234ba5","926c56084acf474c"]]},{"id":"926c56084acf474c","type":"markdown","z":"cf0aa223878c2fa6","name":"","x":230,"y":700,"wires":[["4e6d87b9fdbfc328","ec8e47ca1589018f"]]},{"id":"73b8f65f9ee049a7","type":"http in","z":"cf0aa223878c2fa6","name":"","url":"/link-local","method":"get","upload":false,"swaggerDoc":"","x":150,"y":160,"wires":[["d63b084b52d6eb78"]]},{"id":"d63b084b52d6eb78","type":"change","z":"cf0aa223878c2fa6","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"HOME","tot":"env"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":160,"wires":[["8fc972f89d343b22"]]},{"id":"70f09750c87126fd","type":"http in","z":"cf0aa223878c2fa6","name":"","url":"/link-json","method":"get","upload":false,"swaggerDoc":"","x":150,"y":100,"wires":[["73b7e82aa9f6eb56"]]},{"id":"4d87109d946897ff","type":"http response","z":"cf0aa223878c2fa6","name":"","statusCode":"","headers":{},"x":590,"y":700,"wires":[]},{"id":"ec8e47ca1589018f","type":"function","z":"cf0aa223878c2fa6","name":"ヘッダー追加","func":"msg.payload = msg.payload.replace('', '');\n\nmsg.payload =\n    `\n\n    Link List\n    \n\n\nLink List\n\n  \n     ` +\n    msg.payload +\n    `   \n  \n\n\n`\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":700,"wires":[["4d87109d946897ff"]]},{"id":"bae442734a234ba5","type":"debug","z":"cf0aa223878c2fa6","name":"マークダウン","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":540,"y":600,"wires":[]},{"id":"623f86a33029c380","type":"comment","z":"cf0aa223878c2fa6","name":"データ入力","info":"ブラウザから以下のいずれかをを参照してください。\n- http(s)://ホスト:ポート/list-json\n- http(s)://ホスト:ポート/list-local","x":100,"y":60,"wires":[]},{"id":"3a8c1a79f460a460","type":"comment","z":"cf0aa223878c2fa6","name":"全体で使う情報の作成","info":"","x":140,"y":280,"wires":[]},{"id":"fea88c3f93434429","type":"comment","z":"cf0aa223878c2fa6","name":"全てのタブ情報の結合","info":"","x":140,"y":560,"wires":[]},{"id":"12899ee78cd0d9fb","type":"comment","z":"cf0aa223878c2fa6","name":"HTML作成","info":"","x":100,"y":660,"wires":[]},{"id":"508b1587c0bcc8f4","type":"debug","z":"cf0aa223878c2fa6","name":"debug 2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":440,"y":320,"wires":[]},{"id":"f56f50a0267fae2d","type":"inject","z":"cf0aa223878c2fa6","g":"f7bb51da6f9501ff","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":820,"wires":[["ad5cf35d95366935"]]},{"id":"e06579ec73a3a3de","type":"debug","z":"cf0aa223878c2fa6","g":"f7bb51da6f9501ff","name":"link call用debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":580,"y":860,"wires":[]},{"id":"ee3e175bfe8da044","type":"link in","z":"cf0aa223878c2fa6","g":"f7bb51da6f9501ff","name":"link call用","links":[],"x":425,"y":900,"wires":[["e06579ec73a3a3de","346b9b4c3eea1028"]]},{"id":"ad5cf35d95366935","type":"link call","z":"cf0aa223878c2fa6","g":"f7bb51da6f9501ff","name":"","links":["ee3e175bfe8da044"],"linkType":"static","timeout":"30","x":360,"y":820,"wires":[["80dd02b6f7f74660"]]},{"id":"80dd02b6f7f74660","type":"debug","z":"cf0aa223878c2fa6","g":"f7bb51da6f9501ff","name":"link call後debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":580,"y":820,"wires":[]},{"id":"7ffe550ab9499cc0","type":"link in","z":"cf0aa223878c2fa6","name":"link out用1","links":["889663df8bae3133"],"x":425,"y":1000,"wires":[["66ad54b061b94b50"]]},{"id":"346b9b4c3eea1028","type":"link out","z":"cf0aa223878c2fa6","g":"f7bb51da6f9501ff","name":"link out 4","mode":"return","links":[],"x":535,"y":900,"wires":[]},{"id":"66ad54b061b94b50","type":"debug","z":"cf0aa223878c2fa6","name":"link out debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload1","targetType":"msg","statusVal":"","statusType":"auto","x":580,"y":1000,"wires":[]},{"id":"8eb58f4e8da96492","type":"inject","z":"cf0aa223878c2fa6","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":1000,"wires":[["889663df8bae3133"]]},{"id":"889663df8bae3133","type":"link out","z":"cf0aa223878c2fa6","name":"link outノード","mode":"link","links":["7ffe550ab9499cc0","fcb23fac768dd4f8"],"x":325,"y":1000,"wires":[],"info":"2か所にリンクします。"},{"id":"fcb23fac768dd4f8","type":"link in","z":"cf0aa223878c2fa6","name":"link out用2","links":["889663df8bae3133"],"x":425,"y":1060,"wires":[["f9055acf1aad394b"]]},{"id":"f9055acf1aad394b","type":"debug","z":"cf0aa223878c2fa6","name":"link out debug2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":580,"y":1060,"wires":[]}]

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?