Skip to content

CameraDriver Developer Documentation

Introduction and Goal

This document is intended to provide a more comprehensive developer manual for the SensorAdapter and CameraDriver API, and to help developers better understand the example applications of the SensorAdapter.

In order to fully understand this document, one should have a knowledge of the basics of the SensorAdapter architecture. If not so, it is recommended to read Sensor Adapter Architecture section first, and then continue with this manual. It is advised to be familiar with the Databus system component (read section Sinumerik Edge DataBus API and Configuration) as well.

In the first section we overview both the SensorAdapter and CameraDriver public interfaces, collect developer-relevant classes and functions, and describe them in brief. After that, we present an example code, which introduces the CameraDriver and its functionalities, what the APIs are capable of. From a developer's point of view, we tear down the example code (C++ and python) for the SensorAdapter, and explain the sensoradapter-, and cameradriver-specific code lines one by one.

Content of the SDKs

On the following figure you can find the list of the included files in both SDKs (sensoradapter_sdk and cameradriver_sdk).

sensoradapter_sdk

  • ISensorGateway.hpp: anyone who wants to develop a driver needs to derive from this class and implement its virtual methods.
  • SensorPayloadReader.hpp: use for decoding the sensor data received from the driver
  • SensorAdapter.py: python version of the SensorAdapter API generated from the C++ version by SWIG. Includes SensorPayloadReader python class.
  • __init__.py: it makes importing the SensorAdapter package easier
  • SensorAdapter.so: dynamic library

cameradriver_sdk:

  • LiveStreamPackageReader.hpp: described later
  • SensorImage.hpp: described later
  • CameraSensor.py: python version of the SensorAdapter API generated from the C++ version by SWIG. Includes SensorImage.py and LiveStreamPackageReader.py python classes.
  • __init\__.py: it makes importing the CameraDriver package easier
  • CameraSensor.so: dynamic library

image

Overview of SensorAdapter API

In this section we overview the SensorAdapter public API, collect developer-relevant classes and functions, and describe them in brief.

There are two classes worth mentioning: SensorPayload and SensorPayloadReader. Though the SensorPayload is not included in the sensoradapter_sdk, it is harder to explain the reader without knowing the payload format it reads. From a developer-perspective, it is sufficient to understand the SensorPayloadReader, but we found it important to describe the logic behind the SensorPayload as well, in order for the developer to fully comprehend the principles.

  • The SensorPayload class is a binary dataformat, in which we encode and pack the production-related sensor data provided by the driver (e.g. camera_driver), and the SensorServer publishes it on the Databus. It is not used on the Client side.
  • The SensorPayloadReader class is used for decoding a SensorPayload format, and access the desired datapoint values and metainfo from it. After receiving the message on the Databus, the payload can be passed to the SensorPayloadReader.

image image

SensorPayload class

When the production-related sensor data is collected (for example a stream of an IP camera) on the SensorAdapter's SensorServer side, the sensor data is encoded into a specific binary format, called SensorPayload. This dataformat stores multiple datapoint-value pairs in a well-structured way, and is optimized to access them in an instant. After having prepared the SensorPayload, an AppSDK Producer publishes the package to the Databus, on a messageID predefined in the configuration.

The structure of the dataformat is made of two main parts: a header and a body. The header's task is being like an itinerary, which is responsible for keeping record of each datapoint name, and where the corresponding values are positioned inside the body. In addition, the header stores meta information about the whole package, like the total length of the payload, or the length of the header itself. The body's single purpose is to store the values for each datapoint in a compact way.

