Compare commits

...

5 Commits

Author SHA1 Message Date
root e7ba31d2db 1 1 month ago
root 3609b23b0d 1 1 month ago
root 4f88ce566c 1 1 month ago
root 0fc23d1828 1 1 month ago
root b04d17c01d feat: 全面重构为强类型模型,提升代码健壮性 1 month ago
  1. 24
      LTEMvcApp/Controllers/HomeController.cs
  2. 15
      LTEMvcApp/Controllers/WebSocketController.cs
  3. 99
      LTEMvcApp/Models/LTEClient.cs
  4. 145
      LTEMvcApp/Services/LTEClientWebSocket.cs
  5. 40
      LTEMvcApp/Services/WebSocketManagerService.cs
  6. 8
      LTEMvcApp/Views/Home/Index.cshtml
  7. 212
      LTEMvcApp/Views/Home/Logs.cshtml
  8. 32
      LTEMvcApp/Views/Home/TestClientConfig.cshtml

24
LTEMvcApp/Controllers/HomeController.cs

@ -77,27 +77,15 @@ public class HomeController : Controller
Enabled = true,
ReconnectDelay = 5000,
Password = "test123",
Logs = new Dictionary<string, object>
Logs = new ClientLogsConfig
{
["layers"] = new Dictionary<string, object>
Layers = new Dictionary<string, LogLayerConfig>
{
["PHY"] = new Dictionary<string, object>
{
["level"] = "debug",
["filter"] = "debug",
["max_size"] = 1000,
["payload"] = false
},
["RRC"] = new Dictionary<string, object>
{
["level"] = "debug",
["filter"] = "debug",
["max_size"] = 1000,
["payload"] = false
}
["PHY"] = new LogLayerConfig { Level = "debug", Filter = "debug", MaxSize = 1000, Payload = false },
["RRC"] = new LogLayerConfig { Level = "debug", Filter = "debug", MaxSize = 1000, Payload = false }
},
["signal"] = true,
["cch"] = true
Signal = true,
Cch = true
}
};

15
LTEMvcApp/Controllers/WebSocketController.cs

@ -170,16 +170,19 @@ namespace LTEMvcApp.Controllers
/// 设置客户端日志配置
/// </summary>
/// <param name="clientName">客户端名称</param>
/// <param name="request">日志配置请求</param>
/// <returns>操作结果</returns>
/// <param name="request">请求体</param>
[HttpPost("clients/{clientName}/logs-config")]
public ActionResult SetClientLogsConfig(string clientName, [FromBody] LogsConfigRequest request)
public ActionResult SetClientLogsConfig(string clientName, [FromBody] ClientLogsConfig request)
{
var success = _webSocketManager.SetClientLogsConfig(clientName, request.Config, request.Save);
var success = _webSocketManager.SetClientLogsConfig(clientName, request);
if (success)
return Ok(new { message = $"客户端 '{clientName}' 日志配置已更新" });
{
return Ok(new { message = "日志配置已更新" });
}
else
return BadRequest($"更新客户端 '{clientName}' 日志配置失败");
{
return NotFound(new { message = $"客户端 '{clientName}' 未找到或更新失败" });
}
}
/// <summary>

99
LTEMvcApp/Models/LTEClient.cs

