diff options
-rw-r--r-- | .bzrignore | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 27 | ||||
-rw-r--r-- | apps/CMakeLists.txt | 3 | ||||
-rw-r--r-- | apps/test_multithread.c | 92 | ||||
-rw-r--r-- | docs/Doxyfile.in | 4 | ||||
-rw-r--r-- | docs/ToDo | 13 | ||||
-rw-r--r--[-rwxr-xr-x] | misc/xml/format.sh | 0 | ||||
-rwxr-xr-x | pci | 2 | ||||
-rw-r--r-- | pcilib/config.h.in | 1 | ||||
-rw-r--r-- | pcilib/error.c | 12 | ||||
-rw-r--r-- | pcilib/error.h | 14 | ||||
-rw-r--r-- | pcilib/pci.c | 15 | ||||
-rw-r--r-- | pcilib/pcilib.h | 14 | ||||
-rw-r--r-- | pcilib/property.c | 5 | ||||
-rw-r--r-- | pcilib/py.c | 370 | ||||
-rw-r--r-- | pcilib/py.h | 109 | ||||
-rw-r--r-- | pcilib/value.c | 22 | ||||
-rw-r--r-- | pcilib/view.c | 13 | ||||
-rw-r--r-- | pcilib/view.h | 2 | ||||
-rw-r--r-- | pcilib/xml.c | 24 | ||||
-rw-r--r-- | pcitool/cli.c | 2 | ||||
-rw-r--r-- | pywrap/CMakeLists.txt | 22 | ||||
-rw-r--r-- | pywrap/pcipywrap.c | 503 | ||||
-rw-r--r-- | pywrap/pcipywrap.h | 68 | ||||
-rw-r--r-- | pywrap/pcipywrap.i | 25 | ||||
-rw-r--r-- | pywrap/server.py | 399 | ||||
-rw-r--r-- | pywrap/test_pcipywrap.py | 119 | ||||
-rw-r--r-- | views/transform.c | 54 | ||||
-rw-r--r-- | views/transform.h | 1 | ||||
-rw-r--r-- | xml/test/props.xml | 2 | ||||
-rw-r--r-- | xml/test/test_prop2.py | 6 | ||||
-rw-r--r-- | xml/test/test_prop3.py | 5 | ||||
-rw-r--r-- | xml/types.xsd | 2 |
33 files changed, 1889 insertions, 64 deletions
@@ -33,3 +33,6 @@ Doxyfile html pcilib/build.h build.h +build +pcipywrap.py +pcipywrapPYTHON_wrap.c diff --git a/CMakeLists.txt b/CMakeLists.txt index fcd82b9..b77529e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,14 @@ project(pcitool) -set(PCILIB_VERSION "0.2.5") +set(PCILIB_VERSION "0.2.6") set(PCILIB_ABI_VERSION "2") -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 2.8) #set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH true) #set(CMAKE_PREFIX_PATH ${CMAKE_SYSTEM_PREFIX_PATH}) -set(DISABLE_PCITOOL FALSE CACHE BOOL "Build only the library") +set(DISABLE_PCITOOL FALSE CACHE BOOL "Build only the library") +set(DISABLE_PYTHON FALSE CACHE BOOL "Disable python scripting support") #list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") @@ -35,7 +36,12 @@ SET(ENV{PKG_CONFIG_PATH} "${LIB_INSTALL_DIR}/pkgconfig:$ENV{PKG_CONFIG_PATH}") find_package(PkgConfig REQUIRED) find_package(Threads REQUIRED) -find_package(PythonLibs REQUIRED) + +if (NOT DISABLE_PYTHON) + find_package(PythonLibs 2.7 REQUIRED) + find_package(SWIG REQUIRED) + set(HAVE_PYTHON TRUE) +endif (NOT DISABLE_PYTHON) set(EXTRA_SYSTEM_LIBS -lrt) @@ -91,16 +97,29 @@ add_subdirectory(pcitool) add_subdirectory(apps) add_subdirectory(xml) +if (HAVE_PYTHON) + add_subdirectory(pywrap) +endif (HAVE_PYTHON) + set_target_properties(pcilib PROPERTIES VERSION ${PCILIB_VERSION} SOVERSION ${PCILIB_ABI_VERSION} ) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/misc/pcitool.pc.in ${CMAKE_CURRENT_BINARY_DIR}/misc/pcitool.pc) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pcilib/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/pcilib/config.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pcilib/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/pcilib/version.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile) +if (NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + file(COPY ${CMAKE_SOURCE_DIR}/xml DESTINATION ${CMAKE_BINARY_DIR}) + file(COPY ${CMAKE_SOURCE_DIR}/pci + DESTINATION ${CMAKE_BINARY_DIR} + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + ) +endif(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/misc/pcitool.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 4524db4..45d627b 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -17,3 +17,6 @@ add_executable(compare_to_value compare_to_value.c) add_executable(heb_strip_bad_values heb_strip_bad_values.c) add_executable(check_counter check_counter.c) + +add_executable(test_multithread test_multithread.c) +target_link_libraries (test_multithread pcilib ${CMAKE_THREAD_LIBS_INIT}) diff --git a/apps/test_multithread.c b/apps/test_multithread.c new file mode 100644 index 0000000..4d0e8f2 --- /dev/null +++ b/apps/test_multithread.c @@ -0,0 +1,92 @@ +#include <stdio.h> +#include <pthread.h> +#include "pcilib.h" +#include <stdlib.h> + +const char* prop = "/registers/fpga/reg1"; +char* reg; +int stop = 0; + +void *get_prop(void *arg) +{ + pcilib_t *ctx = (pcilib_t*)arg; + + while(!stop) + { + int err; + pcilib_value_t val = {0}; + err = pcilib_get_property(ctx, prop, &val); + if(err) + { + printf("err pcilib_read_register\n"); + return NULL; + } + long value = pcilib_get_value_as_int(ctx, &val, &err); + pcilib_clean_value(ctx, &val); + if(err) + { + printf("err pcilib_get_value_as_int\n"); + return NULL; + } + printf("reg = %li\n", value); + } + return NULL; +} + +void *read_reg(void *arg) +{ + pcilib_t *ctx = (pcilib_t*)arg; + + while(!stop) + { + int err; + pcilib_register_value_t reg_val = {0}; + pcilib_value_t val = {0}; + + err = pcilib_read_register(ctx, NULL, reg, ®_val); + + if(err) + { + printf("err pcilib_read_register\n"); + return NULL; + } + err = pcilib_set_value_from_register_value(ctx, &val, reg_val); + if(err) + { + printf("err pcilib_set_value_from_register_value\n"); + return NULL; + } + long value = pcilib_get_value_as_int(ctx, &val, &err); + pcilib_clean_value(ctx, &val); + if(err) + { + printf("err pcilib_get_value_as_int\n"); + return NULL; + } + printf("reg = %li\n", value); + } + return NULL; +} + +int main(int argc, char *argv[]) +{ + if (argc < 5) { + printf("Usage:\n\t\t%s <device> <model> <register> <num_threads>\n", argv[0]); + exit(0); + } + + reg = argv[3]; + int threads = atoi( argv[4] ); + + pcilib_t *ctx = pcilib_open(argv[1], argv[2]); + + for(int i = 0; i < threads; i++) + { + pthread_t pth; + pthread_create(&pth, NULL, read_reg, ctx); + } + + getchar(); + stop = 1; + return 0; +} diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index ef98587..9303846 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -243,7 +243,7 @@ TCL_SUBST = # members will be omitted, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored @@ -1486,7 +1486,7 @@ MATHJAX_CODEFILE = # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. -SEARCHENGINE = NO +SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. There @@ -11,12 +11,11 @@ High Priority (we would need it for IPE Camera) Normal Priority (it would make just few things a bit easier) =============== - 1. Support Python-scripts in the views (we need to provide python API to read registers/properties) - 2. Integrate base streaming model into the pcitool - 3. Implement pcilib_configure_autotrigger - 4. Really check the specified min, max values while setting registers - 5. Provide OR and AND operations on registers in cli - 6. Support writting a data from a binary file in cli + 1. Integrate base streaming model into the pcitool + 2. Implement pcilib_configure_autotrigger + 3. Really check the specified min, max values while setting registers + 4. Provide OR and AND operations on registers in cli + 5. Support writting a data from a binary file in cli Low Priority (only as generalization for other projects) ============ @@ -25,6 +24,8 @@ Low Priority (only as generalization for other projects) 3. Define a syntax for register dependencies / delays (?) 4. Use pthread_condition_t instead of polling 5. Support FIFO reads/writes from/to registers + 6. OPC UA interface to the registers + 7. Generate XML models from SystemRDL descriptions Performance =========== diff --git a/misc/xml/format.sh b/misc/xml/format.sh index c522f44..c522f44 100755..100644 --- a/misc/xml/format.sh +++ b/misc/xml/format.sh @@ -2,4 +2,4 @@ APP_PATH=`dirname $0` -PCILIB_MODEL_DIR="$APP_PATH/xml" LD_LIBRARY_PATH="$APP_PATH/pcilib" $APP_PATH/pcitool/pci $* +PYTHONPATH="$APP_PATH/pywrap:$PYTHONPATH" PCILIB_MODEL_DIR="$APP_PATH/xml" LD_LIBRARY_PATH="$APP_PATH/pcilib" $APP_PATH/pcitool/pci $* diff --git a/pcilib/config.h.in b/pcilib/config.h.in index bdd9ec3..cd8033c 100644 --- a/pcilib/config.h.in +++ b/pcilib/config.h.in @@ -3,3 +3,4 @@ #cmakedefine PCILIB_MODEL_DIR "${PCILIB_MODEL_DIR}" #cmakedefine PCILIB_DEBUG_DIR "${PCILIB_DEBUG_DIR}" #cmakedefine HAVE_STDATOMIC_H @HAVE_STDATOMIC_H@ +#cmakedefine HAVE_PYTHON diff --git a/pcilib/error.c b/pcilib/error.c index 06af292..ae8bacb 100644 --- a/pcilib/error.c +++ b/pcilib/error.c @@ -76,3 +76,15 @@ int pcilib_set_logger(pcilib_log_priority_t min_prio, pcilib_logger_t logger, vo return 0; } + +pcilib_logger_t pcilib_get_logger() { + return pcilib_logger; +} + +pcilib_log_priority_t pcilib_get_log_level() { + return pcilib_logger_min_prio; +} + +void* pcilib_get_logger_context() { + return pcilib_logger_argument; +} diff --git a/pcilib/error.h b/pcilib/error.h index a9f4c0b..051ecd8 100644 --- a/pcilib/error.h +++ b/pcilib/error.h @@ -40,6 +40,20 @@ extern "C" { void pcilib_log_message(const char *file, int line, pcilib_log_flags_t flags, pcilib_log_priority_t prio, const char *msg, ...); void pcilib_log_vmessage(const char *file, int line, pcilib_log_flags_t flags, pcilib_log_priority_t prio, const char *msg, va_list va); +/** + * Gets current logger function. + */ +pcilib_logger_t pcilib_get_logger(); + +/** + * Gets current logger min priority. + */ +pcilib_log_priority_t pcilib_get_log_level(); + +/** + * Gets current logger argument. + */ +void* pcilib_get_logger_context(); #ifdef __cplusplus } diff --git a/pcilib/pci.c b/pcilib/pci.c index eaf41ac..19165ba 100644 --- a/pcilib/pci.c +++ b/pcilib/pci.c @@ -191,6 +191,14 @@ pcilib_t *pcilib_open(const char *device, const char *model) { if (!ctx->model) ctx->model = strdup(model?model:"pci"); + + err = pcilib_py_add_script_dir(ctx, NULL); + if (err) { + pcilib_error("Error (%i) add script path to python path", err); + pcilib_close(ctx); + return NULL; + } + xmlerr = pcilib_init_xml(ctx, ctx->model); if ((xmlerr)&&(xmlerr != PCILIB_ERROR_NOTFOUND)) { @@ -198,6 +206,7 @@ pcilib_t *pcilib_open(const char *device, const char *model) { pcilib_close(ctx); return NULL; } + // We have found neither standard model nor XML if ((err)&&(xmlerr)) { @@ -219,7 +228,6 @@ pcilib_t *pcilib_open(const char *device, const char *model) { pcilib_close(ctx); return NULL; } - err = pcilib_init_event_engine(ctx); if (err) { pcilib_error("Error (%i) initializing event engine\n", err); @@ -305,8 +313,6 @@ void pcilib_close(pcilib_t *ctx) { if (ctx->event_plugin) pcilib_plugin_close(ctx->event_plugin); - - pcilib_free_py(ctx); if (ctx->locks.kmem) pcilib_free_locking(ctx); @@ -348,11 +354,12 @@ void pcilib_close(pcilib_t *ctx) { if (ctx->registers) free(ctx->registers); - + if (ctx->model) free(ctx->model); pcilib_free_xml(ctx); + pcilib_free_py(ctx); if (ctx->handle >= 0) close(ctx->handle); diff --git a/pcilib/pcilib.h b/pcilib/pcilib.h index 3e7cf2b..8ab8e9e 100644 --- a/pcilib/pcilib.h +++ b/pcilib/pcilib.h @@ -43,7 +43,8 @@ typedef enum { typedef enum { PCILIB_ACCESS_R = 1, /**< getting property is allowed */ PCILIB_ACCESS_W = 2, /**< setting property is allowed */ - PCILIB_ACCESS_RW = 3 + PCILIB_ACCESS_RW = 3, + PCILIB_ACCESS_INCONSISTENT = 0x10000 /**< inconsistent access, one will not read that one has written */ } pcilib_access_mode_t; typedef enum { @@ -54,6 +55,7 @@ typedef enum { PCILIB_REGISTER_RW1C = 5, PCILIB_REGISTER_W1I = 8, /**< writting 1 inversts the bit, writting 0 keeps the value */ PCILIB_REGISTER_RW1I = 9, + PCILIB_REGISTER_INCONSISTENT = 0x10000 /**< inconsistent register, writting and reading does not match */ } pcilib_register_mode_t; typedef enum { @@ -1256,6 +1258,16 @@ int pcilib_set_value_from_register_value(pcilib_t *ctx, pcilib_value_t *val, pci int pcilib_set_value_from_static_string(pcilib_t *ctx, pcilib_value_t *val, const char *str); /** + * Initializes the polymorphic value from the string. The string is copied. + * If `val` already contains the value, cleans it first. Therefore, before first usage the value should be always initialized to 0. + * @param[in] ctx - pcilib context + * @param[in,out] val - initialized polymorphic value + * @param[in] str - initializer + * @return - 0 on success or memory error + */ +int pcilib_set_value_from_string(pcilib_t *ctx, pcilib_value_t *value, const char *str); + +/** * Get the floating point value from the polymorphic type. May inmply impliced type conversion, * for isntance parsing the number from the string. Will return 0. and report an error if * conversion failed. diff --git a/pcilib/property.c b/pcilib/property.c index a7d1a61..dfab9a6 100644 --- a/pcilib/property.c +++ b/pcilib/property.c @@ -165,7 +165,6 @@ pcilib_property_info_t *pcilib_get_property_list(pcilib_t *ctx, const char *bran continue; } - dir = (struct dir_hash_s*)malloc(sizeof(struct dir_hash_s)); if (!dir) { err = PCILIB_ERROR_MEMORY; @@ -226,6 +225,10 @@ pcilib_property_info_t *pcilib_get_property_list(pcilib_t *ctx, const char *bran }; } + HASH_ITER(hh, dir_hash, dir, dir_tmp) { + HASH_DEL(dir_hash, dir); + free(dir); + } HASH_CLEAR(hh, dir_hash); memset(&info[pos], 0, sizeof(pcilib_property_info_t)); diff --git a/pcilib/py.c b/pcilib/py.c index 4256afc..9254df7 100644 --- a/pcilib/py.c +++ b/pcilib/py.c @@ -1,66 +1,333 @@ -#include <Python.h> - +#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> - +#include <alloca.h> #include "pci.h" #include "debug.h" #include "pcilib.h" #include "py.h" #include "error.h" +#include "config.h" + +#ifdef HAVE_PYTHON +# include <Python.h> +#endif /* HAVE_PYTHON */ + +#ifdef HAVE_PYTHON +typedef struct pcilib_script_s pcilib_script_t; + +struct pcilib_script_s { + const char *name; /**< Script name */ + PyObject *module; /**< PyModule object, contains script enviroment */ + UT_hash_handle hh; /**< hash */ +}; struct pcilib_py_s { - PyObject *main_module; - PyObject *global_dict; + int finalyze; /**< Indicates, that we are initialized from wrapper and should not destroy Python resources in destructor */ + PyObject *main_module; /**< Main interpreter */ + PyObject *global_dict; /**< Dictionary of main interpreter */ + PyObject *pcilib_pywrap; /**< pcilib wrapper module */ + pcilib_script_t *script_hash; /**< Hash with loaded scripts */ }; +#endif /* HAVE_PYTHON */ + +void pcilib_log_python_error(const char *file, int line, pcilib_log_flags_t flags, pcilib_log_priority_t prio, const char *msg, ...) { + va_list va; + const char *type = NULL; + const char *val = NULL; + +#ifdef HAVE_PYTHON + PyObject *pytype = NULL; + PyObject *pyval = NULL; + PyObject *pytraceback = NULL; + + PyErr_Fetch(&pytype, &pyval, &pytraceback); + type = PyString_AsString(pytype); + val = PyString_AsString(pyval); +#endif /* HAVE_PYTHON */ + + va_start(va, msg); + if (type) { + char *str; + size_t len = 32; + + if (msg) len += strlen(msg); + if (type) len += strlen(type); + if (val) len += strlen(val); + + str = alloca(len * sizeof(char)); + if (str) { + if (msg&&val) + sprintf(str, "%s <%s: %s>", msg, type, val); + else if (msg) + sprintf(str, "%s <%s>", msg, type); + else if (val) + sprintf(str, "Python error %s: %s", type, val); + else + sprintf(str, "Python error %s", type); + + pcilib_log_vmessage(file, line, flags, prio, str, va); + } + } else { + pcilib_log_vmessage(file, line, flags, prio, msg, va); + } + va_end(va); + +#ifdef HAVE_PYTHON + Py_XDECREF(pytype); + Py_XDECREF(pyval); + Py_XDECREF(pytraceback); +#endif /* HAVE_PYTHON */ +} + + int pcilib_init_py(pcilib_t *ctx) { +#ifdef HAVE_PYTHON ctx->py = (pcilib_py_t*)malloc(sizeof(pcilib_py_t)); if (!ctx->py) return PCILIB_ERROR_MEMORY; - Py_Initialize(); + memset(ctx->py, 0, sizeof(pcilib_py_t)); + if(Py_IsInitialized()) + ctx->py->finalyze = 1; + else { + Py_Initialize(); + + // Since python is being initializing from c programm, it needs to initialize threads to work properly with c threads + PyEval_InitThreads(); + PyEval_ReleaseLock(); + } + ctx->py->main_module = PyImport_AddModule("__parser__"); - if (!ctx->py->main_module) + if (!ctx->py->main_module) { + pcilib_python_error("Error importing python parser"); return PCILIB_ERROR_FAILED; + } ctx->py->global_dict = PyModule_GetDict(ctx->py->main_module); - if (!ctx->py->global_dict) + if (!ctx->py->global_dict) { + pcilib_python_error("Error locating global python dictionary"); + return PCILIB_ERROR_FAILED; + } + + PyObject *pywrap = PyImport_ImportModule("pcipywrap"); + if (!pywrap) { + pcilib_python_error("Error importing pcilib python wrapper"); + return PCILIB_ERROR_FAILED; + } + + PyObject *mod_name = PyString_FromString("Pcipywrap"); + ctx->py->pcilib_pywrap = PyObject_CallMethodObjArgs(pywrap, mod_name, PyCObject_FromVoidPtr(ctx, NULL), NULL); + Py_XDECREF(mod_name); + + if (!ctx->py->pcilib_pywrap) { + pcilib_python_error("Error initializing python wrapper"); return PCILIB_ERROR_FAILED; + } +#endif /* HAVE_PYTHON */ + + return 0; +} + +int pcilib_py_add_script_dir(pcilib_t *ctx, const char *dir) { +#ifdef HAVE_PYTHON + PyObject* pypath; + char *script_dir; + + const char *model_dir = getenv("PCILIB_MODEL_DIR"); + if (!model_dir) model_dir = PCILIB_MODEL_DIR; + + if (!dir) dir = ctx->model; + + if (*dir == '/') { + script_dir = (char*)dir; + } else { + script_dir = alloca(strlen(model_dir) + strlen(dir) + 2); + if (!script_dir) return PCILIB_ERROR_MEMORY; + sprintf(script_dir, "%s/%s", model_dir, dir); + } + + pypath = PySys_GetObject("path"); + if (!pypath) { + pcilib_python_error("Can't get python path"); + return PCILIB_ERROR_FAILED; + } + + // Shall we check if the directory already in the path? + if(PyList_Append(pypath, PyString_FromString(script_dir)) == -1) { + pcilib_python_error("Can't add directory (%s) to python path", script_dir); + return PCILIB_ERROR_FAILED; + } +#endif /* HAVE_PYTHON */ return 0; } void pcilib_free_py(pcilib_t *ctx) { - if (ctx->py) { - // Dict and module references are borrowed +#ifdef HAVE_PYTHON + int finalyze = 0; + + if (ctx->py) { + if(ctx->py->finalyze) finalyze = 1; + + if (ctx->py->script_hash) { + pcilib_script_t *script, *script_tmp; + + HASH_ITER(hh, ctx->py->script_hash, script, script_tmp) { + HASH_DEL(ctx->py->script_hash, script); + free(script); + } + ctx->py->script_hash = NULL; + } + free(ctx->py); ctx->py = NULL; } + + if (finalyze) + Py_Finalize(); +#endif /* HAVE_PYTHON */ +} + +int pcilib_py_load_script(pcilib_t *ctx, const char *script_name) { +#ifdef HAVE_PYTHON + PyObject* pymodule; + pcilib_script_t *module = NULL; + + + char *module_name = strdupa(script_name); + if (!module_name) return PCILIB_ERROR_MEMORY; + + char *py = strrchr(module_name, '.'); + if ((!py)||(strcasecmp(py, ".py"))) { + pcilib_error("Invalid script name (%s) is specified", script_name); + return PCILIB_ERROR_INVALID_ARGUMENT; + } + *py = 0; + + HASH_FIND_STR(ctx->py->script_hash, script_name, module); + if (module) return 0; + + pymodule = PyImport_ImportModule(module_name); + if (!pymodule) { + pcilib_python_error("Error importing script (%s)", script_name); + return PCILIB_ERROR_FAILED; + } + + module = (pcilib_script_t*)malloc(sizeof(pcilib_script_t)); + if (!module) return PCILIB_ERROR_MEMORY; - Py_Finalize(); + module->module = pymodule; + module->name = script_name; + HASH_ADD_KEYPTR(hh, ctx->py->script_hash, module->name, strlen(module->name), module); +#endif /* HAVE_PYTHON */ + return 0; } -/* -static int pcilib_py_realloc_string(pcilib_t *ctx, size_t required, size_t *size, char **str) { - char *ptr; - size_t cur = *size; +int pcilib_py_get_transform_script_properties(pcilib_t *ctx, const char *script_name, pcilib_access_mode_t *mode_ret) { + pcilib_access_mode_t mode = 0; + +#ifdef HAVE_PYTHON + PyObject *dict; + PyObject *pystr; + pcilib_script_t *module; + + HASH_FIND_STR(ctx->py->script_hash, script_name, module); + + if(!module) { + pcilib_error("Script (%s) is not loaded yet", script_name); + return PCILIB_ERROR_NOTFOUND; + } + + dict = PyModule_GetDict(module->module); + if (!dict) { + pcilib_python_error("Error getting dictionary for script (%s)", script_name); + return PCILIB_ERROR_FAILED; + } - if ((required + 1) > cur) { - while (cur < required) cur *= 2; - ptr = (char*)realloc(*str, cur); - if (!ptr) return PCILIB_ERROR_MEMORY; - *size = cur; - *str = ptr; - } - ] + pystr = PyString_FromString("read_from_register"); + if (pystr) { + if (PyDict_Contains(dict, pystr)) mode |= PCILIB_ACCESS_R; + Py_XDECREF(pystr); + } + + pystr = PyString_FromString("write_to_register"); + if (pystr) { + if (PyDict_Contains(dict, pystr)) mode |= PCILIB_ACCESS_W; + Py_XDECREF(pystr); + } +#endif /* HAVE_PYTHON */ + + if (mode_ret) *mode_ret = mode; return 0; } -*/ +pcilib_py_object *pcilib_get_value_as_pyobject(pcilib_t* ctx, pcilib_value_t *val, int *ret) { +#ifdef HAVE_PYTHON + int err = 0; + PyObject *res = NULL; + + long ival; + double fval; + + switch(val->type) { + case PCILIB_TYPE_LONG: + ival = pcilib_get_value_as_int(ctx, val, &err); + if (!err) res = (PyObject*)PyInt_FromLong(ival); + break; + case PCILIB_TYPE_DOUBLE: + fval = pcilib_get_value_as_float(ctx, val, &err); + if (!err) res = (PyObject*)PyFloat_FromDouble(fval); + break; + default: + err = PCILIB_ERROR_NOTSUPPORTED; + pcilib_error("Can't convert pcilib value of type (%lu) to PyObject", val->type); + } + + if (err) { + if (ret) *ret = err; + return NULL; + } else if (!res) { + if (ret) *ret = PCILIB_ERROR_MEMORY; + return res; + } + + if (ret) *ret = 0; + return res; +#else /* HAVE_PYTHON */ + pcilib_error("Python is not supported"); + return NULL; +#endif /* HAVE_PYTHON */ +} + +int pcilib_set_value_from_pyobject(pcilib_t* ctx, pcilib_value_t *val, pcilib_py_object *pval) { +#ifdef HAVE_PYTHON + int err = 0; + PyObject *pyval = (PyObject*)pval; + + if (PyInt_Check(pyval)) { + err = pcilib_set_value_from_int(ctx, val, PyInt_AsLong(pyval)); + } else if (PyFloat_Check(pyval)) { + err = pcilib_set_value_from_float(ctx, val, PyFloat_AsDouble(pyval)); + } else if (PyString_Check(pyval)) { + err = pcilib_set_value_from_string(ctx, val, PyString_AsString(pyval)); + } else { + pcilib_error("Can't convert PyObject to polymorphic pcilib value"); + err = PCILIB_ERROR_NOTSUPPORTED; + } + + return err; +#else /* HAVE_PYTHON */ + pcilib_error("Python is not supported"); + return PCILIB_ERROR_NOTSUPPORTED; +#endif /* HAVE_PYTHON */ +} + +#ifdef HAVE_PYTHON static char *pcilib_py_parse_string(pcilib_t *ctx, const char *codestr, pcilib_value_t *value) { int i; int err = 0; @@ -166,8 +433,10 @@ static char *pcilib_py_parse_string(pcilib_t *ctx, const char *codestr, pcilib_v return dst; } +#endif /* HAVE_PYTHON */ int pcilib_py_eval_string(pcilib_t *ctx, const char *codestr, pcilib_value_t *value) { +#ifdef HAVE_PYTHON PyGILState_STATE gstate; char *code; PyObject* obj; @@ -189,4 +458,57 @@ int pcilib_py_eval_string(pcilib_t *ctx, const char *codestr, pcilib_value_t *va pcilib_debug(VIEWS, "Evaluating a Python string \'%s\' to %lf=\'%s\'", codestr, PyFloat_AsDouble(obj), code); return pcilib_set_value_from_float(ctx, value, PyFloat_AsDouble(obj)); +#else /* HAVE_PYTHON */ + pcilib_error("Current build not support python."); + return PCILIB_ERROR_NOTAVAILABLE; +#endif /* HAVE_PYTHON */ +} + +int pcilib_py_eval_func(pcilib_t *ctx, const char *script_name, const char *func_name, pcilib_value_t *val) { +#ifdef HAVE_PYTHON + int err = 0; + PyObject *pyfunc; + PyObject *pyval = NULL, *pyret; + pcilib_script_t *module = NULL; + + HASH_FIND_STR(ctx->py->script_hash, script_name, module); + + if (!module) { + pcilib_error("Script (%s) is not loaded", script_name); + return PCILIB_ERROR_NOTFOUND; + } + + if (val) { + pyval = pcilib_get_value_as_pyobject(ctx, val, &err); + if (err) return err; + } + + pyfunc = PyUnicode_FromString(func_name); + if (!pyfunc) { + if (pyval) Py_XDECREF(pyval); + return PCILIB_ERROR_MEMORY; + } + + PyGILState_STATE gstate = PyGILState_Ensure(); + pyret = PyObject_CallMethodObjArgs(module->module, pyfunc, ctx->py->pcilib_pywrap, pyval, NULL); + PyGILState_Release(gstate); + + Py_XDECREF(pyfunc); + Py_XDECREF(pyval); + + if (!pyret) { + pcilib_python_error("Error executing function (%s) of python script (%s)", func_name, script_name); + return PCILIB_ERROR_FAILED; + } + + if ((val)&&(pyret != Py_None)) + err = pcilib_set_value_from_pyobject(ctx, val, pyret); + + Py_XDECREF(pyret); + + return err; +#else /* HAVE_PYTHON */ + pcilib_error("Python is not supported"); + return PCILIB_ERROR_NOTSUPPORTED; +#endif /* HAVE_PYTHON */ } diff --git a/pcilib/py.h b/pcilib/py.h index 21c31e9..c372a09 100644 --- a/pcilib/py.h +++ b/pcilib/py.h @@ -1,16 +1,123 @@ #ifndef _PCILIB_PY_H #define _PCILIB_PY_H +#include <pcilib.h> +#include <pcilib/error.h> + +#define pcilib_python_error(...) pcilib_log_python_error(__FILE__, __LINE__, PCILIB_LOG_DEFAULT, PCILIB_LOG_ERROR, __VA_ARGS__) + typedef struct pcilib_py_s pcilib_py_t; +typedef void pcilib_py_object; #ifdef __cplusplus extern "C" { #endif +void pcilib_log_python_error(const char *file, int line, pcilib_log_flags_t flags, pcilib_log_priority_t prio, const char *msg, ...); + +/** Initializes Python engine + * + * This function will return success if Python support is disabled. Only functions + * executing python call, like pcilib_py_eval_string(), return errors. Either way, + * no script directories are configured. The pcilib_add_script_dir() call is used + * for this purpose. + * + * @param[in,out] ctx - pcilib context + * @return - error or 0 on success + */ int pcilib_init_py(pcilib_t *ctx); -int pcilib_py_eval_string(pcilib_t *ctx, const char *codestr, pcilib_value_t *value); + +/** Cleans up memory used by various python structures + * and finalyzes python environment if pcilib is not started from python script + * + * @param[in] ctx - the pcilib_t context + */ void pcilib_free_py(pcilib_t *ctx); +/** Add an additional path to look for python scripts + * + * The default location for python files is /usr/local/share/pcilib/models/@b{model}. + * This can be altered using CMake PCILIB_MODEL_DIR variable while building or using + * PCILIB_MODEL_DIR environmental variable dynamicly. The default location is added + * with @b{location} = NULL. Additional directories can be added as well either + * by specifying relative path from the default directory or absolute path in the + * system. + * + * @param[in,out] ctx - pcilib context + * @param[in] location - NULL or path to additional scripts + * @return - error or 0 on success + */ +int pcilib_py_add_script_dir(pcilib_t *ctx, const char *location); + +/** Loads the specified python script + * + * Once loaded the script is available until pcilib context is destryoed. + * + * @param[in,out] ctx - pcilib context + * @param[in] name - script name, the passed variable is referenced and, hence, should have static duration + * @return - error or 0 on success + */ +int pcilib_py_load_script(pcilib_t *ctx, const char *name); + +/** Check if the specified script can be used as transform view and detects transform configuration + * + * @param[in,out] ctx - pcilib context + * @param[in] name - script name + * @param[out] mode - supported access mode (read/write/read-write) + * @return - error or 0 on success + */ +int pcilib_py_get_transform_script_properties(pcilib_t *ctx, const char *name, pcilib_access_mode_t *mode); + +/** + * Get the PyObject from the polymorphic type. The returned value should be cleaned with Py_XDECREF() + * @param[in] ctx - pcilib context + * @param[in] val - initialized polymorphic value of arbitrary type + * @param[out] err - error code or 0 on sccuess + * @return - valid PyObject or NULL in the case of error + */ +pcilib_py_object *pcilib_get_value_as_pyobject(pcilib_t* ctx, pcilib_value_t *val, int *err); + +/** + * Initializes the polymorphic value from PyObject. If `val` already contains the value, cleans it first. + * Therefore, before first usage the value should be always initialized to 0. + * @param[in] ctx - pcilib context + * @param[in,out] val - initialized polymorphic value + * @param[in] pyval - valid PyObject* containing PyInt, PyFloat, or PyString + * @return - 0 on success or memory error + */ +int pcilib_set_value_from_pyobject(pcilib_t* ctx, pcilib_value_t *val, pcilib_py_object *pyval); + +/** Evaluates the specified python code and returns result in @b{val} + * + * The python code may include special variables which will be substituted by pcitool before executing Python interpreter + * @b{$value} - will be replaced by the current value of the @b{val} parameter + * @b{$reg} - will be replaced by the current value of the specified register @b{reg} + * @b{${/prop/temp}} - will be replaced by the current value of the specified property @b{/prop/temp} + * @b{${/prop/temp:C}} - will be replaced by the current value of the specified property @b{/prop/temp} in the given units + + * @param[in,out] ctx - pcilib context + * @param[in] codestr - python code to evaluate + * @param[in,out] val - Should contain the value which will be substituted in place of @b{$value} and on + * successful execution will contain the computed value + * @return - error or 0 on success + */ +int pcilib_py_eval_string(pcilib_t *ctx, const char *codestr, pcilib_value_t *val); + +/** Execute the specified function in the Python script which was loaded with pcilib_py_load_script() call + * + * The function is expected to accept two paramters. The first parameter is pcipywrap context and the @b{val} + * is passed as the second parameter. The return value of the script will be returned in the @b{val} as well. + * If function returns Py_None, the value of @b{val} will be unchanged. + * + * @param[in,out] ctx - pcilib context + * @param[in] script - script name + * @param[in] func - function name + * @param[in,out] val - Should contain the value of second parameter of the function before call and on + * successful return will contain the returned value + * @return - error or 0 on success + */ +int pcilib_py_eval_func(pcilib_t *ctx, const char *script, const char *func, pcilib_value_t *val); + #ifdef __cplusplus } #endif diff --git a/pcilib/value.c b/pcilib/value.c index 6e65307..e8268e9 100644 --- a/pcilib/value.c +++ b/pcilib/value.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -71,6 +72,27 @@ int pcilib_set_value_from_static_string(pcilib_t *ctx, pcilib_value_t *value, co return 0; } +int pcilib_set_value_from_string(pcilib_t *ctx, pcilib_value_t *value, const char *str) { + size_t len; + + pcilib_clean_value(ctx, value); + + len = strlen(str) + 1; + if (len < sizeof(value->str)) { + memcpy(value->str, str, len); + value->sval = value->str; + } else { + value->data = (void*)strdup(str); + if (!value->data) return PCILIB_ERROR_MEMORY; + + value->size = strlen(str) + 1; + value->sval = value->data; + } + value->type = PCILIB_TYPE_STRING; + + return 0; +} + double pcilib_get_value_as_float(pcilib_t *ctx, const pcilib_value_t *val, int *ret) { int err; double res; diff --git a/pcilib/view.c b/pcilib/view.c index e31fdba..985c1b2 100644 --- a/pcilib/view.c +++ b/pcilib/view.c @@ -69,8 +69,11 @@ int pcilib_add_views_custom(pcilib_t *ctx, size_t n, const pcilib_view_descripti return PCILIB_ERROR_MEMORY; } + memcpy(cur, v, v->api->description_size); + ctx->views[ctx->num_views + i] = cur; + if (v->api->init) - view_ctx = v->api->init(ctx); + view_ctx = v->api->init(ctx, ctx->num_views + i); else { view_ctx = (pcilib_view_context_t*)malloc(sizeof(pcilib_view_context_t)); if (view_ctx) memset(view_ctx, 0, sizeof(pcilib_view_context_t)); @@ -83,14 +86,12 @@ int pcilib_add_views_custom(pcilib_t *ctx, size_t n, const pcilib_view_descripti return PCILIB_ERROR_FAILED; } - memcpy(cur, v, v->api->description_size); - view_ctx->view = ctx->num_views + i; + view_ctx->view = ctx->num_views + i; view_ctx->name = v->name; - if (refs) refs[i] = view_ctx; - HASH_ADD_KEYPTR(hh, ctx->view_hash, view_ctx->name, strlen(view_ctx->name), view_ctx); - ctx->views[ctx->num_views + i] = cur; + + if (refs) refs[i] = view_ctx; ptr += v->api->description_size; } diff --git a/pcilib/view.h b/pcilib/view.h index 33d4d96..8b1c07c 100644 --- a/pcilib/view.h +++ b/pcilib/view.h @@ -19,7 +19,7 @@ typedef enum { typedef struct { pcilib_version_t version; /**< Version */ size_t description_size; /**< The actual size of the description */ - pcilib_view_context_t *(*init)(pcilib_t *ctx); /**< Optional function which should allocated context used by read/write functions */ + pcilib_view_context_t *(*init)(pcilib_t *ctx, pcilib_view_t view); /**< Optional function which should allocated context used by read/write functions */ void (*free)(pcilib_t *ctx, pcilib_view_context_t *view); /**< Optional function which should clean context */ void (*free_description)(pcilib_t *ctx, pcilib_view_description_t *view); /**< Optional function which shoud clean required parts of the extended description if non-static memory was used to initialize it */ int (*read_from_reg)(pcilib_t *ctx, pcilib_view_context_t *view, pcilib_register_value_t regval, pcilib_value_t *val); /**< Function which computes view value based on the passed the register value (view-based properties should not use register value) */ diff --git a/pcilib/xml.c b/pcilib/xml.c index 50aaa35..fd12636 100644 --- a/pcilib/xml.c +++ b/pcilib/xml.c @@ -40,6 +40,7 @@ #include "xml.h" #include "error.h" #include "view.h" +#include "py.h" #include "views/enum.h" #include "views/transform.h" @@ -48,11 +49,11 @@ #define REGISTERS_PATH ((xmlChar*)"./register") /**< all standard registers nodes */ #define BIT_REGISTERS_PATH ((xmlChar*)"./field") /**< all bits registers nodes */ #define REGISTER_VIEWS_PATH ((xmlChar*)"./view") /**< supported register & field views */ -#define TRANSFORM_VIEWS_PATH ((xmlChar*)"/model/transform") /**< path to complete nodes of views */ +#define TRANSFORM_VIEWS_PATH ((xmlChar*)"/model/transform") /**< path to complete nodes of views */ #define ENUM_VIEWS_PATH ((xmlChar*)"/model/enum") /**< path to complete nodes of views */ #define ENUM_ELEMENTS_PATH ((xmlChar*)"./name") /**< all elements in the enum */ #define UNITS_PATH ((xmlChar*)"/model/unit") /**< path to complete nodes of units */ -#define UNIT_TRANSFORMS_PATH ((xmlChar*)"./transform") /**< all transforms of the unit */ +#define UNIT_TRANSFORMS_PATH ((xmlChar*)"./transform") /**< all transforms of the unit */ @@ -492,6 +493,8 @@ static int pcilib_xml_parse_view(pcilib_t *ctx, xmlXPathContextPtr xpath, xmlDoc xmlAttrPtr cur; const char *value, *name; + int inconsistent = (desc->mode & PCILIB_ACCESS_INCONSISTENT); + for (cur = node->properties; cur != NULL; cur = cur->next) { if (!cur->children) continue; if (!xmlNodeIsText(cur->children)) continue; @@ -537,8 +540,14 @@ static int pcilib_xml_parse_view(pcilib_t *ctx, xmlXPathContextPtr xpath, xmlDoc pcilib_error("Invalid access mode (%s) is specified in the XML register description", value); return PCILIB_ERROR_INVALID_DATA; } - } + } else if (!strcasecmp(name, "write_verification")) { + if (strcmp(value, "0")) inconsistent = 0; + else inconsistent = 1; + } } + + if (inconsistent) desc->mode |= PCILIB_ACCESS_INCONSISTENT; + else desc->mode &= ~PCILIB_ACCESS_INCONSISTENT; return 0; } @@ -585,10 +594,12 @@ static int pcilib_xml_create_transform_view(pcilib_t *ctx, xmlXPathContextPtr xp } desc.write_to_reg = value; if ((value)&&(*value)) mode |= PCILIB_ACCESS_W; - } + } else if (!strcasecmp(name, "script")) { + desc.script = value; + break; + } } - - desc.base.mode &= mode; + desc.base.mode &= (~PCILIB_ACCESS_RW)|mode; err = pcilib_add_views_custom(ctx, 1, (pcilib_view_description_t*)&desc, &view_ctx); if (err) return err; @@ -597,7 +608,6 @@ static int pcilib_xml_create_transform_view(pcilib_t *ctx, xmlXPathContextPtr xp return 0; } - static int pcilib_xml_parse_value_name(pcilib_t *ctx, xmlXPathContextPtr xpath, xmlDocPtr doc, xmlNodePtr node, pcilib_register_value_name_t *desc) { xmlAttr *cur; char *value, *name; diff --git a/pcitool/cli.c b/pcitool/cli.c index bb3d9b8..3f1af74 100644 --- a/pcitool/cli.c +++ b/pcitool/cli.c @@ -1702,7 +1702,7 @@ int WriteRegister(pcilib_t *handle, const pcilib_model_description_t *model_info err = pcilib_write_register(handle, bank, reg, value); if (err) Error("Error writting register %s\n", reg); - if ((model_info->registers[regid].mode&PCILIB_REGISTER_RW) == PCILIB_REGISTER_RW) { + if ((model_info->registers[regid].mode&(PCILIB_REGISTER_RW|PCILIB_REGISTER_INCONSISTENT)) == PCILIB_REGISTER_RW) { const char *format = (val.format?val.format:"%u"); err = pcilib_read_register(handle, bank, reg, &verify); diff --git a/pywrap/CMakeLists.txt b/pywrap/CMakeLists.txt new file mode 100644 index 0000000..1693232 --- /dev/null +++ b/pywrap/CMakeLists.txt @@ -0,0 +1,22 @@ +include_directories( + ${CMAKE_SOURCE_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/pcilib + ${CMAKE_BINARY_DIR}/pcilib + ${LIBXML2_INCLUDE_DIRS} + ${PYTHON_INCLUDE_DIR} + ${UTHASH_INCLUDE_DIRS} +) + +set(HEADERS pcipywrap.h) + +#Creating python wrapping +include(${SWIG_USE_FILE}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +set(CMAKE_SWIG_FLAGS "") + +swig_add_module(pcipywrap python pcipywrap.i pcipywrap.c) +swig_link_libraries(pcipywrap ${PYTHON_LIBRARIES} pcilib) + +#configure_file(server.py server.py) +#configure_file(test_pcipywrap.py test_pcipywrap.py) diff --git a/pywrap/pcipywrap.c b/pywrap/pcipywrap.c new file mode 100644 index 0000000..bcf4561 --- /dev/null +++ b/pywrap/pcipywrap.c @@ -0,0 +1,503 @@ +#include "pcipywrap.h" + +char* full_log = NULL; + +/*! + * \brief Wraping for vsnprintf function, that saves string to char* + * \return saved from vsnprintf string + */ +char* vmake_str(const char* msg, va_list vl) +{ + char *buf; + size_t sz; + + va_list vl_copy; + va_copy(vl_copy, vl); + + sz = vsnprintf(NULL, 0, msg, vl); + buf = (char *)malloc(sz + 1); + + if(!buf) + { + return NULL; + } + + vsnprintf(buf, sz+1, msg, vl_copy); + va_end(vl_copy); + + return buf; +} + + +/*! + * \brief Wraping for vsnprintf function, that saves string to char* + * \return saved from vsnprintf string + */ +char* make_str(const char* msg, ...) +{ + va_list vl; + va_start(vl, msg); + char *buf = vmake_str(msg, vl); + va_end(vl); + return buf; +} + +/*! + * \brief Version of pcilib_logger_t, that saves error text to Python exeption + */ +void pcilib_print_error_to_py(void *arg, const char *file, int line, + pcilib_log_priority_t prio, const char *msg, + va_list va) { + //wrap error message with file and line number + char* buf_raw_msg = vmake_str(msg, va); + char* buf_wrapped_message = make_str("%s [%s:%d]\n", buf_raw_msg, file, line); + + if(prio == PCILIB_LOG_ERROR) + { + if(!full_log) + full_log = make_str(""); + + //copy received message to log + char* buf = full_log; + full_log = make_str("%s%s", buf, buf_wrapped_message); + free(buf); + } + else + printf("%s", buf_wrapped_message); + + free(buf_wrapped_message); + free(buf_raw_msg); +} + +void set_python_exception(const char* msg, ...) +{ + va_list vl; + va_start(vl, msg); + char *buf = vmake_str(msg, vl); + + char* wrapped_exeption; + if(full_log) + wrapped_exeption = make_str("%s\nprogramm error log:\n%s", buf, full_log); + else + wrapped_exeption = buf; + + free(full_log); + full_log = NULL; + + PyErr_SetString(PyExc_Exception, wrapped_exeption); + + free(buf); + if(full_log) + free(wrapped_exeption); + va_end(vl); +} + + +void __redirect_logs_to_exeption() +{ + pcilib_set_logger(pcilib_get_log_level(), + pcilib_print_error_to_py, + pcilib_get_logger_context()); +} + +/*! + * \brief Wrap for PyDict_SetItem, with decrease reference counting after set. + */ +void pcilib_pydict_set_item(PyObject* dict, PyObject* name, PyObject* value) +{ + PyDict_SetItem(dict, + name, + value); + Py_XDECREF(name); + Py_XDECREF(value); +} + +/*! + * \brief Wrap for PyList_Append, with decrease reference counting after append. + */ +void pcilib_pylist_append(PyObject* list, PyObject* value) +{ + PyList_Append(list, value); + Py_XDECREF(value); +} + +void add_pcilib_value_to_dict(pcilib_t* ctx, PyObject* dict, pcilib_value_t* val, const char *name) +{ + PyObject *py_val = (PyObject*)pcilib_get_value_as_pyobject(ctx, val, NULL); + + if(py_val) + pcilib_pydict_set_item(dict, + PyString_FromString(name), + py_val); + else + pcilib_pydict_set_item(dict, + PyString_FromString("defvalue"), + PyString_FromString("invalid")); +} + +PyObject * pcilib_convert_property_info_to_pyobject(pcilib_t* ctx, pcilib_property_info_t listItem) +{ + PyObject* pylistItem = PyDict_New(); + + if(listItem.name) + pcilib_pydict_set_item(pylistItem, + PyString_FromString("name"), + PyString_FromString(listItem.name)); + + if(listItem.description) + pcilib_pydict_set_item(pylistItem, + PyString_FromString("description"), + PyString_FromString(listItem.description)); + + if(listItem.path) + pcilib_pydict_set_item(pylistItem, + PyString_FromString("path"), + PyString_FromString(listItem.path)); + + //serialize types + const char* type = "invalid"; + switch(listItem.type) + { + case PCILIB_TYPE_INVALID: + type = "invalid"; + break; + case PCILIB_TYPE_STRING: + type = "string"; + break; + case PCILIB_TYPE_DOUBLE: + type = "double"; + break; + case PCILIB_TYPE_LONG : + type = "long"; + break; + default: + break; + } + pcilib_pydict_set_item(pylistItem, + PyString_FromString("type"), + PyString_FromString(type)); + + + //serialize modes + PyObject* modes = PyList_New(0); + + if((listItem.mode & PCILIB_ACCESS_R ) == PCILIB_REGISTER_R) + pcilib_pylist_append(modes, PyString_FromString("R")); + if((listItem.mode & PCILIB_ACCESS_W ) == PCILIB_REGISTER_W) + pcilib_pylist_append(modes, PyString_FromString("W")); + if((listItem.mode & PCILIB_ACCESS_RW ) == PCILIB_REGISTER_RW) + pcilib_pylist_append(modes, PyString_FromString("RW")); + if((listItem.mode & PCILIB_REGISTER_INCONSISTENT) == PCILIB_REGISTER_INCONSISTENT) + pcilib_pylist_append(modes, PyString_FromString("NO_CHK")); + + pcilib_pydict_set_item(pylistItem, + PyString_FromString("mode"), + modes); + + //serialize flags + PyObject* flags = PyList_New(0); + + if((listItem.flags & PCILIB_LIST_FLAG_CHILDS ) == PCILIB_LIST_FLAG_CHILDS) + pcilib_pylist_append(flags, PyString_FromString("childs")); + + pcilib_pydict_set_item(pylistItem, + PyString_FromString("flags"), + flags); + + if(listItem.unit) + pcilib_pydict_set_item(pylistItem, + PyString_FromString("unit"), + PyString_FromString(listItem.unit)); + + return pylistItem; +} + +PyObject * pcilib_convert_register_info_to_pyobject(pcilib_t* ctx, pcilib_register_info_t listItem) +{ + PyObject* pylistItem = PyDict_New(); + + if(listItem.name) + pcilib_pydict_set_item(pylistItem, + PyString_FromString("name"), + PyString_FromString(listItem.name)); + + if(listItem.description) + pcilib_pydict_set_item(pylistItem, + PyString_FromString("description"), + PyString_FromString(listItem.description)); + + if(listItem.bank) + pcilib_pydict_set_item(pylistItem, + PyString_FromString("bank"), + PyString_FromString(listItem.bank)); + + //serialize modes + PyObject* modes = PyList_New(0); + + if((listItem.mode & PCILIB_REGISTER_R) == PCILIB_REGISTER_R) + pcilib_pylist_append(modes, PyString_FromString("R")); + if((listItem.mode & PCILIB_REGISTER_W) == PCILIB_REGISTER_W) + pcilib_pylist_append(modes, PyString_FromString("W")); + if((listItem.mode & PCILIB_REGISTER_RW) == PCILIB_REGISTER_RW) + pcilib_pylist_append(modes, PyString_FromString("RW")); + if((listItem.mode & PCILIB_REGISTER_W1C) == PCILIB_REGISTER_W1C) + pcilib_pylist_append(modes, PyString_FromString("W1C")); + if((listItem.mode & PCILIB_REGISTER_RW1C) == PCILIB_REGISTER_RW1C) + pcilib_pylist_append(modes, PyString_FromString("RW1C")); + if((listItem.mode & PCILIB_REGISTER_W1I) == PCILIB_REGISTER_W1I) + pcilib_pylist_append(modes, PyString_FromString("W1I")); + if((listItem.mode & PCILIB_REGISTER_RW1I) == PCILIB_REGISTER_RW1I) + pcilib_pylist_append(modes, PyString_FromString("RW1I")); + if((listItem.mode & PCILIB_REGISTER_INCONSISTENT) == PCILIB_REGISTER_INCONSISTENT) + pcilib_pylist_append(modes, PyString_FromString("NO_CHK")); + + pcilib_pydict_set_item(pylistItem, + PyString_FromString("mode"), + modes); + + pcilib_value_t defval = {0}; + pcilib_set_value_from_register_value(ctx, &defval, listItem.defvalue); + add_pcilib_value_to_dict(ctx, pylistItem, &defval, "defvalue"); + + if(listItem.range) + { + pcilib_value_t minval = {0}; + pcilib_set_value_from_register_value(ctx, &minval, listItem.range->min); + + pcilib_value_t maxval = {0}; + pcilib_set_value_from_register_value(ctx, &maxval, listItem.range->max); + + PyObject* range = PyDict_New(); + add_pcilib_value_to_dict(ctx, range, &minval, "min"); + add_pcilib_value_to_dict(ctx, range, &maxval, "max"); + pcilib_pydict_set_item(pylistItem, + PyString_FromString("range"), + range); + } + + if(listItem.values) + { + PyObject* values = PyList_New(0); + + for (int j = 0; listItem.values[j].name; j++) + { + PyObject* valuesItem = PyDict_New(); + + pcilib_value_t val = {0}; + pcilib_set_value_from_register_value(ctx, &val, listItem.values[j].value); + + pcilib_value_t min = {0}; + pcilib_set_value_from_register_value(ctx, &min, listItem.values[j].min); + + pcilib_value_t max = {0}; + pcilib_set_value_from_register_value(ctx, &max, listItem.values[j].max); + + add_pcilib_value_to_dict(ctx, valuesItem, &val, "value"); + add_pcilib_value_to_dict(ctx, valuesItem, &min, "min"); + add_pcilib_value_to_dict(ctx, valuesItem, &max, "max"); + + if(listItem.values[j].name) + pcilib_pydict_set_item(valuesItem, + PyString_FromString("name"), + PyString_FromString(listItem.values[j].name)); + + if(listItem.values[j].description) + pcilib_pydict_set_item(valuesItem, + PyString_FromString("name"), + PyString_FromString(listItem.values[j].description)); + + pcilib_pylist_append(values, valuesItem); + } + + pcilib_pydict_set_item(pylistItem, + PyString_FromString("values"), + values); + } + + return pylistItem; +} + +Pcipywrap *new_Pcipywrap(const char* fpga_device, const char* model) +{ + //opening device + pcilib_t* ctx = pcilib_open(fpga_device, model); + if(!ctx) + { + set_python_exception("Failed pcilib_open(%s, %s)", fpga_device, model); + return NULL; + } + Pcipywrap *self; + self = (Pcipywrap *) malloc(sizeof(Pcipywrap)); + self->shared = 0; + self->ctx = ctx; + return self; +} + +Pcipywrap *create_Pcipywrap(PyObject* ctx) +{ + if(!PyCObject_Check(ctx)) + { + set_python_exception("Incorrect ctx type. Only PyCObject is allowed"); + return NULL; + } + + Pcipywrap *self; + self = (Pcipywrap *) malloc(sizeof(Pcipywrap)); + self->shared = 1; + self->ctx = PyCObject_AsVoidPtr(ctx); + return self; +} + +void delete_Pcipywrap(Pcipywrap *self) { + if(!self->shared) + pcilib_close(self->ctx); + + free(self); +} + +PyObject* Pcipywrap_read_register(Pcipywrap *self, const char *regname, const char *bank) +{ + pcilib_value_t val = {0}; + pcilib_register_value_t reg_value; + + int err; + + err = pcilib_read_register(self->ctx, bank, regname, ®_value); + if(err) + { + set_python_exception("Failed pcilib_read_register"); + return NULL; + } + + err = pcilib_set_value_from_register_value(self->ctx, &val, reg_value); + if(err) + { + set_python_exception("Failed pcilib_set_value_from_register_value"); + return NULL; + } + + return pcilib_get_value_as_pyobject(self->ctx, &val, NULL); +} + +PyObject* Pcipywrap_write_register(Pcipywrap *self, PyObject* val, const char *regname, const char *bank) +{ + pcilib_value_t val_internal = {0}; + pcilib_register_value_t reg_value; + + int err; + + err = pcilib_set_value_from_pyobject(self->ctx, &val_internal, val); + + if(err) + { + set_python_exception("Failed pcilib_set_value_from_pyobject"); + return NULL; + } + + + reg_value = pcilib_get_value_as_register_value(self->ctx, &val_internal, &err); + if(err) + { + set_python_exception("Failed pcilib_set_value_from_pyobject, (error %i)", err); + return NULL; + } + + err = pcilib_write_register(self->ctx, bank, regname, reg_value); + + if(err) + { + set_python_exception("Failed pcilib_set_value_from_pyobject, (error %i)", err); + return NULL; + } + + return PyInt_FromLong((long)1); +} + +PyObject* Pcipywrap_get_property(Pcipywrap *self, const char *prop) +{ + int err; + pcilib_value_t val = {0}; + + err = pcilib_get_property(self->ctx, prop, &val); + + if(err) + { + set_python_exception("Failed pcilib_get_property, (error %i)", err); + return NULL; + } + + return pcilib_get_value_as_pyobject(self->ctx, &val, NULL); +} + +PyObject* Pcipywrap_set_property(Pcipywrap *self, PyObject* val, const char *prop) +{ + int err; + + pcilib_value_t val_internal = {0}; + err = pcilib_set_value_from_pyobject(self->ctx, &val_internal, val); + if(err) + { + set_python_exception("pcilib_set_value_from_pyobject, (error %i)", err); + return NULL; + } + + err = pcilib_set_property(self->ctx, prop, &val_internal); + if(err) + { + set_python_exception("pcilib_set_property, (error %i)", err); + return NULL; + } + + return PyInt_FromLong((long)1); +} + +PyObject* Pcipywrap_get_registers_list(Pcipywrap *self, const char *bank) +{ + pcilib_register_info_t *list = pcilib_get_register_list(self->ctx, bank, PCILIB_LIST_FLAGS_DEFAULT); + + PyObject* pyList = PyList_New(0); + for(int i = 0; i < ((pcilib_t*)self->ctx)->num_reg; i++) + { + //serialize item attributes + PyObject* pylistItem = pcilib_convert_register_info_to_pyobject(self->ctx, list[i]); + pcilib_pylist_append(pyList, pylistItem); + } + + pcilib_free_register_info(self->ctx, list); + + return pyList; +} + +PyObject* Pcipywrap_get_register_info(Pcipywrap *self, const char* reg,const char *bank) +{ + pcilib_register_info_t *info = pcilib_get_register_info(self->ctx, bank, reg, PCILIB_LIST_FLAGS_DEFAULT); + + if(!info) + { + return NULL; + } + + PyObject* py_info = pcilib_convert_register_info_to_pyobject(self->ctx, info[0]); + + pcilib_free_register_info(self->ctx, info); + + return py_info; +} + +PyObject* Pcipywrap_get_property_list(Pcipywrap *self, const char* branch) +{ + pcilib_property_info_t *list = pcilib_get_property_list(self->ctx, branch, PCILIB_LIST_FLAGS_DEFAULT); + + PyObject* pyList = PyList_New(0); + + for(int i = 0; list[i].path; i++) + { + //serialize item attributes + PyObject* pylistItem = pcilib_convert_property_info_to_pyobject(self->ctx, list[i]); + pcilib_pylist_append(pyList, pylistItem); + } + + pcilib_free_property_info(self->ctx, list); + + return pyList; +} diff --git a/pywrap/pcipywrap.h b/pywrap/pcipywrap.h new file mode 100644 index 0000000..99cebd7 --- /dev/null +++ b/pywrap/pcipywrap.h @@ -0,0 +1,68 @@ +#ifndef PCIPYWRAP_H +#define PCIPYWRAP_H + +#include "pci.h" +#include "error.h" +#include <Python.h> + +typedef struct { + void* ctx; + int shared; +} Pcipywrap; + +/*! + * \brief Redirect pcilib standart log stream to exeption text. + * Logger will accumulate errors untill get message, starts with "#E". + * After that, logger will write last error, and all accumulated errors + * to Python exeption text + */ +void __redirect_logs_to_exeption(); + +/*! + * \brief Wraps for pcilib_open function. + * \param[in] fpga_device path to the device file [/dev/fpga0] + * \param[in] model specifies the model of hardware, autodetected if NULL is passed + * \return Pointer to pcilib_t, created by pcilib_open; NULL with exeption text, if failed. + */ +PyObject* create_pcilib_instance(const char *fpga_device, const char *model); + +Pcipywrap *new_Pcipywrap(const char* fpga_device, const char* model); +Pcipywrap *create_Pcipywrap(PyObject* ctx); +void delete_Pcipywrap(Pcipywrap *self); + +/*! + * \brief Reads register value. Wrap for pcilib_read_register function. + * \param[in] regname the name of the register + * \param[in] bank should specify the bank name if register with the same name may occur in multiple banks, NULL otherwise + * \return register value, can be integer or float type; NULL with exeption text, if failed. + */ +PyObject* Pcipywrap_read_register(Pcipywrap *self, const char *regname, const char *bank); + +/*! + * \brief Writes value to register. Wrap for pcilib_write_register function. + * \param[in] val Register value, that needs to be set. Can be int, float or string. + * \param[in] regname the name of the register + * \param[in] bank should specify the bank name if register with the same name may occur in multiple banks, NULL otherwise + * \return 1, serialized to PyObject or NULL with exeption text, if failed. + */ +PyObject* Pcipywrap_write_register(Pcipywrap *self, PyObject* val, const char *regname, const char *bank); + +/*! + * \brief Reads propety value. Wrap for pcilib_get_property function. + * \param[in] prop property name (full name including path) + * \return property value, can be integer or float type; NULL with exeption text, if failed. + */ +PyObject* Pcipywrap_get_property(Pcipywrap *self, const char *prop); + +/*! + * \brief Writes value to property. Wrap for pcilib_set_property function. + * \param[in] prop property name (full name including path) + * \param[in] val Property value, that needs to be set. Can be int, float or string. + * \return 1, serialized to PyObject or NULL with exeption text, if failed. + */ +PyObject* Pcipywrap_set_property(Pcipywrap *self, PyObject* val, const char *prop); +PyObject* Pcipywrap_get_registers_list(Pcipywrap *self, const char *bank); +PyObject* Pcipywrap_get_register_info(Pcipywrap *self, const char* reg,const char *bank); +PyObject* Pcipywrap_get_property_list(Pcipywrap *self, const char* branch); + +#endif /* PCIPYWRAP_H */ diff --git a/pywrap/pcipywrap.i b/pywrap/pcipywrap.i new file mode 100644 index 0000000..88a746f --- /dev/null +++ b/pywrap/pcipywrap.i @@ -0,0 +1,25 @@ +%module pcipywrap + +%{ +#include "pcipywrap.h" +%} + +extern void __redirect_logs_to_exeption(); + +typedef struct { + %extend { + Pcipywrap(const char* fpga_device = NULL, const char* model = NULL); + Pcipywrap(PyObject* ctx){return create_Pcipywrap(ctx);} + ~Pcipywrap(); + + PyObject* read_register(const char *regname = NULL, const char *bank = NULL); + PyObject* write_register(PyObject* val, const char *regname, const char *bank = NULL); + + PyObject* get_property(const char *prop); + PyObject* set_property(PyObject* val, const char *prop); + + PyObject* get_registers_list(const char *bank = NULL); + PyObject* get_register_info(const char* reg,const char *bank = NULL); + PyObject* get_property_list(const char* branch = NULL); + } +} Pcipywrap; diff --git a/pywrap/server.py b/pywrap/server.py new file mode 100644 index 0000000..03302a2 --- /dev/null +++ b/pywrap/server.py @@ -0,0 +1,399 @@ +import time +import os +import pcipywrap +import json +import BaseHTTPServer +import sys +from optparse import OptionParser + +class PcilibServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def __init__(s, pcilib, *args): + s.pcilib = pcilib + BaseHTTPServer.BaseHTTPRequestHandler.__init__(s, *args) + + def do_HEAD(s): + s.send_response(200) + s.send_header('content-type', 'application/json') + s.end_headers() + + def do_GET(s): + length = int(s.headers['Content-Length']) + + #deserialize input data + data = json.loads(s.rfile.read(length).decode('utf-8')) + + if 'command' in data: + command = data['command'] + if(command == 'help'): + s.help(data) + + #elif(command == 'open'): + # #check required arguments + # if not 'device' in data: + # s.error('message doesnt contains "device" field, ' + # 'which is required for "open" command', data) + # return + # #parse command arguments and convert them to string + # device = str(data.get('device', None)) + # model = data.get('model', None) + # if not model is None: + # model = str(model) + # + # try: + # s.openPcilibInstance(device, model) + # except Exception as e: + # s.error(str(e), data) + # return + # + # #Success! Create and send reply + # s.send_response(200) + # s.send_header('content-type', 'application/json') + # s.end_headers() + # out = dict() + # out['status'] = 'ok' + # s.wrapMessageAndSend(out, data) + + elif(command == 'get_registers_list'): + #parse command arguments and convert them to string + bank = data.get('bank', None) + if not bank is None: + bank = str(bank) + + registers = dict() + try: + registers = s.pcilib.get_registers_list(bank) + except Exception as e: + s.error(str(e), data) + return + + #Success! Create and send reply + s.send_response(200) + s.send_header('content-type', 'application/json') + s.end_headers() + out = dict() + out['status'] = 'ok' + out['registers'] = registers + s.wrapMessageAndSend(out, data) + + elif(command == 'get_register_info'): + #check required arguments + if not 'reg' in data: + s.error('message doesnt contains "reg" field, ' + 'which is required for "get_register_info" command', data) + return + + #parse command arguments and convert them to string + reg = str(data.get('reg', None)) + bank = data.get('bank', None) + if not bank is None: + bank = str(bank) + + register = dict() + try: + register = s.pcilib.get_register_info(reg, bank) + except Exception as e: + s.error(str(e), data) + return + + #Success! Create and send reply + s.send_response(200) + s.send_header('content-type', 'application/json') + s.end_headers() + out = dict() + out['status'] = 'ok' + out['register'] = register + s.wrapMessageAndSend(out, data) + + elif(command == 'get_property_list'): + #parse command arguments and convert them to string + branch = data.get('branch', None) + if not branch is None: + branch = str(branch) + + properties = dict() + try: + properties = s.pcilib.get_property_list(branch) + except Exception as e: + s.error(str(e), data) + return + + #Success! Create and send reply + s.send_response(200) + s.send_header('content-type', 'application/json') + s.end_headers() + out = dict() + out['status'] = 'ok' + out['properties'] = properties + s.wrapMessageAndSend(out, data) + + elif(command == 'read_register'): + #check required arguments + if not 'reg' in data: + s.error('message doesnt contains "reg" field, ' + 'which is required for "read_register" command', data) + return + + #parse command arguments and convert them to string + reg = str(data.get('reg', None)) + bank = data.get('bank', None) + if not bank is None: + bank = str(bank) + + value = 0 + try: + value = s.pcilib.read_register(reg, bank) + except Exception as e: + s.error(str(e), data) + return + + #Success! Create and send reply + s.send_response(200) + s.send_header('content-type', 'application/json') + s.end_headers() + out = dict() + out['status'] = 'ok' + out['value'] = value + s.wrapMessageAndSend(out, data) + + elif(command == 'write_register'): + #check required arguments + if not 'reg' in data: + s.error('message doesnt contains "reg" field, ' + 'which is required for "write_register" command', data) + return + + if not 'value' in data: + s.error('message doesnt contains "value" field, ' + 'which is required for "write_register" command', data) + return + + #parse command arguments and convert them to string + reg = str(data.get('reg', None)) + value = str(data.get('value', None)) + bank = data.get('bank', None) + if not bank is None: + bank = str(bank) + + try: + s.pcilib.write_register(value, reg, bank) + except Exception as e: + s.error(str(e), data) + return + + #Success! Create and send reply + s.send_response(200) + s.send_header('content-type', 'application/json') + s.end_headers() + out = dict() + out['status'] = 'ok' + s.wrapMessageAndSend(out, data) + + elif(command == 'get_property'): + #check required arguments + if not 'prop' in data: + s.error('message doesnt contains "prop" field, ' + 'which is required for "get_property" command', data) + return + + #parse command arguments and convert them to string + prop = str(data.get('prop', None)) + + value = 0 + try: + value = s.pcilib.get_property(prop) + except Exception as e: + s.error(str(e), data) + return + + #Success! Create and send reply + s.send_response(200) + s.send_header('content-type', 'application/json') + s.end_headers() + out = dict() + out['status'] = 'ok' + out['value'] = value + s.wrapMessageAndSend(out, data) + + elif(command == 'set_property'): + #check required arguments + if not 'prop' in data: + s.error('message doesnt contains "prop" field, ' + 'which is required for "set_property" command', data) + return + + if not 'value' in data: + s.error('message doesnt contains "value" field, ' + 'which is required for "set_property" command', data) + return + + #parse command arguments and convert them to string + prop = str(data.get('prop', None)) + value = str(data.get('value', None)) + + try: + s.pcilib.set_property(value, prop) + except Exception as e: + s.error(str(e), data) + return + + #Success! Create and send reply + s.send_response(200) + s.send_header('content-type', 'application/json') + s.end_headers() + out = dict() + out['status'] = 'ok' + s.wrapMessageAndSend(out, data) + + + else: + s.error('command "' + command + '" undefined', data) + return + else: + s.error('message doesnt contains "command" field, which is required', data) + return + + + #print str(s.headers['content-type']) + #print post_data['some'] + + """open device context """ + def openPcilibInstance(s, device, model): + s.pcilib = pcipywrap.create_pcilib_instance(device, model) + + """Send help message""" + def help(s, received_message = None): + s.send_response(200) + s.send_header('content-type', 'application/json') + s.end_headers() + usage = str('Usage:\n' + ' Server receive commands via http GET with json packet.\n' + ' content-type should have value "application/json"\n' + ' Server could handle only commands. to set command, you\n' + ' should specify field "command" in packet with command name\n' + ' List of commands:\n' + '\n' + ' command: help - Get help. This will return usage\n' + '\n' + + ' command: open - Opens context of device. It will be reopened if already open.\n' + ' required fields\n' + ' device: - path to the device file [/dev/fpga0]\n' + ' optional fields\n' + ' model: - specifies the model of hardware, autodetected if doesnt exists\n' + '\n' + + ' command: get_registers_list - Returns the list of registers provided by the hardware model.\n' + ' optional fields\n' + ' bank: - if set, only register within the specified bank will be returned\n' + '\n' + + ' command: get_register_info - Returns the information about the specified register.\n' + ' required fields\n' + ' reg: - the name of the register\n' + ' optional fields\n' + ' bank: - if set, only register within the specified bank will be returned\n' + '\n' + + ' command: get_property_list - Returns the list of properties available under the specified path.\n' + ' optional fields\n' + ' branch: - Path. If not set, will return the top-level properties\n' + '\n' + + ' command: read_register - Reads the specified register.\n' + ' required fields\n' + ' reg: - the name of the register\n' + ' optional fields\n' + ' bank: - if set, only register within the specified bank will be processed\n' + '\n' + + ' command: write_register - Writes to specified register.\n' + ' required fields\n' + ' reg: - the name of the register\n' + ' value: - the register value to write. Should be int, float or string (with number)\n' + ' optional fields\n' + ' bank: - if set, only register within the specified bank will be processed\n' + '\n' + + ' command: get_property - Reads / computes the property value.\n' + ' required fields\n' + ' prop: - full name including path\n' + '\n' + + ' command: set_property - Writes the property value or executes the code associated with property.\n' + ' required fields\n' + ' prop: - full name including path\n' + ' value: - the property value to write. Should be int, float or string (with number)\n' + '\n') + out = {'status': 'ok', 'usage' : usage} + s.wrapMessageAndSend(out, received_message) + + """Send error message with text description""" + def error(s, info, received_message = None): + s.send_response(400) + s.send_header('content-type', 'application/json') + s.end_headers() + out = dict() + + out['status'] = 'error' + out['description'] = info + out['note'] = 'send {"command" : "help"} to get help' + s.wrapMessageAndSend(out, received_message) + + def wrapMessageAndSend(s, message, received_message = None): + if not received_message is None: + message['received_message'] = received_message + s.wfile.write(json.dumps(message)) + +if __name__ == '__main__': + + #parce command line options + parser = OptionParser() + parser.add_option("-p", "--port", action="store", + type="int", dest="port", default=9000, + help="Set server port (9000)") + parser.add_option("-d", "--device", action="store", + type="string", dest="device", default=str('/dev/fpga0'), + help="FPGA device (/dev/fpga0)") + parser.add_option("-m", "--model", action="store", + type="string", dest="model", default=None, + help="Memory model (autodetected)") + opts = parser.parse_args()[0] + + HOST_NAME = '' + PORT_NUMBER = opts.port + MODEL = opts.model + DEVICE = opts.device + + #Set enviroment variables, if it not setted already + if not 'APP_PATH' in os.environ: + APP_PATH = '' + file_dir = os.path.dirname(os.path.abspath(__file__)) + APP_PATH = str(os.path.abspath(file_dir + '/../..')) + os.environ["APP_PATH"] = APP_PATH + + if not 'PCILIB_MODEL_DIR' in os.environ: + os.environ['PCILIB_MODEL_DIR'] = os.environ["APP_PATH"] + "/xml" + + if not 'LD_LIBRARY_PATH' in os.environ: + os.environ['LD_LIBRARY_PATH'] = os.environ["APP_PATH"] + "/pcilib" + + #redirect logs to exeption + pcipywrap.__redirect_logs_to_exeption() + + #start server + pcilib_server = BaseHTTPServer.HTTPServer + + #pass Pcipywrap to to server handler + lib = pcipywrap.Pcipywrap(DEVICE, MODEL) + def handler(*args): + PcilibServerHandler(lib, *args) + + httpd = pcilib_server((HOST_NAME, PORT_NUMBER), handler) + + print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER) + try: + httpd.serve_forever() + except KeyboardInterrupt: + pass + httpd.server_close() + print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER) diff --git a/pywrap/test_pcipywrap.py b/pywrap/test_pcipywrap.py new file mode 100644 index 0000000..257b4a5 --- /dev/null +++ b/pywrap/test_pcipywrap.py @@ -0,0 +1,119 @@ +import threading +import pcipywrap +import random +import os +import json +import requests +import time + +class test_pcipywrap(): + def __init__(self, device, model, num_threads = 150, + write_percentage = 0.1, register = 'test_prop2', + server_host = 'http://localhost', server_port = 12412, + server_message_delay = 0): + #initialize enviroment variables + if not 'APP_PATH' in os.environ: + APP_PATH = '' + file_dir = os.path.dirname(os.path.abspath(__file__)) + APP_PATH = str(os.path.abspath(file_dir + '/../..')) + os.environ["APP_PATH"] = APP_PATH + + if not 'PCILIB_MODEL_DIR' in os.environ: + os.environ['PCILIB_MODEL_DIR'] = os.environ["APP_PATH"] + "/xml" + if not 'LD_LIBRARY_PATH' in os.environ: + os.environ['LD_LIBRARY_PATH'] = os.environ["APP_PATH"] + "/pcilib" + + random.seed() + #create pcilib_instance + self.pcilib = pcipywrap.Pcipywrap(device, model) + self.num_threads = num_threads + self.write_percentage = write_percentage + self.register = register + self.server_message_delay = server_message_delay + self.server_port = server_port + self.server_host = server_host + + def testThreadSafeReadWrite(self): + def threadFunc(): + if random.randint(0, 100) >= (self.write_percentage * 100): + ret = self.pcilib.get_property('/test/prop2') + print self.register, ':', ret + del ret + else: + val = random.randint(0, 65536) + print 'set value:', val + self.pcilib.write_register(val, self.register) + try: + while(1): + thread_list = [threading.Thread(target=threadFunc) for i in range(0, self.num_threads)] + for i in range(0, self.num_threads): + thread_list[i].start() + for i in range(0, self.num_threads): + thread_list[i].join() + print 'cycle done' + except KeyboardInterrupt: + print 'testing done' + pass + + def testMemoryLeak(self): + try: + while(1): + #print self.pcilib.create_pcilib_instance('/dev/fpga0','test_pywrap') + + print self.pcilib.get_property_list('/test') + print self.pcilib.get_register_info('test_prop1') + #print self.pcilib.get_registers_list(); + + #print self.pcilib.read_register('reg1') + #print self.pcilib.write_register(12, 'reg1') + + #print self.pcilib.get_property('/test/prop2') + #print self.pcilib.set_property(12, '/test/prop2') + except KeyboardInterrupt: + print 'testing done' + pass + + def testServer(self): + url = str(self.server_host + ':' + str(self.server_port)) + headers = {'content-type': 'application/json'} + payload =[{'com': 'open', 'data2' : '12341'}, + #{'command': 'open', 'device' : '/dev/fpga0', 'model': 'test_pywrap'}, + {'command': 'help'}, + {'command': 'get_registers_list'}, + {'command': 'get_register_info', 'reg': 'reg1'}, + {'command': 'get_property_list'}, + {'command': 'read_register', 'reg': 'reg1'}, + {'command': 'write_register', 'reg': 'reg1'}, + {'command': 'get_property', 'prop': '/test/prop2'}, + {'command': 'set_property', 'prop': '/test/prop2'}] + + def sendRandomMessage(): + message_number = random.randint(1, len(payload) - 1) + print 'message number: ', message_number + payload[message_number]['value'] = random.randint(0, 65535) + r = requests.get(url, data=json.dumps(payload[message_number]), headers=headers) + print json.dumps(r.json(), sort_keys=True, indent=4, separators=(',', ': ')) + + try: + r = requests.get(url, data=json.dumps(payload[1]), headers=headers) + print json.dumps(r.json(), sort_keys=True, indent=3, separators=(',', ': ')) + + while(1): + time.sleep(self.server_message_delay) + thread_list = [threading.Thread(target=sendRandomMessage) for i in range(0, self.num_threads)] + for i in range(0, self.num_threads): + thread_list[i].start() + for i in range(0, self.num_threads): + thread_list[i].join() + print 'cycle done' + + except KeyboardInterrupt: + print 'testing done' + pass + +if __name__ == '__main__': + lib = test_pcipywrap('/dev/fpga0','test_pywrap', num_threads = 150, + write_percentage = 0.1, register = 'test_prop2',server_host = 'http://localhost', server_port = 12412, + server_message_delay = 0) + lib.testThreadSafeReadWrite() + diff --git a/views/transform.c b/views/transform.c index de7ee0e..f2d4b4a 100644 --- a/views/transform.c +++ b/views/transform.c @@ -9,7 +9,43 @@ #include "model.h" #include "transform.h" #include "py.h" +#include "error.h" +static pcilib_view_context_t * pcilib_transform_view_init(pcilib_t *ctx, pcilib_view_t view) { + int err; + + pcilib_view_context_t *view_ctx; + const pcilib_model_description_t *model_info = pcilib_get_model_description(ctx); + pcilib_transform_view_description_t *v = (pcilib_transform_view_description_t*)(model_info->views[view]); + + if(v->script) { + pcilib_access_mode_t mode = 0; + + err = pcilib_py_load_script(ctx, v->script); + if(err) { + pcilib_error("Error (%i), loading script %s", err, v->script); + return NULL; + } + + err = pcilib_py_get_transform_script_properties(ctx, v->script, &mode); + if(err) { + pcilib_error("Error (%i) obtaining properties of transform script %s", err, v->script); + return NULL; + } + + if ((v->base.mode&PCILIB_REGISTER_RW) == 0) + v->base.mode |= PCILIB_REGISTER_RW; + v->base.mode &= (~PCILIB_REGISTER_RW)|mode; + + if (!v->read_from_reg) v->read_from_reg = "read_from_register"; + if (!v->write_to_reg) v->write_to_reg = "write_to_register"; + } + + view_ctx = (pcilib_view_context_t*)malloc(sizeof(pcilib_view_context_t)); + if (view_ctx) memset(view_ctx, 0, sizeof(pcilib_view_context_t)); + + return view_ctx; +} static int pcilib_transform_view_read(pcilib_t *ctx, pcilib_view_context_t *view_ctx, pcilib_register_value_t regval, pcilib_value_t *val) { int err; @@ -20,7 +56,12 @@ static int pcilib_transform_view_read(pcilib_t *ctx, pcilib_view_context_t *view err = pcilib_set_value_from_register_value(ctx, val, regval); if (err) return err; - return pcilib_py_eval_string(ctx, v->read_from_reg, val); + if (v->script) + err = pcilib_py_eval_func(ctx, v->script, v->read_from_reg, val); + else + err = pcilib_py_eval_string(ctx, v->read_from_reg, val); + + return err; } static int pcilib_transform_view_write(pcilib_t *ctx, pcilib_view_context_t *view_ctx, pcilib_register_value_t *regval, const pcilib_value_t *val) { @@ -33,13 +74,16 @@ static int pcilib_transform_view_write(pcilib_t *ctx, pcilib_view_context_t *vie err = pcilib_copy_value(ctx, &val_copy, val); if (err) return err; - err = pcilib_py_eval_string(ctx, v->write_to_reg, &val_copy); + if (v->script) + err = pcilib_py_eval_func(ctx, v->script, v->write_to_reg, &val_copy); + else + err = pcilib_py_eval_string(ctx, v->write_to_reg, &val_copy); + if (err) return err; - + *regval = pcilib_get_value_as_register_value(ctx, &val_copy, &err); return err; } - const pcilib_view_api_description_t pcilib_transform_view_api = - { PCILIB_VERSION, sizeof(pcilib_transform_view_description_t), NULL, NULL, NULL, pcilib_transform_view_read, pcilib_transform_view_write }; + { PCILIB_VERSION, sizeof(pcilib_transform_view_description_t), pcilib_transform_view_init, NULL, NULL, pcilib_transform_view_read, pcilib_transform_view_write }; diff --git a/views/transform.h b/views/transform.h index f474552..774a019 100644 --- a/views/transform.h +++ b/views/transform.h @@ -6,6 +6,7 @@ typedef struct { pcilib_view_description_t base; + const char *script; /**< Python script module name */ const char *read_from_reg; /**< Formula explaining how to convert the register value to the view value */ const char *write_to_reg; /**< Formula explaining how to convert from the view value to the register value */ } pcilib_transform_view_description_t; diff --git a/xml/test/props.xml b/xml/test/props.xml index cf163eb..57702e2 100644 --- a/xml/test/props.xml +++ b/xml/test/props.xml @@ -1,4 +1,6 @@ <?xml version="1.0"?> <model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <transform path="/test/prop1" register="test_prop1" unit="C" read_from_register="(503975./1024000)*${/registers/fpga/sensor_temperature:C} - 27315./100" description="formula to get real fpga temperature from the fpga_temperature register in decimal"/> + <transform path="/test/prop2" register="test_prop2" unit="C" script="test_prop2.py" description="test python script #1" write_verification="0" /> + <transform path="/test/prop3" register="test_prop3" unit="C" script="test_prop3.py" description="test python script #2" /> </model> diff --git a/xml/test/test_prop2.py b/xml/test/test_prop2.py new file mode 100644 index 0000000..d78dbea --- /dev/null +++ b/xml/test/test_prop2.py @@ -0,0 +1,6 @@ +def read_from_register(ctx, value): + return ctx.get_property('/test/prop3') / 2 + +def write_to_register(ctx, value): + ctx.set_property(value*2, '/test/prop3') + diff --git a/xml/test/test_prop3.py b/xml/test/test_prop3.py new file mode 100644 index 0000000..a082096 --- /dev/null +++ b/xml/test/test_prop3.py @@ -0,0 +1,5 @@ +def read_from_register(ctx, value): + return ctx.get_property('/registers/fpga/reg1') + +def write_to_register(ctx, value): + ctx.set_property(value, '/registers/fpga/reg1') diff --git a/xml/types.xsd b/xml/types.xsd index 78be773..5fc8902 100644 --- a/xml/types.xsd +++ b/xml/types.xsd @@ -59,6 +59,7 @@ <xsd:attribute name="unit" type="xsd:string" /> <xsd:attribute name="type" type="pcilib_data_type_t" /> <xsd:attribute name="mode" type="pcilib_access_mode_t" /> + <xsd:attribute name="write_verification" type="bool_t" default="1"/> <xsd:attribute name="visible" type="bool_t" default="0" /> <xsd:attribute name="description" type="xsd:string" /> </xsd:complexType> @@ -70,6 +71,7 @@ <xsd:attribute name="register" type="xsd:string" /> <xsd:attribute name="read_from_register" type="xsd:string" /> <xsd:attribute name="write_to_register" type="xsd:string" /> + <xsd:attribute name="script" type="xsd:string" /> <!-- xsd 1.1 <xsd:assert test="(@path and not(@name)) or (not(@path) and @name)"/> --> </xsd:extension> </xsd:complexContent> |