GenApi 3.5.1
ValueArrayAdapter.h
1//-----------------------------------------------------------------------------
2// (c) 2020 by SICK AG
3// Author: Mattias Johannesson
4//
5// License: This file is published under the license of the EMVA GenICam Standard Group.
6// A text file describing the legal terms is included in your installation as 'GenICam_license.pdf'.
7// If for some reason you are missing this file please contact the EMVA or visit the website
8// (http://www.genicam.org) for a full copy.
9//
10// THIS SOFTWARE IS PROVIDED BY THE EMVA GENICAM STANDARD GROUP "AS IS"
11// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
12// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
13// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE EMVA GENICAM STANDARD GROUP
14// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
15// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
16// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
17// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
18// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
20// POSSIBILITY OF SUCH DAMAGE.
21//
22//-----------------------------------------------------------------------------
28#ifndef VALUE_ARRAY_ADAPTER_H
29#define VALUE_ARRAY_ADAPTER_H
30
31#include <Base/GCException.h>
32#include <GenApi/GenApi.h>
33#include <GenApi/Synch.h>
34
35#include <algorithm>
36#include <exception>
37#include <functional>
38#include <string>
39#include <vector>
40#include <iterator>
41
42/* Unified access to byte-swapping tools, possibly candidate to get
43 * extracted/extended to a reusable header. */
44#ifdef _MSC_VER
45 #include <cstdlib>
46 #define SW16(s) _byteswap_ushort(s)
47 #define SW32(s) _byteswap_ulong(s)
48 #define SW64(s) _byteswap_uint64(s)
49#elif __APPLE__
50 #include <libkern/OSByteOrder.h>
51 #define SW16(s) __builtin_bswap16( (s) )
52 #define SW32(s) __builtin_bswap32( (s) )
53 #define SW64(s) __builtin_bswap64( (s) )
54#else
55 #include <byteswap.h>
56 #define SW16(s) bswap_16(s)
57 #define SW32(s) bswap_32(s)
58 #define SW64(s) bswap_64(s)
59#endif
60
61namespace GENAPI_NAMESPACE
62{
63
64 /* Helper mini-template to allow specialized template conversions.
65 * Might be reused (or eliminated) in future, for now intended mainly to
66 * implement conversion to bool and numeric types differently.
67 * In future we might check if some type limits checking or similar is
68 * desirable. */
69 template<typename TTargetType>
70 class TypeConverter
71 {
72 public:
73 template<typename TSourceType>
74 static TTargetType Convert (TSourceType src)
75 {
76 return static_cast<TTargetType>(src);
77 }
78 };
79
80 template<>
81 class TypeConverter<bool>
82 {
83 public:
84 template<typename TSourceType>
85 static bool Convert (TSourceType src)
86 {
87 return 0 != src;
88 }
89 };
90
91 /* Helper mini-template to extract byte-swapped values for all numeric
92 * types (incl. floats).
93 * Reverse-copy by default, attempt to perform dedicated swaps for
94 * known sizes. */
95 template<size_t TValueSize>
96 class SwapExtractor
97 {
98 public:
99 static void Extract (const void *src, void *dst)
100 {
101 std::reverse_copy(reinterpret_cast<const uint8_t*>(src),
102 reinterpret_cast<const uint8_t*>(src) + TValueSize,
103 reinterpret_cast<uint8_t*>(dst));
104 }
105 };
106 template<>
107 class SwapExtractor<8>
108 {
109 public:
110 static void Extract (const void *src, void *dst)
111 {
112 *reinterpret_cast<uint64_t*>(dst) = SW64(*reinterpret_cast<const uint64_t*>(src));
113 }
114 };
115 template<>
116 class SwapExtractor<4>
117 {
118 public:
119 static void Extract (const void *src, void *dst)
120 {
121 *reinterpret_cast<uint32_t*>(dst) = SW32(*reinterpret_cast<const uint32_t*>(src));
122 }
123 };
124 template<>
125 class SwapExtractor<2>
126 {
127 public:
128 static void Extract (const void *src, void *dst)
129 {
130 *reinterpret_cast<uint16_t*>(dst) = SW16(*reinterpret_cast<const uint16_t*>(src));
131 }
132 };
133 template<>
134 class SwapExtractor<1>
135 {
136 public:
137 static void Extract (const void *src, void *dst)
138 {
139 *reinterpret_cast<uint8_t*>(dst) = *reinterpret_cast<const uint8_t*>(src);
140 }
141 };
142
151 class GENAPI_DECL CValueArrayAdapterBase
152 {
153 protected:
154 CValueArrayAdapterBase(IValue* base_value, IInteger *selector);
155 virtual ~CValueArrayAdapterBase ();
156 private:
157 /* Not copyable */
160
161 protected:
162 /* Retrieves current status of the value array parameters and reads
163 * "shadow copy" of the memory holding the array from the port.
164 * It is caller's responsibility to apply the lock as needed.
165 * Note: the lock is recursive and ReadFromPort() uses only read operations,
166 * no write that would trigger callbacks, it is therefore safe to apply
167 * the lock from outside. */
168 void ReadFromPort ();
169 CLock& GetLock() const;
170
171 protected:
172 /* Flag indicating successful parse of the dependencies and initialization
173 * of the adapter - if false, the adapter cannot be used. */
174 bool is_valid;
175 /* Static information sniffed from the XML/nodemap at time of creation
176 * of the adapter - info about the structure that does not change at runtime.
177 * These are guaranteed to be up-to-date once the instance is constructed. */
178 bool is_int_reg;
179 bool swap_endian;
180 bool is_signed;
181 bool masked_int;
182 uint32_t lsbit;
183 uint32_t msbit;
184 uint64_t lsbit_mask;
185 /* Dynamic values used for every single read. In particular read copy
186 * of the register itself.
187 * These are updated during each call to ReadFromPort() that should happen
188 * just before attempt to extract the actual values. */
189 uint64_t current_base_address;
190 uint64_t current_reg_length;
191 uint64_t current_address_step;
192 size_t current_num_values;
193 uint8_t *current_array_shadow;
194
195 private:
196 /* Implementation details */
197 struct ValueArrayInternals;
198 ValueArrayInternals* pinternal;
199 };
200
215 template<typename TValueNodeType, typename TOutputValueType, typename TEffectiveValueType = TOutputValueType>
217 {
218 public:
226 CValueArrayAdapter(TValueNodeType* base_value, IInteger *selector)
227 : CValueArrayAdapterBase (base_value, selector),
228 current_array_getter(NULL)
229 { }
230
239 bool IsValid() const
240 {
241 return is_valid;
242 }
243
266 std::vector<TOutputValueType> GetAllValues()
267 {
268 std::vector<TOutputValueType> values(current_num_values);
269 GetAllValues (values);
270 return values;
271 }
272
300 void GetAllValues(std::vector<TOutputValueType> &values)
301 {
302 if (!is_valid)
303 {
304 throw RUNTIME_EXCEPTION("Invalid value array adapter");
305 }
306
307 /* Lock the entire operation, protecting not only against concurrent
308 * changes of underlying nodemap parameters, but also against concurrent
309 * accessess to the value array adapter itself. */
310 AutoLock l(GetLock());
311
312 /* Freeze current state of all the dynamic properties and read the register. */
313 PrepareValues ();
314
315 assert (current_array_getter != NULL);
316 (this->*current_array_getter) (values);
317 }
318
332 static bool CheckAdvertisedCompatibility(TValueNodeType* base_value, IInteger *selector)
333 {
334 try
335 {
336 if (!base_value || !selector)
337 {
338 return false;
339 }
340
341 /* The nodes must necessarily belong to the same nodemap */
342 INodeMap *node_map = base_value->GetNode()->GetNodeMap();
343 if (node_map != selector->GetNode()->GetNodeMap())
344 {
345 return false;
346 }
347
348 /* The set of candidates is per SFNC advertised through string feature
349 * ValueArrayCandidates. */
350 CStringPtr candidates_node = node_map->GetNode("ValueArrayCandidates");
351 if (!candidates_node.IsValid() || !IsReadable(candidates_node))
352 {
353 return false;
354 }
355 GENICAM_NAMESPACE::gcstring rawstr = candidates_node->GetValue ();
356
357 /* Erase everything starting from a first occurence of the '#' character.
358 * This is a backdoor to allow extending the ValueArrayCandidates format
359 * in a backward compatible way. */
360 GENICAM_NAMESPACE::gcstring candidates_str = rawstr.substr (0, rawstr.find_first_of("#"));
361
362 /* The list of adapter candidates is encoded (per SFNC) as comma
363 * separated list of Value[Selector] entries. */
364 GENICAM_NAMESPACE::gcstring_vector tokens;
365 Tokenize(candidates_str, tokens, ",");
366
367 /* Finally check if the requested value/selector pair is in the list. */
368 GENICAM_NAMESPACE::gcstring wanted = base_value->GetNode()->GetName()
369 + "[" + selector->GetNode()->GetName() + "]";
370 return tokens.contains (wanted);
371 }
372 catch (const GenICam::GenericException &)
373 {
374 return false;
375 }
376 }
377
378 private:
379 /* Array getter implementations for individual field types.
380 * Implemented as complete array getter rather than just single value getter
381 * called from a loop to allow for unrolling-related compiler optimizations. */
382 template<typename TFieldType>
383 void GetArrayOfFieldValues (std::vector<TOutputValueType> &values)
384 {
385 assert (current_reg_length!=0 && current_address_step != 0);
386
387 /* Resize the output (note that vector capacity does not change if
388 * the original is bigger than needed). */
389 values.resize (current_num_values);
390
391 /* Extract the values.
392 * The memory read into our shadow copy starts at first desired value
393 * (see PrepareValues), therefore we start at offset 0. */
394 if (swap_endian)
395 {
396 ExtractFieldValues_Swap<TFieldType> (current_num_values,
397 static_cast<size_t>(current_address_step),
398 current_array_shadow, &values[0]);
399 }
400 else
401 {
402 ExtractFieldValues_Noswap<TFieldType> (current_num_values,
403 static_cast<size_t>(current_address_step),
404 current_array_shadow, &values[0]);
405 }
406 }
407 template<typename TFieldType>
408 void GetArrayOfFieldBits (std::vector<TOutputValueType> &values)
409 {
410 assert (current_reg_length!=0 && current_address_step != 0);
411
412 /* Resize the output (note that vector capacity does not change if
413 * the original is bigger than needed). */
414 values.resize (current_num_values);
415
416 /* Extract the values.
417 * The memory read into our shadow copy starts at first desired value
418 * (see PrepareValues), therefore we start at offset 0. */
419 if (swap_endian)
420 {
421 ExtractFieldBits_Swap<TFieldType> (current_num_values,
422 static_cast<size_t>(current_address_step),
423 lsbit_mask, lsbit,
424 current_array_shadow, &values[0]);
425 }
426 else
427 {
428 ExtractFieldBits_Noswap<TFieldType> (current_num_values,
429 static_cast<size_t>(current_address_step),
430 lsbit_mask, lsbit,
431 current_array_shadow, &values[0]);
432 }
433 }
434 /* Static workers responsible for the main-loop part of the GetArray-functions.
435 * Separating similar workers with code duplication and using all params as
436 * local variables instead of referring to member variables to assist
437 * optimizer as much as possible. */
438 template<typename TFieldType>
439 static void ExtractFieldValues_Noswap(size_t num_values, size_t address_step,
440 const uint8_t *src, TOutputValueType *dst)
441 {
442 for (size_t i = 0; i < num_values; ++i)
443 {
444 const uint8_t *field_ptr = src + i * address_step;
445
446 TFieldType field_value = *reinterpret_cast<const TFieldType*>(field_ptr);
447 dst[i] = static_cast<TOutputValueType>(EffectiveValue (field_value));
448 }
449 }
450 template<typename TFieldType>
451 static void ExtractFieldValues_Swap(size_t num_values, size_t address_step,
452 const uint8_t *src, TOutputValueType *dst)
453 {
454 for (size_t i = 0; i < num_values; ++i)
455 {
456 const uint8_t *field_ptr = src + i * address_step;
457
458 TFieldType field_value = ReadSwapped<TFieldType> (field_ptr);
459 dst[i] = static_cast<TOutputValueType>(EffectiveValue (field_value));
460 }
461 }
462 template<typename TFieldType>
463 static void ExtractFieldBits_Noswap(size_t num_values, size_t address_step,
464 uint64_t mask, size_t shift,
465 const uint8_t *src, TOutputValueType *dst)
466 {
467 for (size_t i = 0; i < num_values; ++i)
468 {
469 const uint8_t *field_ptr = src + i * address_step;
470
471 TFieldType field_value = *reinterpret_cast<const TFieldType*>(field_ptr);
472 uint64_t field_bits = mask & (field_value >> shift);
473 dst[i] = static_cast<TOutputValueType>(EffectiveValue (field_bits));
474 }
475 }
476 template<typename TFieldType>
477 static void ExtractFieldBits_Swap(size_t num_values, size_t address_step,
478 uint64_t mask, size_t shift,
479 const uint8_t *src, TOutputValueType *dst)
480 {
481 for (size_t i = 0; i < num_values; ++i)
482 {
483 const uint8_t *field_ptr = src + i * address_step;
484
485 TFieldType field_value = ReadSwapped<TFieldType> (field_ptr);
486 uint64_t field_bits = mask & (field_value >> shift);
487 dst[i] = static_cast<TOutputValueType>(EffectiveValue (field_bits));
488 }
489 }
490
491 /* Common worker for the public read-functions.
492 * Prepares for effective value-by-value read - computes current values
493 * of the possibly dynamic parameters, reads from the register into
494 * the shadow copy. */
495 void PrepareValues()
496 {
497 /* Scan the current dependencies and read the underlying memory of the
498 * value array from the port. Ready for extraction.
499 * This updates all the array "coordinates" (address/step/etc.) and
500 * gets the memory blob from which it can be extracted. */
501 ReadFromPort ();
502
503 /* Finally, knowing all parameters about the array, we can construct
504 * the value getter function. */
505 if (is_int_reg)
506 {
507 /* Integer register */
508 if (masked_int)
509 {
510 /* Masked integer value.
511 * Supported only with unsigned MaskedIntReg registers, signed refused during consruction. */
512 if (is_signed)
513 {
514 assert (false && "Signed MaskeIntReg should be refused in constructor");
515 throw RUNTIME_EXCEPTION("Unsupported signed masked integer register");
516 }
517
518 /* Unsigned integer */
519 switch (current_reg_length)
520 {
521 case 8:
522 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldBits<uint64_t>;
523 break;
524
525 case 4:
526 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldBits<uint32_t>;
527 break;
528
529 case 2:
530 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldBits<uint16_t>;
531 break;
532
533 case 1:
534 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldBits<uint8_t>;
535 break;
536
537 default:
538 throw RUNTIME_EXCEPTION("Unsupported unsigned integer register length");
539 }
540 } /* if (masked_int) */
541 else
542 {
543 /* Full (not masked) integer value */
544 if (is_signed)
545 {
546 /* Signed integer */
547 switch (current_reg_length)
548 {
549 case 8:
550 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<int64_t>;
551 break;
552
553 case 4:
554 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<int32_t>;
555 break;
556
557 case 2:
558 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<int16_t>;
559 break;
560
561 case 1:
562 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<int8_t>;
563 break;
564
565 default:
566 throw RUNTIME_EXCEPTION("Unsupported signed integer register length");
567 }
568 } /* if (signed) */
569 else
570 {
571 /* Unsigned integer */
572 switch (current_reg_length)
573 {
574 case 8:
575 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<uint64_t>;
576 break;
577
578 case 4:
579 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<uint32_t>;
580 break;
581
582 case 2:
583 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<uint16_t>;
584 break;
585
586 case 1:
587 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<uint8_t>;
588 break;
589
590 default:
591 throw RUNTIME_EXCEPTION("Unsupported unsigned integer register length");
592 }
593 } /* if-else (signed) */
594 } /* if-else (masked_int) */
595 } /* if (is_int_reg) */
596 else
597 {
598 /* Float register. */
599 switch (current_reg_length)
600 {
601 case 8:
602 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<float64_t>;
603 break;
604
605 case 4:
606 current_array_getter = &CValueArrayAdapter::GetArrayOfFieldValues<float32_t>;
607 break;
608
609 default:
610 throw RUNTIME_EXCEPTION("Unsupported float register length");
611 }
612 } /* if-else (is_int_reg) */
613 }
614
615 template<typename TRawType>
616 static TEffectiveValueType EffectiveValue (TRawType raw_value)
617 {
618 return TypeConverter<TEffectiveValueType>::Convert(raw_value);
619 }
620
621 template<typename TFieldType>
622 static TFieldType ReadSwapped (const void *ptr)
623 {
624 TFieldType result;
625 SwapExtractor<sizeof(TFieldType)>::Extract (ptr, &result);
626 return result;
627 }
628
629 private:
630 typedef void (CValueArrayAdapter::*ArrayGetterFunction)(std::vector<TOutputValueType> &values);
631 ArrayGetterFunction current_array_getter;
632 };
633
634
640 class CIntegerValueArray : public CValueArrayAdapter<IInteger, int64_t>
641 {
642 public:
643 CIntegerValueArray(IInteger *base_value, IInteger *selector)
644 : CValueArrayAdapter<IInteger, int64_t> (base_value, selector)
645 {
646 }
647 };
648
654 class CFloatValueArray : public CValueArrayAdapter<IFloat, double>
655 {
656 public:
657 CFloatValueArray(IFloat *base_value, IInteger *selector)
658 : CValueArrayAdapter<IFloat, double> (base_value, selector)
659 {
660 }
661 };
662
669 class CBooleanValueArray : public CValueArrayAdapter<IBoolean, uint8_t, bool>
670 {
671 public:
672 CBooleanValueArray(IBoolean *base_value, IInteger *selector)
673 : CValueArrayAdapter<IBoolean, uint8_t, bool> (base_value, selector)
674 {
675 }
676 };
677
678}
679
680#endif // VALUE_ARRAY_ADAPTER_H
681
Standard GenICam Exceptions.
Main include file for using GenApi with smart pointers.
virtual CLock & GetLock() const =0
Returns the lock which guards the node map.
Definition of Lock classes.
Concrete value array implementation to be used with IBoolean based target value nodes.
Definition ValueArrayAdapter.h:670
Concrete value array implementation to be used with IFloat based target value nodes.
Definition ValueArrayAdapter.h:655
Concrete value array implementation to be used with IInteger based target value nodes.
Definition ValueArrayAdapter.h:641
A lock class.
Definition Synch.h:62
Encapsulates a GenApi pointer dealing with the dynamic_cast automatically.
Definition Pointer.h:50
bool IsValid() const
true if the pointer is valid
Definition Pointer.h:109
Base class wrapping internal implementation details of the value array adapter functionality.
Definition ValueArrayAdapter.h:152
Adapter for accessing structured register known to include an array of selector-iterated values.
Definition ValueArrayAdapter.h:217
CValueArrayAdapter(TValueNodeType *base_value, IInteger *selector)
Creates a CValueArrayAdapter object.
Definition ValueArrayAdapter.h:226
void GetAllValues(std::vector< TOutputValueType > &values)
Get all values of the array.
Definition ValueArrayAdapter.h:300
std::vector< TOutputValueType > GetAllValues()
Get all values of the array.
Definition ValueArrayAdapter.h:266
bool IsValid() const
Check if the instance is valid and useable.
Definition ValueArrayAdapter.h:239
static bool CheckAdvertisedCompatibility(TValueNodeType *base_value, IInteger *selector)
Check value/selector feature pair suitability for use with the adapter.
Definition ValueArrayAdapter.h:332
A string class which is a clone of std::string.
Definition GCString.h:51
#define RUNTIME_EXCEPTION
Fires a runtime exception, e.g. throw RUNTIME_EXCEPTION("buh!")
Definition GCException.h:246
GENICAM_INTERFACE GENAPI_DECL_ABSTRACT IBoolean
Interface for Boolean properties.
Definition IBoolean.h:59
GENICAM_INTERFACE INodeMap
Interface to access the node map.
Definition INode.h:50
GENICAM_INTERFACE GENAPI_DECL_ABSTRACT IValue
Interface for value properties.
Definition IValue.h:59
GENICAM_INTERFACE GENAPI_DECL_ABSTRACT IFloat
Interface for float properties.
Definition IFloat.h:58
GENICAM_INTERFACE IInteger
Interface for integer properties.
Definition IFloat.h:118
bool IsReadable(EAccessMode AccessMode)
Tests if readable.
Definition INode.h:176