Source: src/timeSeries/timeSeries-Tat.js

import * as Util from '../util/util.js';
import MappedETat from './timeSeries-MappedETat.js';
import ETat from './timeSeries-ETat.js';
import Cash from './timeSeries-Cash.js';


/**
 * @memberOf TimeSeries
 * @class Tat
 * @classdesc Tat 応答時間(Turnaround time)の時系列データ管理クラス
 * @param {ETAT}
 *            [eTat] [[終了時刻(ms), 処理時間(sec), (任意)ログレコード等], ...]<br>
 *            eTatが指定されているとき、this.seriesSet を設定する
 */
export default function Tat(eTat) {
    
    this.cTpsUnit = Tat.UNIT_CTPS[0];
    this.seriesSet = [];

    if (eTat){
        this.createSeries(eTat);
    }
}

/**
 * クラス定数
 */
Tat.UNIT_CTPS = [
        { label: "/sec",   unit: 1000 },
        { label: "/min",   unit: 60000 },
        { label: "/hour",  unit: 3600000 },
        { label: "/day",   unit: 86400000 }];
Tat.CYCLE = 60000;  // ミリ秒 #57

// グラフ定数
Tat.CONC = {
        key : 'conc',
        name : '多重度(詳細)',
        label : 'conc:%N',
        N : 0,
        scale : 0,
        color : 'rgba(  0,  0,127, 0.3)',
        tpsN : 1
    }; // #7
Tat.CTPS = {
        key : 'cTps',
        name : '多重度(区間最大)',
        label : 'conc(max):%N',
        N : 1,
        scale : 0,
        color : 'rgba(  0,  0,127, 0.1)',
        detailN : 0
    };
Tat.ETPS = {
        key : 'eTps',
        name : 'average tps / min (end) [line]', // #57
        label : 'end:%Ntps',
        N : 2,
        scale : 0,
        color : 'rgba(  0, 127, 127, 0.3)'
    };
Tat.STAT = {
        key : 'sTat',
        name : 'response by start time [Y2軸:plot]',
        label : 'start:%Nms',
        N : 3,
        scale : 1,
        color : 'rgba(127, 127, 0, 0.3)'
    };
Tat.ETAT = {
        key : 'eTat',
        name : 'response by end time   [Y2軸:plot]',
        label : 'end:%Nms',
        N : 4,
        scale : 1,
        color : 'rgba(127,  0,  0, 0.3)'
    };
Tat.EMPS = {
        key : 'eMps',
        name : 'max response / min (end) [Y2軸:line]', // #57
        label : 'max:%Nms',
        N : 5,
        scale : 1,
        color : 'rgba(127,   0,  64, 0.3)'
    };
Tat.EAPS = {
        key : 'eAps',
        name : 'average response / min (end) [Y2軸:line]', // #57
        label : 'ave:%Nms',
        N : 6,
        scale : 1,
        color : 'rgba(127,   0,  64, 0.1)'
    };
Tat.toSeriesArray = function(conc, cTps, eTps, sTat, eTat, eMps, eAps) {
    return [ conc, cTps, eTps, sTat, eTat, eMps, eAps ];
}
Tat.seriesConfig = Tat.toSeriesArray(
        Tat.CONC, Tat.CTPS, Tat.ETPS, Tat.STAT, Tat.ETAT,
        Tat.EMPS, Tat.EAPS);

/**
 * 終了時刻のTAT時系列データ(eTat)から、描画用時系列データ配列を作成する
 * 
 * @memberOf TimeSeries
 * @param {ETAT}
 *            eTat [[終了時刻(ms), 処理時間(sec), (任意)ログレコード等], ...]
 * @return {seriesSet} dygraph用時系列データ配列
 */
