First version with ML prediction code 70/6470/1
authordeepanshuk <deepanshu.k@hcl.com>
Tue, 6 Jul 2021 06:35:30 +0000 (12:05 +0530)
committerdeepanshuk <deepanshu.k@hcl.com>
Tue, 6 Jul 2021 06:35:30 +0000 (12:05 +0530)
Signed-off-by: deepanshuk <deepanshu.k@hcl.com>
Change-Id: Ie7e34b1828adf421276b7924b3b29a63eb1d36a6

18 files changed:
Dockerfile
Dockerfile-Unit-Test
docs/overview.rst
docs/release-notes.rst
qp/cell.json.7z [new file with mode: 0644]
qp/database.py [new file with mode: 0644]
qp/dummy.csv [new file with mode: 0644]
qp/insert.py [new file with mode: 0644]
qp/main.py
qp/prediction.py [new file with mode: 0644]
qp/qptrain.py [new file with mode: 0644]
rmr-version.yaml
setup.py
tests/conftest.py
tests/fixtures/test_local.rt
tests/test_qp.py
tox.ini
xapp-descriptor/config.json

index 402fab7..e7214c5 100644 (file)
@@ -13,8 +13,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 # ==================================================================================
-FROM python:3.8-alpine
-
+FROM frolvlad/alpine-miniconda3
+#FROM python:3.7-alpine
 # RMR setup
 RUN mkdir -p /opt/route/
 # copy rmr files from builder image in lieu of an Alpine package
@@ -25,15 +25,16 @@ ENV LD_LIBRARY_PATH /usr/local/lib/:/usr/local/lib64
 COPY tests/fixtures/local.rt /opt/route/local.rt
 ENV RMR_SEED_RT /opt/route/local.rt
 
+
 # sdl needs gcc
 RUN apk update && apk add gcc musl-dev
 
 # Install
 COPY setup.py /tmp
 COPY LICENSE.txt /tmp/
-COPY qp/ /tmp/qp
+COPY qp/ /qp
 RUN pip install /tmp
 
 # Run
 ENV PYTHONUNBUFFERED 1
-CMD run-qp.py
+CMD PYTHONPATH=/qp:/usr/lib/python3.7/site-packages/:$PYTHONPATH run-qp.py
index 9f9f104..596ad36 100644 (file)
@@ -13,7 +13,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 # ==================================================================================
-FROM python:3.8-alpine
+FROM frolvlad/alpine-miniconda3
+#FROM python:3.8-alpine
 
 # sdl uses hiredis which needs gcc
 RUN apk update && apk add gcc musl-dev
@@ -24,6 +25,14 @@ COPY --from=nexus3.o-ran-sc.org:10002/o-ran-sc/bldr-alpine3-rmr:4.0.5 /usr/local
 # Upgrade pip, install tox
 RUN pip install --upgrade pip && pip install tox
 
+# Install dependencies
+RUN conda install joblib
+RUN conda update -n base -c defaults conda
+RUN conda install pandas
+RUN pip install schedule
+RUN pip install influxdb
+RUN conda install -c conda-forge statsmodels
+
 # copies
 COPY setup.py tox.ini LICENSE.txt /tmp/
 COPY qp/ /tmp/qp
@@ -32,4 +41,4 @@ RUN pip install /tmp
 
 # Run the unit tests
 WORKDIR /tmp
-RUN tox -e code,flake8
+RUN PYTHONPATH=/tmp/qp:/usr/lib/python3.7/site-packages/:$PYTHONPATH tox -e code,flake8
index 736fffe..bb89b06 100644 (file)
@@ -1,6 +1,7 @@
 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
 .. SPDX-License-Identifier: CC-BY-4.0
 .. Copyright (C) 2020 AT&T Intellectual Property
+.. Copyright (C) 2021 HCL Technologies Limited.
 
 QoE Predictor Overview
 ======================
@@ -8,89 +9,15 @@ QoE Predictor Overview
 QoE Predictor (QP) is an Xapp in the Traffic Steering O-RAN use case,
 which uses the following Xapps:
 
-#. Traffic Steering, which sends prediction requests to QP Driver.
-#. QP Driver, which fetches data from SDL on behalf of traffic steering,
-   both UE Data and Cell Data, merges that data together, then sends off 
-   the data to the QoE Predictor.
+#. Traffic Steering, which sends prediction requests to QP.
 #. QoE Predictor, which predicts and sends that prediction back to Traffic Steering
-#. KPIMONN, which populates SDL in the first place.
+#. KPIMONN, which populates UE and Cell metrics into the influxdb.
 
 Expected Input
 --------------
 
 The QP Xapp expects a prediction-request JSON message via RMR with the following structure::