@ -1,5 +1,6 @@
using System.Collections.Immutable;
using System.Text.RegularExpressions;
using System.Text.Json.Serialization;
namespace LTEMvcApp.Models;
@ -45,6 +46,11 @@ public class LTEClient
/// </summary>
public string? Model { get; set; }
/// <summary>
/// 是否有信号记录
/// </summary>
public bool HasSignalRecord { get; set; }
#endregion
#region 日志相关属性
@ -147,11 +153,6 @@ public class LTEClient
/// </summary>
public bool HasRb { get; set; }
/// <summary>
/// 是否有信号记录
/// </summary>
public bool HasSignalRecord { get; set; }
#endregion
#region 参数和组件
@ -366,24 +367,100 @@ public class LTEClient
#endregion
}
/// <summary>
/// 客户端日志配置
/// </summary>
public class ClientLogsConfig
{
/// <summary>
/// 日志层配置
/// </summary>
public Dictionary<string, LogLayerConfig> Layers { get; set; } = new();
/// <summary>
/// 是否启用信号日志
/// </summary>
public bool? Signal { get; set; }
/// <summary>
/// 是否启用控制信道日志
/// </summary>
public bool? Cch { get; set; }
// 允许其他未明确定义的属性
[JsonExtensionData]
public Dictionary<string, object>? ExtensionData { get; set; }
}
/// <summary>
/// 客户端配置
/// </summary>
public class ClientConfig
{
/// <summary>
/// 客户端名称
/// </summary>
public string Name { get; set; } = string.Empty;
public bool Enabled { get; set; } = true;
public string? Model { get; set; }
public string? Address { get; set; }
/// <summary>
/// 服务器地址
/// </summary>
public string Address { get; set; } = string.Empty;
/// <summary>
/// 是否启用
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; } = string.Empty;
/// <summary>
/// 重连延迟(毫秒)
/// </summary>
public int ReconnectDelay { get; set; } = 5000;
/// <summary>
/// 是否启用SSL
/// </summary>
public bool Ssl { get; set; }
public int ReconnectDelay { get; set; } = 15000;
/// <summary>
/// 日志配置
/// </summary>
public ClientLogsConfig Logs { get; set; } = new();
/// <summary>
/// 是否暂停
/// </summary>
public bool Pause { get; set; }
/// <summary>
/// 是否只读
/// </summary>
public bool Readonly { get; set; }
/// <summary>
/// 是否跳过日志菜单
/// </summary>
public bool SkipLogMenu { get; set; }
/// <summary>
/// 是否锁定
/// </summary>
public bool Locked { get; set; }
/// <summary>
/// 是否激活
/// </summary>
public bool Active { get; set; }
public string? Password { get; set; }
public Dictionary<string, object> Logs { get; set; } = new();
/// <summary>
/// 模型
/// </summary>
public string? Model { get; set; }
}
/// <summary>

145
LTEMvcApp/Services/LTEClientWebSocket.cs

