Systems: P25 trunked / P25 conventional / analog as one choice (node-26#24)
- system_config.normalize(): a P25 system with config.mode "conventional"
carries config.channels [{freq MHz, name, nac?}] (1-2, within 1.5 MHz) and
can no longer carry control channels (422). A missing mode is trunked and
passes through untouched, so there is no migration. An analog channel is
mirrored into control_channels for older nodes.
- talkgroups.name_from_system resolves the edge node's synthetic per-channel
id (freq in 100 Hz units) to the channel name.
- The checkin persists radio_channels (conventional channels + encryption).
- Systems editor: Type = P25 trunked / P25 conventional / Analog (NBFM) /
DMR, plus a channel-row editor for conventional and a one-frequency form for
analog. The node page shows conventional channels and an encryption notice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ae0ece9d2c
commit
23860eb31b
@@ -5,6 +5,7 @@ from typing import Dict, List, Optional
|
||||
from app.models import AreaContextBody, SystemCreate, SystemRecord
|
||||
from app.internal import firestore as fstore
|
||||
from app.internal import area_context as area_ctx
|
||||
from app.internal import system_config
|
||||
from app.internal.auth import (
|
||||
require_admin_token,
|
||||
require_node_service_or_firebase_token,
|
||||
@@ -38,6 +39,14 @@ class AiFlagsBody(BaseModel):
|
||||
correlation_enabled: Optional[bool] = None
|
||||
|
||||
|
||||
def _checked_config(system_type: str, config: dict) -> dict:
|
||||
"""Trunked/conventional/analog shape check (node-26#24) — 422 with the reason."""
|
||||
try:
|
||||
return system_config.normalize(system_type, config)
|
||||
except system_config.SystemConfigError as e:
|
||||
raise HTTPException(422, str(e))
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def list_systems(decoded: dict = Depends(require_node_service_or_firebase_token)):
|
||||
org_id = await resolve_caller_org_id(decoded)
|
||||
@@ -64,7 +73,9 @@ async def create_system(
|
||||
_: dict = Depends(require_admin_token),
|
||||
):
|
||||
system_id = str(uuid.uuid4())
|
||||
doc = SystemRecord(system_id=system_id, org_id=org_id or FOUNDING_ORG_ID, **body.model_dump())
|
||||
fields = body.model_dump()
|
||||
fields["config"] = _checked_config(body.type, fields["config"])
|
||||
doc = SystemRecord(system_id=system_id, org_id=org_id or FOUNDING_ORG_ID, **fields)
|
||||
await fstore.doc_set("systems", system_id, doc.model_dump(), merge=False)
|
||||
return doc
|
||||
|
||||
@@ -80,6 +91,8 @@ async def update_system(system_id: str, body: SystemCreate, _: dict = Depends(re
|
||||
# save — they are edited through PUT /{id}/ten-codes and were never in this
|
||||
# payload. area_context (server-26#36) would have been the second casualty.
|
||||
patch = body.model_dump(exclude_unset=True)
|
||||
if "config" in patch:
|
||||
patch["config"] = _checked_config(patch.get("type", existing.get("type")), patch["config"])
|
||||
# The form sends config.talkgroups[] in full, which would erase the resolved
|
||||
# anchor and the pending-term queue the backend put there. Same class of bug
|
||||
# as ten_codes above; the backend merges its own fields back rather than
|
||||
|
||||
Reference in New Issue
Block a user