Pylon事件通知机制的实现

paw5zx Lv4

前言

最近在做自己的相机管理框架的时候,又回想起了Pylon(Basler的相机控制软件)API中的各种事件处理机制,感觉非常有用。但由于Pylon的代码不是开源的,只能尝试自己实现。

在本文中先简单介绍Pylon API中提供的的配置事件处理程序(即Pylon::CConfigurationEventHandler)及其调用机制,然后尝试自己实现。

只是尽可能地尝试复现其内部调用机制,很多细节在本文中不会给出。

Pylon::CConfigurationEventHandler

首先先简单介绍一下Pylon::CConfigurationEventHandler,它是一个用于相机配置的事件处理类,允许用户在相机的生命周期的关键点上自定义配置。例如:

  • OnOpened方法,将会在相机成功打开后被调用,用户可以重写此方法以实现自定义操作;
  • OnGrabStopped方法,将会在抓取行为停止完成后被调用,用户可以重写此方法以实现自定义操作;
  • 等,更多回调函数的声明请见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
//file: ConfigurationEventPrinter.h
#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 /* INCLUDED_CONFIGURATIONEVENTPRINTER_H_663006 */

②使用时通过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 /*argc*/, char* /*argv*/[] )
{
// 必要的初始化操作
...

// 实例化相机对象
CTlFactory& tlFactory = CTlFactory::GetInstance();
CInstantCamera camera( tlFactory.CreateFirstDevice() );

// 注册事件
camera.RegisterConfiguration(new CConfigurationEventPrinter, RegistrationMode_Append, Cleanup_Delete);

// 打开相机,输出OnOpen event,当相机打开成功后,输出OnOpened event
camera.open();

// 必要的资源释放等操作
...
}

自己实现

先分析一下要实现像Pylon::CConfigurationEventHandler这样的事件处理程序,都需要设计哪些组件或步骤。

我打算基于事件驱动架构实现这个机制,因此需要设计实现:

  • 事件源        :发出事件的主体。在我的设计中,这个主体就是相机对象,它负责在其状态发生变化时生成事件。
  • 事件            :一个特定的行为。在我的设计中,事件比较简单,没有携带其他数据,因此我自定义了一个枚举类用于标识事件。
  • 事件通道    :用于事件的传递和分发。在我的设计中,事件通道比较简单,是一个vector。(仍需优化)
  • 事件监听者:从事件通道中获取事件并执行相应的操作。在我的设计中,监听者还是相机对象。(其实这里设计的可能不是很好,按照职责其实应该划分到ConfigurationEventHandler基类中,后续理清思路再优化)
  • 事件处理器:接收事件并进行处理。在我的设计中,是ConfigurationEventHandler的子类中的回调函数。
  • 注册机制    :注册感兴趣的事件。在我的设计中使用一个函数实现。

事件源

事件源是相机对象,因此先创建一个抽象相机实例类CameraDevice,所有不同品牌的具体相机实例类继承自此类。先不急看源文件,头文件部分内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
//file: CameraDevice.h
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
//file: MiscStruct.h
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
//file: CameraDevice.h
#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_; //事件处理器的注册和注销不频繁,且遍历操作占主导,vector综合性能高
};

事件监听者

从事件通道中获取事件并执行相应的操作,示例中使用一个函数NotifyEventHandeler完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//file: CameraDevice.h
#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_; //事件处理器的注册和注销不频繁,且遍历操作占主导,vector综合性能高
};
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
//file: CameraDevice.c
#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
//file: ConfigurationEventHandler.h
#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 // CONFIGURATIONEVENTHANDLER_H

里面的回调函数定义为空实现,即使用户注册了此类,也不会有什么操作。

然后我们创建一个子类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
//file: ConfigurationEventPrinter.h
#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 // CONFIGURATIONEVENTPRINTER_H

注册机制

注册过程,按照pylon的设计思路,也定义为相机对象中的一个成员函数RegisterConfiguration,因此相机实例类的头文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//file: CameraDevice.h
#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_; //事件处理器的注册和注销不频繁,且遍历操作占主导,vector综合性能高
};

源文件如下:

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
//file: CameraDevice.c
#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;
}
}
}

//cleanup_procedure暂时没有用到
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
//file: CameraDevice.h
#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_; //事件处理器的注册和注销不频繁,且遍历操作占主导,vector综合性能高
};

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
//file: CameraDevice.c
#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;
}
}
}

//cleanup_procedure暂时没有用到
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
//file: ConfigurationEventHandler.h
#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 // CONFIGURATIONEVENTHANDLER_H

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
//file: ConfigurationEventPrinter.h
#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 // CONFIGURATIONEVENTPRINTER_H

MiscStruct.h

1
2
3
4
5
6
7
8
9
//file: MiscStruct.h
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
    //file: CameraDevice.h
    #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_; //事件处理器的注册和注销不频繁,且遍历操作占主导,vector综合性能高
    };
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
//file: CameraDevice.c
#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,其中重写OnCameraDeviceRemovedOnOpened

直接在相机对象头文件中定义:

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
//file: CameraDevice.h
#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_; //事件处理器的注册和注销不频繁,且遍历操作占主导,vector综合性能高
};

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();
}
};
  • 标题: Pylon事件通知机制的实现
  • 作者: paw5zx
  • 创建于 : 2024-11-15 13:53:16
  • 更新于 : 2024-11-15 21:34:34
  • 链接: https://paw5zx.github.io/pylon-camera-events-handling/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论