-
-  {
-    "predictionUE": "UEId1",
-    "ueMeasurements" :
-      { "servingCellId" : "CID2",
-        "measTimestampUePrbUsage" : TS1,
-        "measPeriodUePrbUsage" : Int,
-        "uePrbUsageDL" : Int,
-        "uePrbUsageUL" : Int,
-        "measTimestampUePdcpBytes" : TS2,
-        "measPeriodUePdcpByes" : Int,
-        "uePdcpBytesDL": Int,
-        "uePdcpBytesUL" : Int
-      },
-    "cellMeasurements" : [
-      {
-        "cellId" : "CID2",
-        "measTimestampPrbAvailable" : TS,
-        "measPeriodPrbAvailable" : Int,
-        "prbAvailableDL" : Int,
-        "prbAvailableUL" : Int,
-        "measTimestampPdcpBytes" : TS,
-        "measPeriodPdcpBytes" : Int,
-        "pdcpBytesDL" : 30000000,
-        "pdcpBytesUL" : 5000000,
-        "measTimestampRf" : TS,
-        "measPeriodRf" : Int,
-        "rfMeasurements" : {
-          "rsrp": Int,
-          "rsrq": Int,
-          "rsSinr": Int
-       }
-     },
-     {
-       "cellId" : "CID1",
-       "measTimestampPrbAvailable" : TS,
-       "measPeriodPrbAvailable" : Int,
-       "prbAvailableDL" : Int,
-       "prbAvailableUL" : Int,
-       "measTimestampPdcpBytes" : TS,
-       "measPeriodPdcpBytes" : Int,
-       "pdcpBytesDL" : 10000000,
-       "pdcpBytesUL" : 2000000,
-       "measTimestampRf" : TS,
-       "measPeriodRf" : Int,
-       "rfMeasurements" : {
-         "rsrp": Int,
-         "rsrq": Int,
-         "rsSinr": Int
-       }
-     },
-     {
-       "cellId" : "CID3",
-       "measTimestampPrbAvailable" : TS,
-       "measPeriodPrbAvailable" : Int,
-       "prbAvailableDL" : Int,
-       "prbAvailableUL" : Int,
-       "measTimestampPdcpBytes" : TS,
-       "measPeriodPdcpBytes" : Int,
-       "pdcpBytesDL" : 50000000,
-       "pdcpBytesUL" : 4000000,
-       "measTimestampRf" : TS,
-       "measPeriodRf" : Int,
-       "rfMeasurements" : {
-         "rsrp": Int,
-         "rsrq": Int,
-         "rsSinr": Int
-       }
-     }
-    ]
-  }
-
+{"UEPredictionSet": ["Car-1"]}
 
 Expected Output
 ---------------
@@ -98,12 +25,10 @@ Expected Output
 The QP Xapp should send a prediction for both downlink and uplink throughput
 as a JSON message via RMR with the following structure::
 
-  {
-    "UEId1": {
-      "CID1" : [10000000,2000000],
-      "CID2" : [30000000,5000000],
-      "CID3" : [50000000,4000000]
-    }
-  }
-
-
+ {"Car-1":{
+ "c6/B2": [12650, 12721],
+ "c6/N77": [12663, 12739],
+ "c1/B13": [12576, 12655],
+ "c7/B13": [12649, 12697],
+ "c5/B13": [12592, 12688]
+ }}
index 8fa56ed..79fb288 100644 (file)
@@ -12,7 +12,7 @@ and this project adheres to `Semantic Versioning <http://semver.org/>`__.
 
 [0.0.3] - 2020-12-08
 --------------------
-* Process incoming message from QP Driver and generate a dynamic mock Prediction message with one cell having better xput
+* Removed QP Driver functionaity for QP, Incoming request from TS and Add actual protoype ML prediction code (`RICAPP-46 <https://jira.o-ran-sc.org/browse/RICAPP-46>`_)
 
 [0.0.2] - 2020-06-02
 --------------------
