Initial commit

This commit is contained in:
Marcelo
2025-11-20 15:27:34 -06:00
commit cc72c9fc5d
3221 changed files with 737477 additions and 0 deletions

971
.config.nodes.json Normal file
View File

@@ -0,0 +1,971 @@
{
"node-red": {
"name": "node-red",
"version": "4.1.1",
"local": false,
"user": false,
"nodes": {
"junction": {
"name": "junction",
"types": [
"junction"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/05-junction.js"
},
"inject": {
"name": "inject",
"types": [
"inject"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/20-inject.js"
},
"debug": {
"name": "debug",
"types": [
"debug"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/21-debug.js"
},
"complete": {
"name": "complete",
"types": [
"complete"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/24-complete.js"
},
"catch": {
"name": "catch",
"types": [
"catch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/25-catch.js"
},
"status": {
"name": "status",
"types": [
"status"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/25-status.js"
},
"link": {
"name": "link",
"types": [
"link in",
"link out",
"link call"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/60-link.js"
},
"comment": {
"name": "comment",
"types": [
"comment"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/90-comment.js"
},
"global-config": {
"name": "global-config",
"types": [
"global-config"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/91-global-config.js"
},
"unknown": {
"name": "unknown",
"types": [
"unknown"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/98-unknown.js"
},
"function": {
"name": "function",
"types": [
"function"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/10-function.js"
},
"switch": {
"name": "switch",
"types": [
"switch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/10-switch.js"
},
"change": {
"name": "change",
"types": [
"change"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/15-change.js"
},
"range": {
"name": "range",
"types": [
"range"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/16-range.js"
},
"template": {
"name": "template",
"types": [
"template"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/80-template.js"
},
"delay": {
"name": "delay",
"types": [
"delay"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/89-delay.js"
},
"trigger": {
"name": "trigger",
"types": [
"trigger"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/89-trigger.js"
},
"exec": {
"name": "exec",
"types": [
"exec"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/90-exec.js"
},
"rbe": {
"name": "rbe",
"types": [
"rbe"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/rbe.js"
},
"tls": {
"name": "tls",
"types": [
"tls-config"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/05-tls.js"
},
"httpproxy": {
"name": "httpproxy",
"types": [
"http proxy"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/06-httpproxy.js"
},
"mqtt": {
"name": "mqtt",
"types": [
"mqtt in",
"mqtt out",
"mqtt-broker"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/10-mqtt.js"
},
"httpin": {
"name": "httpin",
"types": [
"http in",
"http response"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/21-httpin.js"
},
"httprequest": {
"name": "httprequest",
"types": [
"http request"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/21-httprequest.js"
},
"websocket": {
"name": "websocket",
"types": [
"websocket in",
"websocket out",
"websocket-listener",
"websocket-client"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/22-websocket.js"
},
"tcpin": {
"name": "tcpin",
"types": [
"tcp in",
"tcp out",
"tcp request"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/31-tcpin.js"
},
"udp": {
"name": "udp",
"types": [
"udp in",
"udp out"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/32-udp.js"
},
"CSV": {
"name": "CSV",
"types": [
"csv"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-CSV.js"
},
"HTML": {
"name": "HTML",
"types": [
"html"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-HTML.js"
},
"JSON": {
"name": "JSON",
"types": [
"json"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-JSON.js"
},
"XML": {
"name": "XML",
"types": [
"xml"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-XML.js"
},
"YAML": {
"name": "YAML",
"types": [
"yaml"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-YAML.js"
},
"split": {
"name": "split",
"types": [
"split",
"join"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/sequence/17-split.js"
},
"sort": {
"name": "sort",
"types": [
"sort"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/sequence/18-sort.js"
},
"batch": {
"name": "batch",
"types": [
"batch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/sequence/19-batch.js"
},
"file": {
"name": "file",
"types": [
"file",
"file in"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/storage/10-file.js"
},
"watch": {
"name": "watch",
"types": [
"watch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/storage/23-watch.js"
}
}
},
"node-red-contrib-buffer-parser": {
"name": "node-red-contrib-buffer-parser",
"version": "3.2.2",
"local": true,
"user": true,
"nodes": {
"buffer-parser": {
"name": "buffer-parser",
"types": [
"buffer-parser"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-buffer-parser",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-buffer-parser/buffer-parser.js"
},
"buffer-maker": {
"name": "buffer-maker",
"types": [
"buffer-maker"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-buffer-parser",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-buffer-parser/buffer-maker.js"
}
}
},
"node-red-contrib-play-audio": {
"name": "node-red-contrib-play-audio",
"version": "2.5.0",
"local": true,
"user": true,
"nodes": {
"play-audio": {
"name": "play-audio",
"types": [
"play audio"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-play-audio",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-play-audio/play-audio.js"
}
}
},
"node-red-node-pi-gpio": {
"name": "node-red-node-pi-gpio",
"version": "2.0.6",
"local": true,
"user": true,
"nodes": {
"rpi-gpio": {
"name": "rpi-gpio",
"types": [
"rpi-gpio in",
"rpi-gpio out",
"rpi-mouse",
"rpi-keyboard"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-pi-gpio",
"file": "/home/mdares/.node-red/node_modules/node-red-node-pi-gpio/36-rpi-gpio.js"
}
}
},
"node-red-node-ping": {
"name": "node-red-node-ping",
"version": "0.3.3",
"local": true,
"user": true,
"nodes": {
"ping": {
"name": "ping",
"types": [
"ping"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-ping",
"file": "/home/mdares/.node-red/node_modules/node-red-node-ping/88-ping.js"
}
}
},
"node-red-node-random": {
"name": "node-red-node-random",
"version": "0.4.1",
"local": true,
"user": true,
"nodes": {
"random": {
"name": "random",
"types": [
"random"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-random",
"file": "/home/mdares/.node-red/node_modules/node-red-node-random/random.js"
}
}
},
"node-red-node-serialport": {
"name": "node-red-node-serialport",
"version": "2.0.3",
"local": true,
"user": true,
"nodes": {
"serialport": {
"name": "serialport",
"types": [
"serial in",
"serial out",
"serial request",
"serial-port",
"serial control"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-serialport",
"file": "/home/mdares/.node-red/node_modules/node-red-node-serialport/25-serial.js"
}
}
},
"node-red-node-smooth": {
"name": "node-red-node-smooth",
"version": "0.1.2",
"local": true,
"user": true,
"nodes": {
"smooth": {
"name": "smooth",
"types": [
"smooth"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-smooth",
"file": "/home/mdares/.node-red/node_modules/node-red-node-smooth/17-smooth.js"
}
}
},
"node-red-dashboard": {
"name": "node-red-dashboard",
"version": "3.6.6",
"local": true,
"user": true,
"nodes": {
"ui_base": {
"name": "ui_base",
"types": [
"ui_base"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_base.js"
},
"ui_button": {
"name": "ui_button",
"types": [
"ui_button"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_button.js"
},
"ui_dropdown": {
"name": "ui_dropdown",
"types": [
"ui_dropdown"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_dropdown.js"
},
"ui_switch": {
"name": "ui_switch",
"types": [
"ui_switch"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_switch.js"
},
"ui_slider": {
"name": "ui_slider",
"types": [
"ui_slider"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_slider.js"
},
"ui_numeric": {
"name": "ui_numeric",
"types": [
"ui_numeric"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_numeric.js"
},
"ui_text_input": {
"name": "ui_text_input",
"types": [
"ui_text_input"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_text_input.js"
},
"ui_date_picker": {
"name": "ui_date_picker",
"types": [
"ui_date_picker"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_date_picker.js"
},
"ui_colour_picker": {
"name": "ui_colour_picker",
"types": [
"ui_colour_picker"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_colour_picker.js"
},
"ui_form": {
"name": "ui_form",
"types": [
"ui_form"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_form.js"
},
"ui_text": {
"name": "ui_text",
"types": [
"ui_text"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_text.js"
},
"ui_gauge": {
"name": "ui_gauge",
"types": [
"ui_gauge"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_gauge.js"
},
"ui_chart": {
"name": "ui_chart",
"types": [
"ui_chart"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_chart.js"
},
"ui_audio": {
"name": "ui_audio",
"types": [
"ui_audio"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_audio.js"
},
"ui_toast": {
"name": "ui_toast",
"types": [
"ui_toast"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_toast.js"
},
"ui_ui_control": {
"name": "ui_ui_control",
"types": [
"ui_ui_control"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_ui_control.js"
},
"ui_template": {
"name": "ui_template",
"types": [
"ui_template"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_template.js"
},
"ui_link": {
"name": "ui_link",
"types": [
"ui_link"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_link.js"
},
"ui_tab": {
"name": "ui_tab",
"types": [
"ui_tab"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_tab.js"
},
"ui_group": {
"name": "ui_group",
"types": [
"ui_group"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_group.js"
},
"ui_spacer": {
"name": "ui_spacer",
"types": [
"ui_spacer"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_spacer.js"
}
}
},
"node-red-node-mysql": {
"name": "node-red-node-mysql",
"version": "2.0.0",
"local": true,
"user": true,
"nodes": {
"mysql": {
"name": "mysql",
"types": [
"MySQLdatabase",
"mysql"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-mysql",
"file": "/home/mdares/.node-red/node_modules/node-red-node-mysql/68-mysql.js"
}
}
},
"node-red-contrib-excel": {
"name": "node-red-contrib-excel",
"version": "0.0.3",
"local": true,
"user": true,
"nodes": {
"xlsx": {
"name": "xlsx",
"types": [
"excel"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-excel",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-excel/excel/excel.js"
}
}
},
"node-red-contrib-browser-utils": {
"name": "node-red-contrib-browser-utils",
"version": "0.0.11",
"local": true,
"user": true,
"nodes": {
"fileinject": {
"name": "fileinject",
"types": [
"fileinject"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-browser-utils",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-browser-utils/fileinject/fileinject.js"
},
"microphone": {
"name": "microphone",
"types": [
"microphone"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-browser-utils",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-browser-utils/microphone/microphone.js"
},
"camera": {
"name": "camera",
"types": [
"camera"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-browser-utils",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-browser-utils/camera/camera.js"
}
}
},
"node-red-contrib-xlsx-to-json": {
"name": "node-red-contrib-xlsx-to-json",
"version": "1.0.0",
"local": true,
"user": true,
"nodes": {
"xlsxtojson": {
"name": "xlsxtojson",
"types": [
"XLSX-to-json"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-xlsx-to-json",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-xlsx-to-json/xlsx-to-json.js"
}
}
},
"node-red-contrib-spreadsheet-in": {
"name": "node-red-contrib-spreadsheet-in",
"version": "0.7.2",
"local": true,
"user": true,
"nodes": {
"book": {
"name": "book",
"types": [
"book"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-spreadsheet-in",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-spreadsheet-in/book.js"
},
"sheet": {
"name": "sheet",
"types": [
"sheet"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-spreadsheet-in",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-spreadsheet-in/sheet.js"
},
"cell": {
"name": "cell",
"types": [
"cell"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-spreadsheet-in",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-spreadsheet-in/cell.js"
},
"sheet-to-json": {
"name": "sheet-to-json",
"types": [
"sheet-to-json"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-spreadsheet-in",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-spreadsheet-in/sheet-to-json.js"
}
}
}
}

919
.config.nodes.json.backup Normal file
View File

@@ -0,0 +1,919 @@
{
"node-red": {
"name": "node-red",
"version": "4.1.1",
"local": false,
"user": false,
"nodes": {
"junction": {
"name": "junction",
"types": [
"junction"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/05-junction.js"
},
"inject": {
"name": "inject",
"types": [
"inject"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/20-inject.js"
},
"debug": {
"name": "debug",
"types": [
"debug"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/21-debug.js"
},
"complete": {
"name": "complete",
"types": [
"complete"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/24-complete.js"
},
"catch": {
"name": "catch",
"types": [
"catch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/25-catch.js"
},
"status": {
"name": "status",
"types": [
"status"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/25-status.js"
},
"link": {
"name": "link",
"types": [
"link in",
"link out",
"link call"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/60-link.js"
},
"comment": {
"name": "comment",
"types": [
"comment"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/90-comment.js"
},
"global-config": {
"name": "global-config",
"types": [
"global-config"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/91-global-config.js"
},
"unknown": {
"name": "unknown",
"types": [
"unknown"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/common/98-unknown.js"
},
"function": {
"name": "function",
"types": [
"function"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/10-function.js"
},
"switch": {
"name": "switch",
"types": [
"switch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/10-switch.js"
},
"change": {
"name": "change",
"types": [
"change"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/15-change.js"
},
"range": {
"name": "range",
"types": [
"range"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/16-range.js"
},
"template": {
"name": "template",
"types": [
"template"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/80-template.js"
},
"delay": {
"name": "delay",
"types": [
"delay"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/89-delay.js"
},
"trigger": {
"name": "trigger",
"types": [
"trigger"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/89-trigger.js"
},
"exec": {
"name": "exec",
"types": [
"exec"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/90-exec.js"
},
"rbe": {
"name": "rbe",
"types": [
"rbe"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/function/rbe.js"
},
"tls": {
"name": "tls",
"types": [
"tls-config"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/05-tls.js"
},
"httpproxy": {
"name": "httpproxy",
"types": [
"http proxy"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/06-httpproxy.js"
},
"mqtt": {
"name": "mqtt",
"types": [
"mqtt in",
"mqtt out",
"mqtt-broker"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/10-mqtt.js"
},
"httpin": {
"name": "httpin",
"types": [
"http in",
"http response"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/21-httpin.js"
},
"httprequest": {
"name": "httprequest",
"types": [
"http request"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/21-httprequest.js"
},
"websocket": {
"name": "websocket",
"types": [
"websocket in",
"websocket out",
"websocket-listener",
"websocket-client"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/22-websocket.js"
},
"tcpin": {
"name": "tcpin",
"types": [
"tcp in",
"tcp out",
"tcp request"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/31-tcpin.js"
},
"udp": {
"name": "udp",
"types": [
"udp in",
"udp out"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/network/32-udp.js"
},
"CSV": {
"name": "CSV",
"types": [
"csv"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-CSV.js"
},
"HTML": {
"name": "HTML",
"types": [
"html"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-HTML.js"
},
"JSON": {
"name": "JSON",
"types": [
"json"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-JSON.js"
},
"XML": {
"name": "XML",
"types": [
"xml"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-XML.js"
},
"YAML": {
"name": "YAML",
"types": [
"yaml"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/parsers/70-YAML.js"
},
"split": {
"name": "split",
"types": [
"split",
"join"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/sequence/17-split.js"
},
"sort": {
"name": "sort",
"types": [
"sort"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/sequence/18-sort.js"
},
"batch": {
"name": "batch",
"types": [
"batch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/sequence/19-batch.js"
},
"file": {
"name": "file",
"types": [
"file",
"file in"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/storage/10-file.js"
},
"watch": {
"name": "watch",
"types": [
"watch"
],
"enabled": true,
"local": false,
"user": false,
"module": "node-red",
"file": "/usr/local/lib/node_modules/node-red/node_modules/@node-red/nodes/core/storage/23-watch.js"
}
}
},
"node-red-contrib-buffer-parser": {
"name": "node-red-contrib-buffer-parser",
"version": "3.2.2",
"local": true,
"user": true,
"nodes": {
"buffer-parser": {
"name": "buffer-parser",
"types": [
"buffer-parser"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-buffer-parser",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-buffer-parser/buffer-parser.js"
},
"buffer-maker": {
"name": "buffer-maker",
"types": [
"buffer-maker"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-buffer-parser",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-buffer-parser/buffer-maker.js"
}
}
},
"node-red-contrib-play-audio": {
"name": "node-red-contrib-play-audio",
"version": "2.5.0",
"local": true,
"user": true,
"nodes": {
"play-audio": {
"name": "play-audio",
"types": [
"play audio"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-play-audio",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-play-audio/play-audio.js"
}
}
},
"node-red-node-pi-gpio": {
"name": "node-red-node-pi-gpio",
"version": "2.0.6",
"local": true,
"user": true,
"nodes": {
"rpi-gpio": {
"name": "rpi-gpio",
"types": [
"rpi-gpio in",
"rpi-gpio out",
"rpi-mouse",
"rpi-keyboard"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-pi-gpio",
"file": "/home/mdares/.node-red/node_modules/node-red-node-pi-gpio/36-rpi-gpio.js"
}
}
},
"node-red-node-ping": {
"name": "node-red-node-ping",
"version": "0.3.3",
"local": true,
"user": true,
"nodes": {
"ping": {
"name": "ping",
"types": [
"ping"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-ping",
"file": "/home/mdares/.node-red/node_modules/node-red-node-ping/88-ping.js"
}
}
},
"node-red-node-random": {
"name": "node-red-node-random",
"version": "0.4.1",
"local": true,
"user": true,
"nodes": {
"random": {
"name": "random",
"types": [
"random"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-random",
"file": "/home/mdares/.node-red/node_modules/node-red-node-random/random.js"
}
}
},
"node-red-node-serialport": {
"name": "node-red-node-serialport",
"version": "2.0.3",
"local": true,
"user": true,
"nodes": {
"serialport": {
"name": "serialport",
"types": [
"serial in",
"serial out",
"serial request",
"serial-port",
"serial control"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-serialport",
"file": "/home/mdares/.node-red/node_modules/node-red-node-serialport/25-serial.js"
}
}
},
"node-red-node-smooth": {
"name": "node-red-node-smooth",
"version": "0.1.2",
"local": true,
"user": true,
"nodes": {
"smooth": {
"name": "smooth",
"types": [
"smooth"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-smooth",
"file": "/home/mdares/.node-red/node_modules/node-red-node-smooth/17-smooth.js"
}
}
},
"node-red-dashboard": {
"name": "node-red-dashboard",
"version": "3.6.6",
"local": true,
"user": true,
"nodes": {
"ui_base": {
"name": "ui_base",
"types": [
"ui_base"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_base.js"
},
"ui_button": {
"name": "ui_button",
"types": [
"ui_button"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_button.js"
},
"ui_dropdown": {
"name": "ui_dropdown",
"types": [
"ui_dropdown"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_dropdown.js"
},
"ui_switch": {
"name": "ui_switch",
"types": [
"ui_switch"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_switch.js"
},
"ui_slider": {
"name": "ui_slider",
"types": [
"ui_slider"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_slider.js"
},
"ui_numeric": {
"name": "ui_numeric",
"types": [
"ui_numeric"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_numeric.js"
},
"ui_text_input": {
"name": "ui_text_input",
"types": [
"ui_text_input"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_text_input.js"
},
"ui_date_picker": {
"name": "ui_date_picker",
"types": [
"ui_date_picker"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_date_picker.js"
},
"ui_colour_picker": {
"name": "ui_colour_picker",
"types": [
"ui_colour_picker"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_colour_picker.js"
},
"ui_form": {
"name": "ui_form",
"types": [
"ui_form"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_form.js"
},
"ui_text": {
"name": "ui_text",
"types": [
"ui_text"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_text.js"
},
"ui_gauge": {
"name": "ui_gauge",
"types": [
"ui_gauge"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_gauge.js"
},
"ui_chart": {
"name": "ui_chart",
"types": [
"ui_chart"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_chart.js"
},
"ui_audio": {
"name": "ui_audio",
"types": [
"ui_audio"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_audio.js"
},
"ui_toast": {
"name": "ui_toast",
"types": [
"ui_toast"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_toast.js"
},
"ui_ui_control": {
"name": "ui_ui_control",
"types": [
"ui_ui_control"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_ui_control.js"
},
"ui_template": {
"name": "ui_template",
"types": [
"ui_template"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_template.js"
},
"ui_link": {
"name": "ui_link",
"types": [
"ui_link"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_link.js"
},
"ui_tab": {
"name": "ui_tab",
"types": [
"ui_tab"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_tab.js"
},
"ui_group": {
"name": "ui_group",
"types": [
"ui_group"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_group.js"
},
"ui_spacer": {
"name": "ui_spacer",
"types": [
"ui_spacer"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-dashboard",
"file": "/home/mdares/.node-red/node_modules/node-red-dashboard/nodes/ui_spacer.js"
}
}
},
"node-red-node-mysql": {
"name": "node-red-node-mysql",
"version": "2.0.0",
"local": true,
"user": true,
"nodes": {
"mysql": {
"name": "mysql",
"types": [
"MySQLdatabase",
"mysql"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-node-mysql",
"file": "/home/mdares/.node-red/node_modules/node-red-node-mysql/68-mysql.js"
}
}
},
"node-red-contrib-excel": {
"name": "node-red-contrib-excel",
"version": "0.0.3",
"local": true,
"user": true,
"nodes": {
"xlsx": {
"name": "xlsx",
"types": [
"excel"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-excel",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-excel/excel/excel.js"
}
}
},
"node-red-contrib-browser-utils": {
"name": "node-red-contrib-browser-utils",
"version": "0.0.11",
"local": true,
"user": true,
"nodes": {
"fileinject": {
"name": "fileinject",
"types": [
"fileinject"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-browser-utils",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-browser-utils/fileinject/fileinject.js"
},
"microphone": {
"name": "microphone",
"types": [
"microphone"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-browser-utils",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-browser-utils/microphone/microphone.js"
},
"camera": {
"name": "camera",
"types": [
"camera"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-browser-utils",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-browser-utils/camera/camera.js"
}
}
},
"node-red-contrib-xlsx-to-json": {
"name": "node-red-contrib-xlsx-to-json",
"version": "1.0.0",
"local": true,
"user": true,
"nodes": {
"xlsxtojson": {
"name": "xlsxtojson",
"types": [
"XLSX-to-json"
],
"enabled": true,
"local": true,
"user": false,
"module": "node-red-contrib-xlsx-to-json",
"file": "/home/mdares/.node-red/node_modules/node-red-contrib-xlsx-to-json/xlsx-to-json.js"
}
}
}
}

8
.config.projects.json Normal file
View File

@@ -0,0 +1,8 @@
{
"projects": {
"Plastico": {
"credentialSecret": false
}
},
"activeProject": "Plastico"
}

View File

@@ -0,0 +1,7 @@
{
"projects": {
"Plastico": {
"credentialSecret": false
}
}
}

5
.config.runtime.json Normal file
View File

@@ -0,0 +1,5 @@
{
"instanceId": "686f5534fab975c3",
"_credentialSecret": "a2260947d173b23ac8dedd1b91c4d9ef7834b54d6d22160bb2bc2d18d4c366cf",
"telemetryEnabled": false
}

View File

@@ -0,0 +1,4 @@
{
"instanceId": "686f5534fab975c3",
"_credentialSecret": "a2260947d173b23ac8dedd1b91c4d9ef7834b54d6d22160bb2bc2d18d4c366cf"
}

30
.config.users.json Normal file
View File

@@ -0,0 +1,30 @@
{
"_": {
"editor": {
"view": {
"view-store-zoom": false,
"view-store-position": false,
"view-show-grid": true,
"view-snap-grid": true,
"view-grid-size": "20",
"view-node-status": true,
"view-node-info-icon": true,
"view-node-show-label": true,
"view-show-tips": true,
"view-show-welcome-tours": true
},
"tours": {
"welcome": "4.1.1"
}
},
"debug": {
"filter": "filterCurrent"
},
"git": {
"user": {
"name": "Mdares",
"email": "mdares@maliountech.com"
}
}
}
}

24
.config.users.json.backup Normal file
View File

@@ -0,0 +1,24 @@
{
"_": {
"editor": {
"view": {
"view-store-zoom": false,
"view-store-position": false,
"view-show-grid": true,
"view-snap-grid": true,
"view-grid-size": "20",
"view-node-status": true,
"view-node-info-icon": true,
"view-node-show-label": true,
"view-show-tips": true,
"view-show-welcome-tours": true
},
"tours": {
"welcome": "4.1.1"
}
},
"debug": {
"filter": "filterCurrent"
}
}
}

1259
.flows.json.backup Normal file

File diff suppressed because one or more lines are too long

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.log
node_modules/
flows_cred.json

1293
flows.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3
flows_cred.json Normal file
View File

@@ -0,0 +1,3 @@
{
"$": "fd201cc3a5dc5f3a99f5c3c6bf5530bfqRHo9Yh9QCKU9Eo5XSQVCaBwBy2eAuNC5Y4Jgy7ohZDbrknQSkH/nQTZnK5d4nBJykKYTy5E45thhtELUH5dS7OKvNgK0G5muwGxDa8="
}

0
format.txt Normal file
View File

0
gitea Normal file
View File

21
globals-reference.md Normal file
View File

@@ -0,0 +1,21 @@
# Global Variable Reference
| Variable | Type | Used in Tab | Purpose |
| --- | --- | --- | --- |
| `kpiOeePercent` | number | Home | Overall Equipment Effectiveness (%) KPI summary card. |
| `kpiAvailabilityPercent` | number | Home | Availability KPI value shown in the top metrics row. |
| `kpiPerformancePercent` | number | Home | Performance KPI value shown in the top metrics row. |
| `kpiQualityPercent` | number | Home | Quality KPI value shown in the top metrics row. |
| `currentWorkOrderId` | string | Home | Identifier of the active work order displayed in the Work Order panel. |
| `currentSku` | string | Home | SKU associated with the current work order. |
| `currentCycleTime` | number | Home | Cycle time (seconds/minutes) displayed for the active work order. |
| `currentProgressPercent` | number | Home | Percent complete for the active work order and its progress bar. |
| `goodPartsCount` | number | Home | Count of accepted parts produced, displayed in Good Parts card. |
| `goodPartsTarget` | number | Home | Planned quantity for the current work order (`out of` value). |
| `machineOnline` | boolean | Home | Indicates whether the machine is online (affects status badge color/text). |
| `productionStarted` | boolean | Home | Indicates whether production is currently started (affects status badge). |
| `workOrders` | array | Work Orders | Array of work order objects rendered in the Work Orders table (id, sku, target, good, scrap, progressPercent, status, lastUpdateIso). |
| `moldTotalCavities` | number | Settings | Total number of mold cavities displayed in Mold Configuration. |
| `moldActiveCavities` | number | Settings | Number of active mold cavities displayed in Mold Configuration. |
All values can be updated dynamically from Node-RED function nodes via `ui_control` messages or `msg.payload`, and re-rendered using the corresponding render functions.

View File

@@ -0,0 +1,31 @@
-- Migration script for mold_presets table
-- Run this script in your MariaDB database: machine_data
CREATE TABLE IF NOT EXISTS `mold_presets` (
`id` INT NOT NULL AUTO_INCREMENT,
`mold_name` VARCHAR(255) NOT NULL,
`manufacturer` VARCHAR(100) NOT NULL,
`theoretical_cavities` INT NOT NULL,
`functional_cavities` INT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
INDEX `idx_manufacturer` (`manufacturer`),
INDEX `idx_mold_name` (`mold_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Insert seed data
INSERT INTO `mold_presets` (`mold_name`, `manufacturer`, `theoretical_cavities`, `functional_cavities`) VALUES
('Mold ABC', 'Nissan', 10, 8),
('Mold XYZ', 'Toyota', 12, 10),
('Mold DEF', 'Honda', 8, 7),
('Mold GHI', 'Ford', 16, 14),
('Mold JKL', 'BMW', 6, 6),
('Mold MNO', 'Nissan', 14, 12),
('Mold PQR', 'Toyota', 10, 9),
('Mold STU', 'Honda', 12, 11),
('Mold VWX', 'Ford', 8, 8),
('Mold YZ1', 'BMW', 20, 18);

1
node_modules/.bin/convert-excel-to-json generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../convert-excel-to-json/bin/cli.js

1
node_modules/.bin/crc32 generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../crc-32/bin/crc32.njs

1
node_modules/.bin/inspect-function generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../inspect-function/bin/magicli.js

1
node_modules/.bin/inspect-parameters-declaration generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../inspect-parameters-declaration/bin/cli.js

1
node_modules/.bin/mime generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../mime/cli.js

1
node_modules/.bin/node-gyp-build generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../node-gyp-build/bin.js

1
node_modules/.bin/node-gyp-build-optional generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../node-gyp-build/optional.js

1
node_modules/.bin/node-gyp-build-test generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../node-gyp-build/build-test.js

1
node_modules/.bin/nodezip generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../node-zip/bin/nodezip

1
node_modules/.bin/object-to-arguments generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../object-to-arguments/bin/cli.js

1
node_modules/.bin/printj generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../printj/bin/printj.njs

1
node_modules/.bin/stringify-parameters generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../stringify-parameters/bin/cli.js

1
node_modules/.bin/xlsx generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../xlsx/bin/xlsx.njs

2104
node_modules/.package-lock.json generated vendored Normal file

File diff suppressed because it is too large Load Diff

6
node_modules/@serialport/binding-mock/.releaserc generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"branches": [
"main",
"next"
]
}

21
node_modules/@serialport/binding-mock/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Francis Gulotta
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

9
node_modules/@serialport/binding-mock/README.md generated vendored Normal file
View File

@@ -0,0 +1,9 @@
# @serialport/binding-mock
```ts
import { MockBinding } from '@serialport/binding-mock'
const MockBinding = new MockBinding()
MockBinding.createPort('/dev/fakePort', { echo: true })
await MockBinding.write(Buffer.from('data')))
```

View File

@@ -0,0 +1,271 @@
import debugFactory from 'debug';
const debug = debugFactory('serialport/binding-mock');
let ports = {};
let serialNumber = 0;
function resolveNextTick() {
return new Promise(resolve => process.nextTick(() => resolve()));
}
class CanceledError extends Error {
constructor(message) {
super(message);
this.canceled = true;
}
}
const MockBinding = {
reset() {
ports = {};
serialNumber = 0;
},
// Create a mock port
createPort(path, options = {}) {
serialNumber++;
const optWithDefaults = Object.assign({ echo: false, record: false, manufacturer: 'The J5 Robotics Company', vendorId: undefined, productId: undefined, maxReadSize: 1024 }, options);
ports[path] = {
data: Buffer.alloc(0),
echo: optWithDefaults.echo,
record: optWithDefaults.record,
readyData: optWithDefaults.readyData,
maxReadSize: optWithDefaults.maxReadSize,
info: {
path,
manufacturer: optWithDefaults.manufacturer,
serialNumber: `${serialNumber}`,
pnpId: undefined,
locationId: undefined,
vendorId: optWithDefaults.vendorId,
productId: optWithDefaults.productId,
},
};
debug(serialNumber, 'created port', JSON.stringify({ path, opt: options }));
},
async list() {
debug(null, 'list');
return Object.values(ports).map(port => port.info);
},
async open(options) {
var _a;
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('"options" is not an object');
}
if (!options.path) {
throw new TypeError('"path" is not a valid port');
}
if (!options.baudRate) {
throw new TypeError('"baudRate" is not a valid baudRate');
}
const openOptions = Object.assign({ dataBits: 8, lock: true, stopBits: 1, parity: 'none', rtscts: false, xon: false, xoff: false, xany: false, hupcl: true }, options);
const { path } = openOptions;
debug(null, `open: opening path ${path}`);
const port = ports[path];
await resolveNextTick();
if (!port) {
throw new Error(`Port does not exist - please call MockBinding.createPort('${path}') first`);
}
const serialNumber = port.info.serialNumber;
if ((_a = port.openOpt) === null || _a === void 0 ? void 0 : _a.lock) {
debug(serialNumber, 'open: Port is locked cannot open');
throw new Error('Port is locked cannot open');
}
debug(serialNumber, `open: opened path ${path}`);
port.openOpt = Object.assign({}, openOptions);
return new MockPortBinding(port, openOptions);
},
};
/**
* Mock bindings for pretend serialport access
*/
class MockPortBinding {
constructor(port, openOptions) {
this.port = port;
this.openOptions = openOptions;
this.pendingRead = null;
this.isOpen = true;
this.lastWrite = null;
this.recording = Buffer.alloc(0);
this.writeOperation = null; // in flight promise or null
this.serialNumber = port.info.serialNumber;
if (port.readyData) {
const data = port.readyData;
process.nextTick(() => {
if (this.isOpen) {
debug(this.serialNumber, 'emitting ready data');
this.emitData(data);
}
});
}
}
// Emit data on a mock port
emitData(data) {
if (!this.isOpen || !this.port) {
throw new Error('Port must be open to pretend to receive data');
}
const bufferData = Buffer.isBuffer(data) ? data : Buffer.from(data);
debug(this.serialNumber, 'emitting data - pending read:', Boolean(this.pendingRead));
this.port.data = Buffer.concat([this.port.data, bufferData]);
if (this.pendingRead) {
process.nextTick(this.pendingRead);
this.pendingRead = null;
}
}
async close() {
debug(this.serialNumber, 'close');
if (!this.isOpen) {
throw new Error('Port is not open');
}
const port = this.port;
if (!port) {
throw new Error('already closed');
}
port.openOpt = undefined;
// reset data on close
port.data = Buffer.alloc(0);
debug(this.serialNumber, 'port is closed');
this.serialNumber = undefined;
this.isOpen = false;
if (this.pendingRead) {
this.pendingRead(new CanceledError('port is closed'));
}
}
async read(buffer, offset, length) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
if (typeof offset !== 'number' || isNaN(offset)) {
throw new TypeError(`"offset" is not an integer got "${isNaN(offset) ? 'NaN' : typeof offset}"`);
}
if (typeof length !== 'number' || isNaN(length)) {
throw new TypeError(`"length" is not an integer got "${isNaN(length) ? 'NaN' : typeof length}"`);
}
if (buffer.length < offset + length) {
throw new Error('buffer is too small');
}
if (!this.isOpen) {
throw new Error('Port is not open');
}
debug(this.serialNumber, 'read', length, 'bytes');
await resolveNextTick();
if (!this.isOpen || !this.port) {
throw new CanceledError('Read canceled');
}
if (this.port.data.length <= 0) {
return new Promise((resolve, reject) => {
this.pendingRead = err => {
if (err) {
return reject(err);
}
this.read(buffer, offset, length).then(resolve, reject);
};
});
}
const lengthToRead = this.port.maxReadSize > length ? length : this.port.maxReadSize;
const data = this.port.data.slice(0, lengthToRead);
const bytesRead = data.copy(buffer, offset);
this.port.data = this.port.data.slice(lengthToRead);
debug(this.serialNumber, 'read', bytesRead, 'bytes');
return { bytesRead, buffer };
}
async write(buffer) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
if (!this.isOpen || !this.port) {
debug('write', 'error port is not open');
throw new Error('Port is not open');
}
debug(this.serialNumber, 'write', buffer.length, 'bytes');
if (this.writeOperation) {
throw new Error('Overlapping writes are not supported and should be queued by the serialport object');
}
this.writeOperation = (async () => {
await resolveNextTick();
if (!this.isOpen || !this.port) {
throw new Error('Write canceled');
}
const data = (this.lastWrite = Buffer.from(buffer)); // copy
if (this.port.record) {
this.recording = Buffer.concat([this.recording, data]);
}
if (this.port.echo) {
process.nextTick(() => {
if (this.isOpen) {
this.emitData(data);
}
});
}
this.writeOperation = null;
debug(this.serialNumber, 'writing finished');
})();
return this.writeOperation;
}
async update(options) {
if (typeof options !== 'object') {
throw TypeError('"options" is not an object');
}
if (typeof options.baudRate !== 'number') {
throw new TypeError('"options.baudRate" is not a number');
}
debug(this.serialNumber, 'update');
if (!this.isOpen || !this.port) {
throw new Error('Port is not open');
}
await resolveNextTick();
if (this.port.openOpt) {
this.port.openOpt.baudRate = options.baudRate;
}
}
async set(options) {
if (typeof options !== 'object') {
throw new TypeError('"options" is not an object');
}
debug(this.serialNumber, 'set');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await resolveNextTick();
}
async get() {
debug(this.serialNumber, 'get');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await resolveNextTick();
return {
cts: true,
dsr: false,
dcd: false,
};
}
async getBaudRate() {
var _a;
debug(this.serialNumber, 'getBaudRate');
if (!this.isOpen || !this.port) {
throw new Error('Port is not open');
}
await resolveNextTick();
if (!((_a = this.port.openOpt) === null || _a === void 0 ? void 0 : _a.baudRate)) {
throw new Error('Internal Error');
}
return {
baudRate: this.port.openOpt.baudRate,
};
}
async flush() {
debug(this.serialNumber, 'flush');
if (!this.isOpen || !this.port) {
throw new Error('Port is not open');
}
await resolveNextTick();
this.port.data = Buffer.alloc(0);
}
async drain() {
debug(this.serialNumber, 'drain');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await this.writeOperation;
await resolveNextTick();
}
}
export { CanceledError, MockBinding, MockPortBinding };

73
node_modules/@serialport/binding-mock/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,73 @@
/// <reference types="node" />
import { BindingInterface } from '@serialport/bindings-interface';
import { BindingPortInterface } from '@serialport/bindings-interface';
import { OpenOptions } from '@serialport/bindings-interface';
import { PortInfo } from '@serialport/bindings-interface';
import { PortStatus } from '@serialport/bindings-interface';
import { SetOptions } from '@serialport/bindings-interface';
import { UpdateOptions } from '@serialport/bindings-interface';
export declare class CanceledError extends Error {
canceled: true;
constructor(message: string);
}
export declare interface CreatePortOptions {
echo?: boolean;
record?: boolean;
readyData?: Buffer;
maxReadSize?: number;
manufacturer?: string;
vendorId?: string;
productId?: string;
}
export declare const MockBinding: MockBindingInterface;
export declare interface MockBindingInterface extends BindingInterface<MockPortBinding> {
reset(): void;
createPort(path: string, opt?: CreatePortOptions): void;
}
/**
* Mock bindings for pretend serialport access
*/
export declare class MockPortBinding implements BindingPortInterface {
readonly openOptions: Required<OpenOptions>;
readonly port: MockPortInternal;
private pendingRead;
lastWrite: null | Buffer;
recording: Buffer;
writeOperation: null | Promise<void>;
isOpen: boolean;
serialNumber?: string;
constructor(port: MockPortInternal, openOptions: Required<OpenOptions>);
emitData(data: Buffer | string): void;
close(): Promise<void>;
read(buffer: Buffer, offset: number, length: number): Promise<{
buffer: Buffer;
bytesRead: number;
}>;
write(buffer: Buffer): Promise<void>;
update(options: UpdateOptions): Promise<void>;
set(options: SetOptions): Promise<void>;
get(): Promise<PortStatus>;
getBaudRate(): Promise<{
baudRate: number;
}>;
flush(): Promise<void>;
drain(): Promise<void>;
}
export declare interface MockPortInternal {
data: Buffer;
echo: boolean;
record: boolean;
info: PortInfo;
maxReadSize: number;
readyData?: Buffer;
openOpt?: OpenOptions;
}
export { }

281
node_modules/@serialport/binding-mock/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,281 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var debugFactory = require('debug');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var debugFactory__default = /*#__PURE__*/_interopDefaultLegacy(debugFactory);
const debug = debugFactory__default["default"]('serialport/binding-mock');
let ports = {};
let serialNumber = 0;
function resolveNextTick() {
return new Promise(resolve => process.nextTick(() => resolve()));
}
class CanceledError extends Error {
constructor(message) {
super(message);
this.canceled = true;
}
}
const MockBinding = {
reset() {
ports = {};
serialNumber = 0;
},
// Create a mock port
createPort(path, options = {}) {
serialNumber++;
const optWithDefaults = Object.assign({ echo: false, record: false, manufacturer: 'The J5 Robotics Company', vendorId: undefined, productId: undefined, maxReadSize: 1024 }, options);
ports[path] = {
data: Buffer.alloc(0),
echo: optWithDefaults.echo,
record: optWithDefaults.record,
readyData: optWithDefaults.readyData,
maxReadSize: optWithDefaults.maxReadSize,
info: {
path,
manufacturer: optWithDefaults.manufacturer,
serialNumber: `${serialNumber}`,
pnpId: undefined,
locationId: undefined,
vendorId: optWithDefaults.vendorId,
productId: optWithDefaults.productId,
},
};
debug(serialNumber, 'created port', JSON.stringify({ path, opt: options }));
},
async list() {
debug(null, 'list');
return Object.values(ports).map(port => port.info);
},
async open(options) {
var _a;
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('"options" is not an object');
}
if (!options.path) {
throw new TypeError('"path" is not a valid port');
}
if (!options.baudRate) {
throw new TypeError('"baudRate" is not a valid baudRate');
}
const openOptions = Object.assign({ dataBits: 8, lock: true, stopBits: 1, parity: 'none', rtscts: false, xon: false, xoff: false, xany: false, hupcl: true }, options);
const { path } = openOptions;
debug(null, `open: opening path ${path}`);
const port = ports[path];
await resolveNextTick();
if (!port) {
throw new Error(`Port does not exist - please call MockBinding.createPort('${path}') first`);
}
const serialNumber = port.info.serialNumber;
if ((_a = port.openOpt) === null || _a === void 0 ? void 0 : _a.lock) {
debug(serialNumber, 'open: Port is locked cannot open');
throw new Error('Port is locked cannot open');
}
debug(serialNumber, `open: opened path ${path}`);
port.openOpt = Object.assign({}, openOptions);
return new MockPortBinding(port, openOptions);
},
};
/**
* Mock bindings for pretend serialport access
*/
class MockPortBinding {
constructor(port, openOptions) {
this.port = port;
this.openOptions = openOptions;
this.pendingRead = null;
this.isOpen = true;
this.lastWrite = null;
this.recording = Buffer.alloc(0);
this.writeOperation = null; // in flight promise or null
this.serialNumber = port.info.serialNumber;
if (port.readyData) {
const data = port.readyData;
process.nextTick(() => {
if (this.isOpen) {
debug(this.serialNumber, 'emitting ready data');
this.emitData(data);
}
});
}
}
// Emit data on a mock port
emitData(data) {
if (!this.isOpen || !this.port) {
throw new Error('Port must be open to pretend to receive data');
}
const bufferData = Buffer.isBuffer(data) ? data : Buffer.from(data);
debug(this.serialNumber, 'emitting data - pending read:', Boolean(this.pendingRead));
this.port.data = Buffer.concat([this.port.data, bufferData]);
if (this.pendingRead) {
process.nextTick(this.pendingRead);
this.pendingRead = null;
}
}
async close() {
debug(this.serialNumber, 'close');
if (!this.isOpen) {
throw new Error('Port is not open');
}
const port = this.port;
if (!port) {
throw new Error('already closed');
}
port.openOpt = undefined;
// reset data on close
port.data = Buffer.alloc(0);
debug(this.serialNumber, 'port is closed');
this.serialNumber = undefined;
this.isOpen = false;
if (this.pendingRead) {
this.pendingRead(new CanceledError('port is closed'));
}
}
async read(buffer, offset, length) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
if (typeof offset !== 'number' || isNaN(offset)) {
throw new TypeError(`"offset" is not an integer got "${isNaN(offset) ? 'NaN' : typeof offset}"`);
}
if (typeof length !== 'number' || isNaN(length)) {
throw new TypeError(`"length" is not an integer got "${isNaN(length) ? 'NaN' : typeof length}"`);
}
if (buffer.length < offset + length) {
throw new Error('buffer is too small');
}
if (!this.isOpen) {
throw new Error('Port is not open');
}
debug(this.serialNumber, 'read', length, 'bytes');
await resolveNextTick();
if (!this.isOpen || !this.port) {
throw new CanceledError('Read canceled');
}
if (this.port.data.length <= 0) {
return new Promise((resolve, reject) => {
this.pendingRead = err => {
if (err) {
return reject(err);
}
this.read(buffer, offset, length).then(resolve, reject);
};
});
}
const lengthToRead = this.port.maxReadSize > length ? length : this.port.maxReadSize;
const data = this.port.data.slice(0, lengthToRead);
const bytesRead = data.copy(buffer, offset);
this.port.data = this.port.data.slice(lengthToRead);
debug(this.serialNumber, 'read', bytesRead, 'bytes');
return { bytesRead, buffer };
}
async write(buffer) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
if (!this.isOpen || !this.port) {
debug('write', 'error port is not open');
throw new Error('Port is not open');
}
debug(this.serialNumber, 'write', buffer.length, 'bytes');
if (this.writeOperation) {
throw new Error('Overlapping writes are not supported and should be queued by the serialport object');
}
this.writeOperation = (async () => {
await resolveNextTick();
if (!this.isOpen || !this.port) {
throw new Error('Write canceled');
}
const data = (this.lastWrite = Buffer.from(buffer)); // copy
if (this.port.record) {
this.recording = Buffer.concat([this.recording, data]);
}
if (this.port.echo) {
process.nextTick(() => {
if (this.isOpen) {
this.emitData(data);
}
});
}
this.writeOperation = null;
debug(this.serialNumber, 'writing finished');
})();
return this.writeOperation;
}
async update(options) {
if (typeof options !== 'object') {
throw TypeError('"options" is not an object');
}
if (typeof options.baudRate !== 'number') {
throw new TypeError('"options.baudRate" is not a number');
}
debug(this.serialNumber, 'update');
if (!this.isOpen || !this.port) {
throw new Error('Port is not open');
}
await resolveNextTick();
if (this.port.openOpt) {
this.port.openOpt.baudRate = options.baudRate;
}
}
async set(options) {
if (typeof options !== 'object') {
throw new TypeError('"options" is not an object');
}
debug(this.serialNumber, 'set');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await resolveNextTick();
}
async get() {
debug(this.serialNumber, 'get');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await resolveNextTick();
return {
cts: true,
dsr: false,
dcd: false,
};
}
async getBaudRate() {
var _a;
debug(this.serialNumber, 'getBaudRate');
if (!this.isOpen || !this.port) {
throw new Error('Port is not open');
}
await resolveNextTick();
if (!((_a = this.port.openOpt) === null || _a === void 0 ? void 0 : _a.baudRate)) {
throw new Error('Internal Error');
}
return {
baudRate: this.port.openOpt.baudRate,
};
}
async flush() {
debug(this.serialNumber, 'flush');
if (!this.isOpen || !this.port) {
throw new Error('Port is not open');
}
await resolveNextTick();
this.port.data = Buffer.alloc(0);
}
async drain() {
debug(this.serialNumber, 'drain');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await this.writeOperation;
await resolveNextTick();
}
}
exports.CanceledError = CanceledError;
exports.MockBinding = MockBinding;
exports.MockPortBinding = MockPortBinding;

58
node_modules/@serialport/binding-mock/package.json generated vendored Normal file
View File

@@ -0,0 +1,58 @@
{
"name": "@serialport/binding-mock",
"version": "10.2.2",
"description": "The mock serialport bindings",
"types": "./dist/index.d.ts",
"main": "./dist/index.js",
"exports": {
"require": "./dist/index.js",
"default": "./dist/index-esm.mjs"
},
"engines": {
"node": ">=12.0.0"
},
"repository": "git@github.com:serialport/binding-mock.git",
"homepage": "https://github.com/serialport/binding-mock",
"scripts": {
"test": "mocha",
"lint": "tsc && eslint lib/**/*.ts",
"format": "eslint lib/**/*.ts --fix",
"clean": "rm -rf dist-ts dist",
"build": "npm run clean && tsc -p tsconfig-build.json && rollup -c && node -r esbuild-register bundle-types",
"prepublishOnly": "npm run build",
"semantic-release": "semantic-release"
},
"keywords": [
"serialport-binding",
"debug"
],
"license": "MIT",
"devDependencies": {
"@microsoft/api-extractor": "7.19.4",
"@types/chai": "4.3.0",
"@types/mocha": "9.1.0",
"@types/node": "17.0.15",
"@typescript-eslint/eslint-plugin": "5.10.2",
"@typescript-eslint/parser": "5.10.2",
"chai": "4.3.6",
"esbuild": "0.14.18",
"esbuild-register": "3.3.2",
"eslint": "8.8.0",
"mocha": "9.2.0",
"rollup": "2.67.0",
"rollup-plugin-node-resolve": "5.2.0",
"semantic-release": "19.0.2",
"typescript": "4.5.5"
},
"mocha": {
"bail": true,
"require": [
"esbuild-register"
],
"spec": "lib/**/*-test.ts"
},
"dependencies": {
"@serialport/bindings-interface": "^1.2.1",
"debug": "^4.3.3"
}
}

21
node_modules/@serialport/bindings-cpp/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright 2010 Christopher Williams. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

95
node_modules/@serialport/bindings-cpp/README.md generated vendored Normal file
View File

@@ -0,0 +1,95 @@
# @serialport/bindings-cpp
[![Backers on Open Collective](https://opencollective.com/serialport/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/serialport/sponsors/badge.svg)](#sponsors)
[![codecov](https://codecov.io/gh/serialport/bindings-cpp/branch/main/graph/badge.svg?token=rsGeOmdnsV)](https://codecov.io/gh/serialport/bindings-cpp)
[![Test / Lint](https://github.com/serialport/bindings-cpp/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/serialport/bindings-cpp/actions/workflows/test.yml)
Access serial ports with JavaScript. Linux, OSX and Windows. Welcome your robotic JavaScript overlords. Better yet, program them!
> Go to https://serialport.io/ to learn more, find guides and api documentation.
## Quick Links
- 📚 [**Guides**](https://serialport.io/docs/)
- [**API Docs**](https://serialport.io/docs/api-serialport)
- [`@serialport/bindings-cpp`](https://www.npmjs.com/package/@serialport/bindings-cpp)
- 🐛 [Help and Bugs](https://github.com/serialport/node-serialport/issues/new/choose) All serialport issues are pointed to the main serialport repo.
### Bindings
The Bindings provide a low level interface to work with your serialport. It is possible to use them alone but it's usually easier to use them with an interface.
- [`@serialport/bindings-cpp`](https://serialport.io/docs/api-bindings-cpp) bindings for Linux, Mac and Windows
- [`@serialport/binding-interface`](https://serialport.io/docs/api-bindings-interface) as an interface to use if you're making your own bindings
- [`@serialport/binding-mock`](https://serialport.io/docs/api-binding-mock) for a mock binding package for testing
## Developing
### Developing node serialport projects
1. Clone this repo `git clone git@github.com:serialport/bindings-cpp.git`
1. Run `npm install` to setup local package dependencies (run this any time you depend on a package local to this repo)
1. Run `npm test` to ensure everything is working properly
1. If you have a serial loopback device (TX to RX) you can run run `TEST_PORT=/path/to/port npm test` for a more comprehensive test suite. (Defaults to 115200 baud customize with the TEST_BAUD env.) You can use an arduino with the `test/arduino-echo` sketch.
### Developing Docs
See https://github.com/serialport/website
## License
SerialPort packages are all [MIT licensed](LICENSE) and all it's dependencies are MIT licensed.
## Code of Conduct
SerialPort follows the [Nodebots Code of Conduct](http://nodebots.io/conduct.html). While the code is MIT licensed participation in the community has some rules to make this a good place to work and learn.
### TLDR
- Be respectful.
- Abusive behavior is never tolerated.
- Data published to NodeBots is hosted at the discretion of the service administrators, and may be removed.
- Don't build evil robots.
- Violations of this code may result in swift and permanent expulsion from the NodeBots community.
## Governance and Community
SerialPort is currently employees a [governance](https://medium.com/the-node-js-collection/healthy-open-source-967fa8be7951) with a group of maintainers, committers and contributors, all fixing bugs and adding features and improving documentation. You need not apply to work on SerialPort, all are welcome to join, build, and maintain this project.
- A Contributor is any individual creating or commenting on an issue or pull request. By participating, this is you.
- Committers are contributors who have been given write access to the repository. They can review and merge pull requests.
- Maintainers are committers representing the required technical expertise to resolve rare disputes.
If you have a PR that improves the project people in any or all of the above people will help you land it.
**Maintainers**
- [Francis Gulotta](https://twitter.com/reconbot) | [reconbot](https://github.com/reconbot)
- [Nick Hehr](https://twitter.com/hipsterbrown) | [hipsterbrown](https://github.com/hipsterbrown)
### Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/serialport/node-serialport/graphs/contributors"><img src="https://opencollective.com/serialport/contributors.svg?width=890&button=false" /></a>
### Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/serialport#backer)]
<a href="https://opencollective.com/serialport#backers" target="_blank"><img src="https://opencollective.com/serialport/backers.svg?width=890"></a>
### Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/serialport#sponsor)]
<!-- <a href="https://opencollective.com/serialport/sponsor/0/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/serialport/sponsor/1/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/serialport/sponsor/2/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/serialport/sponsor/3/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/serialport/sponsor/4/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/serialport/sponsor/5/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/serialport/sponsor/6/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/serialport/sponsor/7/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/serialport/sponsor/8/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/serialport/sponsor/9/website" target="_blank"><img src="https://opencollective.com/serialport/sponsor/9/avatar.svg"></a> -->

80
node_modules/@serialport/bindings-cpp/binding.gyp generated vendored Normal file
View File

@@ -0,0 +1,80 @@
{
'variables': {
'openssl_fips': ''
},
'targets': [{
'target_name': 'bindings',
'sources': [
'src/serialport.cpp'
],
'include_dirs': ["<!(node -p \"require('node-addon-api').include_dir\")"],
'cflags!': [ '-fno-exceptions' ],
'cflags_cc!': [ '-fno-exceptions' ],
"defines": ["NAPI_CPP_EXCEPTIONS"],
'conditions': [
['OS=="win"',
{
'defines': ['CHECK_NODE_MODULE_VERSION'],
'sources': [
'src/serialport_win.cpp'
],
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': '1',
'DisableSpecificWarnings': [ '4530', '4506' ],
}
}
}
],
['OS=="mac"',
{
'sources': [
'src/serialport_unix.cpp',
'src/poller.cpp',
'src/darwin_list.cpp'
],
'xcode_settings': {
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'MACOSX_DEPLOYMENT_TARGET': '10.9',
'OTHER_CFLAGS': [
'-arch x86_64',
'-arch arm64'
],
'OTHER_LDFLAGS': [
'-framework CoreFoundation',
'-framework IOKit',
'-arch x86_64',
'-arch arm64'
]
}
}
],
['OS=="linux"',
{
'sources': [
'src/serialport_unix.cpp',
'src/poller.cpp',
'src/serialport_linux.cpp'
]
}
],
['OS=="android"',
{
'sources': [
'src/serialport_unix.cpp',
'src/poller.cpp',
'src/serialport_linux.cpp'
]
}
],
['OS!="win"',
{
'sources': [
'src/serialport_unix.cpp',
'src/poller.cpp'
]
}
]
]
}],
}

39
node_modules/@serialport/bindings-cpp/dist/darwin.d.ts generated vendored Normal file
View File

@@ -0,0 +1,39 @@
/// <reference types="node" />
import { BindingPortInterface } from '.';
import { BindingInterface, OpenOptions, PortStatus, SetOptions, UpdateOptions } from '@serialport/bindings-interface';
import { Poller } from './poller';
export interface DarwinOpenOptions extends OpenOptions {
/** Defaults to none */
parity?: 'none' | 'even' | 'odd';
/** see [`man termios`](http://linux.die.net/man/3/termios) defaults to 1 */
vmin?: number;
/** see [`man termios`](http://linux.die.net/man/3/termios) defaults to 0 */
vtime?: number;
}
export type DarwinBindingInterface = BindingInterface<DarwinPortBinding, DarwinOpenOptions>;
export declare const DarwinBinding: DarwinBindingInterface;
/**
* The Darwin binding layer for OSX
*/
export declare class DarwinPortBinding implements BindingPortInterface {
readonly openOptions: Required<DarwinOpenOptions>;
readonly poller: Poller;
private writeOperation;
fd: null | number;
constructor(fd: number, options: Required<DarwinOpenOptions>);
get isOpen(): boolean;
close(): Promise<void>;
read(buffer: Buffer, offset: number, length: number): Promise<{
buffer: Buffer;
bytesRead: number;
}>;
write(buffer: Buffer): Promise<void>;
update(options: UpdateOptions): Promise<void>;
set(options: SetOptions): Promise<void>;
get(): Promise<PortStatus>;
getBaudRate(): Promise<{
baudRate: number;
}>;
flush(): Promise<void>;
drain(): Promise<void>;
}

148
node_modules/@serialport/bindings-cpp/dist/darwin.js generated vendored Normal file
View File

@@ -0,0 +1,148 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DarwinPortBinding = exports.DarwinBinding = void 0;
const debug_1 = __importDefault(require("debug"));
const load_bindings_1 = require("./load-bindings");
const poller_1 = require("./poller");
const unix_read_1 = require("./unix-read");
const unix_write_1 = require("./unix-write");
const debug = (0, debug_1.default)('serialport/bindings-cpp');
exports.DarwinBinding = {
list() {
debug('list');
return (0, load_bindings_1.asyncList)();
},
async open(options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('"options" is not an object');
}
if (!options.path) {
throw new TypeError('"path" is not a valid port');
}
if (!options.baudRate) {
throw new TypeError('"baudRate" is not a valid baudRate');
}
debug('open');
const openOptions = Object.assign({ vmin: 1, vtime: 0, dataBits: 8, lock: true, stopBits: 1, parity: 'none', rtscts: false, xon: false, xoff: false, xany: false, hupcl: true }, options);
const fd = await (0, load_bindings_1.asyncOpen)(openOptions.path, openOptions);
return new DarwinPortBinding(fd, openOptions);
},
};
/**
* The Darwin binding layer for OSX
*/
class DarwinPortBinding {
constructor(fd, options) {
this.fd = fd;
this.openOptions = options;
this.poller = new poller_1.Poller(fd);
this.writeOperation = null;
}
get isOpen() {
return this.fd !== null;
}
async close() {
debug('close');
if (!this.isOpen) {
throw new Error('Port is not open');
}
const fd = this.fd;
this.poller.stop();
this.poller.destroy();
this.fd = null;
await (0, load_bindings_1.asyncClose)(fd);
}
async read(buffer, offset, length) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
if (typeof offset !== 'number' || isNaN(offset)) {
throw new TypeError(`"offset" is not an integer got "${isNaN(offset) ? 'NaN' : typeof offset}"`);
}
if (typeof length !== 'number' || isNaN(length)) {
throw new TypeError(`"length" is not an integer got "${isNaN(length) ? 'NaN' : typeof length}"`);
}
debug('read');
if (buffer.length < offset + length) {
throw new Error('buffer is too small');
}
if (!this.isOpen) {
throw new Error('Port is not open');
}
return (0, unix_read_1.unixRead)({ binding: this, buffer, offset, length });
}
async write(buffer) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
debug('write', buffer.length, 'bytes');
if (!this.isOpen) {
debug('write', 'error port is not open');
throw new Error('Port is not open');
}
this.writeOperation = (async () => {
if (buffer.length === 0) {
return;
}
await (0, unix_write_1.unixWrite)({ binding: this, buffer });
this.writeOperation = null;
})();
return this.writeOperation;
}
async update(options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw TypeError('"options" is not an object');
}
if (typeof options.baudRate !== 'number') {
throw new TypeError('"options.baudRate" is not a number');
}
debug('update');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await (0, load_bindings_1.asyncUpdate)(this.fd, options);
}
async set(options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('"options" is not an object');
}
debug('set', options);
if (!this.isOpen) {
throw new Error('Port is not open');
}
await (0, load_bindings_1.asyncSet)(this.fd, options);
}
async get() {
debug('get');
if (!this.isOpen) {
throw new Error('Port is not open');
}
return (0, load_bindings_1.asyncGet)(this.fd);
}
async getBaudRate() {
debug('getBaudRate');
if (!this.isOpen) {
throw new Error('Port is not open');
}
throw new Error('getBaudRate is not implemented on darwin');
}
async flush() {
debug('flush');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await (0, load_bindings_1.asyncFlush)(this.fd);
}
async drain() {
debug('drain');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await this.writeOperation;
await (0, load_bindings_1.asyncDrain)(this.fd);
}
}
exports.DarwinPortBinding = DarwinPortBinding;

View File

@@ -0,0 +1,7 @@
import { BindingsErrorInterface } from '@serialport/bindings-interface';
export declare class BindingsError extends Error implements BindingsErrorInterface {
canceled: boolean;
constructor(message: string, { canceled }?: {
canceled?: boolean | undefined;
});
}

10
node_modules/@serialport/bindings-cpp/dist/errors.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BindingsError = void 0;
class BindingsError extends Error {
constructor(message, { canceled = false } = {}) {
super(message);
this.canceled = canceled;
}
}
exports.BindingsError = BindingsError;

13
node_modules/@serialport/bindings-cpp/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import { DarwinBindingInterface } from './darwin';
import { LinuxBindingInterface } from './linux';
import { WindowsBindingInterface } from './win32';
export * from '@serialport/bindings-interface';
export * from './darwin';
export * from './linux';
export * from './win32';
export * from './errors';
export type AutoDetectTypes = DarwinBindingInterface | WindowsBindingInterface | LinuxBindingInterface;
/**
* This is an auto detected binding for your current platform
*/
export declare function autoDetect(): AutoDetectTypes;

48
node_modules/@serialport/bindings-cpp/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.autoDetect = void 0;
/* eslint-disable @typescript-eslint/no-var-requires */
const debug_1 = __importDefault(require("debug"));
const darwin_1 = require("./darwin");
const linux_1 = require("./linux");
const win32_1 = require("./win32");
const debug = (0, debug_1.default)('serialport/bindings-cpp');
__exportStar(require("@serialport/bindings-interface"), exports);
__exportStar(require("./darwin"), exports);
__exportStar(require("./linux"), exports);
__exportStar(require("./win32"), exports);
__exportStar(require("./errors"), exports);
/**
* This is an auto detected binding for your current platform
*/
function autoDetect() {
switch (process.platform) {
case 'win32':
debug('loading WindowsBinding');
return win32_1.WindowsBinding;
case 'darwin':
debug('loading DarwinBinding');
return darwin_1.DarwinBinding;
default:
debug('loading LinuxBinding');
return linux_1.LinuxBinding;
}
}
exports.autoDetect = autoDetect;

View File

@@ -0,0 +1,4 @@
/// <reference types="node" />
import { spawn } from 'child_process';
import { PortInfo } from '@serialport/bindings-interface';
export declare function linuxList(spawnCmd?: typeof spawn): Promise<PortInfo[]>;

View File

@@ -0,0 +1,115 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.linuxList = void 0;
const child_process_1 = require("child_process");
const parser_readline_1 = require("@serialport/parser-readline");
// get only serial port names
function checkPathOfDevice(path) {
return /(tty(S|WCH|ACM|USB|AMA|MFD|O|XRUSB)|rfcomm)/.test(path) && path;
}
function propName(name) {
return {
DEVNAME: 'path',
ID_VENDOR_ENC: 'manufacturer',
ID_SERIAL_SHORT: 'serialNumber',
ID_VENDOR_ID: 'vendorId',
ID_MODEL_ID: 'productId',
DEVLINKS: 'pnpId',
/**
* Workaround for systemd defect
* see https://github.com/serialport/bindings-cpp/issues/115
*/
ID_USB_VENDOR_ENC: 'manufacturer',
ID_USB_SERIAL_SHORT: 'serialNumber',
ID_USB_VENDOR_ID: 'vendorId',
ID_USB_MODEL_ID: 'productId',
// End of workaround
}[name.toUpperCase()];
}
function decodeHexEscape(str) {
return str.replace(/\\x([a-fA-F0-9]{2})/g, (a, b) => {
return String.fromCharCode(parseInt(b, 16));
});
}
function propVal(name, val) {
if (name === 'pnpId') {
const match = val.match(/\/by-id\/([^\s]+)/);
return (match === null || match === void 0 ? void 0 : match[1]) || undefined;
}
if (name === 'manufacturer') {
return decodeHexEscape(val);
}
if (/^0x/.test(val)) {
return val.substr(2);
}
return val;
}
function linuxList(spawnCmd = child_process_1.spawn) {
const ports = [];
const udevadm = spawnCmd('udevadm', ['info', '-e']);
const lines = udevadm.stdout.pipe(new parser_readline_1.ReadlineParser());
let skipPort = false;
let port = {
path: '',
manufacturer: undefined,
serialNumber: undefined,
pnpId: undefined,
locationId: undefined,
vendorId: undefined,
productId: undefined,
};
lines.on('data', (line) => {
const lineType = line.slice(0, 1);
const data = line.slice(3);
// new port entry
if (lineType === 'P') {
port = {
path: '',
manufacturer: undefined,
serialNumber: undefined,
pnpId: undefined,
locationId: undefined,
vendorId: undefined,
productId: undefined,
};
skipPort = false;
return;
}
if (skipPort) {
return;
}
// Check dev name and save port if it matches flag to skip the rest of the data if not
if (lineType === 'N') {
if (checkPathOfDevice(data)) {
ports.push(port);
}
else {
skipPort = true;
}
return;
}
// parse data about each port
if (lineType === 'E') {
const keyValue = data.match(/^(.+)=(.*)/);
if (!keyValue) {
return;
}
const key = propName(keyValue[1]);
if (!key) {
return;
}
port[key] = propVal(key, keyValue[2]);
}
});
return new Promise((resolve, reject) => {
udevadm.on('close', (code) => {
if (code) {
reject(new Error(`Error listing ports udevadm exited with error code: ${code}`));
}
});
udevadm.on('error', reject);
lines.on('error', reject);
lines.on('finish', () => resolve(ports));
});
}
exports.linuxList = linuxList;

46
node_modules/@serialport/bindings-cpp/dist/linux.d.ts generated vendored Normal file
View File

@@ -0,0 +1,46 @@
/// <reference types="node" />
import { Poller } from './poller';
import { BindingInterface, OpenOptions, PortStatus, SetOptions, UpdateOptions } from '@serialport/bindings-interface';
import { BindingPortInterface } from '.';
export interface LinuxOpenOptions extends OpenOptions {
/** Defaults to none */
parity?: 'none' | 'even' | 'odd';
/** see [`man termios`](http://linux.die.net/man/3/termios) defaults to 1 */
vmin?: number;
/** see [`man termios`](http://linux.die.net/man/3/termios) defaults to 0 */
vtime?: number;
}
export interface LinuxPortStatus extends PortStatus {
lowLatency: boolean;
}
export interface LinuxSetOptions extends SetOptions {
/** Low latency mode */
lowLatency?: boolean;
}
export type LinuxBindingInterface = BindingInterface<LinuxPortBinding, LinuxOpenOptions>;
export declare const LinuxBinding: LinuxBindingInterface;
/**
* The linux binding layer
*/
export declare class LinuxPortBinding implements BindingPortInterface {
readonly openOptions: Required<LinuxOpenOptions>;
readonly poller: Poller;
private writeOperation;
fd: number | null;
constructor(fd: number, openOptions: Required<LinuxOpenOptions>);
get isOpen(): boolean;
close(): Promise<void>;
read(buffer: Buffer, offset: number, length: number): Promise<{
buffer: Buffer;
bytesRead: number;
}>;
write(buffer: Buffer): Promise<void>;
update(options: UpdateOptions): Promise<void>;
set(options: LinuxSetOptions): Promise<void>;
get(): Promise<LinuxPortStatus>;
getBaudRate(): Promise<{
baudRate: number;
}>;
flush(): Promise<void>;
drain(): Promise<void>;
}

150
node_modules/@serialport/bindings-cpp/dist/linux.js generated vendored Normal file
View File

@@ -0,0 +1,150 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LinuxPortBinding = exports.LinuxBinding = void 0;
const debug_1 = __importDefault(require("debug"));
const linux_list_1 = require("./linux-list");
const poller_1 = require("./poller");
const unix_read_1 = require("./unix-read");
const unix_write_1 = require("./unix-write");
const load_bindings_1 = require("./load-bindings");
const debug = (0, debug_1.default)('serialport/bindings-cpp');
exports.LinuxBinding = {
list() {
debug('list');
return (0, linux_list_1.linuxList)();
},
async open(options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('"options" is not an object');
}
if (!options.path) {
throw new TypeError('"path" is not a valid port');
}
if (!options.baudRate) {
throw new TypeError('"baudRate" is not a valid baudRate');
}
debug('open');
const openOptions = Object.assign({ vmin: 1, vtime: 0, dataBits: 8, lock: true, stopBits: 1, parity: 'none', rtscts: false, xon: false, xoff: false, xany: false, hupcl: true }, options);
const fd = await (0, load_bindings_1.asyncOpen)(openOptions.path, openOptions);
this.fd = fd;
return new LinuxPortBinding(fd, openOptions);
},
};
/**
* The linux binding layer
*/
class LinuxPortBinding {
constructor(fd, openOptions) {
this.fd = fd;
this.openOptions = openOptions;
this.poller = new poller_1.Poller(fd);
this.writeOperation = null;
}
get isOpen() {
return this.fd !== null;
}
async close() {
debug('close');
if (!this.isOpen) {
throw new Error('Port is not open');
}
const fd = this.fd;
this.poller.stop();
this.poller.destroy();
this.fd = null;
await (0, load_bindings_1.asyncClose)(fd);
}
async read(buffer, offset, length) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
if (typeof offset !== 'number' || isNaN(offset)) {
throw new TypeError(`"offset" is not an integer got "${isNaN(offset) ? 'NaN' : typeof offset}"`);
}
if (typeof length !== 'number' || isNaN(length)) {
throw new TypeError(`"length" is not an integer got "${isNaN(length) ? 'NaN' : typeof length}"`);
}
debug('read');
if (buffer.length < offset + length) {
throw new Error('buffer is too small');
}
if (!this.isOpen) {
throw new Error('Port is not open');
}
return (0, unix_read_1.unixRead)({ binding: this, buffer, offset, length });
}
async write(buffer) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
debug('write', buffer.length, 'bytes');
if (!this.isOpen) {
debug('write', 'error port is not open');
throw new Error('Port is not open');
}
this.writeOperation = (async () => {
if (buffer.length === 0) {
return;
}
await (0, unix_write_1.unixWrite)({ binding: this, buffer });
this.writeOperation = null;
})();
return this.writeOperation;
}
async update(options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw TypeError('"options" is not an object');
}
if (typeof options.baudRate !== 'number') {
throw new TypeError('"options.baudRate" is not a number');
}
debug('update');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await (0, load_bindings_1.asyncUpdate)(this.fd, options);
}
async set(options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('"options" is not an object');
}
debug('set');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await (0, load_bindings_1.asyncSet)(this.fd, options);
}
async get() {
debug('get');
if (!this.isOpen) {
throw new Error('Port is not open');
}
return (0, load_bindings_1.asyncGet)(this.fd);
}
async getBaudRate() {
debug('getBaudRate');
if (!this.isOpen) {
throw new Error('Port is not open');
}
return (0, load_bindings_1.asyncGetBaudRate)(this.fd);
}
async flush() {
debug('flush');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await (0, load_bindings_1.asyncFlush)(this.fd);
}
async drain() {
debug('drain');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await this.writeOperation;
await (0, load_bindings_1.asyncDrain)(this.fd);
}
}
exports.LinuxPortBinding = LinuxPortBinding;

View File

@@ -0,0 +1,11 @@
export declare const asyncClose: Function;
export declare const asyncDrain: Function;
export declare const asyncFlush: Function;
export declare const asyncGet: Function;
export declare const asyncGetBaudRate: Function;
export declare const asyncList: Function;
export declare const asyncOpen: Function;
export declare const asyncSet: Function;
export declare const asyncUpdate: Function;
export declare const asyncRead: Function;
export declare const asyncWrite: Function;

View File

@@ -0,0 +1,22 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.asyncWrite = exports.asyncRead = exports.asyncUpdate = exports.asyncSet = exports.asyncOpen = exports.asyncList = exports.asyncGetBaudRate = exports.asyncGet = exports.asyncFlush = exports.asyncDrain = exports.asyncClose = void 0;
const node_gyp_build_1 = __importDefault(require("node-gyp-build"));
const util_1 = require("util");
const path_1 = require("path");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const binding = (0, node_gyp_build_1.default)((0, path_1.join)(__dirname, '../'));
exports.asyncClose = binding.close ? (0, util_1.promisify)(binding.close) : async () => { throw new Error('"binding.close" Method not implemented'); };
exports.asyncDrain = binding.drain ? (0, util_1.promisify)(binding.drain) : async () => { throw new Error('"binding.drain" Method not implemented'); };
exports.asyncFlush = binding.flush ? (0, util_1.promisify)(binding.flush) : async () => { throw new Error('"binding.flush" Method not implemented'); };
exports.asyncGet = binding.get ? (0, util_1.promisify)(binding.get) : async () => { throw new Error('"binding.get" Method not implemented'); };
exports.asyncGetBaudRate = binding.getBaudRate ? (0, util_1.promisify)(binding.getBaudRate) : async () => { throw new Error('"binding.getBaudRate" Method not implemented'); };
exports.asyncList = binding.list ? (0, util_1.promisify)(binding.list) : async () => { throw new Error('"binding.list" Method not implemented'); };
exports.asyncOpen = binding.open ? (0, util_1.promisify)(binding.open) : async () => { throw new Error('"binding.open" Method not implemented'); };
exports.asyncSet = binding.set ? (0, util_1.promisify)(binding.set) : async () => { throw new Error('"binding.set" Method not implemented'); };
exports.asyncUpdate = binding.update ? (0, util_1.promisify)(binding.update) : async () => { throw new Error('"binding.update" Method not implemented'); };
exports.asyncRead = binding.read ? (0, util_1.promisify)(binding.read) : async () => { throw new Error('"binding.read" Method not implemented'); };
exports.asyncWrite = binding.write ? (0, util_1.promisify)(binding.write) : async () => { throw new Error('"binding.write" Method not implemented'); };

40
node_modules/@serialport/bindings-cpp/dist/poller.d.ts generated vendored Normal file
View File

@@ -0,0 +1,40 @@
/// <reference types="node" />
import { EventEmitter } from 'events';
interface PollerClass {
new (fd: number, cb: (err: Error, flag: number) => void): PollerInstance;
}
interface PollerInstance {
poll(flag: number): void;
stop(): void;
destroy(): void;
}
export declare const EVENTS: {
UV_READABLE: number;
UV_WRITABLE: number;
UV_DISCONNECT: number;
};
/**
* Polls unix systems for readable or writable states of a file or serialport
*/
export declare class Poller extends EventEmitter {
poller: PollerInstance;
constructor(fd: number, FDPoller?: PollerClass);
/**
* Wait for the next event to occur
* @param {string} event ('readable'|'writable'|'disconnect')
* @returns {Poller} returns itself
*/
once(event: 'readable' | 'writable' | 'disconnect', callback: (err: null | Error) => void): this;
/**
* Ask the bindings to listen for an event, it is recommend to use `.once()` for easy use
* @param {EVENTS} eventFlag polls for an event or group of events based upon a flag.
*/
poll(eventFlag?: number): void;
/**
* Stop listening for events and cancel all outstanding listening with an error
*/
stop(): void;
destroy(): void;
emitCanceled(): void;
}
export {};

104
node_modules/@serialport/bindings-cpp/dist/poller.js generated vendored Normal file
View File

@@ -0,0 +1,104 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Poller = exports.EVENTS = void 0;
const debug_1 = __importDefault(require("debug"));
const events_1 = require("events");
const path_1 = require("path");
const node_gyp_build_1 = __importDefault(require("node-gyp-build"));
const errors_1 = require("./errors");
const { Poller: PollerBindings } = (0, node_gyp_build_1.default)((0, path_1.join)(__dirname, '../'));
const logger = (0, debug_1.default)('serialport/bindings-cpp/poller');
exports.EVENTS = {
UV_READABLE: 0b0001,
UV_WRITABLE: 0b0010,
UV_DISCONNECT: 0b0100,
};
function handleEvent(error, eventFlag) {
if (error) {
logger('error', error);
this.emit('readable', error);
this.emit('writable', error);
this.emit('disconnect', error);
return;
}
if (eventFlag & exports.EVENTS.UV_READABLE) {
logger('received "readable"');
this.emit('readable', null);
}
if (eventFlag & exports.EVENTS.UV_WRITABLE) {
logger('received "writable"');
this.emit('writable', null);
}
if (eventFlag & exports.EVENTS.UV_DISCONNECT) {
logger('received "disconnect"');
this.emit('disconnect', null);
}
}
/**
* Polls unix systems for readable or writable states of a file or serialport
*/
class Poller extends events_1.EventEmitter {
constructor(fd, FDPoller = PollerBindings) {
logger('Creating poller');
super();
this.poller = new FDPoller(fd, handleEvent.bind(this));
}
/**
* Wait for the next event to occur
* @param {string} event ('readable'|'writable'|'disconnect')
* @returns {Poller} returns itself
*/
once(event, callback) {
switch (event) {
case 'readable':
this.poll(exports.EVENTS.UV_READABLE);
break;
case 'writable':
this.poll(exports.EVENTS.UV_WRITABLE);
break;
case 'disconnect':
this.poll(exports.EVENTS.UV_DISCONNECT);
break;
}
return super.once(event, callback);
}
/**
* Ask the bindings to listen for an event, it is recommend to use `.once()` for easy use
* @param {EVENTS} eventFlag polls for an event or group of events based upon a flag.
*/
poll(eventFlag = 0) {
if (eventFlag & exports.EVENTS.UV_READABLE) {
logger('Polling for "readable"');
}
if (eventFlag & exports.EVENTS.UV_WRITABLE) {
logger('Polling for "writable"');
}
if (eventFlag & exports.EVENTS.UV_DISCONNECT) {
logger('Polling for "disconnect"');
}
this.poller.poll(eventFlag);
}
/**
* Stop listening for events and cancel all outstanding listening with an error
*/
stop() {
logger('Stopping poller');
this.poller.stop();
this.emitCanceled();
}
destroy() {
logger('Destroying poller');
this.poller.destroy();
this.emitCanceled();
}
emitCanceled() {
const err = new errors_1.BindingsError('Canceled', { canceled: true });
this.emit('readable', err);
this.emit('writable', err);
this.emit('disconnect', err);
}
}
exports.Poller = Poller;

View File

@@ -0,0 +1,18 @@
/// <reference types="node" />
/// <reference types="node" />
import { read as fsRead } from 'fs';
import { LinuxPortBinding } from './linux';
import { DarwinPortBinding } from './darwin';
declare const readAsync: typeof fsRead.__promisify__;
interface UnixReadOptions {
binding: LinuxPortBinding | DarwinPortBinding;
buffer: Buffer;
offset: number;
length: number;
fsReadAsync?: typeof readAsync;
}
export declare const unixRead: ({ binding, buffer, offset, length, fsReadAsync, }: UnixReadOptions) => Promise<{
buffer: Buffer;
bytesRead: number;
}>;
export {};

View File

@@ -0,0 +1,55 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.unixRead = void 0;
const util_1 = require("util");
const fs_1 = require("fs");
const errors_1 = require("./errors");
const debug_1 = __importDefault(require("debug"));
const logger = (0, debug_1.default)('serialport/bindings-cpp/unixRead');
const readAsync = (0, util_1.promisify)(fs_1.read);
const readable = (binding) => {
return new Promise((resolve, reject) => {
if (!binding.poller) {
throw new Error('No poller on bindings');
}
binding.poller.once('readable', err => (err ? reject(err) : resolve()));
});
};
const unixRead = async ({ binding, buffer, offset, length, fsReadAsync = readAsync, }) => {
logger('Starting read');
if (!binding.isOpen || !binding.fd) {
throw new errors_1.BindingsError('Port is not open', { canceled: true });
}
try {
const { bytesRead } = await fsReadAsync(binding.fd, buffer, offset, length, null);
if (bytesRead === 0) {
return (0, exports.unixRead)({ binding, buffer, offset, length, fsReadAsync });
}
logger('Finished read', bytesRead, 'bytes');
return { bytesRead, buffer };
}
catch (err) {
logger('read error', err);
if (err.code === 'EAGAIN' || err.code === 'EWOULDBLOCK' || err.code === 'EINTR') {
if (!binding.isOpen) {
throw new errors_1.BindingsError('Port is not open', { canceled: true });
}
logger('waiting for readable because of code:', err.code);
await readable(binding);
return (0, exports.unixRead)({ binding, buffer, offset, length, fsReadAsync });
}
const disconnectError = err.code === 'EBADF' || // Bad file number means we got closed
err.code === 'ENXIO' || // No such device or address probably usb disconnect
err.code === 'UNKNOWN' ||
err.errno === -1; // generic error
if (disconnectError) {
err.disconnect = true;
logger('disconnecting', err);
}
throw err;
}
};
exports.unixRead = unixRead;

View File

@@ -0,0 +1,14 @@
/// <reference types="node" />
/// <reference types="node" />
import { write } from 'fs';
import { LinuxPortBinding } from './linux';
import { DarwinPortBinding } from './darwin';
declare const writeAsync: typeof write.__promisify__;
interface UnixWriteOptions {
binding: LinuxPortBinding | DarwinPortBinding;
buffer: Buffer;
offset?: number;
fsWriteAsync?: typeof writeAsync;
}
export declare const unixWrite: ({ binding, buffer, offset, fsWriteAsync }: UnixWriteOptions) => Promise<void>;
export {};

View File

@@ -0,0 +1,56 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.unixWrite = void 0;
const fs_1 = require("fs");
const debug_1 = __importDefault(require("debug"));
const util_1 = require("util");
const logger = (0, debug_1.default)('serialport/bindings-cpp/unixWrite');
const writeAsync = (0, util_1.promisify)(fs_1.write);
const writable = (binding) => {
return new Promise((resolve, reject) => {
binding.poller.once('writable', err => (err ? reject(err) : resolve()));
});
};
const unixWrite = async ({ binding, buffer, offset = 0, fsWriteAsync = writeAsync }) => {
const bytesToWrite = buffer.length - offset;
logger('Starting write', buffer.length, 'bytes offset', offset, 'bytesToWrite', bytesToWrite);
if (!binding.isOpen || !binding.fd) {
throw new Error('Port is not open');
}
try {
const { bytesWritten } = await fsWriteAsync(binding.fd, buffer, offset, bytesToWrite);
logger('write returned: wrote', bytesWritten, 'bytes');
if (bytesWritten + offset < buffer.length) {
if (!binding.isOpen) {
throw new Error('Port is not open');
}
return (0, exports.unixWrite)({ binding, buffer, offset: bytesWritten + offset, fsWriteAsync });
}
logger('Finished writing', bytesWritten + offset, 'bytes');
}
catch (err) {
logger('write errored', err);
if (err.code === 'EAGAIN' || err.code === 'EWOULDBLOCK' || err.code === 'EINTR') {
if (!binding.isOpen) {
throw new Error('Port is not open');
}
logger('waiting for writable because of code:', err.code);
await writable(binding);
return (0, exports.unixWrite)({ binding, buffer, offset, fsWriteAsync });
}
const disconnectError = err.code === 'EBADF' || // Bad file number means we got closed
err.code === 'ENXIO' || // No such device or address probably usb disconnect
err.code === 'UNKNOWN' ||
err.errno === -1; // generic error
if (disconnectError) {
err.disconnect = true;
logger('disconnecting', err);
}
logger('error', err);
throw err;
}
};
exports.unixWrite = unixWrite;

View File

@@ -0,0 +1 @@
export declare const serialNumParser: (pnpId?: string) => string | null;

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serialNumParser = void 0;
const PARSERS = [/USB\\(?:.+)\\(.+)/, /FTDIBUS\\(?:.+)\+(.+?)A?\\.+/];
const serialNumParser = (pnpId) => {
if (!pnpId) {
return null;
}
for (const parser of PARSERS) {
const sn = pnpId.match(parser);
if (sn) {
return sn[1];
}
}
return null;
};
exports.serialNumParser = serialNumParser;

35
node_modules/@serialport/bindings-cpp/dist/win32.d.ts generated vendored Normal file
View File

@@ -0,0 +1,35 @@
/// <reference types="node" />
import { BindingPortInterface } from '.';
import { BindingInterface, OpenOptions, PortStatus, SetOptions, UpdateOptions } from '@serialport/bindings-interface';
export interface WindowsOpenOptions extends OpenOptions {
/** Device parity defaults to none */
parity?: 'none' | 'even' | 'odd' | 'mark' | 'space';
/** RTS mode defaults to handshake */
rtsMode?: 'handshake' | 'enable' | 'toggle';
}
export type WindowsBindingInterface = BindingInterface<WindowsPortBinding, WindowsOpenOptions>;
export declare const WindowsBinding: WindowsBindingInterface;
/**
* The Windows binding layer
*/
export declare class WindowsPortBinding implements BindingPortInterface {
fd: null | number;
writeOperation: Promise<void> | null;
openOptions: Required<OpenOptions>;
constructor(fd: number, options: Required<OpenOptions>);
get isOpen(): boolean;
close(): Promise<void>;
read(buffer: Buffer, offset: number, length: number): Promise<{
buffer: Buffer;
bytesRead: number;
}>;
write(buffer: Buffer): Promise<void>;
update(options: UpdateOptions): Promise<void>;
set(options: SetOptions): Promise<void>;
get(): Promise<PortStatus>;
getBaudRate(): Promise<{
baudRate: number;
}>;
flush(): Promise<void>;
drain(): Promise<void>;
}

162
node_modules/@serialport/bindings-cpp/dist/win32.js generated vendored Normal file
View File

@@ -0,0 +1,162 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WindowsPortBinding = exports.WindowsBinding = void 0;
const debug_1 = __importDefault(require("debug"));
const _1 = require(".");
const load_bindings_1 = require("./load-bindings");
const win32_sn_parser_1 = require("./win32-sn-parser");
const debug = (0, debug_1.default)('serialport/bindings-cpp');
exports.WindowsBinding = {
async list() {
const ports = await (0, load_bindings_1.asyncList)();
// Grab the serial number from the pnp id
return ports.map(port => {
if (port.pnpId && !port.serialNumber) {
const serialNumber = (0, win32_sn_parser_1.serialNumParser)(port.pnpId);
if (serialNumber) {
return Object.assign(Object.assign({}, port), { serialNumber });
}
}
return port;
});
},
async open(options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('"options" is not an object');
}
if (!options.path) {
throw new TypeError('"path" is not a valid port');
}
if (!options.baudRate) {
throw new TypeError('"baudRate" is not a valid baudRate');
}
debug('open');
const openOptions = Object.assign({ dataBits: 8, lock: true, stopBits: 1, parity: 'none', rtscts: false, rtsMode: 'handshake', xon: false, xoff: false, xany: false, hupcl: true }, options);
const fd = await (0, load_bindings_1.asyncOpen)(openOptions.path, openOptions);
return new WindowsPortBinding(fd, openOptions);
},
};
/**
* The Windows binding layer
*/
class WindowsPortBinding {
constructor(fd, options) {
this.fd = fd;
this.openOptions = options;
this.writeOperation = null;
}
get isOpen() {
return this.fd !== null;
}
async close() {
debug('close');
if (!this.isOpen) {
throw new Error('Port is not open');
}
const fd = this.fd;
this.fd = null;
await (0, load_bindings_1.asyncClose)(fd);
}
async read(buffer, offset, length) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
if (typeof offset !== 'number' || isNaN(offset)) {
throw new TypeError(`"offset" is not an integer got "${isNaN(offset) ? 'NaN' : typeof offset}"`);
}
if (typeof length !== 'number' || isNaN(length)) {
throw new TypeError(`"length" is not an integer got "${isNaN(length) ? 'NaN' : typeof length}"`);
}
debug('read');
if (buffer.length < offset + length) {
throw new Error('buffer is too small');
}
if (!this.isOpen) {
throw new Error('Port is not open');
}
try {
const bytesRead = await (0, load_bindings_1.asyncRead)(this.fd, buffer, offset, length);
return { bytesRead, buffer };
}
catch (err) {
if (!this.isOpen) {
throw new _1.BindingsError(err.message, { canceled: true });
}
throw err;
}
}
async write(buffer) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('"buffer" is not a Buffer');
}
debug('write', buffer.length, 'bytes');
if (!this.isOpen) {
debug('write', 'error port is not open');
throw new Error('Port is not open');
}
this.writeOperation = (async () => {
if (buffer.length === 0) {
return;
}
await (0, load_bindings_1.asyncWrite)(this.fd, buffer);
this.writeOperation = null;
})();
return this.writeOperation;
}
async update(options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw TypeError('"options" is not an object');
}
if (typeof options.baudRate !== 'number') {
throw new TypeError('"options.baudRate" is not a number');
}
debug('update');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await (0, load_bindings_1.asyncUpdate)(this.fd, options);
}
async set(options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new TypeError('"options" is not an object');
}
debug('set', options);
if (!this.isOpen) {
throw new Error('Port is not open');
}
await (0, load_bindings_1.asyncSet)(this.fd, options);
}
async get() {
debug('get');
if (!this.isOpen) {
throw new Error('Port is not open');
}
return (0, load_bindings_1.asyncGet)(this.fd);
}
async getBaudRate() {
debug('getBaudRate');
if (!this.isOpen) {
throw new Error('Port is not open');
}
return (0, load_bindings_1.asyncGetBaudRate)(this.fd);
}
async flush() {
debug('flush');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await (0, load_bindings_1.asyncFlush)(this.fd);
}
async drain() {
debug('drain');
if (!this.isOpen) {
throw new Error('Port is not open');
}
await this.writeOperation;
await (0, load_bindings_1.asyncDrain)(this.fd);
}
}
exports.WindowsPortBinding = WindowsPortBinding;

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright 2010 Christopher Williams. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

View File

@@ -0,0 +1,3 @@
# @serialport/parser-delimiter
See our api docs https://serialport.io/docs/api-parser-delimiter

View File

@@ -0,0 +1,23 @@
/// <reference types="node" />
/// <reference types="node" />
import { Transform, TransformCallback, TransformOptions } from 'stream';
export interface DelimiterOptions extends TransformOptions {
/** The delimiter on which to split incoming data. */
delimiter: string | Buffer | number[];
/** Should the delimiter be included at the end of data. Defaults to `false` */
includeDelimiter?: boolean;
}
/**
* A transform stream that emits data each time a byte sequence is received.
* @extends Transform
*
* To use the `Delimiter` parser, provide a delimiter as a string, buffer, or array of bytes. Runs in O(n) time.
*/
export declare class DelimiterParser extends Transform {
includeDelimiter: boolean;
delimiter: Buffer;
buffer: Buffer;
constructor({ delimiter, includeDelimiter, ...options }: DelimiterOptions);
_transform(chunk: Buffer, encoding: BufferEncoding, cb: TransformCallback): void;
_flush(cb: TransformCallback): void;
}

View File

@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DelimiterParser = void 0;
const stream_1 = require("stream");
/**
* A transform stream that emits data each time a byte sequence is received.
* @extends Transform
*
* To use the `Delimiter` parser, provide a delimiter as a string, buffer, or array of bytes. Runs in O(n) time.
*/
class DelimiterParser extends stream_1.Transform {
constructor({ delimiter, includeDelimiter = false, ...options }) {
super(options);
if (delimiter === undefined) {
throw new TypeError('"delimiter" is not a bufferable object');
}
if (delimiter.length === 0) {
throw new TypeError('"delimiter" has a 0 or undefined length');
}
this.includeDelimiter = includeDelimiter;
this.delimiter = Buffer.from(delimiter);
this.buffer = Buffer.alloc(0);
}
_transform(chunk, encoding, cb) {
let data = Buffer.concat([this.buffer, chunk]);
let position;
while ((position = data.indexOf(this.delimiter)) !== -1) {
this.push(data.slice(0, position + (this.includeDelimiter ? this.delimiter.length : 0)));
data = data.slice(position + this.delimiter.length);
}
this.buffer = data;
cb();
}
_flush(cb) {
this.push(this.buffer);
this.buffer = Buffer.alloc(0);
cb();
}
}
exports.DelimiterParser = DelimiterParser;

View File

@@ -0,0 +1,25 @@
{
"name": "@serialport/parser-delimiter",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"version": "11.0.0",
"engines": {
"node": ">=12.0.0"
},
"publishConfig": {
"access": "public"
},
"license": "MIT",
"scripts": {
"build": "tsc --build tsconfig-build.json"
},
"repository": {
"type": "git",
"url": "git://github.com/serialport/node-serialport.git"
},
"funding": "https://opencollective.com/serialport/donate",
"devDependencies": {
"typescript": "5.0.4"
},
"gitHead": "6a8202cd947c87ac70c9f3c84d60fe4b5f5d70a9"
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright 2010 Christopher Williams. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

View File

@@ -0,0 +1,3 @@
# @serialport/parser-readline
See our api docs See our api docs https://serialport.io/docs/api-parser-readline

View File

@@ -0,0 +1,19 @@
/// <reference types="node" />
/// <reference types="node" />
import { DelimiterParser } from '@serialport/parser-delimiter';
import { TransformOptions } from 'stream';
export interface ReadlineOptions extends TransformOptions {
/** delimiter to use defaults to \n */
delimiter?: string | Buffer | number[];
/** include the delimiter at the end of the packet defaults to false */
includeDelimiter?: boolean;
/** Defaults to utf8 */
encoding?: BufferEncoding;
}
/**
* A transform stream that emits data after a newline delimiter is received.
* @summary To use the `Readline` parser, provide a delimiter (defaults to `\n`). Data is emitted as string controllable by the `encoding` option (defaults to `utf8`).
*/
export declare class ReadlineParser extends DelimiterParser {
constructor(options?: ReadlineOptions);
}

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReadlineParser = void 0;
const parser_delimiter_1 = require("@serialport/parser-delimiter");
/**
* A transform stream that emits data after a newline delimiter is received.
* @summary To use the `Readline` parser, provide a delimiter (defaults to `\n`). Data is emitted as string controllable by the `encoding` option (defaults to `utf8`).
*/
class ReadlineParser extends parser_delimiter_1.DelimiterParser {
constructor(options) {
const opts = {
delimiter: Buffer.from('\n', 'utf8'),
encoding: 'utf8',
...options,
};
if (typeof opts.delimiter === 'string') {
opts.delimiter = Buffer.from(opts.delimiter, opts.encoding);
}
super(opts);
}
}
exports.ReadlineParser = ReadlineParser;

View File

@@ -0,0 +1,28 @@
{
"name": "@serialport/parser-readline",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"version": "11.0.0",
"dependencies": {
"@serialport/parser-delimiter": "11.0.0"
},
"engines": {
"node": ">=12.0.0"
},
"publishConfig": {
"access": "public"
},
"license": "MIT",
"scripts": {
"build": "tsc --build tsconfig-build.json"
},
"repository": {
"type": "git",
"url": "git://github.com/serialport/node-serialport.git"
},
"funding": "https://opencollective.com/serialport/donate",
"devDependencies": {
"typescript": "5.0.4"
},
"gitHead": "6a8202cd947c87ac70c9f3c84d60fe4b5f5d70a9"
}

113
node_modules/@serialport/bindings-cpp/package.json generated vendored Normal file
View File

@@ -0,0 +1,113 @@
{
"name": "@serialport/bindings-cpp",
"description": "SerialPort Hardware bindings for node serialport written in c++",
"version": "12.0.1",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"keywords": [
"serialport-binding",
"COM",
"com port",
"hardware",
"iot",
"modem",
"serial port",
"serial",
"serialport",
"tty",
"UART"
],
"dependencies": {
"@serialport/bindings-interface": "1.2.2",
"@serialport/parser-readline": "11.0.0",
"debug": "4.3.4",
"node-addon-api": "7.0.0",
"node-gyp-build": "4.6.0"
},
"devDependencies": {
"@semantic-release/exec": "6.0.3",
"@serialport/binding-mock": "10.2.2",
"@types/chai": "4.3.5",
"@types/chai-subset": "1.3.3",
"@types/debug": "4.1.8",
"@types/mocha": "10.0.1",
"@types/node": "18.16.20",
"@typescript-eslint/eslint-plugin": "6.1.0",
"@typescript-eslint/parser": "6.1.0",
"cc": "3.0.1",
"chai": "4.3.7",
"chai-subset": "1.6.0",
"esbuild": "0.18.15",
"esbuild-register": "3.4.2",
"eslint": "8.45.0",
"mocha": "10.2.0",
"node-abi": "3.45.0",
"node-gyp": "9.4.0",
"nyc": "15.1.0",
"prebuildify": "5.0.1",
"prebuildify-cross": "5.0.0",
"semantic-release": "21.0.7",
"shx": "0.3.4",
"sinon": "15.2.0",
"typescript": "5.1.6"
},
"engines": {
"node": ">=16.0.0"
},
"scripts": {
"build": "rm -rf dist && tsc -p tsconfig-build.json",
"install": "node-gyp-build",
"prebuildify": "prebuildify --napi --target 14.0.0 --force --strip --verbose",
"prebuildify-cross": "prebuildify-cross --napi --target 14.0.0 --force --strip --verbose",
"rebuild": "node-gyp rebuild",
"format": "eslint lib test bin --fix",
"lint": "eslint lib test bin && cc --verbose",
"test": "nyc --reporter lcov --reporter text mocha",
"test:arduino": "TEST_PORT=$(./bin/find-arduino.ts) npm test",
"test:watch": "mocha -w",
"semantic-release": "semantic-release",
"typecheck": "tsc"
},
"publishConfig": {
"access": "public"
},
"license": "MIT",
"gypfile": true,
"cc": {
"filter": [
"legal/copyright",
"build/include"
],
"files": [
"src/*.cpp",
"src/*.h"
],
"linelength": "120"
},
"binary": {
"napi_versions": [
6
]
},
"repository": {
"type": "git",
"url": "https://github.com/serialport/bindings-cpp.git"
},
"funding": "https://opencollective.com/serialport/donate",
"changelog": {
"labels": {
"breaking": ":boom: BREAKING CHANGES :boom:",
"feature-request": "Features",
"bug": "Bug Fixes",
"docs": "Documentation",
"internal": "Chores"
}
},
"mocha": {
"bail": true,
"require": [
"esbuild-register"
],
"spec": "lib/**/*.test.*"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,317 @@
#include "./darwin_list.h"
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
#include <sys/ioctl.h>
#include <IOKit/serial/ioss.h>
#endif
#include <string>
#include <list>
uv_mutex_t list_mutex;
Boolean lockInitialised = FALSE;
Napi::Value List(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// callback
if (!info[0].IsFunction()) {
Napi::TypeError::New(env, "First argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[0].As<Napi::Function>();
ListBaton* baton = new ListBaton(callback);
snprintf(baton->errorString, sizeof(baton->errorString), "");
baton->Queue();
return env.Undefined();
}
void setIfNotEmpty(Napi::Object item, std::string key, const char *value) {
Napi::Env env = item.Env();
Napi::String v8key = Napi::String::New(env, key);
if (strlen(value) > 0) {
(item).Set(v8key, Napi::String::New(env, value));
} else {
(item).Set(v8key, env.Undefined());
}
}
// Function prototypes
static kern_return_t FindModems(io_iterator_t *matchingServices);
static io_service_t GetUsbDevice(io_service_t service);
static stDeviceListItem* GetSerialDevices();
static kern_return_t FindModems(io_iterator_t *matchingServices) {
kern_return_t kernResult;
CFMutableDictionaryRef classesToMatch;
classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
if (classesToMatch != NULL) {
CFDictionarySetValue(classesToMatch,
CFSTR(kIOSerialBSDTypeKey),
CFSTR(kIOSerialBSDAllTypes));
}
kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices);
return kernResult;
}
static io_service_t GetUsbDevice(io_service_t service) {
IOReturn status;
io_iterator_t iterator = 0;
io_service_t device = 0;
if (!service) {
return device;
}
status = IORegistryEntryCreateIterator(service,
kIOServicePlane,
(kIORegistryIterateParents | kIORegistryIterateRecursively),
&iterator);
if (status == kIOReturnSuccess) {
io_service_t currentService;
while ((currentService = IOIteratorNext(iterator)) && device == 0) {
io_name_t serviceName;
status = IORegistryEntryGetNameInPlane(currentService, kIOServicePlane, serviceName);
if (status == kIOReturnSuccess && IOObjectConformsTo(currentService, kIOUSBDeviceClassName)) {
device = currentService;
} else {
// Release the service object which is no longer needed
(void) IOObjectRelease(currentService);
}
}
// Release the iterator
(void) IOObjectRelease(iterator);
}
return device;
}
static void ExtractUsbInformation(stSerialDevice *serialDevice, IOUSBDeviceInterface **deviceInterface) {
kern_return_t kernResult;
UInt32 locationID;
kernResult = (*deviceInterface)->GetLocationID(deviceInterface, &locationID);
if (KERN_SUCCESS == kernResult) {
snprintf(serialDevice->locationId, sizeof(serialDevice->locationId), "%08x", locationID);
}
UInt16 vendorID;
kernResult = (*deviceInterface)->GetDeviceVendor(deviceInterface, &vendorID);
if (KERN_SUCCESS == kernResult) {
snprintf(serialDevice->vendorId, sizeof(serialDevice->vendorId), "%04x", vendorID);
}
UInt16 productID;
kernResult = (*deviceInterface)->GetDeviceProduct(deviceInterface, &productID);
if (KERN_SUCCESS == kernResult) {
snprintf(serialDevice->productId, sizeof(serialDevice->productId), "%04x", productID);
}
}
static stDeviceListItem* GetSerialDevices() {
char bsdPath[MAXPATHLEN];
io_iterator_t serialPortIterator;
FindModems(&serialPortIterator);
kern_return_t kernResult = KERN_FAILURE;
Boolean modemFound = false;
// Initialize the returned path
*bsdPath = '\0';
stDeviceListItem* devices = NULL;
stDeviceListItem* lastDevice = NULL;
int length = 0;
io_service_t modemService;
while ((modemService = IOIteratorNext(serialPortIterator))) {
CFTypeRef bsdPathAsCFString;
bsdPathAsCFString = IORegistryEntrySearchCFProperty(
modemService,
kIOServicePlane,
CFSTR(kIODialinDeviceKey),
kCFAllocatorDefault,
kIORegistryIterateRecursively);
if (bsdPathAsCFString) {
Boolean result;
// Convert the path from a CFString to a C (NUL-terminated)
result = CFStringGetCString((CFStringRef) bsdPathAsCFString,
bsdPath,
sizeof(bsdPath),
kCFStringEncodingUTF8);
CFRelease(bsdPathAsCFString);
if (result) {
stDeviceListItem *deviceListItem = reinterpret_cast<stDeviceListItem*>( malloc(sizeof(stDeviceListItem)));
stSerialDevice *serialDevice = &(deviceListItem->value);
snprintf(serialDevice->port, sizeof(serialDevice->port), "%s", bsdPath);
memset(serialDevice->locationId, 0, sizeof(serialDevice->locationId));
memset(serialDevice->vendorId, 0, sizeof(serialDevice->vendorId));
memset(serialDevice->productId, 0, sizeof(serialDevice->productId));
serialDevice->manufacturer[0] = '\0';
serialDevice->serialNumber[0] = '\0';
deviceListItem->next = NULL;
deviceListItem->length = &length;
if (devices == NULL) {
devices = deviceListItem;
} else {
lastDevice->next = deviceListItem;
}
lastDevice = deviceListItem;
length++;
modemFound = true;
kernResult = KERN_SUCCESS;
uv_mutex_lock(&list_mutex);
io_service_t device = GetUsbDevice(modemService);
if (device) {
CFStringRef manufacturerAsCFString = (CFStringRef) IORegistryEntryCreateCFProperty(device,
CFSTR(kUSBVendorString),
kCFAllocatorDefault,
0);
if (manufacturerAsCFString) {
Boolean result;
char manufacturer[MAXPATHLEN];
// Convert from a CFString to a C (NUL-terminated)
result = CFStringGetCString(manufacturerAsCFString,
manufacturer,
sizeof(manufacturer),
kCFStringEncodingUTF8);
if (result) {
snprintf(serialDevice->manufacturer, sizeof(serialDevice->manufacturer), "%s", manufacturer);
}
CFRelease(manufacturerAsCFString);
}
CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(device,
kIOServicePlane,
CFSTR(kUSBSerialNumberString),
kCFAllocatorDefault,
kIORegistryIterateRecursively);
if (serialNumberAsCFString) {
Boolean result;
char serialNumber[MAXPATHLEN];
// Convert from a CFString to a C (NUL-terminated)
result = CFStringGetCString(serialNumberAsCFString,
serialNumber,
sizeof(serialNumber),
kCFStringEncodingUTF8);
if (result) {
snprintf(serialDevice->serialNumber, sizeof(serialDevice->serialNumber), "%s", serialNumber);
}
CFRelease(serialNumberAsCFString);
}
IOCFPlugInInterface **plugInInterface = NULL;
SInt32 score;
HRESULT res;
IOUSBDeviceInterface **deviceInterface = NULL;
kernResult = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
&plugInInterface, &score);
if ((kIOReturnSuccess == kernResult) && plugInInterface) {
// Use the plugin interface to retrieve the device interface.
res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
reinterpret_cast<LPVOID*> (&deviceInterface));
// Now done with the plugin interface.
(*plugInInterface)->Release(plugInInterface);
if (!res && deviceInterface != NULL) {
// Extract the desired Information
ExtractUsbInformation(serialDevice, deviceInterface);
// Release the Interface
(*deviceInterface)->Release(deviceInterface);
}
}
// Release the device
(void) IOObjectRelease(device);
}
uv_mutex_unlock(&list_mutex);
}
}
// Release the io_service_t now that we are done with it.
(void) IOObjectRelease(modemService);
}
IOObjectRelease(serialPortIterator); // Release the iterator.
return devices;
}
void ListBaton::Execute() {
if (!lockInitialised) {
uv_mutex_init(&list_mutex);
lockInitialised = TRUE;
}
stDeviceListItem* devices = GetSerialDevices();
if (devices != NULL && *(devices->length) > 0) {
stDeviceListItem* next = devices;
for (int i = 0, len = *(devices->length); i < len; i++) {
stSerialDevice device = (* next).value;
ListResultItem* resultItem = new ListResultItem();
resultItem->path = device.port;
if (*device.locationId) {
resultItem->locationId = device.locationId;
}
if (*device.vendorId) {
resultItem->vendorId = device.vendorId;
}
if (*device.productId) {
resultItem->productId = device.productId;
}
if (*device.manufacturer) {
resultItem->manufacturer = device.manufacturer;
}
if (*device.serialNumber) {
resultItem->serialNumber = device.serialNumber;
}
results.push_back(resultItem);
stDeviceListItem* current = next;
if (next->next != NULL) {
next = next->next;
}
free(current);
}
}
}

View File

@@ -0,0 +1,68 @@
#ifndef PACKAGES_SERIALPORT_SRC_DARWIN_LIST_H_
#define PACKAGES_SERIALPORT_SRC_DARWIN_LIST_H_
#include <sys/param.h> // For MAXPATHLEN
#include <napi.h>
#include <uv.h>
#include <list>
#include <string>
#define ERROR_STRING_SIZE 1088
Napi::Value List(const Napi::CallbackInfo& info);
void setIfNotEmpty(Napi::Object item, std::string key, const char *value);
struct ListResultItem {
std::string path;
std::string manufacturer;
std::string serialNumber;
std::string pnpId;
std::string locationId;
std::string vendorId;
std::string productId;
};
struct ListBaton : public Napi::AsyncWorker {
ListBaton(Napi::Function& callback) : Napi::AsyncWorker(callback, "node-serialport:ListBaton"),
errorString() {}
std::list<ListResultItem*> results;
char errorString[ERROR_STRING_SIZE];
void Execute() override;
void OnOK() override {
Napi::Env env = Env();
Napi::HandleScope scope(env);
Napi::Array result = Napi::Array::New(env);
int i = 0;
for (std::list<ListResultItem*>::iterator it = results.begin(); it != results.end(); ++it, i++) {
Napi::Object item = Napi::Object::New(env);
setIfNotEmpty(item, "path", (*it)->path.c_str());
setIfNotEmpty(item, "manufacturer", (*it)->manufacturer.c_str());
setIfNotEmpty(item, "serialNumber", (*it)->serialNumber.c_str());
setIfNotEmpty(item, "pnpId", (*it)->pnpId.c_str());
setIfNotEmpty(item, "locationId", (*it)->locationId.c_str());
setIfNotEmpty(item, "vendorId", (*it)->vendorId.c_str());
setIfNotEmpty(item, "productId", (*it)->productId.c_str());
(result).Set(i, item);
}
Callback().Call({env.Null(), result});
}
};
typedef struct SerialDevice {
char port[MAXPATHLEN];
char locationId[MAXPATHLEN];
char vendorId[MAXPATHLEN];
char productId[MAXPATHLEN];
char manufacturer[MAXPATHLEN];
char serialNumber[MAXPATHLEN];
} stSerialDevice;
typedef struct DeviceListItem {
struct SerialDevice value;
struct DeviceListItem *next;
int* length;
} stDeviceListItem;
#endif // PACKAGES_SERIALPORT_SRC_DARWIN_LIST_H_

166
node_modules/@serialport/bindings-cpp/src/poller.cpp generated vendored Normal file
View File

@@ -0,0 +1,166 @@
#include <napi.h>
#include <uv.h>
#include "./poller.h"
Poller::Poller (const Napi::CallbackInfo &info) : Napi::ObjectWrap<Poller>(info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be an int").ThrowAsJavaScriptException();
return;
}
this->fd = info[0].As<Napi::Number>().Int32Value();
// callback
if (!info[1].IsFunction()) {
Napi::TypeError::New(env, "Second argument must be a function").ThrowAsJavaScriptException();
return;
}
this->callback = Napi::Persistent(info[1].As<Napi::Function>());
this->poll_handle = new uv_poll_t();
memset(this->poll_handle, 0, sizeof(uv_poll_t));
poll_handle->data = this;
int status = uv_poll_init(uv_default_loop(), poll_handle, fd);
if (0 != status) {
Napi::Error::New(env, uv_strerror(status)).ThrowAsJavaScriptException();
return;
}
uv_poll_init_success = true;
}
Poller::~Poller() {
// if we call uv_poll_stop after uv_poll_init failed we segfault
if (uv_poll_init_success) {
uv_poll_stop(poll_handle);
uv_unref(reinterpret_cast<uv_handle_t*> (poll_handle));
uv_close(reinterpret_cast<uv_handle_t*> (poll_handle), Poller::onClose);
} else {
delete poll_handle;
}
return;
}
void Poller::onClose(uv_handle_t* poll_handle) {
// fprintf(stdout, "~Poller is closed\n");
delete poll_handle;
}
// Events can be UV_READABLE | UV_WRITABLE | UV_DISCONNECT
void Poller::poll(Napi::Env env, int events) {
Napi::HandleScope scope(env);
// fprintf(stdout, "Poller:poll for %d\n", events);
this->events = this->events | events;
int status = uv_poll_start(this->poll_handle, events, Poller::onData);
if (0 != status) {
Napi::Error::New(env, uv_strerror(status)).ThrowAsJavaScriptException();
}
return;
}
void Poller::stop(Napi::Env env) {
Napi::HandleScope scope(env);
int status = uv_poll_stop(this->poll_handle);
if (0 != status) {
Napi::Error::New(env, uv_strerror(status)).ThrowAsJavaScriptException();
}
return;
}
int Poller::_stop() {
return uv_poll_stop(poll_handle);
}
void Poller::onData(uv_poll_t* handle, int status, int events) {
Poller* obj = static_cast<Poller*>(handle->data);
Napi::Env env = obj->Env();
Napi::HandleScope scope(env);
// if Error
if (0 != status) {
// fprintf(stdout, "OnData Error status=%s events=%d\n", uv_strerror(status), events);
obj->_stop(); // doesn't matter if this errors
obj->callback.Call({Napi::Error::New(env, uv_strerror(status)).Value(), env.Undefined()});
} else {
// fprintf(stdout, "OnData status=%d events=%d subscribed=%d\n", status, events, obj->events);
// remove triggered events from the poll
int newEvents = obj->events & ~events;
obj->poll(env, newEvents);
obj->callback.Call({env.Null(), Napi::Number::New(env, events)});
}
}
Napi::Object Poller::Init(Napi::Env env, Napi::Object exports) {
Napi::Function func = DefineClass(env, "Poller", {
StaticMethod<&Poller::New>("New"),
InstanceMethod<&Poller::poll>("poll"),
InstanceMethod<&Poller::stop>("stop"),
InstanceMethod<&Poller::destroy>("destroy"),
});
Napi::FunctionReference* constructor = new Napi::FunctionReference();
*constructor = Napi::Persistent(func);
exports.Set("Poller", func);
env.SetInstanceData<Napi::FunctionReference>(constructor);
return exports;
}
Napi::Value Poller::New(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "fd must be an int").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Value fd = info[0];
if (!info[1].IsFunction()) {
Napi::TypeError::New(env, "cb must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[1].As<Napi::Function>();
Napi::FunctionReference* constructor = info.Env().GetInstanceData<Napi::FunctionReference>();
return constructor->New({fd, callback});
}
Napi::Value Poller::poll(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Poller* obj = this;
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "events must be an int").ThrowAsJavaScriptException();
return env.Null();
}
int events = info[0].As<Napi::Number>().Int32Value();
obj->poll(env, events);
return env.Undefined();
}
Napi::Value Poller::stop(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
this->stop(env);
return env.Undefined();
}
Napi::Value Poller::destroy(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
Poller* obj = this;
// TODO Fix destruction Segfault
obj->Reset();
// delete obj;
return env.Undefined();
}
inline Napi::FunctionReference & Poller::constructor() {
static Napi::FunctionReference my_constructor;
// TODO Check if required
// my_constructor.SuppressDestruct();
return my_constructor;
}

35
node_modules/@serialport/bindings-cpp/src/poller.h generated vendored Normal file
View File

@@ -0,0 +1,35 @@
#ifndef PACKAGES_SERIALPORT_SRC_POLLER_H_
#define PACKAGES_SERIALPORT_SRC_POLLER_H_
#include <napi.h>
#include <uv.h>
class Poller : public Napi::ObjectWrap<Poller> {
public:
static Napi::Object Init(Napi::Env env, Napi::Object exports);
explicit Poller(const Napi::CallbackInfo &info);
static Napi::Value New(const Napi::CallbackInfo& info);
static void onData(uv_poll_t* handle, int status, int events);
static void onClose(uv_handle_t* poll_handle);
~Poller();
private:
int fd;
uv_poll_t* poll_handle = nullptr;
Napi::FunctionReference callback;
bool uv_poll_init_success = false;
// can this be read off of poll_handle?
int events = 0;
void poll(Napi::Env env, int events);
void stop(Napi::Env env);
int _stop();
Napi::Value poll(const Napi::CallbackInfo& info);
Napi::Value stop(const Napi::CallbackInfo& info);
Napi::Value destroy(const Napi::CallbackInfo& info);
static inline Napi::FunctionReference & constructor();
};
#endif // PACKAGES_SERIALPORT_SRC_POLLER_H_

View File

@@ -0,0 +1,342 @@
#include "./serialport.h"
#ifdef __APPLE__
#include "./darwin_list.h"
#endif
#ifdef WIN32
#define strncasecmp strnicmp
#include "./serialport_win.h"
#else
#include "./poller.h"
#endif
Napi::Value getValueFromObject(Napi::Object options, std::string key) {
Napi::String str = Napi::String::New(options.Env(), key);
return (options).Get(str);
}
int getIntFromObject(Napi::Object options, std::string key) {
return getValueFromObject(options, key).ToNumber().Int64Value();
}
bool getBoolFromObject(Napi::Object options, std::string key) {
return getValueFromObject(options, key).ToBoolean().Value();
}
Napi::String getStringFromObj(Napi::Object options, std::string key) {
return getValueFromObject(options, key).ToString();
}
double getDoubleFromObject(Napi::Object options, std::string key) {
return getValueFromObject(options, key).ToNumber().DoubleValue();
}
Napi::Value Open(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// path
if (!info[0].IsString()) {
Napi::TypeError::New(env, "First argument must be a string").ThrowAsJavaScriptException();
return env.Null();
}
std::string path = info[0].ToString().Utf8Value();
// options
if (!info[1].IsObject()) {
Napi::TypeError::New(env, "Second argument must be an object").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Object options = info[1].ToObject();
// callback
if (!info[2].IsFunction()) {
Napi::TypeError::New(env, "Third argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[2].As<Napi::Function>();
OpenBaton* baton = new OpenBaton(callback);
snprintf(baton->path, sizeof(baton->path), "%s", path.c_str());
baton->baudRate = getIntFromObject(options, "baudRate");
baton->dataBits = getIntFromObject(options, "dataBits");
baton->parity = ToParityEnum(getStringFromObj(options, "parity"));
baton->stopBits = ToStopBitEnum(getDoubleFromObject(options, "stopBits"));
baton->rtscts = getBoolFromObject(options, "rtscts");
baton->rtsMode = ToRtsModeEnum(getStringFromObj(options, "rtsMode"));
baton->xon = getBoolFromObject(options, "xon");
baton->xoff = getBoolFromObject(options, "xoff");
baton->xany = getBoolFromObject(options, "xany");
baton->hupcl = getBoolFromObject(options, "hupcl");
baton->lock = getBoolFromObject(options, "lock");
#ifndef WIN32
baton->vmin = getIntFromObject(options, "vmin");
baton->vtime = getIntFromObject(options, "vtime");
#endif
baton->Queue();
return env.Undefined();
}
Napi::Value Update(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// file descriptor
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
int fd = info[0].As<Napi::Number>().Int32Value();
// options
if (!info[1].IsObject()) {
Napi::TypeError::New(env, "Second argument must be an object").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Object options = info[1].ToObject();
if (!(options).Has("baudRate")) {
Napi::TypeError::New(env, "\"baudRate\" must be set on options object").ThrowAsJavaScriptException();
return env.Null();
}
// callback
if (!info[2].IsFunction()) {
Napi::TypeError::New(env, "Third argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[2].As<Napi::Function>();
ConnectionOptionsBaton* baton = new ConnectionOptionsBaton(callback);
baton->fd = fd;
baton->baudRate = getIntFromObject(options, "baudRate");
baton->Queue();
return env.Undefined();
}
Napi::Value Close(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// file descriptor
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
// callback
if (!info[1].IsFunction()) {
Napi::TypeError::New(env, "Second argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[1].As<Napi::Function>();
CloseBaton* baton = new CloseBaton(callback);
baton->fd = info[0].ToNumber().Int64Value();;
baton->Queue();
return env.Undefined();
}
Napi::Value Flush(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// file descriptor
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
int fd = info[0].As<Napi::Number>().Int32Value();
// callback
if (!info[1].IsFunction()) {
Napi::TypeError::New(env, "Second argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[1].As<Napi::Function>();
FlushBaton* baton = new FlushBaton(callback);
baton->fd = fd;
baton->Queue();
return env.Undefined();
}
Napi::Value Set(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// file descriptor
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
int fd = info[0].As<Napi::Number>().Int32Value();
// options
if (!info[1].IsObject()) {
Napi::TypeError::New(env, "Second argument must be an object").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Object options = info[1].ToObject();
// callback
if (!info[2].IsFunction()) {
Napi::TypeError::New(env, "Third argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[2].As<Napi::Function>();
SetBaton* baton = new SetBaton(callback);
baton->fd = fd;
baton->brk = getBoolFromObject(options, "brk");
baton->rts = getBoolFromObject(options, "rts");
baton->cts = getBoolFromObject(options, "cts");
baton->dtr = getBoolFromObject(options, "dtr");
baton->dsr = getBoolFromObject(options, "dsr");
baton->lowLatency = getBoolFromObject(options, "lowLatency");
baton->Queue();
return env.Undefined();
}
Napi::Value Get(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// file descriptor
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
int fd = info[0].As<Napi::Number>().Int32Value();
// callback
if (!info[1].IsFunction()) {
Napi::TypeError::New(env, "Second argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[1].As<Napi::Function>();
GetBaton* baton = new GetBaton(callback);
baton->fd = fd;
baton->cts = false;
baton->dsr = false;
baton->dcd = false;
baton->lowLatency = false;
baton->Queue();
return env.Undefined();
}
Napi::Value GetBaudRate(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// file descriptor
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
int fd = info[0].As<Napi::Number>().Int32Value();
// callback
if (!info[1].IsFunction()) {
Napi::TypeError::New(env, "Second argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[1].As<Napi::Function>();
GetBaudRateBaton* baton = new GetBaudRateBaton(callback);
baton->fd = fd;
baton->baudRate = 0;
baton->Queue();
return env.Undefined();
}
Napi::Value Drain(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// file descriptor
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
int fd = info[0].As<Napi::Number>().Int32Value();
// callback
if (!info[1].IsFunction()) {
Napi::TypeError::New(env, "Second argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[1].As<Napi::Function>();
DrainBaton* baton = new DrainBaton(callback);
baton->fd = fd;
baton->Queue();
return env.Undefined();
}
inline SerialPortParity ToParityEnum(const Napi::String& napistr) {
auto tmp = napistr.Utf8Value();
const char* str = tmp.c_str();
size_t count = strlen(str);
SerialPortParity parity = SERIALPORT_PARITY_NONE;
if (!strncasecmp(str, "none", count)) {
parity = SERIALPORT_PARITY_NONE;
} else if (!strncasecmp(str, "even", count)) {
parity = SERIALPORT_PARITY_EVEN;
} else if (!strncasecmp(str, "mark", count)) {
parity = SERIALPORT_PARITY_MARK;
} else if (!strncasecmp(str, "odd", count)) {
parity = SERIALPORT_PARITY_ODD;
} else if (!strncasecmp(str, "space", count)) {
parity = SERIALPORT_PARITY_SPACE;
}
return parity;
}
inline SerialPortStopBits ToStopBitEnum(double stopBits) {
if (stopBits > 1.4 && stopBits < 1.6) {
return SERIALPORT_STOPBITS_ONE_FIVE;
}
if (stopBits == 2) {
return SERIALPORT_STOPBITS_TWO;
}
return SERIALPORT_STOPBITS_ONE;
}
inline SerialPortRtsMode ToRtsModeEnum(const Napi::String& napistr) {
auto tmp = napistr.Utf8Value();
const char* str = tmp.c_str();
size_t count = strlen(str);
SerialPortRtsMode mode = SERIALPORT_RTSMODE_HANDSHAKE;
if (!strncasecmp(str, "enable", count)) {
mode = SERIALPORT_RTSMODE_ENABLE;
} else if (!strncasecmp(str, "handshake", count)) {
mode = SERIALPORT_RTSMODE_HANDSHAKE;
} else if (!strncasecmp(str, "toggle", count)) {
mode = SERIALPORT_RTSMODE_TOGGLE;
}
return mode;
}
Napi::Object init(Napi::Env env, Napi::Object exports) {
exports.Set("set", Napi::Function::New(env, Set));
exports.Set("get", Napi::Function::New(env, Get));
exports.Set("getBaudRate", Napi::Function::New(env, GetBaudRate));
exports.Set("open", Napi::Function::New(env, Open));
exports.Set("update", Napi::Function::New(env, Update));
exports.Set("close", Napi::Function::New(env, Close));
exports.Set("flush", Napi::Function::New(env, Flush));
exports.Set("drain", Napi::Function::New(env, Drain));
#ifdef __APPLE__
exports.Set("list", Napi::Function::New(env, List));
#endif
#ifdef WIN32
exports.Set("write", Napi::Function::New(env, Write));
exports.Set("read", Napi::Function::New(env, Read));
exports.Set("list", Napi::Function::New(env, List));
#else
Poller::Init(env, exports);
#endif
return exports;
}
NODE_API_MODULE(serialport, init);

202
node_modules/@serialport/bindings-cpp/src/serialport.h generated vendored Normal file
View File

@@ -0,0 +1,202 @@
#ifndef PACKAGES_SERIALPORT_SRC_SERIALPORT_H_
#define PACKAGES_SERIALPORT_SRC_SERIALPORT_H_
// Workaround for electron 11 abi issue https://github.com/serialport/node-serialport/issues/2191
// TODO Replace with ABI stable runtime check (per https://github.com/serialport/node-serialport/pull/2305#discussion_r697542996)
#include <node_version.h>
#if CHECK_NODE_API_MODULE_VERSION && NODE_API_MODULE_VERSION == 85
#define V8_REVERSE_JSARGS
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <napi.h>
#include <string>
#define ERROR_STRING_SIZE 1088
Napi::Value Open(const Napi::CallbackInfo& info);
Napi::Value Update(const Napi::CallbackInfo& info);
Napi::Value Close(const Napi::CallbackInfo& info);
Napi::Value Flush(const Napi::CallbackInfo& info);
Napi::Value Set(const Napi::CallbackInfo& info);
Napi::Value Get(const Napi::CallbackInfo& info);
Napi::Value GetBaudRate(const Napi::CallbackInfo& info);
Napi::Value Drain(const Napi::CallbackInfo& info);
enum SerialPortParity {
SERIALPORT_PARITY_NONE = 1,
SERIALPORT_PARITY_MARK = 2,
SERIALPORT_PARITY_EVEN = 3,
SERIALPORT_PARITY_ODD = 4,
SERIALPORT_PARITY_SPACE = 5
};
enum SerialPortStopBits {
SERIALPORT_STOPBITS_ONE = 1,
SERIALPORT_STOPBITS_ONE_FIVE = 2,
SERIALPORT_STOPBITS_TWO = 3
};
enum SerialPortRtsMode {
SERIALPORT_RTSMODE_ENABLE = 1,
SERIALPORT_RTSMODE_HANDSHAKE = 2,
SERIALPORT_RTSMODE_TOGGLE = 3
};
SerialPortParity ToParityEnum(const Napi::String& str);
SerialPortStopBits ToStopBitEnum(double stopBits);
SerialPortRtsMode ToRtsModeEnum(const Napi::String& str);
struct OpenBaton : public Napi::AsyncWorker {
OpenBaton(Napi::Function& callback) : Napi::AsyncWorker(callback, "node-serialport:OpenBaton"),
errorString(), path() {}
char errorString[ERROR_STRING_SIZE];
char path[1024];
int fd = 0;
int result = 0;
int baudRate = 0;
int dataBits = 0;
bool rtscts = false;
bool xon = false;
bool xoff = false;
bool xany = false;
bool hupcl = false;
bool lock = false;
SerialPortParity parity;
SerialPortStopBits stopBits;
SerialPortRtsMode rtsMode;
#ifndef WIN32
uint8_t vmin = 0;
uint8_t vtime = 0;
#endif
void Execute() override;
void OnOK() override {
Napi::Env env = Env();
Napi::HandleScope scope(env);
Callback().Call({env.Null(), Napi::Number::New(env, result)});
}
};
struct ConnectionOptions {
ConnectionOptions() : errorString() {}
char errorString[ERROR_STRING_SIZE];
int fd = 0;
int baudRate = 0;
};
struct ConnectionOptionsBaton : ConnectionOptions , Napi::AsyncWorker {
ConnectionOptionsBaton(Napi::Function& callback) : ConnectionOptions() , Napi::AsyncWorker(callback, "node-serialport:ConnectionOptionsBaton") {}
void Execute() override;
void OnOK() override {
Napi::Env env = Env();
Napi::HandleScope scope(env);
Callback().Call({env.Null()});
}
};
struct SetBaton : public Napi::AsyncWorker {
SetBaton(Napi::Function& callback) : Napi::AsyncWorker(callback, "node-serialport:SetBaton"),
errorString() {}
int fd = 0;
int result = 0;
char errorString[ERROR_STRING_SIZE];
bool rts = false;
bool cts = false;
bool dtr = false;
bool dsr = false;
bool brk = false;
bool lowLatency = false;
void Execute() override;
void OnOK() override {
Napi::Env env = Env();
Napi::HandleScope scope(env);
Callback().Call({env.Null()});
}
};
struct GetBaton : public Napi::AsyncWorker {
GetBaton(Napi::Function& callback) : Napi::AsyncWorker(callback, "node-serialport:GetBaton"),
errorString() {}
int fd = 0;
char errorString[ERROR_STRING_SIZE];
bool cts = false;
bool dsr = false;
bool dcd = false;
bool lowLatency = false;
void Execute() override;
void OnOK() override {
Napi::Env env = Env();
Napi::HandleScope scope(env);
Napi::Object results = Napi::Object::New(env);
results.Set("cts", cts);
results.Set("dsr", dsr);
results.Set("dcd", dcd);
results.Set("lowLatency", lowLatency);
Callback().Call({env.Null(), results});
}
};
struct GetBaudRateBaton : public Napi::AsyncWorker {
GetBaudRateBaton(Napi::Function& callback) : Napi::AsyncWorker(callback, "node-serialport:GetBaudRateBaton"),
errorString() {}
int fd = 0;
char errorString[ERROR_STRING_SIZE];
int baudRate = 0;
void Execute() override;
void OnOK() override {
Napi::Env env = Env();
Napi::HandleScope scope(env);
Napi::Object results = Napi::Object::New(env);
(results).Set(Napi::String::New(env, "baudRate"), Napi::Number::New(env, baudRate));
Callback().Call({env.Null(),results});
}
};
struct VoidBaton : public Napi::AsyncWorker {
VoidBaton(Napi::Function& callback, const char *resource_name) : Napi::AsyncWorker(callback, resource_name),
errorString() {}
int fd = 0;
char errorString[ERROR_STRING_SIZE];
void OnOK() override {
Napi::Env env = Env();
Napi::HandleScope scope(env);
Callback().Call({env.Null()});
}
};
struct CloseBaton : VoidBaton {
CloseBaton(Napi::Function& callback) : VoidBaton(callback, "node-serialport:CloseBaton") {}
void Execute() override;
};
struct DrainBaton : VoidBaton {
DrainBaton(Napi::Function& callback) : VoidBaton(callback, "node-serialport:DrainBaton") {}
void Execute() override;
};
struct FlushBaton : VoidBaton {
FlushBaton(Napi::Function& callback) : VoidBaton(callback, "node-serialport:FlushBaton") {}
void Execute() override;
};
int setup(int fd, OpenBaton *data);
int setBaudRate(ConnectionOptions *data);
#endif // PACKAGES_SERIALPORT_SRC_SERIALPORT_H_

View File

@@ -0,0 +1,76 @@
#if defined(__linux__)
#include <sys/ioctl.h>
#include <asm/ioctls.h>
#include <asm/termbits.h>
#include <linux/serial.h>
// Uses the termios2 interface to set nonstandard baud rates
int linuxSetCustomBaudRate(const int fd, const unsigned int baudrate) {
struct termios2 t;
if (ioctl(fd, TCGETS2, &t) == -1) {
return -1;
}
t.c_cflag &= ~CBAUD;
t.c_cflag |= BOTHER;
t.c_ospeed = t.c_ispeed = baudrate;
if (ioctl(fd, TCSETS2, &t) == -1) {
return -2;
}
return 0;
}
// Uses termios2 interface to retrieve system reported baud rate
int linuxGetSystemBaudRate(const int fd, int* const outbaud) {
struct termios2 t;
if (ioctl(fd, TCGETS2, &t) == -1) {
return -1;
}
*outbaud = static_cast<int>(t.c_ospeed);
return 0;
}
int linuxSetLowLatencyMode(const int fd, const bool enable) {
struct serial_struct ss;
if (ioctl(fd, TIOCGSERIAL, &ss) == -1) {
return -1;
}
if ((ss.flags & ASYNC_LOW_LATENCY) == enable) {
return 0;
}
if (enable) {
ss.flags |= ASYNC_LOW_LATENCY;
} else {
ss.flags &= ~ASYNC_LOW_LATENCY;
}
if (ioctl(fd, TIOCSSERIAL, &ss) == -1) {
return -2;
}
return 0;
}
int linuxGetLowLatencyMode(const int fd, bool* const enabled) {
struct serial_struct ss;
if (ioctl(fd, TIOCGSERIAL, &ss) == -1) {
return -1;
}
*enabled = ss.flags & ASYNC_LOW_LATENCY;
return 0;
}
#endif

10
node_modules/@serialport/bindings-cpp/src/serialport_linux.h generated vendored Executable file
View File

@@ -0,0 +1,10 @@
#ifndef PACKAGES_SERIALPORT_SRC_SERIALPORT_LINUX_H_
#define PACKAGES_SERIALPORT_SRC_SERIALPORT_LINUX_H_
int linuxSetCustomBaudRate(const int fd, const unsigned int baudrate);
int linuxGetSystemBaudRate(const int fd, int* const outbaud);
int linuxSetLowLatencyMode(const int fd, const bool enable);
int linuxGetLowLatencyMode(const int fd, bool* const enabled);
#endif // PACKAGES_SERIALPORT_SRC_SERIALPORT_LINUX_H_

View File

@@ -0,0 +1,482 @@
#include "serialport_unix.h"
#include "serialport.h"
#include <sys/file.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#ifdef __APPLE__
#include <AvailabilityMacros.h>
#include <sys/param.h>
#endif
#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
#include <sys/ioctl.h>
#include <IOKit/serial/ioss.h>
#elif defined(__NetBSD__)
#include <sys/ioctl.h>
#elif defined(__OpenBSD__)
#include <sys/ioctl.h>
#elif defined(__linux__)
#include <sys/ioctl.h>
#include <linux/serial.h>
#include "serialport_linux.h"
#endif
int ToStopBitsConstant(SerialPortStopBits stopBits);
int ToBaudConstant(int baudRate) {
switch (baudRate) {
case 0: return B0;
case 50: return B50;
case 75: return B75;
case 110: return B110;
case 134: return B134;
case 150: return B150;
case 200: return B200;
case 300: return B300;
case 600: return B600;
case 1200: return B1200;
case 1800: return B1800;
case 2400: return B2400;
case 4800: return B4800;
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
case 230400: return B230400;
#if defined(__linux__)
case 460800: return B460800;
case 500000: return B500000;
case 576000: return B576000;
case 921600: return B921600;
case 1000000: return B1000000;
case 1152000: return B1152000;
case 1500000: return B1500000;
case 2000000: return B2000000;
case 2500000: return B2500000;
case 3000000: return B3000000;
case 3500000: return B3500000;
case 4000000: return B4000000;
#endif
}
return -1;
}
int ToDataBitsConstant(int dataBits) {
switch (dataBits) {
case 8: default: return CS8;
case 7: return CS7;
case 6: return CS6;
case 5: return CS5;
}
return -1;
}
void OpenBaton::Execute() {
int flags = (O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC | O_SYNC);
int fd = open(path, flags);
if (-1 == fd) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot open %s", strerror(errno), path);
this->SetError(errorString);
return;
}
if (-1 == setup(fd, this)) {
this->SetError(errorString);
close(fd);
return;
}
result = fd;
}
void ConnectionOptionsBaton::Execute() {
// lookup the standard baudrates from the table
int baudRate = ToBaudConstant(this->baudRate);
// get port options
struct termios options;
if (-1 == tcgetattr(fd, &options)) {
snprintf(errorString, sizeof(errorString), "Error: %s setting custom baud rate of %d", strerror(errno), baudRate);
this->SetError(errorString);
return;
}
// If there is a custom baud rate on linux you can do the following trick with B38400
#if defined(__linux__) && defined(ASYNC_SPD_CUST)
if (baudRate == -1) {
int err = linuxSetCustomBaudRate(fd, baudRate);
if (err == -1) {
snprintf(errorString, sizeof(errorString), "Error: %s || while retrieving termios2 info", strerror(errno));
this->SetError(errorString);
return;
} else if (err == -2) {
snprintf(errorString, sizeof(errorString), "Error: %s || while setting custom baud rate of %d", strerror(errno), baudRate);
this->SetError(errorString);
return;
}
return;
}
#endif
// On OS X, starting with Tiger, we can set a custom baud rate with ioctl
#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
if (-1 == baudRate) {
speed_t speed = baudRate;
if (-1 == ioctl(fd, IOSSIOSPEED, &speed)) {
snprintf(errorString, sizeof(errorString), "Error: %s calling ioctl(.., IOSSIOSPEED, %ld )", strerror(errno), speed);
this->SetError(errorString);
return;
} else {
tcflush(fd, TCIOFLUSH);
return;
}
}
#endif
if (-1 == baudRate) {
snprintf(errorString, sizeof(errorString), "Error baud rate of %d is not supported on your platform", baudRate);
this->SetError(errorString);
return;
}
// If we have a good baud rate set it and lets go
cfsetospeed(&options, baudRate);
cfsetispeed(&options, baudRate);
// throw away all the buffered data
tcflush(fd, TCIOFLUSH);
// make the changes now
tcsetattr(fd, TCSANOW, &options);
return;
}
int setup(int fd, OpenBaton *data) {
int dataBits = ToDataBitsConstant(data->dataBits);
if (-1 == dataBits) {
snprintf(data->errorString, sizeof(data->errorString),"Invalid data bits setting %d", data->dataBits);
return -1;
}
// Snow Leopard doesn't have O_CLOEXEC
if (-1 == fcntl(fd, F_SETFD, FD_CLOEXEC)) {
snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot open %s", strerror(errno), data->path);
return -1;
}
// Get port configuration for modification
struct termios options;
tcgetattr(fd, &options);
// IGNPAR: ignore bytes with parity errors
options.c_iflag = IGNPAR;
// ICRNL: map CR to NL (otherwise a CR input on the other computer will not terminate input)
// Future potential option
// options.c_iflag = ICRNL;
// otherwise make device raw (no other input processing)
// Specify data bits
options.c_cflag &= ~CSIZE;
options.c_cflag |= dataBits;
options.c_cflag &= ~(CRTSCTS);
if (data->rtscts) {
options.c_cflag |= CRTSCTS;
// evaluate specific flow control options
}
options.c_iflag &= ~(IXON | IXOFF | IXANY);
if (data->xon) {
options.c_iflag |= IXON;
}
if (data->xoff) {
options.c_iflag |= IXOFF;
}
if (data->xany) {
options.c_iflag |= IXANY;
}
switch (data->parity) {
case SERIALPORT_PARITY_NONE:
options.c_cflag &= ~PARENB;
// options.c_cflag &= ~CSTOPB;
// options.c_cflag &= ~CSIZE;
// options.c_cflag |= CS8;
break;
case SERIALPORT_PARITY_ODD:
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
// options.c_cflag &= ~CSTOPB;
// options.c_cflag &= ~CSIZE;
// options.c_cflag |= CS7;
break;
case SERIALPORT_PARITY_EVEN:
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
// options.c_cflag &= ~CSTOPB;
// options.c_cflag &= ~CSIZE;
// options.c_cflag |= CS7;
break;
default:
snprintf(data->errorString, sizeof(data->errorString), "Invalid parity setting %d", data->parity);
return -1;
}
switch (data->stopBits) {
case SERIALPORT_STOPBITS_ONE:
options.c_cflag &= ~CSTOPB;
break;
case SERIALPORT_STOPBITS_TWO:
options.c_cflag |= CSTOPB;
break;
default:
snprintf(data->errorString, sizeof(data->errorString), "Invalid stop bits setting %d", data->stopBits);
return -1;
}
options.c_cflag |= CLOCAL; // ignore status lines
options.c_cflag |= CREAD; // enable receiver
if (data->hupcl) {
options.c_cflag |= HUPCL; // drop DTR (i.e. hangup) on close
}
// Raw output
options.c_oflag = 0;
// ICANON makes partial lines not readable. It should be optional.
// It works with ICRNL.
options.c_lflag = 0; // ICANON;
options.c_cc[VMIN]= data->vmin;
options.c_cc[VTIME]= data->vtime;
// Note that tcsetattr() returns success if any of the requested changes could be successfully carried out.
// Therefore, when making multiple changes it may be necessary to follow this call with a further call to
// tcgetattr() to check that all changes have been performed successfully.
// This also fails on OSX
tcsetattr(fd, TCSANOW, &options);
if (data->lock) {
if (-1 == flock(fd, LOCK_EX | LOCK_NB)) {
snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot lock port", strerror(errno));
return -1;
}
}
// Copy the connection options into the ConnectionOptionsBaton to set the baud rate
ConnectionOptions* connectionOptions = new ConnectionOptions();
connectionOptions->fd = fd;
connectionOptions->baudRate = data->baudRate;
if (-1 == setBaudRate(connectionOptions)) {
strncpy(data->errorString, connectionOptions->errorString, sizeof(data->errorString));
delete(connectionOptions);
return -1;
}
delete(connectionOptions);
// flush all unread and wrote data up to this point because it could have been received or sent with bad settings
// Not needed since setBaudRate does this for us
// tcflush(fd, TCIOFLUSH);
return 1;
}
int setBaudRate(ConnectionOptions *data) {
// lookup the standard baudrates from the table
int baudRate = ToBaudConstant(data->baudRate);
int fd = data->fd;
// get port options
struct termios options;
if (-1 == tcgetattr(fd, &options)) {
snprintf(data->errorString, sizeof(data->errorString),
"Error: %s setting custom baud rate of %d", strerror(errno), data->baudRate);
return -1;
}
// If there is a custom baud rate on linux you can do the following trick with B38400
#if defined(__linux__) && defined(ASYNC_SPD_CUST)
if (baudRate == -1) {
int err = linuxSetCustomBaudRate(fd, data->baudRate);
if (err == -1) {
snprintf(data->errorString, sizeof(data->errorString),
"Error: %s || while retrieving termios2 info", strerror(errno));
return -1;
} else if (err == -2) {
snprintf(data->errorString, sizeof(data->errorString),
"Error: %s || while setting custom baud rate of %d", strerror(errno), data->baudRate);
return -1;
}
return 1;
}
#endif
// On OS X, starting with Tiger, we can set a custom baud rate with ioctl
#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
if (-1 == baudRate) {
speed_t speed = data->baudRate;
if (-1 == ioctl(fd, IOSSIOSPEED, &speed)) {
snprintf(data->errorString, sizeof(data->errorString),
"Error: %s calling ioctl(.., IOSSIOSPEED, %ld )", strerror(errno), speed);
return -1;
} else {
tcflush(fd, TCIOFLUSH);
return 1;
}
}
#endif
if (-1 == baudRate) {
snprintf(data->errorString, sizeof(data->errorString), "Error baud rate of %d is not supported on your platform", data->baudRate);
return -1;
}
// If we have a good baud rate set it and lets go
cfsetospeed(&options, baudRate);
cfsetispeed(&options, baudRate);
// throw away all the buffered data
tcflush(fd, TCIOFLUSH);
// make the changes now
tcsetattr(fd, TCSANOW, &options);
return 1;
}
void CloseBaton::Execute() {
if (-1 == close(fd)) {
snprintf(errorString, sizeof(errorString), "Error: %s, unable to close fd %d", strerror(errno), fd);
this->SetError(errorString);
}
}
void SetBaton::Execute() {
int bits;
ioctl(fd, TIOCMGET, &bits);
bits &= ~(TIOCM_RTS | TIOCM_CTS | TIOCM_DTR | TIOCM_DSR);
if (rts) {
bits |= TIOCM_RTS;
}
if (cts) {
bits |= TIOCM_CTS;
}
if (dtr) {
bits |= TIOCM_DTR;
}
if (dsr) {
bits |= TIOCM_DSR;
}
int result = 0;
if (brk) {
result = ioctl(fd, TIOCSBRK, NULL);
} else {
result = ioctl(fd, TIOCCBRK, NULL);
}
if (-1 == result) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot set", strerror(errno));
this->SetError(errorString);
return;
}
if (-1 == ioctl(fd, TIOCMSET, &bits)) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot set", strerror(errno));
this->SetError(errorString);
return;
}
#if defined(__linux__)
int err = linuxSetLowLatencyMode(fd, lowLatency);
// Only report errors when the lowLatency is being set to true. Attempting to set as false can error, since the default is false
if (lowLatency) {
if (err == -1) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot get low latency", strerror(errno));
return;
} else if(err == -2) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot set low latency", strerror(errno));
return;
}
}
#endif
}
void GetBaton::Execute() {
int bits;
if (-1 == ioctl(fd, TIOCMGET, &bits)) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot get", strerror(errno));
this->SetError(errorString);
return;
}
cts = bits & TIOCM_CTS;
dsr = bits & TIOCM_DSR;
dcd = bits & TIOCM_CD;
#if defined(__linux__) && defined(ASYNC_LOW_LATENCY)
bool lowlatency = false;
// Try to get low latency info, but we don't care if fails (a failure state will still return lowlatency = false)
linuxGetLowLatencyMode(fd, &lowlatency);
lowLatency = lowlatency;
#else
lowLatency = false;
#endif
}
void GetBaudRateBaton::Execute() {
int outbaud = -1;
#if defined(__linux__) && defined(ASYNC_SPD_CUST)
if (-1 == linuxGetSystemBaudRate(fd, &outbaud)) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot get baud rate", strerror(errno));
this->SetError(errorString);
return;
}
#else
snprintf(errorString, sizeof(errorString), "Error: System baud rate check not implemented on this platform");
this->SetError(errorString);
return;
#endif
baudRate = outbaud;
}
void FlushBaton::Execute() {
if (-1 == tcflush(fd, TCIOFLUSH)) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot flush", strerror(errno));
this->SetError(errorString);
return;
}
}
void DrainBaton::Execute() {
if (-1 == tcdrain(fd)) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot drain", strerror(errno));
this->SetError(errorString);
return;
}
}

View File

@@ -0,0 +1,7 @@
#ifndef PACKAGES_SERIALPORT_SRC_SERIALPORT_UNIX_H_
#define PACKAGES_SERIALPORT_SRC_SERIALPORT_UNIX_H_
int ToBaudConstant(int baudRate);
int ToDataBitsConstant(int dataBits);
#endif // PACKAGES_SERIALPORT_SRC_SERIALPORT_UNIX_H_

View File

@@ -0,0 +1,958 @@
#include "./serialport.h"
#include "./serialport_win.h"
#include <napi.h>
#include <uv.h>
#include <list>
#include <vector>
#include <string.h>
#include <windows.h>
#include <Setupapi.h>
#include <initguid.h>
#include <devpkey.h>
#include <devguid.h>
#include <wchar.h>
#pragma comment(lib, "setupapi.lib")
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
#define MAX_BUFFER_SIZE 1000
// As per https://msdn.microsoft.com/en-us/library/windows/desktop/ms724872(v=vs.85).aspx
#define MAX_REGISTRY_KEY_SIZE 255
// Declare type of pointer to CancelIoEx function
typedef BOOL (WINAPI *CancelIoExType)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
std::list<int> g_closingHandles;
void ErrorCodeToString(const wchar_t* prefix, int errorCode, wchar_t *errorStr) {
switch (errorCode) {
case ERROR_FILE_NOT_FOUND:
_snwprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, L"%ls: File not found", prefix);
break;
case ERROR_INVALID_HANDLE:
_snwprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, L"%ls: Invalid handle", prefix);
break;
case ERROR_ACCESS_DENIED:
_snwprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, L"%ls: Access denied", prefix);
break;
case ERROR_OPERATION_ABORTED:
_snwprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, L"%ls: Operation aborted", prefix);
break;
case ERROR_INVALID_PARAMETER:
_snwprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, L"%ls: The parameter is incorrect %d", prefix, errorCode);
break;
default:
_snwprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, L"%ls: Unknown error code %d", prefix, errorCode);
break;
}
}
void ErrorCodeToString(const char* prefix, int errorCode, char *errorStr) {
switch (errorCode) {
case ERROR_FILE_NOT_FOUND:
_snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: File not found", prefix);
break;
case ERROR_INVALID_HANDLE:
_snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Invalid handle", prefix);
break;
case ERROR_ACCESS_DENIED:
_snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Access denied", prefix);
break;
case ERROR_OPERATION_ABORTED:
_snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Operation aborted", prefix);
break;
case ERROR_INVALID_PARAMETER:
_snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: The parameter is incorrect", prefix);
break;
default:
_snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Unknown error code %d", prefix, errorCode);
break;
}
}
void AsyncCloseCallback(uv_handle_t* handle) {
uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
delete async;
}
void OpenBaton::Execute() {
char originalPath[1024];
strncpy_s(originalPath, sizeof(originalPath), path, _TRUNCATE);
// path is char[1024] but on Windows it has the form "COMx\0" or "COMxx\0"
// We want to prepend "\\\\.\\" to it before we call CreateFile
strncpy(path + 20, path, 10);
strncpy(path, "\\\\.\\", 4);
strncpy(path + 4, path + 20, 10);
int shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
if (lock) {
shareMode = 0;
}
HANDLE file = CreateFile(
path,
GENERIC_READ | GENERIC_WRITE,
shareMode, // dwShareMode 0 Prevents other processes from opening if they request delete, read, or write access
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, // allows for reading and writing at the same time and sets the handle for asynchronous I/O
NULL);
if (file == INVALID_HANDLE_VALUE) {
DWORD errorCode = GetLastError();
char temp[100];
_snprintf_s(temp, sizeof(temp), _TRUNCATE, "Opening %s", originalPath);
ErrorCodeToString(temp, errorCode, errorString);
this->SetError(errorString);
return;
}
DCB dcb = { 0 };
SecureZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(file, &dcb)) {
ErrorCodeToString("Open (GetCommState)", GetLastError(), errorString);
this->SetError(errorString);
CloseHandle(file);
return;
}
if (hupcl) {
dcb.fDtrControl = DTR_CONTROL_ENABLE;
} else {
dcb.fDtrControl = DTR_CONTROL_DISABLE; // disable DTR to avoid reset
}
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fOutxDsrFlow = FALSE;
dcb.fOutxCtsFlow = FALSE;
if (xon) {
dcb.fOutX = TRUE;
} else {
dcb.fOutX = FALSE;
}
if (xoff) {
dcb.fInX = TRUE;
} else {
dcb.fInX = FALSE;
}
if (rtscts) {
switch (rtsMode) {
case SERIALPORT_RTSMODE_ENABLE:
dcb.fRtsControl = RTS_CONTROL_ENABLE;
break;
case SERIALPORT_RTSMODE_HANDSHAKE:
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
break;
case SERIALPORT_RTSMODE_TOGGLE:
dcb.fRtsControl = RTS_CONTROL_TOGGLE;
break;
}
dcb.fOutxCtsFlow = TRUE;
} else {
dcb.fRtsControl = RTS_CONTROL_DISABLE;
}
dcb.fBinary = true;
dcb.BaudRate = baudRate;
dcb.ByteSize = dataBits;
switch (parity) {
case SERIALPORT_PARITY_NONE:
dcb.Parity = NOPARITY;
break;
case SERIALPORT_PARITY_MARK:
dcb.Parity = MARKPARITY;
break;
case SERIALPORT_PARITY_EVEN:
dcb.Parity = EVENPARITY;
break;
case SERIALPORT_PARITY_ODD:
dcb.Parity = ODDPARITY;
break;
case SERIALPORT_PARITY_SPACE:
dcb.Parity = SPACEPARITY;
break;
}
switch (stopBits) {
case SERIALPORT_STOPBITS_ONE:
dcb.StopBits = ONESTOPBIT;
break;
case SERIALPORT_STOPBITS_ONE_FIVE:
dcb.StopBits = ONE5STOPBITS;
break;
case SERIALPORT_STOPBITS_TWO:
dcb.StopBits = TWOSTOPBITS;
break;
}
if (!SetCommState(file, &dcb)) {
ErrorCodeToString("Open (SetCommState)", GetLastError(), errorString);
this->SetError(errorString);
CloseHandle(file);
return;
}
// Set the timeouts for read and write operations.
// Read operation will wait for at least 1 byte to be received.
COMMTIMEOUTS commTimeouts = {};
commTimeouts.ReadIntervalTimeout = 0; // Never timeout, always wait for data.
commTimeouts.ReadTotalTimeoutMultiplier = 0; // Do not allow big read timeout when big read buffer used
commTimeouts.ReadTotalTimeoutConstant = 0; // Total read timeout (period of read loop)
commTimeouts.WriteTotalTimeoutConstant = 0; // Const part of write timeout
commTimeouts.WriteTotalTimeoutMultiplier = 0; // Variable part of write timeout (per byte)
if (!SetCommTimeouts(file, &commTimeouts)) {
ErrorCodeToString("Open (SetCommTimeouts)", GetLastError(), errorString);
this->SetError(errorString);
CloseHandle(file);
return;
}
// Remove garbage data in RX/TX queues
PurgeComm(file, PURGE_RXCLEAR);
PurgeComm(file, PURGE_TXCLEAR);
result = static_cast<int>(reinterpret_cast<uintptr_t>(file));
}
void ConnectionOptionsBaton::Execute() {
DCB dcb = { 0 };
SecureZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(int2handle(fd), &dcb)) {
ErrorCodeToString("Update (GetCommState)", GetLastError(), errorString);
this->SetError(errorString);
return;
}
dcb.BaudRate = baudRate;
if (!SetCommState(int2handle(fd), &dcb)) {
ErrorCodeToString("Update (SetCommState)", GetLastError(), errorString);
this->SetError(errorString);
return;
}
}
void SetBaton::Execute() {
if (rts) {
EscapeCommFunction(int2handle(fd), SETRTS);
} else {
EscapeCommFunction(int2handle(fd), CLRRTS);
}
if (dtr) {
EscapeCommFunction(int2handle(fd), SETDTR);
} else {
EscapeCommFunction(int2handle(fd), CLRDTR);
}
if (brk) {
EscapeCommFunction(int2handle(fd), SETBREAK);
} else {
EscapeCommFunction(int2handle(fd), CLRBREAK);
}
DWORD bits = 0;
GetCommMask(int2handle(fd), &bits);
bits &= ~(EV_CTS | EV_DSR);
if (cts) {
bits |= EV_CTS;
}
if (dsr) {
bits |= EV_DSR;
}
if (!SetCommMask(int2handle(fd), bits)) {
ErrorCodeToString("Setting options on COM port (SetCommMask)", GetLastError(), errorString);
this->SetError(errorString);
return;
}
}
void GetBaton::Execute() {
DWORD bits = 0;
if (!GetCommModemStatus(int2handle(fd), &bits)) {
ErrorCodeToString("Getting control settings on COM port (GetCommModemStatus)", GetLastError(), errorString);
this->SetError(errorString);
return;
}
cts = bits & MS_CTS_ON;
dsr = bits & MS_DSR_ON;
dcd = bits & MS_RLSD_ON;
}
void GetBaudRateBaton::Execute() {
DCB dcb = { 0 };
SecureZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(int2handle(fd), &dcb)) {
ErrorCodeToString("Getting baud rate (GetCommState)", GetLastError(), errorString);
this->SetError(errorString);
return;
}
baudRate = static_cast<int>(dcb.BaudRate);
}
bool IsClosingHandle(int fd) {
for (std::list<int>::iterator it = g_closingHandles.begin(); it != g_closingHandles.end(); ++it) {
if (fd == *it) {
g_closingHandles.remove(fd);
return true;
}
}
return false;
}
void __stdcall WriteIOCompletion(DWORD errorCode, DWORD bytesTransferred, OVERLAPPED* ov) {
WriteBaton* baton = static_cast<WriteBaton*>(ov->hEvent);
DWORD bytesWritten;
if (!GetOverlappedResult(int2handle(baton->fd), ov, &bytesWritten, TRUE)) {
errorCode = GetLastError();
ErrorCodeToString("Writing to COM port (GetOverlappedResult)", errorCode, baton->errorString);
baton->complete = true;
return;
}
if (bytesWritten) {
baton->offset += bytesWritten;
if (baton->offset >= baton->bufferLength) {
baton->complete = true;
}
}
}
DWORD __stdcall WriteThread(LPVOID param) {
uv_async_t* async = static_cast<uv_async_t*>(param);
WriteBaton* baton = static_cast<WriteBaton*>(async->data);
OVERLAPPED* ov = new OVERLAPPED;
memset(ov, 0, sizeof(OVERLAPPED));
ov->hEvent = static_cast<void*>(baton);
while (!baton->complete) {
char* offsetPtr = baton->bufferData + baton->offset;
// WriteFileEx requires calling GetLastError even upon success. Clear the error beforehand.
SetLastError(0);
WriteFileEx(int2handle(baton->fd), offsetPtr,
static_cast<DWORD>(baton->bufferLength - baton->offset), ov, WriteIOCompletion);
// Error codes when call is successful, such as ERROR_MORE_DATA.
DWORD lastError = GetLastError();
if (lastError != ERROR_SUCCESS) {
ErrorCodeToString("Writing to COM port (WriteFileEx)", lastError, baton->errorString);
break;
}
// IOCompletion routine is only called once this thread is in an alertable wait state.
SleepEx(INFINITE, TRUE);
}
delete ov;
// Signal the main thread to run the callback.
uv_async_send(async);
ExitThread(0);
}
void EIO_AfterWrite(uv_async_t* req) {
WriteBaton* baton = static_cast<WriteBaton*>(req->data);
Napi::Env env = baton->callback.Env();
Napi::HandleScope scope(env);
WaitForSingleObject(baton->hThread, INFINITE);
CloseHandle(baton->hThread);
uv_close(reinterpret_cast<uv_handle_t*>(req), AsyncCloseCallback);
v8::Local<v8::Value> argv[1];
if (baton->errorString[0]) {
baton->callback.Call({Napi::Error::New(env, baton->errorString).Value()});
} else {
baton->callback.Call({env.Null()});
}
baton->buffer.Reset();
delete baton;
}
Napi::Value Write(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// file descriptor
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
int fd = info[0].As<Napi::Number>().Int32Value();
// buffer
if (!info[1].IsObject() || !info[1].IsBuffer()) {
Napi::TypeError::New(env, "Second argument must be a buffer").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Buffer<char> buffer = info[1].As<Napi::Buffer<char>>();
//getBufferFromObject(info[1].ToObject().ti);
char* bufferData = buffer.Data(); //.As<Napi::Buffer<char>>().Data();
size_t bufferLength = buffer.Length();//.As<Napi::Buffer<char>>().Length();
// callback
if (!info[2].IsFunction()) {
Napi::TypeError::New(env, "Third argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
WriteBaton* baton = new WriteBaton();
baton->callback = Napi::Persistent(info[2].As<Napi::Function>());
baton->fd = fd;
baton->buffer.Reset(buffer);
baton->bufferData = bufferData;
baton->bufferLength = bufferLength;
baton->offset = 0;
baton->complete = false;
uv_async_t* async = new uv_async_t;
uv_async_init(uv_default_loop(), async, EIO_AfterWrite);
async->data = baton;
// WriteFileEx requires a thread that can block. Create a new thread to
// run the write operation, saving the handle so it can be deallocated later.
baton->hThread = CreateThread(NULL, 0, WriteThread, async, 0, NULL);
return env.Null();
}
void __stdcall ReadIOCompletion(DWORD errorCode, DWORD bytesTransferred, OVERLAPPED* ov) {
ReadBaton* baton = static_cast<ReadBaton*>(ov->hEvent);
if (errorCode) {
ErrorCodeToString("Reading from COM port (ReadIOCompletion)", errorCode, baton->errorString);
baton->complete = true;
return;
}
DWORD lastError;
if (!GetOverlappedResult(int2handle(baton->fd), ov, &bytesTransferred, TRUE)) {
lastError = GetLastError();
ErrorCodeToString("Reading from COM port (GetOverlappedResult)", lastError, baton->errorString);
baton->complete = true;
return;
}
if (bytesTransferred) {
baton->bytesToRead -= bytesTransferred;
baton->bytesRead += bytesTransferred;
baton->offset += bytesTransferred;
}
// ReadFileEx and GetOverlappedResult retrieved only 1 byte. Read any additional data in the input
// buffer. Set the timeout to MAXDWORD in order to disable timeouts, so the read operation will
// return immediately no matter how much data is available.
COMMTIMEOUTS commTimeouts = {};
commTimeouts.ReadIntervalTimeout = MAXDWORD;
if (!SetCommTimeouts(int2handle(baton->fd), &commTimeouts)) {
lastError = GetLastError();
ErrorCodeToString("Setting COM timeout (SetCommTimeouts)", lastError, baton->errorString);
baton->complete = true;
return;
}
// Store additional data after whatever data has already been read.
char* offsetPtr = baton->bufferData + baton->offset;
// ReadFile, unlike ReadFileEx, needs an event in the overlapped structure.
memset(ov, 0, sizeof(OVERLAPPED));
ov->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ReadFile(int2handle(baton->fd), offsetPtr, baton->bytesToRead, &bytesTransferred, ov)) {
errorCode = GetLastError();
if (errorCode != ERROR_IO_PENDING) {
ErrorCodeToString("Reading from COM port (ReadFile)", errorCode, baton->errorString);
baton->complete = true;
CloseHandle(ov->hEvent);
return;
}
if (!GetOverlappedResult(int2handle(baton->fd), ov, &bytesTransferred, TRUE)) {
lastError = GetLastError();
ErrorCodeToString("Reading from COM port (GetOverlappedResult)", lastError, baton->errorString);
baton->complete = true;
CloseHandle(ov->hEvent);
return;
}
}
CloseHandle(ov->hEvent);
baton->bytesToRead -= bytesTransferred;
baton->bytesRead += bytesTransferred;
baton->complete = true;
}
DWORD __stdcall ReadThread(LPVOID param) {
uv_async_t* async = static_cast<uv_async_t*>(param);
ReadBaton* baton = static_cast<ReadBaton*>(async->data);
DWORD lastError;
OVERLAPPED* ov = new OVERLAPPED;
memset(ov, 0, sizeof(OVERLAPPED));
ov->hEvent = static_cast<void*>(baton);
while (!baton->complete) {
// Reset the read timeout to 0, so that it will block until more data arrives.
COMMTIMEOUTS commTimeouts = {};
commTimeouts.ReadIntervalTimeout = 0;
if (!SetCommTimeouts(int2handle(baton->fd), &commTimeouts)) {
lastError = GetLastError();
ErrorCodeToString("Setting COM timeout (SetCommTimeouts)", lastError, baton->errorString);
break;
}
// ReadFileEx doesn't use overlapped's hEvent, so it is reserved for user data.
ov->hEvent = static_cast<HANDLE>(baton);
char* offsetPtr = baton->bufferData + baton->offset;
// ReadFileEx requires calling GetLastError even upon success. Clear the error beforehand.
SetLastError(0);
// Only read 1 byte, so that the callback will be triggered once any data arrives.
ReadFileEx(int2handle(baton->fd), offsetPtr, 1, ov, ReadIOCompletion);
// Error codes when call is successful, such as ERROR_MORE_DATA.
lastError = GetLastError();
if (lastError != ERROR_SUCCESS) {
ErrorCodeToString("Reading from COM port (ReadFileEx)", lastError, baton->errorString);
break;
}
// IOCompletion routine is only called once this thread is in an alertable wait state.
SleepEx(INFINITE, TRUE);
}
delete ov;
// Signal the main thread to run the callback.
uv_async_send(async);
ExitThread(0);
}
void EIO_AfterRead(uv_async_t* req) {
ReadBaton* baton = static_cast<ReadBaton*>(req->data);
Napi::Env env = baton->callback.Env();
Napi::HandleScope scope(env);
WaitForSingleObject(baton->hThread, INFINITE);
CloseHandle(baton->hThread);
uv_close(reinterpret_cast<uv_handle_t*>(req), AsyncCloseCallback);
if (baton->errorString[0]) {
baton->callback.Call({Napi::Error::New(env, baton->errorString).Value(), env.Undefined()});
} else {
baton->callback.Call({env.Null(), Napi::Number::New(env, static_cast<int>(baton->bytesRead))});
}
delete baton;
}
Napi::Value Read(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// file descriptor
if (!info[0].IsNumber()) {
Napi::TypeError::New(env, "First argument must be a fd").ThrowAsJavaScriptException();
return env.Null();
}
int fd = info[0].As<Napi::Number>().Int32Value();
// buffer
if (!info[1].IsObject() || !info[1].IsBuffer()) {
Napi::TypeError::New(env, "Second argument must be a buffer").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Object buffer = info[1].ToObject();
size_t bufferLength = buffer.As<Napi::Buffer<char>>().Length();
// offset
if (!info[2].IsNumber()) {
Napi::TypeError::New(env, "Third argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
int offset = info[2].ToNumber().Int64Value();
// bytes to read
if (!info[3].IsNumber()) {
Napi::TypeError::New(env, "Fourth argument must be an int").ThrowAsJavaScriptException();
return env.Null();
}
size_t bytesToRead = info[3].ToNumber().Int64Value();
if ((bytesToRead + offset) > bufferLength) {
Napi::TypeError::New(env, "'bytesToRead' + 'offset' cannot be larger than the buffer's length").ThrowAsJavaScriptException();
return env.Null();
}
// callback
if (!info[4].IsFunction()) {
Napi::TypeError::New(env, "Fifth argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
ReadBaton* baton = new ReadBaton();
baton->callback = Napi::Persistent(info[4].As<Napi::Function>());
baton->fd = fd;
baton->offset = offset;
baton->bytesToRead = bytesToRead;
baton->bufferLength = bufferLength;
baton->bufferData = buffer.As<Napi::Buffer<char>>().Data();
baton->complete = false;
uv_async_t* async = new uv_async_t;
uv_async_init(uv_default_loop(), async, EIO_AfterRead);
async->data = baton;
baton->hThread = CreateThread(NULL, 0, ReadThread, async, 0, NULL);
// ReadFileEx requires a thread that can block. Create a new thread to
// run the read operation, saving the handle so it can be deallocated later.
return env.Null();
}
void CloseBaton::Execute() {
g_closingHandles.push_back(fd);
HMODULE hKernel32 = LoadLibrary("kernel32.dll");
// Look up function address
CancelIoExType pCancelIoEx = (CancelIoExType)GetProcAddress(hKernel32, "CancelIoEx");
// Do something with it
if (pCancelIoEx) {
// Function exists so call it
// Cancel all pending IO Requests for the current device
pCancelIoEx(int2handle(fd), NULL);
}
if (!CloseHandle(int2handle(fd))) {
ErrorCodeToString("Closing connection (CloseHandle)", GetLastError(), errorString);
this->SetError(errorString);
return;
}
}
wchar_t *copySubstring(wchar_t *someString, int n) {
wchar_t *new_ = reinterpret_cast<wchar_t*>(malloc(sizeof(wchar_t)*n + 1));
wcsncpy_s(new_, n + 1, someString, n);
new_[n] = '\0';
return new_;
}
Napi::Value List(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// callback
if (!info[0].IsFunction()) {
Napi::TypeError::New(env, "First argument must be a function").ThrowAsJavaScriptException();
return env.Null();
}
Napi::Function callback = info[0].As<Napi::Function>();
ListBaton* baton = new ListBaton(callback);
_snwprintf(baton->errorString, sizeof(baton->errorString), L"");
baton->Queue();
return env.Undefined();
}
// It's possible that the s/n is a construct and not the s/n of the parent USB
// composite device. This performs some convoluted registry lookups to fetch the USB s/n.
void getSerialNumber(const wchar_t *vid,
const wchar_t *pid,
const HDEVINFO hDevInfo,
SP_DEVINFO_DATA deviceInfoData,
const unsigned int maxSerialNumberLength,
wchar_t* serialNumber) {
_snwprintf_s(serialNumber, maxSerialNumberLength, _TRUNCATE, L"");
if (vid == NULL || pid == NULL) {
return;
}
DWORD dwSize;
WCHAR szWUuidBuffer[MAX_BUFFER_SIZE];
WCHAR wantedUuid[MAX_BUFFER_SIZE];
// Fetch the "Container ID" for this device node. In USB context, this "Container
// ID" refers to the composite USB device, i.e. the USB device as a whole, not
// just one of its interfaces with a serial port driver attached.
// From https://stackoverflow.com/questions/3438366/setupdigetdeviceproperty-usage-example:
// Because this is not compiled with UNICODE defined, the call to SetupDiGetDevicePropertyW
// has to be setup manually.
DEVPROPTYPE ulPropertyType;
typedef BOOL (WINAPI *FN_SetupDiGetDevicePropertyW)(
__in HDEVINFO DeviceInfoSet,
__in PSP_DEVINFO_DATA DeviceInfoData,
__in const DEVPROPKEY *PropertyKey,
__out DEVPROPTYPE *PropertyType,
__out_opt PBYTE PropertyBuffer,
__in DWORD PropertyBufferSize,
__out_opt PDWORD RequiredSize,
__in DWORD Flags);
FN_SetupDiGetDevicePropertyW fn_SetupDiGetDevicePropertyW = (FN_SetupDiGetDevicePropertyW)
GetProcAddress(GetModuleHandle(TEXT("Setupapi.dll")), "SetupDiGetDevicePropertyW");
if (fn_SetupDiGetDevicePropertyW (
hDevInfo,
&deviceInfoData,
&DEVPKEY_Device_ContainerId,
&ulPropertyType,
reinterpret_cast<BYTE*>(szWUuidBuffer),
sizeof(szWUuidBuffer),
&dwSize,
0)) {
szWUuidBuffer[dwSize] = '\0';
// Given the UUID bytes, build up a (widechar) string from it. There's some mangling
// going on.
StringFromGUID2((REFGUID)szWUuidBuffer, wantedUuid, ARRAY_SIZE(wantedUuid));
} else {
// Container UUID could not be fetched, return empty serial number.
return;
}
// NOTE: Devices might have a containerUuid like {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
// This means they're non-removable, and are not handled (yet).
// Maybe they should inherit the s/n from somewhere else.
// Iterate through all the USB devices with the given VendorID/ProductID
HKEY vendorProductHKey;
DWORD retCode;
wchar_t hkeyPath[MAX_BUFFER_SIZE];
_snwprintf_s(hkeyPath, MAX_BUFFER_SIZE, _TRUNCATE, L"SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_%s&PID_%s", vid, pid);
retCode = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
hkeyPath,
0,
KEY_READ,
&vendorProductHKey);
if (retCode == ERROR_SUCCESS) {
DWORD serialNumbersCount = 0; // number of subkeys
// Fetch how many subkeys there are for this VendorID/ProductID pair.
// That's the number of devices for this VendorID/ProductID known to this machine.
retCode = RegQueryInfoKey(
vendorProductHKey, // hkey handle
NULL, // buffer for class name
NULL, // size of class string
NULL, // reserved
&serialNumbersCount, // number of subkeys
NULL, // longest subkey size
NULL, // longest class string
NULL, // number of values for this key
NULL, // longest value name
NULL, // longest value data
NULL, // security descriptor
NULL); // last write time
if (retCode == ERROR_SUCCESS && serialNumbersCount > 0) {
for (unsigned int i=0; i < serialNumbersCount; i++) {
// Each of the subkeys here is the serial number of a USB device with the
// given VendorId/ProductId. Now fetch the string for the S/N.
DWORD serialNumberLength = maxSerialNumberLength;
retCode = RegEnumKeyExW(vendorProductHKey,
i,
reinterpret_cast<LPWSTR>(serialNumber),
&serialNumberLength,
NULL,
NULL,
NULL,
NULL);
if (retCode == ERROR_SUCCESS) {
// Lookup info for VID_(vendorId)&PID_(productId)\(serialnumber)
_snwprintf_s(hkeyPath, MAX_BUFFER_SIZE, _TRUNCATE,
L"SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_%ls&PID_%ls\\%ls",
vid, pid, serialNumber);
HKEY deviceHKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, hkeyPath, 0, KEY_READ, &deviceHKey) == ERROR_SUCCESS) {
wchar_t readUuid[MAX_BUFFER_SIZE];
DWORD readSize = sizeof(readUuid);
// Query VID_(vendorId)&PID_(productId)\(serialnumber)\ContainerID
retCode = RegQueryValueExW(deviceHKey, L"ContainerID", NULL, NULL, (LPBYTE)&readUuid, &readSize);
if (retCode == ERROR_SUCCESS) {
readUuid[readSize] = '\0';
if (wcscmp(wantedUuid, readUuid) == 0) {
// The ContainerID UUIDs match, return now that serialNumber has
// the right value.
RegCloseKey(deviceHKey);
RegCloseKey(vendorProductHKey);
return;
}
}
}
RegCloseKey(deviceHKey);
}
}
}
/* In case we did not obtain the path, for whatever reason, we close the key and return an empty string. */
RegCloseKey(vendorProductHKey);
}
_snwprintf_s(serialNumber, maxSerialNumberLength, _TRUNCATE, L"");
return;
}
void ListBaton::Execute() {
GUID *guidDev = (GUID*)& GUID_DEVCLASS_PORTS; // NOLINT
HDEVINFO hDevInfo = SetupDiGetClassDevs(guidDev, NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE);
SP_DEVINFO_DATA deviceInfoData;
int memberIndex = 0;
DWORD dwSize, dwPropertyRegDataType;
wchar_t szBuffer[MAX_BUFFER_SIZE];
wchar_t *pnpId;
wchar_t *vendorId;
wchar_t *productId;
wchar_t *name;
wchar_t *manufacturer;
wchar_t *locationId;
wchar_t *friendlyName;
wchar_t serialNumber[MAX_REGISTRY_KEY_SIZE];
bool isCom;
while (true) {
isCom = false;
pnpId = NULL;
vendorId = NULL;
productId = NULL;
name = NULL;
manufacturer = NULL;
locationId = NULL;
friendlyName = NULL;
ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA));
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (SetupDiEnumDeviceInfo(hDevInfo, memberIndex, &deviceInfoData) == FALSE) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
break;
}
}
dwSize = sizeof(szBuffer);
SetupDiGetDeviceInstanceIdW(hDevInfo, &deviceInfoData, reinterpret_cast<PWSTR>(szBuffer), dwSize, &dwSize);
szBuffer[dwSize] = '\0';
pnpId = wcsdup(szBuffer);
vendorId = wcsstr(szBuffer, L"VID_");
if (vendorId) {
vendorId += 4;
vendorId = copySubstring(vendorId, 4);
}
productId = wcsstr(szBuffer, L"PID_");
if (productId) {
productId += 4;
productId = copySubstring(productId, 4);
}
getSerialNumber(vendorId, productId, hDevInfo, deviceInfoData, MAX_REGISTRY_KEY_SIZE, serialNumber);
if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &deviceInfoData,
SPDRP_LOCATION_INFORMATION, &dwPropertyRegDataType,
reinterpret_cast<PBYTE>(szBuffer), sizeof(szBuffer), &dwSize)) {
locationId = wcsdup(szBuffer);
}
if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &deviceInfoData,
SPDRP_FRIENDLYNAME, &dwPropertyRegDataType,
reinterpret_cast<PBYTE>(szBuffer), sizeof(szBuffer), &dwSize)) {
friendlyName = wcsdup(szBuffer);
}
if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &deviceInfoData,
SPDRP_MFG, &dwPropertyRegDataType,
reinterpret_cast<PBYTE>(szBuffer), sizeof(szBuffer), &dwSize)) {
manufacturer = wcsdup(szBuffer);
}
HKEY hkey = SetupDiOpenDevRegKey(hDevInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
if (hkey != INVALID_HANDLE_VALUE) {
dwSize = sizeof(szBuffer);
if (RegQueryValueExW(hkey, L"PortName", NULL, NULL, (LPBYTE)&szBuffer, &dwSize) == ERROR_SUCCESS) {
name = wcsdup(szBuffer);
szBuffer[dwSize] = '\0';
isCom = wcsstr(szBuffer, L"COM") != NULL;
}
}
if (isCom) {
ListResultItem* resultItem = new ListResultItem();
resultItem->path = name;
resultItem->manufacturer = manufacturer;
resultItem->pnpId = pnpId;
if (vendorId) {
resultItem->vendorId = vendorId;
}
if (productId) {
resultItem->productId = productId;
}
resultItem->serialNumber = serialNumber;
if (locationId) {
resultItem->locationId = locationId;
}
if (friendlyName) {
resultItem->friendlyName = friendlyName;
}
results.push_back(resultItem);
}
free(pnpId);
free(vendorId);
free(productId);
free(locationId);
free(manufacturer);
free(name);
RegCloseKey(hkey);
memberIndex++;
}
if (hDevInfo) {
SetupDiDestroyDeviceInfoList(hDevInfo);
}
}
void setIfNotEmpty(Napi::Object item, std::string key, const char *value) {
Napi::Env env = item.Env();
Napi::String v8key = Napi::String::New(env, key);
if (strlen(value) > 0) {
(item).Set(v8key, Napi::String::New(env, value));
} else {
(item).Set(v8key, env.Undefined());
}
}
void setIfNotEmpty(Napi::Object item, std::string key, const wchar_t *value) {
Napi::Env env = item.Env();
Napi::String v8key = Napi::String::New(env, key);
if (wcslen(value) > 0) {
(item).Set(v8key, Napi::String::New(env, (const char16_t*) value));
} else {
(item).Set(v8key, env.Undefined());
}
}
void FlushBaton::Execute() {
DWORD purge_all = PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR;
if (!PurgeComm(int2handle(fd), purge_all)) {
ErrorCodeToString("Flushing connection (PurgeComm)", GetLastError(), errorString);
this->SetError(errorString);
return;
}
}
void DrainBaton::Execute() {
if (!FlushFileBuffers(int2handle(fd))) {
ErrorCodeToString("Draining connection (FlushFileBuffers)", GetLastError(), errorString);
this->SetError(errorString);
return;
}
}

View File

@@ -0,0 +1,93 @@
#ifndef PACKAGES_SERIALPORT_SRC_SERIALPORT_WIN_H_
#define PACKAGES_SERIALPORT_SRC_SERIALPORT_WIN_H_
#include <napi.h>
#include <uv.h>
#include <node_buffer.h>
#include <list>
#include <string>
#define ERROR_STRING_SIZE 1088
static inline HANDLE int2handle(int ptr) {
return reinterpret_cast<HANDLE>(static_cast<uintptr_t>(ptr));
}
struct WriteBaton {
WriteBaton() : bufferData(), errorString() {}
int fd = 0;
char* bufferData = nullptr;
size_t bufferLength = 0;
size_t offset = 0;
size_t bytesWritten = 0;
void* hThread = nullptr;
bool complete = false;
Napi::ObjectReference buffer;
Napi::FunctionReference callback;
int result = 0;
char errorString[ERROR_STRING_SIZE];
};
Napi::Value Write(const Napi::CallbackInfo& info);
struct ReadBaton {
ReadBaton() : errorString() {}
int fd = 0;
char* bufferData = nullptr;
size_t bufferLength = 0;
size_t bytesRead = 0;
size_t bytesToRead = 0;
size_t offset = 0;
void* hThread = nullptr;
Napi::FunctionReference callback;
bool complete = false;
char errorString[ERROR_STRING_SIZE];
};
Napi::Value Read(const Napi::CallbackInfo& info);
Napi::Value List(const Napi::CallbackInfo& info);
void setIfNotEmpty(Napi::Object item, std::string key, const char *value);
void setIfNotEmpty(Napi::Object item, std::string key, const wchar_t *value);
struct ListResultItem {
std::wstring path;
std::wstring manufacturer;
std::wstring serialNumber;
std::wstring pnpId;
std::wstring locationId;
std::wstring friendlyName;
std::wstring vendorId;
std::wstring productId;
};
struct ListBaton : public Napi::AsyncWorker {
ListBaton(Napi::Function& callback) : Napi::AsyncWorker(callback, "node-serialport:ListBaton"),
errorString() {}
std::list<ListResultItem*> results;
wchar_t errorString[ERROR_STRING_SIZE];
void Execute() override;
void OnOK() override {
Napi::Env env = Env();
Napi::HandleScope scope(env);
Napi::Array result = Napi::Array::New(env);
int i = 0;
for (std::list<ListResultItem*>::iterator it = results.begin(); it != results.end(); ++it, i++) {
Napi::Object item = Napi::Object::New(env);
setIfNotEmpty(item, "path", (*it)->path.c_str());
setIfNotEmpty(item, "manufacturer", (*it)->manufacturer.c_str());
setIfNotEmpty(item, "serialNumber", (*it)->serialNumber.c_str());
setIfNotEmpty(item, "pnpId", (*it)->pnpId.c_str());
setIfNotEmpty(item, "locationId", (*it)->locationId.c_str());
setIfNotEmpty(item, "friendlyName", (*it)->friendlyName.c_str());
setIfNotEmpty(item, "vendorId", (*it)->vendorId.c_str());
setIfNotEmpty(item, "productId", (*it)->productId.c_str());
(result).Set(i, item);
}
Callback().Call({env.Null(), result});
}
};
#endif // PACKAGES_SERIALPORT_SRC_SERIALPORT_WIN_H_

View File

@@ -0,0 +1,10 @@
# Code of Conduct
SerialPort follows the Nodebots Code of Conduct. The full text can be found at http://nodebots.io/conduct.html
## TLDR
- Be respectful.
- Abusive behavior is never tolerated.
- Data published to NodeBots is hosted at the discretion of the service administrators, and may be removed.
- Don't build evil robots.
- Violations of this code may result in swift and permanent expulsion from the NodeBots community.

21
node_modules/@serialport/bindings-interface/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Francis Gulotta
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More