Fix Serial Monitor for VS Code 1.43.0 (#267)
This commit is contained in:
Родитель
de76a32bdd
Коммит
e897b845b2
|
@ -21,6 +21,13 @@ jobs:
|
|||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Linux dependencies
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install g++-multilib
|
||||
sudo apt-get install -y build-essential
|
||||
sudo apt-get install libudev-dev
|
||||
- name: Use Node.js ${{ matrix.node-version }} and install npm dependencies
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Device Simulator Express, a Microsoft Garage project
|
||||
|
||||
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/Python-3.7%2B-blue.svg" alt="Python versions: 3.7+" /></a> <img src="https://www.repostatus.org/badges/latest/active.svg" alt="Project Status: Active – The project has reached a stable, usable state and is being actively developed." /> <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: We are using the MIT License"></a> <a href="CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-Welcome-brightgreen.svg" alt="We are welcoming PRS!"></a> <img src="https://img.shields.io/badge/platform-win%20%7C%20osx-lightgrey.svg" alt="Platforms Supported: Windows, MacOSX"/>
|
||||
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/Python-3.7%2B-blue.svg" alt="Python versions: 3.7+" /></a> <img src="https://img.shields.io/badge/VS%20Code-v1.43+-blue" alt="VS Code version 1.43"> <img src="https://www.repostatus.org/badges/latest/active.svg" alt="Project Status: Active – The project has reached a stable, usable state and is being actively developed." /> <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: We are using the MIT License"></a> <a href="CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-Welcome-brightgreen.svg" alt="We are welcoming PRS!"></a> <img src="https://img.shields.io/badge/platform-win%20%7C%20osx-lightgrey.svg" alt="Platforms Supported: Windows, MacOSX"/>
|
||||
|
||||
<a href="https://microsoftgarage.visualstudio.com/002806e2-ebaa-4672-9d2e-5fe5d29154ef/_boards/board/t/227906bb-31ac-4b07-8626-3d757754a616/Microsoft.RequirementCategory/"><img src="https://microsoftgarage.visualstudio.com/002806e2-ebaa-4672-9d2e-5fe5d29154ef/227906bb-31ac-4b07-8626-3d757754a616/_apis/work/boardbadge/73f82653-3da1-4a6f-bb79-c91c9eecec28" alt="Azure DevOps Board Badge" /></a>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"dialogResponses.agreeAndRun": "Agree and Run",
|
||||
"dialogResponses.acceptPrivacy": "Got it",
|
||||
"dialogResponses.cancel": "Cancel",
|
||||
"dialogResponses.select": "Select",
|
||||
"dialogResponses.dontShowAgain": "Don't Show Again",
|
||||
"dialogResponses.exampleCode": "Example Code on GitHub",
|
||||
"dialogResponses.help": "I need help",
|
||||
|
|
|
@ -5019,6 +5019,77 @@
|
|||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
|
||||
"dev": true
|
||||
},
|
||||
"@serialport/binding-abstract": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/binding-abstract/-/binding-abstract-8.0.6.tgz",
|
||||
"integrity": "sha512-1swwUVoRyQ9ubxrkJ8JPppykohUpTAP4jkGr36e9NjbVocSPfqeX6tFZFwl/IdUlwJwxGdbKDqq7FvXniCQUMw==",
|
||||
"requires": {
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"@serialport/binding-mock": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-8.0.6.tgz",
|
||||
"integrity": "sha512-BIbY5/PsDDo0QWDNCCxDgpowAdks+aZR8BOsEtK2GoASTTcJCy1fBwPIfH870o7rnbH901wY3C+yuTfdOvSO9A==",
|
||||
"requires": {
|
||||
"@serialport/binding-abstract": "^8.0.6",
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"@serialport/bindings": {
|
||||
"version": "8.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/bindings/-/bindings-8.0.7.tgz",
|
||||
"integrity": "sha512-IqudDL8ne2Y2S0W5fKA6wdgHCIA2e2OIaPVYhGy6duE6legNHFY+05CLicHAyAeTocXmHU7rVNxzVQrOG5tM4g==",
|
||||
"requires": {
|
||||
"@serialport/binding-abstract": "^8.0.6",
|
||||
"@serialport/parser-readline": "^8.0.6",
|
||||
"bindings": "^1.5.0",
|
||||
"debug": "^4.1.1",
|
||||
"nan": "^2.14.0",
|
||||
"prebuild-install": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"@serialport/parser-byte-length": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-8.0.6.tgz",
|
||||
"integrity": "sha512-92mrFxFEvq3gRvSM7ANK/jfbmHslz91a5oYJy/nbSn4H/MCRXjxR2YOkQgVXuN+zLt+iyDoW3pcOP4Sc1nWdqQ=="
|
||||
},
|
||||
"@serialport/parser-cctalk": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-8.0.6.tgz",
|
||||
"integrity": "sha512-pqtCYQPgxnxHygiXUPCfgX7sEx+fdR/ObjpscidynEULUq2fFrC5kBkrxRbTfHRtTaU2ii9DyjFq0JVRCbhI0Q=="
|
||||
},
|
||||
"@serialport/parser-delimiter": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-8.0.6.tgz",
|
||||
"integrity": "sha512-ogKOcPisPMlVtirkuDu3SFTF0+xT0ijxoH7XjpZiYL41EVi367MwuCnEmXG+dEKKnF0j9EPqOyD2LGSJxaFmhQ=="
|
||||
},
|
||||
"@serialport/parser-readline": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-8.0.6.tgz",
|
||||
"integrity": "sha512-OYBT2mpczh9QUI3MTw8j0A0tIlPVjpVipvuVnjRkYwxrxPeq04RaLFhaDpuRzua5rTKMt89c1y3btYeoDXMjAA==",
|
||||
"requires": {
|
||||
"@serialport/parser-delimiter": "^8.0.6"
|
||||
}
|
||||
},
|
||||
"@serialport/parser-ready": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-8.0.6.tgz",
|
||||
"integrity": "sha512-xcEqv4rc119WR5JzAuu8UeJOlAwET2PTdNb6aIrrLlmTxhvuBbuRFcsnF3BpH9jUL30Kh7a6QiNXIwVG+WR/1Q=="
|
||||
},
|
||||
"@serialport/parser-regex": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-8.0.6.tgz",
|
||||
"integrity": "sha512-J8KY75Azz5ZyExmyM5YfUxbXOWBkZCytKgCCmZ966ttwZS0bUZOuoCaZj2Zp4VILJAiLuxHoqc0foi67Fri5+g=="
|
||||
},
|
||||
"@serialport/stream": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-8.0.6.tgz",
|
||||
"integrity": "sha512-ym1PwM0rwLrj90vRBB66I1hwMXbuMw9wGTxqns75U3N/tuNFOH85mxXaYVF2TpI66aM849NoI1jMm50fl9equg==",
|
||||
"requires": {
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"@sheerun/mutationobserver-shim": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz",
|
||||
|
@ -6532,8 +6603,7 @@
|
|||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
},
|
||||
"archy": {
|
||||
"version": "1.0.0",
|
||||
|
@ -6541,6 +6611,15 @@
|
|||
"integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
|
||||
"dev": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
}
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
|
@ -8108,8 +8187,7 @@
|
|||
"base64-js": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
|
||||
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
|
||||
},
|
||||
"base64id": {
|
||||
"version": "1.0.0",
|
||||
|
@ -8154,12 +8232,46 @@
|
|||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"bl": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
|
||||
"integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"buffer": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.5.0.tgz",
|
||||
"integrity": "sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww==",
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"blob": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||
|
@ -8743,8 +8855,7 @@
|
|||
"chownr": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
|
||||
},
|
||||
"chrome-trace-event": {
|
||||
"version": "1.0.2",
|
||||
|
@ -8941,8 +9052,7 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"dev": true
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
||||
},
|
||||
"collect-v8-coverage": {
|
||||
"version": "1.0.0",
|
||||
|
@ -9159,6 +9269,11 @@
|
|||
"date-now": "^0.1.4"
|
||||
}
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
||||
},
|
||||
"constants-browserify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
|
||||
|
@ -10141,6 +10256,14 @@
|
|||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
||||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
|
||||
},
|
||||
"decompress-response": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
||||
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
|
||||
"requires": {
|
||||
"mimic-response": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"deep-equal": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
|
||||
|
@ -10155,6 +10278,11 @@
|
|||
"regexp.prototype.flags": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
},
|
||||
"deep-is": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
|
||||
|
@ -10301,6 +10429,11 @@
|
|||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
||||
},
|
||||
"denodeify": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz",
|
||||
|
@ -10335,6 +10468,11 @@
|
|||
"integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
|
||||
"dev": true
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
||||
},
|
||||
"detect-newline": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
|
||||
|
@ -11717,6 +11855,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"expand-template": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
|
||||
},
|
||||
"expand-tilde": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
|
||||
|
@ -12266,9 +12409,7 @@
|
|||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
},
|
||||
"filesize": {
|
||||
"version": "6.0.1",
|
||||
|
@ -12681,11 +12822,15 @@
|
|||
"readable-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^4.0.0",
|
||||
|
@ -12695,8 +12840,7 @@
|
|||
"graceful-fs": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
|
||||
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -13295,6 +13439,41 @@
|
|||
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
|
||||
"dev": true
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
||||
"requires": {
|
||||
"aproba": "^1.0.3",
|
||||
"console-control-strings": "^1.0.0",
|
||||
"has-unicode": "^2.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"signal-exit": "^3.0.0",
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1",
|
||||
"wide-align": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"generic-names": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/generic-names/-/generic-names-1.0.3.tgz",
|
||||
|
@ -13383,6 +13562,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"github-from-package": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
|
||||
|
@ -13529,8 +13713,7 @@
|
|||
"graceful-fs": {
|
||||
"version": "4.1.15",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
|
||||
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
|
||||
},
|
||||
"growl": {
|
||||
"version": "1.10.5",
|
||||
|
@ -13980,6 +14163,11 @@
|
|||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
|
||||
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
|
||||
},
|
||||
"has-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
|
||||
|
@ -14344,8 +14532,7 @@
|
|||
"ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||
},
|
||||
"iferr": {
|
||||
"version": "0.1.5",
|
||||
|
@ -14452,8 +14639,7 @@
|
|||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "7.0.4",
|
||||
|
@ -14778,8 +14964,7 @@
|
|||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
||||
},
|
||||
"is-generator-fn": {
|
||||
"version": "2.1.0",
|
||||
|
@ -18270,7 +18455,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
|
@ -19016,6 +19200,11 @@
|
|||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||
"dev": true
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
||||
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
|
||||
},
|
||||
"min-indent": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
|
||||
|
@ -19377,9 +19566,7 @@
|
|||
"nan": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
|
||||
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
|
||||
},
|
||||
"nanomatch": {
|
||||
"version": "1.2.13",
|
||||
|
@ -19406,6 +19593,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
|
||||
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
|
||||
},
|
||||
"natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
|
@ -19451,6 +19643,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.15.0.tgz",
|
||||
"integrity": "sha512-FeLpTS0F39U7hHZU1srAK4Vx+5AHNVOTP+hxBNQknR/54laTHSFIJkDWDqiquY1LeLUgTfPN7sLPhMubx0PLAg==",
|
||||
"requires": {
|
||||
"semver": "^5.4.1"
|
||||
}
|
||||
},
|
||||
"node-environment-flags": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz",
|
||||
|
@ -19569,6 +19769,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"noop-logger": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
|
||||
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||
|
@ -19672,6 +19877,17 @@
|
|||
"path-key": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"requires": {
|
||||
"are-we-there-yet": "~1.1.2",
|
||||
"console-control-strings": "~1.1.0",
|
||||
"gauge": "~2.7.3",
|
||||
"set-blocking": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
|
||||
|
@ -19690,8 +19906,7 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
||||
},
|
||||
"nwsapi": {
|
||||
"version": "2.2.0",
|
||||
|
@ -24894,6 +25109,28 @@
|
|||
"uniq": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
|
||||
"integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==",
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.3",
|
||||
"expand-template": "^2.0.3",
|
||||
"github-from-package": "0.0.0",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"napi-build-utils": "^1.0.1",
|
||||
"node-abi": "^2.7.0",
|
||||
"noop-logger": "^0.1.1",
|
||||
"npmlog": "^4.0.1",
|
||||
"pump": "^3.0.0",
|
||||
"rc": "^1.2.7",
|
||||
"simple-get": "^3.0.3",
|
||||
"tar-fs": "^2.0.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"which-pm-runs": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
|
@ -24992,8 +25229,7 @@
|
|||
"process-nextick-args": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
|
||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
|
||||
},
|
||||
"progress": {
|
||||
"version": "2.0.3",
|
||||
|
@ -25201,6 +25437,17 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"requires": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "16.12.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz",
|
||||
|
@ -27648,7 +27895,6 @@
|
|||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
|
@ -28499,6 +28745,23 @@
|
|||
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
|
||||
"dev": true
|
||||
},
|
||||
"serialport": {
|
||||
"version": "8.0.7",
|
||||
"resolved": "https://registry.npmjs.org/serialport/-/serialport-8.0.7.tgz",
|
||||
"integrity": "sha512-R9bfNebs2dblYf5sD/Aaa7j8+siP4X7TGT02lqHM9DF5fyjlrPGXmsLw9+LKOz1AvjGjkxf2NzBVnDpqRX7clQ==",
|
||||
"requires": {
|
||||
"@serialport/binding-mock": "^8.0.6",
|
||||
"@serialport/bindings": "^8.0.7",
|
||||
"@serialport/parser-byte-length": "^8.0.6",
|
||||
"@serialport/parser-cctalk": "^8.0.6",
|
||||
"@serialport/parser-delimiter": "^8.0.6",
|
||||
"@serialport/parser-readline": "^8.0.6",
|
||||
"@serialport/parser-ready": "^8.0.6",
|
||||
"@serialport/parser-regex": "^8.0.6",
|
||||
"@serialport/stream": "^8.0.6",
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"serve-index": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
|
||||
|
@ -28695,6 +28958,21 @@
|
|||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
|
||||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
|
||||
},
|
||||
"simple-concat": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
|
||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||
"requires": {
|
||||
"decompress-response": "^4.2.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
|
@ -29317,7 +29595,6 @@
|
|||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^4.0.0"
|
||||
|
@ -29326,14 +29603,12 @@
|
|||
"ansi-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
|
||||
"dev": true
|
||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^3.0.0"
|
||||
}
|
||||
|
@ -29375,7 +29650,6 @@
|
|||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
|
@ -29450,8 +29724,7 @@
|
|||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
|
||||
"dev": true
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"style-inject": {
|
||||
"version": "0.3.0",
|
||||
|
@ -29729,6 +30002,41 @@
|
|||
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
|
||||
"dev": true
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz",
|
||||
"integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==",
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
|
||||
"integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
|
||||
"requires": {
|
||||
"bl": "^4.0.1",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"terminal-link": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
|
||||
|
@ -30491,8 +30799,7 @@
|
|||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
|
@ -30648,6 +30955,27 @@
|
|||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"usb-detection": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/usb-detection/-/usb-detection-4.7.0.tgz",
|
||||
"integrity": "sha512-HE9y5uYfA2ebVo64BrIB0OVNaVKZ2oWkadpTItCi4YQXyZ9vD0705iqQ3B7Rz8xevcrW/RNg/85E2o2JoPFmsQ==",
|
||||
"requires": {
|
||||
"bindings": "^1.3.0",
|
||||
"eventemitter2": "^5.0.1",
|
||||
"nan": "^2.13.2",
|
||||
"prebuild-install": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"usb-native": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/usb-native/-/usb-native-5.0.1.tgz",
|
||||
"integrity": "sha512-FuKUVJmzgo7I6jclH0fCLuQLuOTg3jzzDvQwFgFWJM5Qe1L09snrl84adFCNvE/LGbCCCa4XjLuGGIgB7+zGQA==",
|
||||
"requires": {
|
||||
"fs-extra": "^8.1.0",
|
||||
"serialport": "^8.0.7",
|
||||
"usb-detection": "^4.7.0"
|
||||
}
|
||||
},
|
||||
"use": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
|
||||
|
@ -30668,8 +30996,7 @@
|
|||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"util.promisify": {
|
||||
"version": "1.0.0",
|
||||
|
@ -31493,11 +31820,15 @@
|
|||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
|
||||
},
|
||||
"which-pm-runs": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
|
||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"instrumentationKey": "__AIKEY__",
|
||||
"icon": "assets/icon.png",
|
||||
"engines": {
|
||||
"vscode": "^1.34.0"
|
||||
"vscode": "^1.43.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
|
@ -325,6 +325,7 @@
|
|||
"socket.io": "^2.2.0",
|
||||
"svg-inline-react": "^3.1.0",
|
||||
"ts-jest": "^25.0.0",
|
||||
"usb-native": "^5.0.1",
|
||||
"util": "^0.12.1",
|
||||
"vscode-extension-telemetry": "^0.1.1",
|
||||
"vscode-nls": "^4.1.0"
|
||||
|
|
|
@ -403,6 +403,9 @@ export namespace DialogResponses {
|
|||
export const CANCEL: MessageItem = {
|
||||
title: localize("dialogResponses.cancel", "Cancel"),
|
||||
};
|
||||
export const SELECT: MessageItem = {
|
||||
title: localize("dialogResponses.select", "Select"),
|
||||
};
|
||||
export const HELP: MessageItem = {
|
||||
title: localize("dialogResponses.help", "I need help"),
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ import { logToOutputChannel } from "./extension_utils/utils";
|
|||
import { SerialPortControl } from "./serialPortControl";
|
||||
|
||||
export interface ISerialPortDetail {
|
||||
comName: string;
|
||||
path: string;
|
||||
manufacturer: string;
|
||||
vendorId: string;
|
||||
productId: string;
|
||||
|
@ -127,7 +127,7 @@ export class SerialMonitor implements vscode.Disposable {
|
|||
foundPort &&
|
||||
!(this._serialPortControl && this._serialPortControl.isActive)
|
||||
) {
|
||||
this.updatePortListStatus(foundPort.comName);
|
||||
this.updatePortListStatus(foundPort.path);
|
||||
}
|
||||
} else {
|
||||
const chosen = await vscode.window.showQuickPick(
|
||||
|
@ -136,7 +136,7 @@ export class SerialMonitor implements vscode.Disposable {
|
|||
(port: ISerialPortDetail): vscode.QuickPickItem => {
|
||||
return {
|
||||
description: port.manufacturer,
|
||||
label: port.comName,
|
||||
label: port.path,
|
||||
};
|
||||
}
|
||||
)
|
||||
|
@ -160,10 +160,10 @@ export class SerialMonitor implements vscode.Disposable {
|
|||
if (!this._currentPort) {
|
||||
const ans = await vscode.window.showInformationMessage(
|
||||
CONSTANTS.WARNING.NO_SERIAL_PORT_SELECTED,
|
||||
DialogResponses.YES,
|
||||
DialogResponses.NO
|
||||
DialogResponses.SELECT,
|
||||
DialogResponses.CANCEL
|
||||
);
|
||||
if (ans === DialogResponses.YES) {
|
||||
if (ans === DialogResponses.SELECT) {
|
||||
await this.selectSerialPort(null, null);
|
||||
}
|
||||
if (!this._currentPort) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { CONSTANTS } from "./constants";
|
|||
import { logToOutputChannel } from "./extension_utils/utils";
|
||||
|
||||
interface ISerialPortDetail {
|
||||
comName: string;
|
||||
path: string;
|
||||
manufacturer: string;
|
||||
vendorId: string;
|
||||
productId: string;
|
||||
|
@ -18,21 +18,16 @@ interface ISerialPortDetail {
|
|||
export class SerialPortControl {
|
||||
public static get serialport(): any {
|
||||
if (!SerialPortControl._serialport) {
|
||||
SerialPortControl._serialport = require("../vendor/node-usb-native").SerialPort;
|
||||
SerialPortControl._serialport = require("usb-native").SerialPort;
|
||||
}
|
||||
return SerialPortControl._serialport;
|
||||
}
|
||||
|
||||
public static list(): Promise<ISerialPortDetail[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
SerialPortControl.serialport.list(
|
||||
(error: any, ports: ISerialPortDetail[]) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(ports);
|
||||
}
|
||||
}
|
||||
SerialPortControl.serialport.list().then(
|
||||
ports => resolve(ports),
|
||||
err => reject(err)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -53,7 +48,7 @@ export class SerialPortControl {
|
|||
}
|
||||
|
||||
public get isActive(): boolean {
|
||||
return this._currentSerialPort && this._currentSerialPort.isOpen();
|
||||
return this._currentSerialPort && this._currentSerialPort.isOpen;
|
||||
}
|
||||
|
||||
public get currentPort(): string {
|
||||
|
@ -66,7 +61,7 @@ export class SerialPortControl {
|
|||
CONSTANTS.INFO.OPENING_SERIAL_PORT(this._currentPort)
|
||||
);
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this._currentSerialPort && this._currentSerialPort.isOpen()) {
|
||||
if (this._currentSerialPort && this._currentSerialPort.isOpen) {
|
||||
this._currentSerialPort.close((err: any) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
|
@ -89,8 +84,7 @@ export class SerialPortControl {
|
|||
this._outputChannel.show();
|
||||
this._currentSerialPort.on("open", () => {
|
||||
this._currentSerialPort.write(
|
||||
CONSTANTS.MISC.SERIAL_MONITOR_TEST_IF_OPEN,
|
||||
"Both NL & CR",
|
||||
CONSTANTS.MISC.SERIAL_MONITOR_TEST_IF_OPEN + os.EOL,
|
||||
(err: any) => {
|
||||
if (
|
||||
err &&
|
||||
|
|
|
@ -42,7 +42,7 @@ export class UsbDetector {
|
|||
if (os.platform() === "linux" || !enableUSBDetection) {
|
||||
return;
|
||||
}
|
||||
this._usbDetector = require("../vendor/node-usb-native").detector;
|
||||
this._usbDetector = require("usb-native").detector;
|
||||
|
||||
if (!this._usbDetector) {
|
||||
return;
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
"extends": [
|
||||
"standard"
|
||||
],
|
||||
"plugins": [
|
||||
"require-path-exists"
|
||||
],
|
||||
"env": {
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"rules": {
|
||||
"arrow-parens": [2, "as-needed", {"requireForBlockBody": true }],
|
||||
"no-unused-vars": [2, { "vars": "all", "args": "after-used" }],
|
||||
"object-curly-spacing": [2, "always"],
|
||||
"prefer-arrow-callback": 2,
|
||||
"prefer-const": 2,
|
||||
"prefer-template": 2,
|
||||
"require-path-exists/exists": 2,
|
||||
"require-path-exists/notEmpty": 2,
|
||||
"require-path-exists/tooManyArguments": 2,
|
||||
"semi": [2, "always", {"omitLastInOneLineBlock": true}],
|
||||
"space-before-function-paren": [2, "never"],
|
||||
"standard/object-curly-even-spacing": 0
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
$ cat .gitignore
|
||||
|
||||
# Can ignore specific files
|
||||
.settings.xml
|
||||
.monitor
|
||||
.DS_Store
|
||||
|
||||
# Use wildcards as well
|
||||
*~
|
||||
#*.swp
|
||||
|
||||
# Can also ignore all directories and files in a directory.
|
||||
node_modules/
|
||||
build/
|
|
@ -1,2 +0,0 @@
|
|||
node_modules/
|
||||
build/
|
|
@ -1,4 +0,0 @@
|
|||
This is a natvie package contains two modules: detector and serialport, detector provides the functionality of detecting usb changes and serialport provides the functionality of listing serial ports, openning serial ports and sending/receiving message to/from serial ports.
|
||||
|
||||
require("../../../vendor/node-usb-native").SerialPort;
|
||||
require("../../../vendor/node-usb-native").detector;
|
|
@ -1,64 +0,0 @@
|
|||
{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "usb-native",
|
||||
"sources": [
|
||||
"src/detection.cpp",
|
||||
"src/detection.h",
|
||||
"src/deviceList.cpp",
|
||||
"src/combined.cpp",
|
||||
"src/serialport.cpp"
|
||||
],
|
||||
"include_dirs": [
|
||||
"<!(node -e \"require('nan')\")"
|
||||
],
|
||||
"conditions": [
|
||||
[
|
||||
"OS=='win'",
|
||||
{
|
||||
"sources": [
|
||||
"src/detection_win.cpp",
|
||||
"src/serialport_win.cpp"
|
||||
],
|
||||
"msvs_settings": {
|
||||
"VCCLCompilerTool": {
|
||||
"ExceptionHandling": "2",
|
||||
"DisableSpecificWarnings": [
|
||||
"4530",
|
||||
"4506"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include_dirs+": []
|
||||
}
|
||||
],
|
||||
[
|
||||
"OS=='mac'",
|
||||
{
|
||||
"sources": [
|
||||
"src/detection_mac.cpp",
|
||||
"src/serialport_unix.cpp",
|
||||
"src/serialport_poller.cpp"
|
||||
],
|
||||
"libraries": [
|
||||
"-framework CoreFoundation -framework IOKit"
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
"OS=='linux'",
|
||||
{
|
||||
"sources": [
|
||||
"src/detection_linux.cpp",
|
||||
"src/serialport_unix.cpp",
|
||||
"src/serialport_poller.cpp"
|
||||
],
|
||||
"defines": [
|
||||
"DISABLE_USB_DETECTOR"
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
'use strict';
|
||||
const path = require('path');
|
||||
const bindings = require('./native_loader').load(path.join(__dirname, 'native'), 'usb-native');
|
||||
var listUnix = require('./list-unix');
|
||||
|
||||
var linux = process.platform !== 'win32' && process.platform !== 'darwin';
|
||||
|
||||
function listLinux(callback) {
|
||||
callback = callback || function(err) {
|
||||
if (err) {
|
||||
this.emit('error', err);
|
||||
}
|
||||
}.bind(this);
|
||||
return listUnix(callback);
|
||||
}
|
||||
|
||||
var platformOptions = {};
|
||||
if (process.platform !== 'win32') {
|
||||
platformOptions = {
|
||||
vmin: 1,
|
||||
vtime: 0
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
// serialport bindings
|
||||
close: bindings.close,
|
||||
drain: bindings.drain,
|
||||
flush: bindings.flush,
|
||||
list: linux ? listLinux : bindings.list,
|
||||
open: bindings.open,
|
||||
SerialportPoller: bindings.SerialportPoller,
|
||||
set: bindings.set,
|
||||
update: bindings.update,
|
||||
write: bindings.write,
|
||||
platformOptions: platformOptions,
|
||||
|
||||
// usb-detection bindings
|
||||
registerAdded: bindings.registerAdded,
|
||||
registerRemoved: bindings.registerRemoved,
|
||||
startMonitoring: bindings.startMonitoring,
|
||||
stopMonitoring: bindings.stopMonitoring,
|
||||
find: bindings.find
|
||||
};
|
|
@ -1,95 +0,0 @@
|
|||
var detection = require('./bindings');
|
||||
var EventEmitter2 = require('eventemitter2').EventEmitter2;
|
||||
|
||||
var detector = new EventEmitter2({
|
||||
wildcard: true,
|
||||
delimiter: ':',
|
||||
maxListeners: 1000 // default would be 10!
|
||||
});
|
||||
|
||||
// detector.find = detection.find;
|
||||
detector.find = (vid, pid, callback) => {
|
||||
// Suss out the optional parameters
|
||||
if (!pid && !callback) {
|
||||
callback = vid;
|
||||
vid = undefined;
|
||||
} else if (!callback) {
|
||||
callback = pid;
|
||||
pid = undefined;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Assemble the optional args into something we can use with `apply`
|
||||
var args = [];
|
||||
if (vid) {
|
||||
args = args.concat(vid);
|
||||
}
|
||||
if (pid) {
|
||||
args = args.concat(pid);
|
||||
}
|
||||
|
||||
// Tack on our own callback that takes care of things
|
||||
args = args.concat((err, devices) => {
|
||||
// We call the callback if they passed one
|
||||
if (callback) {
|
||||
callback.call(callback, err, devices);
|
||||
}
|
||||
|
||||
// But also do the promise stuff
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(devices);
|
||||
});
|
||||
|
||||
// Fire off the `find` function that actually does all of the work
|
||||
detection.find.apply(detection, args);
|
||||
});
|
||||
};
|
||||
if (detection.registerAdded) {
|
||||
detection.registerAdded((device) => {
|
||||
detector.emit(`add:${device.vendorId}:${device.productId}`, device);
|
||||
detector.emit(`insert:${device.vendorId}:${device.productId}`, device);
|
||||
detector.emit(`add:${device.vendorId}`, device);
|
||||
detector.emit(`insert:${device.vendorId}`, device);
|
||||
detector.emit('add', device);
|
||||
detector.emit('insert', device);
|
||||
|
||||
detector.emit(`change:${device.vendorId}:${device.productId}`, device);
|
||||
detector.emit(`change:${device.vendorId}`, device);
|
||||
detector.emit('change', device);
|
||||
});
|
||||
|
||||
detection.registerRemoved((device) => {
|
||||
detector.emit(`remove:${device.vendorId}:${device.productId}`, device);
|
||||
detector.emit(`remove:${device.vendorId}`, device);
|
||||
detector.emit('remove', device);
|
||||
|
||||
detector.emit(`change:${device.vendorId}:${device.productId}`, device);
|
||||
detector.emit(`change:${device.vendorId}`, device);
|
||||
detector.emit('change', device);
|
||||
});
|
||||
|
||||
var started = true;
|
||||
|
||||
detector.startMonitoring = () => {
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
|
||||
started = true;
|
||||
detection.startMonitoring();
|
||||
};
|
||||
|
||||
detector.stopMonitoring = () => {
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
|
||||
started = false;
|
||||
detection.stopMonitoring();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = detector;
|
|
@ -1,2 +0,0 @@
|
|||
exports.detector = require('./detector');
|
||||
exports.SerialPort = require('./serialport');
|
|
@ -1,109 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var childProcess = require('child_process');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
function promisify(func) {
|
||||
return (arg) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
func(arg, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function promisedFilter(func) {
|
||||
return (data) => {
|
||||
var shouldKeep = data.map(func);
|
||||
return Promise.all(shouldKeep).then((keep) => {
|
||||
return data.filter((path, index) => {
|
||||
return keep[index];
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var statAsync = promisify(fs.stat);
|
||||
var readdirAsync = promisify(fs.readdir);
|
||||
var execAsync = promisify(childProcess.exec);
|
||||
|
||||
function udevParser(output) {
|
||||
var udevInfo = output.split('\n').reduce((info, line) => {
|
||||
if (!line || line.trim() === '') {
|
||||
return info;
|
||||
}
|
||||
var parts = line.split('=').map((part) => {
|
||||
return part.trim();
|
||||
});
|
||||
|
||||
info[parts[0].toLowerCase()] = parts[1];
|
||||
|
||||
return info;
|
||||
}, {});
|
||||
|
||||
var pnpId;
|
||||
if (udevInfo.devlinks) {
|
||||
udevInfo.devlinks.split(' ').forEach((path) => {
|
||||
if (path.indexOf('/by-id/') === -1) { return }
|
||||
pnpId = path.substring(path.lastIndexOf('/') + 1);
|
||||
});
|
||||
}
|
||||
|
||||
var vendorId = udevInfo.id_vendor_id;
|
||||
if (vendorId && vendorId.substring(0, 2) !== '0x') {
|
||||
vendorId = `0x${vendorId}`;
|
||||
}
|
||||
|
||||
var productId = udevInfo.id_model_id;
|
||||
if (productId && productId.substring(0, 2) !== '0x') {
|
||||
productId = `0x${productId}`;
|
||||
}
|
||||
|
||||
return {
|
||||
comName: udevInfo.devname,
|
||||
manufacturer: udevInfo.id_vendor,
|
||||
serialNumber: udevInfo.id_serial,
|
||||
pnpId: pnpId,
|
||||
vendorId: vendorId,
|
||||
productId: productId
|
||||
};
|
||||
}
|
||||
|
||||
function checkPathAndDevice(path) {
|
||||
// get only serial port names
|
||||
if (!(/(tty(S|ACM|USB|AMA|MFD)|rfcomm)/).test(path)) {
|
||||
return false;
|
||||
}
|
||||
return statAsync(path).then((stats) => {
|
||||
return stats.isCharacterDevice();
|
||||
});
|
||||
}
|
||||
|
||||
function lookupPort(file) {
|
||||
var udevadm = `udevadm info --query=property -p $(udevadm info -q path -n ${file})`;
|
||||
return execAsync(udevadm).then(udevParser);
|
||||
}
|
||||
|
||||
function listUnix(callback) {
|
||||
var dirName = '/dev';
|
||||
readdirAsync(dirName)
|
||||
.catch((err) => {
|
||||
// if this directory is not found we just pretend everything is OK
|
||||
// TODO Depreciated this check?
|
||||
if (err.errno === 34) {
|
||||
return [];
|
||||
}
|
||||
throw err;
|
||||
})
|
||||
.then((data) => { return data.map((file) => { return path.join(dirName, file) }) })
|
||||
.then(promisedFilter(checkPathAndDevice))
|
||||
.then((data) => { return Promise.all(data.map(lookupPort)) })
|
||||
.then((data) => { callback(null, data) }, (err) => { callback(err) });
|
||||
}
|
||||
|
||||
module.exports = listUnix;
|
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_ia32.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_ia32.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_x64.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_x64.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_ia32.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_ia32.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_x64.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_x64.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_ia32.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_ia32.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_x64.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_x64.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_ia32.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_ia32.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_x64.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_x64.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_ia32.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_ia32.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_x64.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_x64.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_ia32.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_ia32.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_x64.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_x64.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_ia32.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_ia32.node
поставляемый
Двоичный файл не отображается.
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_x64.node
поставляемый
Двоичные данные
vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_6.1.4_x64.node
поставляемый
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -1,24 +0,0 @@
|
|||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
const loadLibrary = function(parentFolder, libraryName) {
|
||||
const nodegypFiles = glob(path.join(__dirname, `../build/+(Release|Debug)/${libraryName}.node`), {
|
||||
sync: true
|
||||
});
|
||||
const nodepregypFiles = glob(`${parentFolder.replace(/\\/g, '/')}/${libraryName}*${process.arch}*.node`, {
|
||||
sync: true
|
||||
});
|
||||
var binding = null;
|
||||
nodegypFiles.concat(nodepregypFiles).forEach((file) => {
|
||||
try {
|
||||
var _temp = require(file);
|
||||
binding = _temp;
|
||||
console.log('using', file);
|
||||
} catch (e) {
|
||||
}
|
||||
});
|
||||
if (!binding) {
|
||||
console.log('[Warn]', 'no library available after trying files', nodegypFiles.concat(nodepregypFiles));
|
||||
}
|
||||
return binding;
|
||||
};
|
||||
exports.load = loadLibrary;
|
|
@ -1,64 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// Copyright 2011 Chris Williams <chris@iterativedesigns.com>
|
||||
|
||||
module.exports = {
|
||||
raw: function(emitter, buffer) {
|
||||
emitter.emit('data', buffer);
|
||||
},
|
||||
|
||||
// encoding: ascii utf8 utf16le ucs2 base64 binary hex
|
||||
// More: http://nodejs.org/api/buffer.html#buffer_buffer
|
||||
readline: function(delimiter, encoding) {
|
||||
if (typeof delimiter === 'undefined' || delimiter === null) { delimiter = '\r' }
|
||||
if (typeof encoding === 'undefined' || encoding === null) { encoding = 'utf8' }
|
||||
// Delimiter buffer saved in closure
|
||||
var data = '';
|
||||
return function(emitter, buffer) {
|
||||
// Collect data
|
||||
data += buffer.toString(encoding);
|
||||
// Split collected data by delimiter
|
||||
var parts = data.split(delimiter);
|
||||
data = parts.pop();
|
||||
parts.forEach((part) => {
|
||||
emitter.emit('data', part);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
// Emit a data event every `length` bytes
|
||||
byteLength: function(length) {
|
||||
var data = Buffer.alloc(0);
|
||||
return function(emitter, buffer) {
|
||||
data = Buffer.concat([data, buffer]);
|
||||
while (data.length >= length) {
|
||||
var out = data.slice(0, length);
|
||||
data = data.slice(length);
|
||||
emitter.emit('data', out);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// Emit a data event each time a byte sequence (delimiter is an array of byte) is found
|
||||
// Sample usage : byteDelimiter([10, 13])
|
||||
byteDelimiter: function(delimiter) {
|
||||
if (Object.prototype.toString.call(delimiter) !== '[object Array]') {
|
||||
delimiter = [ delimiter ];
|
||||
}
|
||||
var buf = [];
|
||||
var nextDelimIndex = 0;
|
||||
return function(emitter, buffer) {
|
||||
for (var i = 0; i < buffer.length; i++) {
|
||||
buf[buf.length] = buffer[i];
|
||||
if (buf[buf.length - 1] === delimiter[nextDelimIndex]) {
|
||||
nextDelimIndex++;
|
||||
}
|
||||
if (nextDelimIndex === delimiter.length) {
|
||||
emitter.emit('data', buf);
|
||||
buf = [];
|
||||
nextDelimIndex = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,502 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// Copyright 2011 Chris Williams <chris@iterativedesigns.com>
|
||||
|
||||
const _debug = false;
|
||||
const debug = (message) => {
|
||||
if (_debug) console.log(message);
|
||||
};
|
||||
|
||||
// shims
|
||||
// Internal Dependencies
|
||||
var SerialPortBinding = require('./bindings');
|
||||
var parsers = require('./parsers');
|
||||
|
||||
// Built-ins Dependencies
|
||||
var fs = require('fs');
|
||||
var stream = require('stream');
|
||||
var util = require('util');
|
||||
|
||||
// VALIDATION ARRAYS
|
||||
var DATABITS = [5, 6, 7, 8];
|
||||
var STOPBITS = [1, 1.5, 2];
|
||||
var PARITY = ['none', 'even', 'mark', 'odd', 'space'];
|
||||
var FLOWCONTROLS = ['xon', 'xoff', 'xany', 'rtscts'];
|
||||
var SET_OPTIONS = ['brk', 'cts', 'dtr', 'dts', 'rts'];
|
||||
|
||||
// Stuff from ReadStream, refactored for our usage:
|
||||
var kPoolSize = 40 * 1024;
|
||||
var kMinPoolSpace = 128;
|
||||
|
||||
var defaultSettings = {
|
||||
baudRate: 9600,
|
||||
autoOpen: true,
|
||||
parity: 'none',
|
||||
xon: false,
|
||||
xoff: false,
|
||||
xany: false,
|
||||
rtscts: false,
|
||||
hupcl: true,
|
||||
dataBits: 8,
|
||||
stopBits: 1,
|
||||
bufferSize: 64 * 1024,
|
||||
lock: true,
|
||||
parser: parsers.raw,
|
||||
platformOptions: SerialPortBinding.platformOptions
|
||||
};
|
||||
|
||||
var defaultSetFlags = {
|
||||
brk: false,
|
||||
cts: false,
|
||||
dtr: true,
|
||||
dts: false,
|
||||
rts: true
|
||||
};
|
||||
|
||||
// deprecate the lowercase version of these options next major release
|
||||
var LOWERCASE_OPTIONS = [
|
||||
'baudRate',
|
||||
'dataBits',
|
||||
'stopBits',
|
||||
'bufferSize',
|
||||
'platformOptions'
|
||||
];
|
||||
|
||||
function correctOptions(options) {
|
||||
LOWERCASE_OPTIONS.forEach((name) => {
|
||||
var lowerName = name.toLowerCase();
|
||||
if (options.hasOwnProperty(lowerName)) {
|
||||
var value = options[lowerName];
|
||||
delete options[lowerName];
|
||||
options[name] = value;
|
||||
}
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
function SerialPort(path, options, callback) {
|
||||
if (typeof callback === 'boolean') {
|
||||
throw new TypeError('`openImmediately` is now called `autoOpen` and is a property of options');
|
||||
}
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
stream.Stream.call(this);
|
||||
|
||||
if (!path) {
|
||||
throw new TypeError('No path specified');
|
||||
}
|
||||
|
||||
this.path = path;
|
||||
|
||||
var correctedOptions = correctOptions(options);
|
||||
var settings = Object.assign({}, defaultSettings, correctedOptions);
|
||||
|
||||
if (typeof settings.baudRate !== 'number') {
|
||||
throw new TypeError(`Invalid "baudRate" must be a number got: ${settings.baudRate}`);
|
||||
}
|
||||
|
||||
if (DATABITS.indexOf(settings.dataBits) === -1) {
|
||||
throw new TypeError(`Invalid "databits": ${settings.dataBits}`);
|
||||
}
|
||||
|
||||
if (STOPBITS.indexOf(settings.stopBits) === -1) {
|
||||
throw new TypeError(`Invalid "stopbits": ${settings.stopBits}`);
|
||||
}
|
||||
|
||||
if (PARITY.indexOf(settings.parity) === -1) {
|
||||
throw new TypeError(`Invalid "parity": ${settings.parity}`);
|
||||
}
|
||||
|
||||
FLOWCONTROLS.forEach((control) => {
|
||||
if (typeof settings[control] !== 'boolean') {
|
||||
throw new TypeError(`Invalid "${control}" is not boolean`);
|
||||
}
|
||||
});
|
||||
|
||||
settings.disconnectedCallback = this._disconnected.bind(this);
|
||||
settings.dataCallback = settings.parser.bind(this, this);
|
||||
|
||||
this.fd = null;
|
||||
this.paused = true;
|
||||
this.opening = false;
|
||||
this.closing = false;
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
this.bufferSize = settings.bufferSize;
|
||||
this.readable = true;
|
||||
this.reading = false;
|
||||
}
|
||||
|
||||
this.options = settings;
|
||||
|
||||
if (this.options.autoOpen) {
|
||||
// is nextTick necessary?
|
||||
process.nextTick(this.open.bind(this, callback));
|
||||
}
|
||||
}
|
||||
|
||||
util.inherits(SerialPort, stream.Stream);
|
||||
|
||||
SerialPort.prototype._error = function(error, callback) {
|
||||
if (callback) {
|
||||
callback.call(this, error);
|
||||
} else {
|
||||
this.emit('error', error);
|
||||
}
|
||||
};
|
||||
|
||||
SerialPort.prototype.open = function(callback) {
|
||||
if (this.isOpen()) {
|
||||
return this._error(new Error('Port is already open'), callback);
|
||||
}
|
||||
|
||||
if (this.opening) {
|
||||
return this._error(new Error('Port is opening'), callback);
|
||||
}
|
||||
|
||||
this.paused = true;
|
||||
this.readable = true;
|
||||
this.reading = false;
|
||||
this.opening = true;
|
||||
|
||||
SerialPortBinding.open(this.path, this.options, (err, fd) => {
|
||||
this.opening = false;
|
||||
if (err) {
|
||||
debug('SerialPortBinding.open had an error', err);
|
||||
return this._error(err, callback);
|
||||
}
|
||||
this.fd = fd;
|
||||
this.paused = false;
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
this.serialPoller = new SerialPortBinding.SerialportPoller(this.fd, (err) => {
|
||||
if (!err) {
|
||||
this._read();
|
||||
} else {
|
||||
this._disconnected(err);
|
||||
}
|
||||
});
|
||||
this.serialPoller.start();
|
||||
}
|
||||
|
||||
this.emit('open');
|
||||
if (callback) {
|
||||
callback.call(this, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SerialPort.prototype.update = function(options, callback) {
|
||||
if (!this.isOpen()) {
|
||||
debug('update attempted, but port is not open');
|
||||
return this._error(new Error('Port is not open'), callback);
|
||||
}
|
||||
|
||||
var correctedOptions = correctOptions(options);
|
||||
var settings = Object.assign({}, defaultSettings, correctedOptions);
|
||||
this.options.baudRate = settings.baudRate;
|
||||
|
||||
SerialPortBinding.update(this.fd, this.options, (err) => {
|
||||
if (err) {
|
||||
return this._error(err, callback);
|
||||
}
|
||||
if (callback) {
|
||||
callback.call(this, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SerialPort.prototype.isOpen = function() {
|
||||
return this.fd !== null && !this.closing;
|
||||
};
|
||||
|
||||
SerialPort.prototype.write = function(buffer, ending, callback) {
|
||||
if (!this.isOpen()) {
|
||||
debug('write attempted, but port is not open');
|
||||
return this._error(new Error('Port is not open'), callback);
|
||||
}
|
||||
|
||||
if (!Buffer.isBuffer(buffer)) {
|
||||
buffer = Buffer.from(buffer);
|
||||
}
|
||||
|
||||
switch (ending) {
|
||||
case 'Newline':
|
||||
buffer = Buffer.concat([buffer, Buffer.from('\n')]);
|
||||
break;
|
||||
case 'Carriage return':
|
||||
buffer = Buffer.concat([buffer, Buffer.from('\r')]);
|
||||
break;
|
||||
case 'Both NL & CR':
|
||||
buffer = Buffer.concat([buffer, Buffer.from('\r\n')]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
debug(`write ${buffer.length} bytes of data`);
|
||||
SerialPortBinding.write(this.fd, buffer, (err) => {
|
||||
if (err) {
|
||||
debug('SerialPortBinding.write had an error', err);
|
||||
return this._error(err, callback);
|
||||
}
|
||||
if (callback) {
|
||||
callback.call(this, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
SerialPort.prototype._read = function() {
|
||||
if (!this.readable || this.paused || this.reading || this.closing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.reading = true;
|
||||
|
||||
if (!this.pool || this.pool.length - this.pool.used < kMinPoolSpace) {
|
||||
// discard the old pool. Can't add to the free list because
|
||||
// users might have references to slices on it.
|
||||
this.pool = Buffer.alloc(kPoolSize);
|
||||
this.pool.used = 0;
|
||||
}
|
||||
|
||||
// Grab another reference to the pool in the case that while we're in the
|
||||
// thread pool another read() finishes up the pool, and allocates a new
|
||||
// one.
|
||||
var toRead = Math.min(this.pool.length - this.pool.used, ~~this.bufferSize);
|
||||
var start = this.pool.used;
|
||||
|
||||
var _afterRead = (err, bytesRead, readPool, bytesRequested) => {
|
||||
this.reading = false;
|
||||
if (err) {
|
||||
if (err.code && err.code === 'EAGAIN') {
|
||||
if (this.isOpen()) {
|
||||
this.serialPoller.start();
|
||||
}
|
||||
// handle edge case were mac/unix doesn't clearly know the error.
|
||||
} else if (err.code && (err.code === 'EBADF' || err.code === 'ENXIO' || (err.errno === -1 || err.code === 'UNKNOWN'))) {
|
||||
this._disconnected(err);
|
||||
} else {
|
||||
this.fd = null;
|
||||
this.readable = false;
|
||||
this.emit('error', err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Since we will often not read the number of bytes requested,
|
||||
// let's mark the ones we didn't need as available again.
|
||||
this.pool.used -= bytesRequested - bytesRead;
|
||||
|
||||
if (bytesRead === 0) {
|
||||
if (this.isOpen()) {
|
||||
this.serialPoller.start();
|
||||
}
|
||||
} else {
|
||||
var b = this.pool.slice(start, start + bytesRead);
|
||||
|
||||
// do not emit events if the stream is paused
|
||||
if (this.paused) {
|
||||
if (!this.buffer) {
|
||||
this.buffer = Buffer.alloc(0);
|
||||
}
|
||||
this.buffer = Buffer.concat([this.buffer, b]);
|
||||
return;
|
||||
}
|
||||
this._emitData(b);
|
||||
|
||||
// do not emit events anymore after we declared the stream unreadable
|
||||
if (!this.readable) {
|
||||
return;
|
||||
}
|
||||
this._read();
|
||||
}
|
||||
};
|
||||
|
||||
fs.read(this.fd, this.pool, this.pool.used, toRead, null, (err, bytesRead) => {
|
||||
var readPool = this.pool;
|
||||
var bytesRequested = toRead;
|
||||
_afterRead(err, bytesRead, readPool, bytesRequested);
|
||||
});
|
||||
|
||||
this.pool.used += toRead;
|
||||
};
|
||||
|
||||
SerialPort.prototype._emitData = function(data) {
|
||||
this.options.dataCallback(data);
|
||||
};
|
||||
|
||||
SerialPort.prototype.pause = function() {
|
||||
this.paused = true;
|
||||
};
|
||||
|
||||
SerialPort.prototype.resume = function() {
|
||||
this.paused = false;
|
||||
|
||||
if (this.buffer) {
|
||||
var buffer = this.buffer;
|
||||
this.buffer = null;
|
||||
this._emitData(buffer);
|
||||
}
|
||||
|
||||
// No longer open?
|
||||
if (!this.isOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._read();
|
||||
};
|
||||
} // if !'win32'
|
||||
|
||||
SerialPort.prototype._disconnected = function(err) {
|
||||
this.paused = true;
|
||||
this.emit('disconnect', err);
|
||||
if (this.closing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.fd === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.closing = true;
|
||||
if (process.platform !== 'win32') {
|
||||
this.readable = false;
|
||||
this.serialPoller.close();
|
||||
}
|
||||
|
||||
SerialPortBinding.close(this.fd, (err) => {
|
||||
this.closing = false;
|
||||
if (err) {
|
||||
debug('Disconnect close completed with error: ', err);
|
||||
}
|
||||
this.fd = null;
|
||||
this.emit('close');
|
||||
});
|
||||
};
|
||||
|
||||
SerialPort.prototype.close = function(callback) {
|
||||
this.paused = true;
|
||||
|
||||
if (this.closing) {
|
||||
debug('close attempted, but port is already closing');
|
||||
return this._error(new Error('Port is not open'), callback);
|
||||
}
|
||||
|
||||
if (!this.isOpen()) {
|
||||
debug('close attempted, but port is not open');
|
||||
return this._error(new Error('Port is not open'), callback);
|
||||
}
|
||||
|
||||
this.closing = true;
|
||||
|
||||
// Stop polling before closing the port.
|
||||
if (process.platform !== 'win32') {
|
||||
this.readable = false;
|
||||
this.serialPoller.close();
|
||||
}
|
||||
SerialPortBinding.close(this.fd, (err) => {
|
||||
this.closing = false;
|
||||
if (err) {
|
||||
debug('SerialPortBinding.close had an error', err);
|
||||
return this._error(err, callback);
|
||||
}
|
||||
|
||||
this.fd = null;
|
||||
this.emit('close');
|
||||
if (callback) {
|
||||
callback.call(this, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SerialPort.prototype.flush = function(callback) {
|
||||
if (!this.isOpen()) {
|
||||
debug('flush attempted, but port is not open');
|
||||
return this._error(new Error('Port is not open'), callback);
|
||||
}
|
||||
|
||||
SerialPortBinding.flush(this.fd, (err, result) => {
|
||||
if (err) {
|
||||
debug('SerialPortBinding.flush had an error', err);
|
||||
return this._error(err, callback);
|
||||
}
|
||||
if (callback) {
|
||||
callback.call(this, null, result);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SerialPort.prototype.set = function(options, callback) {
|
||||
if (!this.isOpen()) {
|
||||
debug('set attempted, but port is not open');
|
||||
return this._error(new Error('Port is not open'), callback);
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
if (!callback && typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
var settings = {};
|
||||
for (var i = SET_OPTIONS.length - 1; i >= 0; i--) {
|
||||
var flag = SET_OPTIONS[i];
|
||||
if (options[flag] !== undefined) {
|
||||
settings[flag] = options[flag];
|
||||
} else {
|
||||
settings[flag] = defaultSetFlags[flag];
|
||||
}
|
||||
}
|
||||
|
||||
SerialPortBinding.set(this.fd, settings, (err) => {
|
||||
if (err) {
|
||||
debug('SerialPortBinding.set had an error', err);
|
||||
return this._error(err, callback);
|
||||
}
|
||||
if (callback) {
|
||||
callback.call(this, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SerialPort.prototype.drain = function(callback) {
|
||||
if (!this.isOpen()) {
|
||||
debug('drain attempted, but port is not open');
|
||||
return this._error(new Error('Port is not open'), callback);
|
||||
}
|
||||
|
||||
SerialPortBinding.drain(this.fd, (err) => {
|
||||
if (err) {
|
||||
debug('SerialPortBinding.drain had an error', err);
|
||||
return this._error(err, callback);
|
||||
}
|
||||
if (callback) {
|
||||
callback.call(this, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SerialPort.parsers = parsers;
|
||||
SerialPort.list = SerialPortBinding.list;
|
||||
|
||||
// Write a depreciation warning once
|
||||
Object.defineProperty(SerialPort, 'SerialPort', {
|
||||
get: function() {
|
||||
// console.warn('DEPRECATION: Please use `require(\'serialport\')` instead of `require(\'serialport\').SerialPort`');
|
||||
Object.defineProperty(SerialPort, 'SerialPort', {
|
||||
value: SerialPort
|
||||
});
|
||||
return SerialPort;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
|
||||
module.exports = SerialPort;
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2013 Kaba AG
|
||||
|
||||
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.
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"name": "usb-native",
|
||||
"version": "0.0.1",
|
||||
"description": "node-usb && node-serialport combined.",
|
||||
"main": "lib/index.js",
|
||||
"gypfile": true,
|
||||
"keywords": [
|
||||
"usb",
|
||||
"device",
|
||||
"hardware",
|
||||
"list",
|
||||
"insert",
|
||||
"add",
|
||||
"remove",
|
||||
"change",
|
||||
"plug",
|
||||
"unplug",
|
||||
"notification"
|
||||
],
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"install": "node-gyp rebuild --target=1.6.6 --arch=x64 --dist-url=https://atom.io/download/electron"
|
||||
},
|
||||
"dependencies": {
|
||||
"eventemitter2": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^3.0.0",
|
||||
"chai-as-promised": "^5.1.0",
|
||||
"chalk": "^1.0.0",
|
||||
"mocha": "^2.2.5",
|
||||
"nan": "^2.12.1"
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
#include <node.h>
|
||||
#include <v8.h>
|
||||
#include <uv.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <nan.h>
|
||||
|
||||
extern "C" {
|
||||
void init_serialport( v8::Local<v8::Object> target);
|
||||
#ifndef DISABLE_USB_DETECTOR
|
||||
void init_detector( v8::Local<v8::Object> target);
|
||||
#endif
|
||||
void initAll( v8::Local<v8::Object> target) {
|
||||
init_serialport(target);
|
||||
#ifndef DISABLE_USB_DETECTOR
|
||||
init_detector(target);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
NODE_MODULE(detection, initAll);
|
|
@ -1,222 +0,0 @@
|
|||
#include "detection.h"
|
||||
|
||||
|
||||
#define OBJECT_ITEM_LOCATION_ID "locationId"
|
||||
#define OBJECT_ITEM_VENDOR_ID "vendorId"
|
||||
#define OBJECT_ITEM_PRODUCT_ID "productId"
|
||||
#define OBJECT_ITEM_DEVICE_NAME "deviceName"
|
||||
#define OBJECT_ITEM_MANUFACTURER "manufacturer"
|
||||
#define OBJECT_ITEM_SERIAL_NUMBER "serialNumber"
|
||||
#define OBJECT_ITEM_DEVICE_ADDRESS "deviceAddress"
|
||||
|
||||
|
||||
Nan::Callback* addedCallback;
|
||||
bool isAddedRegistered = false;
|
||||
|
||||
Nan::Callback* removedCallback;
|
||||
bool isRemovedRegistered = false;
|
||||
|
||||
void RegisterAdded(const Nan::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
v8::Local<v8::Function> callback;
|
||||
|
||||
if (args.Length() == 0) {
|
||||
return Nan::ThrowTypeError("First argument must be a function");
|
||||
}
|
||||
|
||||
if (args.Length() == 1) {
|
||||
// callback
|
||||
if(!args[0]->IsFunction()) {
|
||||
return Nan::ThrowTypeError("First argument must be a function");
|
||||
}
|
||||
|
||||
callback = args[0].As<v8::Function>();
|
||||
}
|
||||
|
||||
addedCallback = new Nan::Callback(callback);
|
||||
isAddedRegistered = true;
|
||||
}
|
||||
|
||||
void NotifyAdded(ListResultItem_t* it) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
if (it == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAddedRegistered){
|
||||
v8::Local<v8::Value> argv[1];
|
||||
v8::Local<v8::Object> item = Nan::New<v8::Object>();
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New<v8::Number>(it->locationId));
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New<v8::Number>(it->vendorId));
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New<v8::Number>(it->productId));
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New<v8::String>(it->deviceName.c_str()).ToLocalChecked());
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New<v8::String>(it->manufacturer.c_str()).ToLocalChecked());
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New<v8::String>(it->serialNumber.c_str()).ToLocalChecked());
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New<v8::Number>(it->deviceAddress));
|
||||
argv[0] = item;
|
||||
|
||||
addedCallback->Call(1, argv);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterRemoved(const Nan::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
v8::Local<v8::Function> callback;
|
||||
|
||||
if (args.Length() == 0) {
|
||||
return Nan::ThrowTypeError("First argument must be a function");
|
||||
}
|
||||
|
||||
if (args.Length() == 1) {
|
||||
// callback
|
||||
if(!args[0]->IsFunction()) {
|
||||
return Nan::ThrowTypeError("First argument must be a function");
|
||||
}
|
||||
|
||||
callback = args[0].As<v8::Function>();
|
||||
}
|
||||
|
||||
removedCallback = new Nan::Callback(callback);
|
||||
isRemovedRegistered = true;
|
||||
}
|
||||
|
||||
void NotifyRemoved(ListResultItem_t* it) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
if (it == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRemovedRegistered) {
|
||||
v8::Local<v8::Value> argv[1];
|
||||
v8::Local<v8::Object> item = Nan::New<v8::Object>();
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New<v8::Number>(it->locationId));
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New<v8::Number>(it->vendorId));
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New<v8::Number>(it->productId));
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New<v8::String>(it->deviceName.c_str()).ToLocalChecked());
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New<v8::String>(it->manufacturer.c_str()).ToLocalChecked());
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New<v8::String>(it->serialNumber.c_str()).ToLocalChecked());
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New<v8::Number>(it->deviceAddress));
|
||||
argv[0] = item;
|
||||
|
||||
removedCallback->Call(1, argv);
|
||||
}
|
||||
}
|
||||
|
||||
void Find(const Nan::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
int vid = 0;
|
||||
int pid = 0;
|
||||
v8::Local<v8::Function> callback;
|
||||
|
||||
if (args.Length() == 0) {
|
||||
return Nan::ThrowTypeError("First argument must be a function");
|
||||
}
|
||||
|
||||
if (args.Length() == 3) {
|
||||
if (args[0]->IsNumber() && args[1]->IsNumber()) {
|
||||
vid = (int) args[0]->NumberValue();
|
||||
pid = (int) args[1]->NumberValue();
|
||||
}
|
||||
|
||||
// callback
|
||||
if(!args[2]->IsFunction()) {
|
||||
return Nan::ThrowTypeError("Third argument must be a function");
|
||||
}
|
||||
|
||||
callback = args[2].As<v8::Function>();
|
||||
}
|
||||
|
||||
if (args.Length() == 2) {
|
||||
if (args[0]->IsNumber()) {
|
||||
vid = (int) args[0]->NumberValue();
|
||||
}
|
||||
|
||||
// callback
|
||||
if(!args[1]->IsFunction()) {
|
||||
return Nan::ThrowTypeError("Second argument must be a function");
|
||||
}
|
||||
|
||||
callback = args[1].As<v8::Function>();
|
||||
}
|
||||
|
||||
if (args.Length() == 1) {
|
||||
// callback
|
||||
if(!args[0]->IsFunction()) {
|
||||
return Nan::ThrowTypeError("First argument must be a function");
|
||||
}
|
||||
|
||||
callback = args[0].As<v8::Function>();
|
||||
}
|
||||
|
||||
ListBaton* baton = new ListBaton();
|
||||
strcpy(baton->errorString, "");
|
||||
baton->callback = new Nan::Callback(callback);
|
||||
baton->vid = vid;
|
||||
baton->pid = pid;
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
req->data = baton;
|
||||
uv_queue_work(uv_default_loop(), req, EIO_Find, (uv_after_work_cb)EIO_AfterFind);
|
||||
}
|
||||
|
||||
void EIO_AfterFind(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
ListBaton* data = static_cast<ListBaton*>(req->data);
|
||||
|
||||
v8::Local<v8::Value> argv[2];
|
||||
if(data->errorString[0]) {
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(data->errorString).ToLocalChecked());
|
||||
argv[1] = Nan::Undefined();
|
||||
}
|
||||
else {
|
||||
v8::Local<v8::Array> results = Nan::New<v8::Array>();
|
||||
int i = 0;
|
||||
for(std::list<ListResultItem_t*>::iterator it = data->results.begin(); it != data->results.end(); it++, i++) {
|
||||
v8::Local<v8::Object> item = Nan::New<v8::Object>();
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New<v8::Number>((*it)->locationId));
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New<v8::Number>((*it)->vendorId));
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New<v8::Number>((*it)->productId));
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New<v8::String>((*it)->deviceName.c_str()).ToLocalChecked());
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New<v8::String>((*it)->manufacturer.c_str()).ToLocalChecked());
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New<v8::String>((*it)->serialNumber.c_str()).ToLocalChecked());
|
||||
item->Set(Nan::New<v8::String>(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New<v8::Number>((*it)->deviceAddress));
|
||||
results->Set(i, item);
|
||||
}
|
||||
argv[0] = Nan::Undefined();
|
||||
argv[1] = results;
|
||||
}
|
||||
|
||||
data->callback->Call(2, argv);
|
||||
|
||||
for(std::list<ListResultItem_t*>::iterator it = data->results.begin(); it != data->results.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
delete data;
|
||||
delete req;
|
||||
}
|
||||
|
||||
void StartMonitoring(const Nan::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Start();
|
||||
}
|
||||
|
||||
void StopMonitoring(const Nan::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Stop();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void init_detector ( v8::Local<v8::Object> target) {
|
||||
Nan::SetMethod(target, "find", Find);
|
||||
Nan::SetMethod(target, "registerAdded", RegisterAdded);
|
||||
Nan::SetMethod(target, "registerRemoved", RegisterRemoved);
|
||||
Nan::SetMethod(target, "startMonitoring", StartMonitoring);
|
||||
Nan::SetMethod(target, "stopMonitoring", StopMonitoring);
|
||||
InitDetection();
|
||||
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
|
||||
#ifndef _USB_DETECTION_H
|
||||
#define _USB_DETECTION_H
|
||||
|
||||
#include <node.h>
|
||||
#include <v8.h>
|
||||
#include <uv.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <nan.h>
|
||||
|
||||
#include "deviceList.h"
|
||||
|
||||
void Find(const Nan::FunctionCallbackInfo<v8::Value>& args);
|
||||
void EIO_Find(uv_work_t* req);
|
||||
void EIO_AfterFind(uv_work_t* req);
|
||||
void InitDetection();
|
||||
void StartMonitoring(const Nan::FunctionCallbackInfo<v8::Value>& args);
|
||||
void Start();
|
||||
void StopMonitoring(const Nan::FunctionCallbackInfo<v8::Value>& args);
|
||||
void Stop();
|
||||
|
||||
|
||||
struct ListBaton {
|
||||
public:
|
||||
//v8::Persistent<v8::Function> callback;
|
||||
Nan::Callback* callback;
|
||||
std::list<ListResultItem_t*> results;
|
||||
char errorString[1024];
|
||||
int vid;
|
||||
int pid;
|
||||
};
|
||||
|
||||
void RegisterAdded(const Nan::FunctionCallbackInfo<v8::Value>& args);
|
||||
void NotifyAdded(ListResultItem_t* it);
|
||||
void RegisterRemoved(const Nan::FunctionCallbackInfo<v8::Value>& args);
|
||||
void NotifyRemoved(ListResultItem_t* it);
|
||||
|
||||
#endif
|
|
@ -1,317 +0,0 @@
|
|||
#include <libudev.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "detection.h"
|
||||
#include "deviceList.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
/**********************************
|
||||
* Local defines
|
||||
**********************************/
|
||||
#define DEVICE_ACTION_ADDED "add"
|
||||
#define DEVICE_ACTION_REMOVED "remove"
|
||||
|
||||
#define DEVICE_TYPE_DEVICE "usb_device"
|
||||
|
||||
#define DEVICE_PROPERTY_NAME "ID_MODEL"
|
||||
#define DEVICE_PROPERTY_SERIAL "ID_SERIAL_SHORT"
|
||||
#define DEVICE_PROPERTY_VENDOR "ID_VENDOR"
|
||||
|
||||
|
||||
/**********************************
|
||||
* Local typedefs
|
||||
**********************************/
|
||||
|
||||
|
||||
|
||||
/**********************************
|
||||
* Local Variables
|
||||
**********************************/
|
||||
ListResultItem_t* currentItem;
|
||||
bool isAdded;
|
||||
|
||||
struct udev *udev;
|
||||
struct udev_enumerate *enumerate;
|
||||
struct udev_list_entry *devices, *dev_list_entry;
|
||||
struct udev_device *dev;
|
||||
|
||||
struct udev_monitor *mon;
|
||||
int fd;
|
||||
|
||||
pthread_t thread;
|
||||
pthread_mutex_t notify_mutex;
|
||||
pthread_cond_t notifyNewDevice;
|
||||
pthread_cond_t notifyDeviceHandled;
|
||||
|
||||
bool newDeviceAvailable = false;
|
||||
bool deviceHandled = true;
|
||||
|
||||
bool isRunning = false;
|
||||
/**********************************
|
||||
* Local Helper Functions protoypes
|
||||
**********************************/
|
||||
void BuildInitialDeviceList();
|
||||
|
||||
void* ThreadFunc(void* ptr);
|
||||
void WaitForDeviceHandled();
|
||||
void SignalDeviceHandled();
|
||||
void WaitForNewDevice();
|
||||
void SignalDeviceAvailable();
|
||||
|
||||
/**********************************
|
||||
* Public Functions
|
||||
**********************************/
|
||||
void NotifyAsync(uv_work_t* req) {
|
||||
WaitForNewDevice();
|
||||
}
|
||||
|
||||
void NotifyFinished(uv_work_t* req) {
|
||||
if (isRunning) {
|
||||
if (isAdded) {
|
||||
NotifyAdded(currentItem);
|
||||
}
|
||||
else {
|
||||
NotifyRemoved(currentItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Item in case of removal
|
||||
if(isAdded == false) {
|
||||
delete currentItem;
|
||||
}
|
||||
|
||||
SignalDeviceHandled();
|
||||
uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);
|
||||
}
|
||||
|
||||
void Start() {
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
isRunning = false;
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
pthread_cond_signal(¬ifyNewDevice);
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
void InitDetection() {
|
||||
/* Create the udev object */
|
||||
udev = udev_new();
|
||||
if (!udev)
|
||||
{
|
||||
printf("Can't create udev\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set up a monitor to monitor devices */
|
||||
mon = udev_monitor_new_from_netlink(udev, "udev");
|
||||
udev_monitor_enable_receiving(mon);
|
||||
|
||||
/* Get the file descriptor (fd) for the monitor.
|
||||
This fd will get passed to select() */
|
||||
fd = udev_monitor_get_fd(mon);
|
||||
|
||||
BuildInitialDeviceList();
|
||||
|
||||
pthread_mutex_init(¬ify_mutex, NULL);
|
||||
pthread_cond_init(¬ifyNewDevice, NULL);
|
||||
pthread_cond_init(¬ifyDeviceHandled, NULL);
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);
|
||||
|
||||
pthread_create(&thread, NULL, ThreadFunc, NULL);
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
|
||||
void EIO_Find(uv_work_t* req) {
|
||||
ListBaton* data = static_cast<ListBaton*>(req->data);
|
||||
|
||||
CreateFilteredList(&data->results, data->vid, data->pid);
|
||||
}
|
||||
|
||||
/**********************************
|
||||
* Local Functions
|
||||
**********************************/
|
||||
void WaitForDeviceHandled() {
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
if(deviceHandled == false) {
|
||||
pthread_cond_wait(¬ifyDeviceHandled, ¬ify_mutex);
|
||||
}
|
||||
deviceHandled = false;
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
void SignalDeviceHandled() {
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
deviceHandled = true;
|
||||
pthread_cond_signal(¬ifyDeviceHandled);
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
void WaitForNewDevice() {
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
if(newDeviceAvailable == false){
|
||||
pthread_cond_wait(¬ifyNewDevice, ¬ify_mutex);
|
||||
}
|
||||
newDeviceAvailable = false;
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
void SignalDeviceAvailable() {
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
newDeviceAvailable = true;
|
||||
pthread_cond_signal(¬ifyNewDevice);
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
|
||||
ListResultItem_t* GetProperties(struct udev_device* dev, ListResultItem_t* item) {
|
||||
struct udev_list_entry* sysattrs;
|
||||
struct udev_list_entry* entry;
|
||||
sysattrs = udev_device_get_properties_list_entry(dev);
|
||||
udev_list_entry_foreach(entry, sysattrs) {
|
||||
const char *name, *value;
|
||||
name = udev_list_entry_get_name(entry);
|
||||
value = udev_list_entry_get_value(entry);
|
||||
|
||||
if(strcmp(name, DEVICE_PROPERTY_NAME) == 0) {
|
||||
item->deviceName = value;
|
||||
}
|
||||
else if(strcmp(name, DEVICE_PROPERTY_SERIAL) == 0) {
|
||||
item->serialNumber = value;
|
||||
}
|
||||
else if(strcmp(name, DEVICE_PROPERTY_VENDOR) == 0) {
|
||||
item->manufacturer = value;
|
||||
}
|
||||
}
|
||||
item->vendorId = strtol(udev_device_get_sysattr_value(dev,"idVendor"), NULL, 16);
|
||||
item->productId = strtol(udev_device_get_sysattr_value(dev,"idProduct"), NULL, 16);
|
||||
item->deviceAddress = 0;
|
||||
item->locationId = 0;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void DeviceAdded(struct udev_device* dev) {
|
||||
DeviceItem_t* item = new DeviceItem_t();
|
||||
GetProperties(dev, &item->deviceParams);
|
||||
|
||||
AddItemToList((char *)udev_device_get_devnode(dev), item);
|
||||
|
||||
currentItem = &item->deviceParams;
|
||||
isAdded = true;
|
||||
|
||||
SignalDeviceAvailable();
|
||||
}
|
||||
|
||||
void DeviceRemoved(struct udev_device* dev) {
|
||||
ListResultItem_t* item = NULL;
|
||||
|
||||
if(IsItemAlreadyStored((char *)udev_device_get_devnode(dev))) {
|
||||
DeviceItem_t* deviceItem = GetItemFromList((char *)udev_device_get_devnode(dev));
|
||||
if(deviceItem) {
|
||||
item = CopyElement(&deviceItem->deviceParams);
|
||||
}
|
||||
RemoveItemFromList(deviceItem);
|
||||
delete deviceItem;
|
||||
}
|
||||
|
||||
if(item == NULL) {
|
||||
item = new ListResultItem_t();
|
||||
GetProperties(dev, item);
|
||||
}
|
||||
|
||||
currentItem = item;
|
||||
isAdded = false;
|
||||
|
||||
SignalDeviceAvailable();
|
||||
}
|
||||
|
||||
|
||||
void* ThreadFunc(void* ptr) {
|
||||
while (1) {
|
||||
/* Make the call to receive the device.
|
||||
select() ensured that this will not block. */
|
||||
dev = udev_monitor_receive_device(mon);
|
||||
if (dev) {
|
||||
if(udev_device_get_devtype(dev) && strcmp(udev_device_get_devtype(dev), DEVICE_TYPE_DEVICE) == 0) {
|
||||
if(strcmp(udev_device_get_action(dev), DEVICE_ACTION_ADDED) == 0) {
|
||||
WaitForDeviceHandled();
|
||||
DeviceAdded(dev);
|
||||
}
|
||||
else if(strcmp(udev_device_get_action(dev), DEVICE_ACTION_REMOVED) == 0) {
|
||||
WaitForDeviceHandled();
|
||||
DeviceRemoved(dev);
|
||||
}
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void BuildInitialDeviceList() {
|
||||
/* Create a list of the devices */
|
||||
enumerate = udev_enumerate_new(udev);
|
||||
udev_enumerate_scan_devices(enumerate);
|
||||
devices = udev_enumerate_get_list_entry(enumerate);
|
||||
/* For each item enumerated, print out its information.
|
||||
udev_list_entry_foreach is a macro which expands to
|
||||
a loop. The loop will be executed for each member in
|
||||
devices, setting dev_list_entry to a list entry
|
||||
which contains the device's path in /sys. */
|
||||
udev_list_entry_foreach(dev_list_entry, devices) {
|
||||
const char *path;
|
||||
|
||||
/* Get the filename of the /sys entry for the device
|
||||
and create a udev_device object (dev) representing it */
|
||||
path = udev_list_entry_get_name(dev_list_entry);
|
||||
dev = udev_device_new_from_syspath(udev, path);
|
||||
|
||||
/* usb_device_get_devnode() returns the path to the device node
|
||||
itself in /dev. */
|
||||
if(udev_device_get_devnode(dev) == NULL || udev_device_get_sysattr_value(dev,"idVendor") == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* From here, we can call get_sysattr_value() for each file
|
||||
in the device's /sys entry. The strings passed into these
|
||||
functions (idProduct, idVendor, serial, etc.) correspond
|
||||
directly to the files in the /sys directory which
|
||||
represents the USB device. Note that USB strings are
|
||||
Unicode, UCS2 encoded, but the strings returned from
|
||||
udev_device_get_sysattr_value() are UTF-8 encoded. */
|
||||
|
||||
DeviceItem_t* item = new DeviceItem_t();
|
||||
item->deviceParams.vendorId = strtol (udev_device_get_sysattr_value(dev,"idVendor"), NULL, 16);
|
||||
item->deviceParams.productId = strtol (udev_device_get_sysattr_value(dev,"idProduct"), NULL, 16);
|
||||
if(udev_device_get_sysattr_value(dev,"product") != NULL) {
|
||||
item->deviceParams.deviceName = udev_device_get_sysattr_value(dev,"product");
|
||||
}
|
||||
if(udev_device_get_sysattr_value(dev,"manufacturer") != NULL) {
|
||||
item->deviceParams.manufacturer = udev_device_get_sysattr_value(dev,"manufacturer");
|
||||
}
|
||||
if(udev_device_get_sysattr_value(dev,"serial") != NULL) {
|
||||
item->deviceParams.serialNumber = udev_device_get_sysattr_value(dev, "serial");
|
||||
}
|
||||
item->deviceParams.deviceAddress = 0;
|
||||
item->deviceParams.locationId = 0;
|
||||
|
||||
item->deviceState = DeviceState_Connect;
|
||||
|
||||
AddItemToList((char *)udev_device_get_devnode(dev), item);
|
||||
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
/* Free the enumerator object */
|
||||
udev_enumerate_unref(enumerate);
|
||||
}
|
|
@ -1,461 +0,0 @@
|
|||
#include "detection.h"
|
||||
#include "deviceList.h"
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/IOMessage.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
|
||||
typedef struct DeviceListItem {
|
||||
io_object_t notification;
|
||||
IOUSBDeviceInterface** deviceInterface;
|
||||
DeviceItem_t* deviceItem;
|
||||
} stDeviceListItem;
|
||||
|
||||
static IONotificationPortRef gNotifyPort;
|
||||
static io_iterator_t gAddedIter;
|
||||
static CFRunLoopRef gRunLoop;
|
||||
|
||||
|
||||
CFMutableDictionaryRef matchingDict;
|
||||
CFRunLoopSourceRef runLoopSource;
|
||||
|
||||
static pthread_t lookupThread;
|
||||
|
||||
pthread_mutex_t notify_mutex;
|
||||
pthread_cond_t notifyNewDevice;
|
||||
pthread_cond_t notifyDeviceHandled;
|
||||
|
||||
bool newDeviceAvailable = false;
|
||||
bool deviceHandled = true;
|
||||
|
||||
ListResultItem_t* notify_item;
|
||||
bool isAdded = false;
|
||||
bool isRunning = false;
|
||||
bool intialDeviceImport = true;
|
||||
|
||||
void WaitForDeviceHandled();
|
||||
void SignalDeviceHandled();
|
||||
void WaitForNewDevice();
|
||||
void SignalDeviceAvailable();
|
||||
|
||||
//================================================================================================
|
||||
//
|
||||
// DeviceRemoved
|
||||
//
|
||||
// This routine will get called whenever any kIOGeneralInterest notification happens. We are
|
||||
// interested in the kIOMessageServiceIsTerminated message so that's what we look for. Other
|
||||
// messages are defined in IOMessage.h.
|
||||
//
|
||||
//================================================================================================
|
||||
void DeviceRemoved(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) {
|
||||
kern_return_t kr;
|
||||
stDeviceListItem* deviceListItem = (stDeviceListItem *) refCon;
|
||||
DeviceItem_t* deviceItem = deviceListItem->deviceItem;
|
||||
|
||||
if(messageType == kIOMessageServiceIsTerminated) {
|
||||
if(deviceListItem->deviceInterface) {
|
||||
kr = (*deviceListItem->deviceInterface)->Release(deviceListItem->deviceInterface);
|
||||
}
|
||||
|
||||
kr = IOObjectRelease(deviceListItem->notification);
|
||||
|
||||
|
||||
ListResultItem_t* item = NULL;
|
||||
if(deviceItem) {
|
||||
item = CopyElement(&deviceItem->deviceParams);
|
||||
RemoveItemFromList(deviceItem);
|
||||
delete deviceItem;
|
||||
}
|
||||
else {
|
||||
item = new ListResultItem_t();
|
||||
}
|
||||
|
||||
WaitForDeviceHandled();
|
||||
notify_item = item;
|
||||
isAdded = false;
|
||||
SignalDeviceAvailable();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================================
|
||||
//
|
||||
// DeviceAdded
|
||||
//
|
||||
// This routine is the callback for our IOServiceAddMatchingNotification. When we get called
|
||||
// we will look at all the devices that were added and we will:
|
||||
//
|
||||
// 1. Create some private data to relate to each device (in this case we use the service's name
|
||||
// and the location ID of the device
|
||||
// 2. Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device,
|
||||
// using the refCon field to store a pointer to our private data. When we get called with
|
||||
// this interest notification, we can grab the refCon and access our private data.
|
||||
//
|
||||
//================================================================================================
|
||||
void DeviceAdded(void *refCon, io_iterator_t iterator) {
|
||||
kern_return_t kr;
|
||||
io_service_t usbDevice;
|
||||
IOCFPlugInInterface **plugInInterface = NULL;
|
||||
SInt32 score;
|
||||
HRESULT res;
|
||||
|
||||
while((usbDevice = IOIteratorNext(iterator))) {
|
||||
io_name_t deviceName;
|
||||
CFStringRef deviceNameAsCFString;
|
||||
UInt32 locationID;
|
||||
UInt16 vendorId;
|
||||
UInt16 productId;
|
||||
UInt16 addr;
|
||||
|
||||
DeviceItem_t* deviceItem = new DeviceItem_t();
|
||||
|
||||
// Get the USB device's name.
|
||||
kr = IORegistryEntryGetName(usbDevice, deviceName);
|
||||
if(KERN_SUCCESS != kr) {
|
||||
deviceName[0] = '\0';
|
||||
}
|
||||
|
||||
deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, kCFStringEncodingASCII);
|
||||
|
||||
|
||||
if(deviceNameAsCFString) {
|
||||
Boolean result;
|
||||
char deviceName[MAXPATHLEN];
|
||||
|
||||
// Convert from a CFString to a C (NUL-terminated)
|
||||
result = CFStringGetCString(deviceNameAsCFString,
|
||||
deviceName,
|
||||
sizeof(deviceName),
|
||||
kCFStringEncodingUTF8);
|
||||
|
||||
if(result) {
|
||||
deviceItem->deviceParams.deviceName = deviceName;
|
||||
}
|
||||
|
||||
CFRelease(deviceNameAsCFString);
|
||||
}
|
||||
|
||||
CFStringRef manufacturerAsCFString = (CFStringRef)IORegistryEntrySearchCFProperty(
|
||||
usbDevice,
|
||||
kIOServicePlane,
|
||||
CFSTR(kUSBVendorString),
|
||||
kCFAllocatorDefault,
|
||||
kIORegistryIterateRecursively
|
||||
);
|
||||
|
||||
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) {
|
||||
deviceItem->deviceParams.manufacturer = manufacturer;
|
||||
}
|
||||
|
||||
CFRelease(manufacturerAsCFString);
|
||||
}
|
||||
|
||||
CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(
|
||||
usbDevice,
|
||||
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) {
|
||||
deviceItem->deviceParams.serialNumber = serialNumber;
|
||||
}
|
||||
|
||||
CFRelease(serialNumberAsCFString);
|
||||
}
|
||||
|
||||
|
||||
// Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface
|
||||
// for our device. This will create the necessary connections between our userland application and the
|
||||
// kernel object for the USB Device.
|
||||
kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
|
||||
|
||||
if((kIOReturnSuccess != kr) || !plugInInterface) {
|
||||
fprintf(stderr, "IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr);
|
||||
continue;
|
||||
}
|
||||
|
||||
stDeviceListItem *deviceListItem = new stDeviceListItem();
|
||||
|
||||
// Use the plugin interface to retrieve the device interface.
|
||||
res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceListItem->deviceInterface);
|
||||
|
||||
// Now done with the plugin interface.
|
||||
(*plugInInterface)->Release(plugInInterface);
|
||||
|
||||
if(res || deviceListItem->deviceInterface == NULL) {
|
||||
fprintf(stderr, "QueryInterface returned %d.\n", (int) res);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h.
|
||||
// In this case, fetch the locationID. The locationID uniquely identifies the device
|
||||
// and will remain the same, even across reboots, so long as the bus topology doesn't change.
|
||||
|
||||
kr = (*deviceListItem->deviceInterface)->GetLocationID(deviceListItem->deviceInterface, &locationID);
|
||||
if(KERN_SUCCESS != kr) {
|
||||
fprintf(stderr, "GetLocationID returned 0x%08x.\n", kr);
|
||||
continue;
|
||||
}
|
||||
deviceItem->deviceParams.locationId = locationID;
|
||||
|
||||
|
||||
kr = (*deviceListItem->deviceInterface)->GetDeviceAddress(deviceListItem->deviceInterface, &addr);
|
||||
if(KERN_SUCCESS != kr) {
|
||||
fprintf(stderr, "GetDeviceAddress returned 0x%08x.\n", kr);
|
||||
continue;
|
||||
}
|
||||
deviceItem->deviceParams.deviceAddress = addr;
|
||||
|
||||
|
||||
kr = (*deviceListItem->deviceInterface)->GetDeviceVendor(deviceListItem->deviceInterface, &vendorId);
|
||||
if(KERN_SUCCESS != kr) {
|
||||
fprintf(stderr, "GetDeviceVendor returned 0x%08x.\n", kr);
|
||||
continue;
|
||||
}
|
||||
deviceItem->deviceParams.vendorId = vendorId;
|
||||
|
||||
kr = (*deviceListItem->deviceInterface)->GetDeviceProduct(deviceListItem->deviceInterface, &productId);
|
||||
if(KERN_SUCCESS != kr) {
|
||||
fprintf(stderr, "GetDeviceProduct returned 0x%08x.\n", kr);
|
||||
continue;
|
||||
}
|
||||
deviceItem->deviceParams.productId = productId;
|
||||
|
||||
|
||||
// Extract path name as unique key
|
||||
io_string_t pathName;
|
||||
IORegistryEntryGetPath(usbDevice, kIOServicePlane, pathName);
|
||||
deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, pathName, kCFStringEncodingASCII);
|
||||
char cPathName[MAXPATHLEN];
|
||||
|
||||
if(deviceNameAsCFString) {
|
||||
Boolean result;
|
||||
|
||||
// Convert from a CFString to a C (NUL-terminated)
|
||||
result = CFStringGetCString(
|
||||
deviceNameAsCFString,
|
||||
cPathName,
|
||||
sizeof(cPathName),
|
||||
kCFStringEncodingUTF8
|
||||
);
|
||||
|
||||
|
||||
CFRelease(deviceNameAsCFString);
|
||||
}
|
||||
|
||||
AddItemToList(cPathName, deviceItem);
|
||||
deviceListItem->deviceItem = deviceItem;
|
||||
|
||||
if(intialDeviceImport == false) {
|
||||
WaitForDeviceHandled();
|
||||
notify_item = &deviceItem->deviceParams;
|
||||
isAdded = true;
|
||||
SignalDeviceAvailable();
|
||||
}
|
||||
|
||||
// Register for an interest notification of this device being removed. Use a reference to our
|
||||
// private data as the refCon which will be passed to the notification callback.
|
||||
kr = IOServiceAddInterestNotification(
|
||||
gNotifyPort, // notifyPort
|
||||
usbDevice, // service
|
||||
kIOGeneralInterest, // interestType
|
||||
DeviceRemoved, // callback
|
||||
deviceListItem, // refCon
|
||||
&(deviceListItem->notification) // notification
|
||||
);
|
||||
|
||||
if(KERN_SUCCESS != kr) {
|
||||
printf("IOServiceAddInterestNotification returned 0x%08x.\n", kr);
|
||||
}
|
||||
|
||||
// Done with this USB device; release the reference added by IOIteratorNext
|
||||
kr = IOObjectRelease(usbDevice);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WaitForDeviceHandled() {
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
if(deviceHandled == false) {
|
||||
pthread_cond_wait(¬ifyDeviceHandled, ¬ify_mutex);
|
||||
}
|
||||
deviceHandled = false;
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
void SignalDeviceHandled() {
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
deviceHandled = true;
|
||||
pthread_cond_signal(¬ifyDeviceHandled);
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
void WaitForNewDevice() {
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
if(newDeviceAvailable == false) {
|
||||
pthread_cond_wait(¬ifyNewDevice, ¬ify_mutex);
|
||||
}
|
||||
newDeviceAvailable = false;
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
void SignalDeviceAvailable() {
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
newDeviceAvailable = true;
|
||||
pthread_cond_signal(¬ifyNewDevice);
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
|
||||
void *RunLoop(void * arg) {
|
||||
|
||||
runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
|
||||
|
||||
gRunLoop = CFRunLoopGetCurrent();
|
||||
CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode);
|
||||
|
||||
// Start the run loop. Now we'll receive notifications.
|
||||
CFRunLoopRun();
|
||||
|
||||
// We should never get here
|
||||
fprintf(stderr, "Unexpectedly back from CFRunLoopRun()!\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void NotifyAsync(uv_work_t* req) {
|
||||
WaitForNewDevice();
|
||||
}
|
||||
|
||||
void NotifyFinished(uv_work_t* req) {
|
||||
if(isRunning) {
|
||||
if(isAdded) {
|
||||
NotifyAdded(notify_item);
|
||||
}
|
||||
else {
|
||||
NotifyRemoved(notify_item);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Item in case of removal
|
||||
if(isAdded == false) {
|
||||
delete notify_item;
|
||||
}
|
||||
|
||||
if(isRunning) {
|
||||
uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);
|
||||
}
|
||||
SignalDeviceHandled();
|
||||
}
|
||||
|
||||
void Start() {
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
isRunning = false;
|
||||
pthread_mutex_lock(¬ify_mutex);
|
||||
pthread_cond_signal(¬ifyNewDevice);
|
||||
pthread_mutex_unlock(¬ify_mutex);
|
||||
}
|
||||
|
||||
void InitDetection() {
|
||||
|
||||
kern_return_t kr;
|
||||
|
||||
// Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow
|
||||
// the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7.
|
||||
// See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X"
|
||||
// <http://developer.apple.com/qa/qa2001/qa1076.html>.
|
||||
// One exception is that you can use the matching dictionary "as is", i.e. without adding any matching
|
||||
// criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will
|
||||
// consume this dictionary reference, so there is no need to release it later on.
|
||||
|
||||
// Interested in instances of class
|
||||
// IOUSBDevice and its subclasses
|
||||
matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
|
||||
|
||||
if (matchingDict == NULL) {
|
||||
fprintf(stderr, "IOServiceMatching returned NULL.\n");
|
||||
}
|
||||
|
||||
// Create a notification port and add its run loop event source to our run loop
|
||||
// This is how async notifications get set up.
|
||||
|
||||
gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
|
||||
// Now set up a notification to be called when a device is first matched by I/O Kit.
|
||||
kr = IOServiceAddMatchingNotification(
|
||||
gNotifyPort, // notifyPort
|
||||
kIOFirstMatchNotification, // notificationType
|
||||
matchingDict, // matching
|
||||
DeviceAdded, // callback
|
||||
NULL, // refCon
|
||||
&gAddedIter // notification
|
||||
);
|
||||
|
||||
if (KERN_SUCCESS != kr) {
|
||||
printf("IOServiceAddMatchingNotification returned 0x%08x.\n", kr);
|
||||
}
|
||||
|
||||
// Iterate once to get already-present devices and arm the notification
|
||||
DeviceAdded(NULL, gAddedIter);
|
||||
intialDeviceImport = false;
|
||||
|
||||
|
||||
pthread_mutex_init(¬ify_mutex, NULL);
|
||||
pthread_cond_init(¬ifyNewDevice, NULL);
|
||||
pthread_cond_init(¬ifyDeviceHandled, NULL);
|
||||
|
||||
int rc = pthread_create(&lookupThread, NULL, RunLoop, NULL);
|
||||
if (rc) {
|
||||
printf("ERROR; return code from pthread_create() is %d\n", rc);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
void EIO_Find(uv_work_t* req) {
|
||||
ListBaton* data = static_cast<ListBaton*>(req->data);
|
||||
|
||||
CreateFilteredList(&data->results, data->vid, data->pid);
|
||||
}
|
|
@ -1,471 +0,0 @@
|
|||
#include <uv.h>
|
||||
#include <dbt.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <atlstr.h>
|
||||
|
||||
|
||||
// Include Windows headers
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <tchar.h>
|
||||
#include <strsafe.h>
|
||||
#include <Setupapi.h>
|
||||
|
||||
#include "detection.h"
|
||||
#include "deviceList.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**********************************
|
||||
* Local defines
|
||||
**********************************/
|
||||
#define VID_TAG "VID_"
|
||||
#define PID_TAG "PID_"
|
||||
|
||||
#define LIBRARY_NAME ("setupapi.dll")
|
||||
|
||||
|
||||
#define DllImport __declspec(dllimport)
|
||||
|
||||
#define MAX_THREAD_WINDOW_NAME 64
|
||||
|
||||
/**********************************
|
||||
* Local typedefs
|
||||
**********************************/
|
||||
|
||||
|
||||
|
||||
/**********************************
|
||||
* Local Variables
|
||||
**********************************/
|
||||
GUID GUID_DEVINTERFACE_USB_DEVICE = {
|
||||
0xA5DCBF10L,
|
||||
0x6530,
|
||||
0x11D2,
|
||||
0x90,
|
||||
0x1F,
|
||||
0x00,
|
||||
0xC0,
|
||||
0x4F,
|
||||
0xB9,
|
||||
0x51,
|
||||
0xED
|
||||
};
|
||||
|
||||
HWND handle;
|
||||
DWORD threadId;
|
||||
HANDLE threadHandle;
|
||||
|
||||
HANDLE deviceChangedRegisteredEvent;
|
||||
HANDLE deviceChangedSentEvent;
|
||||
|
||||
ListResultItem_t* currentDevice;
|
||||
bool isAdded;
|
||||
bool isRunning = false;
|
||||
|
||||
HINSTANCE hinstLib;
|
||||
|
||||
|
||||
typedef BOOL (WINAPI *_SetupDiEnumDeviceInfo) (HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData);
|
||||
typedef HDEVINFO (WINAPI *_SetupDiGetClassDevs) (const GUID *ClassGuid, PCTSTR Enumerator, HWND hwndParent, DWORD Flags);
|
||||
typedef BOOL (WINAPI *_SetupDiDestroyDeviceInfoList) (HDEVINFO DeviceInfoSet);
|
||||
typedef BOOL (WINAPI *_SetupDiGetDeviceInstanceId) (HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PTSTR DeviceInstanceId, DWORD DeviceInstanceIdSize, PDWORD RequiredSize);
|
||||
typedef BOOL (WINAPI *_SetupDiGetDeviceRegistryProperty) (HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize);
|
||||
|
||||
|
||||
_SetupDiEnumDeviceInfo DllSetupDiEnumDeviceInfo;
|
||||
_SetupDiGetClassDevs DllSetupDiGetClassDevs;
|
||||
_SetupDiDestroyDeviceInfoList DllSetupDiDestroyDeviceInfoList;
|
||||
_SetupDiGetDeviceInstanceId DllSetupDiGetDeviceInstanceId;
|
||||
_SetupDiGetDeviceRegistryProperty DllSetupDiGetDeviceRegistryProperty;
|
||||
|
||||
|
||||
/**********************************
|
||||
* Local Helper Functions protoypes
|
||||
**********************************/
|
||||
void UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam, DeviceState_t state);
|
||||
DWORD WINAPI ListenerThread(LPVOID lpParam);
|
||||
|
||||
void BuildInitialDeviceList();
|
||||
|
||||
void NotifyAsync(uv_work_t* req);
|
||||
void NotifyFinished(uv_work_t* req);
|
||||
|
||||
void ExtractDeviceInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA* pspDevInfoData, TCHAR* buf, DWORD buffSize, ListResultItem_t* resultItem);
|
||||
bool CheckValidity(ListResultItem_t* item);
|
||||
|
||||
|
||||
/**********************************
|
||||
* Public Functions
|
||||
**********************************/
|
||||
void NotifyAsync(uv_work_t* req) {
|
||||
WaitForSingleObject(deviceChangedRegisteredEvent, INFINITE);
|
||||
}
|
||||
|
||||
|
||||
void NotifyFinished(uv_work_t* req) {
|
||||
if (isRunning) {
|
||||
if(isAdded) {
|
||||
NotifyAdded(currentDevice);
|
||||
}
|
||||
else {
|
||||
NotifyRemoved(currentDevice);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Item in case of removal
|
||||
if(isAdded == false) {
|
||||
delete currentDevice;
|
||||
}
|
||||
|
||||
SetEvent(deviceChangedSentEvent);
|
||||
|
||||
currentDevice = NULL;
|
||||
uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);
|
||||
}
|
||||
|
||||
void LoadFunctions() {
|
||||
|
||||
bool success;
|
||||
|
||||
hinstLib = LoadLibrary(LIBRARY_NAME);
|
||||
|
||||
if (hinstLib != NULL) {
|
||||
DllSetupDiEnumDeviceInfo = (_SetupDiEnumDeviceInfo) GetProcAddress(hinstLib, "SetupDiEnumDeviceInfo");
|
||||
|
||||
DllSetupDiGetClassDevs = (_SetupDiGetClassDevs) GetProcAddress(hinstLib, "SetupDiGetClassDevsA");
|
||||
|
||||
DllSetupDiDestroyDeviceInfoList = (_SetupDiDestroyDeviceInfoList) GetProcAddress(hinstLib, "SetupDiDestroyDeviceInfoList");
|
||||
|
||||
DllSetupDiGetDeviceInstanceId = (_SetupDiGetDeviceInstanceId) GetProcAddress(hinstLib, "SetupDiGetDeviceInstanceIdA");
|
||||
|
||||
DllSetupDiGetDeviceRegistryProperty = (_SetupDiGetDeviceRegistryProperty) GetProcAddress(hinstLib, "SetupDiGetDeviceRegistryPropertyA");
|
||||
|
||||
success = (
|
||||
DllSetupDiEnumDeviceInfo != NULL &&
|
||||
DllSetupDiGetClassDevs != NULL &&
|
||||
DllSetupDiDestroyDeviceInfoList != NULL &&
|
||||
DllSetupDiGetDeviceInstanceId != NULL &&
|
||||
DllSetupDiGetDeviceRegistryProperty != NULL
|
||||
);
|
||||
}
|
||||
else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if(!success) {
|
||||
printf("Could not load library functions from dll -> abort (Check if %s is available)\r\n", LIBRARY_NAME);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Start() {
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
isRunning = false;
|
||||
SetEvent(deviceChangedRegisteredEvent);
|
||||
}
|
||||
|
||||
void InitDetection() {
|
||||
|
||||
LoadFunctions();
|
||||
|
||||
deviceChangedRegisteredEvent = CreateEvent(NULL, false /* auto-reset event */, false /* non-signalled state */, "");
|
||||
deviceChangedSentEvent = CreateEvent(NULL, false /* auto-reset event */, true /* non-signalled state */, "");
|
||||
|
||||
BuildInitialDeviceList();
|
||||
|
||||
threadHandle = CreateThread(
|
||||
NULL, // default security attributes
|
||||
0, // use default stack size
|
||||
ListenerThread, // thread function name
|
||||
NULL, // argument to thread function
|
||||
0, // use default creation flags
|
||||
&threadId
|
||||
);
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
|
||||
void EIO_Find(uv_work_t* req) {
|
||||
|
||||
ListBaton* data = static_cast<ListBaton*>(req->data);
|
||||
|
||||
CreateFilteredList(&data->results, data->vid, data->pid);
|
||||
}
|
||||
|
||||
|
||||
/**********************************
|
||||
* Local Functions
|
||||
**********************************/
|
||||
void ToUpper(char * buf) {
|
||||
char* c = buf;
|
||||
while (*c != '\0') {
|
||||
*c = toupper((unsigned char)*c);
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void extractVidPid(char * buf, ListResultItem_t * item) {
|
||||
if(buf == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ToUpper(buf);
|
||||
|
||||
char* string;
|
||||
char* temp;
|
||||
char* pidStr, *vidStr;
|
||||
int vid = 0;
|
||||
int pid = 0;
|
||||
|
||||
string = new char[strlen(buf) + 1];
|
||||
memcpy(string, buf, strlen(buf) + 1);
|
||||
|
||||
vidStr = strstr(string, VID_TAG);
|
||||
pidStr = strstr(string, PID_TAG);
|
||||
|
||||
if(vidStr != NULL) {
|
||||
temp = (char*) (vidStr + strlen(VID_TAG));
|
||||
temp[4] = '\0';
|
||||
vid = strtol (temp, NULL, 16);
|
||||
}
|
||||
|
||||
if(pidStr != NULL) {
|
||||
temp = (char*) (pidStr + strlen(PID_TAG));
|
||||
temp[4] = '\0';
|
||||
pid = strtol (temp, NULL, 16);
|
||||
}
|
||||
item->vendorId = vid;
|
||||
item->productId = pid;
|
||||
|
||||
delete string;
|
||||
}
|
||||
|
||||
|
||||
LRESULT CALLBACK DetectCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
if (msg == WM_DEVICECHANGE) {
|
||||
if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam ) {
|
||||
PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam;
|
||||
PDEV_BROADCAST_DEVICEINTERFACE pDevInf;
|
||||
|
||||
if(pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
|
||||
pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
|
||||
UpdateDevice(pDevInf, wParam, (DBT_DEVICEARRIVAL == wParam) ? DeviceState_Connect : DeviceState_Disconnect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI ListenerThread( LPVOID lpParam ) {
|
||||
char className[MAX_THREAD_WINDOW_NAME];
|
||||
_snprintf_s(className, MAX_THREAD_WINDOW_NAME, "ListnerThreadUsbDetection_%d", GetCurrentThreadId());
|
||||
|
||||
WNDCLASSA wincl = {0};
|
||||
wincl.hInstance = GetModuleHandle(0);
|
||||
wincl.lpszClassName = className;
|
||||
wincl.lpfnWndProc = DetectCallback;
|
||||
|
||||
if (!RegisterClassA(&wincl)) {
|
||||
DWORD le = GetLastError();
|
||||
printf("RegisterClassA() failed [Error: %x]\r\n", le);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
HWND hwnd = CreateWindowExA(WS_EX_TOPMOST, className, className, 0, 0, 0, 0, 0, NULL, 0, 0, 0);
|
||||
if (!hwnd) {
|
||||
DWORD le = GetLastError();
|
||||
printf("CreateWindowExA() failed [Error: %x]\r\n", le);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DEV_BROADCAST_DEVICEINTERFACE_A notifyFilter = {0};
|
||||
notifyFilter.dbcc_size = sizeof(notifyFilter);
|
||||
notifyFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
notifyFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
|
||||
|
||||
HDEVNOTIFY hDevNotify = RegisterDeviceNotificationA(hwnd, ¬ifyFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
if (!hDevNotify) {
|
||||
DWORD le = GetLastError();
|
||||
printf("RegisterDeviceNotificationA() failed [Error: %x]\r\n", le);
|
||||
return 1;
|
||||
}
|
||||
|
||||
MSG msg;
|
||||
while(TRUE) {
|
||||
BOOL bRet = GetMessage(&msg, hwnd, 0, 0);
|
||||
if ((bRet == 0) || (bRet == -1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void BuildInitialDeviceList() {
|
||||
TCHAR buf[MAX_PATH];
|
||||
DWORD dwFlag = (DIGCF_ALLCLASSES | DIGCF_PRESENT);
|
||||
HDEVINFO hDevInfo = DllSetupDiGetClassDevs(NULL, "USB", NULL, dwFlag);
|
||||
|
||||
if(INVALID_HANDLE_VALUE == hDevInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
SP_DEVINFO_DATA* pspDevInfoData = (SP_DEVINFO_DATA*) HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA));
|
||||
pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
for(int i=0; DllSetupDiEnumDeviceInfo(hDevInfo, i, pspDevInfoData); i++) {
|
||||
DWORD nSize=0 ;
|
||||
|
||||
if (!DllSetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) {
|
||||
break;
|
||||
}
|
||||
|
||||
DeviceItem_t* item = new DeviceItem_t();
|
||||
item->deviceState = DeviceState_Connect;
|
||||
|
||||
DWORD DataT;
|
||||
DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, MAX_PATH, &nSize);
|
||||
|
||||
AddItemToList(buf, item);
|
||||
ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, &item->deviceParams);
|
||||
}
|
||||
|
||||
if(pspDevInfoData) {
|
||||
HeapFree(GetProcessHeap(), 0, pspDevInfoData);
|
||||
}
|
||||
|
||||
if(hDevInfo) {
|
||||
DllSetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExtractDeviceInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA* pspDevInfoData, TCHAR* buf, DWORD buffSize, ListResultItem_t* resultItem) {
|
||||
|
||||
DWORD DataT;
|
||||
DWORD nSize;
|
||||
static int dummy = 1;
|
||||
|
||||
resultItem->locationId = 0;
|
||||
resultItem->deviceAddress = dummy++;
|
||||
|
||||
// device found
|
||||
if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_FRIENDLYNAME, &DataT, (PBYTE)buf, buffSize, &nSize)) {
|
||||
resultItem->deviceName = buf;
|
||||
}
|
||||
else if ( DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_DEVICEDESC, &DataT, (PBYTE)buf, buffSize, &nSize))
|
||||
{
|
||||
resultItem->deviceName = buf;
|
||||
}
|
||||
if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_MFG, &DataT, (PBYTE)buf, buffSize, &nSize)) {
|
||||
resultItem->manufacturer = buf;
|
||||
}
|
||||
if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, buffSize, &nSize)) {
|
||||
// Use this to extract VID / PID
|
||||
extractVidPid(buf, resultItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam, DeviceState_t state) {
|
||||
// dbcc_name:
|
||||
// \\?\USB#Vid_04e8&Pid_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
|
||||
// convert to
|
||||
// USB\Vid_04e8&Pid_503b\0002F9A9828E0F06
|
||||
CString szDevId = pDevInf->dbcc_name+4;
|
||||
int idx = szDevId.ReverseFind(_T('#'));
|
||||
|
||||
szDevId.Truncate(idx);
|
||||
szDevId.Replace(_T('#'), _T('\\'));
|
||||
szDevId.MakeUpper();
|
||||
|
||||
CString szClass;
|
||||
idx = szDevId.Find(_T('\\'));
|
||||
szClass = szDevId.Left(idx);
|
||||
|
||||
// if we are adding device, we only need present devices
|
||||
// otherwise, we need all devices
|
||||
DWORD dwFlag = DBT_DEVICEARRIVAL != wParam ? DIGCF_ALLCLASSES : (DIGCF_ALLCLASSES | DIGCF_PRESENT);
|
||||
HDEVINFO hDevInfo = DllSetupDiGetClassDevs(NULL, szClass, NULL, dwFlag);
|
||||
if(INVALID_HANDLE_VALUE == hDevInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
SP_DEVINFO_DATA* pspDevInfoData = (SP_DEVINFO_DATA*) HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA));
|
||||
pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
for(int i=0; DllSetupDiEnumDeviceInfo(hDevInfo, i, pspDevInfoData); i++) {
|
||||
DWORD nSize=0 ;
|
||||
TCHAR buf[MAX_PATH];
|
||||
|
||||
if (!DllSetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(szDevId == buf) {
|
||||
|
||||
WaitForSingleObject(deviceChangedSentEvent, INFINITE);
|
||||
|
||||
DWORD DataT;
|
||||
DWORD nSize;
|
||||
DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, MAX_PATH, &nSize);
|
||||
|
||||
if(state == DeviceState_Connect) {
|
||||
DeviceItem_t* device = new DeviceItem_t();
|
||||
|
||||
AddItemToList(buf, device);
|
||||
ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, &device->deviceParams);
|
||||
|
||||
currentDevice = &device->deviceParams;
|
||||
isAdded = true;
|
||||
}
|
||||
else {
|
||||
|
||||
ListResultItem_t* item = NULL;
|
||||
if(IsItemAlreadyStored(buf)) {
|
||||
DeviceItem_t* deviceItem = GetItemFromList(buf);
|
||||
if(deviceItem)
|
||||
{
|
||||
item = CopyElement(&deviceItem->deviceParams);
|
||||
}
|
||||
RemoveItemFromList(deviceItem);
|
||||
delete deviceItem;
|
||||
}
|
||||
|
||||
if(item == NULL) {
|
||||
item = new ListResultItem_t();
|
||||
ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, item);
|
||||
}
|
||||
currentDevice = item;
|
||||
isAdded = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pspDevInfoData) {
|
||||
HeapFree(GetProcessHeap(), 0, pspDevInfoData);
|
||||
}
|
||||
|
||||
if(hDevInfo) {
|
||||
DllSetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
}
|
||||
|
||||
SetEvent(deviceChangedRegisteredEvent);
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
#include <map>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "deviceList.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
map<string, DeviceItem_t*> deviceMap;
|
||||
|
||||
void AddItemToList(char* key, DeviceItem_t * item) {
|
||||
item->SetKey(key);
|
||||
deviceMap.insert(pair<string, DeviceItem_t*>(item->GetKey(), item));
|
||||
}
|
||||
|
||||
void RemoveItemFromList(DeviceItem_t* item) {
|
||||
deviceMap.erase(item->GetKey());
|
||||
}
|
||||
|
||||
DeviceItem_t* GetItemFromList(char* key) {
|
||||
map<string, DeviceItem_t*>::iterator it;
|
||||
|
||||
it = deviceMap.find(key);
|
||||
if(it == deviceMap.end()) {
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsItemAlreadyStored(char* key) {
|
||||
map<string, DeviceItem_t*>::iterator it;
|
||||
|
||||
it = deviceMap.find(key);
|
||||
if(it == deviceMap.end()) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ListResultItem_t* CopyElement(ListResultItem_t* item) {
|
||||
ListResultItem_t* dst = new ListResultItem_t();
|
||||
dst->locationId = item->locationId;
|
||||
dst->vendorId = item->vendorId;
|
||||
dst->productId = item->productId;
|
||||
dst->deviceName = item->deviceName;
|
||||
dst->manufacturer = item->manufacturer;
|
||||
dst->serialNumber = item->serialNumber;
|
||||
dst->deviceAddress = item->deviceAddress;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void CreateFilteredList(list<ListResultItem_t*> *filteredList, int vid, int pid) {
|
||||
map<string, DeviceItem_t*>::iterator it;
|
||||
|
||||
for (it = deviceMap.begin(); it != deviceMap.end(); ++it) {
|
||||
DeviceItem_t* item = it->second;
|
||||
|
||||
if (
|
||||
(( vid != 0 && pid != 0) && (vid == item->deviceParams.vendorId && pid == item->deviceParams.productId))
|
||||
|| ((vid != 0 && pid == 0) && vid == item->deviceParams.vendorId)
|
||||
|| (vid == 0 && pid == 0)
|
||||
) {
|
||||
(*filteredList).push_back(CopyElement(&item->deviceParams));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
#ifndef _DEVICE_LIST_H
|
||||
#define _DEVICE_LIST_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
typedef struct {
|
||||
public:
|
||||
int locationId;
|
||||
int vendorId;
|
||||
int productId;
|
||||
std::string deviceName;
|
||||
std::string manufacturer;
|
||||
std::string serialNumber;
|
||||
int deviceAddress;
|
||||
} ListResultItem_t;
|
||||
|
||||
typedef enum _DeviceState_t {
|
||||
DeviceState_Connect,
|
||||
DeviceState_Disconnect,
|
||||
} DeviceState_t;
|
||||
|
||||
typedef struct _DeviceItem_t {
|
||||
ListResultItem_t deviceParams;
|
||||
DeviceState_t deviceState;
|
||||
|
||||
private:
|
||||
char* key;
|
||||
|
||||
|
||||
public:
|
||||
_DeviceItem_t() {
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
~_DeviceItem_t() {
|
||||
if(this->key != NULL) {
|
||||
delete this->key;
|
||||
}
|
||||
}
|
||||
|
||||
void SetKey(char* key) {
|
||||
if(this->key != NULL) {
|
||||
delete this->key;
|
||||
}
|
||||
this->key = new char[strlen(key) + 1];
|
||||
memcpy(this->key, key, strlen(key) + 1);
|
||||
}
|
||||
|
||||
char* GetKey() {
|
||||
return this->key;
|
||||
}
|
||||
} DeviceItem_t;
|
||||
|
||||
|
||||
void AddItemToList(char* key, DeviceItem_t * item);
|
||||
void RemoveItemFromList(DeviceItem_t* item);
|
||||
bool IsItemAlreadyStored(char* identifier);
|
||||
DeviceItem_t* GetItemFromList(char* key);
|
||||
ListResultItem_t* CopyElement(ListResultItem_t* item);
|
||||
void CreateFilteredList(std::list<ListResultItem_t*>* filteredList, int vid, int pid);
|
||||
|
||||
#endif
|
|
@ -1,717 +0,0 @@
|
|||
#include "./serialport.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define strncasecmp strnicmp
|
||||
#else
|
||||
#include "./serialport_poller.h"
|
||||
#endif
|
||||
|
||||
struct _WriteQueue {
|
||||
const int _fd; // the fd that is associated with this write queue
|
||||
QueuedWrite _write_queue;
|
||||
uv_mutex_t _write_queue_mutex;
|
||||
_WriteQueue *_next;
|
||||
|
||||
_WriteQueue(const int fd) : _fd(fd), _write_queue(), _next(NULL) {
|
||||
uv_mutex_init(&_write_queue_mutex);
|
||||
}
|
||||
|
||||
void lock() { uv_mutex_lock(&_write_queue_mutex); }
|
||||
void unlock() { uv_mutex_unlock(&_write_queue_mutex); }
|
||||
|
||||
QueuedWrite &get() { return _write_queue; }
|
||||
};
|
||||
|
||||
static _WriteQueue *write_queues = NULL;
|
||||
|
||||
static _WriteQueue *qForFD(const int fd) {
|
||||
_WriteQueue *q = write_queues;
|
||||
while (q != NULL) {
|
||||
if (q->_fd == fd) {
|
||||
return q;
|
||||
}
|
||||
q = q->_next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static _WriteQueue *newQForFD(const int fd) {
|
||||
_WriteQueue *q = qForFD(fd);
|
||||
|
||||
if (q == NULL) {
|
||||
if (write_queues == NULL) {
|
||||
write_queues = new _WriteQueue(fd);
|
||||
return write_queues;
|
||||
} else {
|
||||
q = write_queues;
|
||||
while (q->_next != NULL) {
|
||||
q = q->_next;
|
||||
}
|
||||
q->_next = new _WriteQueue(fd);
|
||||
return q->_next;
|
||||
}
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static void deleteQForFD(const int fd) {
|
||||
if (write_queues == NULL)
|
||||
return;
|
||||
|
||||
_WriteQueue *q = write_queues;
|
||||
if (write_queues->_fd == fd) {
|
||||
write_queues = write_queues->_next;
|
||||
delete q;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while (q->_next != NULL) {
|
||||
if (q->_next->_fd == fd) {
|
||||
_WriteQueue *out_q = q->_next;
|
||||
q->_next = q->_next->_next;
|
||||
delete out_q;
|
||||
|
||||
return;
|
||||
}
|
||||
q = q->_next;
|
||||
}
|
||||
|
||||
// It wasn't found...
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> getValueFromObject(v8::Local<v8::Object> options, std::string key) {
|
||||
v8::Local<v8::String> v8str = Nan::New<v8::String>(key).ToLocalChecked();
|
||||
return Nan::Get(options, v8str).ToLocalChecked();
|
||||
}
|
||||
|
||||
int getIntFromObject(v8::Local<v8::Object> options, std::string key) {
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
return Nan::To<v8::Int32>(getValueFromObject(options, key)).ToLocalChecked()->Value();
|
||||
#else
|
||||
return getValueFromObject(options, key)->ToInt32()->Int32Value();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool getBoolFromObject(v8::Local<v8::Object> options, std::string key) {
|
||||
return getValueFromObject(options, key)->ToBoolean()->BooleanValue();
|
||||
}
|
||||
|
||||
v8::Local<v8::String> getStringFromObj(v8::Local<v8::Object> options, std::string key) {
|
||||
return getValueFromObject(options, key)->ToString();
|
||||
}
|
||||
|
||||
double getDoubleFromObject(v8::Local<v8::Object> options, std::string key) {
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
return Nan::To<double>(getValueFromObject(options, key)).FromMaybe(0);
|
||||
#else
|
||||
return getValueFromObject(options, key)->ToNumber()->NumberValue();
|
||||
#endif
|
||||
}
|
||||
|
||||
NAN_METHOD(Open) {
|
||||
// path
|
||||
if (!info[0]->IsString()) {
|
||||
Nan::ThrowTypeError("First argument must be a string");
|
||||
return;
|
||||
}
|
||||
v8::String::Utf8Value path(info[0]->ToString());
|
||||
|
||||
// options
|
||||
if (!info[1]->IsObject()) {
|
||||
Nan::ThrowTypeError("Second argument must be an object");
|
||||
return;
|
||||
}
|
||||
v8::Local<v8::Object> options = info[1]->ToObject();
|
||||
|
||||
// callback
|
||||
if (!info[2]->IsFunction()) {
|
||||
Nan::ThrowTypeError("Third argument must be a function");
|
||||
return;
|
||||
}
|
||||
|
||||
OpenBaton* baton = new OpenBaton();
|
||||
memset(baton, 0, sizeof(OpenBaton));
|
||||
strcpy(baton->path, *path);
|
||||
baton->baudRate = getIntFromObject(options, "baudRate");
|
||||
baton->dataBits = getIntFromObject(options, "dataBits");
|
||||
baton->bufferSize = getIntFromObject(options, "bufferSize");
|
||||
baton->parity = ToParityEnum(getStringFromObj(options, "parity"));
|
||||
baton->stopBits = ToStopBitEnum(getDoubleFromObject(options, "stopBits"));
|
||||
baton->rtscts = getBoolFromObject(options, "rtscts");
|
||||
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");
|
||||
|
||||
v8::Local<v8::Object> platformOptions = getValueFromObject(options, "platformOptions")->ToObject();
|
||||
baton->platformOptions = ParsePlatformOptions(platformOptions);
|
||||
|
||||
baton->callback.Reset(info[2].As<v8::Function>());
|
||||
baton->dataCallback = new Nan::Callback(getValueFromObject(options, "dataCallback").As<v8::Function>());
|
||||
baton->disconnectedCallback = new Nan::Callback(getValueFromObject(options, "disconnectedCallback").As<v8::Function>());
|
||||
baton->errorCallback = new Nan::Callback(getValueFromObject(options, "errorCallback").As<v8::Function>());
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
req->data = baton;
|
||||
|
||||
uv_queue_work(uv_default_loop(), req, EIO_Open, (uv_after_work_cb)EIO_AfterOpen);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EIO_AfterOpen(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
OpenBaton* data = static_cast<OpenBaton*>(req->data);
|
||||
|
||||
v8::Local<v8::Value> argv[2];
|
||||
if (data->errorString[0]) {
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(data->errorString).ToLocalChecked());
|
||||
argv[1] = Nan::Undefined();
|
||||
// not needed because we're not calling AfterOpenSuccess
|
||||
delete data->dataCallback;
|
||||
delete data->errorCallback;
|
||||
delete data->disconnectedCallback;
|
||||
} else {
|
||||
argv[0] = Nan::Null();
|
||||
argv[1] = Nan::New<v8::Int32>(data->result);
|
||||
|
||||
int fd;
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
fd = argv[1]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
fd = argv[1]->ToInt32()->Int32Value();
|
||||
#endif
|
||||
newQForFD(fd);
|
||||
|
||||
AfterOpenSuccess(data->result, data->dataCallback, data->disconnectedCallback, data->errorCallback);
|
||||
}
|
||||
|
||||
data->callback.Call(2, argv);
|
||||
|
||||
delete data->platformOptions;
|
||||
delete data;
|
||||
delete req;
|
||||
}
|
||||
|
||||
NAN_METHOD(Update) {
|
||||
// file descriptor
|
||||
if (!info[0]->IsInt32()) {
|
||||
Nan::ThrowTypeError("First argument must be an int");
|
||||
return;
|
||||
}
|
||||
int fd;
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
fd = info[0]->ToInt32()->Int32Value();
|
||||
#endif
|
||||
|
||||
// options
|
||||
if (!info[1]->IsObject()) {
|
||||
Nan::ThrowTypeError("Second argument must be an object");
|
||||
return;
|
||||
}
|
||||
v8::Local<v8::Object> options = info[1]->ToObject();
|
||||
|
||||
if (!Nan::Has(options, Nan::New<v8::String>("baudRate").ToLocalChecked()).FromMaybe(false)) {
|
||||
Nan::ThrowTypeError("baudRate must be set on options object");
|
||||
return;
|
||||
}
|
||||
|
||||
// callback
|
||||
if (!info[2]->IsFunction()) {
|
||||
Nan::ThrowTypeError("Third argument must be a function");
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectionOptionsBaton* baton = new ConnectionOptionsBaton();
|
||||
memset(baton, 0, sizeof(ConnectionOptionsBaton));
|
||||
|
||||
baton->fd = fd;
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
baton->baudRate = Nan::Get(options, Nan::New<v8::String>("baudRate").ToLocalChecked()).ToLocalChecked()->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
baton->baudRate = Nan::Get(options, Nan::New<v8::String>("baudRate").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value();
|
||||
#endif
|
||||
baton->callback.Reset(info[2].As<v8::Function>());
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
req->data = baton;
|
||||
|
||||
uv_queue_work(uv_default_loop(), req, EIO_Update, (uv_after_work_cb)EIO_AfterUpdate);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EIO_AfterUpdate(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
ConnectionOptionsBaton* data = static_cast<ConnectionOptionsBaton*>(req->data);
|
||||
|
||||
v8::Local<v8::Value> argv[1];
|
||||
if (data->errorString[0]) {
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(data->errorString).ToLocalChecked());
|
||||
} else {
|
||||
argv[0] = Nan::Null();
|
||||
}
|
||||
|
||||
data->callback.Call(1, argv);
|
||||
|
||||
delete data;
|
||||
delete req;
|
||||
}
|
||||
|
||||
NAN_METHOD(Write) {
|
||||
// file descriptor
|
||||
if (!info[0]->IsInt32()) {
|
||||
Nan::ThrowTypeError("First argument must be an int");
|
||||
return;
|
||||
}
|
||||
int fd;
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
fd = info[0]->ToInt32()->Int32Value();
|
||||
#endif
|
||||
|
||||
// buffer
|
||||
if (!info[1]->IsObject() || !node::Buffer::HasInstance(info[1])) {
|
||||
Nan::ThrowTypeError("Second argument must be a buffer");
|
||||
return;
|
||||
}
|
||||
v8::Local<v8::Object> buffer = info[1]->ToObject();
|
||||
char* bufferData = node::Buffer::Data(buffer);
|
||||
size_t bufferLength = node::Buffer::Length(buffer);
|
||||
|
||||
// callback
|
||||
if (!info[2]->IsFunction()) {
|
||||
Nan::ThrowTypeError("Third argument must be a function");
|
||||
return;
|
||||
}
|
||||
|
||||
WriteBaton* baton = new WriteBaton();
|
||||
memset(baton, 0, sizeof(WriteBaton));
|
||||
baton->fd = fd;
|
||||
baton->buffer.Reset(buffer);
|
||||
baton->bufferData = bufferData;
|
||||
baton->bufferLength = bufferLength;
|
||||
baton->offset = 0;
|
||||
baton->callback.Reset(info[2].As<v8::Function>());
|
||||
|
||||
QueuedWrite* queuedWrite = new QueuedWrite();
|
||||
memset(queuedWrite, 0, sizeof(QueuedWrite));
|
||||
queuedWrite->baton = baton;
|
||||
queuedWrite->req.data = queuedWrite;
|
||||
|
||||
_WriteQueue *q = qForFD(fd);
|
||||
if (!q) {
|
||||
Nan::ThrowTypeError("There's no write queue for that file descriptor (write)!");
|
||||
return;
|
||||
}
|
||||
|
||||
q->lock();
|
||||
QueuedWrite &write_queue = q->get();
|
||||
bool empty = write_queue.empty();
|
||||
|
||||
write_queue.insert_tail(queuedWrite);
|
||||
|
||||
if (empty) {
|
||||
uv_queue_work(uv_default_loop(), &queuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite);
|
||||
}
|
||||
q->unlock();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EIO_AfterWrite(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
QueuedWrite* queuedWrite = static_cast<QueuedWrite*>(req->data);
|
||||
WriteBaton* data = static_cast<WriteBaton*>(queuedWrite->baton);
|
||||
|
||||
v8::Local<v8::Value> argv[1];
|
||||
if (data->errorString[0]) {
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(data->errorString).ToLocalChecked());
|
||||
} else {
|
||||
argv[0] = Nan::Null();
|
||||
}
|
||||
|
||||
if (data->offset < data->bufferLength && !data->errorString[0]) {
|
||||
// We're not done with this baton, so throw it right back onto the queue.
|
||||
// Don't re-push the write in the event loop if there was an error; because same error could occur again!
|
||||
// TODO: Add a uv_poll here for unix...
|
||||
// fprintf(stderr, "Write again...\n");
|
||||
uv_queue_work(uv_default_loop(), req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite);
|
||||
return;
|
||||
}
|
||||
|
||||
// throwing errors instead of returning them at this point is rude
|
||||
int fd = data->fd;
|
||||
_WriteQueue *q = qForFD(fd);
|
||||
if (!q) {
|
||||
Nan::ThrowTypeError("There's no write queue for that file descriptor (after write)!");
|
||||
return;
|
||||
}
|
||||
|
||||
q->lock();
|
||||
QueuedWrite &write_queue = q->get();
|
||||
|
||||
// remove this one from the list
|
||||
queuedWrite->remove();
|
||||
|
||||
data->callback.Call(1, argv);
|
||||
|
||||
// If there are any left, start a new thread to write the next one.
|
||||
if (!write_queue.empty()) {
|
||||
// Always pull the next work item from the head of the queue
|
||||
QueuedWrite* nextQueuedWrite = write_queue.next;
|
||||
uv_queue_work(uv_default_loop(), &nextQueuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite);
|
||||
}
|
||||
q->unlock();
|
||||
|
||||
data->buffer.Reset();
|
||||
delete data;
|
||||
delete queuedWrite;
|
||||
}
|
||||
|
||||
NAN_METHOD(Close) {
|
||||
// file descriptor
|
||||
if (!info[0]->IsInt32()) {
|
||||
Nan::ThrowTypeError("First argument must be an int");
|
||||
return;
|
||||
}
|
||||
|
||||
// callback
|
||||
if (!info[1]->IsFunction()) {
|
||||
Nan::ThrowTypeError("Second argument must be a function");
|
||||
return;
|
||||
}
|
||||
|
||||
CloseBaton* baton = new CloseBaton();
|
||||
memset(baton, 0, sizeof(CloseBaton));
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
baton->fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
baton->fd = info[0]->ToInt32()->Int32Value();
|
||||
#endif
|
||||
|
||||
baton->callback.Reset(info[1].As<v8::Function>());
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
req->data = baton;
|
||||
uv_queue_work(uv_default_loop(), req, EIO_Close, (uv_after_work_cb)EIO_AfterClose);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EIO_AfterClose(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
CloseBaton* data = static_cast<CloseBaton*>(req->data);
|
||||
|
||||
v8::Local<v8::Value> argv[1];
|
||||
if (data->errorString[0]) {
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(data->errorString).ToLocalChecked());
|
||||
} else {
|
||||
argv[0] = Nan::Null();
|
||||
|
||||
// We don't have an error, so clean up the write queue for that fd
|
||||
_WriteQueue *q = qForFD(data->fd);
|
||||
if (q) {
|
||||
q->lock();
|
||||
QueuedWrite &write_queue = q->get();
|
||||
while (!write_queue.empty()) {
|
||||
QueuedWrite *del_q = write_queue.next;
|
||||
del_q->baton->buffer.Reset();
|
||||
del_q->remove();
|
||||
}
|
||||
q->unlock();
|
||||
deleteQForFD(data->fd);
|
||||
}
|
||||
}
|
||||
data->callback.Call(1, argv);
|
||||
|
||||
delete data;
|
||||
delete req;
|
||||
}
|
||||
|
||||
NAN_METHOD(List) {
|
||||
// callback
|
||||
if (!info[0]->IsFunction()) {
|
||||
Nan::ThrowTypeError("First argument must be a function");
|
||||
return;
|
||||
}
|
||||
|
||||
ListBaton* baton = new ListBaton();
|
||||
strcpy(baton->errorString, "");
|
||||
baton->callback.Reset(info[0].As<v8::Function>());
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
req->data = baton;
|
||||
uv_queue_work(uv_default_loop(), req, EIO_List, (uv_after_work_cb)EIO_AfterList);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void setIfNotEmpty(v8::Local<v8::Object> item, std::string key, const char *value) {
|
||||
v8::Local<v8::String> v8key = Nan::New<v8::String>(key).ToLocalChecked();
|
||||
if (strlen(value) > 0) {
|
||||
Nan::Set(item, v8key, Nan::New<v8::String>(value).ToLocalChecked());
|
||||
} else {
|
||||
Nan::Set(item, v8key, Nan::Undefined());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EIO_AfterList(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
ListBaton* data = static_cast<ListBaton*>(req->data);
|
||||
|
||||
v8::Local<v8::Value> argv[2];
|
||||
if (data->errorString[0]) {
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(data->errorString).ToLocalChecked());
|
||||
argv[1] = Nan::Undefined();
|
||||
} else {
|
||||
v8::Local<v8::Array> results = Nan::New<v8::Array>();
|
||||
int i = 0;
|
||||
for (std::list<ListResultItem*>::iterator it = data->results.begin(); it != data->results.end(); ++it, i++) {
|
||||
v8::Local<v8::Object> item = Nan::New<v8::Object>();
|
||||
|
||||
setIfNotEmpty(item, "comName", (*it)->comName.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());
|
||||
|
||||
Nan::Set(results, i, item);
|
||||
}
|
||||
argv[0] = Nan::Null();
|
||||
argv[1] = results;
|
||||
}
|
||||
data->callback.Call(2, argv);
|
||||
|
||||
for (std::list<ListResultItem*>::iterator it = data->results.begin(); it != data->results.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
delete data;
|
||||
delete req;
|
||||
}
|
||||
|
||||
NAN_METHOD(Flush) {
|
||||
// file descriptor
|
||||
if (!info[0]->IsInt32()) {
|
||||
Nan::ThrowTypeError("First argument must be an int");
|
||||
return;
|
||||
}
|
||||
int fd;
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
fd = info[0]->ToInt32()->Int32Value();
|
||||
#endif
|
||||
|
||||
// callback
|
||||
if (!info[1]->IsFunction()) {
|
||||
Nan::ThrowTypeError("Second argument must be a function");
|
||||
return;
|
||||
}
|
||||
v8::Local<v8::Function> callback = info[1].As<v8::Function>();
|
||||
|
||||
FlushBaton* baton = new FlushBaton();
|
||||
memset(baton, 0, sizeof(FlushBaton));
|
||||
baton->fd = fd;
|
||||
baton->callback.Reset(callback);
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
req->data = baton;
|
||||
uv_queue_work(uv_default_loop(), req, EIO_Flush, (uv_after_work_cb)EIO_AfterFlush);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EIO_AfterFlush(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
FlushBaton* data = static_cast<FlushBaton*>(req->data);
|
||||
|
||||
v8::Local<v8::Value> argv[2];
|
||||
|
||||
if (data->errorString[0]) {
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(data->errorString).ToLocalChecked());
|
||||
argv[1] = Nan::Undefined();
|
||||
} else {
|
||||
argv[0] = Nan::Undefined();
|
||||
argv[1] = Nan::New<v8::Int32>(data->result);
|
||||
}
|
||||
|
||||
data->callback.Call(2, argv);
|
||||
|
||||
delete data;
|
||||
delete req;
|
||||
}
|
||||
|
||||
NAN_METHOD(Set) {
|
||||
// file descriptor
|
||||
if (!info[0]->IsInt32()) {
|
||||
Nan::ThrowTypeError("First argument must be an int");
|
||||
return;
|
||||
}
|
||||
int fd;
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
fd = info[0]->ToInt32()->Int32Value();
|
||||
#endif
|
||||
|
||||
// options
|
||||
if (!info[1]->IsObject()) {
|
||||
Nan::ThrowTypeError("Second argument must be an object");
|
||||
return;
|
||||
}
|
||||
v8::Local<v8::Object> options = info[1]->ToObject();
|
||||
|
||||
// callback
|
||||
if (!info[2]->IsFunction()) {
|
||||
Nan::ThrowTypeError("Third argument must be a function");
|
||||
return;
|
||||
}
|
||||
v8::Local<v8::Function> callback = info[2].As<v8::Function>();
|
||||
|
||||
SetBaton* baton = new SetBaton();
|
||||
memset(baton, 0, sizeof(SetBaton));
|
||||
baton->fd = fd;
|
||||
baton->callback.Reset(callback);
|
||||
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");
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
req->data = baton;
|
||||
uv_queue_work(uv_default_loop(), req, EIO_Set, (uv_after_work_cb)EIO_AfterSet);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EIO_AfterSet(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
SetBaton* data = static_cast<SetBaton*>(req->data);
|
||||
|
||||
v8::Local<v8::Value> argv[1];
|
||||
|
||||
if (data->errorString[0]) {
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(data->errorString).ToLocalChecked());
|
||||
} else {
|
||||
argv[0] = Nan::Null();
|
||||
}
|
||||
data->callback.Call(1, argv);
|
||||
|
||||
delete data;
|
||||
delete req;
|
||||
}
|
||||
|
||||
NAN_METHOD(Drain) {
|
||||
// file descriptor
|
||||
if (!info[0]->IsInt32()) {
|
||||
Nan::ThrowTypeError("First argument must be an int");
|
||||
return;
|
||||
}
|
||||
int fd;
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
fd = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
fd = info[0]->ToInt32()->Int32Value();
|
||||
#endif
|
||||
|
||||
// callback
|
||||
if (!info[1]->IsFunction()) {
|
||||
Nan::ThrowTypeError("Second argument must be a function");
|
||||
return;
|
||||
}
|
||||
|
||||
DrainBaton* baton = new DrainBaton();
|
||||
memset(baton, 0, sizeof(DrainBaton));
|
||||
baton->fd = fd;
|
||||
baton->callback.Reset(info[1].As<v8::Function>());
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
req->data = baton;
|
||||
uv_queue_work(uv_default_loop(), req, EIO_Drain, (uv_after_work_cb)EIO_AfterDrain);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EIO_AfterDrain(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
DrainBaton* data = static_cast<DrainBaton*>(req->data);
|
||||
|
||||
v8::Local<v8::Value> argv[1];
|
||||
|
||||
if (data->errorString[0]) {
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(data->errorString).ToLocalChecked());
|
||||
} else {
|
||||
argv[0] = Nan::Null();
|
||||
}
|
||||
data->callback.Call(1, argv);
|
||||
|
||||
delete data;
|
||||
delete req;
|
||||
}
|
||||
|
||||
SerialPortParity NAN_INLINE(ToParityEnum(const v8::Local<v8::String>& v8str)) {
|
||||
Nan::HandleScope scope;
|
||||
Nan::Utf8String str(v8str);
|
||||
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;
|
||||
}
|
||||
|
||||
SerialPortStopBits NAN_INLINE(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;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void init_serialport( v8::Local<v8::Object> target) {
|
||||
Nan::HandleScope scope;
|
||||
Nan::SetMethod(target, "set", Set);
|
||||
Nan::SetMethod(target, "open", Open);
|
||||
Nan::SetMethod(target, "update", Update);
|
||||
Nan::SetMethod(target, "write", Write);
|
||||
Nan::SetMethod(target, "close", Close);
|
||||
Nan::SetMethod(target, "list", List);
|
||||
Nan::SetMethod(target, "flush", Flush);
|
||||
Nan::SetMethod(target, "drain", Drain);
|
||||
|
||||
#ifndef WIN32
|
||||
SerialportPoller::Init(target);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//NODE_MODULE(serialport, init);
|
|
@ -1,195 +0,0 @@
|
|||
#ifndef SRC_SERIALPORT_H_
|
||||
#define SRC_SERIALPORT_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <nan.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#define ERROR_STRING_SIZE 1024
|
||||
|
||||
NAN_METHOD(List);
|
||||
void EIO_List(uv_work_t* req);
|
||||
void EIO_AfterList(uv_work_t* req);
|
||||
|
||||
NAN_METHOD(Open);
|
||||
void EIO_Open(uv_work_t* req);
|
||||
void EIO_AfterOpen(uv_work_t* req);
|
||||
void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback);
|
||||
|
||||
NAN_METHOD(Update);
|
||||
void EIO_Update(uv_work_t* req);
|
||||
void EIO_AfterUpdate(uv_work_t* req);
|
||||
|
||||
NAN_METHOD(Write);
|
||||
void EIO_Write(uv_work_t* req);
|
||||
void EIO_AfterWrite(uv_work_t* req);
|
||||
|
||||
NAN_METHOD(Close);
|
||||
void EIO_Close(uv_work_t* req);
|
||||
void EIO_AfterClose(uv_work_t* req);
|
||||
|
||||
NAN_METHOD(Flush);
|
||||
void EIO_Flush(uv_work_t* req);
|
||||
void EIO_AfterFlush(uv_work_t* req);
|
||||
|
||||
NAN_METHOD(Set);
|
||||
void EIO_Set(uv_work_t* req);
|
||||
void EIO_AfterSet(uv_work_t* req);
|
||||
|
||||
NAN_METHOD(Drain);
|
||||
void EIO_Drain(uv_work_t* req);
|
||||
void EIO_AfterDrain(uv_work_t* req);
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
SerialPortParity ToParityEnum(const v8::Local<v8::String>& str);
|
||||
SerialPortStopBits ToStopBitEnum(double stopBits);
|
||||
|
||||
struct OpenBatonPlatformOptions { };
|
||||
OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local<v8::Object>& options);
|
||||
|
||||
struct OpenBaton {
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
Nan::Callback callback;
|
||||
char path[1024];
|
||||
int fd;
|
||||
int result;
|
||||
int baudRate;
|
||||
int dataBits;
|
||||
int bufferSize;
|
||||
bool rtscts;
|
||||
bool xon;
|
||||
bool xoff;
|
||||
bool xany;
|
||||
bool dsrdtr;
|
||||
bool hupcl;
|
||||
bool lock;
|
||||
Nan::Callback* dataCallback;
|
||||
Nan::Callback* disconnectedCallback;
|
||||
Nan::Callback* errorCallback;
|
||||
SerialPortParity parity;
|
||||
SerialPortStopBits stopBits;
|
||||
OpenBatonPlatformOptions* platformOptions;
|
||||
};
|
||||
|
||||
struct ConnectionOptionsBaton {
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
Nan::Callback callback;
|
||||
int fd;
|
||||
int baudRate;
|
||||
};
|
||||
|
||||
struct WriteBaton {
|
||||
int fd;
|
||||
char* bufferData;
|
||||
size_t bufferLength;
|
||||
size_t offset;
|
||||
Nan::Persistent<v8::Object> buffer;
|
||||
Nan::Callback callback;
|
||||
int result;
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
};
|
||||
|
||||
struct QueuedWrite {
|
||||
uv_work_t req;
|
||||
QueuedWrite *prev;
|
||||
QueuedWrite *next;
|
||||
WriteBaton* baton;
|
||||
|
||||
QueuedWrite() {
|
||||
prev = this;
|
||||
next = this;
|
||||
|
||||
baton = 0;
|
||||
}
|
||||
|
||||
~QueuedWrite() {
|
||||
remove();
|
||||
}
|
||||
|
||||
void remove() {
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
|
||||
next = this;
|
||||
prev = this;
|
||||
}
|
||||
|
||||
void insert_tail(QueuedWrite *qw) {
|
||||
qw->next = this;
|
||||
qw->prev = this->prev;
|
||||
qw->prev->next = qw;
|
||||
this->prev = qw;
|
||||
}
|
||||
|
||||
bool empty() {
|
||||
return next == this;
|
||||
}
|
||||
};
|
||||
|
||||
struct CloseBaton {
|
||||
int fd;
|
||||
Nan::Callback callback;
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
};
|
||||
|
||||
struct ListResultItem {
|
||||
std::string comName;
|
||||
std::string manufacturer;
|
||||
std::string serialNumber;
|
||||
std::string pnpId;
|
||||
std::string locationId;
|
||||
std::string vendorId;
|
||||
std::string productId;
|
||||
};
|
||||
|
||||
struct ListBaton {
|
||||
Nan::Callback callback;
|
||||
std::list<ListResultItem*> results;
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
};
|
||||
|
||||
struct FlushBaton {
|
||||
int fd;
|
||||
Nan::Callback callback;
|
||||
int result;
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
};
|
||||
|
||||
struct SetBaton {
|
||||
int fd;
|
||||
Nan::Callback callback;
|
||||
int result;
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
bool rts;
|
||||
bool cts;
|
||||
bool dtr;
|
||||
bool dsr;
|
||||
bool brk;
|
||||
};
|
||||
|
||||
struct DrainBaton {
|
||||
int fd;
|
||||
Nan::Callback callback;
|
||||
int result;
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
};
|
||||
|
||||
int setup(int fd, OpenBaton *data);
|
||||
int setBaudRate(ConnectionOptionsBaton *data);
|
||||
#endif // SRC_SERIALPORT_H_
|
|
@ -1,128 +0,0 @@
|
|||
// Copyright (C) 2013 Robert Giseburt <giseburt@gmail.com>
|
||||
// serialport_poller.cpp Written as a part of https://github.com/voodootikigod/node-serialport
|
||||
// License to use this is the same as that of node-serialport.
|
||||
|
||||
#include <nan.h>
|
||||
#include "./serialport_poller.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
static Nan::Persistent<v8::FunctionTemplate> serialportpoller_constructor;
|
||||
|
||||
SerialportPoller::SerialportPoller() : Nan::ObjectWrap() {}
|
||||
SerialportPoller::~SerialportPoller() {
|
||||
// printf("~SerialportPoller\n");
|
||||
delete callback_;
|
||||
}
|
||||
|
||||
void _serialportReadable(uv_poll_t *req, int status, int events) {
|
||||
SerialportPoller* sp = (SerialportPoller*) req->data;
|
||||
// We can stop polling until we have read all of the data...
|
||||
sp->_stop();
|
||||
sp->callCallback(status);
|
||||
}
|
||||
|
||||
void SerialportPoller::callCallback(int status) {
|
||||
Nan::HandleScope scope;
|
||||
// uv_work_t* req = new uv_work_t;
|
||||
|
||||
// Call the callback to go read more data...
|
||||
|
||||
v8::Local<v8::Value> argv[1];
|
||||
if (status != 0) {
|
||||
// error handling changed in libuv, see:
|
||||
// https://github.com/joyent/libuv/commit/3ee4d3f183331
|
||||
#ifdef UV_ERRNO_H_
|
||||
const char* err_string = uv_strerror(status);
|
||||
#else
|
||||
uv_err_t errno = uv_last_error(uv_default_loop());
|
||||
const char* err_string = uv_strerror(errno);
|
||||
#endif
|
||||
snprintf(this->errorString, sizeof(this->errorString), "Error %s on polling", err_string);
|
||||
argv[0] = v8::Exception::Error(Nan::New<v8::String>(this->errorString).ToLocalChecked());
|
||||
} else {
|
||||
argv[0] = Nan::Undefined();
|
||||
}
|
||||
|
||||
callback_->Call(1, argv);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SerialportPoller::Init(Local<Object> target) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
// Prepare constructor template
|
||||
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
|
||||
tpl->SetClassName(Nan::New<String>("SerialportPoller").ToLocalChecked());
|
||||
tpl->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
|
||||
|
||||
// Prototype
|
||||
|
||||
// SerialportPoller.close()
|
||||
Nan::SetPrototypeMethod(tpl, "close", Close);
|
||||
|
||||
// SerialportPoller.start()
|
||||
Nan::SetPrototypeMethod(tpl, "start", Start);
|
||||
|
||||
serialportpoller_constructor.Reset(tpl);
|
||||
|
||||
Nan::Set(target, Nan::New<String>("SerialportPoller").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());
|
||||
}
|
||||
|
||||
NAN_METHOD(SerialportPoller::New) {
|
||||
if (!info[0]->IsInt32()) {
|
||||
Nan::ThrowTypeError("First argument must be an fd");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info[1]->IsFunction()) {
|
||||
Nan::ThrowTypeError("Third argument must be a function");
|
||||
return;
|
||||
}
|
||||
|
||||
SerialportPoller* obj = new SerialportPoller();
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
obj->fd_ = info[0]->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
obj->fd_ = info[0]->ToInt32()->Int32Value();
|
||||
#endif
|
||||
obj->callback_ = new Nan::Callback(info[1].As<v8::Function>());
|
||||
// obj->callCallback();
|
||||
|
||||
obj->Wrap(info.This());
|
||||
|
||||
obj->poll_handle_.data = obj;
|
||||
|
||||
uv_poll_init(uv_default_loop(), &obj->poll_handle_, obj->fd_);
|
||||
|
||||
uv_poll_start(&obj->poll_handle_, UV_READABLE, _serialportReadable);
|
||||
|
||||
info.GetReturnValue().Set(info.This());
|
||||
}
|
||||
|
||||
void SerialportPoller::_start() {
|
||||
uv_poll_start(&poll_handle_, UV_READABLE, _serialportReadable);
|
||||
}
|
||||
|
||||
void SerialportPoller::_stop() {
|
||||
uv_poll_stop(&poll_handle_);
|
||||
}
|
||||
|
||||
|
||||
NAN_METHOD(SerialportPoller::Start) {
|
||||
SerialportPoller* obj = Nan::ObjectWrap::Unwrap<SerialportPoller>(info.This());
|
||||
obj->_start();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
NAN_METHOD(SerialportPoller::Close) {
|
||||
SerialportPoller* obj = Nan::ObjectWrap::Unwrap<SerialportPoller>(info.This());
|
||||
obj->_stop();
|
||||
|
||||
// DO SOMETHING!
|
||||
|
||||
return;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (C) 2013 Robert Giseburt <giseburt@gmail.com>
|
||||
// serialport_poller.h Written as a part of https://github.com/voodootikigod/node-serialport
|
||||
// License to use this is the same as that of node-serialport.
|
||||
|
||||
#ifndef SERIALPORT_POLLER_H
|
||||
#define SERIALPORT_POLLER_H
|
||||
|
||||
#include <nan.h>
|
||||
#include "./serialport.h"
|
||||
|
||||
class SerialportPoller : public Nan::ObjectWrap {
|
||||
public:
|
||||
static void Init( v8::Local<v8::Object> target);
|
||||
|
||||
void callCallback(int status);
|
||||
|
||||
void _start();
|
||||
void _stop();
|
||||
|
||||
private:
|
||||
SerialportPoller();
|
||||
~SerialportPoller();
|
||||
|
||||
static NAN_METHOD(New);
|
||||
static NAN_METHOD(Close);
|
||||
static NAN_METHOD(Start);
|
||||
|
||||
uv_poll_t poll_handle_;
|
||||
int fd_;
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
|
||||
Nan::Callback* callback_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,740 +0,0 @@
|
|||
#include "./serialport.h"
|
||||
#include "./serialport_poller.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>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#include <IOKit/serial/IOSerialKeys.h>
|
||||
|
||||
uv_mutex_t list_mutex;
|
||||
Boolean lockInitialised = FALSE;
|
||||
#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>
|
||||
#endif
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/serial.h>
|
||||
#endif
|
||||
|
||||
struct UnixPlatformOptions : OpenBatonPlatformOptions {
|
||||
uint8_t vmin;
|
||||
uint8_t vtime;
|
||||
};
|
||||
|
||||
OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local<v8::Object>& options) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
UnixPlatformOptions* result = new UnixPlatformOptions();
|
||||
#if NODE_MAJOR_VERSION >= 10
|
||||
result->vmin = Nan::Get(options, Nan::New<v8::String>("vmin").ToLocalChecked()).ToLocalChecked()->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
result->vtime = Nan::Get(options, Nan::New<v8::String>("vtime").ToLocalChecked()).ToLocalChecked()->ToInt32(Nan::GetCurrentContext()).ToLocalChecked()->Value();
|
||||
#else
|
||||
result->vmin = Nan::Get(options, Nan::New<v8::String>("vmin").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value();
|
||||
result->vtime = Nan::Get(options, Nan::New<v8::String>("vtime").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value();
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ToBaudConstant(int baudRate);
|
||||
int ToDataBitsConstant(int dataBits);
|
||||
int ToStopBitsConstant(SerialPortStopBits stopBits);
|
||||
|
||||
void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback) {
|
||||
delete dataCallback;
|
||||
delete errorCallback;
|
||||
delete disconnectedCallback;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
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
|
||||
|
||||
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 EIO_Open(uv_work_t* req) {
|
||||
OpenBaton* data = static_cast<OpenBaton*>(req->data);
|
||||
|
||||
int flags = (O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC | O_SYNC);
|
||||
int fd = open(data->path, flags);
|
||||
|
||||
if (-1 == fd) {
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot open %s", strerror(errno), data->path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (-1 == setup(fd, data)) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
data->result = fd;
|
||||
}
|
||||
|
||||
int setBaudRate(ConnectionOptionsBaton *data) {
|
||||
// lookup the standard baudrates from the table
|
||||
int baudRate = ToBaudConstant(data->baudRate);
|
||||
int fd = data->fd;
|
||||
|
||||
// get port options
|
||||
struct termios options;
|
||||
if (tcgetattr(fd, &options)) {
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error: tcgetattr encountering %s", strerror(errno));
|
||||
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) {
|
||||
struct serial_struct serinfo;
|
||||
serinfo.reserved_char[0] = 0;
|
||||
if (-1 != ioctl(fd, TIOCGSERIAL, &serinfo)) {
|
||||
serinfo.flags &= ~ASYNC_SPD_MASK;
|
||||
serinfo.flags |= ASYNC_SPD_CUST;
|
||||
serinfo.custom_divisor = (serinfo.baud_base + (data->baudRate / 2)) / data->baudRate;
|
||||
if (serinfo.custom_divisor < 1)
|
||||
serinfo.custom_divisor = 1;
|
||||
|
||||
ioctl(fd, TIOCSSERIAL, &serinfo);
|
||||
ioctl(fd, TIOCGSERIAL, &serinfo);
|
||||
} else {
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error: %s setting custom baud rate of %d", strerror(errno), data->baudRate);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now we use "B38400" to trigger the special baud rate.
|
||||
baudRate = B38400;
|
||||
}
|
||||
#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 {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we have a good baud rate set it and lets go
|
||||
if (-1 != baudRate) {
|
||||
cfsetospeed(&options, baudRate);
|
||||
cfsetispeed(&options, baudRate);
|
||||
tcflush(fd, TCIFLUSH);
|
||||
tcsetattr(fd, TCSANOW, &options);
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error baud rate of %d is not supported on your platform", data->baudRate);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void EIO_Update(uv_work_t* req) {
|
||||
ConnectionOptionsBaton* data = static_cast<ConnectionOptionsBaton*>(req->data);
|
||||
setBaudRate(data);
|
||||
}
|
||||
|
||||
int setup(int fd, OpenBaton *data) {
|
||||
UnixPlatformOptions* platformOptions = static_cast<UnixPlatformOptions*>(data->platformOptions);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Copy the connection options into the ConnectionOptionsBaton to set the baud rate
|
||||
ConnectionOptionsBaton* connectionOptions = new ConnectionOptionsBaton();
|
||||
memset(connectionOptions, 0, sizeof(ConnectionOptionsBaton));
|
||||
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);
|
||||
|
||||
// Get port configuration for modification
|
||||
struct termios options;
|
||||
if (tcgetattr(fd, &options)) {
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error: tcgetattr encountering %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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]= platformOptions->vmin;
|
||||
options.c_cc[VTIME]= platformOptions->vtime;
|
||||
|
||||
// why?
|
||||
tcflush(fd, TCIFLUSH);
|
||||
|
||||
// check for error?
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void EIO_Write(uv_work_t* req) {
|
||||
QueuedWrite* queuedWrite = static_cast<QueuedWrite*>(req->data);
|
||||
WriteBaton* data = static_cast<WriteBaton*>(queuedWrite->baton);
|
||||
int bytesWritten = 0;
|
||||
|
||||
do {
|
||||
errno = 0; // probably don't need this
|
||||
bytesWritten = write(data->fd, data->bufferData + data->offset, data->bufferLength - data->offset);
|
||||
if (-1 != bytesWritten) {
|
||||
// there wasn't an error, do the math on what we actually wrote and keep writing until finished
|
||||
data->offset += bytesWritten;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The write call was interrupted before anything was written, try again immediately.
|
||||
if (errno == EINTR) {
|
||||
// why try again right away instead of in another event loop?
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try again in another event loop
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK){
|
||||
return;
|
||||
}
|
||||
|
||||
// EBAD would mean we're "disconnected"
|
||||
|
||||
// a real error so lets bail
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error: %s, calling write", strerror(errno));
|
||||
return;
|
||||
} while (data->bufferLength > data->offset);
|
||||
}
|
||||
|
||||
void EIO_Close(uv_work_t* req) {
|
||||
CloseBaton* data = static_cast<CloseBaton*>(req->data);
|
||||
if (-1 == close(data->fd)) {
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error: %s, unable to close fd %d", strerror(errno), data->fd);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
// 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, 11, "0x%08x", locationID);
|
||||
}
|
||||
|
||||
UInt16 vendorID;
|
||||
kernResult = (*deviceInterface)->GetDeviceVendor(deviceInterface, &vendorID);
|
||||
if (KERN_SUCCESS == kernResult) {
|
||||
snprintf(serialDevice->vendorId, 7, "0x%04x", vendorID);
|
||||
}
|
||||
|
||||
UInt16 productID;
|
||||
kernResult = (*deviceInterface)->GetDeviceProduct(deviceInterface, &productID);
|
||||
if (KERN_SUCCESS == kernResult) {
|
||||
snprintf(serialDevice->productId, 7, "0x%04x", productID);
|
||||
}
|
||||
}
|
||||
|
||||
static stDeviceListItem* GetSerialDevices() {
|
||||
kern_return_t kernResult;
|
||||
io_iterator_t serialPortIterator;
|
||||
char bsdPath[MAXPATHLEN];
|
||||
|
||||
FindModems(&serialPortIterator);
|
||||
|
||||
io_service_t modemService;
|
||||
kernResult = KERN_FAILURE;
|
||||
Boolean modemFound = false;
|
||||
|
||||
// Initialize the returned path
|
||||
*bsdPath = '\0';
|
||||
|
||||
stDeviceListItem* devices = NULL;
|
||||
stDeviceListItem* lastDevice = NULL;
|
||||
int length = 0;
|
||||
|
||||
while ((modemService = IOIteratorNext(serialPortIterator))) {
|
||||
CFTypeRef bsdPathAsCFString;
|
||||
bsdPathAsCFString = IORegistryEntrySearchCFProperty(
|
||||
modemService,
|
||||
kIOServicePlane,
|
||||
CFSTR(kIOCalloutDeviceKey),
|
||||
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 = (stDeviceListItem*) malloc(sizeof(stDeviceListItem));
|
||||
stSerialDevice *serialDevice = &(deviceListItem->value);
|
||||
strcpy(serialDevice->port, 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) {
|
||||
strcpy(serialDevice->manufacturer, 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) {
|
||||
strcpy(serialDevice->serialNumber, serialNumber);
|
||||
}
|
||||
|
||||
CFRelease(serialNumberAsCFString);
|
||||
}
|
||||
|
||||
IOCFPlugInInterface **plugInInterface = NULL;
|
||||
SInt32 score;
|
||||
HRESULT res;
|
||||
|
||||
IOUSBDeviceInterface **deviceInterface = NULL;
|
||||
|
||||
kernResult = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
|
||||
&plugInInterface, &score);
|
||||
|
||||
if ((kIOReturnSuccess != kernResult) || !plugInInterface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use the plugin interface to retrieve the device interface.
|
||||
res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
|
||||
(LPVOID*) &deviceInterface);
|
||||
|
||||
// Now done with the plugin interface.
|
||||
(*plugInInterface)->Release(plugInInterface);
|
||||
|
||||
if (res || deviceInterface == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void EIO_List(uv_work_t* req) {
|
||||
ListBaton* data = static_cast<ListBaton*>(req->data);
|
||||
|
||||
#ifndef __APPLE__
|
||||
// This code exists in javascript for other unix platforms
|
||||
snprintf(data->errorString, sizeof(data->errorString), "List is not Implemented");
|
||||
return;
|
||||
# else
|
||||
if (!lockInitialised) {
|
||||
uv_mutex_init(&list_mutex);
|
||||
lockInitialised = TRUE;
|
||||
}
|
||||
|
||||
stDeviceListItem* devices = GetSerialDevices();
|
||||
if (*(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->comName = 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;
|
||||
}
|
||||
data->results.push_back(resultItem);
|
||||
|
||||
stDeviceListItem* current = next;
|
||||
|
||||
if (next->next != NULL) {
|
||||
next = next->next;
|
||||
}
|
||||
|
||||
free(current);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void EIO_Flush(uv_work_t* req) {
|
||||
FlushBaton* data = static_cast<FlushBaton*>(req->data);
|
||||
|
||||
data->result = tcflush(data->fd, TCIFLUSH);
|
||||
}
|
||||
|
||||
void EIO_Set(uv_work_t* req) {
|
||||
SetBaton* data = static_cast<SetBaton*>(req->data);
|
||||
|
||||
int bits;
|
||||
ioctl(data->fd, TIOCMGET, &bits);
|
||||
|
||||
bits &= ~(TIOCM_RTS | TIOCM_CTS | TIOCM_DTR | TIOCM_DSR);
|
||||
|
||||
if (data->rts) {
|
||||
bits |= TIOCM_RTS;
|
||||
}
|
||||
|
||||
if (data->cts) {
|
||||
bits |= TIOCM_CTS;
|
||||
}
|
||||
|
||||
if (data->dtr) {
|
||||
bits |= TIOCM_DTR;
|
||||
}
|
||||
|
||||
if (data->dsr) {
|
||||
bits |= TIOCM_DSR;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
if (data->brk) {
|
||||
result = ioctl(data->fd, TIOCSBRK, NULL);
|
||||
} else {
|
||||
result = ioctl(data->fd, TIOCCBRK, NULL);
|
||||
}
|
||||
|
||||
if (-1 == result) {
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (-1 == ioctl(data->fd, TIOCMSET, &bits)) {
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void EIO_Drain(uv_work_t* req) {
|
||||
DrainBaton* data = static_cast<DrainBaton*>(req->data);
|
||||
|
||||
if (-1 == tcdrain(data->fd)) {
|
||||
snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -1,582 +0,0 @@
|
|||
#include <nan.h>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include "./serialport.h"
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <Setupapi.h>
|
||||
#include <devguid.h>
|
||||
#pragma comment (lib, "setupapi.lib")
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#define MAX_BUFFER_SIZE 1000
|
||||
|
||||
struct WindowsPlatformOptions : OpenBatonPlatformOptions {
|
||||
};
|
||||
|
||||
OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local<v8::Object>& options) {
|
||||
// currently none
|
||||
return new WindowsPlatformOptions();
|
||||
}
|
||||
|
||||
// Declare type of pointer to CancelIoEx function
|
||||
typedef BOOL (WINAPI *CancelIoExType)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
|
||||
|
||||
std::list<int> g_closingHandles;
|
||||
int bufferSize;
|
||||
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;
|
||||
default:
|
||||
_snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Unknown error code %d", prefix, errorCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EIO_Open(uv_work_t* req) {
|
||||
OpenBaton* data = static_cast<OpenBaton*>(req->data);
|
||||
|
||||
char originalPath[1024];
|
||||
strncpy_s(originalPath, sizeof(originalPath), data->path, _TRUNCATE);
|
||||
// data->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(data->path + 20, data->path, 10);
|
||||
strncpy(data->path, "\\\\.\\", 4);
|
||||
strncpy(data->path + 4, data->path + 20, 10);
|
||||
|
||||
int shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
if (data->lock) {
|
||||
shareMode = 0;
|
||||
}
|
||||
|
||||
HANDLE file = CreateFile(
|
||||
data->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, data->errorString);
|
||||
return;
|
||||
}
|
||||
|
||||
bufferSize = data->bufferSize;
|
||||
if (bufferSize > MAX_BUFFER_SIZE) {
|
||||
bufferSize = MAX_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
DCB dcb = { 0 };
|
||||
SecureZeroMemory(&dcb, sizeof(DCB));
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
|
||||
if (!GetCommState(file, &dcb)) {
|
||||
ErrorCodeToString("Open (GetCommState)", GetLastError(), data->errorString);
|
||||
CloseHandle(file);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->hupcl) {
|
||||
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
} else {
|
||||
dcb.fDtrControl = DTR_CONTROL_DISABLE; // disable DTR to avoid reset
|
||||
}
|
||||
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
dcb.fInX = FALSE;
|
||||
dcb.fOutX = FALSE;
|
||||
dcb.fOutxDsrFlow = FALSE;
|
||||
dcb.fOutxCtsFlow = FALSE;
|
||||
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
|
||||
dcb.fBinary = true;
|
||||
dcb.BaudRate = data->baudRate;
|
||||
dcb.ByteSize = data->dataBits;
|
||||
|
||||
switch (data->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 (data->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(), data->errorString);
|
||||
CloseHandle(file);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the com port read/write timeouts
|
||||
DWORD serialBitsPerByte = 8/*std data bits*/ + 1/*start bit*/;
|
||||
serialBitsPerByte += (data->parity == SERIALPORT_PARITY_NONE) ? 0 : 1;
|
||||
serialBitsPerByte += (data->stopBits == SERIALPORT_STOPBITS_ONE) ? 1 : 2;
|
||||
DWORD msPerByte = (data->baudRate > 0) ?
|
||||
((1000 * serialBitsPerByte + data->baudRate - 1) / data->baudRate) :
|
||||
1;
|
||||
if (msPerByte < 1) {
|
||||
msPerByte = 1;
|
||||
}
|
||||
COMMTIMEOUTS commTimeouts = {0};
|
||||
commTimeouts.ReadIntervalTimeout = msPerByte; // Minimize chance of concatenating of separate serial port packets on read
|
||||
commTimeouts.ReadTotalTimeoutMultiplier = 0; // Do not allow big read timeout when big read buffer used
|
||||
commTimeouts.ReadTotalTimeoutConstant = 1000; // Total read timeout (period of read loop)
|
||||
commTimeouts.WriteTotalTimeoutConstant = 1000; // Const part of write timeout
|
||||
commTimeouts.WriteTotalTimeoutMultiplier = msPerByte; // Variable part of write timeout (per byte)
|
||||
if (!SetCommTimeouts(file, &commTimeouts)) {
|
||||
ErrorCodeToString("Open (SetCommTimeouts)", GetLastError(), data->errorString);
|
||||
CloseHandle(file);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove garbage data in RX/TX queues
|
||||
PurgeComm(file, PURGE_RXCLEAR);
|
||||
PurgeComm(file, PURGE_TXCLEAR);
|
||||
|
||||
data->result = (int)file;
|
||||
}
|
||||
|
||||
struct WatchPortBaton {
|
||||
HANDLE fd;
|
||||
DWORD bytesRead;
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
char errorString[ERROR_STRING_SIZE];
|
||||
DWORD errorCode;
|
||||
bool disconnected;
|
||||
Nan::Callback* dataCallback;
|
||||
Nan::Callback* errorCallback;
|
||||
Nan::Callback* disconnectedCallback;
|
||||
};
|
||||
|
||||
void EIO_Update(uv_work_t* req) {
|
||||
ConnectionOptionsBaton* data = static_cast<ConnectionOptionsBaton*>(req->data);
|
||||
|
||||
DCB dcb = { 0 };
|
||||
SecureZeroMemory(&dcb, sizeof(DCB));
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
|
||||
if (!GetCommState((HANDLE)data->fd, &dcb)) {
|
||||
ErrorCodeToString("GetCommState", GetLastError(), data->errorString);
|
||||
return;
|
||||
}
|
||||
|
||||
dcb.BaudRate = data->baudRate;
|
||||
|
||||
if (!SetCommState((HANDLE)data->fd, &dcb)) {
|
||||
ErrorCodeToString("SetCommState", GetLastError(), data->errorString);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void EIO_Set(uv_work_t* req) {
|
||||
SetBaton* data = static_cast<SetBaton*>(req->data);
|
||||
|
||||
if (data->rts) {
|
||||
EscapeCommFunction((HANDLE)data->fd, SETRTS);
|
||||
} else {
|
||||
EscapeCommFunction((HANDLE)data->fd, CLRRTS);
|
||||
}
|
||||
|
||||
if (data->dtr) {
|
||||
EscapeCommFunction((HANDLE)data->fd, SETDTR);
|
||||
} else {
|
||||
EscapeCommFunction((HANDLE)data->fd, CLRDTR);
|
||||
}
|
||||
|
||||
if (data->brk) {
|
||||
EscapeCommFunction((HANDLE)data->fd, SETBREAK);
|
||||
} else {
|
||||
EscapeCommFunction((HANDLE)data->fd, CLRBREAK);
|
||||
}
|
||||
|
||||
DWORD bits = 0;
|
||||
|
||||
GetCommMask((HANDLE)data->fd, &bits);
|
||||
|
||||
bits &= ~(EV_CTS | EV_DSR);
|
||||
|
||||
if (data->cts) {
|
||||
bits |= EV_CTS;
|
||||
}
|
||||
|
||||
if (data->dsr) {
|
||||
bits |= EV_DSR;
|
||||
}
|
||||
|
||||
if (!SetCommMask((HANDLE)data->fd, bits)) {
|
||||
ErrorCodeToString("Setting options on COM port (SetCommMask)", GetLastError(), data->errorString);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EIO_WatchPort(uv_work_t* req) {
|
||||
WatchPortBaton* data = static_cast<WatchPortBaton*>(req->data);
|
||||
data->bytesRead = 0;
|
||||
data->disconnected = false;
|
||||
|
||||
// Event used by GetOverlappedResult(..., TRUE) to wait for incoming data or timeout
|
||||
// Event MUST be used if program has several simultaneous asynchronous operations
|
||||
// on the same handle (i.e. ReadFile and WriteFile)
|
||||
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
while (true) {
|
||||
OVERLAPPED ov = {0};
|
||||
ov.hEvent = hEvent;
|
||||
|
||||
// Start read operation - synchrounous or asynchronous
|
||||
DWORD bytesReadSync = 0;
|
||||
if (!ReadFile((HANDLE)data->fd, data->buffer, bufferSize, &bytesReadSync, &ov)) {
|
||||
data->errorCode = GetLastError();
|
||||
if (data->errorCode != ERROR_IO_PENDING) {
|
||||
// Read operation error
|
||||
if (data->errorCode == ERROR_OPERATION_ABORTED) {
|
||||
data->disconnected = true;
|
||||
} else {
|
||||
ErrorCodeToString("Reading from COM port (ReadFile)", data->errorCode, data->errorString);
|
||||
CloseHandle(hEvent);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Read operation is asynchronous and is pending
|
||||
// We MUST wait for operation completion before deallocation of OVERLAPPED struct
|
||||
// or read data buffer
|
||||
|
||||
// Wait for async read operation completion or timeout
|
||||
DWORD bytesReadAsync = 0;
|
||||
if (!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesReadAsync, TRUE)) {
|
||||
// Read operation error
|
||||
data->errorCode = GetLastError();
|
||||
if (data->errorCode == ERROR_OPERATION_ABORTED) {
|
||||
data->disconnected = true;
|
||||
} else {
|
||||
ErrorCodeToString("Reading from COM port (GetOverlappedResult)", data->errorCode, data->errorString);
|
||||
CloseHandle(hEvent);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// Read operation completed asynchronously
|
||||
data->bytesRead = bytesReadAsync;
|
||||
}
|
||||
} else {
|
||||
// Read operation completed synchronously
|
||||
data->bytesRead = bytesReadSync;
|
||||
}
|
||||
|
||||
// Return data received if any
|
||||
if (data->bytesRead > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hEvent);
|
||||
}
|
||||
|
||||
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 DisposeWatchPortCallbacks(WatchPortBaton* data) {
|
||||
delete data->dataCallback;
|
||||
delete data->errorCallback;
|
||||
delete data->disconnectedCallback;
|
||||
}
|
||||
|
||||
// FinalizerCallback will prevent WatchPortBaton::buffer from getting
|
||||
// collected by gc while finalizing v8::ArrayBuffer. The buffer will
|
||||
// get cleaned up through this callback.
|
||||
static void FinalizerCallback(char* data, void* hint) {
|
||||
uv_work_t* req = reinterpret_cast<uv_work_t*>(hint);
|
||||
WatchPortBaton* wpb = static_cast<WatchPortBaton*>(req->data);
|
||||
delete wpb;
|
||||
delete req;
|
||||
}
|
||||
|
||||
void EIO_AfterWatchPort(uv_work_t* req) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
WatchPortBaton* data = static_cast<WatchPortBaton*>(req->data);
|
||||
if (data->disconnected) {
|
||||
data->disconnectedCallback->Call(0, NULL);
|
||||
DisposeWatchPortCallbacks(data);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
bool skipCleanup = false;
|
||||
if (data->bytesRead > 0) {
|
||||
v8::Local<v8::Value> argv[1];
|
||||
argv[0] = Nan::NewBuffer(data->buffer, data->bytesRead, FinalizerCallback, req).ToLocalChecked();
|
||||
skipCleanup = true;
|
||||
data->dataCallback->Call(1, argv);
|
||||
} else if (data->errorCode > 0) {
|
||||
if (data->errorCode == ERROR_INVALID_HANDLE && IsClosingHandle((int)data->fd)) {
|
||||
DisposeWatchPortCallbacks(data);
|
||||
goto cleanup;
|
||||
} else {
|
||||
v8::Local<v8::Value> argv[1];
|
||||
argv[0] = Nan::Error(data->errorString);
|
||||
data->errorCallback->Call(1, argv);
|
||||
Sleep(100); // prevent the errors from occurring too fast
|
||||
}
|
||||
}
|
||||
AfterOpenSuccess((int)data->fd, data->dataCallback, data->disconnectedCallback, data->errorCallback);
|
||||
|
||||
cleanup:
|
||||
if (!skipCleanup) {
|
||||
delete data;
|
||||
delete req;
|
||||
}
|
||||
}
|
||||
|
||||
void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback) {
|
||||
WatchPortBaton* baton = new WatchPortBaton();
|
||||
memset(baton, 0, sizeof(WatchPortBaton));
|
||||
baton->fd = (HANDLE)fd;
|
||||
baton->dataCallback = dataCallback;
|
||||
baton->errorCallback = errorCallback;
|
||||
baton->disconnectedCallback = disconnectedCallback;
|
||||
|
||||
uv_work_t* req = new uv_work_t();
|
||||
req->data = baton;
|
||||
|
||||
uv_queue_work(uv_default_loop(), req, EIO_WatchPort, (uv_after_work_cb)EIO_AfterWatchPort);
|
||||
}
|
||||
|
||||
void EIO_Write(uv_work_t* req) {
|
||||
QueuedWrite* queuedWrite = static_cast<QueuedWrite*>(req->data);
|
||||
WriteBaton* data = static_cast<WriteBaton*>(queuedWrite->baton);
|
||||
data->result = 0;
|
||||
|
||||
do {
|
||||
OVERLAPPED ov = {0};
|
||||
// Event used by GetOverlappedResult(..., TRUE) to wait for outgoing data or timeout
|
||||
// Event MUST be used if program has several simultaneous asynchronous operations
|
||||
// on the same handle (i.e. ReadFile and WriteFile)
|
||||
ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
// Start write operation - synchronous or asynchronous
|
||||
DWORD bytesWritten = 0;
|
||||
if (!WriteFile((HANDLE)data->fd, data->bufferData, static_cast<DWORD>(data->bufferLength), &bytesWritten, &ov)) {
|
||||
DWORD lastError = GetLastError();
|
||||
if (lastError != ERROR_IO_PENDING) {
|
||||
// Write operation error
|
||||
ErrorCodeToString("Writing to COM port (WriteFile)", lastError, data->errorString);
|
||||
CloseHandle(ov.hEvent);
|
||||
return;
|
||||
}
|
||||
// Write operation is completing asynchronously
|
||||
// We MUST wait for the operation completion before deallocation of OVERLAPPED struct
|
||||
// or write data buffer
|
||||
|
||||
// block for async write operation completion
|
||||
bytesWritten = 0;
|
||||
if (!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesWritten, TRUE)) {
|
||||
// Write operation error
|
||||
DWORD lastError = GetLastError();
|
||||
ErrorCodeToString("Writing to COM port (GetOverlappedResult)", lastError, data->errorString);
|
||||
CloseHandle(ov.hEvent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Write operation completed synchronously
|
||||
data->result = bytesWritten;
|
||||
data->offset += data->result;
|
||||
CloseHandle(ov.hEvent);
|
||||
} while (data->bufferLength > data->offset);
|
||||
}
|
||||
|
||||
void EIO_Close(uv_work_t* req) {
|
||||
CloseBaton* data = static_cast<CloseBaton*>(req->data);
|
||||
|
||||
g_closingHandles.push_back(data->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((HANDLE)data->fd, NULL);
|
||||
}
|
||||
if (!CloseHandle((HANDLE)data->fd)) {
|
||||
ErrorCodeToString("closing connection", GetLastError(), data->errorString);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char *copySubstring(char *someString, int n)
|
||||
{
|
||||
char *new_ = (char*)malloc(sizeof(char)*n + 1);
|
||||
strncpy_s(new_, n + 1, someString, n);
|
||||
new_[n] = '\0';
|
||||
return new_;
|
||||
}
|
||||
|
||||
void EIO_List(uv_work_t* req) {
|
||||
ListBaton* data = static_cast<ListBaton*>(req->data);
|
||||
|
||||
GUID *guidDev = (GUID*)& GUID_DEVCLASS_PORTS;
|
||||
HDEVINFO hDevInfo = SetupDiGetClassDevs(guidDev, NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE);
|
||||
SP_DEVINFO_DATA deviceInfoData;
|
||||
|
||||
int memberIndex = 0;
|
||||
DWORD dwSize, dwPropertyRegDataType;
|
||||
char szBuffer[400];
|
||||
char *pnpId;
|
||||
char *vendorId;
|
||||
char *productId;
|
||||
char *name;
|
||||
char *manufacturer;
|
||||
char *locationId;
|
||||
bool isCom;
|
||||
while (true) {
|
||||
pnpId = NULL;
|
||||
vendorId = NULL;
|
||||
productId = NULL;
|
||||
name = NULL;
|
||||
manufacturer = NULL;
|
||||
locationId = 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);
|
||||
SetupDiGetDeviceInstanceId(hDevInfo, &deviceInfoData, szBuffer, dwSize, &dwSize);
|
||||
szBuffer[dwSize] = '\0';
|
||||
pnpId = strdup(szBuffer);
|
||||
|
||||
vendorId = strstr(szBuffer, "VID_");
|
||||
if (vendorId) {
|
||||
vendorId += 4;
|
||||
vendorId = copySubstring(vendorId, 4);
|
||||
}
|
||||
productId = strstr(szBuffer, "PID_");
|
||||
if (productId) {
|
||||
productId += 4;
|
||||
productId = copySubstring(productId, 4);
|
||||
}
|
||||
|
||||
if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_LOCATION_INFORMATION, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) {
|
||||
locationId = strdup(szBuffer);
|
||||
}
|
||||
if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_MFG, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) {
|
||||
manufacturer = strdup(szBuffer);
|
||||
}
|
||||
|
||||
HKEY hkey = SetupDiOpenDevRegKey(hDevInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
|
||||
if (hkey != INVALID_HANDLE_VALUE) {
|
||||
dwSize = sizeof(szBuffer);
|
||||
if (RegQueryValueEx(hkey, "PortName", NULL, NULL, (LPBYTE)&szBuffer, &dwSize) == ERROR_SUCCESS) {
|
||||
szBuffer[dwSize] = '\0';
|
||||
name = strdup(szBuffer);
|
||||
isCom = strstr(szBuffer, "COM") != NULL;
|
||||
}
|
||||
}
|
||||
if (isCom) {
|
||||
ListResultItem* resultItem = new ListResultItem();
|
||||
resultItem->comName = name;
|
||||
resultItem->manufacturer = manufacturer;
|
||||
resultItem->pnpId = pnpId;
|
||||
if (vendorId) {
|
||||
resultItem->vendorId = vendorId;
|
||||
}
|
||||
if (productId) {
|
||||
resultItem->productId = productId;
|
||||
}
|
||||
if (locationId) {
|
||||
resultItem->locationId = locationId;
|
||||
}
|
||||
data->results.push_back(resultItem);
|
||||
}
|
||||
free(pnpId);
|
||||
free(vendorId);
|
||||
free(productId);
|
||||
free(locationId);
|
||||
free(manufacturer);
|
||||
free(name);
|
||||
|
||||
RegCloseKey(hkey);
|
||||
memberIndex++;
|
||||
}
|
||||
if (hDevInfo) {
|
||||
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void EIO_Flush(uv_work_t* req) {
|
||||
FlushBaton* data = static_cast<FlushBaton*>(req->data);
|
||||
|
||||
if (!FlushFileBuffers((HANDLE)data->fd)) {
|
||||
ErrorCodeToString("flushing connection (FlushFileBuffers)", GetLastError(), data->errorString);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void EIO_Drain(uv_work_t* req) {
|
||||
DrainBaton* data = static_cast<DrainBaton*>(req->data);
|
||||
|
||||
if (!FlushFileBuffers((HANDLE)data->fd)) {
|
||||
ErrorCodeToString("draining connection (FlushFileBuffers)", GetLastError(), data->errorString);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -9,19 +9,6 @@ module.exports = {
|
|||
path: path.resolve(__dirname, "out"),
|
||||
filename: "[name].js"
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
cacheGroups: {
|
||||
vendor: {
|
||||
test: /node_modules/,
|
||||
chunks: "initial",
|
||||
name: "vendor",
|
||||
priority: 10,
|
||||
enforce: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
devtool: "eval-source-map",
|
||||
resolve: {
|
||||
extensions: [".js", ".ts", ".tsx", ".json"]
|
||||
|
|
Загрузка…
Ссылка в новой задаче