From b26d819e9020a44047fbceb7e3d9e608c5687325 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Wed, 8 Mar 2023 19:22:42 +0100 Subject: [PATCH 01/27] use retry loop to handle concurrent multi process open --- ext/pylon/gstpylon.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/ext/pylon/gstpylon.cpp b/ext/pylon/gstpylon.cpp index 9440fd3..edbea8c 100644 --- a/ext/pylon/gstpylon.cpp +++ b/ext/pylon/gstpylon.cpp @@ -46,6 +46,12 @@ #include +/* retry open camera limits in case of collision with other + * process + */ +constexpr int FAILED_OPEN_RETRY_COUNT = 30; +constexpr int FAILED_OPEN_RETRY_WAIT_TIME_MS = 1000; + /* Pixel format definitions */ typedef struct { std::string pfnc_name; @@ -264,7 +270,23 @@ GstPylon *gst_pylon_new(GstElement *gstpylonsrc, const gchar *device_user_name, device_info = device_list.at(device_index); - self->camera->Attach(factory.CreateDevice(device_info)); + /* retry loop to start camera + * handles the cornercase of multiprocess pipelines started + * concurrently + */ + for (auto retry_idx = 0; retry_idx <= FAILED_OPEN_RETRY_COUNT; + retry_idx++) { + try { + self->camera->Attach(factory.CreateDevice(device_info)); + break; + } catch (GenICam::GenericException &e) { + GST_INFO_OBJECT(gstpylonsrc, "Failed to Open %s (%s)\n", + device_info.GetSerialNumber().c_str(), + e.GetDescription()); + /* wait for before new open attempt */ + g_usleep(FAILED_OPEN_RETRY_WAIT_TIME_MS * 1000); + } + } self->camera->Open(); /* Set the camera to a valid state */ From 4d6b1c9794163c84fd8963affea12335753bcaf0 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Thu, 9 Mar 2023 22:34:01 +0100 Subject: [PATCH 02/27] fix version generation --- meson.build | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/meson.build b/meson.build index 6db360b..7b86e48 100644 --- a/meson.build +++ b/meson.build @@ -13,30 +13,33 @@ gst_dep = dependency('gstreamer-1.0', version : gst_req) gst_version = gst_dep.version() message('Building against GStreamer ' + gst_version) +python3 = import('python').find_installation() +# generate project version from git info py_script = ''' import subprocess import sys, os meson_project_path = sys.argv[1] -git_dir = meson_project_path + "/.git" - -if not os.path.exists(git_dir): - sys.exit(1) +git_dir = os.path.join(meson_project_path, ".git") try: - cmd_result = subprocess.run(["git", "describe", "--tags"], capture_output=True) + cmd_result = subprocess.run( + ["git", "--git-dir=" + git_dir, "describe", "--tags", "--abbrev=7", "--dirty=+"], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + check=True, + ) vcs_tag = cmd_result.stdout.decode("utf-8")[1:-1] - print(vcs_tag) -except FileNotFoundError: +except (FileNotFoundError, subprocess.CalledProcessError): sys.exit(1) -''' +print(vcs_tag) +''' git_version = vcs_tag(command : ['python3', '-c', py_script, meson.project_source_root()], - input: 'version.h.in', output: 'version.h', replace_string: '@GIT_VERSION@', - fallback: meson.project_version() + '-local') - + input: 'version.h.in', output: 'version.h', replace_string: '@GIT_VERSION@', + fallback: meson.project_version() + '-local') version_arr = gst_version.split('.') gst_version_major = version_arr[0].to_int() @@ -257,7 +260,6 @@ endif presetdir = join_paths(get_option('datadir'), 'gstreamer-' + api_version, 'presets') -python3 = import('python').find_installation() pkgconfig = import('pkgconfig') plugins_pkgconfig_install_dir = join_paths(plugins_install_dir, 'pkgconfig') if get_option('default_library') == 'shared' From 99a0a90af900e1812a1146847a5c356654dfd7b3 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 27 Feb 2023 10:27:07 +0100 Subject: [PATCH 03/27] add clang-format style definition and update hook This allows to use a common .clang-format definition in editors and pre-commit hook. --- .clang-format | 18 ++++++++++++++++++ hooks/cpp-format | 30 +----------------------------- hooks/pre-commit.hook | 19 +++++++++---------- 3 files changed, 28 insertions(+), 39 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..aea8a5f --- /dev/null +++ b/.clang-format @@ -0,0 +1,18 @@ +BasedOnStyle: Google +IncludeBlocks: Regroup +IndentPPDirectives: AfterHash +SortIncludes: true +IncludeCategories: + - Regex: '^' + Priority: 3 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*' + Priority: 3 + SortPriority: 0 + - Regex: '.*' + Priority: 1 + SortPriority: 0 + diff --git a/hooks/cpp-format b/hooks/cpp-format index 57740e4..35e4069 100755 --- a/hooks/cpp-format +++ b/hooks/cpp-format @@ -19,33 +19,5 @@ else CLANG_INDENT=clang-format fi -CLANG_INDENT_STYLE="{ - BasedOnStyle: Google, - IncludeBlocks: Regroup, - IndentPPDirectives: AfterHash, - IncludeCategories: [ - { - Regex: '^', - Priority: 3 - }, - { - Regex: '^<.*\\\.h>', - Priority: 2 - }, - { - Regex: '^<.*', - Priority: 3 - }, - { - Regex: '.*', - Priority: 1 - } - ] -}" - -CLANG_INDENT_PARAMETERS="--style=\"${CLANG_INDENT_STYLE}\" \ - --sort-includes \ - -i" - echo "--Formatting ${file}--" -eval ${CLANG_INDENT} ${CLANG_INDENT_PARAMETERS} ${file} +eval ${CLANG_INDENT} -i --style=file ${file} diff --git a/hooks/pre-commit.hook b/hooks/pre-commit.hook index 724b47c..688ecad 100755 --- a/hooks/pre-commit.hook +++ b/hooks/pre-commit.hook @@ -3,9 +3,7 @@ # Check that the code follows a consistent code style # -# Check for existence of indent, and error out if not present. -# On some *bsd systems the binary seems to be called gnunindent, -# so check for that first. +# Check for existence of clang-format, and error out if not present. echo "--Checking style--" for file in `git diff-index --cached --name-only HEAD --diff-filter=ACMR| grep -e "\.h$" -e "\.cpp$"` ; do @@ -13,25 +11,25 @@ for file in `git diff-index --cached --name-only HEAD --diff-filter=ACMR| grep - # revision in the index (and not the checked out version). nf=`git checkout-index --temp ${file} | cut -f 1` newfile_name=`basename $file` - touch "/tmp/$newfile_name" || exit 1 - newfile="/tmp/$newfile_name" - cp "${nf}" "${newfile}" + check_dir=$(mktemp -d) + newfile="${check_dir}/${newfile_name}" - INDENT="hooks/cpp-format" + cp "${nf}" "${newfile}" + cp ".clang-format" ${check_dir} - eval ${INDENT} $newfile 2>> /dev/null + eval hooks/cpp-format ${newfile} 2>> /dev/null diff -u -p "${nf}" "${newfile}" r=$? - rm "${newfile}" + rm -r "${check_dir}" rm "${nf}" if [ $r != 0 ] ; then echo "=================================================================================================" echo " Code style error in: $file " echo " " echo " Please fix before committing. Don't forget to run git add before trying to commit again. " -echo " If the whole file is to be committed, run as : " +echo " If the whole file is to be committed, run as (scripts may be found in hooks/): " echo " " echo " hooks/cpp-format $file; git add $file; git commit" echo " " @@ -40,3 +38,4 @@ echo "========================================================================== fi done echo "--Checking style pass--" + From 031b9e9a798552e6b2801d4c85e1a671755f2cb7 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Tue, 21 Feb 2023 22:22:39 +0100 Subject: [PATCH 04/27] fix define clash on new gstreamer versions --- gst-libs/gst/pylon/gstpylon-prelude.h | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/pylon/gstpylon-prelude.h b/gst-libs/gst/pylon/gstpylon-prelude.h index 8f14674..5da085c 100644 --- a/gst-libs/gst/pylon/gstpylon-prelude.h +++ b/gst-libs/gst/pylon/gstpylon-prelude.h @@ -46,7 +46,7 @@ #ifndef EXT_PYLONSRC_API # ifdef BUILDING_EXT_PYLONSRC -# define EXT_PYLONSRC_API GST_API_EXPORT /* from config.h */ +# define EXT_PYLONSRC_API GST_PYLON_API_EXPORT /* from config.h */ # else # define EXT_PYLONSRC_API GST_API_IMPORT # endif diff --git a/meson.build b/meson.build index 7b86e48..5c420e1 100644 --- a/meson.build +++ b/meson.build @@ -171,7 +171,7 @@ else endif # Passing this through the command line would be too messy -cdata.set('GST_API_EXPORT', export_define) +cdata.set('GST_PYLON_API_EXPORT', export_define) warning_flags = [ '-Wmissing-declarations', From ae72092203eb3298a060e2dddd06b8fb89d8e5c6 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Sun, 12 Mar 2023 18:14:54 +0100 Subject: [PATCH 05/27] refactor to classes --- gst-libs/gst/pylon/gstpylonfeaturewalker.cpp | 58 +-- gst-libs/gst/pylon/gstpylonintrospection.cpp | 385 ++----------------- gst-libs/gst/pylon/gstpylonintrospection.h | 21 +- gst-libs/gst/pylon/gstpylonparamfactory.cpp | 254 ++++++++++++ gst-libs/gst/pylon/gstpylonparamfactory.h | 48 +++ gst-libs/gst/pylon/meson.build | 3 +- 6 files changed, 384 insertions(+), 385 deletions(-) create mode 100644 gst-libs/gst/pylon/gstpylonparamfactory.cpp create mode 100644 gst-libs/gst/pylon/gstpylonparamfactory.h diff --git a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp index a7e0a6f..3ac1568 100644 --- a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp +++ b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp @@ -36,7 +36,7 @@ #include "gstpylondebug.h" #include "gstpylonfeaturewalker.h" -#include "gstpylonintrospection.h" +#include "gstpylonparamfactory.h" #include @@ -46,16 +46,19 @@ #define MAX_INT_SELECTOR_ENTRIES 16 /* prototypes */ -static std::vector gst_pylon_get_enum_entries( +std::vector gst_pylon_get_enum_entries( GenApi::IEnumeration* enum_node); -static std::vector gst_pylon_get_int_entries( - GenApi::IInteger* int_node); -static std::vector gst_pylon_camera_handle_node( +std::vector gst_pylon_get_int_entries(GenApi::IInteger* int_node); +std::vector gst_pylon_camera_handle_node( GenApi::INode* node, GenApi::INodeMap& nodemap, const std::string& device_fullname, GstPylonCache& feature_cache); -static void gst_pylon_camera_install_specs( - const std::vector& specs_list, GObjectClass* oclass, - gint& nprop); +void gst_pylon_camera_install_specs(const std::vector& specs_list, + GObjectClass* oclass, gint& nprop); +std::vector gst_pylon_camera_handle_node( + GenApi::INode* node, GstPylonParamFactory& param_factory); +bool is_unsupported_feature(const std::string& feature_name); +bool is_unsupported_category(const std::string& category_name); +bool is_unsupported_selector(const std::string& feature_name); static const std::unordered_set propfilter_set = { "Width", @@ -91,26 +94,26 @@ static const std::unordered_set categoryfilter_set = { }; /* filter for features that are not supported */ -static bool is_unsupported_feature(const std::string& feature_name) { +bool is_unsupported_feature(const std::string& feature_name) { return propfilter_set.find(feature_name) != propfilter_set.end(); } /* filter for categories that are not supported */ -static bool is_unsupported_category(const std::string& category_name) { +bool is_unsupported_category(const std::string& category_name) { return categoryfilter_set.find(category_name) != categoryfilter_set.end(); } /* filter for selector nodes */ -static std::unordered_set selectorfilter_set = { +std::unordered_set selectorfilter_set = { "DeviceLinkSelector", }; /* filter for selectors and categories that are supported */ -static bool is_unsupported_selector(const std::string& feature_name) { +bool is_unsupported_selector(const std::string& feature_name) { return selectorfilter_set.find(feature_name) != selectorfilter_set.end(); } -static std::vector gst_pylon_get_enum_entries( +std::vector gst_pylon_get_enum_entries( GenApi::IEnumeration* enum_node) { GenApi::NodeList_t enum_entries; std::vector entry_names; @@ -138,8 +141,7 @@ static std::vector gst_pylon_get_enum_entries( return entry_names; } -static std::vector gst_pylon_get_int_entries( - GenApi::IInteger* int_node) { +std::vector gst_pylon_get_int_entries(GenApi::IInteger* int_node) { std::vector entry_names; g_return_val_if_fail(int_node, entry_names); @@ -226,9 +228,8 @@ std::vector GstPylonFeatureWalker::process_selector_features( return enum_values; } -static std::vector gst_pylon_camera_handle_node( - GenApi::INode* node, GenApi::INodeMap& nodemap, - const std::string& device_fullname, GstPylonCache& feature_cache) { +std::vector gst_pylon_camera_handle_node( + GenApi::INode* node, GstPylonParamFactory& param_factory) { GenApi::INode* selector_node = NULL; gint64 selector_value = 0; std::vector specs_list; @@ -261,22 +262,20 @@ static std::vector gst_pylon_camera_handle_node( } } - specs_list.push_back(GstPylonParamFactory::make_param( - nodemap, node, selector_node, selector_value, device_fullname, - feature_cache)); + specs_list.push_back( + param_factory.make_param(node, selector_node, selector_value)); } catch (const Pylon::GenericException& e) { - GST_FIXME("Unable to fully install property '%s-%s' on device \"%s\": %s", + GST_FIXME("Unable to fully install property '%s-%s' : %s", node->GetName().c_str(), enum_value.c_str(), - device_fullname.c_str(), e.GetDescription()); + e.GetDescription()); } } return specs_list; } -static void gst_pylon_camera_install_specs( - const std::vector& specs_list, GObjectClass* oclass, - gint& nprop) { +void gst_pylon_camera_install_specs(const std::vector& specs_list, + GObjectClass* oclass, gint& nprop) { g_return_if_fail(oclass); if (!specs_list.empty()) { @@ -300,6 +299,9 @@ void GstPylonFeatureWalker::install_properties( const std::string& device_fullname, GstPylonCache& feature_cache) { g_return_if_fail(oclass); + auto param_factory = + GstPylonParamFactory(nodemap, device_fullname, feature_cache); + gboolean is_cache_valid = feature_cache.IsCacheValid(); gint nprop = 1; @@ -324,8 +326,8 @@ void GstPylonFeatureWalker::install_properties( GenICam::gcstring attrib; try { - std::vector specs_list = gst_pylon_camera_handle_node( - node, nodemap, device_fullname, feature_cache); + std::vector specs_list = + gst_pylon_camera_handle_node(node, param_factory); gst_pylon_camera_install_specs(specs_list, oclass, nprop); diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index 4e96e46..696a1c0 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -68,88 +68,43 @@ class GstPylonTypeAction : public GstPylonActions { }; /* prototypes */ -static GParamSpec *gst_pylon_make_spec_int64(GenApi::INodeMap &nodemap, - GenApi::INode *node, - GstPylonCache &feature_cache); -static GParamSpec *gst_pylon_make_spec_selector_int64( - GenApi::INodeMap &nodemap, GenApi::INode *node, GenApi::INode *selector, - guint64 selector_value, GstPylonCache &feature_cache); -static GParamSpec *gst_pylon_make_spec_bool(GenApi::INodeMap &nodemap, - GenApi::INode *node); -static GParamSpec *gst_pylon_make_spec_selector_bool(GenApi::INodeMap &nodemap, - GenApi::INode *node, - GenApi::INode *selector, - guint64 selector_value); -static GParamSpec *gst_pylon_make_spec_double(GenApi::INodeMap &nodemap, - GenApi::INode *node, - GstPylonCache &feature_cache); -static GParamSpec *gst_pylon_make_spec_selector_double( - GenApi::INodeMap &nodemap, GenApi::INode *node, GenApi::INode *selector, - guint64 selector_value, GstPylonCache &feature_cache); -static GParamSpec *gst_pylon_make_spec_str(GenApi::INodeMap &nodemap, - GenApi::INode *node); -static GParamSpec *gst_pylon_make_spec_selector_str(GenApi::INodeMap &nodemap, - GenApi::INode *node, - GenApi::INode *selector, - guint64 selector_value); -static GType gst_pylon_make_enum_type(GenApi::INodeMap &nodemap, - GenApi::INode *node, - const std::string &device_fullname); -static GParamSpec *gst_pylon_make_spec_enum(GenApi::INodeMap &nodemap, - GenApi::INode *node, - const std::string &device_fullname); -static GParamSpec *gst_pylon_make_spec_selector_enum( - GenApi::INodeMap &nodemap, GenApi::INode *node, GenApi::INode *selector, - guint64 selector_value, const std::string &device_fullname); -static GenApi::INode *gst_pylon_find_limit_node(GenApi::INode *feature_node, - const GenICam::gcstring &limit); -static std::vector gst_pylon_find_parent_features( +GenApi::INode *gst_pylon_find_limit_node(GenApi::INode *feature_node, + const GenICam::gcstring &limit); +std::vector gst_pylon_find_parent_features( GenApi::INode *feature_node); -static void gst_pylon_add_all_property_values( +void gst_pylon_add_all_property_values( GenApi::INode *feature_node, std::string value, std::unordered_map &invalidators); -static std::vector gst_pylon_get_available_features( +std::vector gst_pylon_get_available_features( const std::set &feature_list); template -static std::vector> gst_pylon_cartesian_product( +std::vector> gst_pylon_cartesian_product( std::vector> &v); template -static T gst_pylon_check_for_feature_invalidators( +T gst_pylon_check_for_feature_invalidators( GenApi::INode *feature_node, GenApi::INode *limit_node, std::string limit, std::unordered_map &invalidators); template -static T gst_pylon_query_feature_limits(GenApi::INode *feature_node, - const std::string &limit); -static std::vector> -gst_pylon_create_set_value_actions( +T gst_pylon_query_feature_limits(GenApi::INode *feature_node, + const std::string &limit); +std::vector> gst_pylon_create_set_value_actions( const std::vector &node_list); template -static void gst_pylon_find_limits( - GenApi::INode *node, double &minimum_under_all_settings, - double &maximum_under_all_settings, - std::vector &invalidators_result); +void gst_pylon_find_limits(GenApi::INode *node, + double &minimum_under_all_settings, + double &maximum_under_all_settings, + std::vector &invalidators_result); template -static std::string gst_pylon_build_cache_value_string( - GParamFlags flags, T minimum_under_all_settings, - T maximum_under_all_settings); +std::string gst_pylon_build_cache_value_string(GParamFlags flags, + T minimum_under_all_settings, + T maximum_under_all_settings); -static void gst_pylon_query_feature_properties_double( - GenApi::INodeMap &nodemap, GenApi::INode *node, - GstPylonCache &feature_cache, GParamFlags &flags, - gdouble &minimum_under_all_settings, gdouble &maximum_under_all_settings, - GenApi::INode *selector = NULL, gint64 selector_value = 0); +gboolean gst_pylon_can_feature_later_be_writable(GenApi::INode *node); -static void gst_pylon_query_feature_properties_integer( - GenApi::INodeMap &nodemap, GenApi::INode *node, - GstPylonCache &feature_cache, GParamFlags &flags, - gint64 &minimum_under_all_settings, gint64 &maximum_under_all_settings, - GenApi::INode *selector = NULL, gint64 selector_value = 0); - -static gboolean gst_pylon_can_feature_later_be_writable(GenApi::INode *node); -static GParamFlags gst_pylon_query_access(GenApi::INodeMap &nodemap, - GenApi::INode *node); +std::vector gst_pylon_create_reset_value_actions( + const std::vector &node_list); -static gboolean gst_pylon_can_feature_later_be_writable(GenApi::INode *node) { +gboolean gst_pylon_can_feature_later_be_writable(GenApi::INode *node) { GenICam::gcstring value; GenICam::gcstring attribute; if (node->GetProperty("pIsLocked", value, attribute)) { @@ -166,8 +121,8 @@ static gboolean gst_pylon_can_feature_later_be_writable(GenApi::INode *node) { } } -static GParamFlags gst_pylon_query_access(GenApi::INodeMap &nodemap, - GenApi::INode *node) { +GParamFlags gst_pylon_query_access(GenApi::INodeMap &nodemap, + GenApi::INode *node) { gint flags = 0; g_return_val_if_fail(node, static_cast(flags)); @@ -211,8 +166,8 @@ static GParamFlags gst_pylon_query_access(GenApi::INodeMap &nodemap, return static_cast(flags); } -static GenApi::INode *gst_pylon_find_limit_node( - GenApi::INode *node, const GenICam::gcstring &limit) { +GenApi::INode *gst_pylon_find_limit_node(GenApi::INode *node, + const GenICam::gcstring &limit) { GenApi::INode *limit_node = NULL; GenICam::gcstring value; GenICam::gcstring attribute; @@ -228,7 +183,7 @@ static GenApi::INode *gst_pylon_find_limit_node( return limit_node; } -static std::vector gst_pylon_find_parent_features( +std::vector gst_pylon_find_parent_features( GenApi::INode *node) { std::vector parent_features; @@ -249,7 +204,7 @@ static std::vector gst_pylon_find_parent_features( return parent_features; } -static void gst_pylon_add_all_property_values( +void gst_pylon_add_all_property_values( GenApi::INode *node, std::string value, std::unordered_map &invalidators) { std::string delimiter = "\t"; @@ -271,7 +226,7 @@ static void gst_pylon_add_all_property_values( } } -static std::vector gst_pylon_get_available_features( +std::vector gst_pylon_get_available_features( const std::set &feature_list) { std::vector available_features; for (const auto &feature : feature_list) { @@ -283,7 +238,7 @@ static std::vector gst_pylon_get_available_features( } template -static std::vector> gst_pylon_cartesian_product( +std::vector> gst_pylon_cartesian_product( std::vector> &values) { std::vector> result; auto product = [](long long a, std::vector &b) { return a * b.size(); }; @@ -301,8 +256,8 @@ static std::vector> gst_pylon_cartesian_product( } template -static T gst_pylon_query_feature_limits(GenApi::INode *node, - const std::string &limit) { +T gst_pylon_query_feature_limits(GenApi::INode *node, + const std::string &limit) { g_return_val_if_fail(node, 0); P param(node); @@ -315,7 +270,7 @@ static T gst_pylon_query_feature_limits(GenApi::INode *node, } template -static T gst_pylon_check_for_feature_invalidators( +T gst_pylon_check_for_feature_invalidators( GenApi::INode *node, GenApi::INode *limit_node, std::string limit, std::unordered_map &invalidators) { T limit_under_all_settings = 0; @@ -336,8 +291,7 @@ static T gst_pylon_check_for_feature_invalidators( return limit_under_all_settings; } -static std::vector> -gst_pylon_create_set_value_actions( +std::vector> gst_pylon_create_set_value_actions( const std::vector &node_list) { std::vector> actions_list; @@ -403,7 +357,7 @@ gst_pylon_create_set_value_actions( return actions_list; } -static std::vector gst_pylon_create_reset_value_actions( +std::vector gst_pylon_create_reset_value_actions( const std::vector &node_list) { std::vector actions_list; @@ -449,9 +403,8 @@ static std::vector gst_pylon_create_reset_value_actions( } template -static void gst_pylon_find_limits(GenApi::INode *node, - T &minimum_under_all_settings, - T &maximum_under_all_settings) { +void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, + T &maximum_under_all_settings) { std::unordered_map invalidators; maximum_under_all_settings = 0; minimum_under_all_settings = 0; @@ -613,269 +566,3 @@ void gst_pylon_query_feature_properties_integer( g_free(feature_cache_name); } - -static GParamSpec *gst_pylon_make_spec_int64(GenApi::INodeMap &nodemap, - GenApi::INode *node, - GstPylonCache &feature_cache) { - g_return_val_if_fail(node, NULL); - - Pylon::CIntegerParameter param(node); - gint64 max_value = 0; - gint64 min_value = 0; - GParamFlags flags = G_PARAM_READABLE; - - gst_pylon_query_feature_properties_integer(nodemap, node, feature_cache, - flags, min_value, max_value); - - return g_param_spec_int64(node->GetName(), node->GetDisplayName(), - node->GetToolTip(), min_value, max_value, - param.GetValue(), flags); -} - -static GParamSpec *gst_pylon_make_spec_selector_int64( - GenApi::INodeMap &nodemap, GenApi::INode *node, GenApi::INode *selector, - guint64 selector_value, GstPylonCache &feature_cache) { - g_return_val_if_fail(node, NULL); - g_return_val_if_fail(selector, NULL); - - Pylon::CIntegerParameter param(node); - gint64 max_value = 0; - gint64 min_value = 0; - GParamFlags flags = G_PARAM_READABLE; - - gst_pylon_query_feature_properties_integer(nodemap, node, feature_cache, - flags, min_value, max_value, - selector, selector_value); - - return gst_pylon_param_spec_selector_int64( - nodemap, node->GetName(), selector->GetName(), selector_value, - node->GetDisplayName(), node->GetToolTip(), min_value, max_value, - param.GetValue(), flags); -} - -static GParamSpec *gst_pylon_make_spec_bool(GenApi::INodeMap &nodemap, - GenApi::INode *node) { - g_return_val_if_fail(node, NULL); - - Pylon::CBooleanParameter param(node); - - return g_param_spec_boolean(node->GetName(), node->GetDisplayName(), - node->GetToolTip(), param.GetValue(), - gst_pylon_query_access(nodemap, node)); -} - -static GParamSpec *gst_pylon_make_spec_selector_bool(GenApi::INodeMap &nodemap, - GenApi::INode *node, - GenApi::INode *selector, - guint64 selector_value) { - g_return_val_if_fail(node, NULL); - g_return_val_if_fail(selector, NULL); - - Pylon::CBooleanParameter param(node); - - return gst_pylon_param_spec_selector_boolean( - nodemap, node->GetName(), selector->GetName(), selector_value, - node->GetDisplayName(), node->GetToolTip(), param.GetValue(), - gst_pylon_query_access(nodemap, node)); -} - -static GParamSpec *gst_pylon_make_spec_double(GenApi::INodeMap &nodemap, - GenApi::INode *node, - GstPylonCache &feature_cache) { - g_return_val_if_fail(node, NULL); - - Pylon::CFloatParameter param(node); - gdouble max_value = 0; - gdouble min_value = 0; - GParamFlags flags = G_PARAM_READABLE; - - gst_pylon_query_feature_properties_double(nodemap, node, feature_cache, flags, - min_value, max_value); - - return g_param_spec_double(node->GetName(), node->GetDisplayName(), - node->GetToolTip(), min_value, max_value, - param.GetValue(), flags); -} - -static GParamSpec *gst_pylon_make_spec_selector_double( - GenApi::INodeMap &nodemap, GenApi::INode *node, GenApi::INode *selector, - guint64 selector_value, GstPylonCache &feature_cache) { - g_return_val_if_fail(node, NULL); - g_return_val_if_fail(selector, NULL); - - Pylon::CFloatParameter param(node); - gdouble max_value = 0; - gdouble min_value = 0; - GParamFlags flags = G_PARAM_READABLE; - - gst_pylon_query_feature_properties_double(nodemap, node, feature_cache, flags, - min_value, max_value, selector, - selector_value); - - return gst_pylon_param_spec_selector_double( - nodemap, node->GetName(), selector->GetName(), selector_value, - node->GetDisplayName(), node->GetToolTip(), min_value, max_value, - param.GetValue(), flags); -} - -static GParamSpec *gst_pylon_make_spec_str(GenApi::INodeMap &nodemap, - GenApi::INode *node) { - g_return_val_if_fail(node, NULL); - - Pylon::CStringParameter param(node); - - return g_param_spec_string(node->GetName(), node->GetDisplayName(), - node->GetToolTip(), param.GetValue(), - gst_pylon_query_access(nodemap, node)); -} - -static GParamSpec *gst_pylon_make_spec_selector_str(GenApi::INodeMap &nodemap, - GenApi::INode *node, - GenApi::INode *selector, - guint64 selector_value) { - g_return_val_if_fail(node, NULL); - g_return_val_if_fail(selector, NULL); - - Pylon::CStringParameter param(node); - - return gst_pylon_param_spec_selector_string( - nodemap, node->GetName(), selector->GetName(), selector_value, - node->GetDisplayName(), node->GetToolTip(), param.GetValue(), - gst_pylon_query_access(nodemap, node)); -} - -static GType gst_pylon_make_enum_type(GenApi::INodeMap &nodemap, - GenApi::INode *node, - const std::string &device_fullname) { - /* When registering enums to the GType system, their string pointers - must remain valid throughout the application lifespan. To achieve this - we are saving all found enums into a static hash table - */ - static std::unordered_map> persistent_values; - - g_return_val_if_fail(node, G_TYPE_INVALID); - - Pylon::CEnumParameter param(node); - - gchar *full_name = g_strdup_printf("%s_%s", device_fullname.c_str(), - node->GetName().c_str()); - std::string name = gst_pylon_param_spec_sanitize_name(full_name); - g_free(full_name); - - GType type = g_type_from_name(name.c_str()); - - if (!type) { - std::vector enumvalues; - GenApi::StringList_t values; - - param.GetSettableValues(values); - for (const auto &value_name : values) { - auto entry = param.GetEntryByName(value_name); - auto value = static_cast(entry->GetValue()); - auto tooltip = entry->GetNode()->GetToolTip(); - - /* We need a copy of the strings so that they are persistent - throughout the application lifespan */ - GEnumValue ev = {value, g_strdup(value_name.c_str()), - g_strdup(tooltip.c_str())}; - enumvalues.push_back(ev); - } - - GEnumValue sentinel = {0}; - enumvalues.push_back(sentinel); - - type = g_enum_register_static(name.c_str(), enumvalues.data()); - persistent_values.insert({type, std::move(enumvalues)}); - } - - return type; -} - -static GParamSpec *gst_pylon_make_spec_enum( - GenApi::INodeMap &nodemap, GenApi::INode *node, - const std::string &device_fullname) { - g_return_val_if_fail(node, NULL); - - Pylon::CEnumParameter param(node); - GType type = gst_pylon_make_enum_type(nodemap, node, device_fullname.c_str()); - - return g_param_spec_enum(node->GetName(), node->GetDisplayName(), - node->GetToolTip(), type, param.GetIntValue(), - gst_pylon_query_access(nodemap, node)); -} - -static GParamSpec *gst_pylon_make_spec_selector_enum( - GenApi::INodeMap &nodemap, GenApi::INode *node, GenApi::INode *selector, - guint64 selector_value, const std::string &device_fullname) { - g_return_val_if_fail(node, NULL); - g_return_val_if_fail(selector, NULL); - - Pylon::CEnumParameter param(node); - GType type = gst_pylon_make_enum_type(nodemap, node, device_fullname.c_str()); - - return gst_pylon_param_spec_selector_enum( - nodemap, node->GetName(), selector->GetName(), selector_value, - node->GetDisplayName(), node->GetToolTip(), type, param.GetIntValue(), - gst_pylon_query_access(nodemap, node)); -} - -GParamSpec *GstPylonParamFactory::make_param(GenApi::INodeMap &nodemap, - GenApi::INode *node, - GenApi::INode *selector, - guint64 selector_value, - const std::string &device_fullname, - GstPylonCache &feature_cache) { - g_return_val_if_fail(node, NULL); - - GParamSpec *spec = NULL; - GenApi::EInterfaceType iface = node->GetPrincipalInterfaceType(); - - switch (iface) { - case GenApi::intfIInteger: - if (!selector) { - spec = gst_pylon_make_spec_int64(nodemap, node, feature_cache); - } else { - spec = gst_pylon_make_spec_selector_int64( - nodemap, node, selector, selector_value, feature_cache); - } - break; - case GenApi::intfIBoolean: - if (!selector) { - spec = gst_pylon_make_spec_bool(nodemap, node); - } else { - spec = gst_pylon_make_spec_selector_bool(nodemap, node, selector, - selector_value); - } - break; - case GenApi::intfIFloat: - if (!selector) { - spec = gst_pylon_make_spec_double(nodemap, node, feature_cache); - } else { - spec = gst_pylon_make_spec_selector_double( - nodemap, node, selector, selector_value, feature_cache); - } - break; - case GenApi::intfIString: - if (!selector) { - spec = gst_pylon_make_spec_str(nodemap, node); - } else { - spec = gst_pylon_make_spec_selector_str(nodemap, node, selector, - selector_value); - } - break; - case GenApi::intfIEnumeration: - if (!selector) { - spec = gst_pylon_make_spec_enum(nodemap, node, device_fullname); - } else { - spec = gst_pylon_make_spec_selector_enum( - nodemap, node, selector, selector_value, device_fullname); - } - break; - default: - Pylon::String_t msg = - "Unsupported node of type " + GenApi::GetInterfaceName(node); - throw Pylon::GenericException(msg, __FILE__, __LINE__); - } - - return spec; -} diff --git a/gst-libs/gst/pylon/gstpylonintrospection.h b/gst-libs/gst/pylon/gstpylonintrospection.h index 5cc01a9..9fcc15f 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.h +++ b/gst-libs/gst/pylon/gstpylonintrospection.h @@ -37,12 +37,19 @@ #include #include -class GstPylonParamFactory { - public: - static GParamSpec *make_param(GenApi::INodeMap &nodemap, GenApi::INode *node, - GenApi::INode *selector, guint64 selector_value, - const std::string &device_fullname, - GstPylonCache &feature_cache); -}; +GParamFlags gst_pylon_query_access(GenApi::INodeMap &nodemap, + GenApi::INode *node); + +void gst_pylon_query_feature_properties_double( + GenApi::INodeMap &nodemap, GenApi::INode *node, + GstPylonCache &feature_cache, GParamFlags &flags, + gdouble &minimum_under_all_settings, gdouble &maximum_under_all_settings, + GenApi::INode *selector = NULL, gint64 selector_value = 0); + +void gst_pylon_query_feature_properties_integer( + GenApi::INodeMap &nodemap, GenApi::INode *node, + GstPylonCache &feature_cache, GParamFlags &flags, + gint64 &minimum_under_all_settings, gint64 &maximum_under_all_settings, + GenApi::INode *selector = NULL, gint64 selector_value = 0); #endif diff --git a/gst-libs/gst/pylon/gstpylonparamfactory.cpp b/gst-libs/gst/pylon/gstpylonparamfactory.cpp new file mode 100644 index 0000000..19cbfe9 --- /dev/null +++ b/gst-libs/gst/pylon/gstpylonparamfactory.cpp @@ -0,0 +1,254 @@ +#include "gstpylonparamfactory.h" + +#include "gstpylonintrospection.h" +#include "gstpylonparamspecs.h" + +#include + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_int64( + GenApi::INode *node) { + g_return_val_if_fail(node, NULL); + + Pylon::CIntegerParameter param(node); + gint64 max_value = 0; + gint64 min_value = 0; + GParamFlags flags = G_PARAM_READABLE; + + gst_pylon_query_feature_properties_integer(nodemap, node, feature_cache, + flags, min_value, max_value); + + return g_param_spec_int64(node->GetName(), node->GetDisplayName(), + node->GetToolTip(), min_value, max_value, + param.GetValue(), flags); +} + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_selector_int64( + GenApi::INode *node, GenApi::INode *selector, guint64 selector_value) { + g_return_val_if_fail(node, NULL); + g_return_val_if_fail(selector, NULL); + + Pylon::CIntegerParameter param(node); + gint64 max_value = 0; + gint64 min_value = 0; + GParamFlags flags = G_PARAM_READABLE; + + gst_pylon_query_feature_properties_integer(nodemap, node, feature_cache, + flags, min_value, max_value, + selector, selector_value); + + return gst_pylon_param_spec_selector_int64( + nodemap, node->GetName(), selector->GetName(), selector_value, + node->GetDisplayName(), node->GetToolTip(), min_value, max_value, + param.GetValue(), flags); +} + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_bool( + GenApi::INode *node) { + g_return_val_if_fail(node, NULL); + + Pylon::CBooleanParameter param(node); + + return g_param_spec_boolean(node->GetName(), node->GetDisplayName(), + node->GetToolTip(), param.GetValue(), + gst_pylon_query_access(nodemap, node)); +} + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_selector_bool( + GenApi::INode *node, GenApi::INode *selector, guint64 selector_value) { + g_return_val_if_fail(node, NULL); + g_return_val_if_fail(selector, NULL); + + Pylon::CBooleanParameter param(node); + + return gst_pylon_param_spec_selector_boolean( + nodemap, node->GetName(), selector->GetName(), selector_value, + node->GetDisplayName(), node->GetToolTip(), param.GetValue(), + gst_pylon_query_access(nodemap, node)); +} + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_double( + GenApi::INode *node) { + g_return_val_if_fail(node, NULL); + + Pylon::CFloatParameter param(node); + gdouble max_value = 0; + gdouble min_value = 0; + GParamFlags flags = G_PARAM_READABLE; + + gst_pylon_query_feature_properties_double(nodemap, node, feature_cache, flags, + min_value, max_value); + + return g_param_spec_double(node->GetName(), node->GetDisplayName(), + node->GetToolTip(), min_value, max_value, + param.GetValue(), flags); +} + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_selector_double( + GenApi::INode *node, GenApi::INode *selector, guint64 selector_value) { + g_return_val_if_fail(node, NULL); + g_return_val_if_fail(selector, NULL); + + Pylon::CFloatParameter param(node); + gdouble max_value = 0; + gdouble min_value = 0; + GParamFlags flags = G_PARAM_READABLE; + + gst_pylon_query_feature_properties_double(nodemap, node, feature_cache, flags, + min_value, max_value, selector, + selector_value); + + return gst_pylon_param_spec_selector_double( + nodemap, node->GetName(), selector->GetName(), selector_value, + node->GetDisplayName(), node->GetToolTip(), min_value, max_value, + param.GetValue(), flags); +} + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_str(GenApi::INode *node) { + g_return_val_if_fail(node, NULL); + + Pylon::CStringParameter param(node); + + return g_param_spec_string(node->GetName(), node->GetDisplayName(), + node->GetToolTip(), param.GetValue(), + gst_pylon_query_access(nodemap, node)); +} + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_selector_str( + GenApi::INode *node, GenApi::INode *selector, guint64 selector_value) { + g_return_val_if_fail(node, NULL); + g_return_val_if_fail(selector, NULL); + + Pylon::CStringParameter param(node); + + return gst_pylon_param_spec_selector_string( + nodemap, node->GetName(), selector->GetName(), selector_value, + node->GetDisplayName(), node->GetToolTip(), param.GetValue(), + gst_pylon_query_access(nodemap, node)); +} + +GType GstPylonParamFactory::gst_pylon_make_enum_type(GenApi::INode *node) { + /* When registering enums to the GType system, their string pointers + must remain valid throughout the application lifespan. To achieve this + we are saving all found enums into a static hash table + */ + static std::unordered_map> persistent_values; + + g_return_val_if_fail(node, G_TYPE_INVALID); + + Pylon::CEnumParameter param(node); + + gchar *full_name = g_strdup_printf("%s_%s", device_fullname.c_str(), + node->GetName().c_str()); + std::string name = gst_pylon_param_spec_sanitize_name(full_name); + g_free(full_name); + + GType type = g_type_from_name(name.c_str()); + + if (!type) { + std::vector enumvalues; + GenApi::StringList_t values; + + param.GetSettableValues(values); + for (const auto &value_name : values) { + auto entry = param.GetEntryByName(value_name); + auto value = static_cast(entry->GetValue()); + auto tooltip = entry->GetNode()->GetToolTip(); + + /* We need a copy of the strings so that they are persistent + throughout the application lifespan */ + GEnumValue ev = {value, g_strdup(value_name.c_str()), + g_strdup(tooltip.c_str())}; + enumvalues.push_back(ev); + } + + GEnumValue sentinel = {0}; + enumvalues.push_back(sentinel); + + type = g_enum_register_static(name.c_str(), enumvalues.data()); + persistent_values.insert({type, std::move(enumvalues)}); + } + + return type; +} + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_enum( + GenApi::INode *node) { + g_return_val_if_fail(node, NULL); + + Pylon::CEnumParameter param(node); + GType type = gst_pylon_make_enum_type(node); + + return g_param_spec_enum(node->GetName(), node->GetDisplayName(), + node->GetToolTip(), type, param.GetIntValue(), + gst_pylon_query_access(nodemap, node)); +} + +GParamSpec *GstPylonParamFactory::gst_pylon_make_spec_selector_enum( + GenApi::INode *node, GenApi::INode *selector, guint64 selector_value) { + g_return_val_if_fail(node, NULL); + g_return_val_if_fail(selector, NULL); + + Pylon::CEnumParameter param(node); + GType type = gst_pylon_make_enum_type(node); + + return gst_pylon_param_spec_selector_enum( + nodemap, node->GetName(), selector->GetName(), selector_value, + node->GetDisplayName(), node->GetToolTip(), type, param.GetIntValue(), + gst_pylon_query_access(nodemap, node)); +} + +GParamSpec *GstPylonParamFactory::GstPylonParamFactory::make_param( + GenApi::INode *node, GenApi::INode *selector, guint64 selector_value) { + g_return_val_if_fail(node, NULL); + + GParamSpec *spec = NULL; + GenApi::EInterfaceType iface = node->GetPrincipalInterfaceType(); + + switch (iface) { + case GenApi::intfIInteger: + if (!selector) { + spec = gst_pylon_make_spec_int64(node); + } else { + spec = + gst_pylon_make_spec_selector_int64(node, selector, selector_value); + } + break; + case GenApi::intfIBoolean: + if (!selector) { + spec = gst_pylon_make_spec_bool(node); + } else { + spec = + gst_pylon_make_spec_selector_bool(node, selector, selector_value); + } + break; + case GenApi::intfIFloat: + if (!selector) { + spec = gst_pylon_make_spec_double(node); + } else { + spec = + gst_pylon_make_spec_selector_double(node, selector, selector_value); + } + break; + case GenApi::intfIString: + if (!selector) { + spec = gst_pylon_make_spec_str(node); + } else { + spec = gst_pylon_make_spec_selector_str(node, selector, selector_value); + } + break; + case GenApi::intfIEnumeration: + if (!selector) { + spec = gst_pylon_make_spec_enum(node); + } else { + spec = + gst_pylon_make_spec_selector_enum(node, selector, selector_value); + } + break; + default: + Pylon::String_t msg = + "Unsupported node of type " + GenApi::GetInterfaceName(node); + throw Pylon::GenericException(msg, __FILE__, __LINE__); + } + + return spec; +} diff --git a/gst-libs/gst/pylon/gstpylonparamfactory.h b/gst-libs/gst/pylon/gstpylonparamfactory.h new file mode 100644 index 0000000..419b8be --- /dev/null +++ b/gst-libs/gst/pylon/gstpylonparamfactory.h @@ -0,0 +1,48 @@ +#ifndef GSTPYLONPARAMFACTORY_H +#define GSTPYLONPARAMFACTORY_H + +#include +#include + +class GstPylonParamFactory { + public: + GstPylonParamFactory(GenApi::INodeMap &nodemap, + const std::string &device_fullname, + GstPylonCache &feature_cache) + : nodemap(nodemap), + device_fullname(device_fullname), + feature_cache(feature_cache){}; + + GParamSpec *make_param(GenApi::INode *node, GenApi::INode *selector, + guint64 selector_value); + + private: + GParamSpec *gst_pylon_make_spec_int64(GenApi::INode *node); + GParamSpec *gst_pylon_make_spec_selector_int64(GenApi::INode *node, + GenApi::INode *selector, + guint64 selector_value); + GParamSpec *gst_pylon_make_spec_bool(GenApi::INode *node); + GParamSpec *gst_pylon_make_spec_selector_bool(GenApi::INode *node, + GenApi::INode *selector, + guint64 selector_value); + GParamSpec *gst_pylon_make_spec_double(GenApi::INode *node); + GParamSpec *gst_pylon_make_spec_selector_double(GenApi::INode *node, + GenApi::INode *selector, + guint64 selector_value); + GParamSpec *gst_pylon_make_spec_str(GenApi::INode *node); + GParamSpec *gst_pylon_make_spec_selector_str(GenApi::INode *node, + GenApi::INode *selector, + guint64 selector_value); + GType gst_pylon_make_enum_type(GenApi::INode *node); + GParamSpec *gst_pylon_make_spec_enum(GenApi::INode *node); + GParamSpec *gst_pylon_make_spec_selector_enum(GenApi::INode *node, + GenApi::INode *selector, + guint64 selector_value); + + private: + GenApi::INodeMap &nodemap; + const std::string &device_fullname; + GstPylonCache &feature_cache; +}; + +#endif // GSTPYLONPARAMFACTORY_H diff --git a/gst-libs/gst/pylon/meson.build b/gst-libs/gst/pylon/meson.build index a944bbc..c820c94 100644 --- a/gst-libs/gst/pylon/meson.build +++ b/gst-libs/gst/pylon/meson.build @@ -34,7 +34,8 @@ gstpylon_sources = [ 'gstpylonintrospection.cpp', 'gstpylonmeta.cpp', 'gstpylonobject.cpp', - 'gstpylonparamspecs.cpp' + 'gstpylonparamspecs.cpp', + 'gstpylonparamfactory.cpp' ] gstpylon_headers = [ From 4f18dbe9906fc5df95e639d5dee0b26ae8edc17a Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 27 Feb 2023 10:25:40 +0100 Subject: [PATCH 06/27] add missing files to cpp and format --- bindings/src/pygstpylon.cpp | 2 +- .../{gstpylondebug.c => gstpylondebug.cpp} | 18 +- gst-libs/gst/pylon/meson.build | 2 +- tests/check/generic/states.c | 185 ++++++++---------- tests/prototypes/dynamic_limits.cpp | 22 +-- 5 files changed, 106 insertions(+), 123 deletions(-) rename gst-libs/gst/pylon/{gstpylondebug.c => gstpylondebug.cpp} (83%) diff --git a/bindings/src/pygstpylon.cpp b/bindings/src/pygstpylon.cpp index 681beb0..fa653ab 100644 --- a/bindings/src/pygstpylon.cpp +++ b/bindings/src/pygstpylon.cpp @@ -31,7 +31,7 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif #include "bindaccessfunctions.h" diff --git a/gst-libs/gst/pylon/gstpylondebug.c b/gst-libs/gst/pylon/gstpylondebug.cpp similarity index 83% rename from gst-libs/gst/pylon/gstpylondebug.c rename to gst-libs/gst/pylon/gstpylondebug.cpp index 51ebc3b..e2c3da3 100644 --- a/gst-libs/gst/pylon/gstpylondebug.c +++ b/gst-libs/gst/pylon/gstpylondebug.cpp @@ -31,20 +31,18 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif #include "gstpylondebug.h" -GST_DEBUG_CATEGORY (gst_pylon_debug); +GST_DEBUG_CATEGORY(gst_pylon_debug); -void -gst_pylon_debug_init (void) -{ - if (g_once_init_enter (&gst_pylon_debug)) { - GST_DEBUG_CATEGORY (cat_done); - GST_DEBUG_CATEGORY_INIT (cat_done, "pylonsrc", 0, - "debug category for pylonsrc element"); - g_once_init_leave (&gst_pylon_debug, cat_done); +void gst_pylon_debug_init(void) { + if (g_once_init_enter(&gst_pylon_debug)) { + GST_DEBUG_CATEGORY(cat_done); + GST_DEBUG_CATEGORY_INIT(cat_done, "pylonsrc", 0, + "debug category for pylonsrc element"); + g_once_init_leave(&gst_pylon_debug, cat_done); } } diff --git a/gst-libs/gst/pylon/meson.build b/gst-libs/gst/pylon/meson.build index c820c94..f67cb78 100644 --- a/gst-libs/gst/pylon/meson.build +++ b/gst-libs/gst/pylon/meson.build @@ -29,7 +29,7 @@ endif gstpylon_sources = [ 'gstpyloncache.cpp', - 'gstpylondebug.c', + 'gstpylondebug.cpp', 'gstpylonfeaturewalker.cpp', 'gstpylonintrospection.cpp', 'gstpylonmeta.cpp', diff --git a/tests/check/generic/states.c b/tests/check/generic/states.c index 0c0197c..a08c19a 100644 --- a/tests/check/generic/states.c +++ b/tests/check/generic/states.c @@ -38,196 +38,181 @@ static GList *elements = NULL; -static void -setup (void) -{ +static void setup(void) { GList *features, *f; GList *plugins, *p; gchar **ignorelist = NULL; const gchar *STATE_IGNORE_ELEMENTS = NULL; GstRegistry *def; - GST_DEBUG ("getting elements for package %s", PACKAGE); - STATE_IGNORE_ELEMENTS = g_getenv ("GST_STATE_IGNORE_ELEMENTS"); - if (!g_getenv ("GST_NO_STATE_IGNORE_ELEMENTS") && STATE_IGNORE_ELEMENTS) { - GST_DEBUG ("Will ignore element factories: '%s'", STATE_IGNORE_ELEMENTS); - ignorelist = g_strsplit (STATE_IGNORE_ELEMENTS, " ", 0); + GST_DEBUG("getting elements for package %s", PACKAGE); + STATE_IGNORE_ELEMENTS = g_getenv("GST_STATE_IGNORE_ELEMENTS"); + if (!g_getenv("GST_NO_STATE_IGNORE_ELEMENTS") && STATE_IGNORE_ELEMENTS) { + GST_DEBUG("Will ignore element factories: '%s'", STATE_IGNORE_ELEMENTS); + ignorelist = g_strsplit(STATE_IGNORE_ELEMENTS, " ", 0); } - def = gst_registry_get (); + def = gst_registry_get(); - plugins = gst_registry_get_plugin_list (def); + plugins = gst_registry_get_plugin_list(def); for (p = plugins; p; p = p->next) { GstPlugin *plugin = p->data; - if (strcmp (gst_plugin_get_source (plugin), PACKAGE) != 0) - continue; + if (strcmp(gst_plugin_get_source(plugin), PACKAGE) != 0) continue; - features = - gst_registry_get_feature_list_by_plugin (def, - gst_plugin_get_name (plugin)); + features = gst_registry_get_feature_list_by_plugin( + def, gst_plugin_get_name(plugin)); for (f = features; f; f = f->next) { GstPluginFeature *feature = f->data; - const gchar *name = gst_plugin_feature_get_name (feature); + const gchar *name = gst_plugin_feature_get_name(feature); gboolean ignore = FALSE; - if (!GST_IS_ELEMENT_FACTORY (feature)) - continue; + if (!GST_IS_ELEMENT_FACTORY(feature)) continue; if (ignorelist) { gchar **s; for (s = ignorelist; s && *s; ++s) { - if (g_str_has_prefix (name, *s)) { - GST_DEBUG ("ignoring element %s", name); + if (g_str_has_prefix(name, *s)) { + GST_DEBUG("ignoring element %s", name); ignore = TRUE; } } - if (ignore) - continue; + if (ignore) continue; } - GST_DEBUG ("adding element %s", name); - elements = g_list_prepend (elements, (gpointer) g_strdup (name)); + GST_DEBUG("adding element %s", name); + elements = g_list_prepend(elements, (gpointer)g_strdup(name)); } - gst_plugin_feature_list_free (features); + gst_plugin_feature_list_free(features); } - gst_plugin_list_free (plugins); - g_strfreev (ignorelist); + gst_plugin_list_free(plugins); + g_strfreev(ignorelist); } -static void -teardown (void) -{ +static void teardown(void) { GList *e; for (e = elements; e; e = e->next) { - g_free (e->data); + g_free(e->data); } - g_list_free (elements); + g_list_free(elements); elements = NULL; } - -GST_START_TEST (test_state_changes_up_and_down_seq) -{ +GST_START_TEST(test_state_changes_up_and_down_seq) { GstElement *element; GList *e; for (e = elements; e; e = e->next) { const gchar *name = e->data; - GST_INFO ("testing element %s", name); - element = gst_element_factory_make (name, name); - fail_if (element == NULL, "Could not make element from factory %s", name); + GST_INFO("testing element %s", name); + element = gst_element_factory_make(name, name); + fail_if(element == NULL, "Could not make element from factory %s", name); - if (GST_IS_PIPELINE (element)) { - GST_DEBUG ("element %s is a pipeline", name); + if (GST_IS_PIPELINE(element)) { + GST_DEBUG("element %s is a pipeline", name); } - gst_element_set_state (element, GST_STATE_READY); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_PLAYING); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_READY); - gst_element_set_state (element, GST_STATE_NULL); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_READY); - gst_element_set_state (element, GST_STATE_PLAYING); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_NULL); - gst_object_unref (GST_OBJECT (element)); + gst_element_set_state(element, GST_STATE_READY); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_PLAYING); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_READY); + gst_element_set_state(element, GST_STATE_NULL); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_READY); + gst_element_set_state(element, GST_STATE_PLAYING); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(element)); } } GST_END_TEST; -GST_START_TEST (test_state_changes_up_seq) -{ +GST_START_TEST(test_state_changes_up_seq) { GstElement *element; GList *e; for (e = elements; e; e = e->next) { const gchar *name = e->data; - GST_INFO ("testing element %s", name); - element = gst_element_factory_make (name, name); - fail_if (element == NULL, "Could not make element from factory %s", name); + GST_INFO("testing element %s", name); + element = gst_element_factory_make(name, name); + fail_if(element == NULL, "Could not make element from factory %s", name); - if (GST_IS_PIPELINE (element)) { - GST_DEBUG ("element %s is a pipeline", name); + if (GST_IS_PIPELINE(element)) { + GST_DEBUG("element %s is a pipeline", name); } - gst_element_set_state (element, GST_STATE_READY); + gst_element_set_state(element, GST_STATE_READY); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_READY); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_READY); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_PLAYING); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_READY); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_PLAYING); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_READY); - gst_element_set_state (element, GST_STATE_NULL); - gst_object_unref (GST_OBJECT (element)); + gst_element_set_state(element, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(element)); } } GST_END_TEST; -GST_START_TEST (test_state_changes_down_seq) -{ +GST_START_TEST(test_state_changes_down_seq) { GstElement *element; GList *e; for (e = elements; e; e = e->next) { const gchar *name = e->data; - GST_INFO ("testing element %s", name); - element = gst_element_factory_make (name, name); - fail_if (element == NULL, "Could not make element from factory %s", name); + GST_INFO("testing element %s", name); + element = gst_element_factory_make(name, name); + fail_if(element == NULL, "Could not make element from factory %s", name); - if (GST_IS_PIPELINE (element)) { - GST_DEBUG ("element %s is a pipeline", name); + if (GST_IS_PIPELINE(element)) { + GST_DEBUG("element %s is a pipeline", name); } - gst_element_set_state (element, GST_STATE_READY); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_PLAYING); + gst_element_set_state(element, GST_STATE_READY); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_PLAYING); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_PLAYING); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_PLAYING); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_READY); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_PLAYING); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_READY); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_PLAYING); - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_state (element, GST_STATE_READY); - gst_element_set_state (element, GST_STATE_NULL); - gst_object_unref (GST_OBJECT (element)); + gst_element_set_state(element, GST_STATE_PAUSED); + gst_element_set_state(element, GST_STATE_READY); + gst_element_set_state(element, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(element)); } } GST_END_TEST; +static Suite *states_suite(void) { + Suite *s = suite_create("states_good"); + TCase *tc_chain = tcase_create("general"); -static Suite * -states_suite (void) -{ - Suite *s = suite_create ("states_good"); - TCase *tc_chain = tcase_create ("general"); - - suite_add_tcase (s, tc_chain); - tcase_add_checked_fixture (tc_chain, setup, teardown); - tcase_add_test (tc_chain, test_state_changes_up_and_down_seq); - tcase_add_test (tc_chain, test_state_changes_up_seq); - tcase_add_test (tc_chain, test_state_changes_down_seq); + suite_add_tcase(s, tc_chain); + tcase_add_checked_fixture(tc_chain, setup, teardown); + tcase_add_test(tc_chain, test_state_changes_up_and_down_seq); + tcase_add_test(tc_chain, test_state_changes_up_seq); + tcase_add_test(tc_chain, test_state_changes_down_seq); return s; } -GST_CHECK_MAIN (states); +GST_CHECK_MAIN(states); diff --git a/tests/prototypes/dynamic_limits.cpp b/tests/prototypes/dynamic_limits.cpp index 371ab54..2d98be5 100644 --- a/tests/prototypes/dynamic_limits.cpp +++ b/tests/prototypes/dynamic_limits.cpp @@ -31,25 +31,25 @@ */ #ifdef _MSC_VER // MSVC -#pragma warning(push) -#pragma warning(disable : 4265) +# pragma warning(push) +# pragma warning(disable : 4265) #elif __GNUC__ // GCC, CLANG, MinGW -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" -#pragma GCC diagnostic ignored "-Woverloaded-virtual" -#pragma GCC diagnostic ignored "-Wunused-variable" -#ifdef __clang__ -#pragma GCC diagnostic ignored "-Wunknown-warning-option" -#endif +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +# pragma GCC diagnostic ignored "-Woverloaded-virtual" +# pragma GCC diagnostic ignored "-Wunused-variable" +# ifdef __clang__ +# pragma GCC diagnostic ignored "-Wunknown-warning-option" +# endif #endif #include #include #ifdef _MSC_VER // MSVC -#pragma warning(pop) +# pragma warning(pop) #elif __GNUC__ // GCC, CLANG, MinWG -#pragma GCC diagnostic pop +# pragma GCC diagnostic pop #endif using namespace std; From 14ed081d60d807aa2bbf7c049e7e97071364027b Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Feb 2023 13:56:12 +0100 Subject: [PATCH 07/27] add problematic feature suppression for ace2 --- gst-libs/gst/pylon/gstpylonintrospection.cpp | 64 ++++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index 696a1c0..8d72554 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -70,7 +70,7 @@ class GstPylonTypeAction : public GstPylonActions { /* prototypes */ GenApi::INode *gst_pylon_find_limit_node(GenApi::INode *feature_node, const GenICam::gcstring &limit); -std::vector gst_pylon_find_parent_features( +static std::vector gst_pylon_find_parent_features( GenApi::INode *feature_node); void gst_pylon_add_all_property_values( GenApi::INode *feature_node, std::string value, @@ -183,7 +183,36 @@ GenApi::INode *gst_pylon_find_limit_node(GenApi::INode *node, return limit_node; } -std::vector gst_pylon_find_parent_features( +/* identify the first category a node belongs to */ +static const std::string gst_pylon_find_node_category(GenApi::INode *node) { + std::string category = ""; + g_return_val_if_fail(node, category); + + GenApi::INode *curr_node = node; + + while (true) { + if (curr_node->IsFeature() && + curr_node->GetPrincipalInterfaceType() == GenApi::intfICategory) { + category = curr_node->GetName(); + break; + } else { + GenApi::NodeList_t parents; + curr_node->GetParents(parents); + if (parents.size() == 0) { + /* no parents and still no Category found, abort */ + break; + } else if (parents.size() >= 1) { + /* yes there are genicam nodes that belong to multiple categories ... + * but we ignore this here */ + curr_node = parents[0]; + } + } + } + + return category; +} + +static std::vector gst_pylon_find_parent_features( GenApi::INode *node) { std::vector parent_features; @@ -237,6 +266,17 @@ std::vector gst_pylon_get_available_features( return available_features; } +static std::vector gst_pylon_get_valid_categories( + const std::vector &feature_list) { + std::vector valid_features; + for (const auto &feature : feature_list) { + if (gst_pylon_find_node_category(feature) != "MultipleROI") { + valid_features.push_back(feature); + } + } + return valid_features; +} + template std::vector> gst_pylon_cartesian_product( std::vector> &values) { @@ -349,7 +389,8 @@ std::vector> gst_pylon_create_set_value_actions( default: std::string msg = "Action in unsupported for node of type " + std::to_string(node->GetPrincipalInterfaceType()); - throw Pylon::GenericException(msg.c_str(), __FILE__, __LINE__); + GST_LOG("%s", msg.c_str()); + continue; } actions_list.push_back(values); } @@ -395,7 +436,8 @@ std::vector gst_pylon_create_reset_value_actions( default: std::string msg = "Action in unsupported for node of type " + std::to_string(node->GetPrincipalInterfaceType()); - throw Pylon::GenericException(msg.c_str(), __FILE__, __LINE__); + GST_LOG("%s", msg.c_str()); + continue; } } @@ -443,6 +485,20 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, std::vector available_parent_inv = gst_pylon_get_available_features(parent_invalidators); + /* workaround for ace2/dart2 exposuretime and short exposuretime*/ + if (node->GetName() == "ExposureTime" && + available_parent_inv.end() != + std::find_if(available_parent_inv.begin(), available_parent_inv.end(), + [](const GenApi::INode *n) { + return n->GetName() == "BslExposureTimeMode"; + })) { + minimum_under_all_settings = 1.0; + maximum_under_all_settings = 1e+07; + return; + } + + available_parent_inv = gst_pylon_get_valid_categories(available_parent_inv); + /* Save current set of values */ std::vector reset_list = gst_pylon_create_reset_value_actions(available_parent_inv); From 7a90fef64c33ffb515ab908ab40013b3d18671d8 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Tue, 28 Feb 2023 10:03:14 +0100 Subject: [PATCH 08/27] Debug logging of all introspection fix --- gst-libs/gst/pylon/gstpylonintrospection.cpp | 42 +++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index 8d72554..f57a0fb 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -443,6 +444,29 @@ std::vector gst_pylon_create_reset_value_actions( return actions_list; } +using namespace std ::literals; +class TimeLogger { + public: + TimeLogger(const std::string &message) + : t0(std::chrono::system_clock::now()), message(message) { + GST_DEBUG("Start Checking: %s ", message.c_str()); + }; + + ~TimeLogger() { + auto t1 = std::chrono::system_clock::now(); + GST_DEBUG("TIMELOGGER %s %ld msec -> ", message.c_str(), (t1 - t0) / 1ms); + for (auto &info : info_list) { + GST_DEBUG("%s ", info.c_str()); + } + } + + void add_info(const std::string &info) { info_list.push_back(info); } + + private: + const std::chrono::time_point t0; + std::string message; + std::vector info_list; +}; template void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, @@ -450,9 +474,10 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, std::unordered_map invalidators; maximum_under_all_settings = 0; minimum_under_all_settings = 0; - g_return_if_fail(node); + auto tl = TimeLogger(node->GetName().c_str()); + /* Find the maximum value of a feature under the influence of other elements * of the nodemap */ GenApi::INode *pmax_node = gst_pylon_find_limit_node(node, "pMax"); @@ -499,6 +524,10 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, available_parent_inv = gst_pylon_get_valid_categories(available_parent_inv); + for (auto &node : available_parent_inv) { + tl.add_info(node->GetName().c_str()); + } + /* Save current set of values */ std::vector reset_list = gst_pylon_create_reset_value_actions(available_parent_inv); @@ -507,11 +536,19 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, std::vector> actions_list = gst_pylon_create_set_value_actions(available_parent_inv); + /* try to get support for optimized bulk feature settings */ + auto reg_streaming_start = Pylon::CCommandParameter( + node->GetNodeMap()->GetNode("DeviceRegistersStreamingStart")); + auto reg_streaming_end = Pylon::CCommandParameter( + node->GetNodeMap()->GetNode("DeviceRegistersStreamingEnd")); + /* Create list of all possible setting permutations and execute them all */ auto action_list_permutations = gst_pylon_cartesian_product(actions_list); std::vector min_values; std::vector max_values; for (const auto &actions : action_list_permutations) { + // reg_streaming_start.TryExecute(); + for (const auto &action : actions) { /* Some states might not be valid, so just skip them */ try { @@ -520,6 +557,9 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, continue; } } + + // reg_streaming_end.TryExecute(); + /* Capture min and max values after all setting are applied*/ min_values.push_back(gst_pylon_query_feature_limits(node, "min")); max_values.push_back(gst_pylon_query_feature_limits(node, "max")); From 6121eabc8223fc40966941c06f870167bbcf1097 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:07:26 +0100 Subject: [PATCH 09/27] optimize introspection by grouping into write transactions --- gst-libs/gst/pylon/gstpylonintrospection.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index f57a0fb..3e416c1 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -547,8 +547,7 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, std::vector min_values; std::vector max_values; for (const auto &actions : action_list_permutations) { - // reg_streaming_start.TryExecute(); - + reg_streaming_start.TryExecute(); for (const auto &action : actions) { /* Some states might not be valid, so just skip them */ try { @@ -558,7 +557,7 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, } } - // reg_streaming_end.TryExecute(); + reg_streaming_end.TryExecute(); /* Capture min and max values after all setting are applied*/ min_values.push_back(gst_pylon_query_feature_limits(node, "min")); From 2aa5db3040ae7e2a087973a03342e03a01f6bbda Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Tue, 28 Feb 2023 10:03:21 +0100 Subject: [PATCH 10/27] single feature introspection for debugging fix show node --- gst-libs/gst/pylon/gstpylonfeaturewalker.cpp | 21 +++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp index 3ac1568..a8ebeab 100644 --- a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp +++ b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp @@ -299,6 +299,13 @@ void GstPylonFeatureWalker::install_properties( const std::string& device_fullname, GstPylonCache& feature_cache) { g_return_if_fail(oclass); + /* handle filter for debugging */ + const char* single_feature = NULL; + if (const char* env_p = std::getenv("PYLONSRC_SINGLE_FEATURE")) { + GST_DEBUG("LIMIT to use only feature %s\n", env_p); + single_feature = env_p; + } + auto param_factory = GstPylonParamFactory(nodemap, device_fullname, feature_cache); @@ -326,11 +333,15 @@ void GstPylonFeatureWalker::install_properties( GenICam::gcstring attrib; try { - std::vector specs_list = - gst_pylon_camera_handle_node(node, param_factory); - - gst_pylon_camera_install_specs(specs_list, oclass, nprop); - + if (!single_feature || + (single_feature && std::string(node->GetName().c_str()) == + std::string(single_feature))) { + GST_DEBUG("Install node %s", node->GetName().c_str()); + std::vector specs_list = + gst_pylon_camera_handle_node(node, param_factory); + + gst_pylon_camera_install_specs(specs_list, oclass, nprop); + } } catch (const Pylon::GenericException& e) { GST_FIXME("Unable to install property \"%s\" on device \"%s\": %s", node->GetName().c_str(), device_fullname.c_str(), From 8bde503d3fd1cf5bffef55954af3108a265dfcde Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 10:40:20 +0100 Subject: [PATCH 11/27] clear possible left open transactions on device open --- ext/pylon/gstpylon.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/ext/pylon/gstpylon.cpp b/ext/pylon/gstpylon.cpp index edbea8c..799c58c 100644 --- a/ext/pylon/gstpylon.cpp +++ b/ext/pylon/gstpylon.cpp @@ -289,7 +289,15 @@ GstPylon *gst_pylon_new(GstElement *gstpylonsrc, const gchar *device_user_name, } self->camera->Open(); - /* Set the camera to a valid state */ + /* Set the camera to a valid state + * close left open transactions on the device + */ + self->camera->DeviceFeaturePersistenceEnd.TryExecute(); + self->camera->DeviceRegistersStreamingEnd.TryExecute(); + + /* Set the camera to a valid state + * load the poweron user set + */ if (self->camera->UserSetSelector.IsWritable()) { std::string default_set = "Auto"; gst_pylon_apply_set(self, default_set); @@ -916,6 +924,21 @@ static gchar *gst_pylon_get_string_properties( Pylon::CBaslerUniversalInstantCamera camera(factory.CreateDevice(device), Pylon::Cleanup_Delete); camera.Open(); + + /* Set the camera to a valid state + * close left open transactions on the device + */ + camera.DeviceFeaturePersistenceEnd.TryExecute(); + camera.DeviceRegistersStreamingEnd.TryExecute(); + + /* Set the camera to a valid state + * load the factory default set + */ + if (camera.UserSetSelector.IsWritable()) { + camera.UserSetSelector.SetValue("Default"); + camera.UserSetLoad.Execute(); + } + get_device_string_properties(&camera, &camera_properties, DEFAULT_ALIGNMENT); camera.Close(); From f5e062ea1700b018b7f95db39c070408bae98659 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 17:39:50 +0100 Subject: [PATCH 12/27] python script to list all features that should be mapped to gstreamer --- tests/prototypes/featurewalker.py | 98 +++++++++++++++++++++++++++ tests/prototypes/list_all_features.py | 37 ++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tests/prototypes/featurewalker.py create mode 100644 tests/prototypes/list_all_features.py diff --git a/tests/prototypes/featurewalker.py b/tests/prototypes/featurewalker.py new file mode 100644 index 0000000..3eade76 --- /dev/null +++ b/tests/prototypes/featurewalker.py @@ -0,0 +1,98 @@ +from queue import Queue +import pypylon.genicam as geni + +import itertools + + +class FeatureSelector(object): + def __init__(self, selector_node, value): + self.selector_node = selector_node + self.value = value + + def __repr__(self): + return f"SEL<{self.selector_node.Node.GetName()}/{self.value}>" + + +class FeatureNode(object): + """ + a feature node is the break down of each camera feature + under each specific selector + """ + + def __init__( + self, + node, + selectors: [ + [FeatureSelector], + ] = [], + ): + self.node = node + self.selectors = selectors + + def __repr__(self): + repr = f"FeatureNode {self.node.Node.GetName()} " + if self.selectors: + for sel in self.selectors: + repr += " " + str(sel) + else: + repr += "direct" + return repr + + +def get_node_selectors(node): + selectors = [] + for s in node.Node.GetSelectingFeatures(): + selector_entries = [] + if type(s) == geni.IInteger: + for idx in range(s.GetMin(), s.GetMax() + s.GetInc(), s.GetInc()): + selector_entries.append(FeatureSelector(s, idx)) + elif type(s) == geni.IEnumeration: + for enum_val in s.Symbolics: + if geni.IsImplemented(s.GetEntryByName(enum_val)): + selector_entries.append(FeatureSelector(s, enum_val)) + + selectors.append(selector_entries) + + return selectors + + +# feature walker +# walks down feature tree to get all implemented features +def get_feature_nodes(nodemap, with_selectors: bool, only_implemented: bool): + import queue + + worklist = queue.Queue() + worklist.put(nodemap.GetNode("Root")) + + node_list = [] + while not worklist.empty(): + current_node = worklist.get() + + if only_implemented and not geni.IsImplemented(current_node): + # print(f"skip not implemented node {current_node.Node.Name}") + continue + + if len(current_node.Node.GetSelectedFeatures()): + # print(f"skip selector {current_node.Node.Name}") + continue + + if type(current_node) == geni.ICategory: + # push children of category + children = current_node.Node.GetChildren() + for c in children: + worklist.put(c) + continue + + # loop over all selector options of current node + if with_selectors and len(current_node.Node.GetSelectingFeatures()): + node_selectors = get_node_selectors(current_node) + for selectors in itertools.product(*node_selectors): + node_list.append(FeatureNode(current_node, selectors)) + + # for features with only a single selector output the direct form too + if len(current_node.Node.GetSelectingFeatures()) == 1: + node_list.append(FeatureNode(current_node)) + else: + node_list.append(FeatureNode(current_node)) + + return node_list diff --git a/tests/prototypes/list_all_features.py b/tests/prototypes/list_all_features.py new file mode 100644 index 0000000..5cfd178 --- /dev/null +++ b/tests/prototypes/list_all_features.py @@ -0,0 +1,37 @@ +#! /usr/bin/env python3 + +import featurewalker + +import pypylon.pylon as py + +def create_gst_feature_name(fn: featurewalker.FeatureNode, prefix: str): + f_name = f"{prefix}::{fn.node.Node.GetName()}" + for sel in fn.selectors: + f_name += f"-{sel.value}" + return f_name + + +tlf = py.TlFactory.GetInstance() + + +cam = py.InstantCamera(tlf.CreateFirstDevice()) +cam.Open() + + +features = featurewalker.get_feature_nodes( + cam.GetNodeMap(), with_selectors=True, only_implemented=True +) +for f in features: + print(create_gst_feature_name(f, "cam")) + +features = featurewalker.get_feature_nodes( + cam.GetStreamGrabberNodeMap(), with_selectors=True, only_implemented=True +) +for f in features: + print(create_gst_feature_name(f, "stream")) + +features = featurewalker.get_feature_nodes( + cam.GetTLNodeMap(), with_selectors=True, only_implemented=True +) +for f in features: + print(create_gst_feature_name(f, "tl")) From 981b3fed0ca5eac825e83ad90ec79254d4ae8f07 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:00:11 +0100 Subject: [PATCH 13/27] refactor supported pfnc formats into central location --- ext/pylon/gstpylon.cpp | 20 +-------- gst-libs/gst/pylon/gstpylonformatmapping.h | 47 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 19 deletions(-) create mode 100644 gst-libs/gst/pylon/gstpylonformatmapping.h diff --git a/ext/pylon/gstpylon.cpp b/ext/pylon/gstpylon.cpp index 799c58c..b530ec8 100644 --- a/ext/pylon/gstpylon.cpp +++ b/ext/pylon/gstpylon.cpp @@ -36,6 +36,7 @@ #include "gst/pylon/gstpyloncache.h" #include "gst/pylon/gstpylondebug.h" +#include "gst/pylon/gstpylonformatmapping.h" #include "gst/pylon/gstpylonincludes.h" #include "gst/pylon/gstpylonmetaprivate.h" #include "gst/pylon/gstpylonobject.h" @@ -52,12 +53,6 @@ constexpr int FAILED_OPEN_RETRY_COUNT = 30; constexpr int FAILED_OPEN_RETRY_WAIT_TIME_MS = 1000; -/* Pixel format definitions */ -typedef struct { - std::string pfnc_name; - std::string gst_name; -} PixelFormatMappingType; - /* Mapping of GstStructure with its corresponding formats */ typedef struct { const std::string st_name; @@ -128,19 +123,6 @@ struct _GstPylon { gint requested_device_index; }; -static const std::vector pixel_format_mapping_raw = { - {"Mono8", "GRAY8"}, {"RGB8Packed", "RGB"}, - {"BGR8Packed", "BGR"}, {"RGB8", "RGB"}, - {"BGR8", "BGR"}, {"YCbCr422_8", "YUY2"}, - {"YUV422_8_UYVY", "UYVY"}, {"YUV422_8", "YUY2"}, - {"YUV422Packed", "UYVY"}, {"YUV422_YUYV_Packed", "YUY2"}}; - -static const std::vector pixel_format_mapping_bayer = { - {"BayerBG8", "bggr"}, - {"BayerGR8", "grbg"}, - {"BayerRG8", "rggb"}, - {"BayerGB8", "gbrg"}}; - static const std::vector gst_structure_formats = { {"video/x-raw", pixel_format_mapping_raw}, {"video/x-bayer", pixel_format_mapping_bayer}}; diff --git a/gst-libs/gst/pylon/gstpylonformatmapping.h b/gst-libs/gst/pylon/gstpylonformatmapping.h new file mode 100644 index 0000000..2a873cc --- /dev/null +++ b/gst-libs/gst/pylon/gstpylonformatmapping.h @@ -0,0 +1,47 @@ +#ifndef _GST_PYLON_FORMAT_MAPPING_ +#define _GST_PYLON_FORMAT_MAPPING_ + +#include +#include + +bool isSupportedPylonFormat(const std::string &format); + +/* Pixel format definitions */ +typedef struct { + std::string pfnc_name; + std::string gst_name; +} PixelFormatMappingType; + +const std::vector pixel_format_mapping_raw = { + {"Mono8", "GRAY8"}, {"RGB8Packed", "RGB"}, + {"BGR8Packed", "BGR"}, {"RGB8", "RGB"}, + {"BGR8", "BGR"}, {"YCbCr422_8", "YUY2"}, + {"YUV422_8_UYVY", "UYVY"}, {"YUV422_8", "YUY2"}, + {"YUV422Packed", "UYVY"}, {"YUV422_YUYV_Packed", "YUY2"}}; + +const std::vector pixel_format_mapping_bayer = { + {"BayerBG8", "bggr"}, + {"BayerGR8", "grbg"}, + {"BayerRG8", "rggb"}, + {"BayerGB8", "gbrg"}}; + +bool isSupportedPylonFormat(const std::string &format) { + bool res = false; + for (const auto &fd : pixel_format_mapping_raw) { + if (fd.pfnc_name == format) { + res = true; + break; + } + } + if (!res) { + for (const auto &fd : pixel_format_mapping_bayer) { + if (fd.pfnc_name == format) { + res = true; + break; + } + } + } + return res; +} + +#endif From 79e68454d47b7f2d07eafd3267d28cbd67989aeb Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:04:26 +0100 Subject: [PATCH 14/27] fix cache handling during development --- gst-libs/gst/pylon/gstpyloncache.cpp | 16 ++++++++++------ gst-libs/gst/pylon/gstpyloncache.h | 7 ++++--- gst-libs/gst/pylon/gstpylonfeaturewalker.cpp | 4 +--- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/gst-libs/gst/pylon/gstpyloncache.cpp b/gst-libs/gst/pylon/gstpyloncache.cpp index 7db2cf9..37564ff 100644 --- a/gst-libs/gst/pylon/gstpyloncache.cpp +++ b/gst-libs/gst/pylon/gstpyloncache.cpp @@ -68,11 +68,16 @@ static std::string gst_pylon_cache_create_filepath( GstPylonCache::GstPylonCache(const std::string &name) : filepath(gst_pylon_cache_create_filepath(name)), feature_cache_dict(g_key_file_new()), - is_empty(TRUE) {} + is_modified(FALSE) { + /* load initial cache file */ + if (!LoadCacheFile()) { + GST_LOG("No feature cache file found"); + } +} GstPylonCache::~GstPylonCache() { g_key_file_free(this->feature_cache_dict); } -gboolean GstPylonCache::IsCacheValid() { +gboolean GstPylonCache::LoadCacheFile() { gboolean ret = TRUE; /* Check if file exists */ @@ -88,13 +93,10 @@ gboolean GstPylonCache::IsCacheValid() { if (!ret) { return FALSE; } - - this->is_empty = FALSE; - return TRUE; } -gboolean GstPylonCache::IsEmpty() { return this->is_empty; } +gboolean GstPylonCache::HasNewSettings() { return is_modified; } void GstPylonCache::CreateCacheFile() { GError *file_err = NULL; @@ -128,12 +130,14 @@ void GstPylonCache::SetIntegerAttribute(const char *feature, const char *attribute, const gint64 val) { g_key_file_set_int64(this->feature_cache_dict, feature, attribute, val); + is_modified = true; } void GstPylonCache::SetDoubleAttribute(const char *feature, const char *attribute, const gdouble val) { g_key_file_set_double(this->feature_cache_dict, feature, attribute, val); + is_modified = true; } bool GstPylonCache::GetIntegerAttribute(const char *feature, diff --git a/gst-libs/gst/pylon/gstpyloncache.h b/gst-libs/gst/pylon/gstpyloncache.h index 0fa7f0a..0b57859 100644 --- a/gst-libs/gst/pylon/gstpyloncache.h +++ b/gst-libs/gst/pylon/gstpyloncache.h @@ -41,8 +41,7 @@ class GST_PLUGIN_EXPORT GstPylonCache { public: GstPylonCache(const std::string &name); ~GstPylonCache(); - gboolean IsCacheValid(); - gboolean IsEmpty(); + gboolean HasNewSettings(); void SetIntProps(const gchar *feature_name, const gint64 min, const gint64 max, const GParamFlags flags); @@ -54,6 +53,8 @@ class GST_PLUGIN_EXPORT GstPylonCache { bool GetDoubleProps(const gchar *feature_name, gdouble &min, gdouble &max, GParamFlags &flags); + /* Load from file system */ + gboolean LoadCacheFile(); /* Persist cache to filesystem */ void CreateCacheFile(); @@ -70,7 +71,7 @@ class GST_PLUGIN_EXPORT GstPylonCache { std::string filepath; GKeyFile *feature_cache_dict; - gboolean is_empty; + gboolean is_modified; }; #endif diff --git a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp index a8ebeab..f0dcf6e 100644 --- a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp +++ b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp @@ -309,8 +309,6 @@ void GstPylonFeatureWalker::install_properties( auto param_factory = GstPylonParamFactory(nodemap, device_fullname, feature_cache); - gboolean is_cache_valid = feature_cache.IsCacheValid(); - gint nprop = 1; GenApi::INode* root_node = nodemap.GetNode("Root"); auto worklist = std::queue(); @@ -361,7 +359,7 @@ void GstPylonFeatureWalker::install_properties( } } - if (!is_cache_valid) { + if (feature_cache.HasNewSettings()) { try { feature_cache.CreateCacheFile(); } catch (const Pylon::GenericException& e) { From 5454b69037e179e1c3fc2b254af8d8186eb43d9c Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:05:06 +0100 Subject: [PATCH 15/27] handle indexed feature nodes --- gst-libs/gst/pylon/gstpylonintrospection.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index 3e416c1..2fc36d3 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -180,6 +180,12 @@ GenApi::INode *gst_pylon_find_limit_node(GenApi::INode *node, } else if (node->GetProperty("pValue", value, attribute)) { limit_node = gst_pylon_find_limit_node(node->GetNodeMap()->GetNode(value), limit); + } else if (node->GetProperty("pValueDefault", value, attribute)) { + /* FIXME: pValueDefault might not cover the limits of all selector entries + * properly + */ + limit_node = + gst_pylon_find_limit_node(node->GetNodeMap()->GetNode(value), limit); } return limit_node; } From 6a359aad3585db6dee7671193108ded9bc97c424 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:06:20 +0100 Subject: [PATCH 16/27] filter introspection pixelformats by supported formats --- gst-libs/gst/pylon/gstpylonintrospection.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index 2fc36d3..c68b472 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -35,6 +35,7 @@ #endif #include "gstpylondebug.h" +#include "gstpylonformatmapping.h" #include "gstpylonintrospection.h" #include "gstpylonobject.h" #include "gstpylonparamspecs.h" @@ -380,13 +381,11 @@ std::vector> gst_pylon_create_set_value_actions( GenApi::StringList_t settable_values; param.GetSettableValues(settable_values); for (const auto &value : settable_values) { - /* Skip unsupported packed mono and bayer formats */ + /* Skip only check plugin supported formats */ if (node->GetName() == "PixelFormat" && - (Pylon::IsMonoPacked(static_cast( - param.GetEntryByName(value)->GetValue())) || - Pylon::IsBayerPacked(static_cast( - param.GetEntryByName(value)->GetValue())))) + !isSupportedPylonFormat(value.c_str())) { continue; + } values.push_back( new GstPylonTypeAction( param, value)); From 411abd1a9fe7c1eb3ec920703d9cee73709c564c Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:08:04 +0100 Subject: [PATCH 17/27] update filter tables for introspection --- gst-libs/gst/pylon/gstpylonfeaturewalker.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp index f0dcf6e..0ce8e80 100644 --- a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp +++ b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp @@ -67,29 +67,25 @@ static const std::unordered_set propfilter_set = { "AcquisitionFrameRateEnable", "AcquisitionFrameRate", "AcquisitionFrameRateAbs", - "ChunkData", "AcquisitionStart", "AcquisitionStop", "UserSetLoad", "UserSetSave", "TriggerSoftware", "DeviceReset", - "FileAccessControl", + "DeviceFeaturePersistenceStart", + "DeviceFeaturePersistenceEnd", "DeviceRegistersStreamingStart", "DeviceRegistersStreamingEnd", - "FileAccessControl" /* has to be implemented in access library */ - "EventControl", /* disable full event section until mapped to gst - events/msgs */ - "SequencerControl" /* sequencer control relies on cmd feature */ }; static const std::unordered_set categoryfilter_set = { "ChunkData", - "FileAccessControl" /* has to be implemented in access library */ - "EventControl", /* disable full event section until mapped to gst - events/msgs */ - "SequencerControl", /* sequencer control relies on cmd feature */ - "MultipleROI" /* workaround skip to avoid issues with ace2/dart2 + "FileAccessControl", /* has to be implemented in access library */ + "EventControl", /* disable full event section until mapped to gst + events/msgs */ + "SequencerControl", /* sequencer control relies on cmd feature */ + "MultipleROI", /* workaround skip to avoid issues with ace2/dart2 FIXME: this has to be fixed in feature walker */ }; From a93bdb9872aecd589b48c05ac88d71640bb8ea4a Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:08:30 +0100 Subject: [PATCH 18/27] properly filter sub categories --- gst-libs/gst/pylon/gstpylonfeaturewalker.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp index 0ce8e80..589f6b2 100644 --- a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp +++ b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp @@ -318,7 +318,9 @@ void GstPylonFeatureWalker::install_properties( /* Only handle real features that are not in the filter set, are not * selectors and are available */ auto sel_node = dynamic_cast(node); - if (node->IsFeature() && (node->GetVisibility() != GenApi::Invisible) && + auto category_node = dynamic_cast(node); + if (!category_node && node->IsFeature() && + (node->GetVisibility() != GenApi::Invisible) && GenApi::IsImplemented(node) && !is_unsupported_feature(std::string(node->GetName())) && node->GetPrincipalInterfaceType() != GenApi::intfICategory && @@ -344,7 +346,6 @@ void GstPylonFeatureWalker::install_properties( } /* Walk down all categories */ - auto category_node = dynamic_cast(node); if (category_node && !is_unsupported_category(std::string(node->GetName()))) { GenApi::FeatureList_t features; From 783bfc073506a5a1dc0299d1a034b538ab23943a Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:11:05 +0100 Subject: [PATCH 19/27] reduce loglevel of known introspection isues --- gst-libs/gst/pylon/gstpylonfeaturewalker.cpp | 4 ++-- gst-libs/gst/pylon/gstpylonintrospection.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp index 589f6b2..c50e9e0 100644 --- a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp +++ b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp @@ -261,7 +261,7 @@ std::vector gst_pylon_camera_handle_node( specs_list.push_back( param_factory.make_param(node, selector_node, selector_value)); } catch (const Pylon::GenericException& e) { - GST_FIXME("Unable to fully install property '%s-%s' : %s", + GST_DEBUG("Unable to fully install property '%s-%s' : %s", node->GetName().c_str(), enum_value.c_str(), e.GetDescription()); } @@ -339,7 +339,7 @@ void GstPylonFeatureWalker::install_properties( gst_pylon_camera_install_specs(specs_list, oclass, nprop); } } catch (const Pylon::GenericException& e) { - GST_FIXME("Unable to install property \"%s\" on device \"%s\": %s", + GST_DEBUG("Unable to install property \"%s\" on device \"%s\": %s", node->GetName().c_str(), device_fullname.c_str(), e.GetDescription()); } diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index c68b472..4837f76 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -393,9 +393,9 @@ std::vector> gst_pylon_create_set_value_actions( break; } default: - std::string msg = "Action in unsupported for node of type " + - std::to_string(node->GetPrincipalInterfaceType()); - GST_LOG("%s", msg.c_str()); + std::string msg = + "No test for node " + std::string(node->GetName().c_str()); + GST_DEBUG("%s", msg.c_str()); continue; } actions_list.push_back(values); @@ -440,9 +440,9 @@ std::vector gst_pylon_create_reset_value_actions( break; } default: - std::string msg = "Action in unsupported for node of type " + - std::to_string(node->GetPrincipalInterfaceType()); - GST_LOG("%s", msg.c_str()); + std::string msg = + "No test for node " + std::string(node->GetName().c_str()); + GST_DEBUG("%s", msg.c_str()); continue; } } From 6b5fbc5a1540a4e8e03a4c1f9000c6a59bf088ca Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:11:36 +0100 Subject: [PATCH 20/27] catch correct exception for failed introspection --- gst-libs/gst/pylon/gstpylonintrospection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index 4837f76..b91b0a1 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -557,7 +557,8 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, /* Some states might not be valid, so just skip them */ try { action->set_value(); - } catch (const Pylon::GenericException &) { + } catch (const GenICam::GenericException &e) { + GST_DEBUG("failed to set action"); continue; } } From 6d8f79cc12ce3f7f841f64bfc474ab20e8a0bece Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 13 Mar 2023 21:12:24 +0100 Subject: [PATCH 21/27] extend and document introspection filter rules --- gst-libs/gst/pylon/gstpylonintrospection.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index b91b0a1..7f6495f 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -515,16 +515,25 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, std::vector available_parent_inv = gst_pylon_get_available_features(parent_invalidators); - /* workaround for ace2/dart2 exposuretime and short exposuretime*/ + /* workarounds for ace2/dart2 exposuretime and short exposuretime + * and other nodes with large dependencies + * FIXME: refactor this into a filter class + */ if (node->GetName() == "ExposureTime" && available_parent_inv.end() != std::find_if(available_parent_inv.begin(), available_parent_inv.end(), [](const GenApi::INode *n) { return n->GetName() == "BslExposureTimeMode"; })) { + GST_DEBUG("Apply ExposureTime feature workaround"); minimum_under_all_settings = 1.0; maximum_under_all_settings = 1e+07; return; + } else if (node->GetName() == "AcquisitionBurstFrameCount") { + minimum_under_all_settings = 1; + maximum_under_all_settings = 1023; + GST_DEBUG("Apply AcquisitionBurstFrameCount feature workaround"); + return; } available_parent_inv = gst_pylon_get_valid_categories(available_parent_inv); From edaa6278e6a4ea8472b21a2209cacce8f163c6f6 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Thu, 16 Mar 2023 18:38:17 +0100 Subject: [PATCH 22/27] add license header --- gst-libs/gst/pylon/gstpylonformatmapping.h | 32 +++++++++++++++++++++ gst-libs/gst/pylon/gstpylonparamfactory.cpp | 32 +++++++++++++++++++++ gst-libs/gst/pylon/gstpylonparamfactory.h | 32 +++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/gst-libs/gst/pylon/gstpylonformatmapping.h b/gst-libs/gst/pylon/gstpylonformatmapping.h index 2a873cc..9f11c77 100644 --- a/gst-libs/gst/pylon/gstpylonformatmapping.h +++ b/gst-libs/gst/pylon/gstpylonformatmapping.h @@ -1,3 +1,35 @@ +/* Copyright (C) 2023 Basler AG + * + * + * 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. Neither the name of the copyright holder nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #ifndef _GST_PYLON_FORMAT_MAPPING_ #define _GST_PYLON_FORMAT_MAPPING_ diff --git a/gst-libs/gst/pylon/gstpylonparamfactory.cpp b/gst-libs/gst/pylon/gstpylonparamfactory.cpp index 19cbfe9..1fc2c0c 100644 --- a/gst-libs/gst/pylon/gstpylonparamfactory.cpp +++ b/gst-libs/gst/pylon/gstpylonparamfactory.cpp @@ -1,3 +1,35 @@ +/* Copyright (C) 2023 Basler AG + * + * + * 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. Neither the name of the copyright holder nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #include "gstpylonparamfactory.h" #include "gstpylonintrospection.h" diff --git a/gst-libs/gst/pylon/gstpylonparamfactory.h b/gst-libs/gst/pylon/gstpylonparamfactory.h index 419b8be..e1749e6 100644 --- a/gst-libs/gst/pylon/gstpylonparamfactory.h +++ b/gst-libs/gst/pylon/gstpylonparamfactory.h @@ -1,3 +1,35 @@ +/* Copyright (C) 2023 Basler AG + * + * + * 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. Neither the name of the copyright holder nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #ifndef GSTPYLONPARAMFACTORY_H #define GSTPYLONPARAMFACTORY_H From 196d76bc4ded71a83a043c373cfe016dad26656e Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Thu, 16 Mar 2023 18:42:30 +0100 Subject: [PATCH 23/27] use open lists in target tables --- gst-libs/gst/pylon/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst-libs/gst/pylon/meson.build b/gst-libs/gst/pylon/meson.build index f67cb78..1b617e3 100644 --- a/gst-libs/gst/pylon/meson.build +++ b/gst-libs/gst/pylon/meson.build @@ -35,7 +35,7 @@ gstpylon_sources = [ 'gstpylonmeta.cpp', 'gstpylonobject.cpp', 'gstpylonparamspecs.cpp', - 'gstpylonparamfactory.cpp' + 'gstpylonparamfactory.cpp', ] gstpylon_headers = [ @@ -47,7 +47,7 @@ gstpylon_headers = [ 'gstpylonmeta.h', 'gstpylonmetaprivate.h', 'gstpylonobject.h', - 'gstpylonparamspecs.h' + 'gstpylonparamspecs.h', ] install_headers(gstpylon_headers, subdir : 'gstreamer-1.0/gst/pylon/') From c3d3abf85893c1135250f3d23ba8712041cdcde0 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Thu, 16 Mar 2023 19:52:45 +0100 Subject: [PATCH 24/27] disable command and register until support in introspection --- gst-libs/gst/pylon/gstpylonfeaturewalker.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp index c50e9e0..aa6fb50 100644 --- a/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp +++ b/gst-libs/gst/pylon/gstpylonfeaturewalker.cpp @@ -324,6 +324,8 @@ void GstPylonFeatureWalker::install_properties( GenApi::IsImplemented(node) && !is_unsupported_feature(std::string(node->GetName())) && node->GetPrincipalInterfaceType() != GenApi::intfICategory && + node->GetPrincipalInterfaceType() != GenApi::intfICommand && + node->GetPrincipalInterfaceType() != GenApi::intfIRegister && sel_node && !sel_node->IsSelector()) { GenICam::gcstring value; GenICam::gcstring attrib; From 704fb3f2105d2a32efe11ca6a8bd2438c3411c50 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Thu, 16 Mar 2023 19:53:17 +0100 Subject: [PATCH 25/27] add more heuristics for complex introspection targets --- gst-libs/gst/pylon/gstpylonintrospection.cpp | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/gst-libs/gst/pylon/gstpylonintrospection.cpp b/gst-libs/gst/pylon/gstpylonintrospection.cpp index 7f6495f..31e1591 100644 --- a/gst-libs/gst/pylon/gstpylonintrospection.cpp +++ b/gst-libs/gst/pylon/gstpylonintrospection.cpp @@ -529,6 +529,70 @@ void gst_pylon_find_limits(GenApi::INode *node, T &minimum_under_all_settings, minimum_under_all_settings = 1.0; maximum_under_all_settings = 1e+07; return; + } else if (node->GetName() == "OffsetX") { + GST_DEBUG("Apply OffsetX feature workaround"); + Pylon::CIntegerParameter sensor_width( + node->GetNodeMap()->GetNode("SensorWidth")); + Pylon::CIntegerParameter width(node->GetNodeMap()->GetNode("Width")); + /* try to shortcut if sensor width is available */ + if (sensor_width.IsValid() && width.IsValid()) { + minimum_under_all_settings = 0; + maximum_under_all_settings = sensor_width.GetValue() - width.GetInc(); + return; + } + } else if (node->GetName() == "OffsetY") { + GST_DEBUG("Apply OffsetY feature workaround"); + Pylon::CIntegerParameter sensor_height( + node->GetNodeMap()->GetNode("SensorHeight")); + Pylon::CIntegerParameter height(node->GetNodeMap()->GetNode("Height")); + /* try to shortcut if sensor height is available */ + if (sensor_height.IsValid() && height.IsValid()) { + minimum_under_all_settings = 0; + maximum_under_all_settings = sensor_height.GetValue() - height.GetInc(); + return; + } + } else if (node->GetName() == "AutoFunctionROIOffsetX") { + GST_DEBUG("Apply AutoFunctionROIOffsetX feature workaround"); + Pylon::CIntegerParameter sensor_width( + node->GetNodeMap()->GetNode("SensorWidth")); + Pylon::CIntegerParameter width(node->GetNodeMap()->GetNode("Width")); + /* try to shortcut if sensor width is available */ + if (sensor_width.IsValid() && width.IsValid()) { + minimum_under_all_settings = 0; + maximum_under_all_settings = sensor_width.GetValue() - width.GetInc(); + return; + } + } else if (node->GetName() == "AutoFunctionROIOffsetY") { + GST_DEBUG("Apply AutoFunctionROIOffsetY feature workaround"); + Pylon::CIntegerParameter sensor_height( + node->GetNodeMap()->GetNode("SensorHeight")); + Pylon::CIntegerParameter height(node->GetNodeMap()->GetNode("Height")); + /* try to shortcut if sensor height is available */ + if (sensor_height.IsValid() && height.IsValid()) { + minimum_under_all_settings = 0; + maximum_under_all_settings = sensor_height.GetValue() - height.GetInc(); + return; + } + } else if (node->GetName() == "AutoFunctionROIWidth") { + GST_DEBUG("Apply AutoFunctionROIWidth feature workaround"); + Pylon::CIntegerParameter sensor_width( + node->GetNodeMap()->GetNode("SensorWidth")); + /* try to shortcut if sensor width is available */ + if (sensor_width.IsValid()) { + minimum_under_all_settings = 0; + maximum_under_all_settings = sensor_width.GetValue(); + return; + } + } else if (node->GetName() == "AutoFunctionROIHeight") { + GST_DEBUG("Apply AutoFunctionROIHeight feature workaround"); + Pylon::CIntegerParameter sensor_height( + node->GetNodeMap()->GetNode("SensorHeight")); + /* try to shortcut if sensor height is available */ + if (sensor_height.IsValid()) { + minimum_under_all_settings = 0; + maximum_under_all_settings = sensor_height.GetValue(); + return; + } } else if (node->GetName() == "AcquisitionBurstFrameCount") { minimum_under_all_settings = 1; maximum_under_all_settings = 1023; From ed6e9e3f49d67d44d1e3cd4c7341a6150030a664 Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Fri, 17 Mar 2023 14:43:27 +0100 Subject: [PATCH 26/27] create diff of gst and camera features --- tests/prototypes/featurewalker.py | 52 ++++++++-- tests/prototypes/list_all_features.py | 37 ------- tests/prototypes/list_missing_features.py | 116 ++++++++++++++++++++++ 3 files changed, 159 insertions(+), 46 deletions(-) delete mode 100644 tests/prototypes/list_all_features.py create mode 100755 tests/prototypes/list_missing_features.py diff --git a/tests/prototypes/featurewalker.py b/tests/prototypes/featurewalker.py index 3eade76..69fc641 100644 --- a/tests/prototypes/featurewalker.py +++ b/tests/prototypes/featurewalker.py @@ -1,6 +1,37 @@ +# Copyright (C) 2023 Basler AG +# +# +# 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. Neither the name of the copyright holder nor the names of +# its contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# from queue import Queue +from typing import List import pypylon.genicam as geni - import itertools @@ -22,11 +53,11 @@ class FeatureNode(object): def __init__( self, node, - selectors: [ - [FeatureSelector], - ] = [], + selectors: List[List[FeatureSelector,]] = None, ): self.node = node + if selectors is None: + selectors = [] self.selectors = selectors def __repr__(self): @@ -83,14 +114,17 @@ def get_feature_nodes(nodemap, with_selectors: bool, only_implemented: bool): worklist.put(c) continue + if type(current_node) in (geni.ICommand, geni.IRegister): + continue + # loop over all selector options of current node if with_selectors and len(current_node.Node.GetSelectingFeatures()): node_selectors = get_node_selectors(current_node) - for selectors in itertools.product(*node_selectors): - node_list.append(FeatureNode(current_node, selectors)) - - # for features with only a single selector output the direct form too - if len(current_node.Node.GetSelectingFeatures()) == 1: + if len(list(itertools.product(*node_selectors))) > 1: + for selectors in itertools.product(*node_selectors): + node_list.append(FeatureNode(current_node, selectors)) + else: + # for features with only a single selector output the direct form too node_list.append(FeatureNode(current_node)) else: node_list.append(FeatureNode(current_node)) diff --git a/tests/prototypes/list_all_features.py b/tests/prototypes/list_all_features.py deleted file mode 100644 index 5cfd178..0000000 --- a/tests/prototypes/list_all_features.py +++ /dev/null @@ -1,37 +0,0 @@ -#! /usr/bin/env python3 - -import featurewalker - -import pypylon.pylon as py - -def create_gst_feature_name(fn: featurewalker.FeatureNode, prefix: str): - f_name = f"{prefix}::{fn.node.Node.GetName()}" - for sel in fn.selectors: - f_name += f"-{sel.value}" - return f_name - - -tlf = py.TlFactory.GetInstance() - - -cam = py.InstantCamera(tlf.CreateFirstDevice()) -cam.Open() - - -features = featurewalker.get_feature_nodes( - cam.GetNodeMap(), with_selectors=True, only_implemented=True -) -for f in features: - print(create_gst_feature_name(f, "cam")) - -features = featurewalker.get_feature_nodes( - cam.GetStreamGrabberNodeMap(), with_selectors=True, only_implemented=True -) -for f in features: - print(create_gst_feature_name(f, "stream")) - -features = featurewalker.get_feature_nodes( - cam.GetTLNodeMap(), with_selectors=True, only_implemented=True -) -for f in features: - print(create_gst_feature_name(f, "tl")) diff --git a/tests/prototypes/list_missing_features.py b/tests/prototypes/list_missing_features.py new file mode 100755 index 0000000..97171e3 --- /dev/null +++ b/tests/prototypes/list_missing_features.py @@ -0,0 +1,116 @@ +#! /usr/bin/env python3 +# Copyright (C) 2023 Basler AG +# +# +# 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. Neither the name of the copyright holder nor the names of +# its contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +import featurewalker +import pypylon.pylon as py +import gi +import os + +gi.require_version("Gst", "1.0") +from gi.repository import Gst + +Gst.init() + + +def create_gst_feature_name(fn: featurewalker.FeatureNode, prefix: str): + f_name = f"{prefix}::{fn.node.Node.GetName()}" + for sel in fn.selectors: + f_name += f"-{sel.value}" + return f_name + +def patch_to_gst_name_convention(name: str): + return name.replace("_", "-") + +def get_pylon_features(device_index:int = 0): + tlf = py.TlFactory.GetInstance() + devs = tlf.EnumerateDevices() + # capture all_features + cam = py.InstantCamera(tlf.CreateDevice(devs[device_index])) + cam.Open() + + features = featurewalker.get_feature_nodes( + cam.GetNodeMap(), with_selectors=True, only_implemented=True + ) + cam_features = [] + for f in features: + cam_features.append(create_gst_feature_name(f, "cam")) + + + features = featurewalker.get_feature_nodes( + cam.GetStreamGrabberNodeMap(), with_selectors=True, only_implemented=True + ) + + stream_features = [] + for f in features: + stream_features.append(create_gst_feature_name(f, "stream")) + + features = featurewalker.get_feature_nodes( + cam.GetTLNodeMap(), with_selectors=True, only_implemented=True + ) + tl_features = [] + for f in features: + tl_features.append(create_gst_feature_name(f, "tl")) + + cam.Close() + + return cam_features, stream_features, tl_features + + +def get_gst_features(device_index:int = 0): + pipe = Gst.parse_launch(f"pylonsrc name=src device-index={device_index} ! fakesink") + pylon = pipe.get_by_name("src") + cam = pylon.get_child_by_name("cam") + stream = pylon.get_child_by_name("stream") + + + gst_cam_features = [] + for f in cam.props: + gst_cam_features.append(f"cam::{f.name}") + + + gst_stream_features = [] + for f in stream.props: + gst_stream_features.append(f"stream::{f.name}") + + return gst_cam_features, gst_stream_features, [] + + +print("MISSING FEATURES") +pylon_features = get_pylon_features(0) +gst_features = get_gst_features(0) + +for p_feat, g_feat in zip(pylon_features, gst_features): + for f in [x for x in p_feat if patch_to_gst_name_convention(x) not in set(g_feat)]: + print(f) + From 20ee61e82c9ee50bec07992b5bc00ba6c87a624d Mon Sep 17 00:00:00 2001 From: Thies Moeller Date: Mon, 20 Mar 2023 15:57:28 +0100 Subject: [PATCH 27/27] add info logging of feature accesses proper connection to the correct pylonsrc obj missing for now. requires further restructuring --- ext/pylon/gstpylon.cpp | 5 ++++- gst-libs/gst/pylon/gstpylonobject.cpp | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/ext/pylon/gstpylon.cpp b/ext/pylon/gstpylon.cpp index b530ec8..418b939 100644 --- a/ext/pylon/gstpylon.cpp +++ b/ext/pylon/gstpylon.cpp @@ -793,9 +793,11 @@ gboolean gst_pylon_set_configuration(GstPylon *self, const GstCaps *conf, Pylon::CIntegerParameter width(nodemap, "Width"); width.SetValue(gst_width, Pylon::IntegerValueCorrection_None); + GST_INFO("Set Feature Width: %d", gst_width); Pylon::CIntegerParameter height(nodemap, "Height"); height.SetValue(gst_height, Pylon::IntegerValueCorrection_None); + GST_INFO("Set Feature Height: %d", gst_height); Pylon::CBooleanParameter framerate_enable(nodemap, "AcquisitionFrameRateEnable"); @@ -804,13 +806,14 @@ gboolean gst_pylon_set_configuration(GstPylon *self, const GstCaps *conf, framerate_enable.TrySetValue(true); gdouble div = 1.0 * gst_numerator / gst_denominator; - if (self->camera->GetSfncVersion() >= Pylon::Sfnc_2_0_0) { Pylon::CFloatParameter framerate(nodemap, "AcquisitionFrameRate"); framerate.TrySetValue(div, Pylon::FloatValueCorrection_None); + GST_INFO("Set Feature AcquisitionFrameRate: %f", div); } else { Pylon::CFloatParameter framerate(nodemap, "AcquisitionFrameRateAbs"); framerate.TrySetValue(div, Pylon::FloatValueCorrection_None); + GST_INFO("Set Feature AcquisitionFrameRateAbs: %f", div); } } catch (const Pylon::GenericException &e) { diff --git a/gst-libs/gst/pylon/gstpylonobject.cpp b/gst-libs/gst/pylon/gstpylonobject.cpp index 0de16aa..ce9574c 100644 --- a/gst-libs/gst/pylon/gstpylonobject.cpp +++ b/gst-libs/gst/pylon/gstpylonobject.cpp @@ -206,6 +206,7 @@ static void gst_pylon_object_set_pylon_feature(GstPylonObjectPrivate* priv, const gchar* name) { P param(*priv->nodemap, name); param.SetValue(get_value(value)); + GST_INFO("Set Feature %s: %s", name, param.ToString().c_str()); } template <> @@ -223,6 +224,8 @@ void gst_pylon_object_set_pylon_feature( Pylon::EIntegerValueCorrection::IntegerValueCorrection_Nearest); } else param.SetValue(get_value(value)); + + GST_INFO("Set Feature %s: %s", name, param.ToString().c_str()); } template <> @@ -240,6 +243,7 @@ void gst_pylon_object_set_pylon_feature( Pylon::EFloatValueCorrection::FloatValueCorrection_ClipToRange); } else param.SetValue(get_value(value)); + GST_INFO("Set Feature %s: %s", name, param.ToString().c_str()); } template <> @@ -248,6 +252,7 @@ void gst_pylon_object_set_pylon_feature( const gchar* name) { Pylon::CEnumParameter param(*priv->nodemap, name); param.SetIntValue(get_value(value)); + GST_INFO("Set Feature %s: %s", name, param.ToString().c_str()); } /* Get gst property from pylon feature */ @@ -257,6 +262,7 @@ static void gst_pylon_object_get_pylon_feature(GenApi::INodeMap& nodemap, const gchar* name) { P param(nodemap, name); set_value(value, param.GetValue()); + GST_DEBUG("Get Feature %s: %s", name, param.ToString().c_str()); } template <> @@ -265,6 +271,7 @@ void gst_pylon_object_get_pylon_feature( const gchar* name) { Pylon::CEnumParameter param(nodemap, name); set_value(value, param.GetIntValue()); + GST_DEBUG("Get Feature %s: %s", name, param.ToString().c_str()); } template <> @@ -273,6 +280,7 @@ void gst_pylon_object_get_pylon_feature( const gchar* name) { Pylon::CStringParameter param(nodemap, name); set_value(value, param.GetValue().c_str()); + GST_DEBUG("Get Feature %s: %s", name, param.ToString().c_str()); } void gst_pylon_object_set_pylon_selector(GenApi::INodeMap& nodemap, @@ -283,9 +291,15 @@ void gst_pylon_object_set_pylon_selector(GenApi::INodeMap& nodemap, switch (selector_type) { case GenApi::intfIEnumeration: Pylon::CEnumParameter(nodemap, selector_name).SetIntValue(selector_value); + GST_INFO( + "Set Selector-Feature %s: %s", selector_name, + Pylon::CEnumParameter(nodemap, selector_name).ToString().c_str()); break; case GenApi::intfIInteger: Pylon::CIntegerParameter(nodemap, selector_name).SetValue(selector_value); + GST_INFO( + "Set Selector-Feature %s: %s", selector_name, + Pylon::CIntegerParameter(nodemap, selector_name).ToString().c_str()); break; default: std::string error_msg = "Selector \"" + std::string(selector_name) + @@ -300,7 +314,9 @@ template static T gst_pylon_object_get_pylon_property(GenApi::INodeMap& nodemap, const gchar* name) { P param(nodemap, name); - return param.GetValue(); + T val = param.GetValue(); + GST_DEBUG("Get Feature %s: %s", name, param.ToString().c_str()); + return val; } template