add support to dvmcfggen for logging configuration; add support to dvmcfggen for user supplied answers files to automate skipping certain wizard questions;

pull/115/head
Bryan Biedenkapp 1 month ago
parent 2c61748bbd
commit 24bbfb6d25

@ -0,0 +1,378 @@
# DVMCfgGen Answers File Feature
## Overview
The answers file feature allows you to pre-populate wizard prompts using a YAML file. This enables:
- **Batch Configuration** - Generate multiple configurations with shared defaults
- **Automation** - Integrate with deployment scripts and CI/CD pipelines
- **Reproducibility** - Save and reuse configuration templates
- **Learning** - Understand what each prompt expects
## Quick Start
### 1. Use Example Files
Example answers files are provided in the `examples/` directory:
```bash
# For conventional systems
dvmcfg wizard -a examples/conventional-answers.yml
# For trunked systems
dvmcfg wizard --type trunk -a examples/trunked-answers.yml
```
### 2. Create Your Own
Copy an example file and customize it:
```bash
cp examples/conventional-answers.yml my-config-answers.yml
vim my-config-answers.yml
dvmcfg wizard -a my-config-answers.yml
```
### 3. Run Without Answers File (Default Behavior)
The answers file is completely optional. The wizard works normally without it:
```bash
dvmcfg wizard
```
## Answers File Format
Answers files are YAML format with one key-value pair per configuration option. All fields are optional.
### Conventional System Template
```yaml
# Step 2: Basic Configuration
template: enhanced
config_dir: .
system_identity: MYSITE001
network_peer_id: 100001
# Step 3: Logging Configuration
configure_logging: true
log_path: /var/log/dvm
activity_log_path: /var/log/dvm/activity
log_root: DVM
use_syslog: false
disable_non_auth_logging: false
# Step 4: Modem Configuration
modem_type: uart
modem_mode: air
serial_port: /dev/ttyUSB0
rx_level: 50
tx_level: 50
```
### Trunked System Template
```yaml
# Step 1: System Configuration
system_name: trunked_test
base_dir: .
identity: TRUNKED001
protocol: p25
vc_count: 2
# Step 2: Logging Configuration
configure_logging: true
log_path: /var/log/dvm
activity_log_path: /var/log/dvm/activity
log_root: TRUNKED
use_syslog: false
disable_non_auth_logging: false
```
## Supported Answer Keys
### ConfigWizard (Conventional Systems)
**Basic Configuration:**
- `template` - Template name (conventional, enhanced)
- `config_dir` - Configuration directory
- `system_identity` - System identity/callsign
- `network_peer_id` - Network peer ID (integer)
**Logging Configuration:**
- `configure_logging` - Enable logging (true/false)
- `log_path` - Log file directory
- `activity_log_path` - Activity log directory
- `log_root` - Log filename prefix
- `use_syslog` - Enable syslog (true/false)
- `disable_non_auth_logging` - Disable non-authoritative logging (true/false)
**Modem Configuration:**
- `modem_type` - Modem type (uart, null)
- `modem_mode` - Modem mode (air, dfsi)
- `serial_port` - Serial port path
- `rx_level` - RX level (0-100)
- `tx_level` - TX level (0-100)
**Optional Settings:**
- `rpc_config` - Configure RPC (true/false)
- `generate_rpc_password` - Generate RPC password (true/false)
- `rest_enable` - Enable REST API (true/false)
- `generate_rest_password` - Generate REST password (true/false)
- `update_lookups` - Update lookups (true/false)
- `save_lookups` - Save lookups (true/false)
- `allow_activity_transfer` - Allow activity transfer (true/false)
- `allow_diagnostic_transfer` - Allow diagnostic transfer (true/false)
- `allow_status_transfer` - Allow status transfer (true/false)
**Protocol Configuration (for Step 8/9):**
- `dmr_color_code` - DMR color code (integer)
- `dmr_network_id` - DMR network ID (integer)
- `dmr_site_id` - DMR site ID (integer)
- `dmr_site_model` - DMR site model (small, tiny, large, huge)
- `p25_nac` - P25 NAC code (hex string)
- `p25_network_id` - P25 network ID (integer)
- `p25_system_id` - P25 system ID (integer)
- `p25_rfss_id` - P25 RFSS ID (integer)
- `p25_site_id` - P25 site ID (integer)
### TrunkingWizard (Trunked Systems)
**System Configuration:**
- `system_name` - System name
- `base_dir` - Base directory
- `identity` - System identity
- `protocol` - Protocol (p25, dmr)
- `vc_count` - Number of voice channels (integer)
**Logging Configuration:**
- `configure_logging` - Enable logging (true/false)
- `log_path` - Log file directory
- `activity_log_path` - Activity log directory
- `log_root` - Log filename prefix
- `use_syslog` - Enable syslog (true/false)
- `disable_non_auth_logging` - Disable non-authoritative logging (true/false)
**Network Settings:**
- `fne_address` - FNE address
- `fne_port` - FNE port (integer)
- `fne_password` - FNE password
**Optional Settings:**
- `base_peer_id` - Base peer ID (integer)
- `base_rpc_port` - Base RPC port (integer)
- `modem_type` - Modem type (uart, null)
- `generate_rpc_password` - Generate RPC password (true/false)
- `base_rest_port` - Base REST port (integer)
- `rest_enable` - Enable REST API (true/false)
- `generate_rest_password` - Generate REST password (true/false)
## Usage Examples
### Example 1: Single Configuration with All Defaults
```yaml
# hotspot-answers.yml
template: enhanced
config_dir: /etc/dvm/hotspot
system_identity: MY_HOTSPOT
network_peer_id: 100001
configure_logging: true
log_path: /var/log/dvm
log_root: HOTSPOT
modem_type: uart
modem_mode: air
serial_port: /dev/ttyUSB0
```
Usage:
```bash
dvmcfg wizard -a hotspot-answers.yml
```
### Example 2: Trunked System
```yaml
# trunk-p25-answers.yml
system_name: trunk_p25
base_dir: /etc/dvm/trunk
identity: TRUNK_P25
protocol: p25
vc_count: 4
configure_logging: true
log_path: /var/log/dvm
log_root: TRUNK_P25
p25_nac: 0x293
p25_network_id: 1
p25_system_id: 1
```
Usage:
```bash
dvmcfg wizard --type trunk -a trunk-p25-answers.yml
```
### Example 3: Batch Generation
Create multiple configs programmatically:
```bash
#!/bin/bash
for SITE_NUM in {1..5}; do
SITE_ID=$((100000 + SITE_NUM))
SITE_NAME="SITE$(printf '%03d' $SITE_NUM)"
cat > /tmp/site-${SITE_NUM}-answers.yml << EOF
template: enhanced
config_dir: ./site-${SITE_NUM}
system_identity: ${SITE_NAME}
network_peer_id: ${SITE_ID}
configure_logging: true
log_root: ${SITE_NAME}
modem_type: uart
modem_mode: air
EOF
dvmcfg wizard -a /tmp/site-${SITE_NUM}-answers.yml
echo "Generated config for ${SITE_NAME}"
done
```
### Example 4: CI/CD Pipeline
```yaml
# .github/workflows/generate-test-configs.yml
name: Generate Test Configs
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Generate conventional config
run: |
python3 dvmhost/tools/dvmcfggen/dvmcfg.py wizard \
-a examples/conventional-answers.yml
- name: Generate trunked config
run: |
python3 dvmhost/tools/dvmcfggen/dvmcfg.py wizard \
--type trunk \
-a examples/trunked-answers.yml
- name: Validate configs
run: |
python3 dvmhost/tools/dvmcfggen/dvmcfg.py validate -c config.yml
```
## Advanced Usage
### Partial Answers Files
You only need to specify the fields you want to pre-populate. Missing fields will be prompted for interactively:
```yaml
# minimal-answers.yml
system_identity: MYSITE
network_peer_id: 100001
# User will be prompted for other values
```
### Saving Generated Answers
After creating a configuration interactively, you can extract the answers:
```python
from answers_loader import AnswersLoader
from pathlib import Path
# After wizard completes
answers = {
'template': 'enhanced',
'system_identity': 'MYSITE001',
'network_peer_id': 100001,
# ... etc
}
AnswersLoader.save_answers(answers, Path('my-site-answers.yml'))
```
### Validation
Answers files are validated automatically, but you can check them manually:
```python
from answers_loader import AnswersLoader
from pathlib import Path
answers = AnswersLoader.load_answers(Path('my-answers.yml'))
# Non-strict validation (warns about unknown keys)
is_valid = AnswersLoader.validate_answers(answers, strict=False)
# Strict validation (fails on unknown keys)
is_valid = AnswersLoader.validate_answers(answers, strict=True)
```
## Key Features
### ✅ Backward Compatible
- Existing wizard usage works unchanged
- Answers file is completely optional
- No breaking changes
### ✅ Flexible
- Answer any or all questions
- Interactive prompts for missing answers
- Easy to create custom templates
### ✅ Easy to Use
- Simple YAML format
- Clear comments in example files
- Error messages for invalid files
### ✅ Reproducible
- Save and version control answers files
- Generate identical configs across systems
- Document configuration decisions
## Troubleshooting
### "Unrecognized keys in answers file"
This is a warning (not an error) if your answers file contains unknown keys. It won't stop the wizard from running. To suppress this, ensure you're only using valid keys from the lists above.
### "Error loading answers file"
Check that:
1. The file path is correct
2. The file is valid YAML (use `python3 -c "import yaml; yaml.safe_load(open('file.yml'))"`
3. The file has proper indentation (2 spaces per level)
### Answers not being used
Verify that:
1. The key name matches exactly (case-sensitive)
2. The file is being passed with `-a` or `--answers-file`
3. The value type is correct (string, integer, boolean)
## File Reference
### Core Files
- `answers_loader.py` - Utility for loading/validating answers
- `wizard.py` - ConfigWizard and TrunkingWizard classes (refactored)
- `dvmcfg.py` - CLI interface (updated with `--answers-file` support)
### Example Files
- `examples/conventional-answers.yml` - Example for conventional systems
- `examples/trunked-answers.yml` - Example for trunked systems

@ -0,0 +1,194 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
#/**
#* Digital Voice Modem - Host Configuration Generator
#* GPLv2 Open Source. Use is subject to license terms.
#* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#*
#*/
#/*
#* Copyright (C) 2026 by Bryan Biedenkapp N2PLL
#*
#* This program is free software; you can redistribute it and/or modify
#* it under the terms of the GNU General Public License as published by
#* the Free Software Foundation; either version 2 of the License, or
#* (at your option) any later version.
#*
#* This program is distributed in the hope that it will be useful,
#* but WITHOUT ANY WARRANTY; without even the implied warranty of
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#* GNU General Public License for more details.
#*
#* You should have received a copy of the GNU General Public License
#* along with this program; if not, write to the Free Software
#* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#*/
"""
answers_loader.py - Load and validate wizard answers from YAML files
This module handles loading answers files that can be used to pre-populate
wizard defaults, enabling batch configuration generation and automation.
"""
from pathlib import Path
from typing import Dict, Any, Optional
import yaml
from rich.console import Console
console = Console()
class AnswersLoader:
"""Load and validate wizard answers from YAML files"""
# All supported answer keys for ConfigWizard
CONFIG_WIZARD_KEYS = {
'template', 'config_dir', 'system_identity', 'network_peer_id',
'configure_logging', 'log_path', 'activity_log_path', 'log_root',
'use_syslog', 'disable_non_auth_logging', 'modem_type', 'modem_mode',
'serial_port', 'rx_level', 'tx_level', 'rpc_config', 'generate_rpc_password',
'rest_enable', 'generate_rest_password', 'update_lookups', 'save_lookups',
'allow_activity_transfer', 'allow_diagnostic_transfer', 'allow_status_transfer',
'radio_id_file', 'radio_id_time', 'radio_id_acl', 'talkgroup_id_file',
'talkgroup_id_time', 'talkgroup_id_acl', 'dmr_color_code', 'dmr_network_id',
'dmr_site_id', 'dmr_site_model', 'p25_nac', 'p25_network_id', 'p25_system_id',
'p25_rfss_id', 'p25_site_id', 'nxdn_ran', 'nxdn_location_id', 'site_id',
'latitude', 'longitude', 'location', 'tx_power', 'tx_freq', 'band'
}
# All supported answer keys for TrunkingWizard
TRUNKING_WIZARD_KEYS = {
'system_name', 'base_dir', 'identity', 'protocol', 'vc_count',
'configure_logging', 'log_path', 'activity_log_path', 'log_root',
'use_syslog', 'disable_non_auth_logging', 'fne_address', 'fne_port',
'fne_password', 'base_peer_id', 'base_rpc_port', 'modem_type',
'rpc_password', 'generate_rpc_password', 'base_rest_port', 'rest_enable',
'rest_password', 'generate_rest_password', 'update_lookups', 'save_lookups',
'allow_activity_transfer', 'allow_diagnostic_transfer', 'allow_status_transfer',
'radio_id_file', 'radio_id_time', 'radio_id_acl', 'talkgroup_id_file',
'talkgroup_id_time', 'talkgroup_id_acl', 'dmr_color_code', 'dmr_network_id',
'dmr_site_id', 'p25_nac', 'p25_network_id', 'p25_system_id', 'p25_rfss_id',
'p25_site_id', 'site_id', 'cc_tx_freq', 'cc_band', 'vc_tx_freqs', 'vc_bands'
}
ALL_KEYS = CONFIG_WIZARD_KEYS | TRUNKING_WIZARD_KEYS
@staticmethod
def load_answers(filepath: Path) -> Dict[str, Any]:
"""
Load answers from YAML file
Args:
filepath: Path to YAML answers file
Returns:
Dictionary of answers
Raises:
FileNotFoundError: If file doesn't exist
yaml.YAMLError: If file is invalid YAML
"""
try:
with open(filepath, 'r') as f:
answers = yaml.safe_load(f)
if answers is None:
return {}
if not isinstance(answers, dict):
raise ValueError("Answers file must contain a YAML dictionary")
return answers
except FileNotFoundError:
console.print(f"[red]Error: Answers file not found: {filepath}[/red]")
raise
except yaml.YAMLError as e:
console.print(f"[red]Error: Invalid YAML in answers file: {e}[/red]")
raise
@staticmethod
def validate_answers(answers: Dict[str, Any], strict: bool = False) -> bool:
"""
Validate answers file has recognized keys
Args:
answers: Dictionary of answers to validate
strict: If True, fail on unrecognized keys; if False, warn only
Returns:
True if valid, False if invalid (in strict mode)
"""
invalid_keys = set(answers.keys()) - AnswersLoader.ALL_KEYS
if invalid_keys:
msg = f"Unrecognized keys in answers file: {', '.join(sorted(invalid_keys))}"
if strict:
console.print(f"[red]{msg}[/red]")
return False
else:
console.print(f"[yellow]Warning: {msg}[/yellow]")
return True
@staticmethod
def save_answers(answers: Dict[str, Any], filepath: Path) -> None:
"""
Save answers to YAML file
Args:
answers: Dictionary of answers to save
filepath: Path to save answers to
"""
filepath.parent.mkdir(parents=True, exist_ok=True)
with open(filepath, 'w') as f:
yaml.dump(answers, f, default_flow_style=False, sort_keys=False)
@staticmethod
def merge_answers(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
"""
Merge two answers dictionaries (override takes precedence)
Args:
base: Base answers dictionary
override: Override answers dictionary
Returns:
Merged dictionary
"""
merged = base.copy()
merged.update(override)
return merged
@staticmethod
def create_template() -> Dict[str, Any]:
"""
Create a template answers dictionary with common fields
Returns:
Template dictionary with helpful comments
"""
return {
# Wizard type
'template': 'enhanced',
# Basic configuration
'config_dir': '.',
'system_identity': 'SITE001',
'network_peer_id': 100000,
# Logging configuration
'configure_logging': True,
'log_path': '/var/log/dvm',
'activity_log_path': '/var/log/dvm/activity',
'log_root': 'DVM',
'use_syslog': False,
'disable_non_auth_logging': False,
# Modem configuration
'modem_type': 'uart',
'modem_mode': 'air',
'serial_port': '/dev/ttyUSB0',
'rx_level': 50,
'tx_level': 50,
}

@ -39,6 +39,7 @@ from rich import print as rprint
from config_manager import DVMConfig
from templates import TEMPLATES, get_template
from trunking_manager import TrunkingSystem
from answers_loader import AnswersLoader
from wizard import run_wizard, generate_random_password
from version import __BANNER__, __VER__
from iden_table import IdenTable, calculate_channel_assignment, create_iden_entry_from_preset, BAND_PRESETS
@ -225,6 +226,18 @@ def cmd_create(args):
if args.tx_power is not None:
config.set('system.info.power', args.tx_power)
# Handle logging configuration
if args.log_path:
config.set('log.filePath', args.log_path)
if args.activity_log_path:
config.set('log.activityFilePath', args.activity_log_path)
if args.log_root:
config.set('log.fileRoot', args.log_root)
if args.use_syslog is not None:
config.set('log.useSysLog', args.use_syslog)
if args.disable_non_auth_logging is not None:
config.set('log.disableNonAuthoritiveLogging', args.disable_non_auth_logging)
# Handle frequency configuration
iden_table = None
if args.tx_freq and args.band:
@ -535,6 +548,18 @@ def cmd_trunk_create(args):
if args.talkgroup_id_acl is not None:
create_kwargs['talkgroup_id_acl'] = args.talkgroup_id_acl
# Add logging settings
if args.log_path:
create_kwargs['log_path'] = args.log_path
if args.activity_log_path:
create_kwargs['activity_log_path'] = args.activity_log_path
if args.log_root:
create_kwargs['log_root'] = args.log_root
if args.use_syslog is not None:
create_kwargs['use_syslog'] = args.use_syslog
if args.disable_non_auth_logging is not None:
create_kwargs['disable_non_auth_logging'] = args.disable_non_auth_logging
# Add protocol-specific settings
if args.protocol == 'p25':
create_kwargs['nac'] = args.nac
@ -665,8 +690,20 @@ def cmd_list_templates(args):
def cmd_wizard(args):
"""Run interactive wizard"""
answers = {}
# Load answers from file if provided
if hasattr(args, 'answers_file') and args.answers_file:
try:
answers = AnswersLoader.load_answers(args.answers_file)
# Optionally validate answers
AnswersLoader.validate_answers(answers, strict=False)
except Exception as e:
console.print(f"[red]Error loading answers file:[/red] {e}")
sys.exit(1)
wizard_type = args.type if hasattr(args, 'type') else 'auto'
result = run_wizard(wizard_type)
result = run_wizard(wizard_type, answers)
if not result:
sys.exit(1)
@ -784,6 +821,16 @@ DVMHost Configuration Manager"""
create_parser.add_argument('--tx-freq', type=float, help='Transmit frequency in MHz')
create_parser.add_argument('--band', choices=list(BAND_PRESETS.keys()),
help='Frequency band (required with --tx-freq)')
# Logging configuration
create_parser.add_argument('--log-path', help='Log file directory path')
create_parser.add_argument('--activity-log-path', help='Activity log directory path')
create_parser.add_argument('--log-root', help='Log filename prefix and syslog prefix')
create_parser.add_argument('--use-syslog', type=lambda x: x.lower() in ('true', '1', 'yes'),
help='Enable syslog output (true/false)')
create_parser.add_argument('--disable-non-auth-logging', type=lambda x: x.lower() in ('true', '1', 'yes'),
help='Disable non-authoritative logging (true/false)')
create_parser.add_argument('--validate', action='store_true', help='Validate after creation')
create_parser.set_defaults(func=cmd_create)
@ -881,6 +928,16 @@ DVMHost Configuration Manager"""
help='Voice channel TX frequencies (comma-separated, e.g., 851.0125,851.0250)')
trunk_create_parser.add_argument('--vc-bands',
help='Voice channel bands (comma-separated, e.g., 800mhz,800mhz)')
# Logging configuration
trunk_create_parser.add_argument('--log-path', help='Log file directory path')
trunk_create_parser.add_argument('--activity-log-path', help='Activity log directory path')
trunk_create_parser.add_argument('--log-root', help='Log filename prefix and syslog prefix')
trunk_create_parser.add_argument('--use-syslog', type=lambda x: x.lower() in ('true', '1', 'yes'),
help='Enable syslog output (true/false)')
trunk_create_parser.add_argument('--disable-non-auth-logging', type=lambda x: x.lower() in ('true', '1', 'yes'),
help='Disable non-authoritative logging (true/false)')
trunk_create_parser.set_defaults(func=cmd_trunk_create)
# trunk validate
@ -905,6 +962,8 @@ DVMHost Configuration Manager"""
wizard_parser = subparsers.add_parser('wizard', help='Interactive configuration wizard')
wizard_parser.add_argument('--type', choices=['single', 'trunk', 'auto'], default='auto',
help='Wizard type (auto asks user)')
wizard_parser.add_argument('--answers-file', '-a', type=Path,
help='Optional YAML file with wizard answers (uses as defaults)')
wizard_parser.set_defaults(func=cmd_wizard)
args = parser.parse_args()

@ -0,0 +1,87 @@
#
# Digital Voice Modem - Host Configuration Generator
#
# Example answers file for conventional system configuration
# This file can be used with: dvmcfg wizard -a conventional-answers.yml
# All fields are optional; prompts will appear for missing values
# Conventional system with enhanced template
template: enhanced
# Basic Configuration (Step 2)
config_dir: .
system_identity: MYSITE001
network_peer_id: 100001
# Logging Configuration (Step 3)
configure_logging: true
log_path: /var/log/dvm
activity_log_path: /var/log/dvm/activity
log_root: MYSITE
use_syslog: false
disable_non_auth_logging: false
# Modem Configuration (Step 4)
modem_type: uart
modem_mode: air
serial_port: /dev/ttyUSB0
rx_level: 50
tx_level: 50
# Uncomment and modify the sections below as needed:
# RPC Configuration (Step 5a) - optional
# rpc_config: true
# generate_rpc_password: true
# REST API Configuration (Step 5b) - optional
# rest_enable: true
# generate_rest_password: true
# Network Lookups (Step 5c) - optional
# update_lookups: true
# save_lookups: true
# File Transfer Settings (Step 5d) - optional
# allow_activity_transfer: true
# allow_diagnostic_transfer: true
# allow_status_transfer: true
# Radio ID File Settings (Step 6) - optional
# radio_id_file: /path/to/rid.csv
# radio_id_time: 3600
# radio_id_acl: blacklist
# Talkgroup ID File Settings (Step 7) - optional
# talkgroup_id_file: /path/to/talkgroups.csv
# talkgroup_id_time: 3600
# talkgroup_id_acl: whitelist
# Protocol Configuration (Step 8) - optional
# For DMR:
# dmr_color_code: 1
# dmr_network_id: 1
# dmr_site_id: 1
# dmr_site_model: small
# For P25:
# p25_nac: 0x293
# p25_network_id: 1
# p25_system_id: 1
# p25_rfss_id: 1
# p25_site_id: 1
# For NXDN:
# nxdn_ran: 1
# nxdn_location_id: 1
# Frequency Configuration (Step 9) - optional
# site_id: 1
# tx_power: 50
# tx_freq: 453.0125
# band: uhf
# Location Information (Step 10) - optional
# latitude: 40.7128
# longitude: -74.0060
# location: "New York, NY"

@ -0,0 +1,72 @@
#
# Digital Voice Modem - Host Configuration Generator
#
# Example answers file for trunked system configuration
# This file can be used with: dvmcfg wizard --type trunk -a trunked-answers.yml
# All fields are optional; prompts will appear for missing values
# System Configuration (Step 1)
system_name: trunked_test
base_dir: .
identity: TRUNKED001
protocol: p25
vc_count: 2
# Logging Configuration (Step 2)
configure_logging: true
log_path: /var/log/dvm
activity_log_path: /var/log/dvm/activity
log_root: TRUNKED
use_syslog: false
disable_non_auth_logging: false
# Network Settings (Step 3) - optional
# fne_address: 127.0.0.1
# fne_port: 62031
# fne_password: PASSWORD
# Control Channel Settings (Step 4) - optional
# base_peer_id: 100000
# base_rpc_port: 9000
# modem_type: uart
# Voice Channel Settings (Step 5) - optional
# cc_tx_freq: 453.0125
# cc_band: uhf
# vc_tx_freqs: "453.0375,453.0625"
# vc_bands: "uhf,uhf"
# Network/System IDs (Step 6) - optional
# For P25:
# p25_nac: 0x293
# p25_network_id: 1
# p25_system_id: 1
# p25_rfss_id: 1
# p25_site_id: 1
# For DMR:
# dmr_color_code: 1
# dmr_network_id: 1
# dmr_site_id: 1
# RPC Configuration (Step 6a) - optional
# generate_rpc_password: true
# Network Lookups (Step 6b) - optional
# update_lookups: true
# save_lookups: true
# File Transfer Settings (Step 6c) - optional
# allow_activity_transfer: true
# allow_diagnostic_transfer: true
# allow_status_transfer: true
# Radio ID File Settings (Step 7) - optional
# radio_id_file: /path/to/rid.csv
# radio_id_time: 3600
# radio_id_acl: blacklist
# Talkgroup ID File Settings (Step 8) - optional
# talkgroup_id_file: /path/to/talkgroups.csv
# talkgroup_id_time: 3600
# talkgroup_id_acl: whitelist

@ -91,7 +91,12 @@ class TrunkingSystem:
sys_id: int = None,
rfss_id: int = None,
ran: int = None,
vc_channels: List[Dict[str, int]] = None) -> None:
vc_channels: List[Dict[str, int]] = None,
log_path: str = None,
activity_log_path: str = None,
log_root: str = None,
use_syslog: bool = False,
disable_non_auth_logging: bool = False) -> None:
"""
Create a complete trunked system
@ -134,6 +139,11 @@ class TrunkingSystem:
rfss_id: P25 RFSS ID (for P25 systems)
ran: NXDN RAN (for NXDN systems)
vc_channels: List of voice channel configs with optional DFSI settings [{'channel_id': int, 'channel_no': int, 'dfsi_rtrt': int, ...}, ...]
log_path: Log file directory path
activity_log_path: Activity log directory path
log_root: Log filename prefix and syslog prefix
use_syslog: Enable syslog output
disable_non_auth_logging: Disable non-authoritative logging
"""
# If no VC channel info provided, use defaults (same ID as CC, sequential numbers)
@ -199,6 +209,18 @@ class TrunkingSystem:
self.cc_config.set('system.talkgroup_id.file', talkgroup_id_file)
self.cc_config.set('system.talkgroup_id.acl', talkgroup_id_acl)
# Logging settings
if log_path:
self.cc_config.set('log.filePath', log_path)
if activity_log_path:
self.cc_config.set('log.activityFilePath', activity_log_path)
if log_root:
self.cc_config.set('log.fileRoot', log_root)
if use_syslog:
self.cc_config.set('log.useSysLog', use_syslog)
if disable_non_auth_logging:
self.cc_config.set('log.disableNonAuthoritiveLogging', disable_non_auth_logging)
self.cc_config.set('system.modem.protocol.type', modem_type)
# DFSI Configuration for control channel (if provided)
@ -310,6 +332,18 @@ class TrunkingSystem:
vc_config.set('system.talkgroup_id.file', talkgroup_id_file)
vc_config.set('system.talkgroup_id.acl', talkgroup_id_acl)
# Logging settings
if log_path:
vc_config.set('log.filePath', log_path)
if activity_log_path:
vc_config.set('log.activityFilePath', activity_log_path)
if log_root:
vc_config.set('log.fileRoot', log_root)
if use_syslog:
vc_config.set('log.useSysLog', use_syslog)
if disable_non_auth_logging:
vc_config.set('log.disableNonAuthoritiveLogging', disable_non_auth_logging)
vc_config.set('system.config.channelId', channel_id)
vc_config.set('system.config.channelNo', channel_no)
vc_config.set('system.modem.protocol.type', modem_type)

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save

Powered by TurnKey Linux.