Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
X
xlte
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Paul Graydon
xlte
Commits
6fb3b11b
Commit
6fb3b11b
authored
Dec 18, 2024
by
Paul Graydon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
amari.kpi: Add support for DRB.UEActive measurement
parent
3a35162b
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
216 additions
and
69 deletions
+216
-69
amari/kpi.py
amari/kpi.py
+43
-4
amari/kpi_test.py
amari/kpi_test.py
+173
-65
No files found.
amari/kpi.py
View file @
6fb3b11b
...
...
@@ -205,9 +205,9 @@ def _read(logm):
# _handle_stats handles next stats xlog entry upon _read request.
@
func
(
LogMeasure
)
def
_handle_stats
(
logm
,
stats
:
xlog
.
Message
,
m_prev
:
kpi
.
Measurement
):
# build Measurement from stats' counters.
# build Measurement from stats'
statistical profiles and
counters.
#
# we take δ(stats_prev, stat) and process it mapping Amarisoft counters to
# we take δ(stats_prev, stat) and process it
,
mapping Amarisoft counters to
# 3GPP ones specified by kpi.Measurement. This approach has following limitations:
#
# - for most of the counters there is no direct mapping in between
...
...
@@ -260,6 +260,13 @@ def _handle_stats(logm, stats: xlog.Message, m_prev: kpi.Measurement):
# do init/fini correction if there was also third preceding stats message.
m
=
logm
.
_m
.
copy
()
# [stats_prev, stats)
# cell_statt_profile returns the StatT statistical profile value triplet in stats
def
cell_statt_profile
(
cell
,
statt_profile
):
return
tuple
(
_stats_cell_sp_el
(
stats
,
cell
,
statt_profile
+
'_'
+
el
)
for
el
in
[
'min'
,
'avg'
,
'max'
]
)
# δcc(counter) tells how specified global cumulative counter changed since last stats result.
def
δ
cc
(
counter
):
old
=
_stats_cc
(
stats_prev
,
counter
)
...
...
@@ -305,6 +312,30 @@ def _handle_stats(logm, stats: xlog.Message, m_prev: kpi.Measurement):
# compute δ for counters.
# any logic error in data will be reported via LogError.
try
:
cells
=
set
(
stats
[
'cells'
].
keys
())
# NOTE cells are taken only from stats, not from stat_prev
# DRB: number of active UEs
#
# Aggregate statistical profile for all cells.
# While summing the averages is correct, it is impossible to compute
# an accurate value of the aggregate minimum and maximum across all cells.
# Summing them is the best approximation that will produce
# a wider interval containing the correct values, i.e.:
# Σ(minimums) <= min(Σ) and max(Σ) <= Σ(maximums)
# [ min, avg, max]
Σ
ue_active_count
=
[
None
,
None
,
None
]
for
cell
in
cells
:
cell_ue_active_count
=
cell_statt_profile
(
cell
,
'ue_active_count'
)
for
i
,
el
in
enumerate
(
cell_ue_active_count
):
if
el
is
not
None
:
Σ
ue_active_count
[
i
]
=
Σ
ue_active_count
[
i
]
or
0
Σ
ue_active_count
[
i
]
+=
el
# any None here will map to kpi.NA
m
[
'DRB.UEActive'
]
=
kpi
.
StatT
(
*
Σ
ue_active_count
)
# RRC: connection establishment
#
# Aggregate statistics for all cells because in E-RAB Accessibility we need
...
...
@@ -315,7 +346,6 @@ def _handle_stats(logm, stats: xlog.Message, m_prev: kpi.Measurement):
# same whether we do aggregation here or in kpi.Calc.erab_accessibility().
#
# TODO rework to emit per-cell measurements when/if we need per-cell KPIs
cells
=
set
(
stats
[
'cells'
].
keys
())
# NOTE cells are taken only from stats, not from stat_prev
δΣ
cell_rrc_connection_request
=
0
# (if a cell disappears its counters stop to be accounted)
δΣ
cell_rrc_connection_setup_complete
=
0
for
cell
in
cells
:
...
...
@@ -368,6 +398,15 @@ def _stats_check(stats: xlog.Message):
raise
LogError
(
stats
.
timestamp
,
"stats: %s"
%
e
)
from
None
return
# _stats_cell_sp_el returns specified per-cell element of a statistical profile.
#
# stats is assumed to be already verified by _stats_check.
def
_stats_cell_sp_el
(
stats
:
xlog
.
Message
,
cell
:
str
,
stat_profile_el
:
str
):
_
=
stats
[
'cells'
].
get
(
cell
)
if
_
is
None
:
return
None
# cell is absent in this stats
return
_
.
get
(
stat_profile_el
,
None
)
# _stats_cc returns specified global cumulative counter from stats result.
#
# stats is assumed to be already verified by _stats_check.
...
...
amari/kpi_test.py
View file @
6fb3b11b
...
...
@@ -21,7 +21,7 @@
from
__future__
import
print_function
,
division
,
absolute_import
from
xlte.amari.kpi
import
LogMeasure
,
LogError
,
_trace
as
trace
from
xlte.kpi
import
Measurement
,
isNA
from
xlte.kpi
import
Measurement
,
isNA
,
StatT
from
golang
import
func
,
defer
,
b
import
io
,
json
,
re
...
...
@@ -75,7 +75,8 @@ class tLogMeasure:
def
_mok_init
(
t
):
t
.
_mok
=
Measurement
()
# init fields extracted by amari.kpi from stats to 0
# this will be default values to verify against
# this will be default values to verify against.
# all other fields remain NA.
for
field
in
(
'RRC.ConnEstabAtt.sum'
,
'RRC.ConnEstabSucc.sum'
,
...
...
@@ -143,7 +144,7 @@ def test_LogMeasure():
_
=
t
.
expect1
# empty stats after first attach
t
.
xlog
(
jstats
(
1
,
{})
)
t
.
xlog
(
jstats
(
1
,
{}
,
{}
)
)
_
(
'X.Tstart'
,
0.02
)
_
(
'X.δT'
,
1
-
0.02
)
t
.
expect_nodata
()
...
...
@@ -173,10 +174,10 @@ def test_LogMeasure():
τ
_xlog
=
1
# timestamp of last emitted xlog entry
τ
_logm
=
τ
_xlog
-
2
+
1
# timestamp of next measurement to be read from logm
counters_prev
=
{}
def
tstats
(
counters
):
def
tstats
(
stat_profiles
,
counters
):
nonlocal
τ
_xlog
,
τ
_logm
,
counters_prev
trace
(
'
\
n
>>> tstats τ_xlog: %s τ_logm: %s'
%
(
τ
_xlog
,
τ
_logm
))
t
.
xlog
(
jstats
(
τ
_xlog
+
1
,
counters
)
)
# xlog τ+1
t
.
xlog
(
jstats
(
τ
_xlog
+
1
,
stat_profiles
,
counters
)
)
# xlog τ+1
t
.
read
()
# read+assert M for τ-1
_
(
'X.Tstart'
,
τ
_logm
+
1
)
# start preparing next expected M at τ
_
(
'X.δT'
,
1
)
...
...
@@ -189,7 +190,7 @@ def test_LogMeasure():
counters
=
counters_prev
.
copy
()
for
k
,
δ
v
in
δ
counters
.
items
():
counters
[
k
]
=
counters
.
get
(
k
,
0
)
+
δ
v
tstats
(
counters
)
tstats
(
{},
counters
)
# tevent is the verb to verify handling of events.
# its logic is similar to tstats.
...
...
@@ -216,10 +217,8 @@ def test_LogMeasure():
trace
(
'
\
n
>>> tdrb_stats τ: %s τ_xlog: %s τ_logm: %s'
%
(
τ
,
τ
_xlog
,
τ
_logm
))
t
.
xlog
(
jdrb_stats
(
τ
,
qci_trx
)
)
# further empty stats
tstats
({})
tstats
({}
,
{}
)
_
(
'X.Tstart'
,
1
)
_
(
'X.δT'
,
1
)
_
(
'RRC.ConnEstabAtt.sum'
,
0
)
...
...
@@ -232,6 +231,32 @@ def test_LogMeasure():
_
(
'ERAB.EstabAddSuccNbr.sum'
,
0
)
# DRB.UEActive
tstats
(
{
'C1.ue_active_count_min'
:
0
,
'C1.ue_active_count_avg'
:
2.73
,
'C1.ue_active_count_max'
:
3
},
{}
)
_
(
'DRB.UEActive'
,
StatT
(
0
,
2.73
,
3
))
# p2
tstats
(
{
'C1.ue_active_count_min'
:
1
,
'C1.ue_active_count_avg'
:
6.0
,
'C1.ue_active_count_max'
:
8
},
{}
)
_
(
'DRB.UEActive'
,
StatT
(
1
,
6.0
,
8
))
# p3
tstats
(
{
'C1.ue_active_count_min'
:
3
,
'C1.ue_active_count_avg'
:
4.49
,
'C1.ue_active_count_max'
:
7
},
{}
)
_
(
'DRB.UEActive'
,
StatT
(
3
,
4.49
,
7
))
# RRC.ConnEstab
#
# For init/fini correction LogMeasure accounts termination events in the
...
...
@@ -245,42 +270,59 @@ def test_LogMeasure():
# init 0 3 2 5 0
# fini ø ←─── 2 1←─── 2←─── 4←─── 1
# fini' 0 3 ² 2 ² 3 ¹ 0
tstats
({
'C1.rrc_connection_request'
:
0
,
'C1.rrc_connection_setup_complete'
:
2
})
# completions for previous uncovered period
tstats
(
{},
{
'C1.rrc_connection_request'
:
0
,
'C1.rrc_connection_setup_complete'
:
2
}
# completions for previous uncovered period
)
_
(
'RRC.ConnEstabAtt.sum'
,
0
)
_
(
'RRC.ConnEstabSucc.sum'
,
0
)
# not 2
# p2
tstats
({
'C1.rrc_connection_request'
:
0
+
3
,
# 3 new initiations
'C1.rrc_connection_setup_complete'
:
2
+
1
})
# 1 new completion
tstats
(
{},
{
'C1.rrc_connection_request'
:
0
+
3
,
# 3 new initiations
'C1.rrc_connection_setup_complete'
:
2
+
1
}
# 1 new completion
)
_
(
'RRC.ConnEstabAtt.sum'
,
3
)
_
(
'RRC.ConnEstabSucc.sum'
,
3
)
# not 1
# p3
tstats
({
'C1.rrc_connection_request'
:
0
+
3
+
2
,
# 2 new initiations
'C1.rrc_connection_setup_complete'
:
2
+
1
+
2
})
# 2 completions for p2
tstats
(
{},
{
'C1.rrc_connection_request'
:
0
+
3
+
2
,
# 2 new initiations
'C1.rrc_connection_setup_complete'
:
2
+
1
+
2
}
# 2 completions for p2
)
_
(
'RRC.ConnEstabAtt.sum'
,
2
)
_
(
'RRC.ConnEstabSucc.sum'
,
2
)
# 2, but it is 2 - 2(for_p2) + 2(from_p4)
# p4
tstats
({
'C1.rrc_connection_request'
:
0
+
3
+
2
+
5
,
# 5 new initiations
'C1.rrc_connection_setup_complete'
:
2
+
1
+
2
+
4
})
# 2 completions for p3 + 2 new
tstats
(
{},
{
'C1.rrc_connection_request'
:
0
+
3
+
2
+
5
,
# 5 new initiations
'C1.rrc_connection_setup_complete'
:
2
+
1
+
2
+
4
}
# 2 completions for p3 + 2 new
)
_
(
'RRC.ConnEstabAtt.sum'
,
5
)
_
(
'RRC.ConnEstabSucc.sum'
,
3
)
# p5
tstats
({
'C1.rrc_connection_request'
:
0
+
3
+
2
+
5
+
0
,
# no new initiations
'C1.rrc_connection_setup_complete'
:
2
+
1
+
2
+
4
+
1
})
# 1 completion for p4
tstats
(
{},
{
'C1.rrc_connection_request'
:
0
+
3
+
2
+
5
+
0
,
# no new initiations
'C1.rrc_connection_setup_complete'
:
2
+
1
+
2
+
4
+
1
}
# 1 completion for p4
)
_
(
'RRC.ConnEstabAtt.sum'
,
0
)
_
(
'RRC.ConnEstabSucc.sum'
,
0
)
# S1SIG.ConnEstab, ERAB.InitEstab
t
δ
stats
({
's1_initial_context_setup_request'
:
+
3
,
's1_initial_context_setup_response'
:
+
2
})
's1_initial_context_setup_response'
:
+
2
}
)
_
(
'S1SIG.ConnEstabAtt'
,
3
)
_
(
'S1SIG.ConnEstabSucc'
,
3
)
# 2 + 1(from_next)
_
(
'ERAB.EstabInitAttNbr.sum'
,
3
)
# currently same as S1SIG.ConnEstab
_
(
'ERAB.EstabInitSuccNbr.sum'
,
3
)
# ----//----
t
δ
stats
({
's1_initial_context_setup_request'
:
+
4
,
's1_initial_context_setup_response'
:
+
3
})
's1_initial_context_setup_response'
:
+
3
}
)
_
(
'S1SIG.ConnEstabAtt'
,
4
)
_
(
'S1SIG.ConnEstabSucc'
,
2
)
# 3 - 1(to_prev)
_
(
'ERAB.EstabInitAttNbr.sum'
,
4
)
# currently same as S1SIG.ConnEstab
...
...
@@ -407,10 +449,10 @@ def test_LogMeasure():
tevent
(
"service attach"
)
t
.
expect_nodata
()
t
.
xlog
(
jstats
(
τ
_xlog
+
1
,
{
i
:
1000
,
f
:
1000
})
)
# LogMeasure restarts the queue after data starts to
t
.
xlog
(
jstats
(
τ
_xlog
+
1
,
{
},
{
i
:
1000
,
f
:
1000
})
)
# LogMeasure restarts the queue after data starts to
τ
_xlog
+=
1
# come in again. Do one t.xlog step manually to
# increase t.read - t.xlog distance back to 2.
tstats
({
i
:
1000
+
2
,
f
:
1000
+
2
})
tstats
({
},
{
i
:
1000
+
2
,
f
:
1000
+
2
})
_
(
I
,
2
)
# no "extra" events even if counters start with jumped values after reattach
_
(
F
,
2
)
# and no fini correction going back through detach
...
...
@@ -424,31 +466,69 @@ def test_LogMeasure():
# multiple cells
# TODO emit per-cell measurements instead of accumulating all cells
tstats
({})
tstats
({}
,
{}
)
t
.
expect_nodata
()
tstats
({})
tstats
({}
,
{}
)
_
(
'RRC.ConnEstabAtt.sum'
,
0
)
_
(
'RRC.ConnEstabSucc.sum'
,
0
)
# C1 appears
tstats
({
'C1.rrc_connection_request'
:
12
,
'C1.rrc_connection_setup_complete'
:
11
})
tstats
(
{
'C1.ue_active_count_min'
:
2
,
'C1.ue_active_count_avg'
:
3.59
,
'C1.ue_active_count_max'
:
4
},
{
'C1.rrc_connection_request'
:
12
,
'C1.rrc_connection_setup_complete'
:
11
}
)
_
(
'RRC.ConnEstabAtt.sum'
,
12
)
_
(
'RRC.ConnEstabSucc.sum'
,
11
+
1
)
_
(
'DRB.UEActive'
,
StatT
(
2
,
3.59
,
4
))
# C2 appears
tstats
({
'C1.rrc_connection_request'
:
12
+
3
,
'C1.rrc_connection_setup_complete'
:
11
+
3
,
'C2.rrc_connection_request'
:
22
,
'C2.rrc_connection_setup_complete'
:
21
})
tstats
(
{
'C1.ue_active_count_min'
:
1
,
'C1.ue_active_count_avg'
:
2.87
,
'C1.ue_active_count_max'
:
5
,
'C2.ue_active_count_min'
:
1
,
'C2.ue_active_count_avg'
:
1.43
,
'C2.ue_active_count_max'
:
3
},
{
'C1.rrc_connection_request'
:
12
+
3
,
'C1.rrc_connection_setup_complete'
:
11
+
3
,
'C2.rrc_connection_request'
:
22
,
'C2.rrc_connection_setup_complete'
:
21
}
)
_
(
'RRC.ConnEstabAtt.sum'
,
3
+
22
)
_
(
'RRC.ConnEstabSucc.sum'
,
-
1
+
3
+
21
+
2
)
_
(
'DRB.UEActive'
,
StatT
(
1
+
1
,
2.87
+
1.43
,
5
+
3
))
# C1 and C2 stays
tstats
({
'C1.rrc_connection_request'
:
12
+
3
+
3
,
'C1.rrc_connection_setup_complete'
:
11
+
3
+
3
,
'C2.rrc_connection_request'
:
22
+
4
,
'C2.rrc_connection_setup_complete'
:
21
+
4
})
tstats
(
{
'C1.ue_active_count_min'
:
3
,
'C1.ue_active_count_avg'
:
3.10
,
'C1.ue_active_count_max'
:
5
,
'C2.ue_active_count_min'
:
0
,
'C2.ue_active_count_avg'
:
0.62
,
'C2.ue_active_count_max'
:
1
},
{
'C1.rrc_connection_request'
:
12
+
3
+
3
,
'C1.rrc_connection_setup_complete'
:
11
+
3
+
3
,
'C2.rrc_connection_request'
:
22
+
4
,
'C2.rrc_connection_setup_complete'
:
21
+
4
}
)
_
(
'RRC.ConnEstabAtt.sum'
,
3
+
4
)
_
(
'RRC.ConnEstabSucc.sum'
,
-
2
+
3
+
4
+
2
)
_
(
'DRB.UEActive'
,
StatT
(
3
+
0
,
3.10
+
0.62
,
5
+
1
))
# C1 disappears
tstats
({
'C2.rrc_connection_request'
:
22
+
4
+
4
,
'C2.rrc_connection_setup_complete'
:
21
+
4
+
4
})
tstats
(
{
'C2.ue_active_count_min'
:
0
,
'C2.ue_active_count_avg'
:
1.19
,
'C2.ue_active_count_max'
:
3
},
{
'C2.rrc_connection_request'
:
22
+
4
+
4
,
'C2.rrc_connection_setup_complete'
:
21
+
4
+
4
}
)
_
(
'RRC.ConnEstabAtt.sum'
,
4
)
_
(
'RRC.ConnEstabSucc.sum'
,
4
-
2
)
_
(
'DRB.UEActive'
,
StatT
(
0
,
1.19
,
3
))
# C2 disappears
tstats
({})
tstats
({}
,
{}
)
_
(
'RRC.ConnEstabAtt.sum'
,
0
)
_
(
'RRC.ConnEstabSucc.sum'
,
0
)
...
...
@@ -467,28 +547,28 @@ def test_LogMeasure_badinput():
CC
=
'RRC.ConnEstabAtt.sum'
# initial ok entries
t
.
xlog
(
jstats
(
1
,
{})
)
t
.
xlog
(
jstats
(
2
,
{
cc
:
2
})
)
t
.
xlog
(
jstats
(
3
,
{
cc
:
2
+
3
})
)
t
.
xlog
(
jstats
(
1
,
{}
,
{}
)
)
t
.
xlog
(
jstats
(
2
,
{
},
{
cc
:
2
})
)
t
.
xlog
(
jstats
(
3
,
{
},
{
cc
:
2
+
3
})
)
# bad: no counters
t
.
xlog
(
'{"message":"stats", "utc":21, "counters": {"messages": {}}, "cells": {"1": {}}}'
)
t
.
xlog
(
'{"message":"stats", "utc":22, "counters": {"messages": {}}, "cells": {"1": {"counters": {}}}}'
)
t
.
xlog
(
'{"message":"stats", "utc":23, "cells": {"1": {"counters": {"messages": {}}}}}'
)
t
.
xlog
(
'{"message":"stats", "utc":24, "counters": {}, "cells": {"1": {"counters": {"messages": {}}}}}'
)
# follow-up ok entries
t
.
xlog
(
jstats
(
31
,
{
cc
:
30
+
4
})
)
t
.
xlog
(
jstats
(
32
,
{
cc
:
30
+
4
+
5
})
)
t
.
xlog
(
jstats
(
31
,
{
},
{
cc
:
30
+
4
})
)
t
.
xlog
(
jstats
(
32
,
{
},
{
cc
:
30
+
4
+
5
})
)
# badline 1
t
.
xlog
(
"zzzqqqrrr"
)
# more ok entries
t
.
xlog
(
jstats
(
41
,
{
cc
:
40
+
6
})
)
t
.
xlog
(
jstats
(
42
,
{
cc
:
40
+
6
+
7
})
)
t
.
xlog
(
jstats
(
41
,
{
},
{
cc
:
40
+
6
})
)
t
.
xlog
(
jstats
(
42
,
{
},
{
cc
:
40
+
6
+
7
})
)
# badline 2 + followup event
t
.
xlog
(
"hello world"
)
t
.
xlog
(
'{"meta": {"event": "service attach", "time": 50}}'
)
# more ok entries
t
.
xlog
(
jstats
(
51
,
{
cc
:
50
+
8
})
)
t
.
xlog
(
jstats
(
52
,
{
cc
:
50
+
8
+
9
})
)
t
.
xlog
(
jstats
(
51
,
{
},
{
cc
:
50
+
8
})
)
t
.
xlog
(
jstats
(
52
,
{
},
{
cc
:
50
+
8
+
9
})
)
def
readok
(
τ
,
CC_value
):
_
(
'X.Tstart'
,
τ
)
...
...
@@ -540,11 +620,11 @@ def test_LogMeasure_cc_wraparound():
cc
=
'C1.rrc_connection_request'
CC
=
'RRC.ConnEstabAtt.sum'
t
.
xlog
(
jstats
(
1
,
{})
)
t
.
xlog
(
jstats
(
2
,
{
cc
:
13
})
)
t
.
xlog
(
jstats
(
3
,
{
cc
:
12
})
)
# cc↓ - should be reported
t
.
xlog
(
jstats
(
4
,
{
cc
:
140
})
)
# cc↑↑ - should start afresh
t
.
xlog
(
jstats
(
5
,
{
cc
:
150
})
)
t
.
xlog
(
jstats
(
1
,
{}
,
{}
)
)
t
.
xlog
(
jstats
(
2
,
{
},
{
cc
:
13
})
)
t
.
xlog
(
jstats
(
3
,
{
},
{
cc
:
12
})
)
# cc↓ - should be reported
t
.
xlog
(
jstats
(
4
,
{
},
{
cc
:
140
})
)
# cc↑↑ - should start afresh
t
.
xlog
(
jstats
(
5
,
{
},
{
cc
:
150
})
)
def
readok
(
τ
,
CC_value
):
_
(
'X.Tstart'
,
τ
)
...
...
@@ -574,10 +654,10 @@ def test_LogMeasure_sync():
cc = 'C1.rrc_connection_request'
CC = 'RRC.ConnEstabAtt.sum'
t.xlog( jstats(1, {}) )
t.xlog( jstats(2, {cc: 4}) )
t.xlog( jstats(1, {}
, {}
) )
t.xlog( jstats(2, {
}, {
cc: 4}) )
t.xlog( '{"
meta
": {"
event
": "
sync
", "
time
": 2.5, "
state
": "
attached
", "
reason
": "
periodic
", "
generator
": "
xlog
ws
:
//
localhost
:
9001
stats
[]
/
30.0
s
"}}' )
t.xlog( jstats(3, {cc: 7}) )
t.xlog( jstats(3, {
}, {
cc: 7}) )
def readok(τ, CC_value):
_('X.Tstart', τ)
...
...
@@ -593,14 +673,29 @@ def test_LogMeasure_sync():
readok(2, 3) # 2-3 jumping over sync
# jstats returns json-encoded stats message corresponding to counters dict.
# jstats returns json-encoded stats message corresponding to
# the given statistical profile and counter dicts.
#
#
if a counter goes as "
Cxxx
.
yyy
" it is emitted as counter
yyy of cell xxx in the output.
#
a key formatted as "
Cxxx
.
yyy
" is emitted as measurement
yyy of cell xxx in the output.
# τ goes directly to stats['utc'] as is.
def jstats(τ, counters): # -> str
def jstats(τ, stat_profiles, counters): # -> str
g_sp = {} # global statistical profiles
g_cc = {} # global cumulative counters
cells = {} # .cells
for sp, value in stat_profiles.items():
_ = re.match(r"
^
C
([
^
.]
+
)
\
.(.
+
)
$
", sp)
if _ is not None:
cell = _.group(1)
sp = _.group(2)
# enforce correct cell structure
cells.setdefault(cell, {})
\
.setdefault("
counters
", {})
\
.setdefault("
messages
", {})
cells[cell][sp] = value
else:
g_sp[sp] = value
for cc, value in counters.items():
_ = re.match(r"
^
C
([
^
.]
+
)
\
.(.
+
)
$
", cc)
if _ is not None:
...
...
@@ -616,6 +711,7 @@ def jstats(τ, counters): # -> str
s = {
"
message
": "
stats
",
"
utc
": τ,
**g_sp,
"
cells
": cells,
"
counters
": {"
messages
": g_cc},
}
...
...
@@ -623,17 +719,29 @@ def jstats(τ, counters): # -> str
return json.dumps(s)
def test_jstats():
assert jstats(0, {}) == '{"
message
": "
stats
", "
utc
": 0, "
cells
": {}, "
counters
": {"
messages
": {}}}'
assert jstats(123.4, {"
C1
.
rrc_x
": 1, "
s1_y
": 2, "
C1
.
rrc_z
": 3, "
x2_zz
": 4}) ==
\
'{"
message
": "
stats
", "
utc
": 123.4, "
cells
": {"
1
": {"
counters
": {"
messages
": {"
rrc_x
": 1, "
rrc_z
": 3}}}}, "
counters
": {"
messages
": {"
s1_y
": 2, "
x2_zz
": 4}}}'
# multiple cells
assert jstats(432.1, {"
C1
.
rrc_x
": 11, "
C2
.
rrc_y
": 22, "
C3
.
xyz
": 33, "
C1
.
abc
": 111, "
xyz
": 44}) ==
\
'{"
message
": "
stats
", "
utc
": 432.1, "
cells
": {' +
\
'"
1
": {"
counters
": {"
messages
": {"
rrc_x
": 11, "
abc
": 111}}}, ' +
\
'"
2
": {"
counters
": {"
messages
": {"
rrc_y
": 22}}}, ' +
\
'"
3
": {"
counters
": {"
messages
": {"
xyz
": 33}}}}, ' +
\
'"
counters
": {"
messages
": {"
xyz
": 44}}}'
assert jstats(0, {}, {}) == '{"
message
": "
stats
", "
utc
": 0, "
cells
": {}, "
counters
": {"
messages
": {}}}'
# only statistical profiles
assert jstats(1.2, {"
C1
.
ue_x_min
": 1, "
r1_y
": 2, "
C1
.
ue_x_avg
": 3, "
s2_z
": 4, "
C1
.
ue_x_max
": 5}, {}) ==
\
'{"
message
": "
stats
", "
utc
": 1.2, "
r1_y
": 2, "
s2_z
": 4, ' +
\
'"
cells
": {"
1
": {"
counters
": {"
messages
": {}}, "
ue_x_min
": 1, "
ue_x_avg
": 3, "
ue_x_max
": 5}}, ' +
\
'"
counters
": {"
messages
": {}}}'
# only counters
assert jstats(12.34, {}, {"
C1
.
rrc_x
": 1, "
s1_y
": 2, "
C1
.
rrc_z
": 3, "
x2_zz
": 4}) ==
\
'{"
message
": "
stats
", "
utc
": 12.34, "
cells
": {"
1
": {"
counters
": {"
messages
": {"
rrc_x
": 1, "
rrc_z
": 3}}}}, ' +
\
'"
counters
": {"
messages
": {"
s1_y
": 2, "
x2_zz
": 4}}}'
# multiple cells with both statistical profiles and counters
assert jstats(
432.1,
{"
C1
.
ue_w
": 11, "
C2
.
ue_ww
": 22, "
C3
.
ue_x
": 33, "
C1
.
ue_xx
": 44, "
rst
": 55},
{"
C1
.
rrc_x
": 11, "
C2
.
rrc_y
": 22, "
C3
.
xyz
": 33, "
C1
.
abc
": 44, "
xyz
": 55}
) == '{"
message
": "
stats
", "
utc
": 432.1, "
rst
": 55, "
cells
": {' +
\
'"
1
": {"
counters
": {"
messages
": {"
rrc_x
": 11, "
abc
": 44}}, "
ue_w
": 11, "
ue_xx
": 44}, ' +
\
'"
2
": {"
counters
": {"
messages
": {"
rrc_y
": 22}}, "
ue_ww
": 22}, ' +
\
'"
3
": {"
counters
": {"
messages
": {"
xyz
": 33}}, "
ue_x
": 33}}, ' +
\
'"
counters
": {"
messages
": {"
xyz
": 55}}}'
# jdrb_stats, similarly to jstats, returns json-encoded x.drb_stats message
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment