Browse Source

1231311

feature/MultiClientLog
root 1 month ago
parent
commit
e2421a0b49
  1. 138
      LTEMvcApp/Controllers/ConfigController.cs
  2. 98
      LTEMvcApp/Controllers/StatisticsController.cs
  3. 57
      LTEMvcApp/README_StatisticsConfig_Implementation.md
  4. 353
      LTEMvcApp/Views/Home/StatisticsTest.cshtml
  5. 3
      LTEMvcApp/Views/Shared/_Layout.cshtml
  6. 564
      LTEMvcApp/Views/Statistics/Config.cshtml
  7. 252
      LTEMvcApp/Views/Statistics/Index.cshtml
  8. 25
      LTEMvcApp/statistics_config.json

138
LTEMvcApp/Controllers/ConfigController.cs

@ -21,21 +21,6 @@ namespace LTEMvcApp.Controllers
_logger = logger;
}
/// <summary>
/// 获取客户端配置
/// </summary>
/// <param name="clientName">客户端名称</param>
/// <returns>客户端配置</returns>
[HttpGet("{clientName}")]
public ActionResult<ClientConfig?> GetClientConfig(string clientName)
{
var config = _webSocketManager.GetClientConfig(clientName);
if (config == null)
return NotFound($"客户端 '{clientName}' 不存在");
return Ok(config);
}
/// <summary>
/// 获取所有客户端配置
/// </summary>
@ -48,36 +33,121 @@ namespace LTEMvcApp.Controllers
}
/// <summary>
/// 添加客户端配置
/// 获取指定客户端的统计配置
/// </summary>
/// <param name="clientName">客户端名称</param>
/// <returns>统计配置</returns>
[HttpGet("statistics/{clientName}")]
public ActionResult<StatisticsConfig?> GetClientStatisticsConfig(string clientName)
{
try
{
var config = _webSocketManager.GetClientStatisticsConfig(clientName);
return Json(new { success = true, data = config });
}
catch (Exception ex)
{
_logger.LogError(ex, "获取客户端统计配置时出错: {ClientName}", clientName);
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// 设置指定客户端的统计配置
/// </summary>
/// <param name="config">客户端配置</param>
/// <param name="clientName">客户端名称</param>
/// <param name="config">统计配置</param>
/// <returns>操作结果</returns>
[HttpPost]
public ActionResult AddClientConfig([FromBody] ClientConfig config)
[HttpPost("statistics/{clientName}")]
public ActionResult SetClientStatisticsConfig(string clientName, [FromBody] StatisticsConfig config)
{
if (string.IsNullOrEmpty(config.Name))
return BadRequest("客户端名称不能为空");
try
{
config.ClientName = clientName; // 确保客户端名称正确
_webSocketManager.SetClientStatisticsConfig(config);
return Json(new { success = true, message = "客户端统计配置已更新" });
}
catch (Exception ex)
{
_logger.LogError(ex, "设置客户端统计配置时出错: {ClientName}", clientName);
return Json(new { success = false, message = ex.Message });
}
}
var success = _webSocketManager.AddClientConfig(config);
if (success)
return Ok(new { message = $"客户端 '{config.Name}' 配置已添加" });
else
return BadRequest("添加客户端配置失败");
/// <summary>
/// 更新客户端的Samples参数
/// </summary>
/// <param name="clientName">客户端名称</param>
/// <param name="enableSamples">是否启用Samples</param>
/// <returns>操作结果</returns>
[HttpPost("statistics/{clientName}/samples")]
public ActionResult UpdateClientSamples(string clientName, [FromBody] bool enableSamples)
{
try
{
var config = _webSocketManager.GetClientStatisticsConfig(clientName);
if (config == null)
{
// 创建新配置
config = new StatisticsConfig
{
ClientName = clientName,
EnableSamples = enableSamples,
EnableRf = false,
IsEnabled = true
};
}
else
{
config.EnableSamples = enableSamples;
}
_webSocketManager.SetClientStatisticsConfig(config);
return Json(new { success = true, message = "Samples参数已更新" });
}
catch (Exception ex)
{
_logger.LogError(ex, "更新客户端Samples参数时出错: {ClientName}", clientName);
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// 移除客户端配置
/// 更新客户端的RF参数
/// </summary>
/// <param name="clientName">客户端名称</param>
/// <param name="enableRf">是否启用RF</param>
/// <returns>操作结果</returns>
[HttpDelete("{clientName}")]
public ActionResult RemoveClientConfig(string clientName)
[HttpPost("statistics/{clientName}/rf")]
public ActionResult UpdateClientRf(string clientName, [FromBody] bool enableRf)
{
var success = _webSocketManager.RemoveClientConfig(clientName);
if (success)
return Ok(new { message = $"客户端 '{clientName}' 配置已移除" });
else
return BadRequest($"移除客户端 '{clientName}' 配置失败");
try
{
var config = _webSocketManager.GetClientStatisticsConfig(clientName);
if (config == null)
{
// 创建新配置
config = new StatisticsConfig
{
ClientName = clientName,
EnableSamples = false,
EnableRf = enableRf,
IsEnabled = true
};
}
else
{
config.EnableRf = enableRf;
}
_webSocketManager.SetClientStatisticsConfig(config);
return Json(new { success = true, message = "RF参数已更新" });
}
catch (Exception ex)
{
_logger.LogError(ex, "更新客户端RF参数时出错: {ClientName}", clientName);
return Json(new { success = false, message = ex.Message });
}
}
}
}

98
LTEMvcApp/Controllers/StatisticsController.cs

@ -161,96 +161,6 @@ namespace LTEMvcApp.Controllers
}
}
/// <summary>
/// 获取全局统计配置
/// </summary>
[HttpGet]
public IActionResult GetGlobalStatisticsConfig()
{
try
{
var config = _webSocketManager.GetGlobalStatisticsConfig();
return Json(new { success = true, data = config });
}
catch (Exception ex)
{
_logger.LogError(ex, "获取全局统计配置时出错");
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// 设置全局统计配置
/// </summary>
[HttpPost]
public IActionResult SetGlobalStatisticsConfig([FromBody] GlobalStatisticsConfig config)
{
try
{
_webSocketManager.SetGlobalStatisticsConfig(config);
return Json(new { success = true, message = "全局统计配置已更新" });
}
catch (Exception ex)
{
_logger.LogError(ex, "设置全局统计配置时出错");
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// 获取指定客户端的统计配置
/// </summary>
[HttpGet]
public IActionResult GetClientStatisticsConfig(string clientName)
{
try
{
var config = _webSocketManager.GetClientStatisticsConfig(clientName);
return Json(new { success = true, data = config });
}
catch (Exception ex)
{
_logger.LogError(ex, "获取客户端统计配置时出错: {ClientName}", clientName);
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// 设置指定客户端的统计配置
/// </summary>
[HttpPost]
public IActionResult SetClientStatisticsConfig([FromBody] StatisticsConfig config)
{
try
{
_webSocketManager.SetClientStatisticsConfig(config);
return Json(new { success = true, message = "客户端统计配置已更新" });
}
catch (Exception ex)
{
_logger.LogError(ex, "设置客户端统计配置时出错: {ClientName}", config.ClientName);
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// 根据IP地址获取统计配置
/// </summary>
[HttpGet]
public IActionResult GetStatisticsConfigByIp(string ipAddress)
{
try
{
var config = _webSocketManager.GetStatisticsConfigByIp(ipAddress);
return Json(new { success = true, data = config });
}
catch (Exception ex)
{
_logger.LogError(ex, "根据IP获取统计配置时出错: {IpAddress}", ipAddress);
return Json(new { success = false, message = ex.Message });
}
}
/// <summary>
/// 获取所有客户端的统计配置
/// </summary>
@ -269,6 +179,14 @@ namespace LTEMvcApp.Controllers
}
}
/// <summary>
/// 统计配置管理页面
/// </summary>
public IActionResult Config()
{
return View();
}
/// <summary>
/// SSE推送 - 所有统计数据
/// </summary>

57
LTEMvcApp/README_StatisticsConfig_Implementation.md

@ -19,18 +19,35 @@
- 通过Web界面实时修改配置
- 配置自动保存到`statistics_config.json`文件
- 支持配置列表查看和编辑
- 支持配置导入/导出功能
## 页面结构
### 两个独立页面:
1. **`Statistics/Index.cshtml`** - 统计数据监控页面
- 实时统计数据展示
- 客户端状态监控
- SSE实时数据推送
- 小区详细信息查看
2. **`Statistics/Config.cshtml`** - 统计配置管理页面
- 全局配置管理
- 客户端特定配置
- 配置列表管理
- 配置导入/导出
## 文件结构
### 新增文件
- `Models/StatisticsConfig.cs` - 统计配置模型
- `statistics_config.json` - 统计配置文件
- `Views/Statistics/Config.cshtml` - 统计配置管理页面
### 修改文件
- `Services/LTEClientWebSocket.cs` - 添加统计配置支持
- `Services/WebSocketManagerService.cs` - 添加配置管理功能
- `Controllers/StatisticsController.cs` - 添加配置管理API
- `Views/Home/StatisticsTest.cshtml` - 添加配置管理界面
- `Controllers/StatisticsController.cs` - 添加配置管理API和Config方法
- `Views/Shared/_Layout.cshtml` - 添加导航菜单链接
## 核心实现
@ -111,25 +128,32 @@ private void UpdateAllClientsStatisticsConfig()
[HttpPost] SetClientStatisticsConfig([FromBody] StatisticsConfig config)
[HttpGet] GetStatisticsConfigByIp(string ipAddress)
[HttpGet] GetAllClientStatisticsConfigs()
public IActionResult Config() // 配置管理页面
```
## 使用方法
### 1. 访问配置界面
访问 `/Home/StatisticsTest` 页面,可以看到统计配置管理界面。
### 1. 访问统计数据页面
- 通过导航菜单点击"统计数据"或访问 `/Statistics/Index`
- 查看实时统计数据、客户端状态、小区信息等
### 2. 设置全局配置
- 在"全局统计配置"区域设置默认的samples和rf值
### 2. 访问配置管理页面
- 通过导航菜单点击"统计配置"或访问 `/Statistics/Config`
- 管理全局配置和客户端特定配置
### 3. 设置全局配置
- 在"全局统计配置"部分设置默认的samples和rf值
- 点击"保存配置"按钮保存
### 3. 设置客户端特定配置
- 在"客户端特定配置"区域输入客户端信息
### 4. 设置客户端特定配置
- 在"客户端特定配置"部分输入客户端信息
- 设置特定的samples和rf值
- 点击"保存配置"按钮保存
### 4. 查看配置列表
- 在"配置列表"区域可以看到所有已配置的客户端
### 5. 查看配置列表
- 在"配置列表"部分可以看到所有已配置的客户端
- 点击"编辑"按钮可以修改现有配置
- 支持配置导入/导出功能
## 配置优先级
@ -156,18 +180,27 @@ private void UpdateAllClientsStatisticsConfig()
}
```
## 页面优势
### 拆分的好处:
1. **职责分离** - 数据监控和配置管理各司其职
2. **用户体验** - 每个页面功能专注,界面简洁
3. **维护性** - 代码结构清晰,易于维护和扩展
4. **性能** - 避免单个页面过于复杂,加载更快
## 注意事项
1. 配置修改后会自动应用到所有已连接的客户端
2. 新连接的客户端会自动应用相应的配置
3. 配置文件会在应用启动时自动加载
4. 配置修改会实时保存到文件,重启应用后配置仍然有效
5. 两个页面功能独立,用户可以根据需要分别访问
## 扩展功能
可以根据需要扩展以下功能:
- 配置模板管理
- 配置导入/导出
- 配置版本控制
- 配置变更日志
- 批量配置操作
- 批量配置操作
- 配置验证和测试

353
LTEMvcApp/Views/Home/StatisticsTest.cshtml

@ -1,353 +0,0 @@
@{
ViewData["Title"] = "统计测试";
}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">统计功能测试</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h5>API测试</h5>
<button class="btn btn-primary mb-2" onclick="testGetAllStats()">获取所有统计数据</button>
<button class="btn btn-info mb-2" onclick="testGetLatestStats()">获取最新统计数据</button>
<button class="btn btn-success mb-2" onclick="testGetSummary()">获取统计摘要</button>
<button class="btn btn-warning mb-2" onclick="testClearStats()">清空统计数据</button>
</div>
<div class="col-md-6">
<h5>SSE测试</h5>
<button class="btn btn-primary mb-2" onclick="testSSE()">测试SSE连接</button>
<button class="btn btn-danger mb-2" onclick="stopSSE()">停止SSE</button>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<h5>统计配置管理</h5>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>全局统计配置</h6>
</div>
<div class="card-body">
<div class="form-group">
<label>默认Samples:</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="globalSamples">
<label class="form-check-label" for="globalSamples">启用Samples</label>
</div>
</div>
<div class="form-group">
<label>默认RF:</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="globalRf">
<label class="form-check-label" for="globalRf">启用RF</label>
</div>
</div>
<button class="btn btn-primary btn-sm" onclick="loadGlobalConfig()">加载配置</button>
<button class="btn btn-success btn-sm" onclick="saveGlobalConfig()">保存配置</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>客户端特定配置</h6>
</div>
<div class="card-body">
<div class="form-group">
<label>客户端名称:</label>
<input type="text" class="form-control" id="clientName" placeholder="输入客户端名称">
</div>
<div class="form-group">
<label>IP地址:</label>
<input type="text" class="form-control" id="clientIp" placeholder="输入IP地址">
</div>
<div class="form-group">
<label>配置选项:</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="clientSamples">
<label class="form-check-label" for="clientSamples">启用Samples</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="clientRf">
<label class="form-check-label" for="clientRf">启用RF</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="clientEnabled" checked>
<label class="form-check-label" for="clientEnabled">启用配置</label>
</div>
</div>
<div class="form-group">
<label>描述:</label>
<input type="text" class="form-control" id="clientDescription" placeholder="配置描述">
</div>
<button class="btn btn-primary btn-sm" onclick="loadClientConfig()">加载配置</button>
<button class="btn btn-success btn-sm" onclick="saveClientConfig()">保存配置</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<h5>配置列表</h5>
<button class="btn btn-info btn-sm mb-2" onclick="loadAllClientConfigs()">刷新配置列表</button>
<div id="configList" class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>客户端名称</th>
<th>IP地址</th>
<th>Samples</th>
<th>RF</th>
<th>启用</th>
<th>描述</th>
<th>操作</th>
</tr>
</thead>
<tbody id="configTableBody">
</tbody>
</table>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<h5>测试结果</h5>
<pre id="testResult" style="background-color: #f8f9fa; padding: 10px; border-radius: 5px; max-height: 400px; overflow-y: auto;"></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
let sseConnection = null;
function logResult(message) {
const result = document.getElementById('testResult');
const timestamp = new Date().toLocaleTimeString();
result.textContent += `[${timestamp}] ${message}\n`;
result.scrollTop = result.scrollHeight;
}
function testGetAllStats() {
logResult('测试获取所有统计数据...');
$.get('/Statistics/GetAllStats', function(response) {
logResult('响应: ' + JSON.stringify(response, null, 2));
}).fail(function(xhr, status, error) {
logResult('错误: ' + error);
});
}
function testGetLatestStats() {
logResult('测试获取最新统计数据...');
$.get('/Statistics/GetLatestStats', function(response) {
logResult('响应: ' + JSON.stringify(response, null, 2));
}).fail(function(xhr, status, error) {
logResult('错误: ' + error);
});
}
function testGetSummary() {
logResult('测试获取统计摘要...');
$.get('/Statistics/GetSummary', function(response) {
logResult('响应: ' + JSON.stringify(response, null, 2));
}).fail(function(xhr, status, error) {
logResult('错误: ' + error);
});
}
function testClearStats() {
logResult('测试清空统计数据...');
$.post('/Statistics/ClearStats', function(response) {
logResult('响应: ' + JSON.stringify(response, null, 2));
}).fail(function(xhr, status, error) {
logResult('错误: ' + error);
});
}
function testSSE() {
logResult('测试SSE连接...');
if (sseConnection) {
sseConnection.close();
}
sseConnection = new EventSource('/Statistics/SSEStats');
sseConnection.onopen = function(event) {
logResult('SSE连接已建立');
};
sseConnection.onmessage = function(event) {
logResult('SSE消息: ' + event.data);
};
sseConnection.onerror = function(event) {
logResult('SSE错误: ' + JSON.stringify(event));
};
}
function stopSSE() {
logResult('停止SSE连接...');
if (sseConnection) {
sseConnection.close();
sseConnection = null;
logResult('SSE连接已关闭');
}
}
// 统计配置相关函数
function loadGlobalConfig() {
logResult('加载全局统计配置...');
$.get('/Statistics/GetGlobalStatisticsConfig', function(response) {
if (response.success) {
const config = response.data;
document.getElementById('globalSamples').checked = config.defaultSamples;
document.getElementById('globalRf').checked = config.defaultRf;
logResult('全局配置已加载: ' + JSON.stringify(config, null, 2));
} else {
logResult('加载全局配置失败: ' + response.message);
}
}).fail(function(xhr, status, error) {
logResult('加载全局配置错误: ' + error);
});
}
function saveGlobalConfig() {
const config = {
defaultSamples: document.getElementById('globalSamples').checked,
defaultRf: document.getElementById('globalRf').checked,
clientConfigs: []
};
logResult('保存全局统计配置...');
$.ajax({
url: '/Statistics/SetGlobalStatisticsConfig',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(config),
success: function(response) {
logResult('响应: ' + JSON.stringify(response, null, 2));
},
error: function(xhr, status, error) {
logResult('错误: ' + error);
}
});
}
function loadClientConfig() {
const clientName = document.getElementById('clientName').value;
if (!clientName) {
logResult('请输入客户端名称');
return;
}
logResult('加载客户端统计配置: ' + clientName);
$.get('/Statistics/GetClientStatisticsConfig', { clientName: clientName }, function(response) {
if (response.success && response.data) {
const config = response.data;
document.getElementById('clientName').value = config.clientName;
document.getElementById('clientIp').value = config.ipAddress;
document.getElementById('clientSamples').checked = config.enableSamples;
document.getElementById('clientRf').checked = config.enableRf;
document.getElementById('clientEnabled').checked = config.isEnabled;
document.getElementById('clientDescription').value = config.description;
logResult('客户端配置已加载: ' + JSON.stringify(config, null, 2));
} else {
logResult('客户端配置未找到或加载失败');
}
}).fail(function(xhr, status, error) {
logResult('加载客户端配置错误: ' + error);
});
}
function saveClientConfig() {
const config = {
clientName: document.getElementById('clientName').value,
ipAddress: document.getElementById('clientIp').value,
enableSamples: document.getElementById('clientSamples').checked,
enableRf: document.getElementById('clientRf').checked,
isEnabled: document.getElementById('clientEnabled').checked,
description: document.getElementById('clientDescription').value
};
if (!config.clientName) {
logResult('请输入客户端名称');
return;
}
logResult('保存客户端统计配置...');
$.ajax({
url: '/Statistics/SetClientStatisticsConfig',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(config),
success: function(response) {
logResult('响应: ' + JSON.stringify(response, null, 2));
loadAllClientConfigs(); // 刷新配置列表
},
error: function(xhr, status, error) {
logResult('错误: ' + error);
}
});
}
function loadAllClientConfigs() {
logResult('加载所有客户端配置...');
$.get('/Statistics/GetAllClientStatisticsConfigs', function(response) {
if (response.success) {
const configs = response.data;
const tbody = document.getElementById('configTableBody');
tbody.innerHTML = '';
configs.forEach(function(config) {
const row = document.createElement('tr');
row.innerHTML = `
<td>${config.clientName}</td>
<td>${config.ipAddress}</td>
<td>${config.enableSamples ? '是' : '否'}</td>
<td>${config.enableRf ? '是' : '否'}</td>
<td>${config.isEnabled ? '是' : '否'}</td>
<td>${config.description}</td>
<td>
<button class="btn btn-sm btn-primary" onclick="editConfig('${config.clientName}')">编辑</button>
</td>
`;
tbody.appendChild(row);
});
logResult('配置列表已更新,共 ' + configs.length + ' 个配置');
} else {
logResult('加载配置列表失败: ' + response.message);
}
}).fail(function(xhr, status, error) {
logResult('加载配置列表错误: ' + error);
});
}
function editConfig(clientName) {
document.getElementById('clientName').value = clientName;
loadClientConfig();
}
// 页面加载时初始化
$(document).ready(function() {
document.getElementById('testResult').textContent = '';
loadGlobalConfig();
loadAllClientConfigs();
});
</script>
}

3
LTEMvcApp/Views/Shared/_Layout.cshtml

@ -236,6 +236,9 @@
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Statistics" asp-action="Index">统计数据</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Statistics" asp-action="Config">统计配置</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="TestClientConfig">测试客户端配置</a>
</li>

564
LTEMvcApp/Views/Statistics/Config.cshtml

@ -0,0 +1,564 @@
@{
ViewData["Title"] = "统计配置管理";
}
<style>
/* 页面整体布局 */
.config-container {
max-height: calc(100vh - 280px);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 表单容器 */
.config-form {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 0.75rem;
}
/* 自定义滚动条 */
.config-form::-webkit-scrollbar {
width: 6px;
}
.config-form::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.config-form::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.config-form::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* 表单样式 */
.form-group {
margin-bottom: 0.75rem;
}
.form-group label {
font-weight: 600;
color: #495057;
margin-bottom: 0.25rem;
font-size: 0.9rem;
}
.form-control {
border-radius: 0.375rem;
border: 1px solid #ced4da;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
padding: 0.375rem 0.75rem;
}
.form-control:focus {
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
/* 表格样式 */
.table-responsive {
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.table {
margin-bottom: 0;
font-size: 0.85rem;
}
.table th {
background-color: #343a40;
color: white;
font-weight: 600;
border: none;
padding: 0.5rem;
}
.table td {
vertical-align: middle;
border-color: #dee2e6;
padding: 0.5rem;
}
/* 按钮样式 */
.btn {
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.2s;
padding: 0.375rem 0.75rem;
font-size: 0.9rem;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.form-check {
margin-bottom: 0.5rem;
}
.form-check-input:checked {
background-color: #007bff;
border-color: #007bff;
}
.section-title {
color: #495057;
font-weight: 600;
margin-bottom: 0.5rem;
padding-bottom: 0.25rem;
border-bottom: 2px solid #e9ecef;
font-size: 1rem;
}
/* 按钮区域 */
.button-area {
flex-shrink: 0;
background: white;
border-top: 1px solid #dee2e6;
padding: 0.5rem 0.75rem;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
/* 配置卡片样式 */
.config-card {
border: 1px solid #e9ecef;
border-radius: 0.5rem;
background: #f8f9fa;
padding: 1rem;
margin-bottom: 1rem;
}
.config-card-header {
display: flex;
justify-content: between;
align-items: center;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid #dee2e6;
}
.config-card-title {
font-weight: 600;
color: #495057;
margin: 0;
font-size: 1rem;
}
.config-options {
display: flex;
gap: 1rem;
align-items: center;
flex-wrap: wrap;
}
.config-option {
display: flex;
align-items: center;
gap: 0.5rem;
}
/* 响应式调整 */
@@media (max-width: 768px) {
.config-container {
max-height: calc(100vh - 240px);
}
.button-area {
padding: 0.4rem 0.6rem;
}
.config-form {
padding: 0.6rem;
}
.config-options {
flex-direction: column;
align-items: flex-start;
}
}
@@media (max-width: 576px) {
.config-container {
max-height: calc(100vh - 220px);
}
.button-area {
padding: 0.3rem 0.5rem;
}
.config-form {
padding: 0.5rem;
}
.btn {
font-size: 0.8rem;
padding: 0.3rem 0.6rem;
}
}
</style>
<div class="container">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-cogs"></i> 统计配置管理
</h3>
</div>
<div class="card-body p-0">
<div class="config-container">
<div class="config-form">
<!-- 客户端配置 -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-server"></i> 客户端统计配置
</h5>
</div>
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<span class="badge bg-info" id="clientCount">0</span> 个客户端
</div>
<button class="btn btn-sm btn-outline-primary" onclick="loadClientList()">
<i class="fas fa-sync-alt"></i> 重新加载客户端
</button>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>客户端名称</th>
<th>IP地址</th>
<th>Samples</th>
<th>RF</th>
<th>状态</th>
<th>描述</th>
<th>操作</th>
</tr>
</thead>
<tbody id="clientConfigTableBody">
<!-- 客户端配置数据将通过JavaScript动态填充 -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- 配置信息 -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-info-circle"></i> 配置说明
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6>配置优先级:</h6>
<ol>
<li><strong>客户端特定配置</strong> - 如果客户端有特定的配置且启用,则使用该配置</li>
<li><strong>系统默认值</strong> - 如果客户端没有特定配置,则使用系统默认值(samples=false, rf=false)</li>
</ol>
</div>
<div class="col-md-6">
<h6>参数说明:</h6>
<ul>
<li><strong>Samples</strong> - 控制是否收集详细的采样数据,可能影响性能</li>
<li><strong>RF</strong> - 控制是否收集射频相关数据,包含信号质量等信息</li>
<li><strong>启用配置</strong> - 控制该客户端配置是否生效</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
// 页面加载时初始化
$(document).ready(function() {
loadClientList();
});
// 加载客户端列表并生成配置表格
function loadClientList() {
console.log('加载客户端列表...');
$.get('/api/config', function(response) {
if (response && response.length > 0) {
const tbody = document.getElementById('clientConfigTableBody');
tbody.innerHTML = '';
response.forEach(function(client) {
const row = document.createElement('tr');
row.innerHTML = `
<td><strong>${client.name}</strong></td>
<td><code>${client.address}</code></td>
<td>
<div class="form-check d-flex justify-content-center">
<input class="form-check-input" type="checkbox" id="samples_${client.name}"
onchange="updateConfig('${client.name}', 'samples', this.checked)">
</div>
</td>
<td>
<div class="form-check d-flex justify-content-center">
<input class="form-check-input" type="checkbox" id="rf_${client.name}"
onchange="updateConfig('${client.name}', 'rf', this.checked)">
</div>
</td>
<td>
<div class="form-check d-flex justify-content-center">
<input class="form-check-input" type="checkbox" id="enabled_${client.name}" checked
onchange="updateConfig('${client.name}', 'enabled', this.checked)">
</div>
</td>
<td>
<input type="text" class="form-control form-control-sm" id="desc_${client.name}"
placeholder="配置描述" onblur="updateConfig('${client.name}', 'description', this.value)">
</td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-success" onclick="saveClientConfig('${client.name}')" title="保存">
<i class="fas fa-save"></i>
</button>
<button class="btn btn-info" onclick="loadClientConfig('${client.name}')" title="加载">
<i class="fas fa-download"></i>
</button>
</div>
</td>
`;
tbody.appendChild(row);
});
document.getElementById('clientCount').textContent = response.length;
console.log('客户端列表已加载,共', response.length, '个客户端');
// 加载所有客户端的现有配置
loadAllClientConfigs();
} else {
console.log('没有找到客户端配置');
document.getElementById('clientCount').textContent = '0';
}
}).fail(function(xhr, status, error) {
console.error('加载客户端列表错误:', error);
showAlert('danger', '加载客户端列表失败: ' + error);
});
}
// 更新配置(实时保存到服务器)
function updateConfig(clientName, field, value) {
console.log(`更新配置: ${clientName} - ${field} = ${value}`);
if (field === 'samples') {
// 更新Samples参数
$.ajax({
url: `/api/config/statistics/${encodeURIComponent(clientName)}/samples`,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(value),
success: function(response) {
if (response.success) {
console.log(`客户端 ${clientName} Samples参数已更新`);
} else {
console.error('更新Samples参数失败:', response.message);
showAlert('danger', `更新Samples参数失败: ${response.message}`);
}
},
error: function(xhr, status, error) {
console.error('更新Samples参数错误:', error);
showAlert('danger', `更新Samples参数错误: ${error}`);
}
});
} else if (field === 'rf') {
// 更新RF参数
$.ajax({
url: `/api/config/statistics/${encodeURIComponent(clientName)}/rf`,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(value),
success: function(response) {
if (response.success) {
console.log(`客户端 ${clientName} RF参数已更新`);
} else {
console.error('更新RF参数失败:', response.message);
showAlert('danger', `更新RF参数失败: ${response.message}`);
}
},
error: function(xhr, status, error) {
console.error('更新RF参数错误:', error);
showAlert('danger', `更新RF参数错误: ${error}`);
}
});
} else if (field === 'enabled') {
// 更新启用状态
saveClientConfig(clientName);
} else if (field === 'description') {
// 更新描述
saveClientConfig(clientName);
}
}
// 加载客户端配置
function loadClientConfig(clientName) {
console.log('加载客户端统计配置:', clientName);
$.get(`/api/config/statistics/${encodeURIComponent(clientName)}`, function(response) {
if (response.success && response.data) {
const config = response.data;
document.getElementById(`samples_${clientName}`).checked = config.enableSamples;
document.getElementById(`rf_${clientName}`).checked = config.enableRf;
document.getElementById(`enabled_${clientName}`).checked = config.isEnabled;
document.getElementById(`desc_${clientName}`).value = config.description || '';
console.log('客户端配置已加载:', config);
showAlert('success', `客户端 ${clientName} 配置已加载`);
} else {
console.log('客户端配置未找到,使用默认值');
// 使用默认值
document.getElementById(`samples_${clientName}`).checked = false;
document.getElementById(`rf_${clientName}`).checked = false;
document.getElementById(`enabled_${clientName}`).checked = true;
document.getElementById(`desc_${clientName}`).value = '';
showAlert('info', `客户端 ${clientName} 使用默认配置`);
}
}).fail(function(xhr, status, error) {
console.error('加载客户端配置错误:', error);
showAlert('danger', `加载客户端 ${clientName} 配置失败: ${error}`);
});
}
// 保存客户端配置
function saveClientConfig(clientName) {
const config = {
clientName: clientName,
ipAddress: '', // 将从客户端列表获取
enableSamples: document.getElementById(`samples_${clientName}`).checked,
enableRf: document.getElementById(`rf_${clientName}`).checked,
isEnabled: document.getElementById(`enabled_${clientName}`).checked,
description: document.getElementById(`desc_${clientName}`).value
};
// 从已加载的客户端列表中获取IP地址
const tbody = document.getElementById('clientConfigTableBody');
const rows = tbody.getElementsByTagName('tr');
for (let row of rows) {
const nameCell = row.querySelector('td:first-child strong');
if (nameCell && nameCell.textContent === clientName) {
const ipCell = row.querySelector('td:nth-child(2) code');
if (ipCell) {
config.ipAddress = ipCell.textContent;
}
break;
}
}
console.log('保存客户端统计配置:', config);
$.ajax({
url: `/api/config/statistics/${encodeURIComponent(clientName)}`,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(config),
success: function(response) {
console.log('客户端配置保存响应:', response);
if (response.success) {
showAlert('success', `客户端 ${clientName} 配置已保存`);
} else {
showAlert('danger', `保存失败: ${response.message}`);
}
},
error: function(xhr, status, error) {
console.error('保存客户端配置错误:', error);
showAlert('danger', `保存失败: ${error}`);
}
});
}
// 加载所有客户端配置
function loadAllClientConfigs() {
console.log('加载所有客户端配置...');
// 从WebSocketManagerService获取所有统计配置
$.get('/Statistics/GetAllClientStatisticsConfigs', function(response) {
if (response.success) {
const configs = response.data;
const configDict = {};
// 将配置转换为字典,方便查找
configs.forEach(function(config) {
configDict[config.clientName] = config;
});
// 更新表格中的配置
const tbody = document.getElementById('clientConfigTableBody');
const rows = tbody.getElementsByTagName('tr');
for (let row of rows) {
const nameCell = row.querySelector('td:first-child strong');
if (nameCell) {
const clientName = nameCell.textContent;
const config = configDict[clientName];
if (config) {
row.querySelector(`#samples_${clientName}`).checked = config.enableSamples;
row.querySelector(`#rf_${clientName}`).checked = config.enableRf;
row.querySelector(`#enabled_${clientName}`).checked = config.isEnabled;
row.querySelector(`#desc_${clientName}`).value = config.description || '';
}
}
}
console.log('所有客户端配置已加载,共', configs.length, '个配置');
} else {
console.error('加载配置列表失败:', response.message);
}
}).fail(function(xhr, status, error) {
console.error('加载配置列表错误:', error);
});
}
// 显示提示信息
function showAlert(type, message) {
const alertHtml = `
<div class="alert alert-${type} alert-dismissible fade show" role="alert">
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
`;
// 移除现有的提示
$('.alert').remove();
// 添加新提示到页面顶部
$('.container').prepend(alertHtml);
// 3秒后自动消失
setTimeout(function() {
$('.alert').fadeOut();
}, 3000);
}
</script>
}

252
LTEMvcApp/Views/Statistics/Index.cshtml

@ -137,108 +137,6 @@
</div>
</div>
</div>
<!-- 统计配置管理 -->
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">统计配置管理</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>全局统计配置</h6>
</div>
<div class="card-body">
<div class="form-group">
<label>默认Samples:</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="globalSamples">
<label class="form-check-label" for="globalSamples">启用Samples</label>
</div>
</div>
<div class="form-group">
<label>默认RF:</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="globalRf">
<label class="form-check-label" for="globalRf">启用RF</label>
</div>
</div>
<button class="btn btn-primary btn-sm" onclick="loadGlobalConfig()">加载配置</button>
<button class="btn btn-success btn-sm" onclick="saveGlobalConfig()">保存配置</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h6>客户端特定配置</h6>
</div>
<div class="card-body">
<div class="form-group">
<label>客户端名称:</label>
<input type="text" class="form-control" id="clientName" placeholder="输入客户端名称">
</div>
<div class="form-group">
<label>IP地址:</label>
<input type="text" class="form-control" id="clientIp" placeholder="输入IP地址">
</div>
<div class="form-group">
<label>配置选项:</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="clientSamples">
<label class="form-check-label" for="clientSamples">启用Samples</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="clientRf">
<label class="form-check-label" for="clientRf">启用RF</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="clientEnabled" checked>
<label class="form-check-label" for="clientEnabled">启用配置</label>
</div>
</div>
<div class="form-group">
<label>描述:</label>
<input type="text" class="form-control" id="clientDescription" placeholder="配置描述">
</div>
<button class="btn btn-primary btn-sm" onclick="loadClientConfig()">加载配置</button>
<button class="btn btn-success btn-sm" onclick="saveClientConfig()">保存配置</button>
</div>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<h6>配置列表</h6>
<button class="btn btn-info btn-sm mb-2" onclick="loadAllClientConfigs()">刷新配置列表</button>
<div id="configList" class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>客户端名称</th>
<th>IP地址</th>
<th>Samples</th>
<th>RF</th>
<th>启用</th>
<th>描述</th>
<th>操作</th>
</tr>
</thead>
<tbody id="configTableBody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@section Scripts {
@ -252,10 +150,6 @@
loadSummary();
loadAllStats();
startRefreshTimer();
// 新增的配置管理初始化
loadGlobalConfig();
loadAllClientConfigs();
});
// 加载统计摘要
@ -446,151 +340,5 @@
clearInterval(refreshTimer);
}
});
// 统计配置管理相关函数
function loadGlobalConfig() {
console.log('加载全局统计配置...');
$.get('/Statistics/GetGlobalStatisticsConfig', function(response) {
if (response.success) {
const config = response.data;
document.getElementById('globalSamples').checked = config.defaultSamples;
document.getElementById('globalRf').checked = config.defaultRf;
console.log('全局配置已加载:', config);
} else {
console.error('加载全局配置失败:', response.message);
}
}).fail(function(xhr, status, error) {
console.error('加载全局配置错误:', error);
});
}
function saveGlobalConfig() {
const config = {
defaultSamples: document.getElementById('globalSamples').checked,
defaultRf: document.getElementById('globalRf').checked,
clientConfigs: []
};
console.log('保存全局统计配置...');
$.ajax({
url: '/Statistics/SetGlobalStatisticsConfig',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(config),
success: function(response) {
console.log('全局配置保存响应:', response);
if (response.success) {
alert('全局配置已保存');
} else {
alert('保存失败: ' + response.message);
}
},
error: function(xhr, status, error) {
console.error('保存全局配置错误:', error);
alert('保存失败: ' + error);
}
});
}
function loadClientConfig() {
const clientName = document.getElementById('clientName').value;
if (!clientName) {
alert('请输入客户端名称');
return;
}
console.log('加载客户端统计配置:', clientName);
$.get('/Statistics/GetClientStatisticsConfig', { clientName: clientName }, function(response) {
if (response.success && response.data) {
const config = response.data;
document.getElementById('clientName').value = config.clientName;
document.getElementById('clientIp').value = config.ipAddress;
document.getElementById('clientSamples').checked = config.enableSamples;
document.getElementById('clientRf').checked = config.enableRf;
document.getElementById('clientEnabled').checked = config.isEnabled;
document.getElementById('clientDescription').value = config.description;
console.log('客户端配置已加载:', config);
} else {
console.log('客户端配置未找到或加载失败');
}
}).fail(function(xhr, status, error) {
console.error('加载客户端配置错误:', error);
});
}
function saveClientConfig() {
const config = {
clientName: document.getElementById('clientName').value,
ipAddress: document.getElementById('clientIp').value,
enableSamples: document.getElementById('clientSamples').checked,
enableRf: document.getElementById('clientRf').checked,
isEnabled: document.getElementById('clientEnabled').checked,
description: document.getElementById('clientDescription').value
};
if (!config.clientName) {
alert('请输入客户端名称');
return;
}
console.log('保存客户端统计配置...');
$.ajax({
url: '/Statistics/SetClientStatisticsConfig',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(config),
success: function(response) {
console.log('客户端配置保存响应:', response);
if (response.success) {
alert('客户端配置已保存');
loadAllClientConfigs(); // 刷新配置列表
} else {
alert('保存失败: ' + response.message);
}
},
error: function(xhr, status, error) {
console.error('保存客户端配置错误:', error);
alert('保存失败: ' + error);
}
});
}
function loadAllClientConfigs() {
console.log('加载所有客户端配置...');
$.get('/Statistics/GetAllClientStatisticsConfigs', function(response) {
if (response.success) {
const configs = response.data;
const tbody = document.getElementById('configTableBody');
tbody.innerHTML = '';
configs.forEach(function(config) {
const row = document.createElement('tr');
row.innerHTML = `
<td>${config.clientName}</td>
<td>${config.ipAddress}</td>
<td>${config.enableSamples ? '是' : '否'}</td>
<td>${config.enableRf ? '是' : '否'}</td>
<td>${config.isEnabled ? '是' : '否'}</td>
<td>${config.description}</td>
<td>
<button class="btn btn-sm btn-primary" onclick="editConfig('${config.clientName}')">编辑</button>
</td>
`;
tbody.appendChild(row);
});
console.log('配置列表已更新,共', configs.length, '个配置');
} else {
console.error('加载配置列表失败:', response.message);
}
}).fail(function(xhr, status, error) {
console.error('加载配置列表错误:', error);
});
}
function editConfig(clientName) {
document.getElementById('clientName').value = clientName;
loadClientConfig();
}
</script>
}

25
LTEMvcApp/statistics_config.json

@ -1,22 +1,5 @@
{
"defaultSamples": false,
"defaultRf": false,
"clientConfigs": [
{
"ipAddress": "192.168.13.12",
"clientName": "TestClient1",
"enableSamples": true,
"enableRf": false,
"isEnabled": true,
"description": "测试客户端1 - 启用samples"
},
{
"ipAddress": "192.168.13.13",
"clientName": "TestClient2",
"enableSamples": false,
"enableRf": true,
"isEnabled": true,
"description": "测试客户端2 - 启用rf"
}
]
}
"defaultSamples": true,
"defaultRf": true,
"clientConfigs": []
}
Loading…
Cancel
Save