Events#

/***********************************************************************************
*
* Itala API - Copyright (C) 2022 Opto Engineering
*
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY SUFFERED BY LICENSE AS
* A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*
***********************************************************************************/

/**
 * @example Events.cpp
 * 
 * @brief "Events" example shows how the client code can react to events sent by
 * a device. Itala API is responsible for detecting the incoming events, updating
 * the related values (if any) and notifying the user. The natural way to handle
 * events is via the callback functionality provided by GenApi. Alternatively,
 * Itala API provides an event interface which should be implemented by the client
 * code.
 *
 * Suggested examples:
 * @see GrabChunks.cpp
 * @see Events.cpp
 */

 // Include Itala API 
#include "ItalaApi/Itala.h"

// Include the GenICam library
#include "GenICam.h"

#include <thread>
#include <chrono>

#define SLEEP 1000
#define INDENT "\t"

// Define a callback function which can be registered to a node with GenApi.
// When the target node value changes, the callback is executed. The value
// change is performed automatically by Itala API when the event related
// to the node occurs (e.g. when an exposure end event occurs,
// the EventExposureEndTimestamp node value changes).
void EventExposureEndTimestampCallback(GenApi::INode* pNode)
{
    GenApi::CIntegerPtr pEventExposureEndTimestamp = pNode;
    std::cout << INDENT << "ExposureEndEventTimestamp value change detected by the Callback, new value is  " << 
      pEventExposureEndTimestamp->GetValue() << std::endl;
}

// Define a handler and override the OnDeviceEvent method. The handler can
// be registered to Itala API so that, when a device event is detected,
// the overridden function is automatically called.
class ExposureEndEventHandler : public Itala::DeviceEventHandler
{
public:
  ExposureEndEventHandler(Itala::IDevice* pDevice)
    : pDevice(pDevice)
  { }

  void OnDeviceEvent(uint64_t eventId)
  {
    GenApi::CIntegerPtr pEventExposureEnd = pDevice->GetNodeMap().GetNode("EventExposureEnd");
    if (static_cast<int64_t>(eventId) == pEventExposureEnd->GetValue())
    {
      GenApi::CIntegerPtr pEventExposureEndTimestamp = pDevice->GetNodeMap().GetNode("EventExposureEndTimestamp");
      std::cout << INDENT << "Exposure end event detected by the Handler, occurred at " << pEventExposureEndTimestamp->GetValue() << std::endl << std::endl;;
    }
  }

private:
  Itala::IDevice* pDevice;
};

void Events_Sample()
{
  std::cout << "***** Events example started. *****" << std::endl << std::endl;

  Itala::ISystem* pSystem = Itala::CreateSystem();
  Itala::DeviceInfoList deviceInfos = pSystem->EnumerateDevices(700);

  if (deviceInfos.size() == 0)
    throw GENERIC_EXCEPTION("No devices found. Example canceled.");

  if (deviceInfos[0].AccessStatus() != Itala::DeviceAccessStatus::AvailableReadWrite)
    throw GENERIC_EXCEPTION("Target device is unaccessible in RW mode. Example canceled.");

  Itala::IDevice* pDevice = pSystem->CreateDevice(deviceInfos[0]);
  std::cout << "First device found is initialized." << std::endl;


  // Select the exposure end event..
  GenApi::CEnumerationPtr pEventSelector = pDevice->GetNodeMap().GetNode("EventSelector");
  if (!IsWritable(pEventSelector))
    throw GENERIC_EXCEPTION("Unable to select the exposure end event. Aborting.");
  pEventSelector->FromString("ExposureEnd");

  // ..and enable it on the device. The original value is also kept so 
  // that it can be restored at the end of the example.
  GenApi::CEnumerationPtr pEventNotification = pDevice->GetNodeMap().GetNode("EventNotification");
  if (!IsWritable(pEventSelector))
    throw GENERIC_EXCEPTION("Unable to turn on notifications for exposure end event. Aborting.");
  int64_t originalEventNotification = pEventNotification->GetIntValue();
  pEventNotification->FromString("On");
  std::cout << "Exposure end event enabled on the device." << std::endl;

  // Start to listen for events on the host.
  pDevice->EnableEvents();
  std::cout << "Event listening enabled on the host." << std::endl;

  // Get one of the exposure end event related nodes. In this case, the frame timestamp
  // is chosen. WARNING: the event node is not available until the event occurs, so no
  // availability check is performed here.
  GenApi::CNodePtr pEventExposureEndTimestamp = pDevice->GetNodeMap().GetNode("EventExposureEndTimestamp");

  // The callback is registered to the EventExposureEndTimestamp node. Every time the
  // node value changes, the function gets called and the new value can be read.
  GenApi::CallbackHandleType hCallback =
    GenApi::Register(pEventExposureEndTimestamp, EventExposureEndTimestampCallback);
  std::cout << "Callback registered to the EventExposureEndTimestamp node." << std::endl;

  // Alternatively, an instance of the user-defined handler is created and passed
  // to Itala API. When a device event occurs, the handler's function ovverridden
  // by the user is called.
  ExposureEndEventHandler expEndHandler(pDevice);
  pDevice->RegisterHandler(&expEndHandler);

  pDevice->StartAcquisition();
  std::cout << "Acquisition started." << std::endl << std::endl;;

  // Let the camera perform some exposures..
  std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP));

  pDevice->StopAcquisition();

  // When the callback-on-event functionality is no longer required, the function
  // must be unregistered from the node. The same is true for the handler.
  // The event listening must be disabled.
  GenApi::Deregister(hCallback);
  pDevice->DisableEvents();
  pDevice->DeregisterHandler();

  pEventNotification->SetIntValue(originalEventNotification);

  pDevice->Dispose();
  pDevice = nullptr;
  std::cout << std::endl << "Device instance disposed." << std::endl;
  pSystem->Dispose();
  pSystem = nullptr;
  std::cout << "System instance disposed." << std::endl;
}

int main(int /*argc*/, char** /*argv*/)
{
  try
  {
    Events_Sample();
  }
  catch (GenICam::GenericException& e)
  {
    std::cout << e.what() << std::endl;
  }
}