Adapters

The CCF Package is an SDK, providing a number of tools to build DCS solutions. The “ccf::control::Application” implements the actual CCF Control Application. This can be adapted to accomodate context specific needs, by means of the following adapters:

  • Communication Adapter: Implements protocol specific communication. It is also used, for now, for implementing the simulated behaviour of CCF. New Communication Adapters shall be derived from the base class provided, “ccf:common::ComAdptBase”.

  • Processing Recipe Adapter: Implements on-the-fly processing of image frames, executed in a dedicated thread in a CCF Control instance. Processing Recipes shall be derived from the base class provided, “ccf::common::RecipeBase”.

  • Data Publisher Adapter: Implements publishing of data to specific targets, which may be output files or applications subcribing to the data. The Data Publishers are invoked after the on-the-fly processing. Data Publishers shall be derived from the provided base class, “ccf::common::PubBase”.

The Doxygen documentation available for the three adapter base classes, describe the purpose and function of each method in the classes. In general, the virtual methods of the form “virtual <name>User(…)”, i.e., tagged with “User” are expected/supposed to be implemented in the derived, specific adapter classes.

The specific set of adapters to be used in the target CCF application, shall be specified in the “main()” function, whereby factory classes shall be registered in the CCF Application object, before running the application. This is described in the chapter “Installation & Deployment”.

For all Adapter types, it is important that the method “SetClassName(<adapter name>)” is invoked in the constructor to define the name of the Adapter. The name allocated to the Adapter is used when instantiating it from their factory class instances. The name of the Adapter to instantiate, initialise and install, is defined by the “<prefix>.adapter” parameter in the configuration.

In the following sections, more details about each type of Adapter is provided.

The Communication Adapter

The implementation of a Communication Adapter may involve quite some work, depending on the complexity of the camera protocol/interface and the possible associated SDK.

A number of obligatory Configuration and Setup Parameters are defined for the Communication Adapter. It is possible to define new, Communication Adapter specific Configuration and Setup Parameters. When defining these, it is the obligation of the adapter to check the validity of its specific parameters and to retrieve these.

In the following, an overview of the methods of Communication Adapter base class is provided.

The user methods which probably shall be implemented when developing a new Communication Adapter, are marked with “**”.

Other user methods may be implemented according to the needs. It is not attempted to provide the detailed information about each method, as this can be found in the Doxygen documentation. The adapter methods have been split into the following categories:

  • General Household Keeping.

  • Connection Handling.

  • Parameter Handling.

  • Camera Performance & Properties.

  • Acquisition & Frame Handling.

Basic Household Keeping

void Initialise(const bool re_init = false);
**virtual void InitialiseUser();**

The Initialise() method is called after the Communication Adapter has been created. The “InitialiseUser()” method shall be implemented to provide protocol specific initialisation.

const std::string& GetId() const;

Unique ID assigned to the adapter (used for internal purposes).

void SetHostAdddress(const std::string& host_address);
const std::string& GetHostAddress();

“GetHostAddress()” returns the address of the host system. It is normally necessary to set this via “SetHostAddress()” as it is derived automatically.

void SetIsSimAdapter(const bool is_sim);
bool GetIsSimAdapter() const;

The “SetIsSimAdapter()” shall be invoked in the constructor of Communication Adapters to indicate for CCF if the adapter is a simulation adapter.

Parameter Handling

template <class TYPE> TYPE GetDeviceProperty(const std::string& property);
bool HasDeviceProperty(const std::string& property, std::string& value);
virtual bool HasDevicePropertyUser(const std::string& property, std::string& value);

The methods can be used to retrieve any parameter from the camera. If called before the camera is connected, it will try to retrieve the information from the associated configuration. This is e.g. needed if the maximum frame size shall be computed before a connection to the camera has been established. “HasDevicePropertyUser()” normally, shall not be implemented.

void Read(const std::string& name, ctd::param::Parameter& par);
void Read(const std::vector<std::string>& names, std::vector<ctd::param::Parameter>& pars);
std::string Read(const std::string& name);
template <class TYPE> TYPE Read(const std::string& name)
**virtual void ReadUser(const std::string& name, ctd::param::Parameter& par);**
virtual void
  ReadUser(const std::vector<std::string>& names, std::vector<ctd::param::Parameter>& pars);