The class includes the following public methods:

  • Default constructor and destructor:

        SensorPayload() {}  
    
        ~SensorPayload() {}
    

  • Method, which adds a Datapoint-Value pair into SensorPayload with the strDatapointName datapoint name and strValue value of the corresponding datapoint.

        void add(const std::string& strDatapointName, const std::string& strValue)
    

  • Method, which adds a Datapoint-Value pair into SensorPayload with the strDatapointName datapoint name, strValue value of the corresponding datapoint, and nValueSize size of the corresponding value in bytes.

        void add(const std::string& strDatapointName, const char* tValue, const SPRsizeType nValueSize)
    

  • Method, which retrieves all Datapoint-Value pairs from SensorPayload in binary format

        std::string getSensorPayload();
    

  • Setter and Getter methods for managing the StatusFlag (the StatusFlag show whether the SensorPayload was created successfully)

        void setError(const bool bHasErrorFlag)
        bool hasError()
    

  • Method, which clears all contents (all Datapoint-Value pairs) from SensorPayload

        void clear()
    

SensorPayloadReader class

On the Client side an AppSDK Consumer receives a SensorPayload package on the Databus, published by the SensorAdapter. There needs to be a method/way to decode the message. The SensorPayloadReader is responsible for extracting datapoints' names and their values, using its own itinerary to interpret the SensorPayload format.

After passing a SensorPayload object in std::string or a char* type to the SensorPayloadReader's constructor, it will decode the binary format. As a consequence the user is able to extract the desired values and the corresponding metainfo by calling getter functions.

How does it read out desired data? In a nutshell, the reader possesses a map which helps orienting in the structure. Whenever the user likes to get a datapoint's value or ask for metainfo about the payload, the SensorPayloadReader uses its map to find and return the desired data.

The class includes the following public methods:

  • Constructor - creates an SensorPayloadReader instance and extracts metainfo from the passed SensorPayload std::string parameter/argument.

        `explicit SensorPayloadReader(const std::string& strSensorPayloadString)`
    

  • Constructor - creates an SensorPayloadReader instance and extracts metainfo from the passed SensorPayload char* and its length parameters/arguments.

        SensorPayloadReader(char* pSensorPayloadString, int nLength)
    

  • Supplies the list of datapoint names of the SensorPayload and returns a list (std::vector) of the datapoint names

        const std::vector<std::string>& getDatapointNames() const
    

  • Supplies the value of a given datapoint whose name is passed as the function argument. It returns an empty string if the datapoint is not in the SensorPayload.

        std::string getValue(const std::string& strDatapointName) const
    

Overview of CameraDriver API

Short, detailed description of the CameraDriver

  • This version of the camera driver supports sampling access to an "image" and aggregation access to a "livestream" of the camera.
  • Uses h264 and RTSP over TCP for reliability
  • Sinumerik Edge IPC227E Nanobox NFRs:
  • Supports multiple brands of cameras with a wide range of resolutions from 480p to 1440p
  • Image capture supports up to 5 fps (1080p), Livestream up to 15 fps (480p)
  • Images are provided as raw RGB images for easy processing.
  • Livestream frames are provided as binary jpg data for easy display on a web interface. Also provides frametime data to facilitate playback.
  • Provides a simple header-only library for image extraction for app-developers in C++ and Python
  • Livestream can only support one subscription in this version.

In this section we overview the CameraDriver public API, collect developer-relevant classes and methods, and describe them in brief.

There are two important classes SensorImage and LiveStreamPackageReader. They are both included in the cameradriver_sdk,

  • The SensorImage class represents a structure which gets a binary image in string format and provides methods for displaying it in human readable formats.
  • The LiveStreamPackageReader class represents a structure which gets LiveStreamPackage extracted from livestream datapoint in string format. Then it provides methods for displaying images and getting metainfo about the package.

image

SensorImage class

This class represents a structure which gets a binary image in string format (with the additional meta data in the header) and provides methods for displaying it in human readable formats (print in ASCII or save in ppm).

The provided binary image is unable to be loaded, in case its format does not meet the requirements. It's easy to recognize bad format, as calling any methods of the class will return either 0, false or nullptr. The binary format contains:

  • width of the image
  • height of the image
  • the timestamp of the image. It represents the time when the frame was read from the network video stream.
  • raw image in binary format
  • umls

