Skip to content

Commit 6c5992d

Browse files
committed
Add OFDMA ranging status string enum
- Map docsIf31CmStatusOfdmaUsRangingStatus to enum - Update OFDMA stats docs to show string values - Add tests for OFDMA ranging status mapping
1 parent ed14f83 commit 6c5992d

7 files changed

Lines changed: 1168 additions & 5 deletions

File tree

docs/api/fast-api/single/us/ofdma/stats.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Retrieves statistics and configuration parameters for upstream OFDMA channels fr
7070
"docsIf31CmStatusOfdmaUsRangingAborteds": 0,
7171
"docsIf31CmStatusOfdmaUsT3Exceededs": 0,
7272
"docsIf31CmStatusOfdmaUsIsMuted": false,
73-
"docsIf31CmStatusOfdmaUsRangingStatus": "4"
73+
"docsIf31CmStatusOfdmaUsRangingStatus": "success"
7474
}
7575
}
7676
]
@@ -98,7 +98,7 @@ Retrieves statistics and configuration parameters for upstream OFDMA channels fr
9898
| `docsIf31CmStatusOfdmaUsRangingAborteds` | int | Number of aborted ranging attempts |
9999
| `docsIf31CmStatusOfdmaUsT3Exceededs` | int | Number of times T3 retries exceeded |
100100
| `docsIf31CmStatusOfdmaUsIsMuted` | bool | Indicates if the upstream is muted |
101-
| `docsIf31CmStatusOfdmaUsRangingStatus` | str | Current ranging status (e.g., `4` = success) |
101+
| `docsIf31CmStatusOfdmaUsRangingStatus` | string | Ranging state name (e.g., `success`). |
102102

103103

104104
## Notes

src/pypnm/docsis/data_type/DocsIf31CmUsOfdmaChanEntry.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
from __future__ import annotations
33

44
# SPDX-License-Identifier: Apache-2.0
5-
# Copyright (c) 2025 Maurice Garcia
5+
# Copyright (c) 2025-2026 Maurice Garcia
66
import logging
77
from collections.abc import Callable
88

99
from pydantic import BaseModel
1010

11+
from pypnm.lib.constants import DocsIf31CmStatusOfdmaUsRangingStatus
1112
from pypnm.lib.types import ChannelId, FrequencyHz
1213
from pypnm.snmp.snmp_v2c import Snmp_v2c
1314

@@ -30,7 +31,7 @@ class DocsIf31CmUsOfdmaChan(BaseModel):
3031
docsIf31CmStatusOfdmaUsRangingAborteds: int | None = None
3132
docsIf31CmStatusOfdmaUsT3Exceededs: int | None = None
3233
docsIf31CmStatusOfdmaUsIsMuted: bool | None = None
33-
docsIf31CmStatusOfdmaUsRangingStatus: str | None = None
34+
docsIf31CmStatusOfdmaUsRangingStatus: DocsIf31CmStatusOfdmaUsRangingStatus | None = None
3435

3536
class DocsIf31CmUsOfdmaChanEntry(BaseModel):
3637
index: int
@@ -92,7 +93,9 @@ async def fetch(field: str, cast: Callable | None = None) -> None | int | float
9293
docsIf31CmStatusOfdmaUsRangingAborteds = await fetch("docsIf31CmStatusOfdmaUsRangingAborteds", int),
9394
docsIf31CmStatusOfdmaUsT3Exceededs = await fetch("docsIf31CmStatusOfdmaUsT3Exceededs", int),
9495
docsIf31CmStatusOfdmaUsIsMuted = await fetch("docsIf31CmStatusOfdmaUsIsMuted", Snmp_v2c.truth_value),
95-
docsIf31CmStatusOfdmaUsRangingStatus = await fetch("docsIf31CmStatusOfdmaUsRangingStatus", str)
96+
docsIf31CmStatusOfdmaUsRangingStatus = DocsIf31CmStatusOfdmaUsRangingStatus.from_int(
97+
await fetch("docsIf31CmStatusOfdmaUsRangingStatus", int),
98+
)
9699
)
97100

98101
try:

src/pypnm/lib/constants.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,34 @@ def from_int(cls, value: int | None) -> DocsIf3CmStatusUsRangingStatus | None:
160160
case _:
161161
return None
162162