diff --git a/qp/cell.json.7z b/qp/cell.json.7z
new file mode 100644 (file)
index 0000000..bc98903
Binary files /dev/null and b/qp/cell.json.7z differ
diff --git a/qp/database.py b/qp/database.py
new file mode 100644 (file)
index 0000000..42083ee
--- /dev/null
@@ -0,0 +1,78 @@
+# ==================================================================================
+#       Copyright (c) 2020 AT&T Intellectual Property.
+#       Copyright (c) 2020 HCL Technologies Limited.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#          http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+# ==================================================================================
+from influxdb import DataFrameClient
+import pandas as pd
+import datetime
+
+
+class NoDataError(Exception):
+    pass
+
+
+class DATABASE(object):
+
+    def __init__(self, dbname, user='root', password='root', host='r4-influxdb.ricplt', port='8086'):
+        self.client = DataFrameClient(host, port, user, password, dbname)
+        self.data = None
+
+    def read_data(self, meas='ueMeasReport', limit=100000, cellid=False, ueid=False):
+        query = """select * from """ + meas
+
+        if cellid:
+            query += " where nrCellIdentity= '" + cellid + "'"
+
+        if ueid:
+            query += """ where "ue-id"  = \'{}\'""".format(ueid)
+        query += "  ORDER BY DESC LIMIT " + str(limit)
+        result = self.client.query(query)
+        try:
+            if len(result) != 0:
+                # print("Querying data : " + meas + " : size - " + str(len(result[meas])))
+                self.data = result[meas]
+                self.data['measTimeStampRf'] = self.data.index
+            else:
+                raise NoDataError
+
+        except NoDataError:
+            if cellid:
+                print('Data not found for ' + meas + ' CellID : '+cellid)
+            elif ueid:
+                print('Data not found for ' + meas + ' UEID : '+ueid)
+            else:
+                print('Data not found for ' + meas)
+            pass
+
+    def write_prediction(self, df, meas_name='QP'):
+        df.index = pd.date_range(start=datetime.datetime.now(), freq='10ms', periods=len(df))
+        self.client.write_points(df, meas_name)
+
+
+class DUMMY:
+
+    def __init__(self):
+        self.ue = pd.DataFrame([[1002, "c2/B13", 8, 69, 65, 113, 0.1, 0.1, "Waiting passenger 9", -882, -959, pd.to_datetime("2021-05-12T07:43:51.652")]], columns=["du-id", "nbCellIdentity", "prb_usage", "rsrp", "rsrq", "rssinr", "throughput", "targetTput", "ue-id", "x", "y", "measTimeStampRf"])
+        self.cell = pd.read_csv('qp/dummy.csv')
+        self.data = None
+
+    def read_data(self, meas='ueMeasReport', limit=100000, cellid=False, ueid=False):
+        if ueid:
+            self.data = self.ue.head(limit)
+        if cellid:
+            self.data = self.cell.head(limit)
+
+    def write_prediction(self, df, meas_name='QP'):
+        pass
diff --git a/qp/dummy.csv b/qp/dummy.csv
new file mode 100644 (file)
index 0000000..360ec06
--- /dev/null
@@ -0,0 +1,101 @@
+availPrbDl,availPrbUl,du-id,measPeriodPdcpBytes,measPeriodPrb,measTimeStampRf,nrCellIdentity,pdcpBytesDl,pdcpBytesUl,throughput,x,y
+0,0,1002,10.0,10,2021-06-25T11:42:33.685,c2/B13,4.566447392558995,4.566447392558995,0.44594212817958906,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.695,c2/B13,4.566447392558995,4.566447392558995,0.44594212817958906,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.705,c2/B13,9.132362040900631,9.132362040900631,0.445890102377112,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.715,c2/B13,13.697743446574867,13.697743446574867,0.445838027897874,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.725,c2/B13,18.262591113423376,18.262591113423376,0.44578590496567405,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.735,c2/B13,22.826904547575328,22.826904547575328,0.445733733803901,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.745,c2/B13,27.39068325744312,27.39068325744312,0.445681514635526,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.755,c2/B13,31.953926753718125,31.953926753718125,0.445629247683106,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.765,c2/B13,36.51663454936642,36.51663454936642,0.44557693316877905,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.775,c2/B13,41.07880615962452,41.07880615962452,0.44552457131426704,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.785,c2/B13,45.64044110199503,45.64044110199503,0.44547216234087006,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.795,c2/B13,50.20153889624241,50.20153889624241,0.44541970646947004,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.805,c2/B13,54.76209906438861,54.76209906438861,0.44536720392052703,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.815,c2/B13,59.32212113070874,59.32212113070874,0.445314654914075,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.825,c2/B13,63.88160462172675,63.88160462172675,0.44526205966972804,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.835,c2/B13,68.4405490662111,68.4405490662111,0.44520941840667405,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.845,c2/B13,72.99895399517035,72.99895399517035,0.44515673134367606,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.855,c2/B13,77.55681894184882,77.55681894184882,0.445103998699068,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.865,c2/B13,82.11414344172219,82.11414344172219,0.44505122069075903,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.875,c2/B13,86.67092703249317,86.67092703249317,0.44499839753622805,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.885,c2/B13,91.22716925408702,91.22716925408702,0.444945529452523,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.895,c2/B13,95.78286964864716,95.78286964864716,0.44489261665626406,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.905,c2/B13,100.33802776053082,100.33802776053082,0.444839659363637,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.915,c2/B13,104.89264313630449,104.89264313630449,0.44478665779039706,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.925,c2/B13,109.4467153247396,109.4467153247396,0.444733612151866,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.935,c2/B13,114.000243876808,114.000243876808,0.444680522662929,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.945,c2/B13,118.55322834567752,118.55322834567752,0.44462738953803804,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.955,c2/B13,123.10566828670753,123.10566828670753,0.444574212991211,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.965,c2/B13,127.65756325744442,127.65756325744442,0.444520993236025,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.975,c2/B13,132.20891281761718,132.20891281761718,0.44446773048562105,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.985,c2/B13,136.75971652913285,136.75971652913285,0.44441442495270206,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:33.995,c2/B13,141.30997395607204,141.30997395607204,0.444361076849531,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.005,c2/B13,145.85968466468447,145.85968466468447,0.44430768638793205,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.015,c2/B13,150.40884822338438,150.40884822338438,0.444254253779288,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.025,c2/B13,154.95746420274605,154.95746420274605,0.44420077923453705,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.035,c2/B13,159.50553217549927,159.50553217549927,0.44414726296418106,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.045,c2/B13,164.0530517165248,164.0530517165248,0.444093705178273,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.055,c2/B13,168.60002240284982,168.60002240284982,0.444040106086426,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.065,c2/B13,173.14644381364334,173.14644381364334,0.44398646589780505,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.075,c2/B13,177.69231553021177,177.69231553021177,0.443932784821134,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.085,c2/B13,182.2376371359942,182.2376371359942,0.44387906306468805,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.095,c2/B13,186.78240821655785,186.78240821655785,0.443825300836296,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.105,c2/B13,191.32662835959366,191.32662835959366,0.44377149834334,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.115,c2/B13,195.87029715491147,195.87029715491147,0.443717655792755,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.125,c2/B13,200.4134141944356,200.4134141944356,0.443663773391027,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.135,c2/B13,204.95597907220014,204.95597907220014,0.443609851344193,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.145,c2/B13,209.4979913843444,209.4979913843444,0.44355588985783906,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.155,c2/B13,214.03945072910835,214.03945072910835,0.44350188913710303,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.165,c2/B13,218.58035670682787,218.58035670682787,0.44344784938667103,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.175,c2/B13,223.12070891993022,223.12070891993022,0.443393770810777,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.185,c2/B13,227.66050697292945,227.66050697292945,0.44333965361320404,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.195,c2/B13,232.19975047242164,232.19975047242164,0.443285497997283,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.205,c2/B13,236.73843902708037,236.73843902708037,0.443231304165891,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.215,c2/B13,241.27657224765204,241.27657224765204,0.44317707232145104,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.225,c2/B13,245.81414974695122,245.81414974695122,0.443122802665933,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.235,c2/B13,250.35117113985598,250.35117113985598,0.443068495400854,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.245,c2/B13,254.88763604330327,254.88763604330327,0.443014150727273,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.255,c2/B13,259.42354407628426,259.42354407628426,0.442959768845796,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.265,c2/B13,263.9588948598396,263.9588948598396,0.44290534995657105,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.275,c2/B13,268.49368801705475,268.49368801705475,0.442850894259292,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.285,c2/B13,273.02792317305546,273.02792317305546,0.44279640195319503,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.295,c2/B13,277.5615999550029,277.5615999550029,0.44274187323705805,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.305,c2/B13,282.09471799208916,282.09471799208916,0.44268730830920405,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.315,c2/B13,286.6272769155323,286.6272769155323,0.44263270736749705,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.325,c2/B13,291.15927635857196,291.15927635857196,0.442578070609341,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.335,c2/B13,295.69071595646443,295.69071595646443,0.442523398231685,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.345,c2/B13,300.221595346478,300.221595346478,0.44246869043101406,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.355,c2/B13,304.7519141678884,304.7519141678884,0.44241394740335804,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.365,c2/B13,309.2816720619739,309.2816720619739,0.44235916934428704,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.375,c2/B13,313.8108686720107,313.8108686720107,0.44230435644890803,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.385,c2/B13,318.3395036432683,318.3395036432683,0.44224950891187104,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.395,c2/B13,322.8675766230045,322.8675766230045,0.44219462692736305,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.405,c2/B13,327.39508726046097,327.39508726046097,0.442139710689112,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.415,c2/B13,331.92203520685854,331.92203520685854,0.442084760390384,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.425,c2/B13,336.44842011539214,336.44842011539214,0.44202977622398404,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.435,c2/B13,340.97424164122646,340.97424164122646,0.44197475838225503,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.445,c2/B13,345.4994994414909,345.4994994414909,0.44191970705707806,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.455,c2/B13,350.0241931752752,350.0241931752752,0.441864622439871,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.465,c2/B13,354.5483225036243,354.5483225036243,0.441809504721594,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.475,c2/B13,359.07188708953396,359.07188708953396,0.44175435409273806,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.485,c2/B13,363.59488659794573,363.59488659794573,0.44169917074333703,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.495,c2/B13,368.1173206957424,368.1173206957424,0.44164395486295804,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.505,c2/B13,372.6391890517433,372.6391890517433,0.44158870664070804,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.515,c2/B13,377.16049133669924,377.16049133669924,0.44153342626523,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.525,c2/B13,381.6812272232882,381.6812272232882,0.44147811392470104,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.535,c2/B13,386.2013963861102,386.2013963861102,0.44142276980683803,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.545,c2/B13,390.72099850168286,390.72099850168286,0.44136739409889103,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.555,c2/B13,395.2400332484364,395.2400332484364,0.44131198698765106,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.565,c2/B13,399.7585003067091,399.7585003067091,0.44125654865944103,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.575,c2/B13,404.2763993587423,404.2763993587423,0.44120107930012004,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.585,c2/B13,408.793730088676,408.793730088676,0.44114557909508606,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.595,c2/B13,413.3104921825437,413.3104921825437,0.44109004822927006,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.605,c2/B13,417.826685328268,417.826685328268,0.44103448688714003,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.615,c2/B13,422.34230921565563,422.34230921565563,0.440978895252699,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.625,c2/B13,426.85736353639277,426.85736353639277,0.440923273509486,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.635,c2/B13,431.3718479840403,431.3718479840403,0.44086762184057604,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.645,c2/B13,435.885762254029,435.885762254029,0.44081194042858,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.655,c2/B13,440.39910604365474,440.39910604365474,0.440756229455643,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.665,c2/B13,444.911879052074,444.911879052074,0.44070048910344606,-832.0,-555.0
+0,0,1002,10.0,10,2021-06-25T11:42:34.675,c2/B13,449.42408098029887,449.42408098029887,0.44064471955320705,-832.0,-555.0
diff --git a/qp/insert.py b/qp/insert.py
new file mode 100644 (file)
index 0000000..ee87711
--- /dev/null
@@ -0,0 +1,79 @@
+# ==================================================================================
+#       Copyright (c) 2020 AT&T Intellectual Property.
+#       Copyright (c) 2020 HCL Technologies Limited.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#          http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+# ==================================================================================
+"""
+
+This module is temporary which aims to populate cell data into influxDB. This will be depreciated once KPIMON push cell info. into influxDB.
+
+"""
+import pandas as pd
+from influxdb import DataFrameClient
+import datetime
+
+
+class INSERTDATA:
+
+    def __init__(self):
+        host = 'r4-influxdb.ricplt'
+        self.client = DataFrameClient(host, '8086', 'root', 'root')
+        self.switchdb('UEData')
+        self.dropmeas('QP')
+
+    def switchdb(self, dbname):
+        print("Switch database: " + dbname)
+        self.client.switch_database(dbname)
+
+    def dropmeas(self, measname):
+        print("DROP MEASUREMENT: " + measname)
+        self.client.query('DROP MEASUREMENT '+measname)
+
+
+def explode(df):
+    for col in df.columns:
+        if isinstance(df.iloc[0][col], list):
+            df = df.explode(col)
+        d = df[col].apply(pd.Series)
+        df[d.columns] = d
+        df = df.drop(col, axis=1)
+    return df
+
+
+def jsonToTable(df):
+    df.index = range(len(df))
+    cols = [col for col in df.columns if isinstance(df.iloc[0][col], dict) or isinstance(df.iloc[0][col], list)]
+    if len(cols) == 0:
+        return df
+    for col in cols:
+        d = explode(pd.DataFrame(df[col], columns=[col]))
+        d = d.dropna(axis=1, how='all')
+        df = pd.concat([df, d], axis=1)
+        df = df.drop(col, axis=1).dropna()
+    return jsonToTable(df)
+
+
+def time(df):
+    df.index = pd.date_range(start=datetime.datetime.now(), freq='10ms', periods=len(df))
+    df['measTimeStampRf'] = df['measTimeStampRf'].apply(lambda x: str(x))
+    return df
+
+
+def populatedb():
+    df = pd.read_json('qp/cell.json.gz', lines=True)
+    df = df[['cellMeasReport']].dropna()
+    df = jsonToTable(df)
+    df = time(df)
+    db = INSERTDATA()
+    db.client.write_points(df, 'liveCell', batch_size=500, protocol='line')
index 188e686..002b063 100644 (file)
@@ -1,5 +1,6 @@
 # ==================================================================================
 #       Copyright (c) 2020 AT&T Intellectual Property.
