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.amari import kpi as amari_kpi
from xlte import kpi from xlte import kpi
from zLOG import LOG, WARNING
import io import io
import json
def load_measurements(alogm): def load_measurements(alogm):
mlog = kpi.MeasurementLog() mlog = kpi.MeasurementLog()
...@@ -9,13 +11,14 @@ def load_measurements(alogm): ...@@ -9,13 +11,14 @@ def load_measurements(alogm):
while measurement is not None: while measurement is not None:
try: try:
mlog.append(measurement) mlog.append(measurement)
except AssertionError: except AssertionError as e:
# Invalid measurement: simply skip it # Invalid measurement: simply skip it
pass LOG('WendelinTelecomOrsKpi.loadMeasurements', WARNING, "Skipped data during KPI calculation: %s" % e)
measurement = alogm.read() measurement = alogm.read()
except amari_kpi.LogError: except amari_kpi.LogError as e:
# Invalid measurement: simply skip it # Invalid measurement: simply skip it
pass LOG('WendelinTelecomOrsKpi.loadMeasurements', WARNING, "Skipped data during KPI calculation: %s" % e)
finally: finally:
alogm.close() alogm.close()
return mlog return mlog
...@@ -33,32 +36,115 @@ def calc_periods(mlog, tperiod): ...@@ -33,32 +36,115 @@ def calc_periods(mlog, tperiod):
# No data to read: exit # No data to read: exit
return return
def getOrsKpiValues(data): def calcEnbKpi(data, t_period):
fxlog = io.StringIO(data) fxlog = io.StringIO(data)
alogm = amari_kpi.LogMeasure(fxlog, open('/dev/null', 'r')) alogm = amari_kpi.LogMeasure(fxlog, open('/dev/null', 'r'))
mlog = load_measurements(alogm) mlog = load_measurements(alogm)
# E-RAB Accessibility KPI
vt = [] vt = []
v_initial_epsb_estab_sr = [] v_initial_epsb_estab_sr = []
v_added_epsb_estab_sr = [] v_added_epsb_estab_sr = []
tperiod = 60 # seconds for calc in calc_periods(mlog, t_period):
for calc in calc_periods(mlog, tperiod):
vt.append(calc.tau_lo) vt.append(calc.tau_lo)
erab_accessibility = calc.erab_accessibility() erab_accessibility = calc.erab_accessibility()
v_initial_epsb_estab_sr.append((erab_accessibility[0]['lo'], erab_accessibility[0]['hi'])) 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'])) v_added_epsb_estab_sr.append((erab_accessibility[1]['lo'], erab_accessibility[1]['hi']))
# E-UTRAN IP Throughput KPI
evt = [] evt = []
v_ip_throughput_qci = [] v_ip_throughput_qci = []
tperiod = 3 # seconds for calc in calc_periods(mlog, t_period):
for calc in calc_periods(mlog, tperiod):
evt.append(calc.tau_lo) evt.append(calc.tau_lo)
eutran_ip_throughput = calc.eutran_ip_throughput() eutran_ip_throughput = calc.eutran_ip_throughput()
period_qci_data = [] period_qci_data = []
for qci, qci_measurement in enumerate(eutran_ip_throughput): for qci_measurement in eutran_ip_throughput:
period_qci_data.append((qci, qci_measurement['dl']['lo'], qci_measurement['dl']['hi'], qci_measurement['ul']['lo'], qci_measurement['ul']['hi'])) 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) 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 @@ ...@@ -8,7 +8,13 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>default_reference</string> </key> <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>
<item> <item>
<key> <string>description</string> </key> <key> <string>description</string> </key>
...@@ -18,7 +24,7 @@ ...@@ -18,7 +24,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>extension.erp5.OrsKpiUtils</string> </value> <value> <string>extension.erp5.WendelinTelecomOrsKpi</string> </value>
</item> </item>
<item> <item>
<key> <string>sid</string> </key> <key> <string>sid</string> </key>
......
import json
import numpy as np 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"] in_data_stream = in_stream["Data Stream"]
start = progress_indicator.getIntOffsetIndex() progress_indicator = in_stream["Progress Indicator"]
total_size = in_data_stream.getSize() 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 return
chunk_size = 512 * 512 previous_log_data = ''.join(in_data_stream.readChunkList(start, offset_index))
end = min(start + chunk_size, total_size) new_log_data = ''.join(in_data_stream.readChunkList(offset_index, end))
log_data = ''.join(in_data_stream.readChunkList(start, end)) # Some lines contain several contiguous JSON objects
# Some lines may contain several contiguous JSON objects
# Normalize to one JSON object per line # Normalize to one JSON object per line
newlines_offset = log_data.count('}{') previous_log_data = previous_log_data.replace('}{', '}\n{')
log_data = log_data.replace('}{', '}\n{') new_log_data = new_log_data.replace('}{', '}\n{')
# Last chunk may contain an incomplete JSON object: leave it for next iteration previous_log_data_line_list = previous_log_data.splitlines()
log_data_lines = log_data.splitlines() new_log_data_line_list = new_log_data.splitlines()
if len(log_data_lines) < 2:
return # Sort data lines by UTC timestamp
log_data = '\n'.join(log_data_lines[:-1]) def get_log_line_timestamp(log_line):
try:
end = start + len(log_data) - newlines_offset + 1 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') log_data = log_data.decode('utf8')
e_rab_data_array = None e_rab_data_array = None
...@@ -37,7 +58,10 @@ for array in out_array: ...@@ -37,7 +58,10 @@ for array in out_array:
if array['variation'] == 'e_utran': if array['variation'] == 'e_utran':
e_utran_data_array = array['Data Array'] 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([ e_rab_dtype = np.dtype([
('vt', 'float'), ('vt', 'float'),
...@@ -46,10 +70,8 @@ e_rab_dtype = np.dtype([ ...@@ -46,10 +70,8 @@ e_rab_dtype = np.dtype([
('vAddedEPSBEstabSR_lo', 'float64'), ('vAddedEPSBEstabSR_lo', 'float64'),
('vAddedEPSBEstabSR_hi', 'float64'), ('vAddedEPSBEstabSR_hi', 'float64'),
]) ])
e_utran_dtype = np.dtype([ e_utran_dtype = np.dtype([
('evt', 'float'), ('evt', 'float'),
('qci', 'float'),
('dl_lo', 'float64'), ('dl_lo', 'float64'),
('dl_hi', 'float64'), ('dl_hi', 'float64'),
('ul_lo', 'float64'), ('ul_lo', 'float64'),
...@@ -61,33 +83,37 @@ if not e_rab_array: ...@@ -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 = e_rab_data_array.initArray(shape=(0,), dtype=e_rab_dtype)
e_rab_array_data = [] 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])) 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: 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_rab_array.append(e_rab_array_data)
e_utran_array = e_utran_data_array.getArray() e_utran_array = e_utran_data_array.getArray()
if not e_utran_array: if not e_utran_array:
e_utran_array = e_utran_data_array.initArray(shape=(0,), dtype=e_utran_dtype) e_utran_array = e_utran_data_array.initArray(shape=(0,), dtype=e_utran_dtype)
e_utran_array_data = [] e_utran_array_data = []
seen_qci_list = []
i = e_utran_array.shape[0] - 1 # Don't duplicate KPI data: same here
qci_idx = 1 evt_column = list(map(lambda x: x[0], e_utran_array[::QCI_COUNT]))
while (i >= 0) and (e_utran_array[i][qci_idx] not in seen_qci_list): first_new_row_evt = 0
seen_qci_list.append(e_utran_array[i][qci_idx]) while (first_new_row_evt < len(evt) and evt[first_new_row_evt] in evt_column):
i -= 1 first_new_row_evt += 1
for i in range(len(evt)): for i in range(first_new_row_evt, len(evt)):
for qci_data in v_ip_throughput_qci[i]: for qci_data in v_ip_throughput_qci[i]:
qci = qci_data[0] e_utran_array_data.append((evt[i], qci_data[0], qci_data[1], qci_data[2], qci_data[3]))
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]))
if e_utran_array_data: 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) e_utran_array.append(e_utran_array_data)
progress_indicator.setIntOffsetIndex(end) progress_indicator.setIntOffsetIndex(end)
......
...@@ -5,4 +5,4 @@ It will first unpack the MsgPack, then remove the first item of the tuple (times ...@@ -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". 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 @@ ...@@ -13,7 +13,7 @@
<script src="ndarray_bundle.js" type="text/javascript"></script> <script src="ndarray_bundle.js" type="text/javascript"></script>
<script src="wendelin.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="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"> <link href="gadget_ors_kpi.css" rel="stylesheet" type="text/css">
</head> </head>
<body> <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 */ /*global window, rJS, RSVP, console, Plotly */
/*jslint nomen: true, indent: 2 */ /*jslint indent: 2, maxlen: 80, nomen: true */
(function (window, rJS, RSVP, Plotly, wendelin, loopEventListener) { (function (window, rJS, RSVP, Plotly, loopEventListener) {
"use strict"; "use strict";
var gadget_klass = rJS(window); var gadget_klass = rJS(window);
...@@ -12,41 +12,101 @@ ...@@ -12,41 +12,101 @@
.declareService(function () { .declareService(function () {
var gadget = this; var gadget = this;
return loopEventListener(window, 'resize', false, function () { return loopEventListener(window, 'resize', false, function () {
Plotly.Plots.resize(gadget.element.querySelector('.graph-initial-success-rate')); Plotly.Plots.resize(
Plotly.Plots.resize(gadget.element.querySelector('.graph-added-success-rate')); gadget.element.querySelector('.graph-initial-success-rate')
);
Plotly.Plots.resize(
gadget.element.querySelector('.graph-added-success-rate')
);
}); });
}) })
.declareMethod('render', function (option_dict) { .declareMethod('render', function (option_dict) {
var gadget = this, var gadget = this,
i, kpi_url,
initial_success_data, kpi_data_dict;
added_success_data,
label_list = ["vt", "vInitialEPSBEstabSR_lo", "vInitialEPSBEstabSR_hi", "vAddedEPSBEstabSR_lo", "vAddedEPSBEstabSR_hi"];
return new RSVP.Queue().push(function () { return new RSVP.Queue().push(function () {
return wendelin.getArrayRawSlice(gadget, option_dict.data_array_key); return gadget.getSetting("hateoas_url");
}) })
.push(function (result) { .push(function (hateoas_url) {
var graph_data = nj.unpack(result.pick(null, label_list)), kpi_url =
date = [], (new URI(hateoas_url)).absoluteTo(location.href).toString() +
vInitialEPSBEstabSR_lo = [], 'DataAcquisitionUnit_getOrsEnbKpi?data_array_url=' +
vInitialEPSBEstabSR_hi = [], option_dict.data_array_url +
vAddedEPSBEstabSR_lo = [], '&kpi_type=' + option_dict.kpi_type;
vAddedEPSBEstabSR_hi = []; return gadget.jio_getAttachment("erp5", kpi_url);
graph_data.sort(function (a, b) { })
return (a[0] - b[0]); .push(function (response) {
}); var date = [],
for (i = 0; i < graph_data.length; i += 1) { v_initial_epsb_estab_sr_lo = [],
date.push(new Date(graph_data[i][0] * 1000)); v_initial_epsb_estab_sr_hi = [],
vInitialEPSBEstabSR_lo.push(graph_data[i][1]); v_added_epsb_estab_sr_lo = [],
vInitialEPSBEstabSR_hi.push(graph_data[i][2]); v_added_epsb_estab_sr_hi = [],
vAddedEPSBEstabSR_lo.push(graph_data[i][3]); initial_success_rate_element =
vAddedEPSBEstabSR_hi.push(graph_data[i][4]); 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, x: date,
mode: 'lines+markers', mode: 'lines+markers',
y: vInitialEPSBEstabSR_lo, y: v_initial_epsb_estab_sr_lo,
type: 'scatter', type: 'scatter',
line: {shape: 'hv'}, line: {shape: 'hv'},
hovertemplate: 'Date: %{x}<br>Rate: %{y}%', hovertemplate: 'Date: %{x}<br>Rate: %{y}%',
...@@ -56,7 +116,7 @@ ...@@ -56,7 +116,7 @@
x: date, x: date,
mode: 'lines+markers', mode: 'lines+markers',
fill: 'tonexty', fill: 'tonexty',
y: vInitialEPSBEstabSR_hi, y: v_initial_epsb_estab_sr_hi,
type: 'scatter', type: 'scatter',
line: { line: {
color: "#6cb9e5", color: "#6cb9e5",
...@@ -66,11 +126,11 @@ ...@@ -66,11 +126,11 @@
name: 'InitialEPSBEstabSR uncertainty' name: 'InitialEPSBEstabSR uncertainty'
} }
]; ];
added_success_data = [ added_success_rate_data = [
{ {
x: date, x: date,
mode: 'lines+markers', mode: 'lines+markers',
y: vAddedEPSBEstabSR_lo, y: v_added_epsb_estab_sr_lo,
type: 'scatter', type: 'scatter',
line: {shape: 'hv'}, line: {shape: 'hv'},
hovertemplate: 'Date: %{x}<br>Rate: %{y}%', hovertemplate: 'Date: %{x}<br>Rate: %{y}%',
...@@ -80,7 +140,7 @@ ...@@ -80,7 +140,7 @@
x: date, x: date,
mode: 'lines+markers', mode: 'lines+markers',
fill: 'tonexty', fill: 'tonexty',
y: vAddedEPSBEstabSR_hi, y: v_added_epsb_estab_sr_hi,
type: 'scatter', type: 'scatter',
line: { line: {
color: "#6cb9e5", color: "#6cb9e5",
...@@ -92,7 +152,7 @@ ...@@ -92,7 +152,7 @@
]; ];
Plotly.newPlot( Plotly.newPlot(
gadget.element.querySelector('.graph-initial-success-rate'), gadget.element.querySelector('.graph-initial-success-rate'),
initial_success_data, initial_success_rate_data,
{ {
'title' : 'Initial E-RAB establishment success rate', 'title' : 'Initial E-RAB establishment success rate',
'xaxis': { 'xaxis': {
...@@ -108,7 +168,7 @@ ...@@ -108,7 +168,7 @@
); );
Plotly.newPlot( Plotly.newPlot(
gadget.element.querySelector('.graph-added-success-rate'), gadget.element.querySelector('.graph-added-success-rate'),
added_success_data, added_success_rate_data,
{ {
'title' : 'Added E-RAB establishment success rate', 'title' : 'Added E-RAB establishment success rate',
'xaxis': { 'xaxis': {
...@@ -126,4 +186,4 @@ ...@@ -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 @@ ...@@ -17,8 +17,8 @@
<link href="gadget_ors_kpi.css" rel="stylesheet" type="text/css"> <link href="gadget_ors_kpi.css" rel="stylesheet" type="text/css">
</head> </head>
<body> <body>
<div class="graph-download-link"></div> <div class="graph-downlink"></div>
<div class="graph-upload-link"></div> <div class="graph-uplink"></div>
<div class="ui-icon-spinner ui-btn-icon-notext first-loader"></div> <div class="ui-icon-spinner ui-btn-icon-notext first-loader"></div>
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -240,7 +240,7 @@ ...@@ -240,7 +240,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1018.60805.51967.7987</string> </value> <value> <string>1020.52199.64592.21384</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1724849643.71</float> <float>1732111315.93</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
/*global window, rJS, RSVP, console, Plotly */ /*global window, rJS, RSVP, console, Plotly, location */
/*jslint nomen: true, indent: 2 */ /*jslint indent: 2, maxlen: 80, nomen: true */
(function (window, rJS, RSVP, Plotly, wendelin) { (function (window, rJS, RSVP, Plotly) {
"use strict"; "use strict";
var gadget_klass = rJS(window); var gadget_klass = rJS(window);
function getPlotData(raw_lo, raw_hi, raw_qci, raw_date) { function getPlotData(active_qci, date, lo, hi) {
var color, var color,
color_array = [ color_array = [
{'color': '#1f77b4'}, {'color': '#1f77b4'},
...@@ -19,47 +19,28 @@ ...@@ -19,47 +19,28 @@
{'color': '#bcbd22'}, {'color': '#bcbd22'},
{'color': '#17becf'} {'color': '#17becf'}
], ],
qci_list = [...new Set(...raw_qci)], data_list = [];
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]);
}
active_qci.forEach(function (qci, i) {
color = color_array[i % color_array.length]; 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({ data_list.push({
x: date, x: date,
marker: { marker: {
size: 4 size: 4
}, },
mode: 'lines+markers', mode: 'lines+markers',
y: lo, y: lo[i],
type: 'scatter', type: 'scatter',
line: color, line: color,
name: 'QCI ' + qci, name: 'IPThp.' + qci,
legendgroup: 'legendgroup' + qci, legendgroup: 'legendgroup' + qci,
hovertemplate: 'Date: %{x}<br>Minimum Link Speed: %{y} Mbit/s' hovertemplate: 'Date: %{x}<br>Minimum Link Speed: %{y} Mbit/s'
}); });
...@@ -69,17 +50,17 @@ ...@@ -69,17 +50,17 @@
size: 4 size: 4
}, },
mode: 'lines+markers', mode: 'lines+markers',
y: hi, y: hi[i],
type: 'scatter', type: 'scatter',
line: color, line: color,
opacity: 0.3, opacity: 0.3,
fill: 'tonexty', fill: 'tonexty',
name: 'QCI ' + qci, name: 'IpThp.' + qci,
legendgroup: 'legendgroup' + qci, legendgroup: 'legendgroup' + qci,
showlegend: false, showlegend: false,
hovertemplate: 'Date: %{x}<br>Maximum Link Speed: %{y} Mbit/s' hovertemplate: 'Date: %{x}<br>Maximum Link Speed: %{y} Mbit/s'
}); });
} });
return data_list; return data_list;
} }
...@@ -90,78 +71,99 @@ ...@@ -90,78 +71,99 @@
.declareService(function () { .declareService(function () {
var gadget = this; var gadget = this;
return loopEventListener(window, 'resize', false, function () { return loopEventListener(window, 'resize', false, function () {
Plotly.Plots.resize(gadget.element.querySelector('.graph-upload-link')); Plotly.Plots.resize(gadget.element.querySelector('.graph-downlink'));
Plotly.Plots.resize(gadget.element.querySelector('.graph-download-link')); Plotly.Plots.resize(gadget.element.querySelector('.graph-uplink'));
}); });
}) })
.declareMethod('render', function (option_dict) { .declareMethod('render', function (option_dict) {
var gadget = this, var gadget = this,
i, kpi_url,
download_data, kpi_data_dict;
upload_data,
label_list = ["evt", "qci", "dl_lo", "dl_hi", "ul_lo", "ul_hi"];
return new RSVP.Queue().push(function () { 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) { .push(function (response) {
var graph_data = nj.unpack(result.pick( null, label_list)), var active_qci = [],
date = [], date = [],
qci = [],
dl_lo = [], dl_lo = [],
dl_hi = [], dl_hi = [],
ul_lo = [], ul_lo = [],
ul_hi = [], ul_hi = [],
raw_qci, downlink_element = gadget.element.querySelector('.graph-downlink'),
raw_dl_lo, uplink_element = gadget.element.querySelector('.graph-uplink'),
raw_dl_hi, downlink_data = [],
raw_ul_lo, uplink_data = [];
raw_ul_hi,
download_link = gadget.element.querySelector('.graph-download-link'), kpi_data_dict = response;
upload_link = gadget.element.querySelector('.graph-upload-link');
graph_data.sort(function (a, b) { if (Object.keys(kpi_data_dict).length === 0 ||
return (a[0] - b[0]); kpi_data_dict.active_qci.length === 0) {
}); Plotly.newPlot(
for (i = 0; i < graph_data.length; i += 1) { downlink_element,
if (date.indexOf(graph_data[i][0]) == -1) { [],
date.push(graph_data[i][0]); {
qci.push(raw_qci); 'title' : 'Downlink',
dl_lo.push(raw_dl_lo); "annotations": [
dl_hi.push(raw_dl_hi); {
ul_lo.push(raw_ul_lo); "text": "No data found",
ul_hi.push(raw_ul_hi); "xref": "paper",
raw_qci = [graph_data[i][1]]; "yref": "paper",
raw_dl_lo = [graph_data[i][2] / 1e6]; "showarrow": false,
raw_dl_hi = [graph_data[i][3] / 1e6]; "font": {
raw_ul_lo = [graph_data[i][4] / 1e6]; "size": 28
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); Plotly.newPlot(
raw_ul_hi.push(graph_data[i][5] / 1e6); 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); kpi_data_dict.evt.forEach(function (element) {
dl_hi.push(raw_dl_hi); date.push(new Date(element * 1000));
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);
}); });
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( Plotly.newPlot(
download_link, downlink_element,
download_data, downlink_data,
{ {
'title' : 'Download Link', 'title' : 'Downlink',
'xaxis': { 'xaxis': {
'autorange': true, 'autorange': true,
'autosize': true 'autosize': true
...@@ -174,10 +176,10 @@ ...@@ -174,10 +176,10 @@
} }
); );
Plotly.newPlot( Plotly.newPlot(
upload_link, uplink_element,
upload_data, uplink_data,
{ {
'title' : 'Upload Link', 'title' : 'Uplink',
'xaxis': { 'xaxis': {
'autorange': true, 'autorange': true,
'autosize': true 'autosize': true
...@@ -193,4 +195,4 @@ ...@@ -193,4 +195,4 @@
}); });
}); });
}(window, rJS, RSVP, Plotly, wendelin)); }(window, rJS, RSVP, Plotly));
...@@ -238,7 +238,7 @@ ...@@ -238,7 +238,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1019.54468.57368.35892</string> </value> <value> <string>1020.53730.29312.48571</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -258,7 +258,7 @@ ...@@ -258,7 +258,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1728633034.89</float> <float>1732203028.62</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -238,7 +238,7 @@ ...@@ -238,7 +238,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1018.50736.46413.16776</string> </value> <value> <string>1020.52201.24472.32051</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -258,7 +258,7 @@ ...@@ -258,7 +258,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1724245506.16</float> <float>1732111313.58</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -8,15 +8,15 @@ ...@@ -8,15 +8,15 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_function</string> </key> <key> <string>_function</string> </key>
<value> <string>getOrsKpiValues</string> </value> <value> <string>calcEnbKpi</string> </value>
</item> </item>
<item> <item>
<key> <string>_module</string> </key> <key> <string>_module</string> </key>
<value> <string>OrsKpiUtils</string> </value> <value> <string>WendelinTelecomOrsKpi</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Base_getOrsKpiValues</string> </value> <value> <string>Base_calcEnbKpi</string> </value>
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>DataAcquisitionUnit_getERabDataArrayKey</string> </value> <value> <string>DataAcquisitionUnit_getERabDataArrayUrl</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>DataAcquisitionUnit_getEUtranDataArrayKey</string> </value> <value> <string>DataAcquisitionUnit_getEUtranDataArrayUrl</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </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 @@ ...@@ -211,7 +211,7 @@
</item> </item>
<item> <item>
<key> <string>gadget_url</string> </key> <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>
<item> <item>
<key> <string>hidden</string> </key> <key> <string>hidden</string> </key>
...@@ -253,7 +253,7 @@ ...@@ -253,7 +253,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <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> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -253,7 +253,7 @@ ...@@ -253,7 +253,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <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> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
extension.erp5.OrsKpiUtils extension.erp5.WendelinTelecomOrsKpi
\ No newline at end of file \ No newline at end of file
...@@ -12,7 +12,7 @@ portal_callables/DataIngestionLine_writeOrsFluentdIngestionToDataStream ...@@ -12,7 +12,7 @@ portal_callables/DataIngestionLine_writeOrsFluentdIngestionToDataStream
portal_callables/IngestionPolicy_parseOrsFluentdTag portal_callables/IngestionPolicy_parseOrsFluentdTag
portal_ingestion_policies/ors_enb_log_ingestion portal_ingestion_policies/ors_enb_log_ingestion
web_page_module/ndarray_bundle.js 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/ors_e_utran_ip_throughput_kpi_*
web_page_module/plotly_strict_v2.34.0.js web_page_module/plotly_strict_v2.34.0.js
web_page_module/rjs_gadget_ors_kpi_css 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