@ -10,6 +10,7 @@ using WebSocket4Net;
using LTEMvcApp.Models;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace LTEMvcApp.Services
{
@ -233,66 +234,35 @@ namespace LTEMvcApp.Services
/// <summary>
/// 设置日志配置
/// </summary>
/// <param name="config">日志配置</param>
/// <param name="save">是否保存</param>
public void SetLogsConfig(Dictionary<string, object> config, bool save = false)
public void SetLogsConfig(ClientLogsConfig logsConfig, bool save = false)
{
// 重新格式化配置
var logs = new JObject();
foreach (var kvp in config)
var logsPayload = new JObject();
var layersPayload = new JObject();
foreach (var layerKvp in logsConfig.Layers)
{
switch (kvp.Key)
layersPayload[layerKvp.Key] = JObject.FromObject(new
{
case "layers":
var layers = new JObject();
var layersDict = kvp.Value as Dictionary<string, object>;
if (layersDict != null)
{
foreach (var layer in layersDict)
{
var layerConfig = layer.Value as Dictionary<string, object>;
if (layerConfig != null)
{
layers[layer.Key] = new JObject
{
["level"] = layerConfig.GetValueOrDefault("level", "warn").ToString(),
["max_size"] = Convert.ToInt32(layerConfig.GetValueOrDefault("max_size", 0)),
["payload"] = Convert.ToBoolean(layerConfig.GetValueOrDefault("payload", false))
};
}
}
}
logs["layers"] = layers;
break;
default:
if (IsLogParameter(kvp.Key))
{
logs[kvp.Key] = JToken.FromObject(kvp.Value);
}
break;
}
level = layerKvp.Value.Level,
maxSize = layerKvp.Value.MaxSize,
payload = layerKvp.Value.Payload,
filter = layerKvp.Value.Filter
});
}
logsPayload["layers"] = layersPayload;
if (logsConfig.Signal.HasValue) logsPayload["signal"] = logsConfig.Signal.Value;
if (logsConfig.Cch.HasValue) logsPayload["cch"] = logsConfig.Cch.Value;
SendMessage(new JObject
{
["message"] = "config_set",
["logs"] = logs
["logs"] = logsPayload
}, response =>
{
if (save)
{
// 保存配置
foreach (var kvp in config)
{
if (_config.Logs.ContainsKey(kvp.Key))
{
_config.Logs[kvp.Key] = kvp.Value;
}
else
{
_config.Logs.Add(kvp.Key, kvp.Value);
}
}
_config.Logs = logsConfig;
}
LogGet(new Dictionary<string, object> { ["timeout"] = 0 });
}, true);
@ -392,36 +362,15 @@ namespace LTEMvcApp.Services
/// <param name="parameters">参数</param>
public void LogGet(Dictionary<string, object>? parameters = null)
{
var config = _config;
if (config.Pause)
if (_config.Pause)
return;
var layers = new JObject();
if (config.Logs.ContainsKey("layers"))
if (_config.Logs?.Layers != null)
{
// 处理layers配置,确保键是字符串类型
if (config.Logs["layers"] is JObject layersJObject)
{
foreach (var layer in layersJObject)
{
var layerName = layer.Key; // 这里layer.Key已经是字符串
var layerConfig = layer.Value as JObject;
if (layerConfig != null && layerConfig.ContainsKey("filter"))
{
layers[layerName] = layerConfig["filter"].ToString();
}
}
}
else if (config.Logs["layers"] is Dictionary<string, object> layersDict)
foreach (var layerKvp in _config.Logs.Layers)
{
foreach (var layer in layersDict)
{
var layerConfig = layer.Value as Dictionary<string, object>;
if (layerConfig != null && layerConfig.ContainsKey("filter"))
{
layers[layer.Key] = layerConfig["filter"].ToString();
}
}
layers[layerKvp.Key] = layerKvp.Value.Filter;
}
}
@ -565,7 +514,7 @@ namespace LTEMvcApp.Services
_messageFifo.Clear();
// 检查当前配置
var firstCon = !_config.Logs.ContainsKey("layers");
var firstCon = _config.Logs.Layers.Count == 0;
// 获取配置
SendMessage(new JObject { ["message"] = "config_get" }, config =>
@ -585,54 +534,16 @@ namespace LTEMvcApp.Services
}
var ro = _config.Readonly;
if (ro || firstCon)
{
_config.Logs = config["logs"]?.ToObject<Dictionary<string, object>>() ?? new Dictionary<string, object>();
}
else
{
var serverLogs = config["logs"]?.ToObject<Dictionary<string, object>>() ?? new Dictionary<string, object>();
foreach (var kvp in serverLogs)
{
if (!_config.Logs.ContainsKey(kvp.Key))
{
_config.Logs[kvp.Key] = kvp.Value;
}
}
}
var serverLogsConfig = config["logs"]?.ToObject<ClientLogsConfig>(Newtonsoft.Json.JsonSerializer.CreateDefault(new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
// 清理和设置层配置
if (_config.Logs.ContainsKey("layers"))
if (ro || firstCon)
{
var layers = _config.Logs["layers"] as Dictionary<string, object>;
var configLayers = config["logs"]?["layers"]?.ToObject<Dictionary<string, object>>();
if (layers != null && configLayers != null)
if (serverLogsConfig != null)
{
var keysToRemove = layers.Keys.Where(k => !configLayers.ContainsKey(k)).ToList();
foreach (var key in keysToRemove)
{
layers.Remove(key);
}
foreach (var layer in layers)
{
var layerConfig = layer.Value as Dictionary<string, object>;
if (layerConfig != null && !layerConfig.ContainsKey("filter"))
{
if (ro)
{
layerConfig["filter"] = layerConfig.GetValueOrDefault("level", "warn").ToString();
}
else
{
layerConfig["filter"] = GetDefaultFilter(layer.Key);
}
}
}
_config.Logs = serverLogsConfig;
}
}
// 添加小区信息
if (config["cells"] != null)
{

40
LTEMvcApp/Services/WebSocketManagerService.cs

@ -120,6 +120,19 @@ namespace LTEMvcApp.Services
private ClientConfig GetDefaultTestConfig()
{
var layers = new Dictionary<string, LogLayerConfig>();
foreach(var layerName in LogLayerTypes.AllLayers.Where(l => l != "EVENT"))
{
layers[layerName] = new LogLayerConfig { Level = LogLayerTypes.GetDefaultLevel(layerName), Filter = "warn", MaxSize = 1000, Payload = false };
}
// Set some specific payloads to true
if(layers.ContainsKey("PHY")) layers["PHY"].Payload = true;
if(layers.ContainsKey("MAC")) layers["MAC"].Payload = true;
if(layers.ContainsKey("RRC")) layers["RRC"].Payload = true;
if(layers.ContainsKey("NAS")) layers["NAS"].Payload = true;
return new ClientConfig
{
Name = "TestClient",
@ -128,25 +141,11 @@ namespace LTEMvcApp.Services
Ssl = false,
ReconnectDelay = 15000,
Password = "test123",
Logs = new Dictionary<string, object>
Logs = new ClientLogsConfig
{
["layers"] = new Dictionary<string, object>
{
["PHY"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = true },
["MAC"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = true },
["RLC"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = false },
["PDCP"] = new LogLayerConfig { Level = "warn", Filter = "warn", MaxSize = 1000, Payload = false },
["RRC"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = true },
["NAS"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = true },
["S1AP"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = false },
["NGAP"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = false },
["GTPU"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = false },
["X2AP"] = new LogLayerConfig { Level = "debug", Filter = "warn", MaxSize = 1000, Payload = false },
["XnAP"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = false },
["M2AP"] = new LogLayerConfig { Level = "info", Filter = "warn", MaxSize = 1000, Payload = false }
},
["signal"] = true,
["cch"] = true
Layers = layers,
Signal = true,
Cch = true
}
};
}
@ -335,13 +334,12 @@ namespace LTEMvcApp.Services
/// </summary>
/// <param name="clientName">客户端名称</param>
/// <param name="logsConfig">日志配置</param>
/// <param name="save">是否保存</param>
/// <returns>是否成功设置</returns>
public bool SetClientLogsConfig(string clientName, Dictionary<string, object> logsConfig, bool save = false)
public bool SetClientLogsConfig(string clientName, ClientLogsConfig logsConfig)
{
if (_clients.TryGetValue(clientName, out var client))
{
client.SetLogsConfig(logsConfig, save);
client.SetLogsConfig(logsConfig, true);
return true;
}
return false;

8
LTEMvcApp/Views/Home/Index.cshtml

@ -34,8 +34,8 @@
var state = (LTEMvcApp.Models.ClientState)client.State;
<tr>
<td><a>@config.Name</a></td>
<td><small>@((config.Ssl ? "wss://" : "ws://") + config.Address)</small></td>
<td>@client.Config.Name</td>
<td>@client.Config.Address</td>
<td>
@if (state == LTEMvcApp.Models.ClientState.Connected)
{
@ -87,8 +87,8 @@
<i class="fas fa-list"></i>
消息
</a>
<a class="btn btn-secondary btn-sm" href="@Url.Action("TestClientConfig", "Home")">
<i class="fas fa-cogs"></i>
<a href="@Url.Action("TestClientConfig", "Home")" class="btn btn-sm btn-warning">
<i class="fas fa-cog"></i>
配置
</a>
</td>

212
LTEMvcApp/Views/Home/Logs.cshtml

@ -263,6 +263,85 @@
margin-bottom: 10px;
display: none;
}
/* Layer过滤器样式 */
.layer-filter-section {
background-color: #f8f9fa;
border-bottom: 1px solid #dee2e6;
padding: 8px 10px;
}
.filter-header-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.filter-label {
font-size: 0.9em;
font-weight: bold;
color: #495057;
}
.select-all-label {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.8em;
cursor: pointer;
color: #007bff;
}
.select-all-label:hover {
color: #0056b3;
}
.select-all-label input {
margin: 0;
}
.filter-options-row {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.filter-option {
display: flex;
align-items: center;
gap: 4px;
}
.filter-option label {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.8em;
cursor: pointer;
padding: 2px 6px;
border-radius: 3px;
transition: background-color 0.2s;
}
.filter-option label:hover {
background-color: #e9ecef;
}
.filter-option input {
margin: 0;
}
.filter-option input:checked + span {
font-weight: bold;
color: #007bff;
}
.log-layer {
display: flex;
align-items: center;
flex: 0 0 100px;
}
</style>
<div class="log-container">
@ -279,6 +358,19 @@
<span class="log-info">Info</span>
</div>
<!-- Layer过滤器区域 -->
<div class="layer-filter-section">
<div class="filter-header-row">
<span class="filter-label">Layer过滤:</span>
<label class="select-all-label">
<input type="checkbox" id="select-all-layers"> 全选
</label>
</div>
<div class="filter-options-row" id="layer-filter-options">
<!-- 选项将动态生成 -->
</div>
</div>
<div id="log-scroll-area">
<div id="log-content-area"></div>
</div>
@ -333,8 +425,12 @@
const reconnectBtn = document.getElementById('reconnect-btn');
const logListPanel = document.querySelector('.log-list-panel');
const resizer = document.getElementById('drag-resizer');
const layerFilterOptions = document.getElementById('layer-filter-options');
const selectAllLayers = document.getElementById('select-all-layers');
let allLogsData = [];
let availableLayers = new Set(['PHY', 'MAC', 'RLC', 'PDCP', 'RRC', 'NAS']); // 初始化标准LTE层
let selectedLayers = new Set(); // 用于跟踪选中的日志层
let eventSource = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
@ -350,6 +446,81 @@
no_data_text: "正在等待日志..."
});
// 更新全选复选框状态
function updateSelectAllState() {
if (availableLayers.size === 0) {
selectAllLayers.checked = false;
selectAllLayers.indeterminate = false;
} else if (selectedLayers.size === 0) {
selectAllLayers.checked = false;
selectAllLayers.indeterminate = false;
} else if (selectedLayers.size === availableLayers.size) {
selectAllLayers.checked = true;
selectAllLayers.indeterminate = false;
} else {
selectAllLayers.checked = false;
selectAllLayers.indeterminate = true;
}
}
// 更新Layer过滤器选项
function updateLayerFilter() {
const options = [];
// 按字母顺序排序
const sortedLayers = Array.from(availableLayers).sort();
sortedLayers.forEach(layer => {
const isChecked = selectedLayers.has(layer) ? ' checked' : '';
options.push(`<div class="filter-option">
<label>
<input type="checkbox" value="${layer}"${isChecked}>
<span>${layer}</span>
</label>
</div>`);
});
layerFilterOptions.innerHTML = options.join('');
// 重新绑定事件
bindFilterEvents();
// 更新全选状态
updateSelectAllState();
}
// 绑定过滤器事件
function bindFilterEvents() {
// 绑定单个复选框事件
layerFilterOptions.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
checkbox.addEventListener('change', function() {
if (this.checked) {
selectedLayers.add(this.value);
} else {
selectedLayers.delete(this.value);
}
updateSelectAllState();
refreshLogList();
});
});
}
// 根据当前过滤器重新渲染日志列表
function refreshLogList() {
let filteredLogs = allLogsData;
if (selectedLayers.size > 0 && selectedLayers.size < availableLayers.size) {
filteredLogs = allLogsData.filter(log => selectedLayers.has(log.Layer));
}
const rows = filteredLogs.map((log, i) => formatLogItem(log, i));
clusterize.clear();
clusterize.append(rows);
// 更新总数显示
totalLogsEl.textContent = filteredLogs.length;
}
// 更新连接状态
function updateConnectionStatus(status, text) {
statusIndicator.className = 'status-indicator status-' + status;
@ -377,11 +548,16 @@
// 清空日志显示
function clearLogsDisplay() {
allLogsData = [];
availableLayers.clear();
// 重新添加标准LTE层
['PHY', 'MAC', 'RLC', 'PDCP', 'RRC', 'NAS'].forEach(layer => availableLayers.add(layer));
selectedLayers.clear();
clusterize.clear();
totalLogsEl.textContent = '0';
newLogsCountEl.textContent = '';
detailPlaceholder.classList.remove('d-none');
detailContent.classList.add('d-none');
updateLayerFilter();
}
// 美化方向
@ -443,6 +619,16 @@
function updateLogList(logs, prepend = false) {
if (!logs || logs.length === 0) return;
// 收集新的日志层
logs.forEach(log => {
if (log.Layer) {
availableLayers.add(log.Layer);
}
});
// 更新过滤器选项
updateLayerFilter();
const newRows = logs.map((log, i) => formatLogItem(log, allLogsData.length + i));
if (prepend) {
@ -451,7 +637,9 @@
clusterize.append(newRows);
}
allLogsData.push(...logs);
totalLogsEl.textContent = allLogsData.length;
// 根据当前过滤器更新显示
refreshLogList();
// 显示新日志数量
if (!prepend) {
@ -590,6 +778,22 @@
};
}
// 事件监听器
// 全选/取消全选
selectAllLayers.addEventListener('change', function() {
const isChecked = selectAllLayers.checked;
if (isChecked) {
// 全选
selectedLayers.clear();
availableLayers.forEach(layer => selectedLayers.add(layer));
} else {
// 取消全选
selectedLayers.clear();
}
updateLayerFilter();
refreshLogList();
});
// 事件委托处理点击事件
contentArea.addEventListener('click', function(e) {
const item = e.target.closest('.log-item');
@ -702,6 +906,10 @@
// 初始化连接
connectSSE();
// 初始化Layer过滤器,显示标准LTE层
updateLayerFilter();
});
</script>
}
}
</rewritten_file>

32
LTEMvcApp/Views/Home/TestClientConfig.cshtml

@ -3,32 +3,7 @@
var testConfig = ViewBag.TestConfig as LTEMvcApp.Models.ClientConfig;
// 只保留不含 EVENT 的日志层
var allLayers = LTEMvcApp.Models.LogLayerTypes.AllLayers.Where(l => l != "EVENT").ToArray();
var layerConfigs = new Dictionary<string, LTEMvcApp.Models.LogLayerConfig>();
if (testConfig?.Logs?.ContainsKey("layers") == true && testConfig.Logs["layers"] is System.Text.Json.JsonElement layersElement)
{
var layers = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, LTEMvcApp.Models.LogLayerConfig>>(layersElement.GetRawText(), new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true })
?? new Dictionary<string, LTEMvcApp.Models.LogLayerConfig>();
foreach (var layerName in allLayers)
{
if (layers.TryGetValue(layerName, out var config))
{
layerConfigs[layerName] = config;
}
else
{
layerConfigs[layerName] = new LTEMvcApp.Models.LogLayerConfig { Level = LTEMvcApp.Models.LogLayerTypes.GetDefaultLevel(layerName) };
}
}
}
else
{
foreach (var layerName in allLayers)
{
layerConfigs[layerName] = new LTEMvcApp.Models.LogLayerConfig { Level = LTEMvcApp.Models.LogLayerTypes.GetDefaultLevel(layerName) };
}
}
var layerConfigs = testConfig?.Logs?.Layers ?? new Dictionary<string, LTEMvcApp.Models.LogLayerConfig>();
}
<div class="container-fluid">
@ -216,7 +191,7 @@
};
// 构建日志层配置
var layers = @Html.Raw(Json.Serialize(allLayers));
var layers = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(allLayers));
layers.forEach(function(layer) {
var level = $(`select[name="layers[${layer}][level]"]`).val();
var filter = $(`select[name="layers[${layer}][filter]"]`).val();
@ -231,6 +206,9 @@
};
});
// 添加其他日志配置
// formData.logs.signal = ...; // 如果需要的话
$.ajax({
url: '/api/websocket/test-client-config',
type: 'POST',

Loading…
Cancel
Save