Used to read parameters from the associated camera. Requires the camera to be connected (see also “HasCameraProperty()”). Normally only the “virtual void ReadUser(const std::string& name, ctd::param::Parameter& par)” needs to be implemented.

void Write(const ctd::param::Parameter& par);
void Write(const std::vector<ctd::param::Parameter>& pars);
template <class TYPE> void Write(const std::string& name, const TYPE& value)
**virtual void WriteUser(const ctd::param::Parameter& par);**
virtual void WriteUser(const std::vector<ctd::param::Parameter>& pars);

Used to write parameters to associated the camera. Normally only the “virtual void WriteUser(const ctd::param::Parameter& par)” needs to be implemented.

void Scan(std::vector<ctd::param::Parameter>& pars);
**virtual void ScanUser(std::vector<ctd::param::Parameter>& pars);**

Used to scan the name space of the camera (retrieve all parameters defined in the interface).

void HandleParPreWrite(ctd::param::Parameter& par);
virtual void HandleParPreWriteUser(ctd::param::Parameter& par);

Can be implemented to carry out a protocol/camera specific conversion/computation of the parameter to be written to the associated camera.

void HandleParPostWrite(ctd::param::Parameter& par);
virtual void HandleParPostWriteUser(ctd::param::Parameter& par);

Can be implemented to carry out a protocol/camera specific handling after a parameter was written to the associated camera.

void HandleParPostRead(ctd::param::Parameter& par);
virtual void HandleParPostReadUser(ctd::param::Parameter& par);

Can be implemented to carry out a protocol/camera specific conversion/computation of a parameter read from the associated camera.

void MapParForDevice(ctd::param::Parameter& par) const;
void MapParFromDevice(ctd::param::Parameter& par) const;

Map a parameter before writing it to the device or after having read it from the device. The mapping is done using the associated Camera Name Mapping. These methods are invoked automatically by CCF core and need normally not to be invoked by the user.

void HandleSetup();
**virtual void HandleSetupUser();**

Method invoked when a setup request has been received and needs to be applied to the camera.

Camera Performance & Properties

double GetTheoreticFrameRate();
virtual double GetTheoreticFrameRateUser();

Used to calculate the estimated frame rate in connection with handling a “recif::RecStart” request and other “recif” requests, to estimate the remaining time for executing the Recording Session.

uint32_t GetMaxFrameSize();
virtual uint32_t GetMaxFrameSizeUser();

Shall return the maximum frame size that can be produced by the given camera, in the given configuration. This is used to allocate various buffers to handle frames.

void CheckStatus(ccf::HwStatus& status, std::string& message);
**virtual void CheckStatusUser(ccf::HwStatus& status, std::string& message);**

Invoked periodically by the Monitor Thread when the camera when no image acquisition is on-going.

Connection Handling

const std::string& GetDeviceAddress() const;
void SetDeviceAddress(const std::string& address);
**virtual void SetDeviceAddressUser(const std::string& address);**

Used to get/set the address used to connect to the camera. The “SetDeviceAddressUser()” shall be implemented to check the validity of the specific connection URI.

void CheckConnection(bool& connected);
**virtual void CheckConnectionUser(bool& connected);**

Invoked to check that the connection to the camera seems to be properly established.

bool IsConnected() const;
virtual bool IsConnectedUser() const;
void SetIsConnected(const bool connected);
void Connect();
**virtual void ConnectUser();**

“ConnectUser()” must be provided to carry out the protocol/SDK specific handling of creating a connection to the associated camera.

void Disconnect();
**virtual void DisconnectUser();**

“DisconnectUser()” must be provided to carry out the protocol/SDK specific handling of disconnecting from the associated camera, in a clean way.

Acquisition & Frame Handling

bool     GetAllowLostFrames() const;
uint64_t IncLostFramesCount();
uint64_t GetLostFramesCount() const;
void     ResetLostFramesCount();