The class includes the following public methods:

  • Constructor - Creating a SensorImage instance from the provided binary image in string format.

        explicit SensorImage(const std::string& strImage)
    

  • Printing the picture on the standard output by assigning a predefined ASCII char for each pixels. The user needs to provide the desired dimensions (width, height) of the picture in which it will be displayed. It returns false, in case the provided binary image failed on the format check previously.

        bool printAscii(const uint16_t unAsciiWidth, const uint16_t unAsciiHeight)
    

  • Saving image in ppm format. Only name of the file that the image will be written into is to be provided. It returns false, in case the provided binary image failed on the format check previously.

        bool savePPM(const std::string& strFileName)
    

  • Returns pointer for image It returns nullptr, in case the provided binary image failed on the format check previously.

        const char* getData () const
    

  • Returns the size of image. It returns 0, in case the provided binary image failed on the format check previously.

        size_t getDataLength() const
    

  • Returns width of image It returns 0, in case the provided binary image failed on the format check previously.

        const uint16_t& getWidth() const
    

  • Returns height of image It returns 0, in case the provided binary image failed on the format check previously.

        const uint16_t& getHeight() const
    

  • Returns timestamp of image It returns 0, in case the provided binary image failed on the format check previously.

        const uint64_t& getTimestamp() const 
    

LivestreamPackageReader class

In order to interpret the string received on the livestream datapoint, we need to have a class, which extracts and processes the livestream package. The LiveStreamPackageReader class plays this role, and provides methods for saving the images and getting meta info about the package. The structure of the dataformat is very similar to that of SensorPayload class. It is made of two main parts: a header and a body. The header's task is being like an itinerary, which is responsible for keeping record of each frame in the package, and where the corresponding raw content (the image itself) is positioned inside the body. Also, it contains meta information about the package. The body stores the raw image for each frame in a compact way.

The header stores meta information about the whole package:

  • number (count) of the frames in the package
  • size of the package
  • frame time average of all frames in the package
  • timestamp of the last frame