+#       Copyright (c) 2020 HCL Technologies Limited.
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
 #   limitations under the License.
 # ==================================================================================
 """
-mock qp module
+qp module main -- using Time series ML predictor
 
 RMR Messages:
- #define TS_QOE_PRED_REQ 30001
+ #define TS_UE_LIST 30000
  #define TS_QOE_PREDICTION 30002
-30001 is the message type QP receives from the driver;
+30000 is the message type QP receives from the TS;
 sends out type 30002 which should be routed to TS.
 
 """
-
-import json
+import insert
 import os
+import json
 from mdclogpy import Logger
 from ricxappframe.xapp_frame import RMRXapp, rmr
+from prediction import forecast
+from qptrain import train
+from database import DATABASE, DUMMY
+import warnings
+warnings.filterwarnings("ignore")
 
 # pylint: disable=invalid-name
 qp_xapp = None
@@ -53,34 +59,60 @@ def qp_default_handler(self, summary, sbuf):
 
 def qp_predict_handler(self, summary, sbuf):
     """
-    Function that processes messages for type 30001
-    """
-    logger.debug("predict handler received message type {}".format(summary[rmr.RMR_MS_MSG_TYPE]))
-    logger.debug("adding somethign")
-    logger.debug("message is " + summary[rmr.RMR_MS_PAYLOAD].decode())
-    pred_req_msg = json.loads(summary[rmr.RMR_MS_PAYLOAD].decode())
-    all_cells = {}
-    ind = 0
-    for ncell in pred_req_msg["CellMeasurements"]:
-        if (ind == 0):
-            all_cells[ncell["CellID"]] = [50000, 20000]
-        else:
-            all_cells[ncell["CellID"]] = [20000, 10000]
-        ind += 1
-
-    pred_msg = {}
-    pred_msg[pred_req_msg["PredictionUE"]] = all_cells
+    Function that processes messages for type 30000
+    """
+    logger.debug("predict handler received payload {}".format(summary[rmr.RMR_MS_PAYLOAD]))
+    pred_msg = predict(summary[rmr.RMR_MS_PAYLOAD])
     self.predict_requests += 1
     # we don't use rts here; free this
     self.rmr_free(sbuf)
-    # send a mock message based on input
-    success = self.rmr_send(json.dumps(pred_msg).encode(), 30002)
+    success = self.rmr_send(pred_msg.encode(), 30002)
+    logger.debug("Sending message to ts : {}".format(pred_msg))  # For debug purpose
     if success:
         logger.debug("predict handler: sent message successfully")
     else:
         logger.warning("predict handler: failed to send message")
 
 
+def nbcells(ue):
+    """
+        Extract neighbor cell id for a given UE
+    """
+    db.read_data(meas='liveUE', limit=1, ueid=ue)
+    df = db.data
+
+    nbc = df.filter(regex='nbCell').values[0]
+    return nbc
+
+
+def predict(payload):
+    """
+     Function that forecast the time series
+    """
+    tp = {}
+    payload = json.loads(payload)
+    ueid = payload['UEPredictionSet'][0]
+
+    nbc = nbcells(ueid)
+    for cid in nbc:
+        mcid = cid.replace('/', '')
+        db.read_data(meas='liveCell', cellid=cid, limit=11)
+        if len(db.data) != 0:
+            inp = db.data
+
+            if not os.path.isfile('qp/' + mcid):
+                train(db, cid)
+
+            df_f = forecast(inp, mcid, 1)
+            if df_f is not None:
+                tp[cid] = df_f.values.tolist()[0]
+                df_f['cellid'] = cid
+                db.write_prediction(df_f)
+            else:
+                tp[cid] = [None, None]
+    return json.dumps({ueid: tp})
+
+
 def start(thread=False):
     """
     This is a convenience function that allows this xapp to run in Docker
