Files
openvino/docs/_static/js/graphs.js
bstankix 7c41d78b5d Add OVMS benchmarks (#16984)
* Add ovms support for Graph Builder

* Add new OVMS dataset
2023-04-17 12:27:58 +02:00

1040 lines
38 KiB
JavaScript

// =================== GENERAL OUTPUT CONFIG =========================
const chartDisclaimers = {
Value: 'Value: Performance/(No_of_sockets * Price_of_CPU_dGPU), where prices are in USD as of December 2022.',
Efficiency: 'Efficiency: Performance/(No_of_sockets * TDP_of_CPU_dGPU), where total power dissipation (TDP) is in Watt as of December 2022.'
}
const OVdefaultSelections = {
platforms: {name: 'platform',
data: [
'Intel® Core™ i9-12900K CPU-only',
'Intel® Core™ i9-13900K CPU-only',
'Intel® Core™ i5-10500TE CPU-only',
'Intel® Core™ i5-13600K CPU-only',
'Intel® Core™ i5-8500 CPU-only',
'Intel® Core™ i7-8700T CPU-only',
'Intel® Core™ i9-10900TE CPU-only',
'Intel® Core™ i7-1165G7 CPU-only'
]
},
platformFilters: {name: 'coretype', data: ['CPU']},
models: {name: 'networkmodel',
data: [
'bert-large-uncased-whole-word-masking-squad-0001 ',
'mobilenet-ssd ',
'resnet-50',
'yolo_v3_tiny'
]
},
parameters: {name: 'kpi', data: ['Throughput']},
pracision: {name: 'precision', data: ['INT8', 'FP32']}
}
const OVMSdefaultSelections = {
platforms: {name: 'platform',
data: [
'Intel® Core™ i3-10100 CPU-only',
'Intel® Core™ i5-8500 CPU-only',
'Intel® Core™ i7-8700T CPU-only',
'Intel® Core™ i9-10920X CPU-only',
]
},
models: {name: 'networkmodel',
data: [
'bert-small-uncased-whole-word-masking-squad-0002',
'mobilenet-ssd ',
'resnet-50',
'yolo_v3_tiny'
]
},
parameters: {name: 'kpi', data: ['Throughput']},
pracision: {name: 'precision', data: ['OV-INT8 (reference)', 'INT8']}
}
// ====================================================
class Filter {
// param: GraphData[], networkModels[]
static FilterByNetworkModel(graphDataArr, networkModels) {
// This is a bit obtuse, collect all options from all models
// Some of them might return dupes, so convert them to a map, and get unique objects based on names
const optionMap = new Map();
networkModels.map((model) => graphDataArr.filter((graphData => graphData.networkModel === model)))
.flat(1)
.forEach(item => optionMap.set(item.platformName, item));
// convert the option map back to an array with just the values
return Array.from(optionMap.values());
}
// param: GraphData[], ieType
static FilterByIeType(graphDataArr, value) {
return graphDataArr.filter((data) => data.ieType.includes(value));
}
// param: GraphData[], clientPlatforms[]
static FilterByClientPlatforms(graphDataArr, platformsArr) {
return graphDataArr.filter((data) => platformsArr.includes(data.platformName));
}
// param: GraphData[], coreTypes[]
static FilterByCoreTypes(graphDataArr, coreTypes) {
if (coreTypes) {
return graphDataArr.filter((data) => coreTypes.includes(data.ieType));
}
return graphDataArr;
}
// param: GraphData[] (of one networkModel), key (throughput, latency, efficiency, value)
static getKpiData(graphDataArr, key) {
return graphDataArr.map((data) => {
return data[key];
});
}
}
class ExcelDataTransformer {
static transform(csvdata, version) {
const entries = csvdata.filter((entry) => {
return !entry.includes('begin_rec') && !entry.includes('end_rec');
});
// do other purging and data massaging here
// else generate
return entries.map((entry) => {
if (version == 'ovms')
return new GraphData(new OVMSExcelData(entry));
return new GraphData(new ExcelData(entry));
});
}
}
class ExcelData {
constructor(csvdataline) {
if (!csvdataline) {
return;
}
this.networkModel = csvdataline[0].toLowerCase();
this.release = csvdataline[1];
this.ieType = csvdataline[2];
this.platformName = csvdataline[3];
this.throughputInt8 = csvdataline[4];
this.throughputFP16 = csvdataline[5];
this.throughputFP32 = csvdataline[6];
this.value = csvdataline[7];
this.efficiency = csvdataline[8];
this.price = csvdataline[9];
this.tdp = csvdataline[10];
this.sockets = csvdataline[11];
this.pricePerSocket = csvdataline[12];
this.tdpPerSocket = csvdataline[13];
this.latency = csvdataline[14];
}
}
class OVMSExcelData extends ExcelData {
constructor(csvdataline) {
super(csvdataline);
this.throughputOVMSInt8 = csvdataline[5];
this.throughputInt8 = csvdataline[4];
this.throughputOVMSFP32 = csvdataline[7];
this.throughputFP32 = csvdataline[6];
}
}
class GraphData {
constructor(excelData) {
if (!excelData) {
return;
}
this.networkModel = excelData.networkModel;
this.release = excelData.release;
this.ieType = excelData.ieType;
this.platformName = excelData.platformName;
this.kpi = new KPI(
{
'ovmsint8': excelData.throughputOVMSInt8,
'ovmsfp32': excelData.throughputOVMSFP32,
'int8': excelData.throughputInt8,
'fp16': excelData.throughputFP16,
'fp32': excelData.throughputFP32
},
excelData.value,
excelData.efficiency,
excelData.latency);
this.price = excelData.price;
this.tdp = excelData.tdp;
this.sockets = excelData.sockets;
this.pricePerSocket = excelData.pricePerSocket;
this.tdpPerSocket = excelData.tdpPerSocket;
this.latency = excelData.latency;
}
}
class KPI {
constructor(precisions, value, efficiency, latency) {
this.throughput = precisions;
this.value = value;
this.efficiency = efficiency;
this.latency = latency;
}
}
class Modal {
static getIeTypeLabel(ietype) {
switch (ietype) {
case 'core':
return 'Client Platforms (Intel® Core™)';
case 'xeon':
return 'Server Platforms (Intel® Xeon®)';
case 'atom':
return 'Mobile Platforms (Intel® Atom™)';
case 'accel':
return 'Accelerator Platforms';
default:
return '';
}
}
static getCoreTypesLabels() {
return ['CPU', 'iGPU', 'CPU+iGPU'];
}
static getKpisLabels(version) {
if (version == 'ovms')
return ['Throughput'];
return ['Throughput', 'Value', 'Efficiency', 'Latency'];
}
static getPrecisionsLabels(version) {
if (version == 'ovms')
return ['OV-INT8 (reference)', 'INT8', 'OV-FP32 (reference)', 'FP32'];
return ['INT8', 'FP16', 'FP32'];
}
static getCoreTypes(labels) {
return labels.map((label) => {
switch (label) {
case 'CPU':
return 'core';
case 'iGPU':
return 'core-iGPU';
case 'CPU+iGPU':
return 'core-CPU+iGPU';
default:
return '';
}
});
}
static getPrecisions(labels) {
return labels.map((label) => {
switch (label) {
case 'OV-INT8 (reference)':
return 'ovmsint8';
case 'OV-FP32 (reference)':
return 'ovmsfp32';
case 'INT8':
return 'int8';
case 'FP16':
return 'fp16';
case 'FP32':
return 'fp32';
default:
return '';
}
});
}
}
class Graph {
constructor(data) {
this.data = data;
}
data = new GraphData();
// functions to get unique keys
static getNetworkModels(graphDataArr) {
return Array.from(new Set(graphDataArr.map((obj) => obj.networkModel)));
}
static getIeTypes(graphDataArr) {
return Array.from(new Set(graphDataArr.map((obj) => obj.ieType)));
}
static getPlatforms(graphDataArr) {
return Array.from(new Set(graphDataArr.map((obj) => obj.platformName)));
}
static getCoreTypes(graphDataArr) {
return Array.from(new Set(graphDataArr.map((obj) => obj.ieType)));
}
// param: GraphData[]
static getPlatformNames(graphDataArr) {
return graphDataArr.map((data) => data.platformName);
}
// param: GraphData[], kpi: string
static getDatabyKPI(graphDataArr, kpi) {
switch (kpi) {
case 'throughput':
return graphDataArr.map((data) => data.kpi.throughput);
case 'latency':
return graphDataArr.map((data) => data.kpi.latency);
case 'efficiency':
return graphDataArr.map((data) => data.kpi.efficiency);
case 'value':
return graphDataArr.map((data) => data.kpi.value);
default:
return [];
}
}
// this returns an object that is used to ender the chart
static getGraphConfig(kpi, precisions) {
switch (kpi) {
case 'throughput':
return {
chartTitle: 'Throughput',
chartSubtitle: '(higher is better)',
iconClass: 'throughput-icon',
datasets: precisions.map((precision) => this.getPrecisionConfig(precision)),
};
case 'latency':
return {
chartTitle: 'Latency',
chartSubtitle: '(lower is better)',
iconClass: 'latency-icon',
datasets: [{ data: null, color: '#8F5DA2', label: 'Milliseconds' }],
};
case 'value':
return {
chartTitle: 'Value',
chartSubtitle: '(higher is better)',
iconClass: 'value-icon',
datasets: [{ data: null, color: '#8BAE46', label: 'FPS/$ (INT8)' }],
};
case 'efficiency':
return {
chartTitle: 'Efficiency',
chartSubtitle: '(higher is better)',
iconClass: 'efficiency-icon',
datasets: [{ data: null, color: '#E96115', label: 'FPS/TDP (INT8)' }],
};
default:
return {};
}
}
static getPrecisionConfig(precision) {
switch (precision) {
case 'ovmsint8':
return { data: null, color: '#FF8F51', label: 'FPS (OV Ref. INT8)' };
case 'ovmsfp32':
return { data: null, color: '#B24501', label: 'FPS (OV Ref. FP32)' };
case 'int8':
return { data: null, color: '#00C7FD', label: 'FPS (INT8)' };
case 'fp16':
return { data: null, color: '#009fca', label: 'FPS (FP16)' };
case 'fp32':
return { data: null, color: '#007797', label: 'FPS (FP32)' };
default:
return {};
}
}
static getGraphPlatformText(platform) {
switch (platform) {
case 'atom':
return 'Mobile Platforms';
case 'core':
return 'Client Platforms';
case 'xeon':
return 'Server Platforms';
case 'accel':
return 'Accelerated Platforms';
default:
return '';
}
}
}
class ChartDisplay {
constructor(mode, numberOfCharts) {
this.mode = mode;
this.numberOfChartsInRow = numberOfCharts;
}
}
$(document).ready(function () {
$('.ov-toolkit-benchmark-results').on('click', () => showModal('ov'));
$('.ovms-toolkit-benchmark-results').on('click', () => showModal('ovms'));
function clickBuildGraphs(graph, networkModels, ietype, platforms, kpis, precisions) {
renderData(graph, networkModels, ietype, platforms, kpis, precisions);
$('.modal-footer').show();
$('#modal-display-graphs').show();
$('.edit-settings-btn').on('click', (event) => {
$('#modal-configure-graphs').show();
$('#modal-display-graphs').hide();
$('.modal-footer').hide();
$('.chart-placeholder').empty();
});
$('.graph-chart-title-header').on('click', (event) => {
var parent = event.target.parentElement;
if ($(parent).children('.chart-wrap,.empty-chart-container').is(":visible")) {
$(parent).children('.chart-wrap,.empty-chart-container').hide();
$(parent).children('.chevron-right-btn').show();
$(parent).children('.chevron-down-btn').hide();
$
} else {
$(parent).children('.chart-wrap,.empty-chart-container').show();
$(parent).children('.chevron-down-btn').show();
$(parent).children('.chevron-right-btn').hide();
}
});
}
function hideModal() {
$('#graphModal').remove();
$('body').css('overflow', 'auto');
}
function showModal(version) {
$('body').css('overflow', 'hidden');
let dataPath = '_static/benchmarks_files/OV-benchmark-data.csv';
if (version == 'ovms')
dataPath = '_static/benchmarks_files/OVMS-benchmark-data.csv';
Papa.parse(dataPath, {
download: true,
complete: (result) => renderModal(result, version)
});
}
function getSelectedNetworkModels() {
return $('.models-column-one input:checked, .models-column-two input:checked').not('[data-networkmodel="Select All"]').map(function () {
return $(this).data('networkmodel');
}).get();
}
function getSelectedIeType() {
return $('.ietype-column input:checked').map(function () {
return $(this).data('ietype');
}).get().pop();
}
function getSelectedCoreTypes() {
return $('.client-platform-column .selected').map(function () {
return $(this).data('coretype');
}).get();
}
function getSelectedClientPlatforms() {
return $('.client-platform-column input:checked').map(function () {
return $(this).data('platform');
}).get();
}
function getSelectedKpis() {
return $('.kpi-column input:checked').map(function () {
return $(this).data('kpi');
}).get();
}
function getSelectedPrecisions() {
return $('.precisions-column input:checked').map(function () {
return $(this).data('precision');
}).get();
}
function validateSelections() {
if (getSelectedNetworkModels().length > 0
&& getSelectedIeType()
&& getSelectedClientPlatforms().length > 0
&& getSelectedKpis().length > 0) {
if (getSelectedKpis().includes('Throughput')) {
if (getSelectedPrecisions().length > 0) {
$('#build-graphs-btn').prop('disabled', false);
return;
}
$('#build-graphs-btn').prop('disabled', true);
return;
}
$('#build-graphs-btn').prop('disabled', false);
return;
}
$('#build-graphs-btn').prop('disabled', true);
}
function renderModal(result, version) {
// remove header from csv line
result.data.shift();
var graph = new Graph(ExcelDataTransformer.transform(result.data, version));
var networkModels = Graph.getNetworkModels(graph.data);
var ieTypes = Graph.getIeTypes(graph.data);
fetch('_static/html/modal.html').then((response) => response.text()).then((text) => {
// generate and configure modal container
var modal = $('<div>');
modal.attr('id', 'graphModal');
modal.addClass('modal');
// generate and configure modal content from html import
var modalContent = $(text);
modalContent.attr('id', 'graphModalContent');
modalContent.addClass('modal-content');
modal.append(modalContent);
const models = networkModels.map((networkModel) => createCheckMark(networkModel, 'networkmodel'));
const selectAllModelsButton = createCheckMark('Select All', 'networkmodel')
modal.find('.models-column-one').append(selectAllModelsButton).append(models.slice(0, models.length / 2));
modal.find('.models-column-two').append(models.slice(models.length / 2));
const precisions = Modal.getPrecisionsLabels(version).map((precision) => createCheckMark(precision, 'precision'));
modal.find('.precisions-column').append(precisions);
selectAllCheckboxes(precisions);
disableAllCheckboxes(precisions);
const types = ieTypes.map((ieType) => {
var labelText = Modal.getIeTypeLabel(ieType);
if (labelText) {
const item = $('<label class="checkmark-container">');
const checkboxSpan = $('<span class="checkmark radiobutton">');
item.text(Modal.getIeTypeLabel(ieType));
const radio = $('<input type="radio" name="ietype"/>');
item.append(radio);
item.append(checkboxSpan);
radio.attr('data-ietype', ieType);
return item;
}
});
modal.find('#modal-display-graphs').hide();
modal.find('.ietype-column').append(types);
modal.find('.ietype-column input').first().prop('checked', true);
const kpiLabels = Modal.getKpisLabels(version).map((kpi) => createCheckMark(kpi, 'kpi'));
modal.find('.kpi-column').append(kpiLabels);
$('body').prepend(modal);
renderClientPlatforms(graph.data, modal, version, true);
preselectDefaultSettings(graph.data, modal, version);
$('.clear-all-btn').on('click', clearAll);
$('#build-graphs-btn').on('click', () => {
$('#modal-configure-graphs').hide();
clickBuildGraphs(graph, getSelectedNetworkModels(), getSelectedIeType(), getSelectedClientPlatforms(), getSelectedKpis(), Modal.getPrecisions(getSelectedPrecisions()));
});
$('.modal-close').on('click', hideModal);
$('.close-btn').on('click', hideModal);
modal.find('.models-column-one input[data-networkmodel="Select All"]').on('click', function() {
if ($(this).prop('checked'))
selectAllCheckboxes(models);
else deSelectAllCheckboxes(models);
});
modal.find('.ietype-column input').on('click', () => renderClientPlatforms(graph.data, modal, version, true));
modal.find('.kpi-column input').on('click', validateThroughputSelection);
modal.find('input').on('click', validateSelections);
});
}
function validateThroughputSelection() {
const precisions = $('.precisions-column').find('input')
if (getSelectedKpis().includes('Throughput')) {
precisions.prop('disabled', false);
}
else {
precisions.prop('disabled', true);
}
}
function clearAll() {
$('.modal-content-grid-container input:checkbox').each((index, object) => $(object).prop('checked', false));
// Uncomment if you want the Clear All button to reset the Platform Type column as well
// modal.find('.ietype-column input').first().prop('checked', true);
validateThroughputSelection();
validateSelections();
}
function preselectDefaultSettings(data, modal, version) {
const defaultSelections = (version == 'ov') ? OVdefaultSelections : OVMSdefaultSelections;
if (defaultSelections.platformFilters) {
const filters = modal.find('.selectable-box-container').children('.selectable-box');
filters.removeClass('selected');
defaultSelections.platformFilters.data.forEach(selection => {
filters.filter(`[data-${defaultSelections.platformFilters.name}="${selection}"]`).addClass('selected');
});
renderClientPlatforms(data, modal, version);
}
clearAll();
for (setting in defaultSelections) {
let name = defaultSelections[setting].name;
defaultSelections[setting].data.forEach(selection => {
$(`input[data-${name}="${selection}"]`).prop('checked', true);
});
}
validateThroughputSelection();
validateSelections();
}
function showCoreSelectorTypes(coreTypes, graphDataArr, modal) {
if ($('.client-platform-column').find('.selectable-box-container').length) {
$('.client-platform-column').find('.selectable-box-container').show();
return;
}
var container = $('<div>');
container.addClass('selectable-box-container');
coreTypes.forEach((type) => {
var box = $('<div>' + type + '</div>');
box.attr('data-coretype', type);
box.addClass('selectable-box selected');
container.append(box);
});
$('.client-platform-column').prepend(container);
$('.client-platform-column .selectable-box').on('click', function () {
if ($(this).hasClass('selected')) {
$(this).removeClass('selected');
} else {
$(this).addClass('selected');
}
var fPlatforms = filterClientPlatforms(graphDataArr, getSelectedNetworkModels(), getSelectedIeType(), Modal.getCoreTypes(getSelectedCoreTypes()));
renderClientPlatformsItems(modal, Graph.getPlatformNames(fPlatforms), true);
validateSelections();
});
}
function hideCoreSelectorTypes() {
$('.client-platform-column').find('.selectable-box-container').hide();
}
function filterClientPlatforms(data, networkModels, ietype, coreTypes) {
// No longer filtering on the network type, if at some point we want the network type as a filter, uncomment this
// var first = Filter.FilterByNetworkModel(data, networkModels);
var second = Filter.FilterByIeType(data, ietype);
if (ietype === 'core') {
second = Filter.FilterByCoreTypes(second, coreTypes);
}
const optionMap = new Map();
second.forEach(item => optionMap.set(item.platformName, item));
return Array.from(optionMap.values());
}
function renderClientPlatforms(data, modal, version, preselectEveryItem) {
if (getSelectedIeType() === 'core') {
showCoreSelectorTypes(Modal.getCoreTypesLabels(), data, modal);
if (version === 'ovms')
hideCoreSelectorTypes();
}
else {
hideCoreSelectorTypes();
}
var fPlatforms = filterClientPlatforms(data, getSelectedNetworkModels(), getSelectedIeType(), Modal.getCoreTypes(getSelectedCoreTypes()));
renderClientPlatformsItems(modal, Graph.getPlatformNames(fPlatforms), preselectEveryItem);
}
function renderClientPlatformsItems(modal, platformNames, preselectEveryItem) {
$('.client-platform-column .checkmark-container').remove();
const clientPlatforms = platformNames.map((platform) => createCheckMark(platform, 'platform'));
if (preselectEveryItem)
selectAllCheckboxes(clientPlatforms);
modal.find('.client-platform-column').append(clientPlatforms);
modal.find('.client-platform-column input').on('click', validateSelections);
}
function createCheckMark(itemLabel, modelLabel) {
const item = $('<label class="checkmark-container">');
item.text(itemLabel);
const checkbox = $('<input type="checkbox"/>');
const checkboxSpan = $('<span class="checkmark">');
item.append(checkbox);
item.append(checkboxSpan);
checkbox.attr('data-' + modelLabel, itemLabel);
return item;
}
// receives a jquery list of items and selects all input checkboxes
function selectAllCheckboxes(items) {
items.forEach((item) => {
item.find(':input').prop('checked', true);
});
}
function enableAllCheckboxes(items) {
items.forEach((item) => {
item.find(':input').prop('disabled', false);
})
}
function disableAllCheckboxes(items) {
items.forEach((item) => {
item.find(':input').prop('disabled', true);
})
}
function deSelectAllCheckboxes(items) {
items.forEach((item) => {
item.find(':input').prop('checked', false);
});
}
// =================== HTMLLEGEND =========================
const getOrCreateLegendList = (chart, id) => {
const legendContainer = document.getElementById(id);
let listContainer = legendContainer.querySelector('ul');
if (!listContainer) {
listContainer = document.createElement('ul');
listContainer.style.display = 'flex';
listContainer.style.flexDirection = 'column';
listContainer.style.margin = 0;
listContainer.style.padding = 0;
listContainer.style.paddingLeft = '10px';
legendContainer.appendChild(listContainer);
}
return listContainer;
};
const htmlLegendPlugin = {
id: 'htmlLegend',
afterUpdate(chart, args, options) {
const ul = getOrCreateLegendList(chart, chart.options.plugins.htmlLegend.containerID);
// Remove old legend items
while (ul.firstChild) {
ul.firstChild.remove();
}
// Reuse the built-in legendItems generator
const items = chart.legend.legendItems;
items.forEach(item => {
const li = document.createElement('li');
li.style.alignItems = 'center';
li.style.display = 'flex';
li.style.flexDirection = 'row';
li.style.marginLeft = '10px';
li.onclick = () => {
const {type} = chart.config;
if (type === 'pie' || type === 'doughnut') {
// Pie and doughnut charts only have a single dataset and visibility is per item
chart.toggleDataVisibility(item.index);
} else {
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
}
chart.update();
};
// Color box
const boxSpan = document.createElement('span');
boxSpan.style.background = item.fillStyle;
boxSpan.style.borderColor = item.strokeStyle;
boxSpan.style.borderWidth = item.lineWidth + 'px';
boxSpan.style.display = 'inline-block';
boxSpan.style.height = '12px';
boxSpan.style.marginRight = '10px';
boxSpan.style.width = '30px';
// Text
const textContainer = document.createElement('p');
textContainer.style.color = item.fontColor;
textContainer.style.margin = 0;
textContainer.style.padding = 0;
// textContainer.style.fontFamily = 'Roboto';
textContainer.style.fontSize = '0.8rem';
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
const text = document.createTextNode(item.text);
textContainer.appendChild(text);
li.appendChild(boxSpan);
li.appendChild(textContainer);
ul.appendChild(li);
});
}
};
// ====================================================
function getChartOptions(title, containerId) {
return {
responsive: true,
maintainAspectRatio: false,
legend: {display: false},
title: {
display: false,
text: title
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true
}
}],
yAxes: [{
ticks: {
display: false, //this will remove only the label
beginAtZero: true
}
}]
},
plugins: {
htmlLegend: {
// ID of the container to put the legend in
containerID: containerId,
}
}
}
}
// params: string[], Datasets[]
function getChartDataNew(labels, datasets) {
return {
labels: labels,
datasets: datasets.map((item) => {
return {
label: item.label,
data: item.data,
backgroundColor: item.color,
borderColor: 'rgba(170,170,170,0)',
barThickness: 12
}
})
}
}
function renderData(graph, networkModels, ietype, platforms, kpis, precisions) {
$('.chart-placeholder').empty();
$('.modal-disclaimer-box').empty();
const display = new ChartDisplay(getChartsDisplayMode(kpis.length), kpis.length);
networkModels.forEach((networkModel) => {
var chartName = networkModel;
var chartSlug = chartName.replace(')', '').replace(' (', '-');
var chartContainer = $('<div>');
var chevronDown = '<span class="chevron-down-btn"></span>';
var chevronRight = '<span style="display:none" class="chevron-right-btn"></span>';
$(chevronRight).hide();
var chartContainerHeader = $(chevronDown + chevronRight + '<span class="graph-chart-title">' + networkModel + '</span>');
chartContainerHeader.addClass('graph-chart-title-header');
chartContainer.prepend(chartContainerHeader);
chartContainer.attr('id', 'ov-chart-container-' + chartSlug);
chartContainer.addClass('chart-container');
var filteredNetworkModels = Filter.FilterByNetworkModel(graph.data, [networkModel]);
var filteredIeTypes = Filter.FilterByIeType(filteredNetworkModels, ietype);
var filteredGraphData = Filter.FilterByClientPlatforms(filteredIeTypes, platforms);
$('.chart-placeholder').append(chartContainer);
if (filteredGraphData.length > 0) {
createChartWithNewData(filteredGraphData, chartContainer, kpis, ietype, precisions, display);
} else {
createEmptyChartContainer(chartContainer);
}
})
for (let kpi of kpis) {
if (chartDisclaimers[kpi])
$('.modal-disclaimer-box').append($('<p>').text(chartDisclaimers[kpi]))
}
$(window).off('resize');
$(window).resize(() => resetChartsDisplay(display));
};
function createEmptyChartContainer(chartContainer) {
chartContainer.append($('<div>').addClass('empty-chart-container').text('No data for this configuration.'));
}
// this function should take the final data set and turn it into graphs
// params: GraphData, unused, chartContainer
function createChartWithNewData(model, chartContainer, kpis, ietype, precisions, display) {
var chartWrap = $('<div>');
chartWrap.addClass('chart-wrap');
chartContainer.append(chartWrap);
var labels = Graph.getPlatformNames(model);
var graphConfigs = kpis.map((str) => {
var kpi = str.toLowerCase();
if (kpi === 'throughput') {
var throughputData = Graph.getDatabyKPI(model, kpi);
var config = Graph.getGraphConfig(kpi, precisions);
precisions.forEach((prec, index) => {
config.datasets[index].data = throughputData.map(tData => tData[prec]);
});
return config;
}
var config = Graph.getGraphConfig(kpi);
config.datasets[0].data = Graph.getDatabyKPI(model, kpi);
return config;
});
// get the client platform labels and create labels for all the graphs
var labelsContainer = $('<div>');
labelsContainer.addClass('chart-labels-container');
chartWrap.append(labelsContainer);
// get the kpi title's and create headers for the graphs
var chartGraphsContainer = $('<div>');
chartGraphsContainer.addClass('chart-graphs-container');
chartWrap.append(chartGraphsContainer);
graphConfigs.forEach((graphConfig, index) => {
const id = getRandomNumber();
var graphItem = $(`<div id=${id}>`);
graphItem.addClass('graph-item');
var columnHeaderContainer = $('<div>');
columnHeaderContainer.addClass('chart-column-title');
var columnIcon = $('<div class="icon">');
columnIcon.addClass(graphConfig.iconClass);
columnHeaderContainer.append(columnIcon);
var columnHeader = $('<div class="chart-header">');
columnHeader.append($('<div class="title">' + graphConfig.chartTitle + '</div>'));
columnHeader.append($('<div class="title">' + Graph.getGraphPlatformText(ietype) + '</div>'));
columnHeader.append($('<div class="subtitle">' + graphConfig.chartSubtitle + '</div>'));
columnHeaderContainer.append(columnHeader);
chartGraphsContainer.append(graphItem);
var graphClass = $('<div>');
graphClass.addClass('graph-row');
graphItem.append(columnHeaderContainer);
graphItem.append(graphClass);
processMetricNew(labels, graphConfig.datasets, graphConfig.chartTitle, graphClass, 'graph-row-column', id);
window.setTimeout(() => {
const topPadding = getLabelsTopPadding(display.mode);
const labelsHeight = (labels.length * 55);
const chartHeight = $(graphItem).outerHeight();
const bottomPadding = (chartHeight - (topPadding + labelsHeight));
var labelsItem = $('<div>');
labelsItem.addClass('chart-labels-item');
labels.forEach((label) => {
labelsItem.append($('<div class="title">' + label + '</div>'));
});
labelsItem.css('padding-top', topPadding + 'px');
labelsItem.css('padding-bottom', bottomPadding + 'px');
setInitialItemsVisibility(labelsItem, index, display.mode);
labelsContainer.append(labelsItem);
});
});
setChartsDisplayDirection(display.mode);
adjustHeaderIcons(display.mode);
}
function processMetricNew(labels, datasets, chartTitle, container, widthClass, id) {
// ratio for consistent chart label height
var heightRatio = (30 + (labels.length * 55));
var chart = $('<div>');
const containerId = `legend-container-${id}`;
const legend = $(`<div id="${containerId}">`);
legend.addClass('graph-legend-container');
chart.addClass('chart');
chart.addClass(widthClass);
chart.height(heightRatio);
var canvas = $('<canvas>');
chart.append(canvas);
container.append(chart);
container.append(legend);
var context = canvas.get(0).getContext('2d');
context.canvas.height = heightRatio;
window.setTimeout(() => {
new Chart(context, {
type: 'horizontalBar',
data: getChartDataNew(labels, datasets),
options: getChartOptions(chartTitle, containerId),
plugins: [htmlLegendPlugin]
});
});
}
function getRandomNumber() {
return Math.floor(Math.random() * 100000);
}
function resetChartsDisplay(currentDisplay) {
const newDisplayMode = getChartsDisplayMode(currentDisplay.numberOfChartsInRow);
if (currentDisplay.mode != newDisplayMode) {
currentDisplay.mode = newDisplayMode;
setChartsDisplayDirection(currentDisplay.mode);
adjustLabels(currentDisplay.mode);
adjustHeaderIcons(currentDisplay.mode);
}
}
function adjustLabels(displayMode) {
const firstLabels = $('.chart-labels-container').find('.chart-labels-item:first-child');
const labels = $('.chart-labels-container').find('.chart-labels-item');
labels.css('padding-top', getLabelsTopPadding(displayMode));
if (displayMode == 'column') {
labels.show();
}
else {
labels.hide()
firstLabels.show();
}
}
function adjustHeaderIcons(displayMode) {
const icons = $('.graph-item').find('.chart-column-title');
if (displayMode == 'rowCompact')
icons.css('flex-direction', 'column')
else
icons.css('flex-direction', 'row')
}
function getLabelsTopPadding(displayMode) {
return (displayMode == 'rowCompact') ? 105.91 : 83.912;
}
function setChartsDisplayDirection(displayMode) {
const container = $('.chart-placeholder').find('.chart-graphs-container');
if (displayMode == 'column') {
container.css('flex-direction', 'column');
}
else {
container.css('flex-direction', 'row');
}
}
function setInitialItemsVisibility(item, count, displayMode) {
if (count == 0 || displayMode == 'column') item.show();
else item.hide();
}
function getChartsDisplayMode(numberOfCharts) {
switch (numberOfCharts) {
case 4:
return window.matchMedia('(max-width: 721px)').matches ? 'column'
: window.matchMedia('(max-width: 830px)').matches ? 'rowCompact'
: 'row';
case 3:
return window.matchMedia('(max-width: 569px)').matches ? 'column'
: window.matchMedia('(max-width: 649px)').matches ? 'rowCompact'
: 'row';
case 2:
return window.matchMedia('(max-width: 500px)').matches ? 'column'
: 'row';
default:
return 'row';
}
}
});