The class includes the following public methods:

  • Constructor - creates an LiveStreamPackageReader instance and extracts meta info from the passed sensor payload string parameter/argument.

        explicit LiveStreamPackageReader(const std::string& strLiveStreamPackage)
    

  • Returns the current state of the LiveStreamPackageReader

        ReaderState getState() const
    

  • Returns the number of the frames in the current live stream package

        uint64_t getFrameCountInBuffer() const
    

  • Returns the size in bytes of the current live stream package

        uint64_t getPackageSize() const
    

  • Returns the average time between two consecutive frames in the package

        uint64_t getFrameTimeAverage() const
    

  • Returns the timestamp of the last frame in the current live stream package

        uint64_t getTimeStampOfPackage() const
    

  • Returns the image in binary format of a given frame. The paramater unIndex (0, 1, ...) is the subscription of the frame in the live stream package

        std::string getImage(uint64_t unIndex) const
    

  • Returns the list of the counters in the current live stream package

        const std::vector<uint64_t>& getFrameCounters() const
    

  • Returns the image in binary format of a nFrameCounter-nth frame.

    std::string getImageByFrameCounter(uint64_t nFrameCounter) const {
    

Example code

The example application of the SensorAdapter intends to introduce the functionalities of the SensorAdapter and CameraDriver API through a generic use-case.

There are 3 sensor_sample_consumer applications:

  • sensor_sample_consumer_1: C++ application, subscribing to image and livestream datapoints
  • sensor_sample_consumer_2: python application, subscribing only to image datapoint.
  • sensor_sample_consumer_3: python application, subscribing to image and livestream datapoints

First we explain the sensor_sample_consumer_1 example code written in C++. We tear the code down line by line showing the developer the sensoradapter, and cameradrver specific usages. Second we spare some words on the sensor_sample_consumer_3 written in python.

The whole example code (both in C++ and python) will be stated in the Appendix.

Config

We have an individual description about the configuration of the SensorAdapter in SensorAdapter configuration (CameraDriver).

C++

Include necessary standard library components, Databus from AppSDK, and the 3 public headers from the sensoradapter_sdk and the cameradriver_sdk.

    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <string>

    #include "Databus.h"
    #include "SensorPayloadReader.hpp"
    #include "SensorImage.hpp"
    #include "LiveStreamPackageReader.hpp"

saveAndLoadPayloadToBinaryFile function

It is an utility function, which shows the verification of the saved payload: the binary payload can be written into a binary file, and loaded from it, and SensorPayloadReader can be initialized with its value. It is present in the code only for testing purposes: call of this function got commented out, in order not to deviate from the general use cases.

    std::string saveAndLoadPayloadToBinaryFile(std::string &strFileName, std::string strPayload) {
      uint64_t unPayloadSize= strPayload.size();
      std::fstream tBinFile;
      tBinFile = std::fstream(strFileName, std::ios::out | std::ios::binary);
      tBinFile.write(strPayload.c_str(), unPayloadSize);
      tBinFile.close();
      std::string strPayloadFromFile(unPayloadSize,'*');
      tBinFile = std::fstream(strFileName, std::ios::in | std::ios::binary);
      tBinFile.read(const_cast<char*>(strPayloadFromFile.c_str()),unPayloadSize);
      tBinFile.close();
      return strPayloadFromFile;
    }
main

We start the sensor_sample_consumer_1 by initializing an outputfolder, named "log", next to the compiled binary. This is where we are going to save our images extracted from the image and livestream datapoints.

   const std::string strOutputFolder = "./log";
  struct stat tStatBuffer;
  if (stat(strOutputFolder.c_str(), &tStatBuffer) != 0) {
    mkdir(strOutputFolder.c_str(), 0755);
  } else if (!S_ISDIR(tStatBuffer.st_mode)) {
    std::cerr << "Exiting sensor_sample_consumer_1: cannot use/create " << strOutputFolder
              << " because it already exists and it is not a directory!" << std::endl;
    return 1;
  }

We initialize AppSDK Databus by calling its getInstance method, which - when calling it for the first time - creates a static object of the Databus, so no more than one instance can exist at a time.

  if (Status::Status_OK != DataBus::getInstance().getDataBusState()) {
    std::cerr << "Failed to start Databus." << std::endl;
    return 1;
  }

An AppSDK Consumer is initialized and subscribed to "example_camera_image" messageIDlist. This correlates with the messageID defined in the configuration file of the example application. In order to use the Consumer to pop SensorPayload messages, it needs to be registered on the AppSDK Databus first.

std::vector<std::string> tMessageIdList{{"example_camera_image"}};
  Consumer tConsumer{tMessageIdList};
  if (Status::Status_OK == DataBus::getInstance().registerConsumer(tConsumer)) {
    std::cout << "Successfully registered Consumer_1." << std::endl;
  } else {
    std::cerr << "Failed to register Consumer_1." << std::endl;
    return 1;
  }

We declare all the necessary variables:

  • unAsciiWidth - for printing the picture on the standard output
  • unAsciiHeight - for printing the picture on the standard output
  • cunMaxValidImageCount - the example application runs until 60 images are extracted
  • unValidImageCount - iterator on how many valid images we have received till now
  • tMessageStruct - message popped from AppSDK Consumer will be saved in this struct
  • unBinarySaveCounter - semantically unused variable. It is useful for the saveAndLoadPayloadToBinaryFile utility/test function.
      uint16_t unAsciiWidth = 128;
      uint16_t unAsciiHeight = 32;
      const unsigned long long cunMaxValidImageCount = 60;
      unsigned long long unValidImageCount = 0;
      MessageStruct tMessageStruct;
      uint16_t unBinarySaveCounter = 0;
    

The example application runs until 60 images are extracted

    while (unValidImageCount <= cunMaxValidImageCount) {

Popped message by AppSDK Consumer will be saved in tMessageStruct struct. The popped message will be in SensorPayload binary format, which can be decoded

    tMessageStruct = tConsumer.pop();

The popped message is in SensorPayload binary format, which can be decoded by initializing a SensorPayloadReader instance with its payload. The SensorPayloadReader can extract datapoints' names and their values, and other meta info.

    SensorPayloadReader tSensorPayloadReader(tMessageStruct.getPayload());

We invoke getDatapointNames on the tSensorPayloadReader instance, which will supply the list of datapoint names of the SensorPayload and return a list (std::vector) of the datapoint names. Then we traverse thorough the list, so we can distinguish between the different datapoints and process them individually

      for(const std::string& strDatapointName : tSensorPayloadReader.getDatapointNames()) {

On this branch, we process the image datapoint. Calling the getValue method on the tSensorPayloadReader will return the raw binary image in string format. It returns an empty string if the "image" datapoint is not in the SensorPayload. If the string is not emtpy, the current image is considered valid, and unValidImageCount is incremented.

The SensorImage class gets a binary image in string format and provides methods for displaying it in human readable formats (print in ASCII or save in ppm). An instance will be initialized with strValue, and we are good to go calling the helper methods. Both methods return boolean false, in case strValue did not meet the format requirements. The printAscii method will

      if (strDatapointName == "image") {
        std::string strValue = tSensorPayloadReader.getValue("image");
        if(!strValue.empty()) {
          unValidImageCount++;
        }
        SensorImage tSensorImage(strValue);
        if (!tSensorImage.printAscii(unAsciiWidth, unAsciiHeight)) {
          std::cout << "Failed to print image." <<std::endl;
        }
        std::string strPayloadNumber = tSensorPayloadReader.getValue("sensoradapter_subscription_message_count");
        if(!tSensorImage.savePPM(strOutputFolder + "/test" + strPayloadNumber + ".ppm") ) {
          std::cout << "Failed to save image." <<std::endl;
        }

On this branch, we process the livestream datapoint. Calling the getValue method on the tSensorPayloadReader will return a value in binary format. It returns an empty string if the "image" datapoint is not in the SensorPayload. In order to interpret the string received on the livestream datapoint, we need to have a class, which extracts and processes the livestream package. We can initialize a LiveStreamPackageReader instance which provides methods for saving the images and getting meta info about the package.

} else if (strDatapointName == "livestream") {
        std::string strValue = tSensorPayloadReader.getValue("livestream");
        LiveStreamPackageReader tLiveStreamPackageReader(strValue);

After having loaded the strValue into LiveStreamPackageReader instance, we get meta info about the package. These methods were described in detail in section LiveStreamPackageReader.

        std::cout << "frameCountInBuffer : " << tLiveStreamPackageReader.getFrameCountInBuffer() << std::endl;
        std::cout << "sizeLiveStreamBinaryPackage : " << tLiveStreamPackageReader.getPackageSize() << std::endl;
        std::cout << "frameTimeAverage : " << tLiveStreamPackageReader.getFrameTimeAverage() << std::endl;
        std::cout << "timeStampOfPackage : " << tLiveStreamPackageReader.getTimeStampOfPackage() << std::endl;
        std::vector<uint64_t> tFrameCounters = tLiveStreamPackageReader.getFrameCounters();
        std::cout << tFrameCounters [0] << "-" << tFrameCounters[tLiveStreamPackageReader.getFrameCountInBuffer()-1] << std::endl;
        std::cout << "frameCounters : " ;

The getFrameCounters method returns the list of the counters in the current live stream package, so we can traverse through the counters and get each livestream image by the frame counter in string format. At the end with the help of a std::stringstream and an std::ofstream we save each frame into a subfolder (sensor_sample_consumer_1_livestream) of output folder.

        uint64_t unIndex=0;
        for (auto const& unStreamFrameCounter : tLiveStreamPackageReader.getFrameCounters()) {
          std::cout << " " << tFrameCounters[unIndex] << " / " << unStreamFrameCounter;
          if (unIndex%4 == 3) std::cout << std::endl;
          auto strJpgImage = tLiveStreamPackageReader.getImageByFrameCounter(unStreamFrameCounter);
          std::stringstream strFrameCounter;
          strFrameCounter << unStreamFrameCounter;
          std::ofstream tOutputFileStream(strOutputFolder + "/sensor_sample_consumer_1_livestream" + strFrameCounter.str() + ".jpg" );
          tOutputFileStream.write(strJpgImage.c_str(), strJpgImage.size());
          tOutputFileStream.close();
          unIndex++;
        }
        std::cout << std::endl;

Other datapoints (besides image and livestream) will be extracted as well and printed on the standard output.

      } else {
        std::cout << strDatapointName << " : |" << tSensorPayloadReader.getValue(strDatapointName) << "|" << std::endl;
      }

python

This section should describe sensor_sample_consumer_3 python version of the SensorAdapter example application. Although from a developer's perspective, it does not include extra about the content. That's why we think it is enough to state the complete python version in the Appendix.

Appendix

C++ Example code (complete)

    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <string>#include "Databus.h"
    #include "SensorPayloadReader.hpp"
    #include "SensorImage.hpp"
    #include "LiveStreamPackageReader.hpp"
    using namespace sinumerik_edge::databus;
    std::string saveAndLoadPayloadToBinaryFile(std::string &strFileName, std::string strPayload) {
      uint64_t unPayloadSize= strPayload.size();
      std::fstream tBinFile;
      tBinFile = std::fstream(strFileName, std::ios::out | std::ios::binary);
      tBinFile.write(strPayload.c_str(), unPayloadSize);
      tBinFile.close();
      std::string strPayloadFromFile(unPayloadSize,'*');
      tBinFile = std::fstream(strFileName, std::ios::in | std::ios::binary);
      tBinFile.read(const_cast<char*>(strPayloadFromFile.c_str()),unPayloadSize);
      tBinFile.close();
      return strPayloadFromFile;
    }
    int main() {
      std::cout << "sensor_sample_consumer_1 started..." << std::endl;
      const std::string strOutputFolder = "./log";
      struct stat tStatBuffer;
      if (stat(strOutputFolder.c_str(), &tStatBuffer) != 0) {
        mkdir(strOutputFolder.c_str(), 0755);
      } else if (!S_ISDIR(tStatBuffer.st_mode)) {
        std::cerr << "Exiting sensor_sample_consumer_1: cannot use/create " << strOutputFolder
                  << " because it already exists and it is not a directory!" << std::endl;
        return 1;
      }  if (Status::Status_OK != DataBus::getInstance().getDataBusState()) {
        std::cerr << "Failed to start Databus." << std::endl;
        return 1;
      } 

      std::vector<std::string> tMessageIdList{{"example_camera_image"}};
      Consumer tConsumer{tMessageIdList};
      if (Status::Status_OK == DataBus::getInstance().registerConsumer(tConsumer)) {
        std::cout << "Successfully registered Consumer_1." << std::endl;
      } else {
        std::cerr << "Failed to register Consumer_1." << std::endl;
        return 1;
      }

      std::ofstream logfile;
      logfile.open(strOutputFolder + "/sensor_sample_consumer_1_log.txt");
      logfile << "THIS IS A LOG.\n";
      logfile.close();

      int nFileNameCounter = 0;
      uint16_t unAsciiWidth = 128;
      uint16_t unAsciiHeight = 32;
      const unsigned long long cunMaxValidImageCount = 60;
      unsigned long long unValidImageCount = 0;
      MessageStruct tMessageStruct;
      uint16_t unBinarySaveCounter = 0;
      while (unValidImageCount <= cunMaxValidImageCount) {
        tMessageStruct = tConsumer.pop();
        std::cout << "message_id : " << tMessageStruct.getMessageId() << std::endl;    // Save payload in binary format for testing at Base team
        std::string strPayloadBinaryFileName = strOutputFolder + "/example_binary" + std::to_string(unBinarySaveCounter) + ".dat";
        ++unBinarySaveCounter;
        // Verification of the saved payload: read back payload from the file and initialize SensorpayloadReader with that value
        // SensorPayloadReader tSensorPayloadReader(saveAndLoadPayloadToBinaryFile(strPayloadBinaryFileName, tMessageStruct.getPayload()));   

          SensorPayloadReader tSensorPayloadReader(tMessageStruct.getPayload());    for(const std::string& strDatapointName : tSensorPayloadReader.getDatapointNames()) {
          if (strDatapointName == "image") {
            std::string strValue = tSensorPayloadReader.getValue("image");
            if(!strValue.empty()) {
              unValidImageCount++;
            }
            SensorImage tSensorImage(strValue);
            if (!tSensorImage.printAscii(unAsciiWidth, unAsciiHeight)) {
              std::cout << "Failed to print image." <<std::endl;
            }
            std::string strPayloadNumber = tSensorPayloadReader.getValue("sensoradapter_subscription_message_count");
            if(!tSensorImage.savePPM(strOutputFolder + "/test" + strPayloadNumber + ".ppm") ) {
              std::cout << "Failed to save image." <<std::endl;
            }
          } else if (strDatapointName == "livestream") {
            std::string strValue = tSensorPayloadReader.getValue("livestream");
            LiveStreamPackageReader tLiveStreamPackageReader(strValue);
            std::cout << "frameCountInBuffer : " << tLiveStreamPackageReader.getFrameCountInBuffer() << std::endl;
            std::cout << "sizeLiveStreamBinaryPackage : " << tLiveStreamPackageReader.getPackageSize() << std::endl;
            std::cout << "frameTimeAverage : " << tLiveStreamPackageReader.getFrameTimeAverage() << std::endl;
            std::cout << "timeStampOfPackage : " << tLiveStreamPackageReader.getTimeStampOfPackage() << std::endl;
            std::vector<uint64_t> tFrameCounters = tLiveStreamPackageReader.getFrameCounters();
            std::cout << tFrameCounters [0] << "-" << tFrameCounters[tLiveStreamPackageReader.getFrameCountInBuffer()-1] << std::endl;
            std::cout << "frameCounters : " ;
            uint64_t unIndex=0;
            for (auto const& unStreamFrameCounter : tLiveStreamPackageReader.getFrameCounters()) {
              std::cout << " " << tFrameCounters[unIndex] << " / " << unStreamFrameCounter;
              if (unIndex%4 == 3) std::cout << std::endl;
              auto strJpgImage = tLiveStreamPackageReader.getImageByFrameCounter(unStreamFrameCounter);
              std::stringstream strFrameCounter;
              strFrameCounter << unStreamFrameCounter;
              std::ofstream tOutputFileStream(strOutputFolder + "/sensor_sample_consumer_1_livestream" + strFrameCounter.str() + ".jpg" );
              tOutputFileStream.write(strJpgImage.c_str(), strJpgImage.size());
              tOutputFileStream.close();
              unIndex++;
            }
            std::cout << std::endl;
          } else {
            std::cout << strDatapointName << " : |" << tSensorPayloadReader.getValue(strDatapointName) << "|" << std::endl;
          }
        }
      }  std::cout << "sensor_sample_consumer_1 finished..." << std::endl;
      return 0;
    }

python Example code (complete)

    #!/usr/bin/python3

    import os
    import sys
    import locale

    import cProfile
    pr = cProfile.Profile()
    pr.enable()


    sys.path.append('EdgeAPI_python/include')
    sys.path.append('sensoradapter')
    sys.path.append('camerasensor')

    from edge import *
    from SensorAdapter import *
    from CameraSensor import *

    def main():
      print("sensor_sample_consumer_3 started...")
      strOutputFolder = "./log"
      if not os.path.exists(strOutputFolder):
        os.mkdir(strOutputFolder, 0o755)
      elif os.path.isdir(strOutputFolder) :
      print("Exiting sensor_sample_consumer_1: cannot use/create " \
        + strOutputFolder + " because is already exists and it is not a directory!")
      #Get databus instance
      bus = DataBus.getInstance()
      if bus.getDataBusState() != Status_OK:
        print("Failed to start Databus.")
        sys.exit(1)

      consumerIdList = ["other_example_camera_image"]
      consumer = Consumer(consumerIdList)
      if bus.registerConsumer(consumer) == Status_OK:
        print("Successfully registered Consumer_3.")
      else:
        print("Failed to register Consumer_3.")
        sys.exit(1)

      unMaxMessageCount = 60
      tMessageStruct = MessageStruct()
      for unMessageCount in range(1,unMaxMessageCount+1):
        tMessageStruct = consumer.pop()
        #Check if data read was successful
        if tMessageStruct.getStatus() == Status_OK:
          print("POP RESULT: " + tMessageStruct.getMessageId())
          strPayload = tMessageStruct.getPayloadRaw()
          print(len(strPayload))
          sys.stdout.flush()
          tSensorPayloadReader = SensorPayloadReader(strPayload)
          strPayloadNumber = tSensorPayloadReader.getValueAsString("sensoradapter_subscription_message_count")
          for strDatapointName in tSensorPayloadReader.getDatapointNames():
            if strDatapointName == 'livestream':
              tLiveStreamPackageReader = LiveStreamPackageReader(tSensorPayloadReader.getValue(strDatapointName))
              if tLiveStreamPackageReader.getState() == ReaderState_READER_STATE_OK :
                print("frameCountInBuffer : " + str(tLiveStreamPackageReader.getFrameCountInBuffer()))
                print("packageSize : " + str(tLiveStreamPackageReader.getPackageSize()))
                print("frameTimeAverage : " + str(tLiveStreamPackageReader.getFrameTimeAverage()))
                print("timeStampOfPackage : " + str(tLiveStreamPackageReader.getTimeStampOfPackage()))

                tFrameCounters = tLiveStreamPackageReader.getFrameCounters();
                print(str(tFrameCounters[0]) + "-" + str(tFrameCounters[tLiveStreamPackageReader.getFrameCountInBuffer()-1]))
                print("frameCounters : " )
                unIndex=0;
                for unFrameCounter in tLiveStreamPackageReader.getFrameCounters() :
                  print(str(unFrameCounter), end=" ")
                  if unIndex % 4 == 3 or unIndex == len(tLiveStreamPackageReader.getFrameCounters())-1:
                    print()
                  strJpgImage = tLiveStreamPackageReader.getImageByFrameCounter(unFrameCounter)
                  strJpgImagePath = strOutputFolder + "/python_livestream" + str(unFrameCounter) + ".jpg"
                  tJpgImageFile = open(strJpgImagePath,'w+b')
                  tJpgImageFile.write(strJpgImage)
                  tJpgImageFile.close()
                  unIndex += 1
              else :
                print("Wrong LiveStreamPackageReader state")
            elif strDatapointName == 'image' :
              print("image size : " + str(len(tSensorPayloadReader.getValue(strDatapointName))))
            else :
              strValue = tSensorPayloadReader.getValueAsString(strDatapointName)
              print(strDatapointName + " : |" + strValue + "|")
        else:
          print("Failed to pop.")
          break

    main()

    pr.print_stats()

Any questions left?

Ask the community


Except where otherwise noted, content on this site is licensed under the The Siemens Inner Source License - 1.1.