Tat.prototype.createSeries = function (eTat) {
    // 時系列データを初期化する
    var conc = [], cTps = [], eTps = [], sTat = [], eMps = [], eAps = [];

    // 集計対象データがないとき
    if (eTat.length === 0)
        return Tat.toSeriesArray(conc, cTps, eTps, sTat, eTat, eMps, eAps);

    /** eTatをソートする * */
    // 開始時刻でソートする #35
    eTat.sort(function (a, b) {
        return a.x - b.x;
    });
    Util.Logger.ShowLogText("[1:eTat sorten ] " + eTat.length + " plots",
            "calc");

    /** eTps(時間あたり処理件数),eMps,eAps(時間あたり最大/平均応答時間)時系列データを作成する * */
    var dFrom = Math.floor(eTat[0].x / Tat.CYCLE) * Tat.CYCLE,
        dTo = dFrom + Tat.CYCLE,
        num = 0, // #39
        maxTat = 0.0, // #19
        aveTmp = 0.0;
    eTat.forEach(function (e) {
        if (e.x < dTo) {
            num += 1;
            if (maxTat < e.y) maxTat = e.y; // #19
            aveTmp += e.y;
        } else {
            eTps.push({x : dFrom, y : num * 1000 / Tat.CYCLE}); // #57
            eMps.push({x : dFrom, y : maxTat}); // #19
            eAps.push({x : dFrom, y : aveTmp / num});
            dFrom = Math.floor(e.x / Tat.CYCLE) * Tat.CYCLE;
            dTo = dFrom + Tat.CYCLE;
            num = 1;
            maxTat = e.y; // #19 #39
            aveTmp = e.y; // #39
        }
    }, this);
    eTps.push({x : dFrom,             y : num * 1000 / Tat.CYCLE}); // #57
    eTps.push({x : dFrom + Tat.CYCLE, y : num * 1000 / Tat.CYCLE}); // #57
    eMps.push({x : dFrom,             y : maxTat}); // #19
    eMps.push({x : dFrom + Tat.CYCLE, y : maxTat}); // #57
    eAps.push({x : dFrom,             y : aveTmp / num});
    eAps.push({x : dFrom + Tat.CYCLE, y : aveTmp / num}); // #57
    
    Util.Logger.ShowLogText("[3:eTps,eMps,eAps created] " + eTps.length
            + " plots", "calc");

    /** sTat(開始時間)時系列データを作成する,同時に入力eTatを補正する * */
    // eTatからsTatを登録する
    eTat.forEach(function (e, i) {
        // 処理時間=0 のとき、1マイクロ秒とみなす(有効桁0.2マイクロ秒に切上される)
        if (e.y === 0) {
            e.y = 0.001;
            e.x += e.y;
        } // ミリ秒
        // sTatにeTatデータを登録する
        sTat.push({
            x : e.x - e.y,
            y : e.y,
            eTatIdx : i
        });
    });
    // 開始時刻でソートする
    sTat.sort(function (a, b) {
        return a.x - b.x;
    });
    // eTatにsTatの位置を設定する
    sTat.forEach(function (s, i) {
        eTat[s.eTatIdx].sTatIdx = i;
    });
    Util.Logger.ShowLogText("[2:sTat created] " + sTat.length + " plots",
            "calc");

    /** CONC(多重度)時系列データを作成する * */
    var concTmp = [];
    // eTatから、多重度が変化した時刻の一覧を作成する
    eTat.map(function (e, i) {
        // 開始時刻にカウントアップ情報を追加する
        concTmp.push({x : e.x - e.y, y : 1});
        // 終了時刻をカウントダウン情報を追加する
        concTmp.push({x : e.x, y : -1});
    });
    // concを変化した時刻(開始or終了)でソートする
    concTmp.sort(function (a, b) {
        return a.x - b.x;
    });
    // concに同時取引数を設定する
    var concNum = 0;
    concTmp.forEach(function (c, i, a) {
        // 重複削除用フラグを立てる #23
        if (i > 0 && c.x === a[i - 1].x) {
            a[i - 1].del = true;
        }
        // 同時取引数を集計する(前提:c.y に、開始なら1、終了なら(-1)が設定されている)
        concNum += c.y;
        c.y = concNum;
    });
    // concの同じ時刻の点を削除する #23
    conc = concTmp.filter(function (c) {
        return !c.del;
    });
    Util.Logger.ShowLogText("[4:conc created] " + conc.length + " plots",
            "calc");

    /** cTPS秒間同時処理件数(concurrent transactions/sec)時系列データを作成する #18 * */
    var XSec = floorTime(conc[0].x, Tat.CYCLE), // ミリ秒
    YMax = conc[0].y, YNext = conc[0].y;
    // concの先頭と末尾の時刻(x)の差よりPlot数を求め、Plot数が規定数(8000個)を超えたら、桁上げする #38
    var cTpsMaxPlots = 8000, // 桁上げするPlot数
    cTpsUnits = Tat.UNIT_CTPS, // #48
    concTerm = conc[conc.length - 1].x - conc[0].x, // ミリ秒
    cTpsIdx = 0;
    while (cTpsIdx < cTpsUnits.length
            && concTerm / cTpsUnits[cTpsIdx].unit > cTpsMaxPlots) {
        cTpsIdx++;
    }
    cTpsIdx = (cTpsUnits.length > cTpsIdx) ? cTpsIdx : cTpsUnits.length - 1;
    this.cTpsUnit = cTpsUnits[cTpsIdx];
    // メニューのViewのcTPSのラベルに単位を追加する
    var pos = (this === HJN.chart) ? 0 : 1;
    document.getElementsByName("cTps")[pos]
                .parentNode.lastChild.data = HJN.Tat.CTPS.name + this.cTpsUnit.label;

    // 規定時間単位の最大同時処理数cTPSを作成する
    conc.forEach(function (c) {
        if (floorTime(c.x, this.cTpsUnit.unit) === XSec) { // c.xは ミリ秒
            YMax = Math.max(YMax, c.y);
        } else {
            cTps.push({x : XSec, y : Math.max(YMax, YNext)});
            for (var t = XSec + this.cTpsUnit.unit;
                    t < floorTime(c.x, this.cTpsUnit.unit);
                    t += this.cTpsUnit.unit) { // c.xはミリ秒
                        cTps.push({x : t, y : YNext});
                if (YNext === 0) break;
            }
            XSec = floorTime(c.x, this.cTpsUnit.unit);
            YMax = Math.max(YNext, c.y);
        }
        YNext = c.y;
    }, this);
    cTps.push({x : XSec, y : YMax});
    cTps.push({x : XSec + this.cTpsUnit.unit, y : YNext});

    // Util.Logger.ShowLogText("[5-1:cTps created] " + cTps.length + "
    // plots","calc");

    // cTpsのxからindexを引くMapを作成する #18
    eTat = new ETat(eTat); // #75
    Util.Logger.ShowLogText("[5:cTps created] " + cTps.length + " plots("
            + +Math.floor(concTerm / 1000) + "sec" + cTpsUnits[cTpsIdx].label
            + ")", "calc");

    // 集計結果をHJN.Graphに設定する 注)this.SERIESESと同じ順番にすること
    this.seriesSet = Tat.toSeriesArray(conc, cTps, eTps, sTat, eTat, eMps, eAps);

    return this;

    
    // 時刻を指定ミリ秒間隔で切り捨てる(内部関数)
    function floorTime(t, cycle) {
        return Math.floor(Math.floor(t / cycle) * cycle);
    }
};

/**
 * cTpsの集計単位を取得する
 * 
 * @memberOf TimeSeries
 * @return {object} 単位構造体 { label: "/sec", unit: 1000 }
 */
Tat.prototype.getCTpsUnit = function () {
    return this.cTpsUnit;
}

/**
 * seriesSetを取得する
 * 
 * @memberOf TimeSeries
 * @return {array} seriesSet
 */
Tat.prototype.getSeriesSet = function () {
    return this.seriesSet;
}