import * as Util from '../util/util.js';
/**
* @memberOf Simulator
* @class VirtualApp
* @classdesc 仮想アプリケーション
*
* @param {String}
* [userName = "dafault"] ログに出力するユーザ名
* @param {Array}
* [sequence = SQL3個のオン処理] 取引の処理シーケンスを格納した配列
* @param {Number}
* [times = 2 回] 繰返し回数
* @param {Number}
* [thinkTime = 300 ms] 繰返し時の次回処理開始までの平均時間(ミリ秒)
*/
export default (function() { // #53
/** @constructor */
function VirtualApp(userName, model){
if(!(this instanceof VirtualApp)){
return new VirtualApp(userName, model);
}
this._userName = userName || "default"; // ログ出力テキスト
// 定数の設定
// イベントシーケンスを取得する
if (typeof(model) === "undefined") model = {}
model.baseModel = model.baseModel || {"holds": []};
this._sequence = model.sequence;
// イベントシーケンスの繰り返し回数
this._times = (typeof(model.times) !== "undefined") ? model.times : 1;
// イベントシーケンス終了時に再実行する場合の平均再開時間
this._thinkTime = Math.max(0,
(typeof(model.thinkTime) !== "undefined") ? model.thinkTime : 500);
// イベントシーケンス終了時に再実行する場合の最小再開時間
this._thinkTimeMin = Math.max(0,
(typeof(model.thinkTimeMin) !== "undefined") ? model.thinkTimeMin : 500);
// メッセージ(ログ末尾に付与する文字列)
this._baseMessage = model.message; // + "\n"; // #88
this._message = "";
this._history = []; // #62
// 変数の設定
this._startTime = Number.MIN_SAFE_INTEGER; // イベントシーケンス開始時刻(UNIX時刻:ミリ秒)
this._sequenceTime = Number.MIN_SAFE_INTEGER; // シミュレータに登録したイベントの時刻(現在時刻)
this._sequenceIdx = 0; // シミュレータに登録したイベントシーケンスの位置
}
/** @private */
//
// public
/**
* シミュレータのログを出力する
*
* @memberof Simulator.VirtualApp
* @param {Number}
* logLv ログレベル(isLog <= logLvのときログ出力する)<br>
* (0:なし, 1:エラー時のみ, 2:+ETAT, 3:+push/pop, 4:+HOLD/FREE)
* @param {boolean}
* isLog ログ出力レベル
* @param {Number}
* time 日時を表す数値(ミリ秒)
* @param {Object}
* vApp 仮想アプリ
* @param {Object}
* resource リソース
* @param {String}
* text ログテキスト
* @param {String}
* highText 強調表示テキスト
*/
VirtualApp.prototype.logger = function(logLv, isLog, time, vApp, resource, text, highText) {
// 0:なしの時
if (!isLog) return;
var errCode = 0;
if (resource && (resource._holdHeap.size() !== resource._holdingQty)) {
// エラー時★
errCode = 1;
highText = highText ? highText + " unkown error!!" : " unkown error!!";
} else if (logLv > +isLog) {
return;
}
// エラーログ編集
var user = vApp ? " " + vApp._userName : " ";
var resourceText = resource ? (" [" + resource._name + " wait:"
+ resource._waitHeap.size() + "hold:"
+ resource._holdHeap.size() + "="
+ resource._holdingQty + " qty] ") : " ";
text = text || "";
var logText = Util.D2S(this.getTime(),"hh:mm:ss.000",true)
+ user +"(" + this._times + "-" + this._sequenceIdx + ")"
+ resourceText
+ text;
highText = highText || "";
// エラー時の強制補正★
var modify = "";
if (errCode === 1) { // リソースヒープもしくはリソース量を強制補正する
var deleted = undefined;
if (resource._holdHeap.size() < resource._holdingQty) {
deleted = resource._holdHeap.del(this);
}
if (deleted){
modify = "FORCE★: holdHeap.del(" + deleted.userName + ")";
} else {
modify = "FORCE★: holdingQty modified"
resource._holdingQty = resource._holdHeap.size();
}
}
// エラーログ出力
if (highText || modify){
console.log(logText + " %o", highText + " " + modify);
} else {
console.log(logText);
}
}
/**
* 取引を開始する
*
* @memberof Simulator.VirtualApp
* @param {Number}
* startTime 開始時刻(UNIX時刻:ミリ秒)
* @return {Object}仮想アプリケーション(this)
*/
VirtualApp.prototype.start = function(startTime) {
this._times--; // イベントシーケンスの繰り返し回数を1減らす
this._sequenceIdx = 0; // シミュレータに登録したイベントシーケンスの位置
this._startTime = startTime; // イベントシーケンス開始時刻(UNIX時刻:ミリ秒)
this._sequenceTime = startTime; // シミュレータに登録したイベントの時刻
this._message = this._baseMessage; // ログ末尾に出力する文字列の初期化
this._history = []; // ログ末尾に出力する状態遷移履歴情報の初期化 #62
this.addHistory("start");
return this;
};
/**
* イベント時刻を返却する
*
* @memberof Simulator.VirtualApp
* @return {Number} イベント時刻(UNIX時刻:ミリ秒)
*/
VirtualApp.prototype.getTime = function() {
return this._sequenceTime;
};
/**
* リソース使用量を返却する
*
* @memberof Simulator.VirtualApp
* @param {Object}
* [resource| 指定なしのとき1.0を返却する] リソース
* @return {Number} リソース使用量
*/
VirtualApp.prototype.getAmount = function(resource) {
return 1.0;
};
/**
* 次の状態に遷移する、シーケンス終了時TATログを出力する
*
* @memberof Simulator.VirtualApp
* @param {Object}
* system VirtualSystem
* @return {Array}再スケジュールするイベント(仮想アプリケーションorリソース)の配列、登録処理完了時はthisを含まない
*/
VirtualApp.prototype.next = function(system) {
this.logger(4, system._log, system.getTime(), this, undefined, 'NEXT', undefined);
var events = []; // 戻り値
var ret = {result: true, events: [this]};
if (this._sequenceIdx < this._sequence.length) { // イベントシーケンス処理途中のとき
var seq = this._sequence[this._sequenceIdx]; // 現在の処理シーケンス位置
// holdリソースを取得する
if (seq.hold && seq.hold !== "") {
// holdリソースが指定されているとき、指定リソースを確保する(確保できたとき[this]が戻り値)
ret = system.getResouce(seq.hold).hold(system, this); // #61
events = ret.events;
} else {
// holdリソースが指定されていないとき、無条件に自身をスケジュール対象に加える
events = [this];
}
// リソースを確保できたとき、該当シーケンスを完了させる
if (0 < events.length && (0 <= this._times) && ret.result) { // #61
// 完了した処理の処理時間を加える
var tatAdd = Math.ceil(Util.Random().exponential(seq.tat - seq.tatMin));
this.setSequenceTime(this._sequenceTime += seq.tatMin + tatAdd, seq.hold); // #61
// シーケンスのfreeで指定されているリソースの解放
if (seq.free) {
for (var i = 0; i < seq.free.length; i++) {
events = events.concat(system.getResouce(seq.free[i]).free(this));
}
}
// 次の処理を参照する( シミュレータに登録したイベントシーケンスの位置)
this._sequenceIdx++;
}
return events;
}
// イベントシーケンスを終えたときTATログを出力する(this._sequenceIdx >= this._sequence.length)
var vApp = this._finish(system, "N_000");
// シーケンスをstart状態に設定する
// this._sequenceTime = system.getTime(); はfinishで更新されるので不可
// this._startTime = this._sequenceTime; finishで設定する
// 繰返し処理を継続する場合、自アプリケーションを再スケジュールする
if (vApp) {
events.push(this);
}
return events;
};
/**
* Freeに伴い、次の状態に遷移する
*
* @memberof Simulator.VirtualApp
* @param {Number}
* [time | 変更しない} イベント時刻(UNIX時刻:ミリ秒)
* @param {String}
* status ログに追記する時刻設定理由文字列
* @return {Object} 仮想アプリケーション(this)
*/
VirtualApp.prototype.setSequenceTime = function(time, status) {
status = status || "";
// 解放された時刻をイベント時刻に設定する
this._sequenceTime = time;
// ログに状態遷移履歴を追記する
return this.addHistory(status);
};
/**
* ログにステータス変更履歴を追記する
*
* @memberof Simulator.VirtualApp
* @param {String}
* status ログに追記する状態遷移の理由文字列
* @param {Number}
* time 状態遷移時刻(ミリ秒)
* @return {Object} 仮想アプリケーション(this)
*/
VirtualApp.prototype.addHistory = function(status, time) {
// var timeStr = "";
// if (typeof(time) === "number") {
// timeStr = Util.D2S(time, "mm:ss.000", true) + " seq:"
// }
// this._message += " [" + this._sequenceIdx + ":" + status + "]" // #61
// + timeStr + Util.D2S(this._sequenceTime, "mm:ss.000", true);
// 状態遷移履歴(ログ出力用)を追加する
this._history.push({ // #62
sequenceIdx : this._sequenceIdx,
status : status,
time : time,
sequenceTime : this._sequenceTime
});
return this;
};
/**
* アベンド処理(holdしている可能性のあるリソースを解放し、イベントシーケンスを強制終了する)
*
* @memberof Simulator.VirtualApp
* @param {Object}
* system VirtualSystem
* @param {Object}
* holdedResource アベンドさせたVirtualResource
* @param {String}
* [logID="E_600"] ログID
* @param {String}
* [logMesseage=""] ログメッセージ
* @param {Boolean}
* [isHolding=true] リソース(holdedResource)をholdしているか否か<br>
* @param {Number}
* [abendTime=sytem.getTime()] abend時刻
* @return {Array}再スケジュールするイベント(仮想アプリケーションorリソース)の配列、登録処理完了時はthisを含まない
*/
VirtualApp.prototype.abend = function(system, holdedResource,
logID, logMesseage, isHolding, abendTime) {
logID = logID || "E_600";
abendTime = abendTime || system.getTime();
isHolding = (typeof(isHolding) === "boolean") ? isHolding : true; // #61
var events = []; // 戻り値
this.logger(3, system._log, this._sequenceTime, this, holdedResource, 'abend', undefined);
var seq = this._sequence[this._sequenceIdx];
// 現在のシーケンスでholdするリソースがあるとき、waitしている可能性があるため、waitから削除する
if (seq && seq.hold) { // #61
system._resources[seq.hold].release(this);
}
// holdingリソースを開放する
var holdings;
// シーケンスから、holdingリソース一覧を取得する #59
if (seq) holdings = seq.holding;
if (holdings) { // シーケンス上holdingリソースがあるとき(undefined対策) #61
for (var i = holdings.length - 1; 0 <= i; i--) { // #61
// holdedResourceは削除処理対象外(呼び出し元リソースは、呼び出し前に削除済なので)
if (holdings[i] !== holdedResource._name || isHolding){ // #61
// holdingリソースを開放し、使用リソース減に伴って新たにスケジュールするvAppを取得 #59
events = events.concat(system._resources[holdings[i]].free(this));
}
}
}
// イベントシーケンスを強制終了する
this.setSequenceTime(abendTime, logID); // #59
var vApp = this._finish(system, logID, logMesseage);
// 自vAppに継続処理があれば、自vAppをスケジュール対象に加える
if (vApp && (0 <= this._times)) { // #61
events = events.concat([vApp]);
}
return events;
};
/**
* イベント終了時処理(ログ出力と、繰り返し判定)
*
* @memberof Simulator.VirtualApp
* @param {Object}
* system VirtualSystem
* @param {String}
* [logID="N_000"] ログID(ログメッセージの先頭文字)
* @param {String}
* [logMesseage=""] ログメッセージ
* @param {Number}
* [forceTime] 強制終了時刻を指定する(ミシュレーション停止後のeTat強制出力用)
* @return {Object|undefined} 再スケジュールするときthis、再スケジュールしないときundefined
*/
VirtualApp.prototype._finish = function(system, logID, logMesseage, forceTime) {
logID = logID || "N__00";
logMesseage = logMesseage || "";
var events = []; // 戻り値
var logText = logID + " " + this._userName + " " + logMesseage + " " + this._message;
var now = this._sequenceTime;
// 強制終了時(シミュレーション終了時刻到来時)、自APのシミュレーションを強制終了する
if (typeof(forceTime) === "number") {
// 起動済処理はTATログを出力する #59
if (this._startTime <= forceTime) {
// TATログを出力する
system.eTat.push( { x: forceTime,
y: Math.round(forceTime - this._startTime),
sTatIdx: 0,
message: logText,
history: this._history} ); // #62
this.logger(2, system._log, forceTime, this, undefined, 'finish() FORCE"', logText);
}
this._sequenceIdx = this._sequence.length; // #61 処理完了状態にする
this._startTime = this._sequenceTime; // #61 念のため設定
return undefined;
}
// 起動済処理はTATログを出力する #59
if (this._startTime <= now ) { // || this._sequenceIdx ===
// this._sequence.length) {
system.eTat.push( { x: now,
y: Math.round(now - this._startTime),
sTatIdx: 0,
message: logText,
history: this._history} ); // #62
this.logger(2, system._log, now, this, undefined, 'finish() "', logText);
} else {
this.logger(0, system._log, now, this, undefined,
'finish() Unexpected error★ _startTime > _sequenceTime:'
+ Util.D2S(this._startTime, "hh:mm:ss.000", true)
+ " " + this._startTime + " > " + now
+ " Idx:" + this._sequenceIdx
, logText);
}
this._message = this._baseMessage; // ログ末尾に追加するメッセージの初期化
// 継続判定
if (0 < this._times) { // イベントシーケンスを繰り返すとき
// イベント時刻にThink time(指数分布)を加える
var nextTime = this.getTime() + this._thinkTimeMin;
if (this._thinkTimeMin < this._thinkTime) {
nextTime += Math.ceil(Math.abs( // #61
Util.Random().exponential(this._thinkTime - this._thinkTimeMin)));
}
// 処理の先頭に戻る
return this.start(nextTime);
}
// イベントシーケンスを継続しない時(this._times < 0)
this._times--; // イベントシーケンスの繰り返し回数を1減らす
this._sequenceIdx = this._sequence.length; // #61 処理完了状態にする
this._startTime = this._sequenceTime; // #61
return undefined;
};
/* new */
return VirtualApp;
}());