Commit e8ece533 authored by Paul Graydon's avatar Paul Graydon

wendelin_telecom_base: Refactor KPI pipeline

parent cf866716
from xlte.amari import kpi as amari_kpi
from xlte import kpi
from zLOG import LOG, WARNING
import io
import json
def load_measurements(alogm):
mlog = kpi.MeasurementLog()
......@@ -9,13 +11,14 @@ def load_measurements(alogm):
while measurement is not None:
try:
mlog.append(measurement)
except AssertionError:
except AssertionError as e:
# Invalid measurement: simply skip it
pass
LOG('WendelinTelecomOrsKpi.loadMeasurements', WARNING, "Skipped data during KPI calculation: %s" % e)
measurement = alogm.read()
except amari_kpi.LogError:
except amari_kpi.LogError as e:
# Invalid measurement: simply skip it
pass
LOG('WendelinTelecomOrsKpi.loadMeasurements', WARNING, "Skipped data during KPI calculation: %s" % e)
finally:
alogm.close()
return mlog
......@@ -33,32 +36,115 @@ def calc_periods(mlog, tperiod):
# No data to read: exit
return
def getOrsKpiValues(data):
def calcEnbKpi(data, t_period):
fxlog = io.StringIO(data)
alogm = amari_kpi.LogMeasure(fxlog, open('/dev/null', 'r'))
mlog = load_measurements(alogm)
# E-RAB Accessibility KPI
vt = []
v_initial_epsb_estab_sr = []
v_added_epsb_estab_sr = []
tperiod = 60 # seconds
for calc in calc_periods(mlog, tperiod):
for calc in calc_periods(mlog, t_period):
vt.append(calc.tau_lo)
erab_accessibility = calc.erab_accessibility()
v_initial_epsb_estab_sr.append((erab_accessibility[0]['lo'], erab_accessibility[0]['hi']))
v_added_epsb_estab_sr.append((erab_accessibility[1]['lo'], erab_accessibility[1]['hi']))
# E-UTRAN IP Throughput KPI
evt = []
v_ip_throughput_qci = []
tperiod = 3 # seconds
for calc in calc_periods(mlog, tperiod):
for calc in calc_periods(mlog, t_period):
evt.append(calc.tau_lo)
eutran_ip_throughput = calc.eutran_ip_throughput()
period_qci_data = []
for qci, qci_measurement in enumerate(eutran_ip_throughput):
period_qci_data.append((qci, qci_measurement['dl']['lo'], qci_measurement['dl']['hi'], qci_measurement['ul']['lo'], qci_measurement['ul']['hi']))
for qci_measurement in eutran_ip_throughput:
period_qci_data.append((
qci_measurement['dl']['lo'],
qci_measurement['dl']['hi'],
qci_measurement['ul']['lo'],
qci_measurement['ul']['hi']
))
v_ip_throughput_qci.append(period_qci_data)
return vt, v_initial_epsb_estab_sr, v_added_epsb_estab_sr, evt, v_ip_throughput_qci
kpi_data_dict = dict(
e_rab_accessibility=(vt, v_initial_epsb_estab_sr, v_added_epsb_estab_sr),
e_utran_ip_throughput=(evt, v_ip_throughput_qci)
)
return kpi_data_dict
def getOrsEnbKpi(self, data_array_url, kpi_type, REQUEST=None, RESPONSE=None):
content_type = "application/json"
RESPONSE.setHeader("Content-Type", content_type)
portal = self.getPortalObject()
# Return empty response if Data Array does not exist or is empty
try:
data_array = portal.restrictedTraverse(data_array_url)
data_array_view = data_array.getArray()[:]
except (KeyError, TypeError):
RESPONSE.write(json.dumps(dict()))
return
if kpi_type == 'e_rab_accessibility':
vt = [x[0] for x in data_array_view]
v_initial_epsb_estab_sr_lo = [x[1] for x in data_array_view]
v_initial_epsb_estab_sr_hi = [x[2] for x in data_array_view]
v_added_epsb_estab_sr_lo = [x[3] for x in data_array_view]
v_added_epsb_estab_sr_hi = [x[4] for x in data_array_view]
response_dict = dict(
vt=vt,
v_initial_epsb_estab_sr_lo=v_initial_epsb_estab_sr_lo,
v_initial_epsb_estab_sr_hi=v_initial_epsb_estab_sr_hi,
v_added_epsb_estab_sr_lo=v_added_epsb_estab_sr_lo,
v_added_epsb_estab_sr_hi=v_added_epsb_estab_sr_hi,
)
RESPONSE.write(json.dumps(response_dict))
elif kpi_type == 'e_utran_ip_throughput':
qci_count = 256
evt = [x[0] for x in data_array.getArray()[::256]]
active_qci = []
dl_lo = []
dl_hi = []
ul_lo = []
ul_hi = []
qci_rows = len(data_array_view) // qci_count
for qci in range(qci_count):
qci_dl_lo = []
qci_dl_hi = []
qci_ul_lo = []
qci_ul_hi = []
for row in range(qci_rows):
array_index = qci + (row * qci_count)
qci_dl_lo.append(data_array_view[array_index][1])
qci_dl_hi.append(data_array_view[array_index][2])
qci_ul_lo.append(data_array_view[array_index][3])
qci_ul_hi.append(data_array_view[array_index][4])
qci_is_silent = all([value == 0.0 for value in qci_dl_hi]) \
and all([value == 0.0 for value in qci_ul_hi])
if qci_is_silent:
continue
active_qci.append(qci)
dl_lo.append(qci_dl_lo)
dl_hi.append(qci_dl_hi)
ul_lo.append(qci_ul_lo)
ul_hi.append(qci_ul_hi)
response_dict = dict(
evt=evt,
active_qci=active_qci,
dl_lo=dl_lo,
dl_hi=dl_hi,
ul_lo=ul_lo,
ul_hi=ul_hi
)
RESPONSE.write(json.dumps(response_dict))
......@@ -8,7 +8,13 @@
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>OrsKpiUtils</string> </value>
<value> <string>WendelinTelecomOrsKpi</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -18,7 +24,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.OrsKpiUtils</string> </value>
<value> <string>extension.erp5.WendelinTelecomOrsKpi</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
......
import json
import numpy as np
progress_indicator = in_stream["Progress Indicator"]
HISTORY_LINE_COUNT = 64
T_PERIOD = 30 # seconds
QCI_COUNT = 256
in_data_stream = in_stream["Data Stream"]
start = progress_indicator.getIntOffsetIndex()
total_size = in_data_stream.getSize()
progress_indicator = in_stream["Progress Indicator"]
offset_index = progress_indicator.getIntOffsetIndex()
start = 0
end = in_data_stream.getSize()
if start >= total_size:
# No new data to process
if offset_index >= end:
return
chunk_size = 512 * 512
end = min(start + chunk_size, total_size)
previous_log_data = ''.join(in_data_stream.readChunkList(start, offset_index))
new_log_data = ''.join(in_data_stream.readChunkList(offset_index, end))
log_data = ''.join(in_data_stream.readChunkList(start, end))
# Some lines may contain several contiguous JSON objects
# Some lines contain several contiguous JSON objects
# Normalize to one JSON object per line
newlines_offset = log_data.count('}{')
log_data = log_data.replace('}{', '}\n{')
# Last chunk may contain an incomplete JSON object: leave it for next iteration
log_data_lines = log_data.splitlines()
if len(log_data_lines) < 2:
return
log_data = '\n'.join(log_data_lines[:-1])
end = start + len(log_data) - newlines_offset + 1
previous_log_data = previous_log_data.replace('}{', '}\n{')
new_log_data = new_log_data.replace('}{', '}\n{')
previous_log_data_line_list = previous_log_data.splitlines()
new_log_data_line_list = new_log_data.splitlines()
# Sort data lines by UTC timestamp
def get_log_line_timestamp(log_line):
try:
log_line_json = json.loads(log_line)
# Use srv_utc as best approximation for meta/event logs
return float(log_line_json['meta']['srv_utc'])
except ValueError:
# Invalid JSON
return 0.0
except KeyError:
return float(log_line_json.get('utc', 0.0))
previous_log_data_line_list.sort(key=get_log_line_timestamp)
new_log_data_line_list.sort(key=get_log_line_timestamp)
# Only use partial history of previous logs to save computing resources
# This also gives some tolerance to log errors
history_start_index = max(0, len(previous_log_data_line_list) - HISTORY_LINE_COUNT)
log_data = '\n'.join(
previous_log_data_line_list[history_start_index:] + new_log_data_line_list
)
log_data = log_data.decode('utf8')
e_rab_data_array = None
......@@ -37,7 +58,10 @@ for array in out_array:
if array['variation'] == 'e_utran':
e_utran_data_array = array['Data Array']
vt, v_initial_epsb_estab_sr, v_added_epsb_estab_sr, evt, v_ip_throughput_qci = context.Base_getOrsKpiValues(log_data)
# Calculate the KPI data
kpi_data_dict = context.Base_calcEnbKpi(log_data, T_PERIOD)
vt, v_initial_epsb_estab_sr, v_added_epsb_estab_sr = kpi_data_dict['e_rab_accessibility']
evt, v_ip_throughput_qci = kpi_data_dict['e_utran_ip_throughput']
e_rab_dtype = np.dtype([
('vt', 'float'),
......@@ -46,10 +70,8 @@ e_rab_dtype = np.dtype([
('vAddedEPSBEstabSR_lo', 'float64'),
('vAddedEPSBEstabSR_hi', 'float64'),
])
e_utran_dtype = np.dtype([
('evt', 'float'),
('qci', 'float'),
('dl_lo', 'float64'),
('dl_hi', 'float64'),
('ul_lo', 'float64'),
......@@ -61,33 +83,37 @@ if not e_rab_array:
e_rab_array = e_rab_data_array.initArray(shape=(0,), dtype=e_rab_dtype)
e_rab_array_data = []
for i in range(len(vt)):
# Don't duplicate KPI data:
# search and start inserting from first new timestamp
vt_column = list(map(lambda x: x[0], e_rab_array[:]))
first_new_row_vt = 0
while (first_new_row_vt < len(vt) and vt[first_new_row_vt] in vt_column):
first_new_row_vt += 1
for i in range(first_new_row_vt, len(vt)):
e_rab_array_data.append((vt[i], v_initial_epsb_estab_sr[i][0], v_initial_epsb_estab_sr[i][1], v_added_epsb_estab_sr[i][0], v_added_epsb_estab_sr[i][1]))
if e_rab_array_data:
e_rab_array_data = np.ndarray((len(e_rab_array_data), ), e_rab_dtype, np.array(e_rab_array_data))
e_rab_array_data = np.ndarray((len(e_rab_array_data), ), e_rab_dtype, np.array(e_rab_array_data))
e_rab_array.append(e_rab_array_data)
e_utran_array = e_utran_data_array.getArray()
if not e_utran_array:
e_utran_array = e_utran_data_array.initArray(shape=(0,), dtype=e_utran_dtype)
e_utran_array_data = []
seen_qci_list = []
i = e_utran_array.shape[0] - 1
qci_idx = 1
while (i >= 0) and (e_utran_array[i][qci_idx] not in seen_qci_list):
seen_qci_list.append(e_utran_array[i][qci_idx])
i -= 1
for i in range(len(evt)):
# Don't duplicate KPI data: same here
evt_column = list(map(lambda x: x[0], e_utran_array[::QCI_COUNT]))
first_new_row_evt = 0
while (first_new_row_evt < len(evt) and evt[first_new_row_evt] in evt_column):
first_new_row_evt += 1
for i in range(first_new_row_evt, len(evt)):
for qci_data in v_ip_throughput_qci[i]:
qci = qci_data[0]
qci_data_hi = [qci_data[2], qci_data[4]]
if qci in seen_qci_list or any([data != 0 for data in qci_data_hi]):
e_utran_array_data.append((evt[i], qci, qci_data[1], qci_data[2], qci_data[3], qci_data[4]))
e_utran_array_data.append((evt[i], qci_data[0], qci_data[1], qci_data[2], qci_data[3]))
if e_utran_array_data:
e_utran_array_data = np.ndarray((len(e_utran_array_data), ), e_utran_dtype, np.array(e_utran_array_data))
e_utran_array_data = np.ndarray((len(e_utran_array_data), ), e_utran_dtype, np.array(e_utran_array_data))
e_utran_array.append(e_utran_array_data)
progress_indicator.setIntOffsetIndex(end)
......
......@@ -5,4 +5,4 @@ It will first unpack the MsgPack, then remove the first item of the tuple (times
get the actual data stored under the 'log' key and append the corresponding string to "Data Stream".
"""
out_stream["Data Stream"].appendData('\n'.join([c[1]['log'] for c in context.unpack(data_chunk)]))
out_stream["Data Stream"].appendData('\n'.join([c[1]["log"] for c in context.unpack(data_chunk)]))
......@@ -13,7 +13,7 @@
<script src="ndarray_bundle.js" type="text/javascript"></script>
<script src="wendelin.js" type="text/javascript"></script>
<script src="plotly_strict_v2.34.0.js" type="text/javascript"></script>
<script src="gadget_ors_e_rab_kpi.js" type="text/javascript"></script>
<script src="gadget_ors_e_rab_accessibility_kpi.js" type="text/javascript"></script>
<link href="gadget_ors_kpi.css" rel="stylesheet" type="text/css">
</head>
<body>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Page" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_ors_e_rab_accessibility_kpi.html</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ors_e_rab_accessibility_kpi_html</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value> <string>en</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Page</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Gadget ORS E RAB Accessibility KPI HTML</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>001</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*global window, rJS, RSVP, console, Plotly */
/*jslint nomen: true, indent: 2 */
(function (window, rJS, RSVP, Plotly, wendelin, loopEventListener) {
/*jslint indent: 2, maxlen: 80, nomen: true */
(function (window, rJS, RSVP, Plotly, loopEventListener) {
"use strict";
var gadget_klass = rJS(window);
......@@ -12,41 +12,101 @@
.declareService(function () {
var gadget = this;
return loopEventListener(window, 'resize', false, function () {
Plotly.Plots.resize(gadget.element.querySelector('.graph-initial-success-rate'));
Plotly.Plots.resize(gadget.element.querySelector('.graph-added-success-rate'));
Plotly.Plots.resize(
gadget.element.querySelector('.graph-initial-success-rate')
);
Plotly.Plots.resize(
gadget.element.querySelector('.graph-added-success-rate')
);
});
})
.declareMethod('render', function (option_dict) {
var gadget = this,
i,
initial_success_data,
added_success_data,
label_list = ["vt", "vInitialEPSBEstabSR_lo", "vInitialEPSBEstabSR_hi", "vAddedEPSBEstabSR_lo", "vAddedEPSBEstabSR_hi"];
kpi_url,
kpi_data_dict;
return new RSVP.Queue().push(function () {
return wendelin.getArrayRawSlice(gadget, option_dict.data_array_key);
return gadget.getSetting("hateoas_url");
})
.push(function (result) {
var graph_data = nj.unpack(result.pick(null, label_list)),
date = [],
vInitialEPSBEstabSR_lo = [],
vInitialEPSBEstabSR_hi = [],
vAddedEPSBEstabSR_lo = [],
vAddedEPSBEstabSR_hi = [];
graph_data.sort(function (a, b) {
return (a[0] - b[0]);
});
for (i = 0; i < graph_data.length; i += 1) {
date.push(new Date(graph_data[i][0] * 1000));
vInitialEPSBEstabSR_lo.push(graph_data[i][1]);
vInitialEPSBEstabSR_hi.push(graph_data[i][2]);
vAddedEPSBEstabSR_lo.push(graph_data[i][3]);
vAddedEPSBEstabSR_hi.push(graph_data[i][4]);
.push(function (hateoas_url) {
kpi_url =
(new URI(hateoas_url)).absoluteTo(location.href).toString() +
'DataAcquisitionUnit_getOrsEnbKpi?data_array_url=' +
option_dict.data_array_url +
'&kpi_type=' + option_dict.kpi_type;
return gadget.jio_getAttachment("erp5", kpi_url);
})
.push(function (response) {
var date = [],
v_initial_epsb_estab_sr_lo = [],
v_initial_epsb_estab_sr_hi = [],
v_added_epsb_estab_sr_lo = [],
v_added_epsb_estab_sr_hi = [],
initial_success_rate_element =
gadget.element.querySelector('.graph-initial-success-rate'),
added_success_rate_element =
gadget.element.querySelector('.graph-added-success-rate'),
initial_success_rate_data = [],
added_success_rate_data = [];
kpi_data_dict = response;
if (Object.keys(kpi_data_dict).length === 0 ||
kpi_data_dict.vt.length === 0) {
Plotly.newPlot(
initial_success_rate_element,
[],
{
'title' : 'Initial E-RAB establishment success rate',
"annotations": [
{
"text": "No data found",
"xref": "paper",
"yref": "paper",
"showarrow": false,
"font": {
"size": 28
}
}
]
}
);
Plotly.newPlot(
added_success_rate_element,
[],
{
'title' : 'Added E-RAB establishment success rate',
"annotations": [
{
"text": "No data found",
"xref": "paper",
"yref": "paper",
"showarrow": false,
"font": {
"size": 28
}
}
]
}
);
gadget.element.querySelector('.ui-icon-spinner').hidden = true;
return;
}
initial_success_data = [
kpi_data_dict.vt.forEach(function (element) {
date.push(new Date(element * 1000));
});
v_initial_epsb_estab_sr_lo = kpi_data_dict.v_initial_epsb_estab_sr_lo;
v_initial_epsb_estab_sr_hi = kpi_data_dict.v_initial_epsb_estab_sr_hi;
v_added_epsb_estab_sr_lo = kpi_data_dict.v_added_epsb_estab_sr_lo;
v_added_epsb_estab_sr_hi = kpi_data_dict.v_added_epsb_estab_sr_hi;
initial_success_rate_data = [
{
x: date,
mode: 'lines+markers',
y: vInitialEPSBEstabSR_lo,
y: v_initial_epsb_estab_sr_lo,
type: 'scatter',
line: {shape: 'hv'},
hovertemplate: 'Date: %{x}<br>Rate: %{y}%',
......@@ -56,7 +116,7 @@
x: date,
mode: 'lines+markers',
fill: 'tonexty',
y: vInitialEPSBEstabSR_hi,
y: v_initial_epsb_estab_sr_hi,
type: 'scatter',
line: {
color: "#6cb9e5",
......@@ -66,11 +126,11 @@
name: 'InitialEPSBEstabSR uncertainty'
}
];
added_success_data = [
added_success_rate_data = [
{
x: date,
mode: 'lines+markers',
y: vAddedEPSBEstabSR_lo,
y: v_added_epsb_estab_sr_lo,
type: 'scatter',
line: {shape: 'hv'},
hovertemplate: 'Date: %{x}<br>Rate: %{y}%',
......@@ -80,7 +140,7 @@
x: date,
mode: 'lines+markers',
fill: 'tonexty',
y: vAddedEPSBEstabSR_hi,
y: v_added_epsb_estab_sr_hi,
type: 'scatter',
line: {
color: "#6cb9e5",
......@@ -92,7 +152,7 @@
];
Plotly.newPlot(
gadget.element.querySelector('.graph-initial-success-rate'),
initial_success_data,
initial_success_rate_data,
{
'title' : 'Initial E-RAB establishment success rate',
'xaxis': {
......@@ -108,7 +168,7 @@
);
Plotly.newPlot(
gadget.element.querySelector('.graph-added-success-rate'),
added_success_data,
added_success_rate_data,
{
'title' : 'Added E-RAB establishment success rate',
'xaxis': {
......@@ -126,4 +186,4 @@
});
});
}(window, rJS, RSVP, Plotly, wendelin, loopEventListener));
}(window, rJS, RSVP, Plotly, loopEventListener));
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Script" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_ors_e_rab_accessibility_kpi.js</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ors_e_rab_accessibility_kpi_js</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value> <string>en</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Gadget ORS E RAB Accessibility KPI JS</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>001</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -17,8 +17,8 @@
<link href="gadget_ors_kpi.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="graph-download-link"></div>
<div class="graph-upload-link"></div>
<div class="graph-downlink"></div>
<div class="graph-uplink"></div>
<div class="ui-icon-spinner ui-btn-icon-notext first-loader"></div>
</body>
</html>
\ No newline at end of file
......@@ -240,7 +240,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1018.60805.51967.7987</string> </value>
<value> <string>1020.52199.64592.21384</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1724849643.71</float>
<float>1732111315.93</float>
<string>UTC</string>
</tuple>
</state>
......
/*global window, rJS, RSVP, console, Plotly */
/*jslint nomen: true, indent: 2 */
(function (window, rJS, RSVP, Plotly, wendelin) {
/*global window, rJS, RSVP, console, Plotly, location */
/*jslint indent: 2, maxlen: 80, nomen: true */
(function (window, rJS, RSVP, Plotly) {
"use strict";
var gadget_klass = rJS(window);
function getPlotData(raw_lo, raw_hi, raw_qci, raw_date) {
function getPlotData(active_qci, date, lo, hi) {
var color,
color_array = [
{'color': '#1f77b4'},
......@@ -19,47 +19,28 @@
{'color': '#bcbd22'},
{'color': '#17becf'}
],
qci_list = [...new Set(...raw_qci)],
qci_count = qci_list.length,
data_count = raw_date.length,
data_list = [],
qci,
date,
lo,
hi,
qci_idx,
i,
j;
qci_list.sort(function(a, b) {
return a - b;
});
for (i = 0; i < qci_count; i += 1) {
qci = qci_list[i];
date = [];
lo = [];
hi = [];
for (j = 0; j < data_count; j += 1) {
qci_idx = raw_qci[j].indexOf(qci);
if (qci_idx == -1) {
continue;
}
date.push(raw_date[j]);
lo.push(raw_lo[j][qci_idx]);
hi.push(raw_hi[j][qci_idx]);
}
data_list = [];
active_qci.forEach(function (qci, i) {
color = color_array[i % color_array.length];
lo[i].forEach(function (value, j) {
lo[i][j] = value / 1e6;
});
hi[i].forEach(function (value, j) {
hi[i][j] = value / 1e6;
});
data_list.push({
x: date,
marker: {
size: 4
},
mode: 'lines+markers',
y: lo,
y: lo[i],
type: 'scatter',
line: color,
name: 'QCI ' + qci,
name: 'IPThp.' + qci,
legendgroup: 'legendgroup' + qci,
hovertemplate: 'Date: %{x}<br>Minimum Link Speed: %{y} Mbit/s'
});
......@@ -69,17 +50,17 @@
size: 4
},
mode: 'lines+markers',
y: hi,
y: hi[i],
type: 'scatter',
line: color,
opacity: 0.3,
fill: 'tonexty',
name: 'QCI ' + qci,
name: 'IpThp.' + qci,
legendgroup: 'legendgroup' + qci,
showlegend: false,
hovertemplate: 'Date: %{x}<br>Maximum Link Speed: %{y} Mbit/s'
});
}
});
return data_list;
}
......@@ -90,78 +71,99 @@
.declareService(function () {
var gadget = this;
return loopEventListener(window, 'resize', false, function () {
Plotly.Plots.resize(gadget.element.querySelector('.graph-upload-link'));
Plotly.Plots.resize(gadget.element.querySelector('.graph-download-link'));
Plotly.Plots.resize(gadget.element.querySelector('.graph-downlink'));
Plotly.Plots.resize(gadget.element.querySelector('.graph-uplink'));
});
})
.declareMethod('render', function (option_dict) {
var gadget = this,
i,
download_data,
upload_data,
label_list = ["evt", "qci", "dl_lo", "dl_hi", "ul_lo", "ul_hi"];
kpi_url,
kpi_data_dict;
return new RSVP.Queue().push(function () {
return wendelin.getArrayRawSlice(gadget, option_dict.data_array_key);
return gadget.getSetting("hateoas_url");
})
.push(function (hateoas_url) {
kpi_url =
(new URI(hateoas_url)).absoluteTo(location.href).toString() +
'DataAcquisitionUnit_getOrsEnbKpi?data_array_url=' +
option_dict.data_array_url +
'&kpi_type=' + option_dict.kpi_type;
return gadget.jio_getAttachment("erp5", kpi_url);
})
.push(function (result) {
var graph_data = nj.unpack(result.pick( null, label_list)),
.push(function (response) {
var active_qci = [],
date = [],
qci = [],
dl_lo = [],
dl_hi = [],
ul_lo = [],
ul_hi = [],
raw_qci,
raw_dl_lo,
raw_dl_hi,
raw_ul_lo,
raw_ul_hi,
download_link = gadget.element.querySelector('.graph-download-link'),
upload_link = gadget.element.querySelector('.graph-upload-link');
graph_data.sort(function (a, b) {
return (a[0] - b[0]);
});
for (i = 0; i < graph_data.length; i += 1) {
if (date.indexOf(graph_data[i][0]) == -1) {
date.push(graph_data[i][0]);
qci.push(raw_qci);
dl_lo.push(raw_dl_lo);
dl_hi.push(raw_dl_hi);
ul_lo.push(raw_ul_lo);
ul_hi.push(raw_ul_hi);
raw_qci = [graph_data[i][1]];
raw_dl_lo = [graph_data[i][2] / 1e6];
raw_dl_hi = [graph_data[i][3] / 1e6];
raw_ul_lo = [graph_data[i][4] / 1e6];
raw_ul_hi = [graph_data[i][5] / 1e6];
} else {
raw_qci.push(graph_data[i][1]);
raw_dl_lo.push(graph_data[i][2] / 1e6);
raw_dl_hi.push(graph_data[i][3] / 1e6);
raw_ul_lo.push(graph_data[i][4] / 1e6);
raw_ul_hi.push(graph_data[i][5] / 1e6);
}
downlink_element = gadget.element.querySelector('.graph-downlink'),
uplink_element = gadget.element.querySelector('.graph-uplink'),
downlink_data = [],
uplink_data = [];
kpi_data_dict = response;
if (Object.keys(kpi_data_dict).length === 0 ||
kpi_data_dict.active_qci.length === 0) {
Plotly.newPlot(
downlink_element,
[],
{
'title' : 'Downlink',
"annotations": [
{
"text": "No data found",
"xref": "paper",
"yref": "paper",
"showarrow": false,
"font": {
"size": 28
}
}
]
}
);
Plotly.newPlot(
uplink_element,
[],
{
'title' : 'Uplink',
"annotations": [
{
"text": "No data found",
"xref": "paper",
"yref": "paper",
"showarrow": false,
"font": {
"size": 28
}
}
]
}
);
gadget.element.querySelector('.ui-icon-spinner').hidden = true;
return;
}
qci.push(raw_qci);
dl_lo.push(raw_dl_lo);
dl_hi.push(raw_dl_hi);
ul_lo.push(raw_ul_lo);
ul_hi.push(raw_ul_hi);
qci.shift();
dl_lo.shift();
dl_hi.shift();
ul_lo.shift();
ul_hi.shift();
date.forEach(function (element, index) {
date[index] = new Date(element * 1000);
kpi_data_dict.evt.forEach(function (element) {
date.push(new Date(element * 1000));
});
download_data = getPlotData(dl_lo, dl_hi, qci, date);
upload_data = getPlotData(ul_lo, ul_hi, qci, date);
active_qci = kpi_data_dict.active_qci;
dl_lo = kpi_data_dict.dl_lo;
dl_hi = kpi_data_dict.dl_hi;
ul_lo = kpi_data_dict.ul_lo;
ul_hi = kpi_data_dict.ul_hi;
downlink_data = getPlotData(active_qci, date, dl_lo, dl_hi);
uplink_data = getPlotData(active_qci, date, ul_lo, ul_hi);
Plotly.newPlot(
download_link,
download_data,
downlink_element,
downlink_data,
{
'title' : 'Download Link',
'title' : 'Downlink',
'xaxis': {
'autorange': true,
'autosize': true
......@@ -174,10 +176,10 @@
}
);
Plotly.newPlot(
upload_link,
upload_data,
{
'title' : 'Upload Link',
uplink_element,
uplink_data,
{
'title' : 'Uplink',
'xaxis': {
'autorange': true,
'autosize': true
......@@ -193,4 +195,4 @@
});
});
}(window, rJS, RSVP, Plotly, wendelin));
}(window, rJS, RSVP, Plotly));
......@@ -238,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1019.54468.57368.35892</string> </value>
<value> <string>1020.53730.29312.48571</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -258,7 +258,7 @@
</tuple>
<state>
<tuple>
<float>1728633034.89</float>
<float>1732203028.62</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -238,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1018.50736.46413.16776</string> </value>
<value> <string>1020.52201.24472.32051</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -258,7 +258,7 @@
</tuple>
<state>
<tuple>
<float>1724245506.16</float>
<float>1732111313.58</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -8,15 +8,15 @@
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>getOrsKpiValues</string> </value>
<value> <string>calcEnbKpi</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>OrsKpiUtils</string> </value>
<value> <string>WendelinTelecomOrsKpi</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_getOrsKpiValues</string> </value>
<value> <string>Base_calcEnbKpi</string> </value>
</item>
<item>
<key> <string>title</string> </key>
......
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>DataAcquisitionUnit_getERabDataArrayKey</string> </value>
<value> <string>DataAcquisitionUnit_getERabDataArrayUrl</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>DataAcquisitionUnit_getEUtranDataArrayKey</string> </value>
<value> <string>DataAcquisitionUnit_getEUtranDataArrayUrl</string> </value>
</item>
</dictionary>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>getOrsEnbKpi</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>WendelinTelecomOrsKpi</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>DataAcquisitionUnit_getOrsEnbKpi</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -211,7 +211,7 @@
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string>gadget_ors_e_rab_kpi.html</string> </value>
<value> <string>gadget_ors_e_rab_accessibility_kpi.html</string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
......@@ -253,7 +253,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [(\'data_array_key\', context.DataAcquisitionUnit_getERabDataArrayKey())]</string> </value>
<value> <string>python: [(\'data_array_url\', context.DataAcquisitionUnit_getERabDataArrayUrl()), (\'kpi_type\', \'e_rab_accessibility\')]</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -253,7 +253,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [(\'data_array_key\', context.DataAcquisitionUnit_getEUtranDataArrayKey())]</string> </value>
<value> <string>python: [(\'data_array_url\', context.DataAcquisitionUnit_getEUtranDataArrayUrl()), (\'kpi_type\', \'e_utran_ip_throughput\')]</string> </value>
</item>
</dictionary>
</pickle>
......
extension.erp5.OrsKpiUtils
\ No newline at end of file
extension.erp5.WendelinTelecomOrsKpi
\ No newline at end of file
......@@ -12,7 +12,7 @@ portal_callables/DataIngestionLine_writeOrsFluentdIngestionToDataStream
portal_callables/IngestionPolicy_parseOrsFluentdTag
portal_ingestion_policies/ors_enb_log_ingestion
web_page_module/ndarray_bundle.js
web_page_module/ors_e_rab_kpi_*
web_page_module/ors_e_rab_accessibility_kpi_*
web_page_module/ors_e_utran_ip_throughput_kpi_*
web_page_module/plotly_strict_v2.34.0.js
web_page_module/rjs_gadget_ors_kpi_css
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment