Skip to content

Commit

Permalink
Coda56 (#38)
Browse files Browse the repository at this point in the history
* Coda56 modem support

* Parsing through Json
  • Loading branch information
andresp authored Oct 6, 2024
1 parent 9040e35 commit 96ca121
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 19 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ __pycache__/

# Distribution / packaging
.Python
bin/
build/
develop-eggs/
dist/
Expand Down Expand Up @@ -139,4 +140,6 @@ cython_debug/
cablemodem-status.last
data/configuration.ini

.kube/
.kube/

Scripts/
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ testpaths = [
pythonpath = [
"src"
]

[tool.setuptools.packages.find]
# All the following settings are optional:
where = ["src"] # ["."] by default
include = ["docsismodem*"] # ["*"] by default
exclude = ["docsismodem.tests*"] # empty by default
namespaces = false # true by default
16 changes: 16 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from setuptools import setup, find_packages

setup(
name='docsismodem',
version='0.0.34',
install_requires=[
'requests',
'importlib-metadata; python_version<"3.11"',
],
packages=find_packages(
# All keyword arguments below are optional:
where='src', # '.' by default
include=['mypackage*'], # ['*'] by default
exclude=['mypackage.tests'], # empty by default
),
)
7 changes: 5 additions & 2 deletions src/docsismodem/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# from . import retriever
from .exceptions import ModemConnectionError, ModemCredentialsError
from .collectionJob import CollectionJob

__version__ = '0.0.33'
__all__ = ["CollectionJob", "ModemConnectionError", "ModemCredentialsError"]

__version__ = '0.0.33'
6 changes: 3 additions & 3 deletions src/docsismodem/collectionJob.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import functools
from logging import Logger
from sched import scheduler
from .modems.observablemodem import ObservableModem
from modems.observablemodem import ObservableModem

class collectionJob():
class CollectionJob():

modem = None
collectLogs = False
Expand All @@ -31,7 +31,7 @@ def wrapper(*args, **kwargs):
return catch_exceptions_decorator

@catch_exceptions(cancel_on_failure=False)
def collectionJob(self):
def CollectionJob(self):

self.modem.login()

Expand Down
152 changes: 152 additions & 0 deletions src/docsismodem/modems/hitron_coda56.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import json
from datetime import datetime, timezone
from influxdb_client import Point
import requests

from .observablemodem import ObservableModem

class HitronCoda56(ObservableModem):
baseUrl = ""
hostname = ""
session = None

def __init__(self, config, logger):
self.hostname = config['Modem']['Host']
self.baseUrl = "https://" + self.hostname
self.session = requests.Session()

super(HitronCoda56, self).__init__(config, logger)

def formatUpstreamPoints(self, data, sampleTime):
points = []
for index in range(0, len(data)):

values = data[index]

point = Point("upstreamQam") \
.tag("channel", values["portId"]) \
.tag("modulation", values["modtype"]) \
.tag("mode", values["scdmaMode"]) \
.tag("symbolRate", int(values["bandwidth"])) \
.tag("channelId", int(values["channelId"])) \
.tag("frequency", values["frequency"]) \
.time(sampleTime) \
.field("power", float(values["signalStrength"]))

points.append(point)

return points

def formatUpstreamOfdmaPoints(self, data, sampleTime):
points = []
for index in range(0, len(data)):

values = data[index]

if values["state"] == ' DISABLED':
continue

point = Point("upstreamOfdma") \
.tag("channel", values["uschindex"]) \
.tag("modulation", "OFDMA") \
.tag("channelId", int(values["uschindex"])) \
.tag("frequency", values["frequency"]) \
.tag("fftsize", values["fftVal"]) \
.time(sampleTime) \
.field("digatten", float(values["digAtten"])) \
.field("digattenbo", float(values["digAttenBo"])) \
.field("channelbw", float(values["channelBw"])) \
.field("reppower", float(values["repPower"])) \
.field("reppower1_6", float(values["repPower1_6"]))

points.append(point)

return points

def formatDownstreamPoints(self, data, sampleTime):
points = []

for index in range(0, len(data)):

values = data[index]

point = Point("downstreamQam") \
.tag("channel", values["portId"]) \
.tag("modulation", "256QAM" if values["modulation"] == "2" else '"') \
.tag("channelId", int(values["channelId"])) \
.tag("frequency", values["frequency"]) \
.time(sampleTime) \
.field("power", float(values["signalStrength"])) \
.field("snr", float(values["snr"])) \
.field("octets", int(values["dsoctets"])) \
.field("correctables", int(values["correcteds"])) \
.field("uncorrectables", int(values["uncorrect"]))

points.append(point)

return points

def formatDownstreamOfdmPoints(self, data, sampleTime):
points = []

for index in range(0, len(data)):

values = data[index]

if values["plclock"] != 'YES':
continue

point = Point("downstreamOFDM") \
.tag("receiver", values["receive"]) \
.tag("modulation", "OFDM") \
.tag("ffttype", values["ffttype"]) \
.tag("frequency", values["Subcarr0freqFreq"]) \
.time(sampleTime) \
.field("power", float(values["plcpower"])) \
.field("snr", float(values["SNR"])) \
.field("octets", int(values["dsoctets"])) \
.field("correctables", int(values["correcteds"])) \
.field("uncorrectables", int(values["uncorrect"]))

points.append(point)

return points

def login(self):
pass

def collectStatus(self):
self.logger.info("Getting modem status")

sampleTime = datetime.now(timezone.utc).isoformat()
now = datetime.now(timezone.utc).timestamp

# QAM down
response = self.session.get(self.baseUrl + "/data/dsinfo.asp?_=" + str(now), verify=False)
downstreamData = json.loads(response.text)
downstreamPoints = self.formatDownstreamPoints(downstreamData, sampleTime)

# OFDM down
response = self.session.get(self.baseUrl + "/data/dsofdminfo.asp?_=" + str(now), verify=False)
downstreamTableOfdm = json.loads(response.text)
downstreamOfdmPoints = self.formatDownstreamOfdmPoints(downstreamTableOfdm, sampleTime)

# TDMA up
response = self.session.get(self.baseUrl + "/data/usinfo.asp?_=" + str(now), verify=False)
upstreamData = json.loads(response.text)
upstreamPoints = self.formatUpstreamPoints(upstreamData, sampleTime)

# OFDMA up
response = self.session.get(self.baseUrl + "/data/usofdminfo.asp?_=" + str(now), verify=False)
upstreamOfdmaData = json.loads(response.text)
upstreamOfdmaPoints = self.formatUpstreamOfdmaPoints(upstreamOfdmaData, sampleTime)

# Store data to InfluxDB
self.timeseriesWriter.write(record=downstreamPoints)
self.timeseriesWriter.write(record=downstreamOfdmPoints)
self.timeseriesWriter.write(record=upstreamPoints)
self.timeseriesWriter.write(record=upstreamOfdmaPoints)

def collectLogs(self):
# Not implemented yet
pass
3 changes: 2 additions & 1 deletion src/docsismodem/modems/modemtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ class ModemType(StrEnum):
MotorolaMB8600 = "MotorolaMB8600"
NetgearCM2000 = "NetgearCM2000"
TechnicolorXB7 = "TechnicolorXB7"
TouchstoneTG3492UPCCH = "TouchstoneTG3492UPCCH"
TouchstoneTG3492UPCCH = "TouchstoneTG3492UPCCH"
HitronCoda56 = "HitronCoda56"
2 changes: 1 addition & 1 deletion src/docsismodem/modems/observablemodem.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from abc import ABC, abstractmethod

from ..storage.timeserieswriterfactory import TimeseriesWriterFactory
from storage.timeserieswriterfactory import TimeseriesWriterFactory

class ObservableModem(ABC):

Expand Down
4 changes: 3 additions & 1 deletion src/docsismodem/modems/observablemodemfactory.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from .modemtype import ModemType
from .hitron_coda56 import HitronCoda56
from .motorola_mb8600 import MotorolaMB8600
from .netgear_cm2000 import NetgearCM2000
from .observablemodem import ObservableModem
from .technicolor_xb7 import TechnicolorXB7
from .touchstone_tg3492_upc_ch import TouchstoneTG3492UPCCH


class ObservableModemFactory():
@staticmethod
def get(type: ModemType, config, logger) -> ObservableModem:
Expand All @@ -19,5 +19,7 @@ def get(type: ModemType, config, logger) -> ObservableModem:
return TechnicolorXB7(config, logger)
case ModemType.TouchstoneTG3492UPCCH:
return TouchstoneTG3492UPCCH(config, logger)
case ModemType.HitronCoda56:
return HitronCoda56(config, logger)
case _:
raise ValueError("Invalid modem type selected.")
2 changes: 1 addition & 1 deletion src/docsismodem/modems/technicolor_xb7.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ..exceptions import ModemConnectionError, ModemCredentialsError
from exceptions import ModemConnectionError, ModemCredentialsError
from .observablemodem import ObservableModem
from bs4 import BeautifulSoup
from datetime import datetime
Expand Down
5 changes: 2 additions & 3 deletions src/docsismodem/probe.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import datetime
from flask_healthz import HealthError
from .collectionJob import collectionJob

from collectionJob import CollectionJob

class Probe():

runner = None
runEveryMinutes = 0

def __init__(self, runner: collectionJob, runEveryMinutes: int) -> None:
def __init__(self, runner: CollectionJob, runEveryMinutes: int) -> None:
self.runner = runner
self.runEveryMinutes = runEveryMinutes

Expand Down
11 changes: 5 additions & 6 deletions src/docsismodem/retriever.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
from requests.packages import urllib3
import schedule
import time
from .modems.observablemodemfactory import ObservableModemFactory
from collectionJob import CollectionJob
from probe import Probe
from modems import ObservableModemFactory

from flask import Flask
from flask_healthz import healthz

from .probe import Probe
from .collectionJob import collectionJob

def main():
consoleLogger.info("Connecting to InfluxDB")

Expand All @@ -31,7 +30,7 @@ def main():

modem = ObservableModemFactory.get(config['General']['ModemType'], config, consoleLogger)

jobRunner = collectionJob(modem, config['Modem'].getboolean('CollectLogs'), consoleLogger)
jobRunner = CollectionJob(modem, config['Modem'].getboolean('CollectLogs'), consoleLogger)

# Because the modem uses a self-signed certificate and this is expected, disabling the warning to reduce noise.
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
Expand All @@ -57,7 +56,7 @@ def runDaemon():
time.sleep(1)
else:
consoleLogger.info("One-time execution")
jobRunner.collectionJob()
jobRunner.CollectionJob()



Expand Down

0 comments on commit 96ca121

Please sign in to comment.