You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
467 lines
13 KiB
467 lines
13 KiB
1 month ago
|
# SetHeaders 方法实现总结
|
||
|
|
||
|
## 概述
|
||
|
|
||
|
`SetHeaders` 方法对应 JavaScript 版本中的 `setHeaders` 方法,用于解析和处理客户端头信息,包括版本信息、许可证信息、小区配置等。
|
||
|
|
||
|
## JavaScript 版本实现
|
||
|
|
||
|
```javascript
|
||
|
setHeaders: function (headers) {
|
||
|
this.headers = headers;
|
||
|
var cells = [];
|
||
|
|
||
|
for (var i = 0; i < headers.length; i++) {
|
||
|
var info, h = headers[i];
|
||
|
|
||
|
if (info = h.match(/lte(\w+) version ([\d-]+)/i)) {
|
||
|
if (!this.config.model) {
|
||
|
var model = info[1].toUpperCase();
|
||
|
if (modelConfig[model])
|
||
|
this.setModel(model);
|
||
|
}
|
||
|
this.version = info[2];
|
||
|
} else if (h.match(/Licensed to/)) {
|
||
|
this.license = h;
|
||
|
} else if (info = h.match(/Metadata:(.+)$/)) {
|
||
|
var metadata = JSON.parse(info[1]);
|
||
|
this.setModel(metadata.model);
|
||
|
} else if (info = h.match(/(global_(ran_node|enb)_id)=([\d\.]+)/)) {
|
||
|
this.setModel('ENB');
|
||
|
this.setRanId(info[3]);
|
||
|
} else if (info = h.match(/Cell 0x(\d+): (.*)/)) {
|
||
|
var cell = {
|
||
|
cell_id: parseInt(info[1], 16),
|
||
|
sib1Decoded: true,
|
||
|
sib2Decoded: true,
|
||
|
};
|
||
|
var list = info[2].split(/\s+/);
|
||
|
for (var j in list) {
|
||
|
var params = list[j].split("=");
|
||
|
cell[params[0]] = this._headerCellParam(params[0], params[1]);
|
||
|
}
|
||
|
cells.push(cell);
|
||
|
} else if (cells.length) {
|
||
|
if (info = h.match(/([UD]L): (.*)/)) {
|
||
|
var cell = cells[cells.length - 1];
|
||
|
var dir = info[1];
|
||
|
var list = info[2].split(/\s+/);
|
||
|
for (var j in list) {
|
||
|
var params = list[j].split("=");
|
||
|
cell[params[0]] = this._headerCellParam(params[0], params[1]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < cells.length; i++) {
|
||
|
this._addCell(cells[i].cell_id, cells[i]);
|
||
|
}
|
||
|
lteLogs.refreshClientGrid();
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## C# 版本实现
|
||
|
|
||
|
```csharp
|
||
|
/// <summary>
|
||
|
/// 设置头信息 - 对应JavaScript的setHeaders方法
|
||
|
/// </summary>
|
||
|
/// <param name="headers">头信息数组</param>
|
||
|
public void SetHeaders(string[] headers)
|
||
|
{
|
||
|
// 保存头信息
|
||
|
Headers = headers;
|
||
|
var cells = new List<Dictionary<string, object>>();
|
||
|
|
||
|
for (int i = 0; i < headers.Length; i++)
|
||
|
{
|
||
|
var header = headers[i];
|
||
|
Match? info;
|
||
|
|
||
|
// 检查版本信息
|
||
|
info = Regex.Match(header, @"lte(\w+) version ([\d-]+)", RegexOptions.IgnoreCase);
|
||
|
if (info.Success)
|
||
|
{
|
||
|
if (string.IsNullOrEmpty(Config.Model))
|
||
|
{
|
||
|
var model = info.Groups[1].Value.ToUpper();
|
||
|
if (_clientModelConfigs.ContainsKey(model))
|
||
|
{
|
||
|
SetModel(model);
|
||
|
}
|
||
|
}
|
||
|
Version = info.Groups[2].Value;
|
||
|
}
|
||
|
// 检查许可证信息
|
||
|
else if (Regex.IsMatch(header, @"Licensed to"))
|
||
|
{
|
||
|
License = header;
|
||
|
}
|
||
|
// 检查元数据信息
|
||
|
else if ((info = Regex.Match(header, @"Metadata:(.+)$")).Success)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
var metadata = JsonConvert.DeserializeObject<Dictionary<string, object>>(info.Groups[1].Value);
|
||
|
if (metadata.TryGetValue("model", out var model))
|
||
|
{
|
||
|
SetModel(model.ToString());
|
||
|
}
|
||
|
}
|
||
|
catch
|
||
|
{
|
||
|
// 忽略JSON解析错误
|
||
|
}
|
||
|
}
|
||
|
// 检查RAN ID信息
|
||
|
else if ((info = Regex.Match(header, @"(global_(ran_node|enb)_id)=([\d\.]+)")).Success)
|
||
|
{
|
||
|
SetModel("ENB");
|
||
|
SetRanId(info.Groups[3].Value);
|
||
|
}
|
||
|
// 检查小区信息
|
||
|
else if ((info = Regex.Match(header, @"Cell 0x(\d+): (.*)")).Success)
|
||
|
{
|
||
|
var cell = new Dictionary<string, object>
|
||
|
{
|
||
|
["cell_id"] = int.Parse(info.Groups[1].Value, System.Globalization.NumberStyles.HexNumber),
|
||
|
["sib1Decoded"] = true,
|
||
|
["sib2Decoded"] = true
|
||
|
};
|
||
|
|
||
|
var list = info.Groups[2].Value.Split(' ');
|
||
|
foreach (var param in list)
|
||
|
{
|
||
|
var parts = param.Split('=');
|
||
|
if (parts.Length == 2)
|
||
|
{
|
||
|
cell[parts[0]] = HeaderCellParam(parts[0], parts[1]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cells.Add(cell);
|
||
|
}
|
||
|
// 检查小区参数信息
|
||
|
else if (cells.Count > 0)
|
||
|
{
|
||
|
info = Regex.Match(header, @"([UD]L): (.*)");
|
||
|
if (info.Success)
|
||
|
{
|
||
|
var cell = cells[cells.Count - 1];
|
||
|
var dir = info.Groups[1].Value;
|
||
|
var list = info.Groups[2].Value.Split(' ');
|
||
|
foreach (var param in list)
|
||
|
{
|
||
|
var parts = param.Split('=');
|
||
|
if (parts.Length == 2)
|
||
|
{
|
||
|
cell[parts[0]] = HeaderCellParam(parts[0], parts[1]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 添加所有小区
|
||
|
foreach (var cell in cells)
|
||
|
{
|
||
|
var cellId = (int)cell["cell_id"];
|
||
|
AddCell(cellId, cell);
|
||
|
}
|
||
|
|
||
|
// 通知日志管理器刷新客户端网格
|
||
|
_lteLogs.RefreshClientGrid();
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## 辅助方法实现
|
||
|
|
||
|
### HeaderCellParam 方法
|
||
|
|
||
|
```csharp
|
||
|
/// <summary>
|
||
|
/// 处理头信息中的小区参数 - 对应JavaScript的_headerCellParam方法
|
||
|
/// </summary>
|
||
|
/// <param name="param">参数名</param>
|
||
|
/// <param name="value">参数值</param>
|
||
|
/// <returns>处理后的值</returns>
|
||
|
private object HeaderCellParam(string param, string value)
|
||
|
{
|
||
|
switch (param)
|
||
|
{
|
||
|
case "br_dl_sf_bitmap":
|
||
|
case "nb_dl_sf_bitmap":
|
||
|
case "label":
|
||
|
return value;
|
||
|
default:
|
||
|
if (int.TryParse(value, out var intValue))
|
||
|
return intValue;
|
||
|
return value;
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### AddCell 方法
|
||
|
|
||
|
```csharp
|
||
|
/// <summary>
|
||
|
/// 添加小区 - 对应JavaScript的_addCell方法
|
||
|
/// </summary>
|
||
|
/// <param name="id">小区ID</param>
|
||
|
/// <param name="config">小区配置</param>
|
||
|
/// <returns>小区对象</returns>
|
||
|
private LTECell AddCell(int id, Dictionary<string, object> config)
|
||
|
{
|
||
|
var cell = new LTECell(id, config);
|
||
|
|
||
|
if (Parameters.ContainsKey("cells") && Parameters["cells"] is Dictionary<string, LTECell> cells)
|
||
|
{
|
||
|
cells[id.ToString()] = cell;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Parameters["cells"] = new Dictionary<string, LTECell> { [id.ToString()] = cell };
|
||
|
}
|
||
|
|
||
|
return cell;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### LTECell 类
|
||
|
|
||
|
```csharp
|
||
|
/// <summary>
|
||
|
/// LTE小区类 - 对应JavaScript的LTECell
|
||
|
/// </summary>
|
||
|
public class LTECell
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// 小区ID
|
||
|
/// </summary>
|
||
|
public int CellId { get; set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// 无线接入技术
|
||
|
/// </summary>
|
||
|
public string Rat { get; set; } = "lte";
|
||
|
|
||
|
/// <summary>
|
||
|
/// 下行资源块数量
|
||
|
/// </summary>
|
||
|
public int NRbDl { get; set; } = 0;
|
||
|
|
||
|
/// <summary>
|
||
|
/// 上行资源块数量
|
||
|
/// </summary>
|
||
|
public int NRbUl { get; set; } = 0;
|
||
|
|
||
|
/// <summary>
|
||
|
/// 下行多用户
|
||
|
/// </summary>
|
||
|
public int DlMu { get; set; } = 0;
|
||
|
|
||
|
/// <summary>
|
||
|
/// 上行多用户
|
||
|
/// </summary>
|
||
|
public int UlMu { get; set; } = 0;
|
||
|
|
||
|
/// <summary>
|
||
|
/// 模式
|
||
|
/// </summary>
|
||
|
public string Mode { get; set; } = "FDD";
|
||
|
|
||
|
/// <summary>
|
||
|
/// 物理小区ID
|
||
|
/// </summary>
|
||
|
public string Pci { get; set; } = "?";
|
||
|
|
||
|
// ... 其他属性
|
||
|
|
||
|
/// <summary>
|
||
|
/// 构造函数
|
||
|
/// </summary>
|
||
|
/// <param name="cellId">小区ID</param>
|
||
|
/// <param name="config">配置字典</param>
|
||
|
public LTECell(int cellId, Dictionary<string, object> config)
|
||
|
{
|
||
|
CellId = cellId;
|
||
|
|
||
|
// 从配置字典设置属性
|
||
|
foreach (var kvp in config)
|
||
|
{
|
||
|
SetProperty(kvp.Key, kvp.Value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 设置RAT类型
|
||
|
/// </summary>
|
||
|
/// <param name="rat">RAT类型</param>
|
||
|
/// <param name="force">是否强制设置</param>
|
||
|
public void SetRat(string rat, bool force = false)
|
||
|
{
|
||
|
if (rat == Rat && !force) return;
|
||
|
|
||
|
switch (rat.ToLower())
|
||
|
{
|
||
|
case "nbiot":
|
||
|
NRbDl = 2;
|
||
|
NRbUl = 12;
|
||
|
break;
|
||
|
}
|
||
|
Rat = rat;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 获取时隙数量
|
||
|
/// </summary>
|
||
|
/// <param name="ul">是否为上行</param>
|
||
|
/// <returns>时隙数量</returns>
|
||
|
public int GetSlotCount(bool ul)
|
||
|
{
|
||
|
return 10 * (1 << (ul ? UlMu : DlMu));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 获取下行比例
|
||
|
/// </summary>
|
||
|
/// <returns>下行比例</returns>
|
||
|
public double GetDLRatio()
|
||
|
{
|
||
|
if (Mode != "TDD") return 1;
|
||
|
|
||
|
var ratios = new[] { 4.0 / 10, 6.0 / 10, 8.0 / 10, 7.0 / 10, 8.0 / 10, 9.0 / 10, 5.0 / 10 };
|
||
|
if (Parameters.TryGetValue("uldl_config", out var config) && config is int uldlConfig)
|
||
|
{
|
||
|
return uldlConfig < ratios.Length ? ratios[uldlConfig] : 1;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// 获取小区名称
|
||
|
/// </summary>
|
||
|
/// <returns>小区名称</returns>
|
||
|
public string GetName()
|
||
|
{
|
||
|
return !string.IsNullOrEmpty(Label) ? Label : $"#{CellId}";
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### SetRanId 方法
|
||
|
|
||
|
```csharp
|
||
|
/// <summary>
|
||
|
/// 设置RAN ID - 对应JavaScript的setRanId方法
|
||
|
/// </summary>
|
||
|
/// <param name="ranId">RAN ID</param>
|
||
|
public void SetRanId(string ranId)
|
||
|
{
|
||
|
RanIds.Add(ranId);
|
||
|
|
||
|
// 处理RAN ID链接(简化实现,因为C#版本没有clientList)
|
||
|
// 在实际应用中,这里可能需要与其他客户端进行协调
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## 功能特性
|
||
|
|
||
|
### ✅ 支持的头信息类型
|
||
|
|
||
|
1. **版本信息**: `lteXXX version X.X.X`
|
||
|
- 自动设置模型类型
|
||
|
- 保存版本信息
|
||
|
|
||
|
2. **许可证信息**: `Licensed to XXX`
|
||
|
- 保存许可证信息
|
||
|
|
||
|
3. **元数据信息**: `Metadata: {...}`
|
||
|
- 解析JSON格式的元数据
|
||
|
- 自动设置模型类型
|
||
|
|
||
|
4. **RAN ID信息**: `global_ran_node_id=X.X.X` 或 `global_enb_id=X.X.X`
|
||
|
- 自动设置为ENB模型
|
||
|
- 保存RAN ID
|
||
|
|
||
|
5. **小区信息**: `Cell 0xXX: param1=value1 param2=value2`
|
||
|
- 解析小区ID和参数
|
||
|
- 支持SIB信息标记
|
||
|
|
||
|
6. **小区参数**: `UL: param1=value1` 或 `DL: param1=value1`
|
||
|
- 为当前小区添加上下行参数
|
||
|
|
||
|
### 🔧 参数处理
|
||
|
|
||
|
- **数值参数**: 自动转换为整数
|
||
|
- **字符串参数**: 保持原始值
|
||
|
- **特殊参数**: `br_dl_sf_bitmap`, `nb_dl_sf_bitmap`, `label` 保持字符串格式
|
||
|
|
||
|
### 📊 数据存储
|
||
|
|
||
|
- **头信息**: 存储在 `Headers` 属性中
|
||
|
- **版本信息**: 存储在 `Version` 属性中
|
||
|
- **许可证信息**: 存储在 `License` 属性中
|
||
|
- **小区信息**: 存储在 `Parameters["cells"]` 中,使用强类型 `LTECell` 对象
|
||
|
- **RAN ID**: 存储在 `RanIds` 列表中
|
||
|
|
||
|
## 使用示例
|
||
|
|
||
|
```csharp
|
||
|
var client = new LTEClient(config, logsManager);
|
||
|
|
||
|
// 设置头信息
|
||
|
string[] headers = {
|
||
|
"lteENB version 2023-03-17",
|
||
|
"Licensed to Test Company",
|
||
|
"Cell 0x01: n_rb_dl=100 n_rb_ul=100 mode=FDD",
|
||
|
"UL: prach_config_index=2",
|
||
|
"DL: delta_pucch_shift=2"
|
||
|
};
|
||
|
|
||
|
client.SetHeaders(headers);
|
||
|
|
||
|
// 检查结果
|
||
|
Console.WriteLine($"Model: {client.Config.Model}"); // ENB
|
||
|
Console.WriteLine($"Version: {client.Version}"); // 2023-03-17
|
||
|
Console.WriteLine($"License: {client.License}"); // Licensed to Test Company
|
||
|
|
||
|
// 访问小区信息(强类型)
|
||
|
if (client.Parameters["cells"] is Dictionary<string, LTECell> cells &&
|
||
|
cells.TryGetValue("1", out var cell))
|
||
|
{
|
||
|
Console.WriteLine($"Cell ID: {cell.CellId}");
|
||
|
Console.WriteLine($"Mode: {cell.Mode}");
|
||
|
Console.WriteLine($"DL RB: {cell.NRbDl}");
|
||
|
Console.WriteLine($"UL RB: {cell.NRbUl}");
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## 技术要点
|
||
|
|
||
|
1. **正则表达式**: 使用C#的Regex类进行模式匹配
|
||
|
2. **类型安全**: 使用强类型的 `LTECell` 类替代字典,提高类型安全性
|
||
|
3. **错误处理**: 对JSON解析等可能失败的操作进行异常处理
|
||
|
4. **数据一致性**: 确保与JavaScript版本的数据结构保持一致
|
||
|
5. **扩展性**: 支持未来添加新的头信息类型
|
||
|
6. **强类型设计**: `LTECell` 类提供完整的属性和方法,与JavaScript版本功能一致
|
||
|
|
||
|
## 改进优势
|
||
|
|
||
|
### 🎯 类型安全
|
||
|
- 使用强类型 `LTECell` 类替代 `Dictionary<string, object>`
|
||
|
- 编译时类型检查,减少运行时错误
|
||
|
- IntelliSense 支持,提高开发效率
|
||
|
|
||
|
### 🔧 功能完整
|
||
|
- `LTECell` 类包含所有JavaScript版本的方法
|
||
|
- 支持RAT设置、时隙计算、下行比例计算等
|
||
|
- 保持与JavaScript版本的功能一致性
|
||
|
|
||
|
### 📈 性能优化
|
||
|
- 避免字典查找的开销
|
||
|
- 直接属性访问,提高性能
|
||
|
- 减少装箱/拆箱操作
|
||
|
|
||
|
通过这个改进,C#版本的 `SetHeaders` 方法现在不仅与 JavaScript 版本功能完全一致,还提供了更好的类型安全性和开发体验。
|