0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

はじめてのOMNeT++~待ち行列が1つでサービスが2つの場合

Posted at

1.はじめに

今回はサービスが2つで待ち行列が1つの場合のシミュレーションを紹介します。例題は前回および前々回と同様に平鍋氏の「サルでもわかる待ち行列」からの引用です。
例題イメージ

2.シミュレーションモデルの説明

今回は待ち行列は「M/M/2」と言われるタイプです。待合室は1つであり、患者は空いた診療室に先着順に案内されます。
ネットワークモデル

3.ソースコードの説明

ソースコードをGitHubから入手可能ですので、以前の説明を参考にOMNeT++ IDEに取り込んで下さい。前々回(SimLesson01A)からの変更点について以下に説明します。

ファイル名 内容 主な変更点
Simulation.ned ノード及びノード間のネットワークを定義 追加したノードとノード間接続の定義
Queue.ned 待合室モジュールのパラメータやコネクションの定義 分岐に関するパラメータの追加
Queue.h 待合室モジュールのヘッダー 分岐に関するクラスプロパティやメソッドの追加
Queue.cc 待合室モジュールのソース 分岐に関するロジックの実装

Simlation.nedの変更点は、(1)医師ノード(doctor)を配列に変更、(2)ノード追加に伴う経路追加の2点です。

network Simulation01
{
	(中略)
	submodules:
		(中略)
- 		doctor: Sink {中略}
+ 		doctor[2]: Sink {中略}	//---(1)
	connections:
		(中略)
-		wait.out --> doctor.in;
+		wait.out++ --> doctor[0].in;	//---(2)
+		wait.out++ --> doctor[1].in;	//---(2)
-		doctor.out --> wait.in++;
+		doctor[0].out --> wait.in++;	//---(2)
+		doctor[1].out --> wait.in++;	//---(2)
}

Queue.nedの変更点は、(1)最大分岐数を指定するためのforkNumberパラメータ追加、(2)outゲートを配列化の2点です。

simple Queue
{
	parameters:
+		int forkNumber = default(0);	//---(1)
		volatile double intervalTime @unit(s) = default(0);
	gates:
		input in[];
-		output out;
+		output out[];	//---(2)
}

Queue.hの変更点は、(1)最大分岐数を格納するmaxプロパティの追加、(2)診療室の空き状況を格納するnxtプロパティの追加、(3)患者を診療室に案内するforwardメソッド追加の3点です。

(前略)
class Queue : public cSimpleModule
private:
+	int max;	//---(1)
+	int ntx;	//---(2)
	cQueue queue;
	cLongHistogram waitTime;

protected:
	virtual void initialize();
	virtual void handleMessage(cMessage *msg);
+	virtual void forward(int);		//---(3)
	virtual void finish();
};
(後略)

Queue.ccの変更点は(1)各プロパティの初期化、(2)WATCHマクロによる監視設定、(3)患者が到着した際に診療室にrequestメッセージを送る処理を削除、(4)患者が到着した際に変数nxtの状況に応じてforwardメソッドを呼び出す処理を追加、(5)待合室からcallメッセージが届いた際に患者を案内する処理を削除、(6)待合室からcallメッセージが届いた際に変数nxtを更新してforwardメソッドを呼び出す処理を追加、(7)患者を診療室に案内するforwardメソッドの追加の7点です。

(前略)
void Queue::initialize()
{
	queue.setName("queue");
+	max = par("forkNumber").intValue();		//---(1)
+	nxt = 0;
+	WATCH(nxt);								//---(2)
	scheduleAt(simTime() + par("intervalTime"), new cMessage("beat"));
}

void Queue::handleMessage(cMessage *msg)
{
	if (strcmp(msg->getName(), "patient") == 0) {
		msg->setTimestamp(simTime());
		queue.insert(msg);
-		send(new cMessage("request"), "out");			//---(3)
+		if (nxt < 3) {									//---(4)
+			if (nxt == 0)  j = intuniform(0, max-1);
+			else           j = (nxt == 1) ? 1: 0;
+			forward(j);
+		}
	} else if (strcmp(msg->getName(), "call") == 0) {
-		if (queue.getLength() > 0) {					//---(5)
-			cMessage *patient = check_and_cast<cMessage *>(queue.pop());    
-			waitTime.collect(simTime() - patient->getTimestamp());          
-			send(patient, "out");
-		}
+		int j = (strcmp(msg->getArrivalGate()->getFullName(), "in[1]") == 0) ? 0: 1;	//---(6)
+		nxt ^= j + 1;
+		forward(j);
		delete msg;
	}
}

+ void Queue::forward(int doctor)			//---(7)
+ {
+	if (queue.getLength() > 0) {
+		cMessage *patient = check_and_cast<cMessage *>(queue.pop());    
+		waitTime.collect(simTime() - patient->getTimestamp());          
+		send(patient, "out", doctor);
+		nxt ^= doctor + 1;
+	}
+ }

なお変数nxtは0から3の値を取り、その意味は以下の通りです。

意味
0 診療室は両方空いている
1 診療室0は使用中だが、診療室1は空いている
2 診療室0は空いているが、診療室1は使用中
3 診療室は両方使用中

omnetpp.iniの変更点は、(1)分岐数の設定、(2)医師ノードの配列化、(3)乱数設定の変更の3点です。

[General]
(前略)
network = Simulation01
*.enter.intervalTime = exponential(10.0s)
+*.wait.forkNumber = 2								//---(1)
-*.doctor.serviceTime = exponential(8.0s)
+*.doctor[*].serviceTime = exponential(8.0s)		//---(2)
num-rngs = 1

-Config Run2]
-*.doctor.serviceTime = exponential(4.0s)

+[Config Run1]
+num-rngs = 2										//---(3)
+*.enter[*].rng-0 = 0
+*.doctor[*].rng-0 = 1

4.例題の実行

シミレーションのConfigurationを「General」として実行すると待ち行列の長さが1.21分となりました。公式(M/M/2)の解は1.5分なので約2割の誤差があります。このように結果が想定と異なる場合、原因としてはロジックの設計ミス、実装のバグ、パラメータが乱数に依拠することに起因する誤差等が考えられます。
実行結果1

そこでConfigurationを「Run1」として乱数体系を変更して実行すると結果は1.49分となり、公式の解とほぼ同じ結果となりました。
実行結果2

5.Tips

シミュレーションの乱数体系はomnetpp.iniの設定により簡単に変更できます。Generalでは(1)にて乱数生成器が1つしか定義されていないので、全てのノードは同じ乱数生成器を使用します。いっぽうRun1では(2)にて乱数生成器を2つ定義しており、(3)にてenterノードが乱数生成器0、(4)にてdoctorノードが乱数生成器1を利用するよう設定しています。

[General]
(前略)
num-rngs = 1				//---(1)

[Config Run1]
num-rngs = 2				//---(2)
*.enter[*].rng-0 = 0		//---(3)
*.doctor[*].rng-0 = 1		//---(4)

6.改訂履歴

改定日 改定前 改定後
2022.4.30 - 初期登録
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?