前言
最近在做自己的相机管理框架的时候,又回想起了Pylon(Basler的相机控制软件)API中的各种事件处理机制,感觉非常有用。但由于Pylon的代码不是开源的,只能尝试自己实现。
在本文中先简单介绍Pylon API中提供的的配置事件处理程序(即Pylon::CConfigurationEventHandler
)及其调用机制,然后尝试自己实现。
只是尽可能地尝试复现其内部调用机制,很多细节在本文中不会给出。
Pylon::CConfigurationEventHandler
首先先简单介绍一下Pylon::CConfigurationEventHandler
,它是一个用于相机配置的事件处理类,允许用户在相机的生命周期的关键点上自定义配置。例如:
在Pylon示例中展示了它的使用方法:
①首先在头文件创建一个自定义的配置事件处理类,它继承自ConfigurationEventHandler
,并在这个类中重写有需要的回调函数。在示例中,将每个回调函数的内容定义为打印相应事件的名称,方便观察。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #ifndef INCLUDED_CONFIGURATIONEVENTPRINTER_H_663006 #define INCLUDED_CONFIGURATIONEVENTPRINTER_H_663006
#include <pylon/ConfigurationEventHandler.h> #include <iostream>
namespace Pylon { class CInstantCamera;
class CConfigurationEventPrinter : public CConfigurationEventHandler { public: void OnOpen( CInstantCamera& camera ) { std::cout << "OnOpen event" << std::endl; }
void OnOpened( CInstantCamera& camera ) { std::cout << "OnOpened event" << std::endl; }
...
void OnCameraDeviceRemoved( CInstantCamera& camera ) { std::cout << "OnCameraDeviceRemoved event" << std::endl; } }; }
#endif
|
②使用时通过RegisterConfiguration 在相机对象中注册。注册完成后,当遇到相机行为的关键节点,重写的相应的回调函数会被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <pylon/PylonIncludes.h> #include "ConfigurationEventPrinter.h"
int main( int , char* [] ) { ...
CTlFactory& tlFactory = CTlFactory::GetInstance(); CInstantCamera camera( tlFactory.CreateFirstDevice() );
camera.RegisterConfiguration(new CConfigurationEventPrinter, RegistrationMode_Append, Cleanup_Delete);
camera.open();
... }
|
自己实现
先分析一下要实现像Pylon::CConfigurationEventHandler
这样的事件处理程序,都需要设计哪些组件或步骤。
我打算基于事件驱动架构实现这个机制,因此需要设计实现:
- 事件源 :发出事件的主体。在我的设计中,这个主体就是相机对象,它负责在其状态发生变化时生成事件。
- 事件 :一个特定的行为。在我的设计中,事件比较简单,没有携带其他数据,因此我自定义了一个枚举类用于标识事件。
- 事件通道 :用于事件的传递和分发。在我的设计中,事件通道比较简单,是一个vector。(仍需优化)
- 事件监听者:从事件通道中获取事件并执行相应的操作。在我的设计中,监听者还是相机对象。(其实这里设计的可能不是很好,按照职责其实应该划分到
ConfigurationEventHandler
基类中,后续理清思路再优化)
- 事件处理器:接收事件并进行处理。在我的设计中,是
ConfigurationEventHandler
的子类中的回调函数。
- 注册机制 :注册感兴趣的事件。在我的设计中使用一个函数实现。
事件源
事件源是相机对象,因此先创建一个抽象相机实例类CameraDevice
,所有不同品牌的具体相机实例类继承自此类。先不急看源文件,头文件部分内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class CameraDevice { public: virtual bool OpenDevice() final;
virtual bool StartGrabbing() final; protected: virtual bool SpecificOpenDevice() = 0;
virtual bool SpecificStartGrabbing() = 0; };
|
事件
一个特定的行为,由于事件不携带其他数据,我这里简单使用一个枚举类:
1 2 3 4 5 6 7 8 9
| enum ConfigurationEvent { OnOpen, OnOpened, OnGrabStart, OnGrabStarted };
|
事件通道
用于事件的传递和分发。在示例中没有使用像消息队列或发布/订阅系统这样的高级事件通道结构,而是通过简单的vector来管理和调用事件处理器,变相实现了事件的传递和分发的功能。由于注册行为和注销行为不频繁,且遍历操作为主导,这里选用vector。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include "ConfigurationEventHandler.h"
class CameraDevice { public: virtual bool OpenDevice() final;
virtual bool StartGrabbing() final; protected: virtual bool SpecificOpenDevice() = 0;
virtual bool SpecificStartGrabbing() = 0; protected: std::vector<ConfigurationEventHandler*> configuration_event_handlers_; };
|
事件监听者
从事件通道中获取事件并执行相应的操作,示例中使用一个函数NotifyEventHandeler
完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include "ConfigurationEventHandler.h" #include "MiscStruct.h"
class CameraDevice { public: virtual bool OpenDevice() final;
virtual bool StartGrabbing() final; protected: virtual bool SpecificOpenDevice() = 0;
virtual bool SpecificStartGrabbing() = 0;
virtual void NotifyEventHandeler(ConfigurationEvent event); protected: std::vector<ConfigurationEventHandler*> configuration_event_handlers_; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include "CameraDevice.h"
...
void CameraDevice::NotifyEventHandeler(ConfigurationEvent event) { for (auto handler : configuration_event_handlers_) { switch (event) { case ConfigurationEvent::OnGrabStart: handler->OnGrabStart(*this); break; case ConfigurationEvent::OnGrabStarted: handler->OnGrabStarted(*this); break; case ConfigurationEvent::OnOpen: handler->OnOpen(*this); break; case ConfigurationEvent::OnOpened: handler->OnOpened(*this); break; } } }
|
事件处理器
接收事件并进行处理。在示例中,是ConfigurationEventHandler
的子类中的回调函数。
我们先看一下ConfigurationEventHandler
的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #ifndef CONFIGURATIONEVENTHANDLER_H #define CONFIGURATIONEVENTHANDLER_H
class CameraDevice;
class ConfigurationEventHandler { public: ConfigurationEventHandler(){}; virtual ~ConfigurationEventHandler(){}; public: virtual void OnGrabStart(CameraDevice& camera){} virtual void OnGrabStarted(CameraDevice& camera){} virtual void OnOpen(CameraDevice& camera){} virtual void OnOpened(CameraDevice& camera){} };
#endif
|
里面的回调函数定义为空实现,即使用户注册了此类,也不会有什么操作。
然后我们创建一个子类ConfigurationEventPrinter
,其中每个重写的回调函数的内容定义为打印相应事件的名称,方便观察。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #ifndef CONFIGURATIONEVENTPRINTER_H #define CONFIGURATIONEVENTPRINTER_H
#include "ConfigurationEventHandler.h"
class CameraDevice;
class ConfigurationEventPrinter : public ConfigurationEventHandler { public: void OnGrabStart(CameraDevice& camera) override { std::cout << "ConfigurationEventPrinter::OnGrabStart" << std::endl; } void OnGrabStarted(CameraDevice& camera) override { std::cout << "ConfigurationEventPrinter::OnGrabStarted" << std::endl; } void OnOpen(CameraDevice& camera) override { std::cout << "ConfigurationEventPrinter::OnOpen" << std::endl; } void OnOpened(CameraDevice& camera) override { std::cout << "ConfigurationEventPrinter::OnOpened" << std::endl; } };
#endif
|
注册机制
注册过程,按照pylon的设计思路,也定义为相机对象中的一个成员函数RegisterConfiguration
,因此相机实例类的头文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include "ConfigurationEventHandler.h" #include "MiscStruct.h"
class CameraDevice { public: virtual bool OpenDevice() final;
virtual bool StartGrabbing() final; protected: virtual bool SpecificOpenDevice() = 0;
virtual bool SpecificStartGrabbing() = 0;
virtual void NotifyEventHandeler(ConfigurationEvent event);
virtual void RegisterConfiguration(ConfigurationEventHandler* handler, RegistrationMode mode, Cleanup cleanup_procedure); protected: std::vector<ConfigurationEventHandler*> configuration_event_handlers_; };
|
源文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include "CameraDevice.h"
...
void CameraDevice::NotifyEventHandeler(ConfigurationEvent event) { for (auto handler : configuration_event_handlers_) { switch (event) { case ConfigurationEvent::OnGrabStart: handler->OnGrabStart(*this); break; case ConfigurationEvent::OnGrabStarted: handler->OnGrabStarted(*this); break; case ConfigurationEvent::OnOpen: handler->OnOpen(*this); break; case ConfigurationEvent::OnOpened: handler->OnOpened(*this); break; } } }
void CameraDevice::RegisterConfiguration(ConfigurationEventHandler* handler, RegistrationMode mode, Cleanup cleanup_procedure) { std::cout << "CameraDevice::RegisterConfiguration" << std::endl; if (RegistrationMode_ReplaceAll == mode) { configuration_event_handlers_.clear(); configuration_event_handlers_.push_back(handler); } else if (RegistrationMode_Append == mode) { configuration_event_handlers_.push_back(handler); } std::cout << "CameraDevice::RegisterConfiguration success" << std::endl; }
|
注销过程与注册过程相反,本文不做展示。
示例汇总
除了上述展示的组件以外,还需将NotifyEventHandeler
在特定的位置调用,以达到在特定节点触发特性事件的效果。接下来直接展示全部的代码:
CameraDevice.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include "ConfigurationEventHandler.h" #include "MiscStruct.h"
class CameraDevice { public: virtual bool OpenDevice() final;
virtual bool StartGrabbing() final; protected: virtual bool SpecificOpenDevice() = 0;
virtual bool SpecificStartGrabbing() = 0; virtual void NotifyEventHandeler(ConfigurationEvent event); virtual void RegisterConfiguration(ConfigurationEventHandler* handler, RegistrationMode mode, Cleanup cleanup_procedure); protected: std::vector<ConfigurationEventHandler*> configuration_event_handlers_; };
|
CameraDevice.c
这里添加了对NotifyEventHandeler
的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include "CameraDevice.h"
...
void CameraDevice::NotifyEventHandeler(ConfigurationEvent event) { for (auto handler : configuration_event_handlers_) { switch (event) { case ConfigurationEvent::OnGrabStart: handler->OnGrabStart(*this); break; case ConfigurationEvent::OnGrabStarted: handler->OnGrabStarted(*this); break; case ConfigurationEvent::OnOpen: handler->OnOpen(*this); break; case ConfigurationEvent::OnOpened: handler->OnOpened(*this); break; } } }
void CameraDevice::RegisterConfiguration(ConfigurationEventHandler* handler, RegistrationMode mode, Cleanup cleanup_procedure) { std::cout << "CameraDevice::RegisterConfiguration" << std::endl; if (RegistrationMode_ReplaceAll == mode) { configuration_event_handlers_.clear(); configuration_event_handlers_.push_back(handler); } else if (RegistrationMode_Append == mode) { configuration_event_handlers_.push_back(handler); } std::cout << "CameraDevice::RegisterConfiguration success" << std::endl; }
bool CameraDevice::StartGrabbing() { NotifyEventHandeler(ConfigurationEvent::OnGrabStart); bool ret = SpecificStartGrabbing(); if(ret) NotifyEventHandeler(ConfigurationEvent::OnGrabStarted); return ret; }
bool CameraDevice::OpenDevice() { NotifyEventHandeler(ConfigurationEvent::OnOpen); bool ret = SpecificOpenDevice(); if(ret) NotifyEventHandeler(ConfigurationEvent::OnOpened); return ret; }
|
ConfigurationEventHandler.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #ifndef CONFIGURATIONEVENTHANDLER_H #define CONFIGURATIONEVENTHANDLER_H
class CameraDevice;
class ConfigurationEventHandler { public: ConfigurationEventHandler(){}; virtual ~ConfigurationEventHandler(){}; public: virtual void OnGrabStart(CameraDevice& camera){} virtual void OnGrabStarted(CameraDevice& camera){} virtual void OnOpen(CameraDevice& camera){} virtual void OnOpened(CameraDevice& camera){} };
#endif
|
ConfigurationEventPrinter.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #ifndef CONFIGURATIONEVENTPRINTER_H #define CONFIGURATIONEVENTPRINTER_H
#include "ConfigurationEventHandler.h"
class CameraDevice;
class ConfigurationEventPrinter : public ConfigurationEventHandler { public: void OnGrabStart(CameraDevice& camera) override { std::cout << "ConfigurationEventPrinter::OnGrabStart" << std::endl; } void OnGrabStarted(CameraDevice& camera) override { std::cout << "ConfigurationEventPrinter::OnGrabStarted" << std::endl; } void OnOpen(CameraDevice& camera) override { std::cout << "ConfigurationEventPrinter::OnOpen" << std::endl; } void OnOpened(CameraDevice& camera) override { std::cout << "ConfigurationEventPrinter::OnOpened" << std::endl; } };
#endif
|
MiscStruct.h
1 2 3 4 5 6 7 8 9
| enum ConfigurationEvent { OnOpen, OnOpened, OnGrabStart, OnGrabStarted };
|
断线重连
在实际使用的过程中,我们需要将ConfigurationEventPrinter
更换为自定义的事件处理器类即可,里面的回调函数依照实际需求进行重写。(上述示例中的回调相关的条目,碍于篇幅,没有展示完全,用户使用时需自己补充,具体条目见Pylon::CConfigurationEventHandler )
现在我演示一下通过事件通知机制实现相机的断线重连功能:
补充条目
补充上述示例,增加CameraDeviceRemoved相关条目(具体代码略)
增加函数定义
定义:
- 连接保活函数:用于维持和检测与相机的连接。其为纯虚函数,子类必须实现。由于子类代表着不同品牌的相机,因此在这个函数中调用品牌提供的可以访问相机寄存器的函数即可。如果相机离线,则无法成功访问相机寄存器,此时直接在此函数中抛出异常。
- 线程启动函数:用于在相机打开成功后循环调用连接保活函数。循环调用连接保活函数,并负责捕获其抛出的异常。收到异常后直接发出相机离线事件并返回,结束保活行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include "ConfigurationEventHandler.h" #include "MiscStruct.h"
class CameraDevice { public: virtual bool OpenDevice() final;
virtual bool StartGrabbing() final;
void StartHealthCheckThread(); protected: virtual bool SpecificOpenDevice() = 0;
virtual bool SpecificStartGrabbing() = 0; virtual void NotifyEventHandeler(ConfigurationEvent event); virtual void RegisterConfiguration(ConfigurationEventHandler* handler, RegistrationMode mode, Cleanup cleanup_procedure);
virtual void SendHeartBeat() = 0; protected: std::vector<ConfigurationEventHandler*> configuration_event_handlers_; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include "CameraDevice.h"
...
void CameraDevice::StartHealthCheckThread() { std::cout << "CameraDevice::StartHealthCheckThread" << std::endl;
std::thread([this]() { try { while(true) { SendHeartBeat(); std::this_thread::sleep_for(std::chrono::seconds(1)); } } catch(const std::exception& e) { std::cout << "CameraDevice::StartHealthCheckThread exception:" << e.what() << std::endl; NotifyEventHandeler(ConfigurationEvent::OnCameraDeviceRemoved); return; } }).detach(); }
|
定义事件处理器
定义一个新的事件处理器类ReconnectHandler
,其中重写OnCameraDeviceRemoved
和OnOpened
。
直接在相机对象头文件中定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include "ConfigurationEventHandler.h" #include "MiscStruct.h"
class CameraDevice { public: virtual bool OpenDevice() final;
virtual bool StartGrabbing() final;
void StartHealthCheckThread(); protected: virtual bool SpecificOpenDevice() = 0;
virtual bool SpecificStartGrabbing() = 0; virtual void NotifyEventHandeler(ConfigurationEvent event); virtual void RegisterConfiguration(ConfigurationEventHandler* handler, RegistrationMode mode, Cleanup cleanup_procedure);
virtual void SendHeartBeat() = 0; protected: std::vector<ConfigurationEventHandler*> configuration_event_handlers_; };
class ReconnectHandler : public ConfigurationEventHandler { public: void OnCameraDeviceRemoved(CameraDevice& camera) override { ...
int loop_count = 600; bool ret = false; while(loop_count > 0 && !ret) { if(loop_count % 10 == 0) { ret = camera.OpenDevice(); } --loop_count; std::this_thread::sleep_for(std::chrono::seconds(1)); }
}
void OnOpened(CameraDevice& camera) override { ... camera.StartHealthCheckThread(); } };
|