Indicates if it has been configured that frames may be lost silently. The Communication Adapter may then choose to use this for deciding whether to notify about this event or not. “IncLostFramesCount()” shall be invoked when a frame is lost. “GetLostFramesCount()” indicates the number of frames lost since the last acquisition was started. “ResetLostFramesCount()” shall be invoked when starting a new image acquisition sequence.

void Start();
**virtual void StartUser();**

“StartUser()” shall be implemented to handle the protocol/SDK specific logic to start a new image acquisition sequence.

void Stop();
**virtual void StopUser();**

“StopUser()” shall be implemented to handle the protocol/SDK specific logic to stop a new image acquisition sequence.

void Receive(ccf::common::DataFrame& frame,
             bool& frame_received,
             const double timeout,
             bool& timed_out);
**virtual void ReceiveUser(ccf::common::DataFrame& frame,
                           bool& frame_received,
                           const double timeout,
                           bool& timed_out);**

“ReceiveUser()” shall be implemented to receive the next frame. If a timeout is specified the method may time out but shall not throw an exception. Instead the “timed_out” parameter shall indicate if a timeout condition was encountered. When a frame has been received, the “frame_received” shall be true to indicate this.

Note: This version of CCF only handles entire frames. It may be evaluated if support for handling of sub-frames shall be provided.

The Processing Recipe Adapter

The Processing Recipe Adapters are invoked on the image frames to reduce them or to extract features from these.

It is strongly recommended to use the image processing routines provided by the ICS ODP Package to simplify the Processing Recipes and to make maintenance easier. However, if needed, other image processing packages may be used. It must be assured however, that these are threadsafe if it is the intention to run multiple Processing Pipelines in parallel, possibly leading to race conditions.

When a Processing Recipe is invoked on a CCF frame object, a copy of the frame object is handed over to the Recipe; it may, consequently, change the data and other information in the frame object.

It is possible to specify a series of Processing Recipes to be invoked on the image frames in the Processing Pipeline. The sequence and parameters of these, is defined in the configuration, for the given Processing Pipeline.

A number of obligatory Configuration and Setup Parameters are defined for the Processing Recipe Adapter.

It is possible to define new adapter specific Configuration and Setup Parameters. When defining these, it is the obligation of the adapter to check the validity of its specific parameters and to retrieve these. New Recipe specific keys added, shall have a name of the form “<prefix>.<proc thr name>.<proc recipe name>.<name>”. The data type and format of the value is implementation dependent.

Basic House Keeping

RecipeBase(const std::string& proc_thread_name,
           const std::string& recipe_name);
virtual ~RecipeBase();

The Processing Thread Recipe Numbers are derived from the Configuration parameter (indeces).

static const std::string& GenId(const std::string& proc_thread_name,
                                const std::string& recipe_name);

Generate the unique (within CCF application) ID for the given Processing Recipe. The ID is of the form: “Proc#Recipe#”, where the index refers to the number of the Processing Thread and the number of the Recipe, respectively.

void SetStatus(const ProcStatus status);
ProcStatus GetStatus() const;

Set/get the status of the Recipe: “ccf::common:ProcStatus”: UNDEFINED, IDLE, PROCESSING, FINISHED, FAILED.

const std::string& GetRecipeId() const;
const std::string& GetRecipeName() const;
const std::string& GetProcThreadName() const;

The Recipe ID corresponds to what is generated by GenId(). The Recipe Name is the name allocated via the configuration. The Processing Thread Name refers to the name allocated to the given Processing Thread according to the configuration.

Processing Recipe Adapters Instance Management

**virtual void CreateObjectUser(const std::string& proc_thread_name,
                                const std::string& recipe_name,
                                std::shared_ptr<RecipeBase>& new_object);**

template <class TYPE>
static void AddRecipeFactoryObj(TYPE& recipe_factory_obj);

static void CreateRecipeObj(const std::string& class_name,
                            const std::string& proc_thread_name,
                            const std::string& recipe_name,
                            std::shared_ptr<RecipeBase>& new_obj);

