Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • jiddon/labRemote
  • itk-strips-at-sr1/labRemote
  • kkrizka/labRemote
  • bbrueers/labRemote
  • usoldevi/labRemote
  • dsperlic/labRemote
  • eballabe/labRemote
  • yanght/labRemote
  • jgrosse/labRemote
  • scipp/pixels/labRemote
  • zhicaiz/labRemote
  • otoldaie/labRemote
  • bsmart/labRemote
  • scipp/labRemote
  • losojnak/labRemote
  • jdopke/labRemote
  • epianori/labRemote
  • berkeleylab/labRemote
18 results
Select Git revision
Show changes
Commits on Source (313)
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -3
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseTab: Never
...
# labRemote external dependencies
./src/exts/*
./src/libGalil/*
./src/libZaber/*
# labRemote configuration files
./src/configs/*
......@@ -12,6 +12,12 @@ build/*
*.egg-info
*.pickle
*pycache*
*.whl
src/labRemote/version.py
MANIFEST
_skbuild
dist
wheelhouse
##
## build files
......
stages:
- check
- build
- package
- deploy
variables:
GIT_SUBMODULE_STRATEGY: recursive
.base:
image: centos:7
before_script:
......@@ -18,56 +16,107 @@ variables:
cmake3
json-devel
libusb-devel
python3-devel
.cibuildwheel:
image: python:3.8
stage: package
# make a docker daemon available for cibuildwheel to use
tags:
- docker-privileged-xl
services:
- name: docker:dind
entrypoint: ["env", "-u", "DOCKER_HOST"]
command: ["dockerd-entrypoint.sh"]
variables:
DOCKER_HOST: tcp://docker:2375/
DOCKER_DRIVER: overlay2
# See https://github.com/docker-library/docker/pull/166
DOCKER_TLS_CERTDIR: ""
CIBW_ARCHS_LINUX: x86_64
CIBW_ARCHS_MACOS: x86_64
GIT_SUBMODULE_STRATEGY: recursive
script:
- curl -sSL https://get.docker.com/ | sh
- python -m pip install cibuildwheel
- cibuildwheel --output-dir dist
artifacts:
paths:
- dist/
check_format:
image:
name: kratsg/clang-format
entrypoint: [""]
stage: check
script:
- run-clang-format -r ./src/ ./arduino/ --extensions c,h,cpp,hpp,cxx,hxx,ino
check_json:
image: centos:7
stage: check
before_script:
- yum -y install yajl
script:
- json_verify < src/configs/input-hw.json
build-bare:
check_manifest:
image: python:3.8
stage: check
script:
- python -m pip install --upgrade pip setuptools wheel
- python -m pip install check-manifest
- check-manifest
build_bare:
extends: .base
stage: build
variables:
GIT_SUBMODULE_STRATEGY: recursive
script:
- mkdir build
- cd build
- cmake3 ..
- make
build-bare-python:
build_bare-python:
extends: .base
stage: build
variables:
GIT_SUBMODULE_STRATEGY: recursive
script:
- yum -y install python3-devel
- mkdir build
- cd build
- cmake3 -DUSE_PYTHON=on ..
- make
- PYTHONPATH=lib:$PYTHONPATH python3 -c "import _labRemote as labRemote"
- PYTHONPATH=lib:$PYTHONPATH python3 -c "import _labRemote as labRemote; testPS = labRemote.ps.AgilentPs('testPS'); testChannel = labRemote.ps.PowerSupplyChannel('testChannel', testPS, 123)"
- PYTHONPATH=lib:$PYTHONPATH python3 -c "import _labRemote as labRemote; testChiller = labRemote.chiller.PolySciLM();"
- PYTHONPATH=lib:$PYTHONPATH python3 -c "import _labRemote as labRemote; testChiller = labRemote.chiller.PolySciLM('testPolySciLMChiller');"
build-pip-install:
build_pip-install:
extends: .base
stage: build
variables:
GIT_SUBMODULE_STRATEGY: recursive
script:
- yum -y install python3-devel
- yum install -y git
- python3 -m venv myenv
- source myenv/bin/activate
- pip install -U pip
- pip install .
- python3 -c "import labRemote"
- python3 -c "import labRemote; testPS = labRemote.ps.AgilentPs('testPS'); testChannel = labRemote.ps.PowerSupplyChannel('testChannel', testPS, 123)"
- python3 -c "import labRemote; testChiller = labRemote.chiller.PolySciLM();"
- python3 -c "import labRemote; testChiller = labRemote.chiller.PolySciLM('testPolySciLMChiller');"
- python3 -c "import labRemote.com"
- python3 -c "import labRemote.ps"
- python3 -c "import labRemote.ec"
- python3 -c "from labRemote.com import GPIBSerialCom"
build:
extends: .base
stage: build
variables:
GIT_SUBMODULE_STRATEGY: recursive
script:
- echo "Hello World"
#- curl -o /etc/yum.repos.d/picoscope.repo https://labs.picotech.com/rpm/picoscope.repo
......@@ -90,31 +139,125 @@ build:
- cmake3 ..
- make
validate_json:
image: centos:7
stage: build
before_script:
- yum -y install yajl
script:
- json_verify < src/configs/input-hw.json
package_image:
stage: package
only:
refs:
- main
- devel
- tags
image:
name: gitlab-registry.cern.ch/ci-tools/docker-image-builder
entrypoint: [""]
variables:
GIT_SUBMODULE_STRATEGY: recursive
needs: []
script:
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context $CI_PROJECT_DIR
--dockerfile $CI_PROJECT_DIR/Dockerfile
--dockerfile $CI_PROJECT_DIR/ci/Dockerfile
--destination $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
deploy:
extends: .base
package_linux_cpy36:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp36-*
CIBW_PLATFORM: linux
package_linux_py37:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp37-*
CIBW_PLATFORM: linux
package_linux_py38:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp38-*
CIBW_PLATFORM: linux
package_linux_py39:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp39-*
CIBW_PLATFORM: linux
package_linux_py310:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp310-*
CIBW_PLATFORM: linux
package_macos_cpy36:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp36-*
CIBW_PLATFORM: linux
package_macos_py37:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp37-*
CIBW_PLATFORM: linux
package_macos_py38:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp38-*
CIBW_PLATFORM: linux
package_macos_py39:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp39-*
CIBW_PLATFORM: linux
package_macos_py310:
extends: .cibuildwheel
variables:
CIBW_BUILD: cp310-*
CIBW_PLATFORM: linux
package_sdist:
image: python:3.8
stage: package
script:
- python -m pip install --upgrade pip setuptools wheel
- python -m pip install build
- python -m build --sdist --outdir dist/ .
artifacts:
paths:
- dist/
build_doxygen:
image: quay.io/centos/centos:stream8
stage: build
variables:
GIT_SUBMODULE_STRATEGY: recursive
artifacts:
paths:
- build/doc/html
script:
- dnf -y install dnf-plugins-core
- dnf -y config-manager --set-enabled powertools
- dnf -y install make
gcc
gcc-c++
autoconf
cmake
libarchive
- dnf -y install doxygen
- sed -i '1 s/^#\(.*\)$/\1 {#mainpage}\n============/' README.md
- mkdir build
- cd build
- cmake ..
- make doxygen
deploy_doxygen:
stage: deploy
only:
refs:
- master
- main
- devel
- tags
variables:
......@@ -122,12 +265,26 @@ deploy:
variables:
EOS_PATH: "/eos/user/l/labremote/www/${CI_COMMIT_REF_SLUG}"
image: gitlab-registry.cern.ch/ci-tools/ci-web-deployer
needs: ["build_doxygen"]
script:
- yum -y install doxygen
- sed -i '1 s/^#\(.*\)$/\1 {#mainpage}\n============/' README.md
- mkdir build
- cd build
- cmake3 ..
- make doxygen
- cd doc/html
- cd build/doc/html
- CI_OUTPUT_DIR=./ deploy-eos
deploy_python:
image: python:3.8
stage: deploy
only:
refs:
- main
- devel
- tags
variables:
TWINE_PASSWORD: '${CI_JOB_TOKEN}'
TWINE_USERNAME: 'gitlab-ci-token'
TWINE_REPOSITORY_URL: 'https://gitlab.cern.ch/api/v4/projects/${CI_PROJECT_ID}/packages/pypi'
script:
- python -m pip install --upgrade pip setuptools wheel
- python -m pip install twine
- ls -lavh dist/
- python -m twine check dist/*
- python -m twine upload dist/*
......@@ -10,3 +10,6 @@
[submodule "src/exts/influxdb-cpp"]
path = src/exts/influxdb-cpp
url = https://github.com/orca-zhang/influxdb-cpp.git
[submodule "src/exts/json-schema-validator"]
path = src/exts/json-schema-validator
url = https://github.com/pboettch/json-schema-validator.git
cmake_minimum_required (VERSION 3.13)
cmake_minimum_required (VERSION 3.15)
project(labRemote
DESCRIPTION "Library for controlling laboratory equipment.")
......@@ -12,6 +12,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
set(CMAKE_CXX_STANDARD 11)
......
......@@ -7,20 +7,21 @@ __Project Maintainers:__
- Simone Pagan Griso
- Karol Krizka
- Elisabetta Pianori
- Daniel Antrim
## Definition of Roles
The _Project Manager_'s role is to keep an overview of the development happening in labRemote. The PM's responsibilities include:
- being the main contact for labRemote
- regularly merging the `devel` branch into `master`
- regularly merging the `devel` branch into `main`
- merging all Merge Requests
The _Maintainers_ have a very good knowledge of the labRemote code base and are responsible for reviewing Merge Requests.
## Usage of The devel Branch
The `master` branch is to be assumed as relatively stable. All new development should be merged into the `devel` branch. The `devel` branch is __not__ to be used for applications!
The `main` branch is to be assumed as relatively stable. All new development should be merged into the `devel` branch. The `devel` branch is __not__ to be used for applications!
The `devel` branch will be merged into `master` every Monday, with weeks skipped based on the Project Manager's discretion.
The `devel` branch will be merged into `main` every Monday, with weeks skipped based on the Project Manager's discretion.
## Making Changes to labRemote
......
This diff is collapsed.
prune *
prune **
graft src/labRemote
include README.md
include setup.py
include MANIFEST.in
include pyproject.toml
include LICENSE
global-exclude __pycache__ *.py[cod] .*
global-exclude __pycache__ *.py[cod] CMakeLists.txt
......@@ -78,8 +78,6 @@ that the user is responsible for installing them on their system if required.
| [libmpsse](https://github.com/l29ah/libmpsse) | Library that implements common communication protocols (I2C, SPI) using the MPSSE framework on FTDI chips | FTDICom | **NO** |
| [linux-gpib](https://linux-gpib.sourceforge.io/) | Support for GPIB hardware | [libPS](src/libPS) | **NO** |
| [Qt 5.9.2](https://www.qt.io/download-qt-for-application-development) | GUI fun in C++ | [Probe Station](src/libWaferProb) | **NO** |
| [OpenCV 3.3.1](https://docs.opencv.org/3.3.1/d7/d9f/tutorial_linux_install.html) | | [Probe Station](src/libWaferProb) | **NO** |
| [gclib](http://galilmc.com/sw/pub/all/doc/gclib/html/osx.html) | | [Probe Station](src/libWaferProb) | **NO** |
| [graphviz](https://graphviz.org/) | Requirement for `doxygen`, provides `dot` utility | | **NO** |
One can also refer to the [.gitlab-ci.yml](.gitlab-ci.yml) file for a complete listing
......@@ -129,7 +127,7 @@ An example `labRemote-app` is provided for by [ExampleLabRemoteProject](https://
<!---------------------------------------------------------------------------->
# Python Bindings
Python bindings to the `labRemote` target can be enabled either by setting a flag
Python bindings to the `labRemote` target can be enabled either by setting the flag `-DUSE_PYTHON=on`
during the [cmake compilation step](#compiling-labremote) or via `pip`.
## Requirements for Python Bindings
......@@ -186,3 +184,24 @@ source myenv/bin/activate # start the virtual environment
Examples of how to use the `labRemote` `python` bindings are provided in the [examples](src/examples) directory,
where you will find `python` versions of existing `C++` `labRemote` examples.
## Run clang-format before committing changes
Below are the commands for runing clang-fomat in order to pass the pipeline.
```Shell
# use the venv
cd /path/to/labRemote
source myenv/bin/activate
python -m pip install clang-format==9.0.0
# in the labRemote directory
./ci/run-clang-format.py -r ./src/ ./arduino/ --extensions c,h,cpp,hpp,cxx,hxx,ino
# if it works, like output of git diff
# --- ./src/examples/fluke45_example.cpp (original)
# +++ ./src/examples/fluke45_example.cpp (reformatted)
# add the option -i
# the files are edited in-place
./ci/run-clang-format.py -r ./src/ ./arduino/ --extensions c,h,cpp,hpp,cxx,hxx,ino -i
```
// Arduino Uno
#if defined(ARDUINO_AVR_UNO)
static const uint8_t analog_pins[] = {A0,A1,A2,A3,A4,A5}; //Arduino Uno
static const uint8_t analog_pins[] = {A0, A1, A2, A3, A4, A5};
static const uint8_t n_digital_pins = 14;
// Arduino mega
#elif defined(ARDUINO_AVR_MEGA2560)
static const uint8_t analog_pins[] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15}; //Arduino mega
static const uint8_t analog_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7,
A8, A9, A10, A11, A12, A13, A14, A15};
static const uint8_t n_digital_pins = 54;
// Arduino Nano
#elif defined(ARDUINO_AVR_NANO)
static const uint8_t analog_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7};
static const uint8_t n_digital_pins = 22;
#endif
#include <EEPROM.h>
#include "Wire.h"
#include "devcomuino.h"
const unsigned int MAXARGS=5;
const unsigned int MAXBYTES=16;
const unsigned int MAXCMDLENGTH=256;
const unsigned int MAXARGLENGTH=16;
const unsigned int MAXARGS = 5;
const unsigned int MAXBYTES = 16;
const unsigned int MAXCMDLENGTH = 64;
const unsigned int MAXARGLENGTH = 16;
// Command parsing
size_t cmdptr=0;
size_t cmdptr = 0;
char command[MAXCMDLENGTH];
unsigned int argc=0;
unsigned int argc = 0;
char argv[MAXARGS][MAXARGLENGTH];
// Parse command execute the right function
void runcommand()
{
// Tokenize string into command and argument
char* tok=strtok(command," ");
argc=0;
while(tok != NULL)
{
strcpy(argv[argc++], tok);
tok = strtok(NULL, " ");
void runcommand() {
// Tokenize string into command and argument
char *tok = strtok(command, " ");
argc = 0;
while (tok != NULL) {
strcpy(argv[argc++], tok);
tok = strtok(NULL, " ");
}
// Execute the right command
if(strncmp("HELP", argv[0], 4)==0)
{ // Print help menu
cmdHELP();
// Execute the right command
if (strncmp("HELP", argv[0], 4) == 0) { // Print help menu
cmdHELP();
}
//need to check ADC functionality
else if( strncmp("ADC", argv[0], 3)==0)
{ // Read ADC
if(argc!=2)
{
Serial.println("ERR wrong number of arguments to ADC");
return;
// need to check ADC functionality
else if (strncmp("ADC", argv[0], 3) == 0) { // Read ADC
if (argc != 2) {
Serial.println("ERR wrong number of args to ADC");
return;
}
cmdADC(atoi(argv[1]));
cmdADC(atoi(argv[1]));
}
else if(strncmp("I2C", argv[0], 3)==0)
{ // I2C commands
if(argc<4)
{
Serial.println("ERR wrong number of arguments to I2C");
return;
else if (strncmp("EEPROM", argv[0], 6) == 0) { // EEPROM commands
if (strncmp("WRITE", argv[1], 5) == 0) {
if (argc < 4) {
Serial.println("ERR wrong number of args to EEPROM WRITE");
return;
}
int address = atoi(argv[2]);
int value = atoi(argv[3]);
cmdEEPROMwrite(address, value);
}
if (strncmp("READ", argv[1], 4) == 0) {
if (argc < 3) {
Serial.println("ERR wrong number of args to EEPROM READ");
return;
}
int address = atoi(argv[2]);
cmdEEPROMread(address);
}
int addr =0;
sscanf(argv[2], "%02x", &addr);
if(strncmp("WRITE",argv[1],5)==0)
{
cmdI2Cwrite(addr, argv[3]);
} else if (strncmp("I2C", argv[0], 3) == 0) { // I2C commands
if (argc < 4) {
Serial.println("ERR wrong number of args to I2C");
return;
}
else if(strncmp("READ",argv[1],4)==0)
{
cmdI2Cread(addr,atoi(argv[3]));
int addr = 0;
sscanf(argv[2], "%02x", &addr);
if (strncmp("WRITE", argv[1], 5) == 0) {
cmdI2Cwrite(addr, argv[3]);
} else if (strncmp("READ", argv[1], 4) == 0) {
cmdI2Cread(addr, atoi(argv[3]));
} else {
Serial.println("ERR unknown I2C command");
}
else
{
Serial.println("ERR unknown I2C command");
} else if (strncmp("DGT", argv[0], 3) == 0) { // DGT commands
if (argc == 3) {
int chan = atoi(argv[2]);
if (strncmp("OUT", argv[1], 3) == 0) {
cmdDGToutput(chan);
} else if (strncmp("IN", argv[1], 2) == 0) {
cmdDGTinput(chan);
} else if (strncmp("PULLUP", argv[1], 6) == 0) {
cmdDGTinputpullup(chan);
} else if (strncmp("READ", argv[1], 4) == 0) {
cmdDGTread(chan);
} else {
Serial.println("ERR unknown DGT command");
}
} else if (argc == 4 && strncmp("WRITE", argv[1], 5) == 0) {
int chan = atoi(argv[2]);
int value = atoi(argv[3]);
cmdDGTwrite(chan, value);
} else {
Serial.println("ERR wrong number of args to DGT");
return;
}
}
else
{
Serial.println("ERR unknown command");
} else {
Serial.println("ERR unknown command");
}
}
......@@ -75,76 +108,141 @@ void runcommand()
//
// Print
void cmdHELP()
{
Serial.println("Hello World from DevComduino!");
Serial.println("");
Serial.println("Available commands:");
Serial.println("\tHELP - Print this help");
Serial.println("\tADC ch - Return ADC reading on channel ch");
Serial.println("\tI2C WRITE addr byte-string - Write a byte-string using I2C to addr, MSB first");
Serial.println("\tI2C READ addr nbytes - Read number of bytes from addr");
void cmdHELP() {
Serial.println(F("Hello World from DevComuino!"));
Serial.println("");
Serial.println(F("Commands:"));
Serial.println(F("\tHELP - This help"));
Serial.println(F("\tADC ch - Read ADC channel ch"));
Serial.println(
F("\tI2C WRITE addr byte-string - Write byte-string to I2C addr, "
"MSB first"));
Serial.println(F("\tI2C READ addr nbytes - Read nbytes from I2C addr"));
Serial.println(F("\tEEPROM WRITE addr value - Write to addr in EEPROM"));
Serial.println(F("\tEEPROM READ addr - Read addr from EEPROM"));
Serial.println(F("\tDGT OUT ch - Set digital pin ch as output"));
Serial.println(F("\tDGT IN ch - Set digital pin ch as input"));
Serial.println(F("\tDGT PULLUP ch - Set digital pin ch as input w/pullup"));
Serial.println(F("\tDGT READ ch - Read channel ch"));
Serial.println(
F("\tDGT WRITE ch 0|1 - Set channel ch to LOW (0) or HIGH (0)"));
}
//
// Read an analogue pin
void cmdADC(int channel) {
float V;
void cmdADC(int channel)
{
float V;
if (channel < sizeof(analog_pins)) {
V=analogRead(analog_pins[channel]);
Serial.println(V);
}
else {
Serial.println("ERR invalid channel");
}
if (channel < sizeof(analog_pins)) {
V = analogRead(analog_pins[channel]);
Serial.println(V);
} else {
Serial.println("ERR invalid channel");
}
}
//
// EEPROM Write
void cmdEEPROMwrite(int address, int value) { EEPROM.write(address, value); }
//
// EEPROM Read
void cmdEEPROMread(int address) {
int val = EEPROM.read(address);
Serial.println(val);
}
//
// I2C write
void cmdI2Cwrite(int address, char *cmd)
{
Wire.beginTransmission(address);
int c =0;
for(int i=0; i<strlen(cmd); i+=2)
{
sscanf(&cmd[i], "%02x", &c);
Wire.write(c);
void cmdI2Cwrite(int address, char *cmd) {
Wire.beginTransmission(address);
int c = 0;
for (int i = 0; i < strlen(cmd); i += 2) {
sscanf(&cmd[i], "%02x", &c);
Wire.write(c);
}
Wire.endTransmission();
Serial.println("OK");
Wire.endTransmission();
Serial.println("OK");
}
//
// I2C read
void cmdI2Cread(int address, unsigned int nBytes)
{
Wire.requestFrom(address,nBytes);
unsigned char c;
char cstr[4];
//sprintf(cstr, "%02x:", nBytes);
//Serial.print(cstr);
for(unsigned int i=0;i<nBytes;i++)
{
if(Wire.available())
{
c=Wire.read();
sprintf(cstr, "%02x", c);
Serial.print(cstr);
//Serial.print(strlen(cstr));
}
else
{
Serial.print("ERR");
}
void cmdI2Cread(int address, unsigned int nBytes) {
Wire.requestFrom(address, nBytes);
unsigned char c;
char cstr[4];
// sprintf(cstr, "%02x:", nBytes);
// Serial.print(cstr);
for (unsigned int i = 0; i < nBytes; i++) {
if (Wire.available()) {
c = Wire.read();
sprintf(cstr, "%02x", c);
Serial.print(cstr);
// Serial.print(strlen(cstr));
} else {
Serial.print("ERR");
}
}
Serial.println();
Serial.println();
}
//
// DGT input
void cmdDGTinput(int channel) {
if (channel < n_digital_pins) {
pinMode(channel, INPUT);
Serial.println("OK");
} else {
Serial.println("ERR invalid channel");
}
}
//
// DGT input with pullup resistors
void cmdDGTinputpullup(int channel) {
if (channel < n_digital_pins) {
pinMode(channel, INPUT_PULLUP);
Serial.println("OK");
} else {
Serial.println("ERR invalid channel");
}
}
//
// DGT output
void cmdDGToutput(int channel) {
if (channel < n_digital_pins) {
pinMode(channel, OUTPUT);
Serial.println("OK");
} else {
Serial.println("ERR invalid channel");
}
}
//
// DGT write
void cmdDGTwrite(int channel, int value) {
if (channel < n_digital_pins) {
if (value == 0) {
digitalWrite(channel, LOW);
} else {
digitalWrite(channel, HIGH);
}
Serial.println("OK");
} else {
Serial.println("ERR invalid channel");
}
}
//
// DGT read
void cmdDGTread(int channel) {
uint8_t data = digitalRead(channel);
Serial.println(data);
}
//
......@@ -153,40 +251,41 @@ void cmdI2Cread(int address, unsigned int nBytes)
//
// Setup serial
void setup()
{
Serial.begin(9600);
Wire.begin();
void setup() {
Serial.begin(115200);
Wire.begin();
}
//
// The main loop looks for commands
void loop()
{
if(Serial.available()==0)
return;
// Read new data
size_t length=Serial.readBytes(&command[cmdptr], min(Serial.available(), MAXCMDLENGTH-cmdptr));
if(length==0) return; // No new data...
cmdptr+=length;
// Check if command finished (new line)
if (cmdptr < 2) return;
if (command[cmdptr-2]!='\r' && command[cmdptr-1]!='\n') {
if (cmdptr >= MAXCMDLENGTH-1) {
//overflow command. Clean-up buffer to avoid stalled program
cmdptr=0;
Serial.print("ERR Command too long");
}
return;
}
void loop() {
if (Serial.available() == 0) return;
// There is a command! Process it...
char *c=command; while(*c) { *c=toupper(*c); c++; }
command[cmdptr-2]='\0';
// Read new data
size_t length = Serial.readBytes(
&command[cmdptr], min(Serial.available(), MAXCMDLENGTH - cmdptr));
if (length == 0) return; // No new data...
cmdptr += length;
runcommand();
cmdptr=0; // Reset command
// Check if command finished (new line)
if (cmdptr < 2) return;
if (command[cmdptr - 2] != '\r' && command[cmdptr - 1] != '\n') {
if (cmdptr >= MAXCMDLENGTH - 1) {
// overflow command. Clean-up buffer to avoid stalled program
cmdptr = 0;
Serial.print("ERR command too long");
}
return;
}
// There is a command! Process it...
char *c = command;
while (*c) {
*c = toupper(*c);
c++;
}
command[cmdptr - 2] = '\0';
runcommand();
cmdptr = 0; // Reset command
}
File moved
#!/usr/bin/env python
"""A wrapper script around clang-format, suitable for linting multiple files
and to use for continuous integration.
This is an alternative API for the clang-format command line.
It runs over multiple files and directories in parallel.
A diff output is produced and a sensible exit code is returned.
Taken from: https://github.com/Sarcasm/run-clang-format
"""
from __future__ import print_function, unicode_literals
import argparse
import codecs
import difflib
import fnmatch
import io
import errno
import multiprocessing
import os
import signal
import subprocess
import sys
import traceback
from functools import partial
try:
from subprocess import DEVNULL # py3k
except ImportError:
DEVNULL = open(os.devnull, "wb")
DEFAULT_EXTENSIONS = 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx'
DEFAULT_CLANG_FORMAT_IGNORE = '.clang-format-ignore'
class ExitStatus:
SUCCESS = 0
DIFF = 1
TROUBLE = 2
def excludes_from_file(ignore_file):
excludes = []
try:
with io.open(ignore_file, 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('#'):
# ignore comments
continue
pattern = line.rstrip()
if not pattern:
# allow empty lines
continue
excludes.append(pattern)
except EnvironmentError as e:
if e.errno != errno.ENOENT:
raise
return excludes;
def list_files(files, recursive=False, extensions=None, exclude=None):
if extensions is None:
extensions = []
if exclude is None:
exclude = []
out = []
for file in files:
if recursive and os.path.isdir(file):
for dirpath, dnames, fnames in os.walk(file):
fpaths = [os.path.join(dirpath, fname) for fname in fnames]
for pattern in exclude:
# os.walk() supports trimming down the dnames list
# by modifying it in-place,
# to avoid unnecessary directory listings.
dnames[:] = [
x for x in dnames
if
not fnmatch.fnmatch(os.path.join(dirpath, x), pattern)
]
fpaths = [
x for x in fpaths if not fnmatch.fnmatch(x, pattern)
]
for f in fpaths:
ext = os.path.splitext(f)[1][1:]
if ext in extensions:
out.append(f)
else:
out.append(file)
return out
def make_diff(file, original, reformatted):
return list(
difflib.unified_diff(
original,
reformatted,
fromfile='{}\t(original)'.format(file),
tofile='{}\t(reformatted)'.format(file),
n=3))
class DiffError(Exception):
def __init__(self, message, errs=None):
super(DiffError, self).__init__(message)
self.errs = errs or []
class UnexpectedError(Exception):
def __init__(self, message, exc=None):
super(UnexpectedError, self).__init__(message)
self.formatted_traceback = traceback.format_exc()
self.exc = exc
def run_clang_format_diff_wrapper(args, file):
try:
ret = run_clang_format_diff(args, file)
return ret
except DiffError:
raise
except Exception as e:
raise UnexpectedError('{}: {}: {}'.format(file, e.__class__.__name__,
e), e)
def run_clang_format_diff(args, file):
try:
with io.open(file, 'r', encoding='utf-8') as f:
original = f.readlines()
except IOError as exc:
raise DiffError(str(exc))
if args.in_place:
invocation = [args.clang_format_executable, '-i', file]
else:
invocation = [args.clang_format_executable, file]
if args.dry_run:
print(" ".join(invocation))
return [], []
# Use of utf-8 to decode the process output.
#
# Hopefully, this is the correct thing to do.
#
# It's done due to the following assumptions (which may be incorrect):
# - clang-format will returns the bytes read from the files as-is,
# without conversion, and it is already assumed that the files use utf-8.
# - if the diagnostics were internationalized, they would use utf-8:
# > Adding Translations to Clang
# >
# > Not possible yet!
# > Diagnostic strings should be written in UTF-8,
# > the client can translate to the relevant code page if needed.
# > Each translation completely replaces the format string
# > for the diagnostic.
# > -- http://clang.llvm.org/docs/InternalsManual.html#internals-diag-translation
#
# It's not pretty, due to Python 2 & 3 compatibility.
encoding_py3 = {}
if sys.version_info[0] >= 3:
encoding_py3['encoding'] = 'utf-8'
try:
proc = subprocess.Popen(
invocation,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
**encoding_py3)
except OSError as exc:
raise DiffError(
"Command '{}' failed to start: {}".format(
subprocess.list2cmdline(invocation), exc
)
)
proc_stdout = proc.stdout
proc_stderr = proc.stderr
if sys.version_info[0] < 3:
# make the pipes compatible with Python 3,
# reading lines should output unicode
encoding = 'utf-8'
proc_stdout = codecs.getreader(encoding)(proc_stdout)
proc_stderr = codecs.getreader(encoding)(proc_stderr)
# hopefully the stderr pipe won't get full and block the process
outs = list(proc_stdout.readlines())
errs = list(proc_stderr.readlines())
proc.wait()
if proc.returncode:
raise DiffError(
"Command '{}' returned non-zero exit status {}".format(
subprocess.list2cmdline(invocation), proc.returncode
),
errs,
)
if args.in_place:
return [], errs
return make_diff(file, original, outs), errs
def bold_red(s):
return '\x1b[1m\x1b[31m' + s + '\x1b[0m'
def colorize(diff_lines):
def bold(s):
return '\x1b[1m' + s + '\x1b[0m'
def cyan(s):
return '\x1b[36m' + s + '\x1b[0m'
def green(s):
return '\x1b[32m' + s + '\x1b[0m'
def red(s):
return '\x1b[31m' + s + '\x1b[0m'
for line in diff_lines:
if line[:4] in ['--- ', '+++ ']:
yield bold(line)
elif line.startswith('@@ '):
yield cyan(line)
elif line.startswith('+'):
yield green(line)
elif line.startswith('-'):
yield red(line)
else:
yield line
def print_diff(diff_lines, use_color):
if use_color:
diff_lines = colorize(diff_lines)
if sys.version_info[0] < 3:
sys.stdout.writelines((l.encode('utf-8') for l in diff_lines))
else:
sys.stdout.writelines(diff_lines)
def print_trouble(prog, message, use_colors):
error_text = 'error:'
if use_colors:
error_text = bold_red(error_text)
print("{}: {} {}".format(prog, error_text, message), file=sys.stderr)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
'--clang-format-executable',
metavar='EXECUTABLE',
help='path to the clang-format executable',
default='clang-format')
parser.add_argument(
'--extensions',
help='comma separated list of file extensions (default: {})'.format(
DEFAULT_EXTENSIONS),
default=DEFAULT_EXTENSIONS)
parser.add_argument(
'-r',
'--recursive',
action='store_true',
help='run recursively over directories')
parser.add_argument(
'-d',
'--dry-run',
action='store_true',
help='just print the list of files')
parser.add_argument(
'-i',
'--in-place',
action='store_true',
help='format file instead of printing differences')
parser.add_argument('files', metavar='file', nargs='+')
parser.add_argument(
'-q',
'--quiet',
action='store_true',
help="disable output, useful for the exit code")
parser.add_argument(
'-j',
metavar='N',
type=int,
default=0,
help='run N clang-format jobs in parallel'
' (default number of cpus + 1)')
parser.add_argument(
'--color',
default='auto',
choices=['auto', 'always', 'never'],
help='show colored diff (default: auto)')
parser.add_argument(
'-e',
'--exclude',
metavar='PATTERN',
action='append',
default=[],
help='exclude paths matching the given glob-like pattern(s)'
' from recursive search')
args = parser.parse_args()
# use default signal handling, like diff return SIGINT value on ^C
# https://bugs.python.org/issue14229#msg156446
signal.signal(signal.SIGINT, signal.SIG_DFL)
try:
signal.SIGPIPE
except AttributeError:
# compatibility, SIGPIPE does not exist on Windows
pass
else:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
colored_stdout = False
colored_stderr = False
if args.color == 'always':
colored_stdout = True
colored_stderr = True
elif args.color == 'auto':
colored_stdout = sys.stdout.isatty()
colored_stderr = sys.stderr.isatty()
version_invocation = [args.clang_format_executable, str("--version")]
try:
subprocess.check_call(version_invocation, stdout=DEVNULL)
except subprocess.CalledProcessError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
return ExitStatus.TROUBLE
except OSError as e:
print_trouble(
parser.prog,
"Command '{}' failed to start: {}".format(
subprocess.list2cmdline(version_invocation), e
),
use_colors=colored_stderr,
)
return ExitStatus.TROUBLE
retcode = ExitStatus.SUCCESS
excludes = excludes_from_file(DEFAULT_CLANG_FORMAT_IGNORE)
excludes.extend(args.exclude)
files = list_files(
args.files,
recursive=args.recursive,
exclude=excludes,
extensions=args.extensions.split(','))
if not files:
return
njobs = args.j
if njobs == 0:
njobs = multiprocessing.cpu_count() + 1
njobs = min(len(files), njobs)
if njobs == 1:
# execute directly instead of in a pool,
# less overhead, simpler stacktraces
it = (run_clang_format_diff_wrapper(args, file) for file in files)
pool = None
else:
pool = multiprocessing.Pool(njobs)
it = pool.imap_unordered(
partial(run_clang_format_diff_wrapper, args), files)
pool.close()
while True:
try:
outs, errs = next(it)
except StopIteration:
break
except DiffError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
retcode = ExitStatus.TROUBLE
sys.stderr.writelines(e.errs)
except UnexpectedError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
sys.stderr.write(e.formatted_traceback)
retcode = ExitStatus.TROUBLE
# stop at the first unexpected error,
# something could be very wrong,
# don't process all files unnecessarily
if pool:
pool.terminate()
break
else:
sys.stderr.writelines(errs)
if outs == []:
continue
if not args.quiet:
print_diff(outs, use_color=colored_stdout)
if retcode == ExitStatus.SUCCESS:
retcode = ExitStatus.DIFF
if pool:
pool.join()
return retcode
if __name__ == '__main__':
sys.exit(main())
# - Try to find gclib
# Once done this will define
# gclib_FOUND - System has gclib
# gclib_INCLUDE_DIRS - The gclib include directories
# gclib_LIBRARIES - The libraries needed to use gclib
# gclib_DEFINITIONS - Compiler switches required for using gclib
FIND_PATH(gclib_INCLUDE_DIR gclib.h
HINTS /usr/include)
FIND_LIBRARY(gclib_LIBRARY NAMES gclib gclib.so.0 gclib.so gclib.so.0.422
HINTS /usr/lib )
INCLUDE(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set gclib_FOUND to TRUE
# if all listed variables are TRUE
FIND_PACKAGE_HANDLE_STANDARD_ARGS(gclib DEFAULT_MSG
gclib_LIBRARY gclib_INCLUDE_DIR)
MARK_AS_ADVANCED(gclib_INCLUDE_DIR gclib_LIBRARY )
SET(gclib_LIBRARIES ${gclib_LIBRARY} )
SET(gclib_INCLUDE_DIRS ${gclib_INCLUDE_DIR} )
# - Try to find libmpsse
# Once done this will define
# LIBPICO_FOUND - System has libmpsse
# LIBPICO_INCLUDE_DIRS - The libmpsse include directories
# LIBPICO_LIBRARIES - The libraries needed to use libmpsse
# LIBPICO_DEFINITIONS - Compiler switches required for using libmpsse
FIND_PATH(LIBPICO_INCLUDE_DIR libps6000-1.4/ps6000Api.h
HINTS /usr/include /opt/picoscope/include)
FIND_LIBRARY(LIBPICO_LIBRARY NAMES ps6000 libps6000
HINTS /usr/lib64 /opt/picoscope/lib )
INCLUDE(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBPICO_FOUND to TRUE
# if all listed variables are TRUE
FIND_PACKAGE_HANDLE_STANDARD_ARGS(libpico DEFAULT_MSG
LIBPICO_LIBRARY LIBPICO_INCLUDE_DIR)
MARK_AS_ADVANCED(LIBPICO_INCLUDE_DIR LIBPICO_LIBRARY )
SET(LIBPICO_LIBRARIES ${LIBPICO_LIBRARY} )
SET(LIBPICO_INCLUDE_DIRS ${LIBPICO_INCLUDE_DIR} )
# - Try to find libps6000
# Once done this will define
# LIBPS6000_FOUND - System has libps6000
# LIBPS6000_INCLUDE_DIRS - The libps6000 include directories
# LIBPS6000_LIBRARIES - The libraries needed to use libps6000
# LIBPS6000_DEFINITIONS - Compiler switches required for using libps6000
FIND_PATH(LIBPS6000_INCLUDE_DIR ps6000Api.h
HINTS /usr/include /opt/picoscope/include
PATH_SUFFIXES libps6000 libps6000-1.4)
FIND_LIBRARY(LIBPS6000_LIBRARY NAMES ps6000 libps6000
HINTS /usr/lib64 /opt/picoscope/lib )
INCLUDE(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBPS6000_FOUND to TRUE
# if all listed variables are TRUE
FIND_PACKAGE_HANDLE_STANDARD_ARGS(libps6000 DEFAULT_MSG
LIBPS6000_LIBRARY LIBPS6000_INCLUDE_DIR)
MARK_AS_ADVANCED(LIBPS6000_INCLUDE_DIR LIBPS6000_LIBRARY )
SET(LIBPS6000_LIBRARIES ${LIBPS6000_LIBRARY} )
SET(LIBPS6000_INCLUDE_DIRS ${LIBPS6000_INCLUDE_DIR} )
[build-system]
requires = ["setuptools", "wheel", "scikit-build", "cmake", "ninja"]
requires = ["setuptools>=42", "wheel", "scikit-build==0.13.1", "cmake", "ninja", "setuptools_scm[toml]>=3.4"]
build-backend = "setuptools.build_meta"
[tool.setuptools_scm]
write_to = "src/labRemote/version.py"
[tool.check-manifest]
ignore = [
'.clang-format',
'.clang-format-ignore',
'.gitlab-ci.yml',
'.gitmodules',
'.pypirc',
'.syntastic_cpp_config',
'CMakeLists.txt',
'CONTRIBUTING.md',
]
#!/bin/env python
#
# Script to update labRemote JSON configurations (c.f. !168).
#
# author: Daniel Joseph Antrim
# e-mail: dantrim AT lbl DOT gov
# date: February 2021
#
import sys
from argparse import ArgumentParser
import json
import jq # needed for updating the fields
from typing import Dict
from pathlib import Path
FIELDS_TO_UPDATE = ["devices", "channels", "datastreams", "datasinks"]
def update_path(path : str, json_config : Dict) -> Dict :
"""
Method to update the JSON object and/or value
located at the position within the input (potentially
arbitrarily deeply nested) JSON configuration.
If no node appears in the input JSON configuration
with the provided path, then an exception is thrown.
The update takes the JSON objects located under
the pointed-to node path by replacing them
with a list of the same objects now with a "name" field
that was once the key to the initial object.
For example, providing the path "labA.devices"
along with the following JSON config,
{
"labA" : {
"devices" : {
"PS1" : {
"foo" : 123,
"bar" : "/dev/ttyAMC0"
},
"PS2" : {
"foo" : 456,
"bar" : "/dev/ttyAMC1"
}
}
},
"labB" : {
"devices" : {
"PS3" : {
"foo" : 200,
"bar" : "/dev/ttyAMC0"
},
"PS4" : {
"foo" : 800,
"bar" : "/dev/ttyAMC1"
}
}
}
}
would produce the following updated JSON config as an output:
{
"labA" : {
"devices" : [
{
"name" : "PS1",
"foo" : 123,
"bar" : "/dev/ttyAMC0"
},
{
"name" : "PS2",
"foo" : 456,
"bar" : "/dev/ttyAMC1"
}
]
},
"labB" : {
"devices" : {
"PS3" : {
"foo" : 200,
"bar" : "/dev/ttyAMC0"
},
"PS4" : {
"foo" : 800,
"bar" : "/dev/ttyAMC1"
}
}
}
}
Notice that the "devices" node under the "labB" parent node
is unaffected since the provided path was "labA.devices".
Parameters
----------
path: str
Indicates the path to a JSON node that should be updated.
For example, a path "labA.foo.bar.devices" will update
the JSON objects appearing under ["labA"]["foo"]["bar"]["devices"]
in Python dict notiation.
json_config: Dict
The JSON configuration to be updated.
"""
keys = [x.strip() for x in path.strip().split('.')]
final_node = json_config
for key in keys :
if key not in final_node :
raise ValueError(f"ERROR: Key \"{key}\" does not appear in specified path \"{path}\" in provided JSON config")
final_node = final_node[key]
# the keys appearing in the JSON objects under the final node
# specified by 'path' are now values to "name" fields of
# JSON objects that will be grouped into a list
updated_node = []
for key, val in final_node.items() :
new_object = {}
new_object["name"] = key
for sub_key, sub_val in val.items() :
new_object[sub_key] = sub_val
updated_node.append(new_object)
# use jq to update the arbitrarily nested node
jq_path = '.' + path.strip()
compile_string = f'{jq_path} = {updated_node}'
# jq needs double-quotes in order to interpet the string fields in the JSON
compile_string = compile_string.replace("'", '"')
updated_string = jq.compile(compile_string).input(json_config).text()
updated_config = json.loads(updated_string)
return updated_config
def update_config(input_filename : str) -> None :
"""
Insepect the input JSON file and update
any of the labRemote JSON objects that
need to be.
The updates are not made in place, rather a
new file is created with a suffix indicating
that it is updated.
Parameters
----------
input_filename: str
The input file to open and parse
Returns
----------
None
"""
global FIELDS_TO_UPDATE
with open(input_filename, "r") as infile :
try :
input_config = json.load(infile)
except json.JSONDecodeError as e:
print(f"ERROR: failed to parse JSON from input: {e}")
sys.exit(1)
def find_path_to_field(element_to_look_for, node, path = '', paths = []) :
"""
Find the paths to any child nodes whose name is "element_to_look_for".
"""
if element_to_look_for in node :
path = path + element_to_look_for #+ ' = ' + node[element_to_look_for] #.encode("utf-8")
paths.append(path)
for key, val in node.items() :
if isinstance(val, dict) :
find_path_to_field(element_to_look_for, val, path + key + ".", paths)
# update those fields indicated in the global list
# (can handle cases where a JSON config has multiple
# paths leading to nodes whose names appear in the
# global list)
for field_to_update in FIELDS_TO_UPDATE :
found_paths = []
find_path_to_field(field_to_update, input_config, '', found_paths)
for found_path in found_paths :
input_config = update_path(found_path, input_config)
# create the output filename based on adding a suffix
# to the input file's name
input_path = Path(input_filename)
output_filename = input_path.stem + "_updated.json"
# here we don't worry about overwriting, since applying
# this script multiple times would in any case lead to
# issues (e.g. running it over an already-updated config
# is not recommended and may case issues)
print(f"Storing updated configuration: {Path(output_filename).absolute()}")
with open(output_filename, "w", encoding = "utf-8") as ofile :
json.dump(input_config, ofile, ensure_ascii = False, indent = 4)
def main() :
parser = ArgumentParser(description = "Update a labRemote JSON configuration file!")
parser.add_argument("input", type = str,
help = "The JSON file to inspect and update"
)
args = parser.parse_args()
path = Path(args.input)
if not path.is_file() or not path.exists() :
print(f"ERROR: bad input \"{args.input}\"")
sys.exit(1)
# update the config and save the updated one as a new file
update_config(args.input)
if __name__ == "__main__" :
main()
[metadata]
name = labRemote
description = A library to control and read out lab equipment
long_description = file: README.md
long_description_content_type = text/markdown
url = https://gitlab.cern.ch/berkeleylab/labRemote/
author = Giordon Stark
author_email = gstark@cern.ch
license = GPLv3
license_file = LICENSE
keywords = physics slow-control communication serial
project_urls =
Documentation = https://gitlab.cern.ch/berkeleylab/labRemote
Source = https://gitlab.cern.ch/berkeleylab/labRemote/
Tracker = https://gitlab.cern.ch/berkeleylab/labRemote/-/issues
classifiers =
Development Status :: 4 - Beta
Intended Audience :: Science/Research
Topic :: Scientific/Engineering
Topic :: Scientific/Engineering :: Physics
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: Implementation :: CPython
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
[options]
python_requires = >=3.6