Commit 1b9d4e1a authored by Kirill Smelkov's avatar Kirill Smelkov

amari.kpi: Fix LogMeasure to handle multiple x.drb_stats messages in one period

When computing Measurements amari.kpi derives period from stats messages
- for example 60s if `xamari xlog` was running with stats/60s. But
inside that period there could be multiple x.drb_stats messages - for
example if xlog was running with `stats/60s x.drb_stats/10s`.
However the code was handling only the last such x.drb_stats instead of
accumulating counters from there during the period.

As the result, if. e.g. during the period there were 90B/9s and 2B/0.2s
x.drb_stats events, only the latter was accounted giving wrong data for
transmitted amount and transmission time.

-> Fix it.
parent 51c01b14
...@@ -480,9 +480,9 @@ def _drb_update(m: kpi.Measurement, drb_stats: xlog.Message): ...@@ -480,9 +480,9 @@ def _drb_update(m: kpi.Measurement, drb_stats: xlog.Message):
ΣT_hi = ΣT + ΣT_err ΣT_hi = ΣT + ΣT_err
ΣTT_lo = ΣTT - ΣTT_err ΣTT_lo = ΣTT - ΣTT_err
qvol[qci] = 8*ΣB # in bits qvol[qci] += 8*ΣB # in bits
qtime[qci] = (ΣT_hi + ΣTT_lo) / 2 qtime[qci] += (ΣT_hi + ΣTT_lo) / 2
qtime_err[qci] = (ΣT_hi - ΣTT_lo) / 2 qtime_err[qci] += (ΣT_hi - ΣTT_lo) / 2
# LogError(timestamp|None, *argv). # LogError(timestamp|None, *argv).
......
...@@ -104,12 +104,13 @@ class tLogMeasure: ...@@ -104,12 +104,13 @@ class tLogMeasure:
# also automatically initialize XXX.DRB.IPTimeX_err to 0.01 upon seeing DRB.IPTimeX # also automatically initialize XXX.DRB.IPTimeX_err to 0.01 upon seeing DRB.IPTimeX
# ( in tests we use precise values for tx_time and tx_time_notailtti # ( in tests we use precise values for tx_time and tx_time_notailtti
# with δ=0.02 - see drb_trx and jdrb_stats) # with δ=0.02 - see drb_trx and jdrb_stats)
# this pre-initialization is correct only if there was single x.drb_stats message.
n = _.group(1) n = _.group(1)
if n.startswith('DRB.IPTime'): if n.startswith('DRB.IPTime'):
ferr = "XXX.%s_err" % n ferr = "XXX.%s_err" % n
if isNA(t._mok[ferr+'.QCI']).all(): if isNA(t._mok[ferr+'.QCI']).all():
t._mok[ferr+'.QCI'][:] = 0 t._mok[ferr+'.QCI'][:] = 0
t._mok["%s.%s" % (ferr, _.group(2))] = ((vok + 0.01) - (vok - 0.01)) / 2 # ≈ 0.01 t._mok["%s.%s" % (ferr, _.group(2))] = drb_terr(vok)
t._mok[field] = vok t._mok[field] = vok
...@@ -361,6 +362,15 @@ def test_LogMeasure(): ...@@ -361,6 +362,15 @@ def test_LogMeasure():
_('ERAB.EstabInitAttNbr.sum', 3) # currently same as S1SIG.ConnEstab _('ERAB.EstabInitAttNbr.sum', 3) # currently same as S1SIG.ConnEstab
_('ERAB.EstabInitSuccNbr.sum', 2) # ----//---- _('ERAB.EstabInitSuccNbr.sum', 2) # ----//----
tdrb_stats(+0.5, {9: drb_trx(9.1,91, 9.2,92)}) # multiple d are accumulated
tdrb_stats(+0.6, {9: drb_trx(0.2, 2, 1.2,12)}) # ─d·S──ddd─S──
tdrb_stats(+0.7, {9: drb_trx(0.3, 3, 1.3,13)}) # cont↑
tδstats({})
_('DRB.IPTimeDl.9', 9.1+0.2+0.3); _('DRB.IPVolDl.9', 8*(91+2+3))
_('DRB.IPTimeUl.9', 9.2+1.2+1.3); _('DRB.IPVolUl.9', 8*(92+12+13))
t._mok['XXX.DRB.IPTimeDl_err.9'] = drb_terr(9.1) + drb_terr(0.2) + drb_terr(0.3)
t._mok['XXX.DRB.IPTimeUl_err.9'] = drb_terr(9.2) + drb_terr(1.2) + drb_terr(1.3)
# service detach/attach, connect failure, xlog failure # service detach/attach, connect failure, xlog failure
tδstats({}) # untie from previous history tδstats({}) # untie from previous history
...@@ -672,6 +682,14 @@ def drb_trx(dl_tx_time, dl_tx_bytes, ul_tx_time, ul_tx_bytes): ...@@ -672,6 +682,14 @@ def drb_trx(dl_tx_time, dl_tx_bytes, ul_tx_time, ul_tx_bytes):
"ul_tx_bytes": ul_tx_bytes, "ul_tx_time": ul_tx_time + 0.01, "ul_tx_time_notailtti": ul_tx_time - 0.01} "ul_tx_bytes": ul_tx_bytes, "ul_tx_time": ul_tx_time + 0.01, "ul_tx_time_notailtti": ul_tx_time - 0.01}
# drb_terr returns what XXX.DRB.IPTimeX_err should be for given DRB.IPTimeX
# it is ≈ 0.01 but due to loss of float-point precision is a bit different for
# particular value of time.
# ( in tests we use precise values for tx_time and tx_time_notailtti
# with δ=0.02 - see drb_trx and jdrb_stats)
def drb_terr(t):
return ((t + 0.01) - (t - 0.01)) / 2 # ≈ 0.01
# ionone returns empty data source. # ionone returns empty data source.
def ionone(): def ionone():
return io.BytesIO(b'') return io.BytesIO(b'')
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