static void GetRecipeObj(const std::string& class_name,
                         const std::string& proc_thread_name,
                         std::shared_ptr<RecipeBase>& recipe_obj,
                         const bool initialise = true);

static const std::map<std::string, std::shared_ptr<RecipeBase**& GetRecipeObjs();

static bool HasRecipeObj(const std::string& class_name,
                         const std::string& proc_thread_name,
                         std::shared_ptr<RecipeBase>& recipe_obj);

The “CreateObjectUser()” method must be implemented to create the type specific Processing Recipe object, which is subsequently used by CCF when applying the procesing to the frames handled. The remaining methods are usually not needed by the specific Communication Adapter implementation

Initialisation & Enabling/Disabling

void Initialise();
virtual void InitialiseUser();
bool GetInitialised() const;

Initialise the Recipe instance. Recipe specific Configuration parameters shall be parsed/handled during the initialisation.

void Enable();
virtual void EnableUser();
void Disable();
virtual void DisableUser();
bool GetEnabled() const;

Methods to enable/disable the Recipe. Only enabled Recipes are invoked on the data frames. It is possible to provide context specific handling in connection with the enabling/disabling, but usually this will not be necessary.

Data Processing

void Process(DataFrame& frame);
**virtual void ProcessUser(DataFrame& frame);**

Invoked by the Processing Thread to execute the processing on the image frames. The data contained in the frame object ‘belongs’ to the Processing Thread and may be modified by the Recipe, without creating a new copy.

The Data Publisher Adapter

Data Publishers are classified in two main categories:

  • Recording Data Publisher: Recording Data Publishers, are publisher recording data into Output Data Products. This normally is triggered on demand from client software or user, by submitting a “recif::RecStart” request.

  • Non-Recording Data Publisher: Non-Recording Data Publisher, are publishers which inject the data into other application software. This type of Data Publisher typically continuously publishes data, but may implement internal rules for how and when to publish data. I.e., a publisher may implement a rule to only publish data at a certain frequency, and to discard the rest of the image frames without further actions.

Whether a Data Publisher is Recording or Non-Recording is defined in the constructor of the Data Publisher.

It shall also be defined whether a Data Publisher is activated by default in the constructor. Recording Publishers are typically disabled by default and enabled on demand, by means of the “recif::RecStart” request. The Non-Recording Data Publishers may continuously activated.

The three properties described above, are exemplified in the FITS and DDT Data Publishers provided as Standard Data Publishers:

PubFits::PubFits(const std::string& proc_thread_name,
                 const std::string& pub_thread_name) :
    PubBase(proc_thread_name, pub_thread_name) {
  LOG4CPLUS_TRACE_METHOD(Logger(), __PRETTY_FUNCTION__);
  SetClassName("ccf::stdpub::PubFits");
  m_publisher_type = ccf::common::PubType::RECORDING;
  ...
  SetActivated(false);
}
PubDdt::PubDdt(const std::string& proc_thread_name,
               const std::string& pub_thread_name) :
    PubBase(proc_thread_name, pub_thread_name) {
  LOG4CPLUS_TRACE_METHOD(Logger(), __PRETTY_FUNCTION__);
  SetClassName("ccf::stdpub::PubDdt");
  m_publisher_type = ccf::common::PubType::NOT_RECORDING;
  ...
  SetActivated(true);
}

A number of obligatory Configuration and Setup Parameters are defined for the Data Publisher Adapter. It is possible to define new adapter specific Configuration and Setup Parameters. When defining these, it is the obligation of the adapter to check the validity of its specific parameters and to retrieve these. New Data Publisher specific keys added, shall have a name of the form “<prefix>.<proc thr name>.<pub thr name>.<name>”. The data type and format of the value is implementation dependent.

The “ccf::common::PublisherStatus” is used to keep track of the publishing, which is used by CCF core for various statistics and status purposes.

Basic House Keeping

PubBase(const std::string& proc_thread_name,
        const std::string& pub_thread_name);
virtual ~PubBase();

The Processing and Publisher Thread numbers are derived from the Configuration parameter (indeces).

