specpress
Version:
Export PDF and/or DOCX files from a subset of Markdown, ASN.1 and JSON files
306 lines • 13.3 kB
JavaScript
// DualConnectivity.ts — DC configuration data model & list
import { BaseClass, BaseList, KEYS, UnsupportedKeyException } from "./Utils.js";
import { logger } from "./Logger.js";
import { BC_ID } from "./BC_ID.js";
import { MissingBcIdException } from "./BandCombinations.js";
import { HtmlTable } from "./HtmlTable.js";
import * as path from "node:path";
////////////////////////////////////////////////////////////////
// UlConfigDC //
////////////////////////////////////////////////////////////////
/**
* An uplink configuration entry within a dual connectivity band combination.
*
* Each UlConfigDC identifies one UL component by its BC-ID, and may carry
* optional specification notes.
*/
export class UlConfigDC extends BaseClass {
/** The BC-ID of this UL configuration (parsed or raw string). */
bcId = null;
/** Optional specification notes. */
notes = {};
/**
* @param aValue — a dict from JSON or an existing UlConfigDC to copy.
* @param aParent — the parent DualConnectivityConfig.
* @param validateBcIds — if true, parse bcId strings into BC_ID objects.
*/
constructor(aValue, aParent = null, validateBcIds = true) {
super(aParent);
if (aValue instanceof UlConfigDC) {
this.bcId = aValue.bcId;
if (validateBcIds && typeof this.bcId === "string") {
this.bcId = new BC_ID(this.bcId);
}
this.notes = { ...aValue.notes };
}
else {
for (const oneKey of Object.keys(aValue)) {
if (oneKey === KEYS.bcId) {
this.bcId = String(aValue[KEYS.bcId]).trim();
if (validateBcIds) {
this.bcId = new BC_ID(this.bcId);
}
}
else if (oneKey === KEYS.notes) {
this.notes = { ...aValue[KEYS.notes] };
}
else {
throw new UnsupportedKeyException(`${this.getDescriptor()}: Does not support the given key '${oneKey}' as element`);
}
}
}
}
/** Validates that bcId is present. */
validate() {
if (this.bcId === null) {
throw new Error(`${this.getDescriptor()}: bcId shall not be None`);
}
}
toJSON(anEncoder, aLevel) {
anEncoder.write("{\n", aLevel);
anEncoder.writeKeyAndValue(KEYS.bcId, String(this.bcId), aLevel + 1);
if (Object.keys(this.notes).length > 0) {
anEncoder.writeKeyAndValue(KEYS.notes, this.notes, aLevel + 1, ",\n");
}
anEncoder.write("\n}", aLevel);
}
toString() { return String(this.bcId); }
getTag() { return this.toString(); }
}
////////////////////////////////////////////////////////////////
// DualConnectivityConfig //
////////////////////////////////////////////////////////////////
/**
* A dual connectivity band combination configuration.
*
* Represents one DC entry (EN-DC, NE-DC, or NR-DC) with its DL BC-ID,
* list of UL configurations, and optional flags for single-UL and
* DL-interruptions. Corresponds to one `DC_*.json` file.
*/
export class DualConnectivityConfig extends BaseClass {
/** The DL BC-ID (parsed or raw string). */
bcId = null;
/** List of uplink configurations for this DC combination. */
ulConfigList = [];
/** Whether single UL is allowed (e.g. "yes", "no"), or null if not specified. */
singleUlAllowed = null;
/** Whether DL interruptions are allowed, or null if not specified. */
dlInterruptionsAllowed = null;
/** Optional specification notes. */
notes = {};
/**
* @param aValue — a dict from JSON or an existing DualConnectivityConfig to copy.
* @param aParent — the parent DcBandCombinationList.
* @param validateBcIds_DL — if true, parse the DL bcId string into a BC_ID.
* @param validateBC_IDs_UL — if true, parse UL bcId strings into BC_ID objects.
*/
constructor(aValue, aParent = null, validateBcIds_DL = true, validateBC_IDs_UL = true) {
super(aParent);
if (aValue instanceof DualConnectivityConfig) {
this.bcId = aValue.bcId;
if (validateBcIds_DL && typeof this.bcId === "string") {
this.bcId = new BC_ID(this.bcId);
}
for (const oneElement of aValue.ulConfigList) {
const ul = new UlConfigDC(oneElement, this, validateBC_IDs_UL);
this.ulConfigList.push(ul);
}
this.notes = { ...aValue.notes };
this.singleUlAllowed = aValue.singleUlAllowed;
this.dlInterruptionsAllowed = aValue.dlInterruptionsAllowed;
}
else {
for (const oneKey of Object.keys(aValue)) {
if (oneKey === KEYS.bcId) {
this.bcId = aValue[KEYS.bcId];
if (validateBcIds_DL) {
try {
this.bcId = new BC_ID(this.bcId);
}
catch (e) {
throw new Error(`DualConnectivityConfig('${aValue[KEYS.bcId]}'): ${e.message}`);
}
}
}
else if (oneKey === KEYS.ulConfigList) {
for (const oneElement of aValue[KEYS.ulConfigList]) {
const ul = new UlConfigDC(oneElement, this, validateBC_IDs_UL);
this.ulConfigList.push(ul);
}
}
else if (oneKey === KEYS.notes) {
this.notes = { ...aValue[KEYS.notes] };
}
else if (oneKey === KEYS.singleUlAllowed) {
this.singleUlAllowed = aValue[KEYS.singleUlAllowed];
}
else if (oneKey === KEYS.dlInterruptionsAllowed) {
this.dlInterruptionsAllowed = aValue[KEYS.dlInterruptionsAllowed];
}
else if (oneKey === KEYS.specification || oneKey === KEYS.schemaVersion) {
// Silently ignore
}
else {
throw new UnsupportedKeyException(`${this.getDescriptor()}: Does not support the given key '${oneKey}' as element`);
}
}
}
}
/** Validates that bcId is present. */
validate() {
if (this.bcId === null) {
throw new MissingBcIdException(`${this.getDescriptor()}: A band combination shall contain a 'bcId' key`);
}
}
toString() {
if (this.bcId !== null) {
return this.bcId instanceof BC_ID ? this.bcId.valueOf() : String(this.bcId);
}
return super.toString();
}
getTag() {
if (this.bcId !== null) {
return this.bcId instanceof BC_ID ? this.bcId.valueOf() : String(this.bcId);
}
return super.toString();
}
toJSON(anEncoder, aLevel) {
anEncoder.write("{\n", aLevel);
anEncoder.writeKeyAndValue(KEYS.bcId, String(this.bcId), aLevel + 1, "");
if (this.ulConfigList.length > 0) {
anEncoder.writeKeyAndValue(KEYS.ulConfigList, this.ulConfigList, aLevel + 1, ",\n");
}
if (this.singleUlAllowed !== null) {
anEncoder.writeKeyAndValue(KEYS.singleUlAllowed, this.singleUlAllowed, aLevel + 1, ",\n");
}
if (this.dlInterruptionsAllowed !== null) {
anEncoder.writeKeyAndValue(KEYS.dlInterruptionsAllowed, this.dlInterruptionsAllowed, aLevel + 1, ",\n");
}
if (Object.keys(this.notes).length > 0) {
anEncoder.writeKeyAndValue(KEYS.notes, this.notes, aLevel + 1, ",\n");
}
anEncoder.write("\n}", aLevel);
}
toHTML(aHtmlTable, aRow = 0, aColumn = 0) {
const rowIndexToUse = aHtmlTable.getNrofRows();
// Column 0: DL Configuration
aHtmlTable.setValue(rowIndexToUse, aColumn, String(this.bcId));
// Column 1: UL Configurations (line-break separated)
const ulConfigs = this.ulConfigList.map(ul => String(ul.bcId)).join('<br>');
aHtmlTable.setValue(rowIndexToUse, aColumn + 1, ulConfigs || '–');
// Column 2: Single UL Allowed
aHtmlTable.setValue(rowIndexToUse, aColumn + 2, this.singleUlAllowed || '–');
// Column 3: DL Interruptions Allowed
aHtmlTable.setValue(rowIndexToUse, aColumn + 3, this.dlInterruptionsAllowed || '–');
// Column 4: Notes
const noteKeys = Object.keys(this.notes);
const notesStr = noteKeys.length > 0 ? noteKeys.join(', ') : '–';
aHtmlTable.setValue(rowIndexToUse, aColumn + 4, notesStr);
}
}
////////////////////////////////////////////////////////////////
// DcBandCombinationList //
////////////////////////////////////////////////////////////////
function compareDCs(a, b) {
const aId = a.bcId instanceof BC_ID ? a.bcId : new BC_ID(String(a.bcId));
const bId = b.bcId instanceof BC_ID ? b.bcId : new BC_ID(String(b.bcId));
if (aId.lessThan(bId))
return -1;
if (aId.greaterThan(bId))
return 1;
return 0;
}
/**
* Collection of dual connectivity band combination configurations.
*
* Keyed by BC-ID value string. Supports loading from individual `DC_*.json`
* files, validation, and export to JSON or HTML.
*/
export class DcBandCombinationList extends BaseList {
/** HTML table used for HTML export. */
html;
/** Whether to parse DL BC-IDs into BC_ID objects during loading. */
validatingBC_IDs_DL;
/** Whether to parse UL BC-IDs into BC_ID objects during loading. */
validatingBC_IDs_UL;
constructor(aParent = null, validateBcIds_DL = false, validateBcIds_UL = false) {
super(aParent);
this.validatingBC_IDs_DL = validateBcIds_DL;
this.validatingBC_IDs_UL = validateBcIds_UL;
this.html = new HtmlTable();
}
_createEntry(aValue, aParent) {
return new DualConnectivityConfig(aValue, aParent, this.validatingBC_IDs_DL, this.validatingBC_IDs_UL);
}
_getEntryId(anEntry) {
const bcId = anEntry.bcId;
if (bcId instanceof BC_ID)
return bcId.toString();
return String(bcId);
}
/** Returns true if a DC configuration with the given BC-ID exists. */
hasBC(aBcId) {
return this.has(aBcId);
}
_getTargetSubfolder(anEntry) {
const dc = anEntry;
if (dc.bcId instanceof BC_ID) {
const bcId = dc.bcId;
if (!bcId.isDualConnectivity()) {
throw new Error(`${this.getDescriptor()}: Expected DC configuration but found '${bcId.getPrefix()}${bcId.valueOf()}'`);
}
if (bcId.isNR()) {
if (bcId.isFr1())
return path.join("ts-38.101-1", "NR_Inter-band_DC_FR1");
if (bcId.isFr2())
throw new Error(`${this.getDescriptor()}: Does not expect FR2 DC configurations but found: ${bcId}`);
return path.join("ts-38.101-3", "NR_Inter-band_DC_FR1_and_FR2");
}
else {
if (bcId.isIntraBand())
return path.join("ts-38.101-3", "Intra-band_DC_FR1");
if (bcId.hasFr2())
return path.join("ts-38.101-3", "Inter-band_DC_Including_FR2");
return path.join("ts-38.101-3", "Inter-band_DC_FR1");
}
}
return path.join("ts-38.101-3", "UNKNOWN");
}
_getFileName(anEntry) {
return `${this._getEntryId(anEntry)}.json`;
}
/** Exports all DC configurations to an HTML table file, sorted by BC-ID. */
storeAsHtmlFile(aFileName) {
DcBandCombinationList.addTableHeaders(this.html);
const sorted = [...this.data.values()].sort((a, b) => compareDCs(a, b));
for (const oneBC of sorted) {
oneBC.toHTML(this.html);
}
this.html.dump(aFileName);
logger.log(`${this.getDescriptor()}: Wrote ${this.data.size} BCs to HTML file '${aFileName}'`);
}
/** Adds standard DC table headers to the given HTML table. */
static addTableHeaders(aHtmlTable) {
aHtmlTable.setValue(0, 0, 'DL Configuration');
aHtmlTable.setValue(0, 1, 'UL Configurations');
aHtmlTable.setValue(0, 2, 'Single UL');
aHtmlTable.setValue(0, 3, 'DL Interruptions');
aHtmlTable.setValue(0, 4, 'Notes');
}
/**
* Renders a single DC configuration as a complete HTML table string.
* @param data — Raw JSON object containing DC data
* @returns HTML table string with headers and one DC row
*/
static renderAsHtml(data) {
const dc = new DualConnectivityConfig(data, null, false, false);
const htmlTable = new HtmlTable();
DcBandCombinationList.addTableHeaders(htmlTable);
dc.toHTML(htmlTable);
return htmlTable.toHtmlString();
}
toJSON(anEncoder, aLevel) {
throw new Error("DcBandCombinationList.toJSON() is not implemented");
}
}
//# sourceMappingURL=DualConnectivity.js.map