@@ -89,9 +121,15 @@ def start(thread=False):
     """
     logger.debug("QP xApp starting")
     global qp_xapp
+    global db
+    if not thread:
+        insert.populatedb()   # temporory method to popuate db, it will be removed when data will be coming through KPIMON to influxDB
+        db = DATABASE('UEData')
+    else:
+        db = DUMMY()
     fake_sdl = os.environ.get("USE_FAKE_SDL", None)
     qp_xapp = RMRXapp(qp_default_handler, rmr_port=4560, post_init=post_init, use_fake_sdl=bool(fake_sdl))
-    qp_xapp.register_callback(qp_predict_handler, 30001)
+    qp_xapp.register_callback(qp_predict_handler, 30000)
     qp_xapp.run(thread)
 
 
diff --git a/qp/prediction.py b/qp/prediction.py
new file mode 100644 (file)
index 0000000..c861ca7
--- /dev/null
@@ -0,0 +1,45 @@
+# ==================================================================================
+#  Copyright (c) 2020 HCL Technologies Limited.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+# ==================================================================================
+# import pandas as pd
+import os
+import joblib
+import pandas as pd
+from qptrain import PROCESS
+
+
+def forecast(data, cid, nobs=1):
+    """
+     forecast the time series using the saved model.
+    """
+    data = data[['pdcpBytesUl', 'pdcpBytesDl']]
+    ps = PROCESS(data.copy())
+    ps.make_stationary()
+
+    if not ps.valid():
+        df_f = data.tail(1)
+    elif os.path.isfile('qp/'+cid):
+        model = joblib.load('qp/'+cid)
+        pred = model.forecast(y=ps.data.values, steps=nobs)
+
+        if pred is not None:
+            df_f = pd.DataFrame(pred, columns=data.columns)
+            df_f.index = pd.date_range(start=data.index[-1], freq='10ms', periods=len(df_f))
+            df_f = df_f[data.columns].astype(int)
+            df_f = ps.invert_transformation(data, df_f)
+    else:
+        return None
+    df_f = df_f[data.columns].astype(int)
+    return df_f
diff --git a/qp/qptrain.py b/qp/qptrain.py
new file mode 100644 (file)
index 0000000..68dcb35
--- /dev/null
@@ -0,0 +1,91 @@
+# ==================================================================================
+#  Copyright (c) 2020 HCL Technologies Limited.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+# ==================================================================================
+
+from statsmodels.tsa.api import VAR
+from statsmodels.tsa.stattools import adfuller
+import joblib
+
+
+class PROCESS(object):
+
+    def __init__(self, data):
+        self.diff = 0
+        self.data = data
+
+    def adfuller_test(self, series, thresh=0.05, verbose=False):
+        """ADFuller test for Stationarity of given series and return True or False"""
+        r = adfuller(series, autolag='AIC')
+        output = {'test_statistic': round(r[0], 4), 'pvalue': round(r[1], 4), 'n_lags': round(r[2], 4), 'n_obs': r[3]}
+        p_value = output['pvalue']
+        if p_value <= thresh:
+            return True
+        else:
+            return False
+
+    def make_stationary(self):
+        """ call adfuller_test() to check for stationary
+            If the column is stationary, perform 1st differencing and return data"""
+        df = self.data.copy()
+        res_adf = []
+        for name, column in df.iteritems():
+            res_adf.append(self.adfuller_test(column))  # Perform ADF test
+        if not all(res_adf):
+            self.data = df.diff().dropna()
+            self.diff += 1
+
+    def invert_transformation(self, inp, forecast):
+        """Revert back the differencing to get the forecast to original scale."""
+        if self.diff == 0:
+            return forecast
+        df = forecast.copy()
+        columns = inp.columns
+        for col in columns:
+            df[col] = inp[col].iloc[-1] + df[col].cumsum()
+        self.diff = 0
+        return df
+
+    def process(self):
+        """ Filter throughput parameters, call make_stationary() to check for Stationarity time series
+        """
+        df = self.data.copy()
+        df = df[['pdcpBytesDl', 'pdcpBytesUl']]
+        self.data = df.loc[:, (df != 0).any(axis=0)]
+        self.make_stationary()  # check for Stationarity and make the Time Series Stationary
+
+    def valid(self):
+        df = self.data.copy()
+        df = df.loc[:, (df != 0).any(axis=0)]
+        if len(df) != 0 and df.shape[1] == 2:
+            return True
+        else:
+            return False
+
+
+def train(db, cid):
+    """
+     Read the input file(based on cell id received from the main program)
+     call process() to forecast the downlink and uplink of the input cell id
+     Make a VAR model, call the fit method with the desired lag order.
+    """
+    db.read_data(meas='liveCell', cellid=cid)
+    md = PROCESS(db.data)
+    md.process()
+    if md.valid():
+        model = VAR(md.data)          # Make a VAR model
+        model_fit = model.fit(10)            # call fit method with lag order
+        file_name = 'qp/'+cid.replace('/', '')
+        with open(file_name, 'wb') as f:
+            joblib.dump(model_fit, f)     # Save the model with the cell id name
index d7b94dd..02723fb 100644 (file)
@@ -1,3 +1,3 @@
 # CI script installs RMR from PackageCloud using this version
 ---
-version: 4.0.5
+version: 4.7.4
index eb58e77..9160a86 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -17,11 +17,11 @@ from setuptools import setup, find_packages
 
 setup(
     name="qp",
-    version="0.0.2",
+    version="0.0.3",
     packages=find_packages(exclude=["tests.*", "tests"]),
     description="Quality-of-Service Predictor Xapp for Traffic Steering",
     url="https://gerrit.o-ran-sc.org/r/admin/repos/ric-app/qp",
-    install_requires=["ricxappframe>=1.1.1,<2.0.0"],
+    install_requires=["ricxappframe>=1.1.1,<2.0.0", "joblib>=0.3.2", "statsmodels>=0.11.1", "mdclogpy<=1.1.1", "influxdb", "pandas"],
     entry_points={"console_scripts": ["run-qp.py=qp.main:start"]},  # adds a magical entrypoint for Docker
     license="Apache 2.0",
     data_files=[("", ["LICENSE.txt"])],
index 79f648d..4c2e6c4 100644 (file)
@@ -1,5 +1,6 @@
 # ==================================================================================
 #       Copyright (c) 2020 AT&T Intellectual Property.
+#       Copyright (c) 2021 HCL Technologies Limited.
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
@@ -20,72 +21,9 @@ import pytest
 
 @pytest.fixture
 def qp_prediction():
-    return {
-        "12345": {
-            "310-680-200-555001": [50000, 20000],
-            "310-680-200-555003": [20000, 10000],
-            "310-680-200-555002": [20000, 10000]
-        }
-    }
+    return {"Car-1": {"c6/B2": [12650, 12721], "c6/N77": [12663, 12739], "c1/B13": [12576, 12655], "c7/B13": [12649, 12697], "c5/B13": [12592, 12688]}}
 
 
 @pytest.fixture
-def qpd_to_qp():
-    return {
-        "PredictionUE": "12345",
-        "UEMeasurements": {
-            "ServingCellID": "310-680-200-555002",
-            "MeasTimestampUEPDCPBytes": "2020-03-18 02:23:18.220",
-            "MeasPeriodUEPDCPBytes": 20,
-            "UEPDCPBytesDL": 250000,
-            "UEPDCPBytesUL": 100000,
-            "MeasTimestampUEPRBUsage": "2020-03-18 02:23:18.220",
-            "MeasPeriodUEPRBUsage": 20,
-            "UEPRBUsageDL": 10,
-            "UEPRBUsageUL": 30,
-        },
-        "CellMeasurements": [
-            {
-                "CellID": "310-680-200-555001",
-                "MeasTimestampPDCPBytes": "2020-03-18 02:23:18.220",
-                "MeasPeriodPDCPBytes": 20,
-                "PDCPBytesDL": 2000000,
-                "PDCPBytesUL": 1200000,
-                "MeasTimestampAvailPRB": "2020-03-18 02:23:18.220",
-                "MeasPeriodAvailPRB": 20,
-                "AvailPRBDL": 30,
-                "AvailPRBUL": 50,
-                "MeasTimestampRF": "2020-03-18 02:23:18.210",
-                "MeasPeriodRF": 40,
-                "RFMeasurements": {"RSRP": -90, "RSRQ": -13, "RSSINR": -2.5},
-            },
-            {
-                "CellID": "310-680-200-555003",
-                "MeasTimestampPDCPBytes": "2020-03-18 02:23:18.220",
-                "MeasPeriodPDCPBytes": 20,
-                "PDCPBytesDL": 1900000,
-                "PDCPBytesUL": 1000000,
-                "MeasTimestampAvailPRB": "2020-03-18 02:23:18.220",
-                "MeasPeriodAvailPRB": 20,
-                "AvailPRBDL": 60,
-                "AvailPRBUL": 80,
-                "MeasTimestampRF": "2020-03-18 02:23:18.210",
-                "MeasPeriodRF": 40,
-                "RFMeasurements": {"RSRP": -140, "RSRQ": -17, "RSSINR": -6},
-            },
-            {
-                "CellID": "310-680-200-555002",
-                "MeasTimestampPDCPBytes": "2020-03-18 02:23:18.220",
-                "MeasPeriodPDCPBytes": 20,
-                "PDCPBytesDL": 800000,
-                "PDCPBytesUL": 400000,
-                "MeasTimestampAvailPRB": "2020-03-18 02:23:18.220",
-                "MeasPeriodAvailPRB": 20,
-                "AvailPRBDL": 30,
-                "AvailPRBUL": 45,
-                "MeasTimestampRF": "2020-03-18 02:23:18.210",
-                "MeasPeriodRF": 40,
-                "RFMeasurements": {"RSRP": -115, "RSRQ": -16, "RSSINR": -5},
-            },
-        ],
-    }
+def ts_to_qp():
+    return '{"UEPredictionSet": ["Car-1"]}'
index 6fcafb5..322b93e 100644 (file)
@@ -1,6 +1,6 @@
 # do NOT use localhost, seems unresolved on jenkins VMs
 newrt|start
-mse| 30001 | -1 | 127.0.0.1:4560 # prediction request from QPD to QP
-mse| 60001 | -1 | 127.0.0.1:4560 # other message from QPD to QP
+mse| 30000 | -1 | 127.0.0.1:4560 # prediction request from TS to QP
+mse| 60001 | -1 | 127.0.0.1:4560 # other message from TS to QP
 mse| 30002 | -1 | 127.0.0.1:4563 # prediction response from QP to TS
 newrt|end
index cf3a960..9473bc2 100644 (file)
@@ -19,7 +19,7 @@ from contextlib import suppress
 from qp import main
 from ricxappframe.xapp_frame import Xapp, RMRXapp
 
-mock_qpd_xapp = None
+mock_qp_xapp = None
 mock_ts_xapp = None
 
 """
