首先应该从receiveSignal函数开始说起,既然说起 receiveSignal 那也一定要谈下其所属类 baseModule,关于其官方是这么介绍的

Base class for all simple modules of a host. This method raises an error if the host state changes to something else than ACTIVE. Therefore that a sub-classing module can be used in a simulation where the host state can change it has to override that method which forces the author to make sure the module reacts well to host state changes.

Alternatively one can also set a “notAffectedByHostState” parameter of the module to true. The base module additionally provides a function findHost which returns a pointer to the host module and a function hostIndex to return the index of the host module. The latter one correspondes to the index shown in tkenv and comes in very handy for testing and debugging using tkenv. It is used e.g. in all the ‘print’ macros used for debugging. There will never be a stand-alone BaseModule module.

Note: most modules wont derive from BaseModule directly but from its sub class “BatteryAccess” which extends BaseModule by several methods for accessing the battery module.

在其下实现了很多接口函数,其派生类里也调用其来注册订阅 signal,如应用层的 DemoBaseApplLayer,在 initialize() 函数的初始化中订阅了 mobility 消息和 parking 消息。

1
2
findHost()->subscribe(BaseMobility::mobilityStateChangedSignal, this);
findHost()->subscribe(TraCIMobility::parkingStateChangedSignal, this);

之后继承了 BaseModule 中的 receiveSignal 函数并 override

1
2
3
4
5
6
7
8
9
10
11
void DemoBaseApplLayer::receiveSignal(cComponent* source, simsignal_t signalID, cObject* obj, cObject* details)
{
Enter_Method_Silent();
EV_INFO << "Run DemoBaseApplLayer::receiveSignal" << endl;
if (signalID == BaseMobility::mobilityStateChangedSignal) {
handlePositionUpdate(obj);
}
else if (signalID == TraCIMobility::parkingStateChangedSignal) {
handleParkingUpdate(obj);
}
}

即只有收到 Basemobility::mobilityStateChangedSignal 信号后才进行位置的变动处理。

OMNet++ 在仿真中使用 Simulation Signals(或者 Signals)来实现 “发布-订阅” 机制。假设信号发送模块如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// module emitting some signal whenever it receives a message (contrived example)
class SignalEmitter: public cModule {
public:
static const simsignal_t counterSignal;
static const simsignal_t nameSignal;
protected:
long messageCounter = 0;
void handleMessage(cMessage *msg) {
emit(counterSignal, messageCounter);
messageCounter++;
emit(nameSignal, msg->str());
// ...
}
};

const simsignal_t SignalEmitter::counterSignal = registerSignal("messageCounter");
const simsignal_t SignalEmitter::nameSignal = registerSignal("messageName");

虽然很容易配置和发出信号,但信号反应的实现特别复杂包括一系列模板类代码。

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
// module reacting to signals in plain OMNeT++ fashion, has to inherit from cListener or implement cIListener
class WithoutSignalCallbacks: public cModule, public cListener {
protected:
void initialize() override {
// subscribe to the signal with itself as handler
subscribe(SignalEmitter::counterSignal, this);
subscribe(SignalEmitter::nameSignal, this);
}
public:
// signal handler for all signals with a long parameter
void receiveSignal(cComponent* source, simsignal_t signalID, long l, cObject* details) override {
// identify signal to handle
if (signalID == SignalEmitter::counterSignal) {
// react to signal
std::cerr << "Module " << source->getFullName() << " received message nr " << l << std::endl;
}
}
// signal handler for all signals with a string parameter
void receiveSignal(cComponent* source, simsignal_t signalID, const char* s, cObject* details) override {
// identify signal to handle
if (signalID == SignalEmitter::nameSignal) {
// react to signal
std::cerr << "Module " << source->getFullName() << " received message with content " << c << std::endl;
}
}
};

SignalManager

使用 [SignalManager],可以封装所有信号处理,并将配置保留在一个地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// module reacting to signals using the SignalManager
class WithSignalManager: public cModule {
protected:
veins::SignalManager signalManager;
void initialize() override {
// reaction to the signal
auto nameSignalCallback = [this](veins::SignalPayload<const char*> payload) {
std::cerr << "Module " << payload.source->getFullName() << " received message with content " << payload.p << std::endl;
};
auto counterSignalCallback = [this](veins::SignalPayload<long> payload) {
std::cerr << "Module " << payload.source->getFullName() << " received message nr " << payload.p << " via signal " << payload.signalID << std::endl;
};
// register callbacks with the signal manager, which takes care to perform the actual subscription
signalManager.subscribeCallback(getSystemModule(), SignalEmitter::nameSignal, nameSignalCallback);
signalManager.subscribeCallback(getSystemModule(), SignalEmitter::counterSignal, counterSignalCallback);
}
};

SignalCallbackListener

或者,我们可以省略 Manager 部分并为每个注册的回调创建单独的 [SignalCallbackListener] 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// module reacting to signals using the SignalCallback instances
class WithSignalCallbacks: public cModule {
protected:
veins::SignalCallbackListener counterCallback;
veins::SignalCallbackListener nameCallback;
void initialize() override {
// reaction to the signal
auto nameSignalCallback = [this](veins::SignalPayload<const char*> payload) {
std::cerr << "Module " << payload.source->getFullName() << " received message with content " << payload.p << std::endl;
};
auto counterSignalCallback = [this](veins::SignalPayload<long> payload) {
std::cerr << "Module " << payload.source->getFullName() << " received message nr " << payload.p << " via signal " << payload.signalID << std::endl;
};
// register callback objects, which takes care to perform the actual subscription
counterCallback = veins::SignalCallbackListener(getSystemModule(), SignalEmitter::counterSignal, counterSignalCallback);
nameCallback = veins::SignalCallbackListener(getSystemModule(), SignalEmitter::nameSignal, nameCallback);
}
};

这样可以更清楚地控制每个 SignalCallbackListener 实例的生命周期。

Common Features

使用 [SignalManager] 和 [SignalCallbackListener] 的话便无需自己实现 cIListener,生命周期是自动管理的,并且代码都保存在一个地方,除了 Lambda 还可以使用其他形式的回调例如仿函数。