163+
class DocsIf31CmStatusOfdmaUsRangingStatus(StringEnum):
164+
OTHER = "other"
165+
ABORTED = "aborted"
166+
RETRIES_EXCEEDED = "retriesExceeded"
167+
SUCCESS = "success"
168+
CONTINUE = "continue"
169+
TIMEOUT_T4 = "timeoutT4"
170+
171+
@classmethod
172+
def from_int(cls, value: int | None) -> DocsIf31CmStatusOfdmaUsRangingStatus | None:
173+
if value is None:
174+
return None
175+
match value:
176+
case 1:
177+
return cls.OTHER
178+
case 2:
179+
return cls.ABORTED
180+
case 3:
181+
return cls.RETRIES_EXCEEDED
182+
case 4:
183+
return cls.SUCCESS
184+
case 5:
185+
return cls.CONTINUE
186+
case 6:
187+
return cls.TIMEOUT_T4
188+
case _:
189+
return None
190+
163191
class DocsIf31CmDsOfdmChanIndicator(StringEnum):
164192
OTHER = "other"
165193
PRIMARY = "primary"
@@ -217,6 +245,7 @@ def from_int(cls, value: int | None) -> DocsIf31CmDsOfdmChanIndicator | None:
217245
"DocsIfDownChannelModulation",
218246
"DocsIfDownChannelInterleave",
219247
"DocsIf3CmStatusUsRangingStatus",
248+
"DocsIf31CmStatusOfdmaUsRangingStatus",
220249
"DocsIf31CmDsOfdmChanIndicator",
221250
"DEFAULT_SPECTRUM_ANALYZER_INDICES",
222251
"FEC_SUMMARY_TYPE_STEP_SECONDS", "FEC_SUMMARY_TYPE_LABEL",
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# Copyright (c) 2026 Maurice Garcia
3+
4+
from __future__ import annotations
5+
6+
import pytest
7+
8+
from pypnm.docsis.data_type.DocsIf31CmDsOfdmChanEntry import DocsIf31CmDsOfdmChanChannelEntry
9+
from pypnm.lib.constants import DocsIf31CmDsOfdmChanIndicator
10+
from pypnm.snmp.snmp_v2c import Snmp_v2c
11+
12+
13+
class _FakeSnmp:
14+
def __init__(self, values: dict[str, str]) -> None:
15+
self._values = values
16+
17+
async def get(self, oid: str) -> tuple[str, str]:
18+
return (oid, self._values.get(oid, ""))
19+
20+
21+
@pytest.mark.asyncio
22+
async def test_docs_if31_cm_ds_ofdm_chan_indicator_maps_nonprimary(monkeypatch: pytest.MonkeyPatch) -> None:
23+
values = {
24+
"docsIf31CmDsOfdmChanChanIndicator.1": "4",
25+
"docsIf31CmDsOfdmChanChannelId.1": "1",
26+
"docsIf31CmDsOfdmChanSubcarrierSpacing.1": "25",
27+
}
28+
29+
fake = _FakeSnmp(values)
30+
monkeypatch.setattr(Snmp_v2c, "get_result_value", lambda res: res[1])
31+
32+
result = await DocsIf31CmDsOfdmChanChannelEntry.from_snmp(1, fake)
33+
assert result.entry.docsIf31CmDsOfdmChanChanIndicator == DocsIf31CmDsOfdmChanIndicator.NON_PRIMARY
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# Copyright (c) 2026 Maurice Garcia
3+
4+
from __future__ import annotations
5+
6+
import pytest
7+
8+
from pypnm.docsis.data_type.DocsIf31CmUsOfdmaChanEntry import DocsIf31CmUsOfdmaChanEntry
9+
from pypnm.lib.constants import DocsIf31CmStatusOfdmaUsRangingStatus
10+
from pypnm.snmp.snmp_v2c import Snmp_v2c
11+
12+
13+
class _FakeSnmp:
14+
def __init__(self, values: dict[str, str]) -> None:
15+
self._values = values
16+
17+
async def get(self, oid: str) -> tuple[str, str]:
18+
return (oid, self._values.get(oid, ""))
19+
20+
21+
@pytest.mark.asyncio
22+
async def test_docs_if31_cm_status_ofdma_us_ranging_status_maps_success(
23+
monkeypatch: pytest.MonkeyPatch,
24+
) -> None:
25+
values = {
26+
"docsIf31CmStatusOfdmaUsRangingStatus.1": "4",
27+
"docsIf31CmUsOfdmaChanChannelId.1": "1",
28+
}
29+
30+
fake = _FakeSnmp(values)
31+
monkeypatch.setattr(Snmp_v2c, "get_result_value", lambda res: res[1])
32+
33+
result = await DocsIf31CmUsOfdmaChanEntry.from_snmp(1, fake)
34+
assert result.entry.docsIf31CmStatusOfdmaUsRangingStatus == (
35+
DocsIf31CmStatusOfdmaUsRangingStatus.SUCCESS
36+
)

0 commit comments

Comments
 (0)