@@ -43,7 +43,11 @@ def test_init_xapp(monkeypatch):
     main.start(thread=True)
 
 
-def test_rmr_flow(monkeypatch, qpd_to_qp, qp_prediction):
+def test_predict(monkeypatch, ts_to_qp):
+    main.predict(ts_to_qp)
+
+
+def test_rmr_flow(monkeypatch, ts_to_qp, qp_prediction):
     """
     this flow mocks out the xapps on both sides of QP.
     It first stands up a mock ts, then it starts up a mock qp-driver
@@ -68,25 +72,20 @@ def test_rmr_flow(monkeypatch, qpd_to_qp, qp_prediction):
 
     time.sleep(1)
 
-    # define a mock qp driver xapp that sends a message to QP under test
-    def mock_qpd_entry(self):
+    # define a mock qp xapp that listens on 4560
+    def mock_qp_entry(self):
 
         # good traffic steering request
-        val = json.dumps(qpd_to_qp).encode()
-        self.rmr_send(val, 30001)
+        val = json.dumps(ts_to_qp).encode()
+        self.rmr_send(val, 30000)
 
         # should trigger the default handler and do nothing
         val = json.dumps({"test send 60001": 2}).encode()
         self.rmr_send(val, 60001)
 
-    global mock_qpd_xapp
-    mock_qpd_xapp = Xapp(entrypoint=mock_qpd_entry, rmr_port=4666, use_fake_sdl=True)
-    mock_qpd_xapp.run()  # this will return since entry isn't a loop
-
-    time.sleep(1)
-
-    assert main.get_stats() == {"PredictRequests": 1}
-    assert expected_result == qp_prediction
+    global mock_qp_xapp
+    mock_qp_xapp = Xapp(entrypoint=mock_qp_entry, rmr_port=4560, use_fake_sdl=True)
+    mock_qp_xapp.run()  # this will return since entry isn't a loop
 
 
 def teardown_module():
@@ -99,6 +98,6 @@ def teardown_module():
     with suppress(Exception):
         mock_ts_xapp.stop()
     with suppress(Exception):
-        mock_qpd_xapp.stop()
+        mock_qp_xapp.stop()
     with suppress(Exception):
         main.stop()
diff --git a/tox.ini b/tox.ini
index 18bedff..c6e9565 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -18,25 +18,28 @@ envlist = code,flake8,docs,docs-linkcheck
 minversion = 2.0
 
 [testenv:code]
-basepython = python3.8
+basepython = python3.7
 deps=
     pytest
     coverage
     pytest-cov
 setenv =
     LD_LIBRARY_PATH = /usr/local/lib/:/usr/local/lib64
+    PYTHONPATH = {toxinidir}:qp:/usr/lib/python3.7/site-packages/
     RMR_SEED_RT = tests/fixtures/test_local.rt
     RMR_ASYNC_CONN = 0
     USE_FAKE_SDL = 1
 
 commands =
-    pytest -v --cov qp --cov-report xml --cov-report term-missing --cov-report html --cov-fail-under=70
+    pytest -v --cov qp --cov-report xml --cov-report term-missing --cov-report html --cov-fail-under=60
     coverage xml -i
 
 [testenv:flake8]
-basepython = python3.8
+basepython = python3.7
 skip_install = true
 deps = flake8
+setenv =
+    PYTHONPATH = {toxinidir}:qp:/usr/lib/python3.7/site-packages/
 commands = flake8 setup.py qp tests
 
 [flake8]
@@ -51,7 +54,7 @@ commands = sh -c 'pip freeze > requirements.txt'
 [testenv:docs]
 whitelist_externals = echo
 skipsdist = true
-basepython = python3.8
+basepython = python3.7
 deps =
     sphinx
     sphinx-rtd-theme
@@ -64,7 +67,7 @@ commands =
 
 [testenv:docs-linkcheck]
 skipsdist = true
-basepython = python3.8
+basepython = python3.7
 deps = sphinx
        sphinx-rtd-theme
        sphinxcontrib-httpdomain
index 3a5084d..10c2d6a 100644 (file)
@@ -1,6 +1,6 @@
 {
         "xapp_name": "qp",
-        "version": "0.0.2",
+        "version": "0.0.3",
         "containers": [
             {
                 "name": "qp",
@@ -17,7 +17,7 @@
                     "name": "rmr-data",
                     "container": "qp",
                     "port": 4560,
-                    "rxMessages": ["TS_QOE_PRED_REQ"],
+                    "rxMessages": ["TS_UE_LIST"],
                     "txMessages": ["TS_QOE_PREDICTION"],
                     "policies": [],
                     "description": "rmr receive data port for qp"
@@ -34,7 +34,7 @@
             "protPort": "tcp:4560",
             "maxSize": 2072,
             "numWorkers": 1,
-            "rxMessages": ["TS_QOE_PRED_REQ"],
+            "rxMessages": ["TS_UE_LIST"],
             "txMessages": ["TS_QOE_PREDICTION"],
             "policies": []
         }