WiFi Channel Settings.

This commit is contained in:
Robin Davies
2021-08-20 10:35:37 -04:00
parent 140f2abd39
commit f6aa331d77
40 changed files with 2409 additions and 597 deletions
-547
View File
@@ -1,547 +0,0 @@
# This is the CMakeCache file.
# For build in directory: /home/patch/src/piddle
# It was generated by CMake: /usr/local/bin/cmake
# You can edit this file to change values found and used by cmake.
# If you do not want to change any of the values, simply exit the editor.
# If you do want to change a value, simply edit, save, and exit the editor.
# The syntax for the file is as follows:
# KEY:TYPE=VALUE
# KEY is the name of a variable in the cache.
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
# VALUE is the current value for the KEY.
########################
# EXTERNAL cache entries
########################
//Build the testing tree.
BUILD_TESTING:BOOL=ON
//Path to a program.
BZRCOMMAND:FILEPATH=BZRCOMMAND-NOTFOUND
//Path to a program.
CMAKE_ADDR2LINE:FILEPATH=/usr/bin/addr2line
//Path to a program.
CMAKE_AR:FILEPATH=/usr/bin/ar
//Choose the type of build, options are: None Debug Release RelWithDebInfo
// MinSizeRel ...
CMAKE_BUILD_TYPE:STRING=
//Enable/Disable color output during build.
CMAKE_COLOR_MAKEFILE:BOOL=ON
//CXX compiler
CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++
//A wrapper around 'ar' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_CXX_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar-8
//A wrapper around 'ranlib' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_CXX_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib-8
//Flags used by the CXX compiler during all build types.
CMAKE_CXX_FLAGS:STRING=
//Flags used by the CXX compiler during DEBUG builds.
CMAKE_CXX_FLAGS_DEBUG:STRING=-g
//Flags used by the CXX compiler during MINSIZEREL builds.
CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
//Flags used by the CXX compiler during RELEASE builds.
CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
//Flags used by the CXX compiler during RELWITHDEBINFO builds.
CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
//C compiler
CMAKE_C_COMPILER:FILEPATH=/usr/bin/cc
//A wrapper around 'ar' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_C_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar-8
//A wrapper around 'ranlib' adding the appropriate '--plugin' option
// for the GCC compiler
CMAKE_C_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib-8
//Flags used by the C compiler during all build types.
CMAKE_C_FLAGS:STRING=
//Flags used by the C compiler during DEBUG builds.
CMAKE_C_FLAGS_DEBUG:STRING=-g
//Flags used by the C compiler during MINSIZEREL builds.
CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
//Flags used by the C compiler during RELEASE builds.
CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
//Flags used by the C compiler during RELWITHDEBINFO builds.
CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
//Path to a program.
CMAKE_DLLTOOL:FILEPATH=CMAKE_DLLTOOL-NOTFOUND
//Flags used by the linker during all build types.
CMAKE_EXE_LINKER_FLAGS:STRING=
//Flags used by the linker during DEBUG builds.
CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during MINSIZEREL builds.
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during RELEASE builds.
CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during RELWITHDEBINFO builds.
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Enable/Disable output of compile commands during generation.
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=
//Install path prefix, prepended onto install directories.
CMAKE_INSTALL_PREFIX:PATH=/usr/local
//Path to a program.
CMAKE_LINKER:FILEPATH=/usr/bin/ld
//Path to a program.
CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/make
//Flags used by the linker during the creation of modules during
// all build types.
CMAKE_MODULE_LINKER_FLAGS:STRING=
//Flags used by the linker during the creation of modules during
// DEBUG builds.
CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during the creation of modules during
// MINSIZEREL builds.
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during the creation of modules during
// RELEASE builds.
CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during the creation of modules during
// RELWITHDEBINFO builds.
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Path to a program.
CMAKE_NM:FILEPATH=/usr/bin/nm
//Path to a program.
CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy
//Path to a program.
CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump
//Value Computed by CMake
CMAKE_PROJECT_DESCRIPTION:STATIC=
//Value Computed by CMake
CMAKE_PROJECT_HOMEPAGE_URL:STATIC=
//Value Computed by CMake
CMAKE_PROJECT_NAME:STATIC=PiddleTest
//Value Computed by CMake
CMAKE_PROJECT_VERSION:STATIC=0.1.0
//Value Computed by CMake
CMAKE_PROJECT_VERSION_MAJOR:STATIC=0
//Value Computed by CMake
CMAKE_PROJECT_VERSION_MINOR:STATIC=1
//Value Computed by CMake
CMAKE_PROJECT_VERSION_PATCH:STATIC=0
//Value Computed by CMake
CMAKE_PROJECT_VERSION_TWEAK:STATIC=
//Path to a program.
CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib
//Path to a program.
CMAKE_READELF:FILEPATH=/usr/bin/readelf
//Flags used by the linker during the creation of shared libraries
// during all build types.
CMAKE_SHARED_LINKER_FLAGS:STRING=
//Flags used by the linker during the creation of shared libraries
// during DEBUG builds.
CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during the creation of shared libraries
// during MINSIZEREL builds.
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during the creation of shared libraries
// during RELEASE builds.
CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during the creation of shared libraries
// during RELWITHDEBINFO builds.
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//If set, runtime paths are not added when installing shared libraries,
// but are added when building.
CMAKE_SKIP_INSTALL_RPATH:BOOL=NO
//If set, runtime paths are not added when using shared libraries.
CMAKE_SKIP_RPATH:BOOL=NO
//Flags used by the linker during the creation of static libraries
// during all build types.
CMAKE_STATIC_LINKER_FLAGS:STRING=
//Flags used by the linker during the creation of static libraries
// during DEBUG builds.
CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during the creation of static libraries
// during MINSIZEREL builds.
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during the creation of static libraries
// during RELEASE builds.
CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during the creation of static libraries
// during RELWITHDEBINFO builds.
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Path to a program.
CMAKE_STRIP:FILEPATH=/usr/bin/strip
//If this value is on, makefiles will be generated without the
// .SILENT directive, and all commands will be echoed to the console
// during the make. This is useful for debugging only. With Visual
// Studio IDE projects all commands are done without /nologo.
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
//Path to the coverage program that CTest uses for performing coverage
// inspection
COVERAGE_COMMAND:FILEPATH=/usr/bin/gcov
//Extra command line flags to pass to the coverage tool
COVERAGE_EXTRA_FLAGS:STRING=-l
//How many times to retry timed-out CTest submissions.
CTEST_SUBMIT_RETRY_COUNT:STRING=3
//How long to wait between timed-out CTest submissions.
CTEST_SUBMIT_RETRY_DELAY:STRING=5
//Path to a program.
CVSCOMMAND:FILEPATH=/usr/bin/cvs
//Options passed to the cvs update command.
CVS_UPDATE_OPTIONS:STRING=-d -A -P
//Maximum time allowed before CTest will kill the test.
DART_TESTING_TIMEOUT:STRING=1500
//Path to a program.
GITCOMMAND:FILEPATH=/usr/bin/git
//Path to a program.
HGCOMMAND:FILEPATH=/usr/bin/hg
//Command to build the project
MAKECOMMAND:STRING=/usr/local/bin/cmake --build . --config "${CTEST_CONFIGURATION_TYPE}"
//Path to the memory checking command, used for memory error detection.
MEMORYCHECK_COMMAND:FILEPATH=MEMORYCHECK_COMMAND-NOTFOUND
//File that contains suppressions for the memory checker
MEMORYCHECK_SUPPRESSIONS_FILE:FILEPATH=
//Path to a program.
P4COMMAND:FILEPATH=P4COMMAND-NOTFOUND
//pkg-config executable
PKG_CONFIG_EXECUTABLE:FILEPATH=/usr/bin/pkg-config
//Value Computed by CMake
PiddleTest_BINARY_DIR:STATIC=/home/patch/src/piddle
//Value Computed by CMake
PiddleTest_SOURCE_DIR:STATIC=/home/patch/src/piddle/test
//Name of the computer/site where compile is being run
SITE:STRING=patchbox
//Path to the SLURM sbatch executable
SLURM_SBATCH_COMMAND:FILEPATH=SLURM_SBATCH_COMMAND-NOTFOUND
//Path to the SLURM srun executable
SLURM_SRUN_COMMAND:FILEPATH=SLURM_SRUN_COMMAND-NOTFOUND
//Path to a program.
SVNCOMMAND:FILEPATH=/usr/bin/svn
//Path to a library.
pkgcfg_lib_LILV_0_dl:FILEPATH=/usr/lib/arm-linux-gnueabihf/libdl.so
//Path to a library.
pkgcfg_lib_LILV_0_lilv-0:FILEPATH=/usr/lib/arm-linux-gnueabihf/liblilv-0.so
//Path to a library.
pkgcfg_lib_LILV_0_serd-0:FILEPATH=/usr/lib/arm-linux-gnueabihf/libserd-0.so
//Path to a library.
pkgcfg_lib_LILV_0_sord-0:FILEPATH=/usr/lib/arm-linux-gnueabihf/libsord-0.so
//Path to a library.
pkgcfg_lib_LILV_0_sratom-0:FILEPATH=/usr/lib/arm-linux-gnueabihf/libsratom-0.so
########################
# INTERNAL cache entries
########################
//ADVANCED property for variable: BZRCOMMAND
BZRCOMMAND-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_ADDR2LINE
CMAKE_ADDR2LINE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_AR
CMAKE_AR-ADVANCED:INTERNAL=1
//This is the directory where this CMakeCache.txt was created
CMAKE_CACHEFILE_DIR:INTERNAL=/home/patch/src/piddle
//Major version of cmake used to create the current loaded cache
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
//Minor version of cmake used to create the current loaded cache
CMAKE_CACHE_MINOR_VERSION:INTERNAL=19
//Patch version of cmake used to create the current loaded cache
CMAKE_CACHE_PATCH_VERSION:INTERNAL=4268486
//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE
CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1
//Path to CMake executable.
CMAKE_COMMAND:INTERNAL=/usr/local/bin/cmake
//Path to cpack program executable.
CMAKE_CPACK_COMMAND:INTERNAL=/usr/local/bin/cpack
//ADVANCED property for variable: CMAKE_CTEST_COMMAND
CMAKE_CTEST_COMMAND-ADVANCED:INTERNAL=1
//Path to ctest program executable.
CMAKE_CTEST_COMMAND:INTERNAL=/usr/local/bin/ctest
//ADVANCED property for variable: CMAKE_CXX_COMPILER
CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_COMPILER_AR
CMAKE_CXX_COMPILER_AR-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_COMPILER_RANLIB
CMAKE_CXX_COMPILER_RANLIB-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS
CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_COMPILER
CMAKE_C_COMPILER-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_COMPILER_AR
CMAKE_C_COMPILER_AR-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_COMPILER_RANLIB
CMAKE_C_COMPILER_RANLIB-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS
CMAKE_C_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_DLLTOOL
CMAKE_DLLTOOL-ADVANCED:INTERNAL=1
//Executable file format
CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS
CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG
CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE
CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS
CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1
//Name of external makefile project generator.
CMAKE_EXTRA_GENERATOR:INTERNAL=
//Name of generator.
CMAKE_GENERATOR:INTERNAL=Unix Makefiles
//Generator instance identifier.
CMAKE_GENERATOR_INSTANCE:INTERNAL=
//Name of generator platform.
CMAKE_GENERATOR_PLATFORM:INTERNAL=
//Name of generator toolset.
CMAKE_GENERATOR_TOOLSET:INTERNAL=
//Source directory with the top level CMakeLists.txt file for this
// project
CMAKE_HOME_DIRECTORY:INTERNAL=/home/patch/src/piddle/test
//Install .so files without execute permission.
CMAKE_INSTALL_SO_NO_EXE:INTERNAL=1
//ADVANCED property for variable: CMAKE_LINKER
CMAKE_LINKER-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MAKE_PROGRAM
CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS
CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG
CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE
CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_NM
CMAKE_NM-ADVANCED:INTERNAL=1
//number of local generators
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
//ADVANCED property for variable: CMAKE_OBJCOPY
CMAKE_OBJCOPY-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_OBJDUMP
CMAKE_OBJDUMP-ADVANCED:INTERNAL=1
//Platform information initialized
CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
//ADVANCED property for variable: CMAKE_RANLIB
CMAKE_RANLIB-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_READELF
CMAKE_READELF-ADVANCED:INTERNAL=1
//Path to CMake installation.
CMAKE_ROOT:INTERNAL=/usr/local/share/cmake-3.19
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS
CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG
CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE
CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH
CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SKIP_RPATH
CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS
CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG
CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE
CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STRIP
CMAKE_STRIP-ADVANCED:INTERNAL=1
//uname command
CMAKE_UNAME:INTERNAL=/usr/bin/uname
//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE
CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: COVERAGE_COMMAND
COVERAGE_COMMAND-ADVANCED:INTERNAL=1
//ADVANCED property for variable: COVERAGE_EXTRA_FLAGS
COVERAGE_EXTRA_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CTEST_SUBMIT_RETRY_COUNT
CTEST_SUBMIT_RETRY_COUNT-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CTEST_SUBMIT_RETRY_DELAY
CTEST_SUBMIT_RETRY_DELAY-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CVSCOMMAND
CVSCOMMAND-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CVS_UPDATE_OPTIONS
CVS_UPDATE_OPTIONS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: DART_TESTING_TIMEOUT
DART_TESTING_TIMEOUT-ADVANCED:INTERNAL=1
//Details about finding PkgConfig
FIND_PACKAGE_MESSAGE_DETAILS_PkgConfig:INTERNAL=[/usr/bin/pkg-config][v0.29()]
//ADVANCED property for variable: GITCOMMAND
GITCOMMAND-ADVANCED:INTERNAL=1
//ADVANCED property for variable: HGCOMMAND
HGCOMMAND-ADVANCED:INTERNAL=1
LILV_0_CFLAGS:INTERNAL=-I/usr/local/include;-I/usr/include/lilv-0;-I/usr/include/sratom-0;-I/usr/include/sord-0;-I/usr/include/serd-0
LILV_0_CFLAGS_I:INTERNAL=
LILV_0_CFLAGS_OTHER:INTERNAL=
LILV_0_FOUND:INTERNAL=1
LILV_0_INCLUDEDIR:INTERNAL=/usr/include
LILV_0_INCLUDE_DIRS:INTERNAL=/usr/local/include;/usr/include/lilv-0;/usr/include/sratom-0;/usr/include/sord-0;/usr/include/serd-0
LILV_0_LDFLAGS:INTERNAL=-llilv-0;-ldl;-lsratom-0;-lsord-0;-lserd-0
LILV_0_LDFLAGS_OTHER:INTERNAL=
LILV_0_LIBDIR:INTERNAL=/usr/lib/arm-linux-gnueabihf
LILV_0_LIBRARIES:INTERNAL=lilv-0;dl;sratom-0;sord-0;serd-0
LILV_0_LIBRARY_DIRS:INTERNAL=
LILV_0_LIBS:INTERNAL=
LILV_0_LIBS_L:INTERNAL=
LILV_0_LIBS_OTHER:INTERNAL=
LILV_0_LIBS_PATHS:INTERNAL=
LILV_0_MODULE_NAME:INTERNAL=lilv-0
LILV_0_PREFIX:INTERNAL=/usr
LILV_0_STATIC_CFLAGS:INTERNAL=-I/usr/local/include;-I/usr/include/lilv-0;-I/usr/include/sratom-0;-I/usr/include/sord-0;-I/usr/include/serd-0
LILV_0_STATIC_CFLAGS_I:INTERNAL=
LILV_0_STATIC_CFLAGS_OTHER:INTERNAL=
LILV_0_STATIC_INCLUDE_DIRS:INTERNAL=/usr/local/include;/usr/include/lilv-0;/usr/include/sratom-0;/usr/include/sord-0;/usr/include/serd-0
LILV_0_STATIC_LDFLAGS:INTERNAL=-llilv-0;-ldl;-lsratom-0;-lsord-0;-lserd-0;-lm
LILV_0_STATIC_LDFLAGS_OTHER:INTERNAL=
LILV_0_STATIC_LIBDIR:INTERNAL=
LILV_0_STATIC_LIBRARIES:INTERNAL=lilv-0;dl;sratom-0;sord-0;serd-0;m
LILV_0_STATIC_LIBRARY_DIRS:INTERNAL=
LILV_0_STATIC_LIBS:INTERNAL=
LILV_0_STATIC_LIBS_L:INTERNAL=
LILV_0_STATIC_LIBS_OTHER:INTERNAL=
LILV_0_STATIC_LIBS_PATHS:INTERNAL=
LILV_0_VERSION:INTERNAL=0.24.2
LILV_0_lilv-0_INCLUDEDIR:INTERNAL=
LILV_0_lilv-0_LIBDIR:INTERNAL=
LILV_0_lilv-0_PREFIX:INTERNAL=
LILV_0_lilv-0_VERSION:INTERNAL=
//ADVANCED property for variable: MAKECOMMAND
MAKECOMMAND-ADVANCED:INTERNAL=1
//ADVANCED property for variable: MEMORYCHECK_COMMAND
MEMORYCHECK_COMMAND-ADVANCED:INTERNAL=1
//ADVANCED property for variable: MEMORYCHECK_SUPPRESSIONS_FILE
MEMORYCHECK_SUPPRESSIONS_FILE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: P4COMMAND
P4COMMAND-ADVANCED:INTERNAL=1
//ADVANCED property for variable: PKG_CONFIG_EXECUTABLE
PKG_CONFIG_EXECUTABLE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: SITE
SITE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: SLURM_SBATCH_COMMAND
SLURM_SBATCH_COMMAND-ADVANCED:INTERNAL=1
//ADVANCED property for variable: SLURM_SRUN_COMMAND
SLURM_SRUN_COMMAND-ADVANCED:INTERNAL=1
//ADVANCED property for variable: SVNCOMMAND
SVNCOMMAND-ADVANCED:INTERNAL=1
__pkg_config_arguments_LILV_0:INTERNAL=lilv-0
__pkg_config_checked_LILV_0:INTERNAL=1
//ADVANCED property for variable: pkgcfg_lib_LILV_0_dl
pkgcfg_lib_LILV_0_dl-ADVANCED:INTERNAL=1
//ADVANCED property for variable: pkgcfg_lib_LILV_0_lilv-0
pkgcfg_lib_LILV_0_lilv-0-ADVANCED:INTERNAL=1
//ADVANCED property for variable: pkgcfg_lib_LILV_0_serd-0
pkgcfg_lib_LILV_0_serd-0-ADVANCED:INTERNAL=1
//ADVANCED property for variable: pkgcfg_lib_LILV_0_sord-0
pkgcfg_lib_LILV_0_sord-0-ADVANCED:INTERNAL=1
//ADVANCED property for variable: pkgcfg_lib_LILV_0_sratom-0
pkgcfg_lib_LILV_0_sratom-0-ADVANCED:INTERNAL=1
prefix_result:INTERNAL=/usr/lib/arm-linux-gnueabihf
+10 -2
View File
@@ -5,20 +5,22 @@ find_program(NPM_COMMAND npm)
if (CMAKE_BUILD_TYPE MATCHES Debug)
set(BUILD_REACT "echo")
set(BUILD_REACT_ARGS Skipping react debug build)
else()
set(BUILD_REACT ${NPM_COMMAND})
set(BUILD_REACT_ARGS run build)
endif()
set (BUILD_DIRECTORY ${PROJECT_SOURCE_DIR}/build/react/build)
message(STATUS "React Build...")
add_custom_command(
OUTPUT ${BUILD_DIRECTORY}/index.html
COMMAND ${BUILD_REACT}
ARGS run build
ARGS ${BUILD_REACT_ARGS}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/react
DEPENDS
src/WifiChannel.tsx
src/JackServerSettingsDialog.tsx
src/JackServerSettings.tsx
@@ -74,7 +76,13 @@ add_custom_command(
src/Utility.tsx
src/VuMeter.tsx
src/PluginPreset.tsx
src/XxxSnippet.tsx
src/WifiConfigSettings.tsx
src/WifiConfigDialog.tsx
src/NoChangePassword.tsx
src/XxxSnippet.tsx
public/iso_codes.json
public/logo512.png
public/index.html
public/manifest.json
+1 -1
View File
@@ -20,7 +20,7 @@
},
"scripts": {
"start": "react-scripts start",
"build": "BUILD_PATH='../../build/react/src/build' react-scripts build",
"build": "BUILD_PATH='../build/react/build' react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
+248
View File
@@ -0,0 +1,248 @@
{
"AF": "Afghanistan",
"AX": "Aland Islands",
"AL": "Albania",
"DZ": "Algeria",
"AS": "American Samoa",
"AD": "Andorra",
"AO": "Angola",
"AI": "Anguilla",
"AQ": "Antarctica",
"AG": "Antigua And Barbuda",
"AR": "Argentina",
"AM": "Armenia",
"AW": "Aruba",
"AU": "Australia",
"AT": "Austria",
"AZ": "Azerbaijan",
"BS": "Bahamas",
"BH": "Bahrain",
"BD": "Bangladesh",
"BB": "Barbados",
"BY": "Belarus",
"BE": "Belgium",
"BZ": "Belize",
"BJ": "Benin",
"BM": "Bermuda",
"BT": "Bhutan",
"BO": "Bolivia",
"BA": "Bosnia And Herzegovina",
"BW": "Botswana",
"BV": "Bouvet Island",
"BR": "Brazil",
"IO": "British Indian Ocean Territory",
"BN": "Brunei Darussalam",
"BG": "Bulgaria",
"BF": "Burkina Faso",
"BI": "Burundi",
"KH": "Cambodia",
"CM": "Cameroon",
"CA": "Canada",
"CV": "Cape Verde",
"KY": "Cayman Islands",
"CF": "Central African Republic",
"TD": "Chad",
"CL": "Chile",
"CN": "China",
"CX": "Christmas Island",
"CC": "Cocos (Keeling) Islands",
"CO": "Colombia",
"KM": "Comoros",
"CG": "Congo",
"CD": "Congo, Democratic Republic",
"CK": "Cook Islands",
"CR": "Costa Rica",
"CI": "Cote D\"Ivoire",
"HR": "Croatia",
"CU": "Cuba",
"CY": "Cyprus",
"CZ": "Czech Republic",
"DK": "Denmark",
"DJ": "Djibouti",
"DM": "Dominica",
"DO": "Dominican Republic",
"EC": "Ecuador",
"EG": "Egypt",
"SV": "El Salvador",
"GQ": "Equatorial Guinea",
"ER": "Eritrea",
"EE": "Estonia",
"ET": "Ethiopia",
"FK": "Falkland Islands (Malvinas)",
"FO": "Faroe Islands",
"FJ": "Fiji",
"FI": "Finland",
"FR": "France",
"GF": "French Guiana",
"PF": "French Polynesia",
"TF": "French Southern Territories",
"GA": "Gabon",
"GM": "Gambia",
"GE": "Georgia",
"DE": "Germany",
"GH": "Ghana",
"GI": "Gibraltar",
"GR": "Greece",
"GL": "Greenland",
"GD": "Grenada",
"GP": "Guadeloupe",
"GU": "Guam",
"GT": "Guatemala",
"GG": "Guernsey",
"GN": "Guinea",
"GW": "Guinea-Bissau",
"GY": "Guyana",
"HT": "Haiti",
"HM": "Heard Island & Mcdonald Islands",
"VA": "Holy See (Vatican City State)",
"HN": "Honduras",
"HK": "Hong Kong",
"HU": "Hungary",
"IS": "Iceland",
"IN": "India",
"ID": "Indonesia",
"IR": "Iran, Islamic Republic Of",
"IQ": "Iraq",
"IE": "Ireland",
"IM": "Isle Of Man",
"IL": "Israel",
"IT": "Italy",
"JM": "Jamaica",
"JP": "Japan",
"JE": "Jersey",
"JO": "Jordan",
"KZ": "Kazakhstan",
"KE": "Kenya",
"KI": "Kiribati",
"KR": "Korea",
"KP": "North Korea",
"KW": "Kuwait",
"KG": "Kyrgyzstan",
"LA": "Lao People\"s Democratic Republic",
"LV": "Latvia",
"LB": "Lebanon",
"LS": "Lesotho",
"LR": "Liberia",
"LY": "Libyan Arab Jamahiriya",
"LI": "Liechtenstein",
"LT": "Lithuania",
"LU": "Luxembourg",
"MO": "Macao",
"MK": "Macedonia",
"MG": "Madagascar",
"MW": "Malawi",
"MY": "Malaysia",
"MV": "Maldives",
"ML": "Mali",
"MT": "Malta",
"MH": "Marshall Islands",
"MQ": "Martinique",
"MR": "Mauritania",
"MU": "Mauritius",
"YT": "Mayotte",
"MX": "Mexico",
"FM": "Micronesia, Federated States Of",
"MD": "Moldova",
"MC": "Monaco",
"MN": "Mongolia",
"ME": "Montenegro",
"MS": "Montserrat",
"MA": "Morocco",
"MZ": "Mozambique",
"MM": "Myanmar",
"NA": "Namibia",
"NR": "Nauru",
"NP": "Nepal",
"NL": "Netherlands",
"AN": "Netherlands Antilles",
"NC": "New Caledonia",
"NZ": "New Zealand",
"NI": "Nicaragua",
"NE": "Niger",
"NG": "Nigeria",
"NU": "Niue",
"NF": "Norfolk Island",
"MP": "Northern Mariana Islands",
"NO": "Norway",
"OM": "Oman",
"PK": "Pakistan",
"PW": "Palau",
"PS": "Palestinian Territory, Occupied",
"PA": "Panama",
"PG": "Papua New Guinea",
"PY": "Paraguay",
"PE": "Peru",
"PH": "Philippines",
"PN": "Pitcairn",
"PL": "Poland",
"PT": "Portugal",
"PR": "Puerto Rico",
"QA": "Qatar",
"RE": "Reunion",
"RO": "Romania",
"RU": "Russian Federation",
"RW": "Rwanda",
"BL": "Saint Barthelemy",
"SH": "Saint Helena",
"KN": "Saint Kitts And Nevis",
"LC": "Saint Lucia",
"MF": "Saint Martin",
"PM": "Saint Pierre And Miquelon",
"VC": "Saint Vincent And Grenadines",
"WS": "Samoa",
"SM": "San Marino",
"ST": "Sao Tome And Principe",
"SA": "Saudi Arabia",
"SN": "Senegal",
"RS": "Serbia",
"SC": "Seychelles",
"SL": "Sierra Leone",
"SG": "Singapore",
"SK": "Slovakia",
"SI": "Slovenia",
"SB": "Solomon Islands",
"SO": "Somalia",
"ZA": "South Africa",
"GS": "South Georgia And Sandwich Isl.",
"ES": "Spain",
"LK": "Sri Lanka",
"SD": "Sudan",
"SR": "Suriname",
"SJ": "Svalbard And Jan Mayen",
"SZ": "Swaziland",
"SE": "Sweden",
"CH": "Switzerland",
"SY": "Syrian Arab Republic",
"TW": "Taiwan",
"TJ": "Tajikistan",
"TZ": "Tanzania",
"TH": "Thailand",
"TL": "Timor-Leste",
"TG": "Togo",
"TK": "Tokelau",
"TO": "Tonga",
"TT": "Trinidad And Tobago",
"TN": "Tunisia",
"TR": "Turkey",
"TM": "Turkmenistan",
"TC": "Turks And Caicos Islands",
"TV": "Tuvalu",
"UG": "Uganda",
"UA": "Ukraine",
"AE": "United Arab Emirates",
"GB": "United Kingdom",
"US": "United States",
"UM": "United States Outlying Islands",
"UY": "Uruguay",
"UZ": "Uzbekistan",
"VU": "Vanuatu",
"VE": "Venezuela",
"VN": "Vietnam",
"VG": "Virgin Islands, British",
"VI": "Virgin Islands, U.S.",
"WF": "Wallis And Futuna",
"EH": "Western Sahara",
"YE": "Yemen",
"ZM": "Zambia",
"ZW": "Zimbabwe"
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"socket_server_port": 80,
"socket_server_port": 8080,
"socket_server_address": "*",
"max_upload_size": 1048576,
+1 -1
View File
@@ -646,7 +646,7 @@ const App = withStyles(appStyles)(class extends ResizeResponsiveComponent<AppPro
)}
</div>
)}
<TemporaryDrawer position='left' title="piddle"
<TemporaryDrawer position='left' title="PiPedal"
is_open={this.state.isDrawerOpen} onClose={() => { this.hideDrawer(); }} >
<List subheader={
<ListSubheader component="div" id="nested-list-subheader">Banks</ListSubheader>
+119
View File
@@ -0,0 +1,119 @@
import React from 'react';
import Dialog, {DialogProps} from '@material-ui/core/Dialog';
interface DialogExProps extends DialogProps {
tag: string;
}
interface DialogExState {
}
class DialogEx extends React.Component<DialogExProps,DialogExState> {
constructor(props: DialogExProps)
{
super(props);
this.state = {
};
this.handlePopState = this.handlePopState.bind(this);
}
mounted: boolean = false;
hasHooks: boolean = false;
stateWasPopped: boolean = false;
handlePopState(e: any): any {
this.stateWasPopped = true;
let shouldClose = (!e.state || !e.state[this.props.tag]);
if (shouldClose)
{
if (this.props.onClose)
{
this.props.onClose(e,"backdropClick");
}
}
}
updateHooks() : void {
let wantHooks = this.mounted && this.props.open;
if (wantHooks !== this.hasHooks)
{
this.hasHooks = wantHooks;
if (this.hasHooks)
{
this.stateWasPopped = false;
window.addEventListener("popstate",this.handlePopState);
// eslint-disable-next-line no-restricted-globals
let state = history.state;
if (!state)
{
state = {};
}
state[this.props.tag] = true;
// eslint-disable-next-line no-restricted-globals
history.pushState(
state,
"",
"#" + this.props.tag
);
} else {
window.removeEventListener("popstate",this.handlePopState);
if (!this.stateWasPopped)
{
// eslint-disable-next-line no-restricted-globals
history.back();
}
}
}
}
onWindowSizeChanged(width: number, height: number): void
{
this.setState({fullScreen: height < 200})
}
componentDidMount()
{
if (super.componentDidMount)
{
super.componentDidMount();
}
this.mounted = true;
this.updateHooks();
}
componentWillUnmount()
{
if (super.componentWillUnmount)
{
super.componentWillUnmount();
}
this.mounted = false;
this.updateHooks();
}
componentDidUpdate()
{
this.updateHooks();
}
render() {
let { tag, ...extra} = this.props;
return (
<Dialog {...extra}>
{this.props.children}
</Dialog>
);
}
};
export default DialogEx;
+1 -1
View File
@@ -187,7 +187,7 @@ export const MidiBindingDialog =
this.updateHooks();
}
componentWillUnmount() {
super.componentDidMount();
super.componentWillUnmount();
this.mounted = false;
this.updateHooks();
+118
View File
@@ -0,0 +1,118 @@
import React from 'react';
import InputAdornment from '@material-ui/core/InputAdornment';
import IconButton from '@material-ui/core/IconButton';
import FormControl from '@material-ui/core/FormControl';
import FormHelperText from '@material-ui/core/FormHelperText';
import InputLabel from '@material-ui/core/InputLabel';
import Input from '@material-ui/core/Input';
import Visibility from '@material-ui/icons/Visibility';
import VisibilityOff from '@material-ui/icons/VisibilityOff';
interface NoChangePasswordProps {
hasPassword: boolean;
label: string;
defaultValue: string;
onPasswordChange: (text: string) => void;
disabled?: boolean;
helperText?: string;
error?: boolean;
};
interface NoChangePasswordState {
focused: boolean;
passwordText: string;
showPassword: boolean;
};
class NoChangePassword extends React.Component<NoChangePasswordProps, NoChangePasswordState> {
refText: React.Ref<HTMLInputElement | HTMLTextAreaElement>;
constructor(props: NoChangePasswordProps) {
super(props);
this.refText = React.createRef();
this.state = {
focused: false,
showPassword: false,
passwordText: this.props.defaultValue as string
};
}
handleShowPassword() {
this.setState({
showPassword: !this.state.showPassword
});
}
handleChange(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
this.textChanged = true;
this.props.onPasswordChange(e.target.value);
}
textChanged: boolean = false;
handleFocus(e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) {
console.log("onFocus");
if (!this.state.focused) {
this.textChanged = false;
e.target.value = this.state.passwordText;
}
e.currentTarget.removeAttribute("readonly");
this.setState({ focused: true });
}
handleBlur(e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) {
console.log("onBlur");
let text = e.target.value as string;
this.setState({ focused: false, passwordText: text });
this.props.onPasswordChange(e.target.value);
this.textChanged = false;
if (text.length === 0 && this.props.hasPassword)
{
e.target.value = "(Unchanged)";
}
}
render() {
console.log("Render focus: " + this.state.focused)
let showUnchanged = (this.state.passwordText.length === 0) && this.props.hasPassword && (!this.state.focused);
let thisDefaultValue = showUnchanged ? "(Unchanged)" : this.props.defaultValue;
let isText = showUnchanged || this.state.showPassword;
return (
<FormControl disabled={this.props.disabled} fullWidth error={this.props.error} >
<InputLabel htmlFor="standard-adornment-password">{this.props.label}</InputLabel>
<Input
fullWidth
color="primary"
spellCheck="false"
autoComplete="off"
readOnly
id="standard-adornment-password"
onChange={(e) => this.handleChange(e)}
value={thisDefaultValue}
onFocus={(e) => this.handleFocus(e)}
onBlur={(e) => this.handleBlur(e)}
type={isText ? 'text' : 'password'}
style={ { color: showUnchanged? '#888': '#000'} }
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={(e) => this.handleShowPassword()}
onMouseDown={(e) => e.preventDefault()}
>
{this.state.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
/>
<FormHelperText >{ this.props.helperText}</FormHelperText>
</FormControl>
);
}
};
export default NoChangePassword;
+1 -1
View File
@@ -117,7 +117,7 @@ const pedalBoardStyles = (theme: Theme) => createStyles({
marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2,
width: FRAME_SIZE,
height: FRAME_SIZE,
border: "1pt black solid",
border: "1pt #666 solid",
borderRadius: 6
},
pedalIcon: {
+107 -5
View File
@@ -13,6 +13,8 @@ import JackHostStatus from './JackHostStatus';
import JackServerSettings from './JackServerSettings';
import MidiBinding from './MidiBinding';
import PluginPreset from './PluginPreset';
import WifiConfigSettings from './WifiConfigSettings';
import WifiChannel from './WifiChannel';
export enum State {
@@ -235,6 +237,7 @@ interface ControlChangedBody {
export interface PiPedalModel {
clientId: number;
countryCodes: Object;
serverVersion?: PiPedalVersion;
errorMessage: ObservableProperty<string>;
alertMessage: ObservableProperty<string>;
@@ -245,6 +248,7 @@ export interface PiPedalModel {
jackSettings: ObservableProperty<JackChannelSelection>;
banks: ObservableProperty<BankIndex>;
jackServerSettings: ObservableProperty<JackServerSettings>;
wifiConfigSettings: ObservableProperty<WifiConfigSettings>;
presets: ObservableProperty<PresetIndex>;
@@ -320,13 +324,17 @@ export interface PiPedalModel {
uploadPreset(file: File, uploadAfter: number): Promise<number>;
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void>;
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>;
};
class PiPedalModelImpl implements PiPedalModel {
clientId: number = -1;
serverVersion?: PiPedalVersion;
countryCodes: Object = {};
socketServerUrl: string = "";
varServerUrl: string = "";
lv2Path: string = "";
@@ -346,6 +354,8 @@ class PiPedalModelImpl implements PiPedalModel {
jackServerSettings: ObservableProperty<JackServerSettings>
= new ObservableProperty<JackServerSettings>(new JackServerSettings());
wifiConfigSettings: ObservableProperty<WifiConfigSettings> = new ObservableProperty<WifiConfigSettings>(new WifiConfigSettings());
presets: ObservableProperty<PresetIndex> = new ObservableProperty<PresetIndex>
(
@@ -459,6 +469,9 @@ class PiPedalModelImpl implements PiPedalModel {
} else if (message === "onJackServerSettingsChanged") {
let jackServerSettings = new JackServerSettings().deserialize(body);
this.jackServerSettings.set(jackServerSettings);
} else if (message === "onWifiConfigSettingsChanged") {
let wifiConfigSettings = new WifiConfigSettings().deserialize(body);
this.wifiConfigSettings.set(wifiConfigSettings);
} else if (message === "onBanksChanged") {
let banks = new BankIndex().deserialize(body);
this.banks.set(banks);
@@ -537,6 +550,11 @@ class PiPedalModelImpl implements PiPedalModel {
.then(data => {
this.pedalBoard.set(new PedalBoard().deserialize(data));
return this.getWebSocket().request<any>("getWifiConfigSettings");
})
.then(data => {
this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data));
return this.getWebSocket().request<any>("getJackServerSettings");
})
.then(data => {
@@ -573,7 +591,7 @@ class PiPedalModelImpl implements PiPedalModel {
})
}
makeSocketServerUrl(hostName: string, port: number): string {
return "ws://" + hostName + ":" + port + "/piddle";
return "ws://" + hostName + ":" + port + "/pipedal";
}
makeVarServerUrl(protocol: string, hostName: string, port: number): string {
@@ -587,11 +605,12 @@ class PiPedalModelImpl implements PiPedalModel {
const myRequest = new Request(this.varRequest('config.json'));
return fetch(myRequest)
.then(
response => response.json()
(response) =>
{
return response.json();
}
)
.then(data => {
console.log("Got config");
if (data.max_upload_size)
{
this.maxUploadSize = data.max_upload_size;
@@ -617,6 +636,15 @@ class PiPedalModelImpl implements PiPedalModel {
return this.webSocket.connect();
})
.then(() => {
const isoRequest = new Request('iso_codes.json');
return fetch(isoRequest);
})
.then((response) => {
return response.json();
})
.then((countryCodes) => {
this.countryCodes = countryCodes as Object;
return this.getWebSocket().request<number>("hello");
})
.then((clientId) => {
@@ -652,6 +680,11 @@ class PiPedalModelImpl implements PiPedalModel {
})
.then((data) => {
this.presets.set(new PresetIndex().deserialize(data));
return this.getWebSocket().request<any>("getWifiConfigSettings");
})
.then(data => {
this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data));
return this.getWebSocket().request<any>("getJackServerSettings");
})
@@ -1427,6 +1460,75 @@ class PiPedalModelImpl implements PiPedalModel {
});
return result;
}
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void>
{
let result = new Promise<void>((resolve,reject) =>{
let oldSettings = this.wifiConfigSettings.get();
wifiConfigSettings = wifiConfigSettings.clone();
if ((!oldSettings.enable) && (!wifiConfigSettings.enable))
{
// no effective change.
resolve();
return;
}
let countryCodeChanged = wifiConfigSettings.countryCode !== oldSettings.countryCode;
if (!wifiConfigSettings.enable)
{
wifiConfigSettings.hasPassword = false;
wifiConfigSettings.hotspotName = oldSettings.hotspotName;
} else {
if (!wifiConfigSettings.hasPassword) {
if (wifiConfigSettings.hotspotName === oldSettings.hotspotName && !countryCodeChanged) {
// no effective change.
resolve();
return;
}
}
}
// save a version for the server (potentially carrying a password)
let serverConfigSettings = wifiConfigSettings.clone();
// notify the server.
let ws = this.webSocket;
if (!ws)
{
reject("Not connected.");
return;
}
ws.request<void>(
"setWifiConfigSettings",
serverConfigSettings
)
.then(()=> {
resolve();
})
.catch((err) =>{
reject(err);
});
});
return result;
}
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>
{
let result = new Promise<WifiChannel[]>((resolve,reject) => {
if (!this.webSocket) {
reject("Connection closed.");
return;
}
this.webSocket.request<WifiChannel[]>("getWifiChannels",countryIso3661)
.then((data) => {
resolve(WifiChannel.deserialize_array(data));
})
.catch((err)=> reject(err));
});
return result;
}
};
+2 -2
View File
@@ -100,12 +100,12 @@ const styles = (theme: Theme) => createStyles({
marginRight: 8,
marginBottom: 12,
position: "relative",
paddingLeft: 0,
paddingLeft: 3,
paddingRight: 0,
paddingTop: 0,
paddingBottom: 0,
border: "2pt #AAA solid",
borderRadius: 10,
borderRadius: 4,
elevation: 12,
display: "flex",
flexDirection: "row", flexWrap: "wrap",
+1 -1
View File
@@ -95,7 +95,7 @@ export default class RenameDialog extends ResizeResponsiveComponent<RenameDialog
}
componentWillUnmount()
{
super.componentDidMount();
super.componentWillUnmount();
this.mounted = false;
this.updateHooks();
}
+59 -6
View File
@@ -5,7 +5,6 @@ import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
import ButtonBase from "@material-ui/core/ButtonBase";
import { TransitionProps } from '@material-ui/core/transitions/transition';
import Slide from '@material-ui/core/Slide';
import Dialog from '@material-ui/core/Dialog';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import { Theme, withStyles, WithStyles, createStyles } from '@material-ui/core/styles';
@@ -18,6 +17,9 @@ import SelectHoverBackground from './SelectHoverBackground';
import JackServerSettings from './JackServerSettings';
import JackServerSettingsDialog from './JackServerSettingsDialog';
import JackHostStatus from './JackHostStatus';
import WifiConfigSettings from './WifiConfigSettings';
import WifiConfigDialog from './WifiConfigDialog';
import DialogEx from './DialogEx'
@@ -34,6 +36,9 @@ interface SettingsDialogState {
jackServerSettings: JackServerSettings;
jackStatus?: JackHostStatus;
wifiConfigSettings: WifiConfigSettings;
showWifiConfigDialog: boolean;
showInputSelectDialog: boolean;
showOutputSelectDialog: boolean;
showMidiSelectDialog: boolean;
@@ -120,6 +125,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
jackConfiguration: this.model.jackConfiguration.get(),
jackStatus: undefined,
jackSettings: this.model.jackSettings.get(),
wifiConfigSettings: this.model.wifiConfigSettings.get(),
showWifiConfigDialog: false,
showInputSelectDialog: false,
showOutputSelectDialog: false,
showMidiSelectDialog: false,
@@ -132,10 +139,28 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.handleJackConfigurationChanged = this.handleJackConfigurationChanged.bind(this);
this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this);
this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this);
this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this);
}
handleApplyWifiConfig(wifiConfigSettings: WifiConfigSettings): void {
this.setState({showWifiConfigDialog: false});
this.model.setWifiConfigSettings(wifiConfigSettings)
.then(() => {
})
.catch((err) => {
this.model.showAlert(err);
});
}
handleWifiConfigSettingsChanged(): void {
this.setState(
{
wifiConfigSettings: this.model.wifiConfigSettings.get()
}
)
}
handleJackSettingsChanged(): void {
this.setState({
jackSettings: this.model.jackSettings.get()
@@ -177,6 +202,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.model.jackSettings.addOnChangedHandler(this.handleJackSettingsChanged);
this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged);
this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged);
this.model.wifiConfigSettings.addOnChangedHandler(this.handleWifiConfigSettingsChanged);
this.model.getJackStatus()
.then((jackStatus) =>
this.setState(
@@ -193,7 +219,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.handleJackConfigurationChanged();
this.handleJackSettingsChanged();
this.handleJackServerSettingsChanged();
this.timerHandle = setInterval(() => this.tick(), 1000);
this.handleWifiConfigSettingsChanged();
// xxx UNCOMMENT ME! this.timerHandle = setInterval(() => this.tick(), 1000);
} else {
if (this.timerHandle) {
clearInterval(this.timerHandle);
@@ -201,6 +228,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.model.jackConfiguration.removeOnChangedHandler(this.handleJackConfigurationChanged);
this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged);
this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged);
this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged);
}
}
@@ -292,25 +321,33 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
if (ports.length === 1) return ports[0];
return ports.length + " channels";
}
handleShowWifiConfigDialog() {
this.setState({
showWifiConfigDialog: true
});
}
handleRestart() {
this.setState({ restarting: true });
this.model.restart()
.then(() => {
this.setState({ restarting: true });
// this.setState({ restarting: true });
})
.catch((error) => {
this.model.showAlert(error);
this.setState({ restarting: false });
});
}
handleShutdown() {
this.setState({ shuttingDown: true });
this.model.shutdown()
.then(() => {
this.setState({ shuttingDown: true });
})
.catch((error) => {
this.model.showAlert(error);
this.setState({ shuttingDown: false });
});
}
@@ -322,7 +359,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
let disableShutdown = this.state.shuttingDown || this.state.restarting;
return (
<Dialog fullScreen open={this.props.open}
<DialogEx tag="SettingsDialog" fullScreen open={this.props.open}
onClose={() => { this.props.onClose() }} TransitionComponent={Transition}>
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
@@ -428,6 +465,18 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
<Divider />
<div >
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">SYSTEM</Typography>
<ButtonBase className={classes.setting} disabled={!this.state.wifiConfigSettings.valid}
onClick={() => this.handleShowWifiConfigDialog()} >
<SelectHoverBackground selected={false} showHover={true} />
<div style={{ width: "100%" }}>
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
Configure Wi-fi Hotspot</Typography>
<Typography display="block" variant="caption" noWrap color="textSecondary">
{this.state.wifiConfigSettings.getSummaryText()}
</Typography>
</div>
</ButtonBase>
<ButtonBase className={classes.setting} disabled={!isConfigValid || disableShutdown}
onClick={() => this.handleRestart()} >
<SelectHoverBackground selected={false} showHover={true} />
@@ -483,8 +532,12 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
)
}
<WifiConfigDialog wifiConfigSettings={this.state.wifiConfigSettings} open={this.state.showWifiConfigDialog}
onClose={()=> this.setState({showWifiConfigDialog: false})}
onOk={(wifiConfigSettings) => this.handleApplyWifiConfig(wifiConfigSettings)}
/>
</Dialog >
</DialogEx >
);
+1 -1
View File
@@ -93,7 +93,7 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
this.updateHooks();
}
componentWillUnmount() {
super.componentDidMount();
super.componentWillUnmount();
this.mounted = false;
this.updateHooks();
}
+20
View File
@@ -0,0 +1,20 @@
export default class WifiChannel {
deserialize(input: any): WifiChannel {
this.channelId = input.channelId;
this.channelName = input.channelName;
return this;
}
static deserialize_array(input: any): WifiChannel[]
{
let result: WifiChannel[] = [];
for (let i = 0; i < input.length; ++i)
{
result.push(new WifiChannel().deserialize(input[i]));
}
return result;
}
channelId: string = "";
channelName: string = "";
};
+374
View File
@@ -0,0 +1,374 @@
import React from 'react';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import Switch from '@material-ui/core/Switch';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
import WifiConfigSettings from './WifiConfigSettings';
import NoChangePassword from './NoChangePassword';
import DialogEx from './DialogEx'
import Typography from '@material-ui/core/Typography';
import { Theme, withStyles, WithStyles,createStyles } from '@material-ui/core/styles';
import Select from '@material-ui/core/Select';
import FormControl from '@material-ui/core/FormControl';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import WifiChannel from './WifiChannel';
import {PiPedalModel, PiPedalModelFactory} from './PiPedalModel';
const styles = (theme: Theme) => createStyles({
pgraph: {
paddingBottom: 16
}
});
const NBSP = "\u00A0";
export interface WifiConfigProps extends WithStyles<typeof styles> {
open: boolean,
wifiConfigSettings: WifiConfigSettings,
onOk: (wifiConfigSettings: WifiConfigSettings) => void,
onClose: () => void
};
export interface WifiConfigState {
fullScreen: boolean;
showWifiWarningDialog: boolean;
wifiWarningGiven: boolean;
enabled: boolean;
name: string;
newPassword: string;
nameError: boolean;
nameErrorMessage: string;
passwordError: boolean;
passwordErrorMessage: string;
countryCode: string;
channel: string;
wifiChannels: WifiChannel[];
};
let validNameRegex = /^[a-zA-Z][a-zA-Z0-9_-]*$/
function isValidName(text: string)
{
return validNameRegex.test(text);
}
const WifiConfigDialog = withStyles(styles, { withTheme: true })(
class extends ResizeResponsiveComponent<WifiConfigProps, WifiConfigState> {
refName: React.RefObject<HTMLInputElement>;
refPassword: React.RefObject<HTMLInputElement>;
model: PiPedalModel;
constructor(props: WifiConfigProps) {
super(props);
this.model = PiPedalModelFactory.getInstance();
this.state = {
fullScreen: false,
enabled: this.props.wifiConfigSettings.enable,
name: this.props.wifiConfigSettings.hotspotName,
newPassword: "",
nameError: false,
nameErrorMessage: NBSP,
passwordError: false,
showWifiWarningDialog: false,
wifiWarningGiven: this.props.wifiConfigSettings.wifiWarningGiven,
passwordErrorMessage: NBSP,
countryCode: this.props.wifiConfigSettings.countryCode,
channel: this.props.wifiConfigSettings.channel,
wifiChannels: []
};
this.model.getWifiChannels(this.props.wifiConfigSettings.countryCode)
.then((wifiChannels_) => {
this.setState({wifiChannels: wifiChannels_});
}).catch((err)=> {});
this.refName = React.createRef<HTMLInputElement>();
this.refPassword = React.createRef<HTMLInputElement>();
this.handlePopState = this.handlePopState.bind(this);
}
mounted: boolean = false;
hasHooks: boolean = false;
stateWasPopped: boolean = false;
handleEnableChanged(e: any) {
this.setState({ enabled: e.target.checked });
}
handlePopState(e: any): any {
this.stateWasPopped = true;
let shouldClose = (!e.state || !e.state.WifiConfig);
if (shouldClose) {
this.props.onClose();
}
}
updateHooks(): void {
let wantHooks = this.mounted && this.props.open;
if (wantHooks !== this.hasHooks) {
this.hasHooks = wantHooks;
if (this.hasHooks) {
this.stateWasPopped = false;
window.addEventListener("popstate", this.handlePopState);
// eslint-disable-next-line no-restricted-globals
let state = history.state;
if (!state) {
state = {};
}
state.WifiConfig = true;
// eslint-disable-next-line no-restricted-globals
history.pushState(
state,
"Wi-Fi Configuration",
"#WifiConfig"
);
} else {
window.removeEventListener("popstate", this.handlePopState);
if (!this.stateWasPopped) {
// eslint-disable-next-line no-restricted-globals
history.back();
}
}
}
}
onWindowSizeChanged(width: number, height: number): void {
this.setState({ fullScreen: height < 200 })
}
componentDidMount() {
super.componentDidMount();
this.mounted = true;
this.updateHooks();
}
componentWillUnmount() {
super.componentWillUnmount();
this.mounted = false;
this.updateHooks();
}
componentDidUpdate(prevProps: WifiConfigProps) {
this.updateHooks();
if (this.props.open && !prevProps.open)
{
this.setState({
enabled: this.props.wifiConfigSettings.enable,
name: this.props.wifiConfigSettings.hotspotName,
newPassword: "",
wifiWarningGiven: this.props.wifiConfigSettings.wifiWarningGiven,
countryCode: this.props.wifiConfigSettings.countryCode,
channel: this.props.wifiConfigSettings.channel
});
if (this.props.wifiConfigSettings.countryCode !== this.state.countryCode)
{
this.model.getWifiChannels(this.props.wifiConfigSettings.countryCode)
.then((wifiChannels) => {
this.setState({wifiChannels: wifiChannels});
})
.catch((error) => {});
}
}
}
handleOk(wifiWarningGiven: boolean) {
let hasError = false;
if (this.state.enabled)
{
let name = this.state.name;
if (name.length === 0) {
this.setState({nameError: true, nameErrorMessage: "* Required"});
hasError = true;
} else {
if (!isValidName(name))
{
this.setState({nameError: true, nameErrorMessage: "Invalid name"});
hasError = true;
} else if (name.length > 15)
{
this.setState({nameError: true, nameErrorMessage: "> 15 characters"});
hasError = true;
}
}
}
if (this.state.enabled && (!this.props.wifiConfigSettings.hasPassword) && this.state.newPassword.length === 0)
{
this.setState({passwordError: true, passwordErrorMessage: "* Required"});
hasError = true;
}
if (!hasError)
{
let wifiConfigSettings = new WifiConfigSettings();
wifiConfigSettings.valid = true;
wifiConfigSettings.enable = this.state.enabled;
wifiConfigSettings.hotspotName = this.state.name;
if (this.state.newPassword.length === 0 || !this.state.enabled)
{
wifiConfigSettings.hasPassword = false;
} else {
wifiConfigSettings.hasPassword = true;
wifiConfigSettings.password = this.state.newPassword;
}
if (!this.props.wifiConfigSettings.wifiWarningGiven && !wifiWarningGiven) {
this.setState({showWifiWarningDialog: true});
} else {
wifiConfigSettings.wifiWarningGiven = true;
this.props.onOk(wifiConfigSettings);
}
}
}
handleChannelChange(e: any)
{
let value = e.target.value as string;
this.setState({channel: value });
}
handleCountryChanged(e: any)
{
let value = e.target.value as string;
this.setState({countryCode: value});
this.model.getWifiChannels(value)
.then(wifiChannels => {
this.setState({wifiChannels: wifiChannels});
})
.catch((error)=> { });
}
render() {
let props = this.props;
let classes = this.props.classes;
let { open, onClose} = props;
const handleClose = () => {
onClose();
};
return (
<Dialog open={open} fullWidth onClose={handleClose} style={{}}
fullScreen={this.state.fullScreen}
>
{ this.state.fullScreen && (
<DialogTitle>Wi-fi Hotspot</DialogTitle>
)}
<DialogContent>
<FormControlLabel
control={(
<Switch
checked={this.state.enabled}
onChange={(e: any) => this.handleEnableChanged(e)}
color="secondary"
/>
)}
label="Enable"
/>
<TextField style={{ marginBottom: 16, marginTop: 16 }}
autoComplete="off"
id="name"
spellCheck="false"
label="SSID"
type="text"
fullWidth
error={this.state.nameError}
helperText={this.state.nameErrorMessage}
value={this.state.name}
onChange={(e) => this.setState({name: e.target.value, nameError: false, nameErrorMessage: NBSP})}
inputRef={this.refName}
disabled={!this.state.enabled}
/>
<div style={{ marginBottom: 16 }}>
<NoChangePassword
onPasswordChange={(text): void => { this.setState({ newPassword: text, passwordError: false, passwordErrorMessage: NBSP }); }}
hasPassword={this.props.wifiConfigSettings.hasPassword}
label="WEP Passphrase"
error={this.state.passwordError}
helperText={this.state.passwordErrorMessage}
defaultValue={this.state.newPassword}
disabled={!this.state.enabled}
/>
</div>
<div style={{ marginBottom: 16}}>
<FormControl>
<InputLabel htmlFor="countryCodeSelect">Country</InputLabel>
<Select id="countryCodeSelect" value={this.state.countryCode} style={{width: 220}}
onChange={(event)=>this.handleCountryChanged(event)} >
{Object.entries(this.model.countryCodes).map(([key,value])=> {
return (
<MenuItem value={key}>{value}</MenuItem>
);
})}
</Select>
</FormControl>
</div>
<div style={{ marginBottom: 24}}>
<FormControl>
<InputLabel htmlFor="channelSelect">Channel</InputLabel>
<Select id="channelSelect" value={this.state.channel} style={{width: 220}} onChange={(e)=>{
this.handleChannelChange(e);
}}>
{this.state.wifiChannels.map((channel)=> {
return (
<MenuItem value={channel.channelId}>{channel.channelName}</MenuItem>
)
})}
</Select>
</FormControl>
</div>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary" style={{ width: 120 }}>
Cancel
</Button>
<Button onClick={()=> this.handleOk(false)} color="secondary" style={{ width: 120 }} >
OK
</Button>
</DialogActions>
<DialogEx open={this.state.showWifiWarningDialog} tag="wifiConfirm" >
<DialogContent>
<Typography className={classes.pgraph} variant="body2" color="textPrimary">
Enabling the Wi-Fi hotspot will disable regular Wi-Fi network access on the PiPedal device. Once
enabled, connect to the hotspot and launch http://172.24.1.1 or http://{this.state.name}.local to access the PiPedal web app again.
</Typography>
<Typography className={classes.pgraph} variant="body2" color="textPrimary" gutterBottom>
Are you sure you want to proceed?
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={()=> this.setState({showWifiWarningDialog: false})} color="primary" style={{ width: 120 }}>
Cancel
</Button>
<Button onClick={()=> {
this.setState({showWifiWarningDialog: false});
this.handleOk(true);
}} color="secondary" style={{ width: 120 }} >
PROCEED
</Button>
</DialogActions>
</DialogEx>
</Dialog>
);
}
});
export default WifiConfigDialog;
+45
View File
@@ -0,0 +1,45 @@
export default class WifiConfigSettings {
deserialize(input: any) : WifiConfigSettings{
this.valid = input.valid;
this.wifiWarningGiven = input.wifiWarningGiven;
this.rebootRequired = input.rebootRequired;
this.enable = input.enable;
this.hotspotName = input.hotspotName;
this.hasPassword = input.hasPassword;
this.password = input.password;
this.countryCode = input.countryCode;
this.channel = input.channel;
return this;
}
clone() : WifiConfigSettings {
return this.deserialize(this);
}
valid: boolean = true;
wifiWarningGiven: boolean = false;
enable: boolean = true;
hasPassword: boolean = false;
rebootRequired = false;
hotspotName: string = "pipedal";
password: string = "";
countryCode: string = "US";
channel: string = "g6";
getSummaryText() {
let result: string;
if (!this.valid) {
result = "Not available.";
} else if (!this.enable) {
result = "Disabled.";
} else {
result = this.hotspotName;
}
if (this.rebootRequired)
{
result += " (Restart required)";
}
return result;
}
}
+36
View File
@@ -0,0 +1,36 @@
import React from 'react';
import { Theme, withStyles, WithStyles,createStyles } from '@material-ui/core/styles';
const styles = (theme: Theme) => createStyles({
});
interface XxxProps extends WithStyles<typeof styles> {
theme: Theme,
};
interface XxxState {
};
const Xxx = withStyles(styles, { withTheme: true })(
class extends React.Component<XxxProps, XxxState> {
constructor(props: XxxProps)
{
super(props);
this.state = {
};
}
render() {
return (
<div/>
);
}
}
);
export default Xxx;
View File
+3
View File
@@ -458,6 +458,9 @@ public:
{
this->ws_.close(websocket::close_reason());
}
virtual std::string getFromAddress() const {
return this->fromAddress;
}
virtual void writeCallback(const std::string &text)
{
{
+2
View File
@@ -28,6 +28,7 @@ public:
virtual void close() = 0;
virtual void writeCallback(const std::string& text) = 0;
virtual std::string getFromAddress() const = 0;
};
private:
@@ -43,6 +44,7 @@ private:
protected:
virtual void onReceive(const std::string_view&text) = 0;
public:
std::string getFromAddress() const { return writeCallback_->getFromAddress(); }
void receive(const std::string_view&text) {
onReceive(text);
}
+53 -8
View File
@@ -4,13 +4,27 @@ cmake_minimum_required(VERSION 3.19.0)
include(FindPkgConfig)
# Can't get the pkg_check to work.
# pkg_check_modules(LIBNL3 "nl-genl-3")
# if(!LIBNL3_FOUND)
# message(ERROR "libnl-genl-3-dev package not installed.")
# else()
# message(STATUS "LIBNL3 libraries: ${LIBNL3_LIBRARIES}")
# message(STATUS "LBNL3 includes: ${LIBNL3_INCLUDE_DIRS}")
# endif()
# nlgenl-3 library.
execute_process(COMMAND ls /usr/include/libnl3/netlink/netlink.h RESULT_VARIABLE LNL3_MISSING OUTPUT_QUIET ERROR_QUIET)
if(LNL3_MISSING)
message(ERROR " Need to: sudo apt get libnl-3-dev libnl-genl-3-dev ")
endif()
set(LIBNL3_INCLUDE_DIRS /usr/include/libnl3)
set(LIBNL3_LIBRARIES nl-3 nl-genl-3)
pkg_check_modules(SYSTEMD "systemd")
if(!SYSTEMD_FOUND)
message(ERROR "libsystemd-dev package not installed.")
else()
message(STATUS "SYSTEMD_LIBRARIES: ${SYSTEMD_LIBRARIES}")
message(STATUS "SYSTEMD_INCLUDE_DIRS: ${SYSTEMD_INCLUDE_DIRS}")
endif()
@@ -55,8 +69,10 @@ message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}" )
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
add_executable(pipedald BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
RequestHandler.hpp main.cpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp
set (PIPEDAL_SOURCES
BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
WifiConfigSettings.hpp WifiConfigSettings.cpp
RequestHandler.hpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp
PluginType.hpp PluginType.cpp
Lv2Log.hpp Lv2Log.cpp
PiPedalSocket.hpp PiPedalSocket.cpp
@@ -93,22 +109,51 @@ add_executable(pipedald BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelpe
Locale.hpp Locale.cpp
Lv2EventBufferWriter.hpp Lv2EventBufferWriter.cpp
IpSubnet.hpp
WifiChannels.hpp
WifiChannels.cpp
RegDb.cpp RegDb.hpp
)
configure_file(config.hpp.in config.hpp)
#################################
add_executable(pipedald ${PIPEDAL_SOURCES} main.cpp
)
include_directories( ${PiPedal_SOURCE_DIR}/. ../build/src)
target_precompile_headers(pipedald PRIVATE pch.h)
target_include_directories(pipedald PRIVATE
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${SYSTEMD_INCLUDE_DIRS}
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS}
)
target_link_libraries(pipedald PRIVATE pthread atomic stdc++fs
${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${SYSTEMD_LIBRARIES} systemd
${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd
)
#target_link_libraries(pipedald boost_system)
#################################
add_executable(pipedaltest ${PIPEDAL_SOURCES} testMain.cpp
jsonTest.cpp
WifiChannelsTest.cpp
)
configure_file(config.hpp.in config.hpp)
include_directories( ${PiPedal_SOURCE_DIR}/. ../build/src)
target_precompile_headers(pipedaltest PRIVATE pch.h)
target_include_directories(pipedaltest PRIVATE
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS}
)
target_link_libraries(pipedaltest PRIVATE pthread atomic stdc++fs
${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd
)
#################################
add_executable(pipedalconfig json.cpp json.hpp
+29 -7
View File
@@ -95,7 +95,6 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
this->jackHost->SetNotificationCallbacks(this);
if (configuration.GetMLock())
{
#ifndef NO_MLOCK
@@ -126,10 +125,8 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
catch (PiPedalException &e)
{
Lv2Log::error("Failed to load initial plugin. (%s)", e.what());
}
}
}
IPiPedalModelSubscriber *PiPedalModel::GetNotificationSubscriber(int64_t clientId)
@@ -278,7 +275,6 @@ void PiPedalModel::setPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
}
}
void PiPedalModel::setPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled)
{
std::lock_guard(this->mutex);
@@ -318,7 +314,6 @@ PedalBoard PiPedalModel::getPreset(int64_t instanceId)
return this->storage.GetPreset(instanceId);
}
void PiPedalModel::setPresetChanged(int64_t clientId, bool value)
{
if (value != this->hasPresetChanged)
@@ -376,7 +371,6 @@ int64_t PiPedalModel::uploadPreset(const BankFile&bankFile,int64_t uploadAfter)
int64_t newPreset = this->storage.UploadPreset(bankFile, uploadAfter);
firePresetsChanged(-1);
return newPreset;
}
void PiPedalModel::loadPreset(int64_t clientId, int64_t instanceId)
@@ -462,6 +456,35 @@ bool PiPedalModel::renamePreset(int64_t clientId, int64_t instanceId, const std:
}
}
void PiPedalModel::setWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
{
std::lock_guard lock(this->mutex);
// xxx Commit to local!
this->storage.SetWifiConfigSettings(wifiConfigSettings);
{
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
for (size_t i = 0; i < subscribers.size(); ++i)
{
t[i] = this->subscribers[i];
}
size_t n = this->subscribers.size();
WifiConfigSettings tWifiConfigSettings = storage.GetWifiConfigSettings(); // (the passwordless version)
for (size_t i = 0; i < n; ++i)
{
t[i]->OnWifiConfigSettingsChanged(tWifiConfigSettings);
}
delete[] t;
}
}
WifiConfigSettings PiPedalModel::getWifiConfigSettings()
{
std::lock_guard lock(this->mutex);
return this->storage.GetWifiConfigSettings();
}
JackConfiguration PiPedalModel::getJackConfiguration()
{
std::lock_guard lock(this->mutex); // copy atomically.
@@ -983,4 +1006,3 @@ void PiPedalModel::cancelListenForMidiEvent(int64_t clientId, int64_t clientHand
jackHost->SetListenForMidiEvent(false);
}
}
+6
View File
@@ -12,6 +12,7 @@
#include "Banks.hpp"
#include "PiPedalConfiguration.hpp"
#include "JackServerSettings.hpp"
#include "WifiConfigSettings.hpp"
namespace pipedal {
@@ -34,6 +35,7 @@ public:
virtual void OnLoadPluginPreset(int64_t instanceId,const std::vector<ControlValue>&controlValues) = 0;
virtual void OnMidiValueChanged(int64_t instanceId, const std::string&symbol, float value) = 0;
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0;
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) = 0;
virtual void Close() = 0;
};
@@ -145,6 +147,10 @@ public:
void setJackChannelSelection(int64_t clientId,const JackChannelSelection &channelSelection);
JackChannelSelection getJackChannelSelection();
void setWifiConfigSettings(const WifiConfigSettings&wifiConfigSettings);
WifiConfigSettings getWifiConfigSettings();
int64_t addVuSubscription(int64_t instanceId);
void removeVuSubscription(int64_t subscriptionHandle);
+34
View File
@@ -11,6 +11,8 @@
#include <atomic>
#include "ShutdownClient.hpp"
#include "WifiConfigSettings.hpp"
#include "WifiChannels.hpp"
using namespace std;
using namespace pipedal;
@@ -667,6 +669,12 @@ public:
{
JackHostStatus status = model.getJackStatus();
this->Reply(replyTo,"getJackStatus",status);
} else if (message == "getWifiChannels")
{
std::string country;
pReader->read(&country);
std::vector<WifiChannel> channels = pipedal::getWifiChannels(country.c_str());
this->Reply(replyTo,"getWifiChannels",channels);
}
else if (message == "getPluginPresets")
{
@@ -688,6 +696,28 @@ public:
this->Reply(replyTo,"setJackserverSettings");
}
else if (message == "setWifiConfigSettings") {
WifiConfigSettings wifiConfigSettings;
pReader->read(&wifiConfigSettings);
if (!ShutdownClient::CanUseShutdownClient())
{
throw PiPedalException("Can't change server settings when running interactively.");
}
std::string fromAddress = this->getFromAddress();
if (!ShutdownClient::IsOnLocalSubnet(fromAddress))
{
throw PiPedalException("Permission denied. Not on local subnet.");
}
this->model.setWifiConfigSettings(wifiConfigSettings);
this->Reply(replyTo,"setWifiConfigSettings");
}
else if (message == "getWifiConfigSettings") {
this->Reply(replyTo, "getWifiConfigSettings", model.getWifiConfigSettings());
}
else if (message == "getJackServerSettings") {
this->Reply(replyTo, "getJackServerSettings", model.GetJackServerSettings());
@@ -1220,6 +1250,10 @@ public:
Send("onJackServerSettingsChanged",jackServerSettings);
}
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) {
Send("onWifiConfigSettingsChanged",wifiConfigSettings);
}
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard)
{
SetCurrentPedalBoardBody body;
+228
View File
@@ -0,0 +1,228 @@
#include "pch.h"
#include "RegDb.hpp"
#include <fstream>
#include <filesystem>
#include <unordered_set>
#include "PiPedalException.hpp"
#define REGDB_MAGIC 0x52474442
#define REGDB_VERSION 19
using namespace pipedal;
using namespace std;
struct RegDbFileHeader {
uint32_t magic;
uint32_t version;
uint32_t countryOffset;
uint32_t countryCount;
uint32_t signatureLength;
void toNs() {
this->magic = htonl(this->magic);
this->version = htonl(this->version);
this->countryOffset = htonl(this->countryOffset);
this->countryCount = htonl(this->countryCount);
this->signatureLength = htonl(this->signatureLength);
}
};
struct CountryHeader {
char alpha2[2];
uint8_t pad;
uint8_t dfsRegion; // first 2 bits only.
uint32_t rulesOffset;
void toNs() {
this->rulesOffset = htonl(rulesOffset);
}
};
struct RulesCollection {
uint32_t ruleCount;
uint32_t ruleOffsets[1];
void toNs() {
this->ruleCount = htonl(this->ruleCount);
uint32_t *p = ruleOffsets;
for (uint32_t i = 0; i < this->ruleCount; ++i)
{
*p = htonl(*p);
++p;
}
}
};
struct Rule {
uint32_t frequencyRangeOffset;
uint32_t powerRuleOffset;
uint32_t flags;
void toNs() {
frequencyRangeOffset = htonl(frequencyRangeOffset);
powerRuleOffset = htonl(powerRuleOffset);
flags = htonl(flags);
}
};
struct FrequencyRange {
uint32_t startFrequency; // in khz.
uint32_t endFrequency; // in khz.
uint32_t maxBandwidth; // in khz.
void toNs() {
startFrequency = htonl(startFrequency);
endFrequency = htonl(endFrequency);
maxBandwidth = htonl(maxBandwidth);
}
};
struct PowerRule {
uint32_t maximumAntennaGain;
uint32_t maximumEirp;
void toNs() {
maximumAntennaGain = htonl(maximumAntennaGain);
maximumEirp = htonl(maximumEirp);
}
};
const char*filePaths[] =
{
"/usr/lib/crda/regulatory.bin"
};
static std::filesystem::path getFilePath()
{
for (size_t i = 0; i < sizeof(filePaths)/sizeof(filePaths[0]); ++i)
{
std::filesystem::path path(filePaths[i]);
if (std::filesystem::exists(path)) {
return path;
}
}
throw PiPedalException("Could not find 'regulatory.bin' file.");
}
RegDb::~RegDb()
{
delete[] pData;
}
RegDb::RegDb()
:pData(nullptr)
{
RegDbFileHeader header;
std::filesystem::path path = getFilePath();
ifstream f(path);
if (!f.is_open()) {
stringstream s;
s << "Can't read " << path;
throw PiPedalException(s.str());
}
size_t fileSize = std::filesystem::file_size(path);
this->pData = new uint8_t[fileSize];
f.read((char*)this->pData,fileSize);
// convert from big-endian to local byte order;
RegDbFileHeader *fileHeader = (RegDbFileHeader*)pData;
fileHeader->toNs();
if (fileHeader->magic != REGDB_MAGIC)
{
throw PiPedalException("Invalid file format.");
}
std::unordered_set<uint32_t> visited;
CountryHeader*countryHeader = (CountryHeader*)(pData + fileHeader->countryOffset);
for (uint32_t i = 0; i < fileHeader->countryCount; ++i)
{
countryHeader->toNs();
if (visited.count(countryHeader->rulesOffset) == 0)
{
visited.insert(countryHeader->rulesOffset);
RulesCollection*pRules = (RulesCollection*)(pData + countryHeader->rulesOffset);
pRules->toNs();
for (uint32_t nRule = 0; nRule < pRules->ruleCount; ++nRule)
{
uint32_t ruleOffset = pRules->ruleOffsets[nRule];
if (visited.count(ruleOffset) == 0)
{
visited.insert(ruleOffset);
Rule *pRule = (Rule*)(pData + ruleOffset);
pRule->toNs();
if (visited.count(pRule->frequencyRangeOffset) == 0)
{
visited.insert(pRule->frequencyRangeOffset);
FrequencyRange*pf = (FrequencyRange*)(pData + pRule->frequencyRangeOffset);
pf->toNs();
}
if (visited.count(pRule->powerRuleOffset) == 0)
{
visited.insert(pRule->powerRuleOffset);
PowerRule *pr = (PowerRule*)(pData + pRule->powerRuleOffset);
pr->toNs();
}
}
}
}
++countryHeader;
}
}
bool CrdbRules::GetFrequencyRule(uint32_t frequencyRuleMHz, FrequencyRule*pResult)
{
memset(pResult,0,sizeof(FrequencyRule));
RulesCollection*pRules = (RulesCollection*)(regDb->pData + countryOffset);
uint32_t frequencyRuleKHz = frequencyRuleMHz*1000;
for (uint32_t i = 0; i < pRules->ruleCount; ++i)
{
Rule *pRule = (Rule*)(regDb->pData + pRules->ruleOffsets[i]);
FrequencyRange*pf = (FrequencyRange*)(regDb->pData + pRule->frequencyRangeOffset);
if (frequencyRuleKHz >= pf->startFrequency && frequencyRuleKHz < pf->endFrequency)
{
PowerRule *pr = (PowerRule*)(regDb->pData + pRule->powerRuleOffset);
pResult->flags = (RegRuleFlags)(pRule->flags);
pResult->maximumAntennaGain = pr->maximumAntennaGain;
pResult->maximumEirp = pr->maximumEirp;
return true;
}
}
return false;
}
CrdbRules*RegDb::GetCrdbRules(const char*countryCodeIso633)
{
const char c0 = countryCodeIso633[0];
if (c0 == '\0') return nullptr;
const char c1 = countryCodeIso633[1];
RegDbFileHeader *fileHeader = (RegDbFileHeader*)pData;
CountryHeader*countryHeader = (CountryHeader*)(pData + fileHeader->countryOffset);
for (uint32_t i = 0; i < fileHeader->countryCount; ++i)
{
if (countryHeader->alpha2[0] == c0 && countryHeader->alpha2[1] == c1) {
return new CrdbRules(
this,
countryHeader->rulesOffset,
(DfsRegion)(countryHeader->dfsRegion & 0x03)
);
}
++countryHeader;
}
return nullptr;
}
CrdbRules::CrdbRules(RegDb *regDb,uint32_t countryOffset, DfsRegion dfsRegion )
: regDb(regDb),
countryOffset(countryOffset),
dfsRegion(dfsRegion)
{
}
DfsRegion CrdbRules::GetDfsRegion() const {
return this->dfsRegion;
}
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#include <stdint.h>
namespace pipedal {
// sync with <linux/nl80211.h
enum class RegRuleFlags {
NO_OFDM = 1<<0,
NO_CCK = 1<<1,
NO_INDOOR = 1<<2,
NO_OUTDOOR = 1<<3,
DFS = 1<<4,
PTP_ONLY = 1<<5,
PTMP_ONLY = 1<<6,
NO_IR = 1<<7,
__NO_IBSS = 1<<8,
AUTO_BW = 1<<11,
IR_CONCURRENT = 1<<12,
NO_HT40MINUS = 1<<13,
NO_HT40PLUS = 1<<14,
NO_80MHZ = 1<<15,
NO_160MHZ = 1<<16,
NO_HE = 1<<17,
};
enum class DfsRegion {
Unset = 0,
Fcc = 1,
Etsi = 2,
Japan = 3
};
struct FrequencyRule {
uint32_t maximumAntennaGain; // in mBiu (100*dBi)
uint32_t maximumEirp; // in mBm (100*dBm)
RegRuleFlags flags;
bool hasFlag(RegRuleFlags flag) const { return (((int)flag) & (int)this->flags) != 0; }
};
class RegDb;
class CrdbRules {
friend class RegDb;
RegDb *regDb = nullptr;
uint32_t countryOffset;
DfsRegion dfsRegion;
CrdbRules(RegDb *regDb,uint32_t countryOffset, DfsRegion dfsRegion );
public:
virtual DfsRegion GetDfsRegion() const;
virtual bool GetFrequencyRule(uint32_t frequencyMHz,FrequencyRule*pResult);
};
class RegDb {
private:
friend class CrdbRules;
uint8_t*pData;
public:
RegDb();
~RegDb();
CrdbRules *GetCrdbRules(const char*countryCodeIso6333);
bool isValid() const { return pData != nullptr; }
};
}
+52
View File
@@ -6,6 +6,9 @@
#include <arpa/inet.h>
#include "Lv2Log.hpp"
#include <sstream>
#include <sys/types.h>
#include <ifaddrs.h>
using namespace pipedal;
@@ -138,3 +141,52 @@ bool ShutdownClient::SetJackServerConfiguration(const JackServerSettings & jackS
return WriteMessage(s.str().c_str());
}
bool ShutdownClient::IsOnLocalSubnet(const std::string&fromAddress)
{
std::string address = fromAddress;
if (address.size() == 0) return false;
char lastChar = address[address.size()-1];
if (address[0] != '[' || lastChar != ']') // i.e. not an ipv6 address;
{
size_t pos = address.find_last_of(':');
if (pos != std::string::npos)
{
address = address.substr(0,pos);
}
}
if (address[0] == '[') return false;
char buf[512];
struct in_addr inetAddr;
memset(&inetAddr,0,sizeof(inetAddr));
if (inet_pton(AF_INET,address.c_str(),&inetAddr) == 0) {
return false;
}
uint32_t remoteAddress = htonl(inetAddr.s_addr);
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0) return false;
bool result = false;
for (ifaddrs*p = ifap; p != nullptr; p = p->ifa_next) {
if (p->ifa_addr->sa_family == AF_INET && p->ifa_addr != nullptr && p->ifa_netmask != nullptr) { // TODO: Add support for AF_INET6
uint32_t netmask = htonl(((sockaddr_in*)(p->ifa_netmask))->sin_addr.s_addr);
uint32_t ifAddr = htonl(((sockaddr_in*)(p->ifa_addr))->sin_addr.s_addr);
if ((netmask & ifAddr) == (netmask & remoteAddress))
{
result = true;
break;
}
}
}
freeifaddrs(ifap);
return result;
}
+1
View File
@@ -11,6 +11,7 @@ public:
static bool CanUseShutdownClient();
static bool RequestShutdown(bool restart);
static bool SetJackServerConfiguration(const JackServerSettings & jackServerSettings);
static bool IsOnLocalSubnet(const std::string&fromAddress);
};
} // namespace
+72
View File
@@ -11,6 +11,8 @@ using namespace pipedal;
const char *BANK_EXTENSION = ".bank";
const char *BANKS_FILENAME = "index.banks";
#define WIFI_CONFIG_SETTINGS_FILENAME "wifiConfigSettings.json";
Storage::Storage()
{
SetDataRoot("~/var/PiPedal");
@@ -157,6 +159,7 @@ void Storage::Initialize()
catch (const std::exception &)
{
}
LoadWifiConfigSettings();
}
void Storage::LoadBank(int64_t instanceId)
@@ -683,3 +686,72 @@ int64_t Storage::UploadPreset(const BankFile&bankFile,int64_t uploadAfter)
this->SaveCurrentBank();
return lastPreset;
}
void Storage::SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings)
{
WifiConfigSettings copyToSave = wifiConfigSettings;
copyToSave.rebootRequired_ = false;
if (!copyToSave.enable_)
{
copyToSave.hasPassword_ = false;
}
copyToSave.password_ = "";
std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME;
{
std::ofstream f(path);
if (!f.is_open())
{
throw PiPedalException("Unable to write to " + ((std::string)path));
}
json_writer writer(f);
writer.write(&copyToSave);
}
WifiConfigSettings copyToStore = wifiConfigSettings;
if (copyToStore.enable_)
{
copyToStore.hasPassword_ = copyToStore.password_.length() != 0 || this->wifiConfigSettings.hasPassword_;
} else {
copyToStore.hasPassword_ = false;
}
copyToStore.password_ = "";
copyToStore.rebootRequired_ = true;
if (copyToStore.enable_ && !copyToStore.hasPassword_)
{
copyToStore.hasPassword_ = this->wifiConfigSettings.hasPassword_;
}
this->wifiConfigSettings = copyToStore;
}
void Storage::LoadWifiConfigSettings()
{
std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME;
try {
if (std::filesystem::is_regular_file(path))
{
std::ifstream f(path);
if (!f.is_open())
{
throw PiPedalException("Unable to write to " + ((std::string)path));
}
json_reader reader(f);
WifiConfigSettings wifiConfigSettings;
reader.read(&wifiConfigSettings);
this->wifiConfigSettings = wifiConfigSettings;
}
} catch (const std::exception&)
{
}
this->wifiConfigSettings.valid_ = true;
}
WifiConfigSettings Storage::GetWifiConfigSettings()
{
return this->wifiConfigSettings;
}
+6
View File
@@ -5,6 +5,7 @@
#include "Presets.hpp"
#include "Banks.hpp"
#include "JackConfiguration.hpp"
#include "WifiConfigSettings.hpp"
namespace pipedal {
@@ -39,6 +40,7 @@ private:
std::string GetPresetCopyName(const std::string &name);
bool isJackChannelSelectionValid = false;
JackChannelSelection jackChannelSelection;
WifiConfigSettings wifiConfigSettings;
public:
Storage();
void Initialize();
@@ -51,6 +53,7 @@ public:
const BankIndex & GetBanks() const { return bankIndex; }
void LoadWifiConfigSettings();
void LoadBank(int64_t instanceId);
const PedalBoard& GetCurrentPreset();
void saveCurrentPreset(const PedalBoard&pedalBoard);
@@ -77,6 +80,9 @@ public:
void SetJackChannelSelection(const JackChannelSelection&channelSelection);
const JackChannelSelection&GetJackChannelSelection(const JackConfiguration &jackConfiguration);
void SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings);
WifiConfigSettings GetWifiConfigSettings();
};
+615
View File
@@ -0,0 +1,615 @@
#include "pch.h"
#include <string>
#include <net/if.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include <netlink/genl/ctrl.h>
#include <netlink/msg.h>
#include <netlink/attr.h>
#include <linux/nl80211.h>
#include <linux/nl80211.h>
#include "PiPedalException.hpp"
#include "WifiChannels.hpp"
#include "Lv2Log.hpp"
#include "RegDb.hpp"
/*
Heavily based on code from iw (1) command.
https://kernel.googlesource.com/pub/scm/linux/kernel/git/jberg/iw/+/v0.9/COPYING
Copyright (c) 2007, 2008 Johannes Berg
Copyright (c) 2007 Andy Lutomirski
Copyright (c) 2007 Mike Kershaw
Copyright (c) 2008 Luis R. Rodriguez
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
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. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
using namespace pipedal;
struct MHzToChannel {
char band;
int channel;
int mhz;
};
MHzToChannel mhzToChannels[] = {
{'g', 1, 2412},
{'g',2, 2417},
{'g',3, 2422},
{'g',4, 2427},
{'g',5, 2432},
{'g',6, 2437},
{'g',7, 2442},
{'g',8, 2447},
{'g',9, 2452},
{'g',10, 2457},
{'g',11, 2462},
{'g',12, 2467},
{'g',13, 2472},
{'g',14, 2484},
{'a',7, 5035},
{'a',8, 5040},
{'a',9, 5045},
{'a',11, 5055},
{'a',12, 5060},
{'a',16, 5080},
{'a',32, 5160},
{'a',34, 5170},
{'a',36, 5180},
{'a',38, 5190},
{'a',40, 5200},
{'a',42, 5210},
{'a',44, 5220},
{'a',46, 5230},
{'a',48, 5240},
{'a',50, 5250},
{'a',52, 5260},
{'a',54, 5270},
{'a',56, 5280},
{'a',58, 5290},
{'a',60, 5300},
{'a',62, 5310},
{'a',64, 5320},
{'a',68, 5340},
{'a',96, 5480},
{'a',100, 5500},
{'a',102, 5510},
{'a',104, 5520},
{'a',106, 5530},
{'a',108, 5540},
{'a',110, 5550},
{'a',112, 5560},
{'a',114, 5570},
{'a',116, 5580},
{'a',118, 5590},
{'a',120, 5600},
{'a',122, 5610},
{'a',124, 5620},
{'a',126, 5630},
{'a',128, 5640},
{'a',132, 5660},
{'a',134, 5670},
{'a',136, 5680},
{'a',138, 5690},
{'a',140, 5700},
{'a',142, 5710},
{'a',144, 5720},
{'a',149, 5745},
{'a',151, 5755},
{'a',153, 5765},
{'a',155, 5775},
{'a',157, 5785},
{'a',159, 5795},
{'a',161, 5805},
{'a',163, 5815},
{'a',165, 5825},
{'a',167, 5835},
{'a',169, 5845},
{'a',171, 5855},
{'a',173, 5865},
{'a',175, 5875},
{'a',177, 5885},
{'a',180, 5900},
{'a',182, 5910},
{'a',18, 5915},
{'a',184, 5920},
{'a',187, 5935},
{'a',188, 5940},
{'a',189, 5945},
{'a',192, 5960},
{'a',196, 5980},
};
bool MHzToChannelNumber(int mhz, char*pBand, int*pChannel)
{
for (size_t i = 0; i < sizeof(mhzToChannels)/sizeof(mhzToChannels[0]); ++i)
{
if (std::abs(mhz-mhzToChannels[i].mhz) <= 1) {
*pBand = mhzToChannels[i].band;
*pChannel = mhzToChannels[i].channel;
return true;
}
}
return false;
}
static nla_policy *makeFreqPolicy()
{
nla_policy* result = new nla_policy[NL80211_FREQUENCY_ATTR_MAX + 1];
memset(result,0,sizeof(nla_policy[NL80211_FREQUENCY_ATTR_MAX + 1]));
result[NL80211_FREQUENCY_ATTR_FREQ] = {NLA_U32,0,0};
result[NL80211_FREQUENCY_ATTR_DISABLED] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_NO_IR] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_RADAR] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_INDOOR_ONLY] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_INDOOR_ONLY] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_NO_20MHZ] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_NO_20MHZ] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_NO_80MHZ] = {NLA_FLAG,0,0};
result[NL80211_FREQUENCY_ATTR_NO_160MHZ] = {NLA_FLAG,0,0};
return result;
}
static struct nla_policy* freq_policy = makeFreqPolicy();
static nla_policy *makeRatePolicy() {
nla_policy* result = new nla_policy[NL80211_BITRATE_ATTR_MAX + 1];
memset(result,0,sizeof(nla_policy[NL80211_BITRATE_ATTR_MAX + 1]));
result[NL80211_BITRATE_ATTR_RATE] = {.type = NLA_U32};
result[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = {.type = NLA_FLAG};
return result;
};
static struct nla_policy* rate_policy = makeRatePolicy();
struct IfType {
enum nl80211_iftype iftype;
const char*name;
};
static struct IfType ifTypes[] = {
{NL80211_IFTYPE_UNSPECIFIED,"Unspecified"},
{NL80211_IFTYPE_ADHOC,"Adhoc"},
{NL80211_IFTYPE_STATION,"Station"},
{NL80211_IFTYPE_AP,"AP"},
{NL80211_IFTYPE_AP_VLAN,"VLAN"},
{NL80211_IFTYPE_WDS,"WDS"},
{NL80211_IFTYPE_MONITOR,"Monitor"},
{NL80211_IFTYPE_MESH_POINT,"Mesh point"},
{NL80211_IFTYPE_P2P_CLIENT,"Client"},
{NL80211_IFTYPE_P2P_GO,"P2P Go"},
{NL80211_IFTYPE_P2P_DEVICE,"P2P"},
{NL80211_IFTYPE_OCB,"OCB"},
{NL80211_IFTYPE_NAN,"NAN"},
};
const char *iftype_name(enum nl80211_iftype iftype)
{
for (size_t i = 0; i < sizeof(ifTypes)/sizeof(ifTypes[0]); ++i)
{
if (ifTypes[i].iftype == iftype)
{
return ifTypes[i].name;
}
}
return "Unknown";
}
static int phy_lookup(const char *name)
{
char buf[200];
int fd, pos;
snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
fd = open(buf, O_RDONLY);
pos = read(fd, buf, sizeof(buf) - 1);
if (pos < 0)
return -1;
buf[pos] = '\0';
return atoi(buf);
}
static int getWifiName_callback(struct nl_msg *msg, void *arg);
static int getWifiInfo_callback(struct nl_msg *msg, void *arg);
struct WifiChannelInfo
{
int channelNumber = -1;
char band = 'g'; // 'g' or 'a'
int mhz = 0;
bool disabled = false;
bool ir = true;
bool radarDetection = false;
std::vector<float> bitrates;
bool indoorOnly = false;
bool noHt40Minus = false;
bool noHt40Plus = false;
bool no10MHz = false;
bool no20MHz = false;
bool no80MHz = false;
bool no160MHz = false;
};
struct WifiBand
{
int index;
std::vector<WifiChannelInfo> channels;
};
struct WifiInfo
{
std::string name;
std::vector<WifiBand> bands;
std::vector<std::string> supportedIfTypes;
};
class NetLink
{
private:
std::vector<WifiBand> result;
nl_sock*socket = nullptr;
nl_cache*cache = nullptr;
genl_family*nl80211 = nullptr;
int errorCode = -1;
static int finish_handler(struct nl_msg *msg, void *arg)
{
NetLink*this_ = (NetLink*)arg;
this_->errorCode = 0;
return NL_SKIP;
}
static int ack_handler(struct nl_msg *msg, void *arg)
{
NetLink*this_ = (NetLink*)arg;
this_->errorCode = 0;
return NL_STOP;
}
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
NetLink*this_ = (NetLink*)arg;
this_->errorCode = err->error;
return NL_STOP;
}
static int info_phy_handler(struct nl_msg *msg, void *arg)
{
return ((NetLink *)arg)->InfoPhyHandler(msg, (WifiInfo *)arg);
}
int InfoPhyHandler(struct nl_msg *msg, WifiInfo *result)
{
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = (genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
struct nlattr *nl_band;
struct nlattr *nl_freq;
struct nlattr *nl_rate;
struct nlattr *nl_mode;
int bandidx = 1;
int rem_band, rem_freq, rem_rate, rem_mode;
int open;
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
return NL_SKIP;
if (tb_msg[NL80211_ATTR_WIPHY_NAME])
result->name = nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME]);
nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
{
WifiBand band;
band.index = bandidx;
bandidx++;
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, (nlattr *)nla_data(nl_band),
nla_len(nl_band), NULL);
nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq)
{
nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, (nlattr *)nla_data(nl_freq),
nla_len(nl_freq), freq_policy);
if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
continue;
WifiChannelInfo channel;
channel.mhz = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
open = 0;
if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
channel.disabled = true;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
channel.ir = false;
if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
channel.radarDetection = true;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ])
channel.no10MHz = true;
if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
channel.indoorOnly = true;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
channel.no20MHz = true;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
channel.noHt40Minus = true;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
channel.noHt40Plus = true;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
channel.no80MHz = true;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
channel.no160MHz = true;
if (MHzToChannelNumber(channel.mhz,&channel.band, &channel.channelNumber))
{
band.channels.push_back(std::move(channel));
} else {
// throw PiPedalException("Frequency not recognized.");
}
}
nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate)
{
nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, (nlattr *)nla_data(nl_rate),
nla_len(nl_rate), rate_policy);
if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
continue;
//channel.bitRates.push_back(0.1f * nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
}
result->bands.push_back(std::move(band));
}
if (!tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES])
return NL_SKIP;
nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode)
result->supportedIfTypes.push_back(iftype_name((nl80211_iftype)(nl_mode->nla_type)));
return NL_SKIP;
}
public:
~NetLink()
{
if (this->cache)
{
nl_cache_free(cache);
cache = nullptr;
}
if (nl80211 != nullptr)
{
genl_family_put(nl80211); // (release reference)
nl80211 = nullptr;
}
if (socket)
{
nl_close(socket);
nl_socket_free(socket);
socket = nullptr;
}
}
NetLink()
{
socket = nl_socket_alloc();
if (!socket)
{
throw PiPedalException("Can't allocate netlink socket.");
}
nl_socket_set_buffer_size(socket, 8192, 8192);
if (genl_connect(socket))
{
throw PiPedalException("Failed to connect to netlink socket.");
}
if (genl_ctrl_alloc_cache(this->socket,&this->cache) != 0) {
throw PiPedalException("Failed to allocate generic netlink cache.");
}
this->nl80211 = genl_ctrl_search_by_name(this->cache, "nl80211");
if (!this->nl80211) {
throw PiPedalException("nl80211 family not found.");
}
}
void GetAvailableChannels(const char *phyName, WifiInfo *result)
{
struct nl_msg *msg = nullptr;
struct nl_cb*cb = nullptr;
int devidx = 0;
int err;
devidx = phy_lookup(phyName);
try {
msg = nlmsg_alloc();
if (!msg)
{
throw PiPedalException("failed to allocate netlink message");
}
cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb)
{
throw PiPedalException("failed to allocate netlink callbacks");
}
genlmsg_put(msg, 0, 0, genl_family_get_id(this->nl80211), 0,
0, NL80211_CMD_GET_WIPHY, 0);
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
this->errorCode = 0;
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, info_phy_handler, result);
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, this);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, this);
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, this);
err = nl_send_auto_complete(this->socket, msg);
if (err < 0)
throw PiPedalException("Command failed.");
nl_recvmsgs(this->socket, cb);
nlmsg_free(msg);
if (this->errorCode != 0)
{
throw PiPedalException("Command failed.");
}
nl_cb_put(cb);
return;
nla_put_failure:
throw PiPedalException("NLA Put failure.");
} catch (const std::exception&e)
{
if (cb != nullptr)
{
nl_cb_put(cb);
cb = nullptr;
}
if (msg != nullptr)
{
nlmsg_free(msg);
msg = nullptr;
}
throw;
}
}
};
WifiInfo getWifiInfo(const char *phyName)
{
int index = phy_lookup(phyName);
if (index == -1)
{
throw PiPedalArgumentException("No such device.");
}
NetLink netLink;
WifiInfo result;
netLink.GetAvailableChannels(phyName,&result);
return result;
}
const char*allowOutdoorsLocales[] = {
"US","TW","SG",
};
static bool allowOutdoors(const char*countryCode)
{
for (size_t i = 0; i < sizeof(allowOutdoorsLocales)/sizeof(allowOutdoorsLocales[0]); ++i)
{
if (strcmp(allowOutdoorsLocales[i],countryCode) == 0) return true;
}
return false;
}
static bool firstTime = true;
static std::unique_ptr<RegDb> g_regDb;
std::vector<WifiChannel> pipedal::getWifiChannels(const char*countryIso3661)
{
// Use the wifi physical device to get a list of channels
// that the hardware supports.
// The override the hardware's regulation flags using regulation.bin
// database info for the selected country code.
// If the regulation.bin database doesn't have a matching record,
// just pass the data that the wifi reported.
if (firstTime) {
firstTime = false;
try {
g_regDb = std::make_unique<RegDb>();
} catch (const std::exception& e)
{
std::stringstream s;
s << "Failed to open the Wi-Fi regulations.bin database. " << e.what();
Lv2Log::error(s.str());
}
}
std::vector<WifiChannel> result;
WifiInfo wifiInfo = getWifiInfo("phy0");
std::unique_ptr<CrdbRules> crdbRules = nullptr;
if (g_regDb) {
crdbRules = std::unique_ptr<CrdbRules>(g_regDb->GetCrdbRules(countryIso3661));
if (!crdbRules) {
crdbRules = std::unique_ptr<CrdbRules>(g_regDb->GetCrdbRules("00")); // unset default.
}
}
for (auto &band : wifiInfo.bands)
{
for (auto &channel: band.channels) {
if (crdbRules)
{
FrequencyRule crdbRule;
if (crdbRules->GetFrequencyRule(channel.mhz,&crdbRule))
{
channel.disabled = false;
channel.radarDetection = crdbRule.hasFlag(RegRuleFlags::DFS);
channel.ir = !crdbRule.hasFlag(RegRuleFlags::NO_IR);
channel.indoorOnly = crdbRule.hasFlag(RegRuleFlags::NO_OUTDOOR);
if (crdbRule.hasFlag(RegRuleFlags::PTP_ONLY)
|| crdbRule.hasFlag(RegRuleFlags::PTMP_ONLY))
{
channel.disabled = true;
}
} else {
channel.disabled = true;
}
}
if ((!channel.disabled) && (!channel.radarDetection) && channel.ir)
{
WifiChannel ch;
std::stringstream s;
s << channel.band << channel.channelNumber;
ch.channelId_ = s.str();
std::stringstream t;
t << channel.channelNumber;
if (channel.band == 'a')
t << " (5GHz)";
else
t << " (2.4GHz)";
if (channel.indoorOnly) {
t << " Indoor only";
}
ch.channelName_ = t.str();
result.push_back(ch);
}
}
}
return result;
}
JSON_MAP_BEGIN(WifiChannel)
JSON_MAP_REFERENCE(WifiChannel,channelId)
JSON_MAP_REFERENCE(WifiChannel,channelName)
JSON_MAP_END()
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "json.hpp"
namespace pipedal {
class WifiChannel {
public:
std::string channelId_;
std::string channelName_;
DECLARE_JSON_MAP(WifiChannel);
};
std::vector<WifiChannel> getWifiChannels(const char*countryIso3661);
}
+18
View File
@@ -0,0 +1,18 @@
#include "pch.h"
#include "catch.hpp"
#include <sstream>
#include <cstdint>
#include <string>
#include "WifiChannels.hpp"
using namespace pipedal;
TEST_CASE( "Wifi Channel Test", "[wifi_channels_test]" ) {
std::vector<WifiChannel> result = pipedal::getWifiChannels("CA");
}
+17
View File
@@ -0,0 +1,17 @@
#include "pch.h"
#include "WifiConfigSettings.hpp"
using namespace pipedal;
JSON_MAP_BEGIN(WifiConfigSettings)
JSON_MAP_REFERENCE(WifiConfigSettings,valid)
JSON_MAP_REFERENCE(WifiConfigSettings,wifiWarningGiven)
JSON_MAP_REFERENCE(WifiConfigSettings,rebootRequired)
JSON_MAP_REFERENCE(WifiConfigSettings,enable)
JSON_MAP_REFERENCE(WifiConfigSettings,hotspotName)
JSON_MAP_REFERENCE(WifiConfigSettings,hasPassword)
JSON_MAP_REFERENCE(WifiConfigSettings,password)
JSON_MAP_REFERENCE(WifiConfigSettings,countryCode)
JSON_MAP_REFERENCE(WifiConfigSettings,channel)
JSON_MAP_END()
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include "json.hpp"
namespace pipedal {
class WifiConfigSettings {
public:
bool valid_ = false;
bool wifiWarningGiven_ = false;
bool rebootRequired_ = false;
bool enable_ = false;
std::string countryCode_ = "US"; // iso 3661
std::string hotspotName_ = "pipedal";
bool hasPassword_ = false;
std::string password_;
std::string channel_ = "g6";
public:
DECLARE_JSON_MAP(WifiConfigSettings);
};
}
+2 -2
View File
@@ -5,9 +5,9 @@
#include <string>
#include <json.hpp>
#include "json.hpp"
using namespace piddle;
using namespace pipedal;
class JsonTestTarget {
private:
+5
View File
@@ -0,0 +1,5 @@
#include "pch.h"
#define CATCH_CONFIG_MAIN
#include <catch/catch.hpp>
uint16_t g_ShutdownPort = 0;
+10 -4
View File
@@ -3,9 +3,10 @@ Features:
Patches.
8 Updload/download patches.
9 Hotspot config.
9 Upload/download banks.
9 Delete bank warning dialo.
7 Settings dialog doesn't have back button nav.
- have to make sure it's set in the USER's environment.
@@ -30,8 +31,7 @@ pulseaudio --kill
Reconnect of controls after config change.
Layout of 2nd and subsequent splits.
Run when there's no midi device.
Crash when changing input channels to mono.
@@ -55,6 +55,12 @@ Open source copyrights.
add pipedal.local to etc/hosts.
/etc/dhcpcd.config:
nohook wpa_supplicant
Dependents:
libnl-3-dev libnl-genl-3-dev