diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index e7d6470..c8d657b 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -34,3 +34,20 @@ jobs: - name: "Run pytest" run: python -m pytest + + - name: "Run from cli" + run: python -m hvps --version + + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: 'npm' + cache-dependency-path: bindings/nodejs/package-lock.json + + - name: "Install nodejs bindings" + run: | + cd bindings/nodejs + npm --version + node --version + npm install + node cli.js diff --git a/.gitignore b/.gitignore index 78bef46..385844d 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,5 @@ dmypy.json .idea/ .vscode/ + +node_modules/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1393cc0..8024ec5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,20 @@ repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: check-yaml + - id: check-xml + - id: requirements-txt-fixer + - id: end-of-file-fixer + - id: mixed-line-ending + - id: trailing-whitespace + - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 23.3.0 hooks: - id: black diff --git a/LICENSE b/LICENSE index e278411..a325152 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,28 @@ -MIT License +BSD 3-Clause License -Copyright (c) 2022 Luis Antonio Obis Aparicio +Copyright (c) 2023, Luis Antonio Obis Aparicio -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: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -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. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bindings/nodejs/cli.js b/bindings/nodejs/cli.js new file mode 100644 index 0000000..0b12fc1 --- /dev/null +++ b/bindings/nodejs/cli.js @@ -0,0 +1,25 @@ +const { ExecutionContext } = require('./index'); +const yargs = require('yargs'); + +const cli = yargs + .scriptName('hvps') + .usage('$0 [args]') + .option('python', { + description: 'Specify the Python path to run HVPS.', + alias: 'p', + type: 'string', + default: 'python', // Set a default string value + }) + .help() + .alias('h', 'help') + .argv; + +// check python is available +const context = new ExecutionContext(cli.python); +context.print(); + +// Get the remaining arguments as a string +const remainingArgs = cli._.join(' '); +// these should be passed to the python code. TODO: not working! + +context.run(remainingArgs); diff --git a/bindings/nodejs/index.js b/bindings/nodejs/index.js new file mode 100644 index 0000000..5d2d90d --- /dev/null +++ b/bindings/nodejs/index.js @@ -0,0 +1,62 @@ +const { execSync } = require('child_process'); + + +class ExecutionContext { + constructor(pythonPath) { + this.python = pythonPath; + // check if python path is correct and use absolute path + this.python = this.getPythonPath(); + + // check if the hvps package is available + this.getPackageVersion(); + } + + getPythonVersion() { + const command = `${this.python} -c "import sys; print(sys.version)"`; + try { + return execSync(command, { encoding: 'utf-8' }).trim(); + } catch (err) { + console.error(`Error executing command (${command}): ${err}`); + throw err; + } + } + + getPythonPath() { + const command = `${this.python} -c "import sys; print(sys.executable)"`; + try { + return execSync(command, { encoding: 'utf-8' }).trim(); + } catch (err) { + console.error(`Error executing command (${command}): ${err}`); + throw err; + } + } + + getPackageVersion() { + const command = `${this.python} -m hvps --version`; + try { + return execSync(command, { encoding: 'utf-8' }).trim(); + } catch (err) { + throw new Error(`Error executing command (${command}). Please make sure the package is installed.`); + } + } + + print() { + console.log(`Python version: ${this.getPythonVersion()}`); + console.log(`Python path: ${this.getPythonPath()}`); + console.log(`Package version: ${this.getPackageVersion()}`); + } + + // run method should call the python script with the arguments + run(args) { + const command = `${this.python} -m hvps ${args}`; + try { + return execSync(command, { encoding: 'utf-8' }).trim(); + } catch (err) { + throw new Error(`Error executing command (${command}): ${err}`); + } + } +} + +module.exports = { + ExecutionContext, +}; diff --git a/bindings/nodejs/package-lock.json b/bindings/nodejs/package-lock.json new file mode 100644 index 0000000..87f33fc --- /dev/null +++ b/bindings/nodejs/package-lock.json @@ -0,0 +1,180 @@ +{ + "name": "hvps", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hvps", + "version": "0.0.1", + "license": "BSD-3-Clause", + "dependencies": { + "yargs": "^17.7.2" + }, + "bin": { + "hvps": "cli.js" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/bindings/nodejs/package.json b/bindings/nodejs/package.json new file mode 100644 index 0000000..6de6043 --- /dev/null +++ b/bindings/nodejs/package.json @@ -0,0 +1,25 @@ +{ + "name": "hvps", + "version": "0.0.1", + "description": "Bindings for the HVPS python package", + "main": "index.js", + "bin": { + "hvps": "cli.js" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/lobis/hvps.git" + }, + "author": "Luis Antonio Obis Aparicio (@lobis)", + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/lobis/hvps/issues" + }, + "homepage": "https://github.com/lobis/hvps#readme", + "dependencies": { + "yargs": "^17.7.2" + } +} diff --git a/src/hvps/__main__.py b/src/hvps/__main__.py new file mode 100644 index 0000000..3a1f96c --- /dev/null +++ b/src/hvps/__main__.py @@ -0,0 +1,13 @@ +from . import version, HVPS +import argparse + + +def main(): + parser = argparse.ArgumentParser(description="HVPS control") + parser.add_argument("--version", action="version", version=version.__version__) + + args = parser.parse_args() + + +if __name__ == "__main__": + main() diff --git a/src/hvps/commands/caen/__init__.py b/src/hvps/commands/caen/__init__.py index 651905c..0400a31 100644 --- a/src/hvps/commands/caen/__init__.py +++ b/src/hvps/commands/caen/__init__.py @@ -1,8 +1,14 @@ from __future__ import annotations import re -from src.hvps.commands.caen.module import _get_mon_module_command, _get_set_module_command -from src.hvps.commands.caen.channel import _get_mon_channel_command, _get_set_channel_command +from .module import ( + _get_mon_module_command, + _get_set_module_command, +) +from .channel import ( + _get_mon_channel_command, + _get_set_channel_command, +) import logging diff --git a/src/hvps/commands/iseg/__init__.py b/src/hvps/commands/iseg/__init__.py index 2935c28..7986074 100644 --- a/src/hvps/commands/iseg/__init__.py +++ b/src/hvps/commands/iseg/__init__.py @@ -1,7 +1,10 @@ from __future__ import annotations import re -from src.hvps.commands.iseg.channel import _get_set_channel_command, _get_mon_channel_command +from .channel import ( + _get_set_channel_command, + _get_mon_channel_command, +) import logging diff --git a/src/hvps/commands/iseg/channel.py b/src/hvps/commands/iseg/channel.py index 4b34517..60ef77d 100644 --- a/src/hvps/commands/iseg/channel.py +++ b/src/hvps/commands/iseg/channel.py @@ -6,6 +6,8 @@ def _get_mon_channel_command(channel: int, parameter: str) -> bytes: pass -def _get_set_channel_command(channel: int, parameter: str, value: str | int | float | None) -> bytes: +def _get_set_channel_command( + channel: int, parameter: str, value: str | int | float | None +) -> bytes: # TODO: implement pass diff --git a/src/hvps/devices/channel.py b/src/hvps/devices/channel.py index 81be6c9..f36f769 100644 --- a/src/hvps/devices/channel.py +++ b/src/hvps/devices/channel.py @@ -1,7 +1,10 @@ import serial -from src.hvps.commands.caen.channel import _get_set_channel_command, _get_mon_channel_command -from src.hvps.commands.caen import _parse_response +from ..commands.caen.channel import ( + _get_set_channel_command, + _get_mon_channel_command, +) +from ..commands.caen import _parse_response from time import sleep diff --git a/src/hvps/devices/hvps.py b/src/hvps/devices/hvps.py index 2ff5509..cf2d649 100644 --- a/src/hvps/devices/hvps.py +++ b/src/hvps/devices/hvps.py @@ -32,7 +32,6 @@ def detect_baudrate(port: str) -> int: logger.debug(f"Trying baud rate {baudrate}") # Open the serial port with the current baud rate with serial.Serial(port, baudrate, timeout=1.0) as ser: - # Send the baud rate detection message start_time = time.time() ser.write(baudrate_detection_message) diff --git a/src/hvps/devices/module.py b/src/hvps/devices/module.py index c344f00..9835e87 100644 --- a/src/hvps/devices/module.py +++ b/src/hvps/devices/module.py @@ -3,9 +3,9 @@ import serial -from src.hvps.commands.caen.module import _get_mon_module_command -from src.hvps.commands.caen import _parse_response -from src.hvps.devices.channel import Channel +from ..commands.caen.module import _get_mon_module_command +from ..commands.caen import _parse_response +from .channel import Channel class Module: diff --git a/tests/test_caen.py b/tests/test_caen.py index d7101be..6cb7081 100644 --- a/tests/test_caen.py +++ b/tests/test_caen.py @@ -5,6 +5,7 @@ # find a way to only run these tests if a serial port connection exists + @pytest.mark.skip(reason="Needs fixing") def test_caen_no_connection(): with pytest.raises(Exception): diff --git a/tests/test_caen_commands.py b/tests/test_caen_commands.py index c24b05e..7ebbe3c 100644 --- a/tests/test_caen_commands.py +++ b/tests/test_caen_commands.py @@ -1,6 +1,10 @@ import pytest -from hvps.commands.caen import _parse_response, _get_set_module_command, _get_mon_module_command +from hvps.commands.caen import ( + _parse_response, + _get_set_module_command, + _get_mon_module_command, +) def test_caen_module_get_commands():