Note, in this release it is only possible to add one Data Publisher per Publisher Thread. For the best performance, it is probably better to have only one Publisher per Publisher Thread, at least when Publishing into Output Data Products.

static const std::string& GenId(const std::string& proc_thread_name,
                                const std::string& pub_thread_name);

Generate the unique (within CCF application) ID for the given Processing Recipe. The ID is of the form: “<proc thr name>::<pub thr name>”, where the index refers to the number of the Processing and Publisher Threads.

void Dismantle();
virtual void DismantleUser();

Invoked when terminating the application to clean up memory etc., which might have been allocated by the Data Publisher during execution.

const std::string& GetProcThreadName() const;
const std::string& GetPubId() const;
const std::string& GetPubThreadName() const;

The Publisher ID corresponds to what is generated by GenId(). The Data Publisher Name is the name allocated via the configuration (key: “server.processing.pipeline”). The Processing Thread Number refers to the index allocated to the given Processing and Publisher Threads according to the configuration.

PubType GetPublisherType() const;

Type of Data Publisher: Recording or not Recording, where Recording refers to that Output Data Product files are generated.

PublisherStatus& GetPubStatus();
void GetPubStatus(PublisherStatus& pub_stat) const;

Get the Publisher status information, with information about the execution of the publishing. The method “GetPubStatus(PublisherStatus& pub_stat)” is the preferred way to get a snapshot of the progress, but it makes a copy of the internal “ccf::common::PublisherStatus” object.

Data Publisher Adapters Instance Management

**virtual void CreateObjectUser(const std::string& proc_thread_name,
                                const std::string& pub_thread_name,
                                PubBase** new_object);**

static void AddPubFactoryObj(TYPE& pub_factory_obj);

static void CreatePubObj(const std::string& class_name,
                         const std::string& proc_thread_name,
                         const std::string& pub_thread_name,
                         PubBase** new_object);

static void GetPubObj(const std::string& proc_thread_name,
                      const std::string& pub_thread_name,
                      PubBase** pub_obj,
                      const bool initialise = true);

static void GetPubObjs(std::vector<PubBase*>& pub_objs);

static bool HasPubObj(const std::string& proc_thread_name,
                      const std::string& pub_thread_name,
                      PubBase** pub_obj);

The “CreateObjectUser()” method must be implemented to create the type specific Data Publisher object, which is subsequently used by CCF when applying the procesing to the frames handled. The remaining methods are usually not needed by (of interest to) the specific Data Publisher Adapter implementation

Initialisation & Enabling/Disabling

void Initialise()/virtual void InitialiseUser();
bool GetInitialised() const;

void SetEnabled(const bool enabled);
bool GetEnabled() const;

Initialisation is invoked when creating a Data Publisher instance.

Whether or not to apply a Data Publisher on a given data frame, is defined by the ‘enabled state’.

Activation/Deactivation

void Activate()
virtual void ActivateUser()
void Deactivate()
virtual void DeactivateUser()
void SetActivated(const bool activated)
bool GetActivated() const
virtual void CheckForDeactivationUser();

The ‘activation flag’ indicates if the Publisher actually is publishing data, if it is enabled. This is needed for Recording Publishers as they are activated, e.g. when a “recif::RecStart” request is received, and remain activated until the specified set of Output Data Products have been generated.

Data Publishing

void Publish(DataFrame& frame,
             const bool force);
virtual void PublishUser(DataFrame& frame);

The “PublishUser()” method is the actual method to publish the data. The method should not attempt to release the Data Frame Object in the Output Queue when the publishing is completed; this is done automatically by CCF core.

Status Handling

void EstimateExecution(double& duration,
                       int64_t& nb_of_frames);
virtual void EstimateExecutionUser(double& duration,
                                   int64_t& nb_of_frames);

The method is used to provide an estimate for the recording status computation, for how long time the Recording Session is expected to execute.

void ResetDbStatus() const;
void UpdateDbStatus() const;

Resets the status key values in the DB (OLDB), and updates these during executation of the Data Publisher. By default, the information in the “ccf::common::PublisherStatus” is written to the OLDB at every cycle of the publishing.