This commit is contained in:
Logan
2026-04-06 00:23:33 -04:00
parent 1a9c92b6db
commit 7de55f9885
10 changed files with 189 additions and 44 deletions
@@ -29,7 +29,7 @@ def save_whitelist(talkgroup_tags: List[int]) -> None:
with open("/configs/active.cfg.whitelist.tsv", 'w', newline='', encoding='utf-8') as file:
writer = csv.writer(file, delimiter='\t', lineterminator='\n')
# Write rows
for tag in talkgroup_tags:
for tag in (talkgroup_tags or []):
writer.writerow([tag])
def del_none_in_dict(d):
+22 -3
View File
@@ -1,11 +1,30 @@
from fastapi import FastAPI
from contextlib import asynccontextmanager
import os
import routers.op25_controller as op25_controller
from internal.logger import create_logger
from internal.liquidsoap_config_utils import generate_liquid_script
from models import IcecastConfig
# Initialize logging
LOGGER = create_logger(__name__)
# Define FastAPI app
app = FastAPI()
@asynccontextmanager
async def lifespan(app: FastAPI):
try:
config = IcecastConfig(
icecast_host=os.getenv("ICECAST_HOST", "localhost"),
icecast_port=int(os.getenv("ICECAST_PORT", "8000")),
icecast_mountpoint=os.getenv("ICECAST_MOUNT", "/radio"),
icecast_password=os.getenv("ICECAST_SOURCE_PASSWORD", "hackme"),
)
generate_liquid_script(config)
LOGGER.info("op25.liq generated from environment variables.")
except Exception as e:
LOGGER.error(f"Failed to generate op25.liq: {e}")
yield
app = FastAPI(lifespan=lifespan)
app.include_router(op25_controller.create_op25_router(), prefix="/op25")
+46 -23
View File
@@ -10,42 +10,64 @@ from internal.liquidsoap_config_utils import generate_liquid_script
LOGGER = create_logger(__name__)
op25_process = None
OP25_PATH = "/op25/op25/gr-op25_repeater/apps/"
OP25_SCRIPT = "run_multi-rx_service.sh"
_PGID_FILE = "/tmp/op25.pgid"
def _save_pgid(pgid: int) -> None:
with open(_PGID_FILE, "w") as f:
f.write(str(pgid))
def _read_pgid():
try:
return int(open(_PGID_FILE).read().strip())
except Exception:
return None
def _is_running() -> bool:
pgid = _read_pgid()
if pgid is None:
return False
try:
os.killpg(pgid, 0)
return True
except OSError:
return False
def create_op25_router():
router = APIRouter()
@router.post("/start")
async def start_op25():
global op25_process
if op25_process is None:
try:
op25_process = subprocess.Popen(os.path.join(OP25_PATH, OP25_SCRIPT), shell=True, preexec_fn=os.setsid, cwd=OP25_PATH)
LOGGER.debug(op25_process)
return {"status": "OP25 started"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
else:
if _is_running():
return {"status": "OP25 already running"}
try:
proc = subprocess.Popen(os.path.join(OP25_PATH, OP25_SCRIPT), shell=True, preexec_fn=os.setsid, cwd=OP25_PATH)
_save_pgid(proc.pid)
LOGGER.debug(proc)
return {"status": "OP25 started"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/stop")
async def stop_op25():
global op25_process
if op25_process is not None:
try:
os.killpg(os.getpgid(op25_process.pid), signal.SIGTERM)
op25_process = None
return {"status": "OP25 stopped"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
else:
pgid = _read_pgid()
if pgid is None or not _is_running():
return {"status": "OP25 is not running"}
try:
os.killpg(pgid, signal.SIGTERM)
os.remove(_PGID_FILE)
return {"status": "OP25 stopped"}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/status")
async def get_status():
return {"status": "running" if op25_process else "stopped"}
return {"status": "running" if _is_running() else "stopped"}
@router.post("/generate-config")
async def generate_config(generator: ConfigGenerator):
@@ -63,13 +85,14 @@ def create_op25_router():
devices = [DeviceConfig()]
save_talkgroup_tags(generator.tags)
save_whitelist(generator.whitelist)
has_talkgroups = bool(generator.whitelist)
trunking = TrunkingConfig(
module="tk_p25.py",
chans=[TrunkingChannelConfig(
sysname=generator.systemName,
control_channel_list=','.join(generator.channels),
tagsFile="/configs/active.cfg.tags.tsv",
whitelist="/configs/active.cfg.whitelist.tsv"
control_channel_list=','.join(str(ch) for ch in generator.channels),
tagsFile="/configs/active.cfg.tags.tsv" if has_talkgroups else None,
whitelist="/configs/active.cfg.whitelist.tsv" if has_talkgroups else None
)]
)