V0.21.10.250611_alpha:数据监控单次注入功能已可用

This commit is contained in:
jinchao 2025-06-11 10:59:35 +08:00
parent 3958965fbf
commit 1e2581a239
7 changed files with 408 additions and 22 deletions

Binary file not shown.

View File

@ -213,6 +213,9 @@ protected:
while (std::getline(ss, item, ',')) {
items.push_back(item);
}
if (items.size() != getArrayWholeSize<T>()) {
return;
}
T temp;
setStdArrayFromString(temp, items, 0);
data = temp;

View File

@ -199,7 +199,7 @@ private:
static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string>
|| std::is_convertible_v<T, std::string> || std::is_same_v<T, char *>
|| std::is_same_v<T, const char *>,
"错误码010211001不支持的类型转换详见XNLogger.cppline 199");
"A01021001: 不支持的类型转换详见XNLogger.cppline 199");
}
}
@ -214,7 +214,7 @@ private:
static std::string formatMessage(const std::string &message, Args &&...args)
{
static_assert(sizeof...(Args) <= 9,
"错误码010211002单条日志参数数量超过限制详见XNLogger.cppline 216");
"A01021002: 单条日志参数数量超过限制详见XNLogger.cppline 216");
std::string result = message;
// 使用初始化列表展开参数包

View File

@ -43,3 +43,14 @@ constexpr size_t getTypeSize()
return sizeof(T);
}
}
template <typename T>
constexpr size_t getArrayWholeSize()
{
if constexpr (is_std_array_v<T>) {
// 对于std::array计算所有元素的总大小
return getArrayWholeSize<typename T::value_type>() * array_size_v<T>;
} else {
return 1;
}
}

View File

@ -213,6 +213,9 @@ protected:
while (std::getline(ss, item, ',')) {
items.push_back(item);
}
if (items.size() != getArrayWholeSize<T>()) {
return;
}
T temp;
setStdArrayFromString(temp, items, 0);
data = temp;

View File

@ -43,3 +43,14 @@ constexpr size_t getTypeSize()
return sizeof(T);
}
}
template <typename T>
constexpr size_t getArrayWholeSize()
{
if constexpr (is_std_array_v<T>) {
// 对于std::array计算所有元素的总大小
return getArrayWholeSize<typename T::value_type>() * array_size_v<T>;
} else {
return 1;
}
}

View File

@ -196,23 +196,154 @@ class DataMonitor extends HTMLElement {
<td title="${item.InterfaceName}">${item.InterfaceName}</td>
<td title="${item.ModelStructName}">${item.ModelStructName}</td>
<td class="data-cell" title="-">-</td>
<td title="-">-</td>
<td>
<input type="text"
class="inject-input"
value=""
placeholder="输入注入值"
data-interface="${item.InterfaceName}"
data-struct="${item.ModelStructName}">
</td>
<td>
<div class="action-buttons">
<button class="action-button plot" data-interface="${item.InterfaceName}" data-struct="${item.ModelStructName}">绘图</button>
<button class="action-button">注入一次</button>
<button class="action-button">连续注入</button>
<button class="action-button inject-once" data-interface="${item.InterfaceName}" data-struct="${item.ModelStructName}">注入一次</button>
<button class="action-button inject-continuous" data-interface="${item.ModelStructName}" data-struct="${item.ModelStructName}">连续注入</button>
<button class="action-button delete" data-interface="${item.InterfaceName}" data-struct="${item.ModelStructName}">删除</button>
</div>
</td>
`;
// 添加删除按钮事件
// 为输入框绑定事件
const input = tr.querySelector('.inject-input');
input.addEventListener('input', (e) => {
const value = e.target.value;
const interfaceName = e.target.dataset.interface;
const modelStructName = e.target.dataset.struct;
// 检查接口类型
const isArray = this.isInterfaceArray(interfaceName, modelStructName);
// 只允许数字、小数点、负号和逗号
const validValue = value.replace(/[^0-9.,-]/g, '');
if (value !== validValue) {
e.target.value = validValue;
}
// 验证格式
const isValid = this.validateInjectValue(validValue, isArray, interfaceName, modelStructName);
e.target.classList.toggle('invalid', !isValid);
// 实时更新表格数据
const row = this.tableData.find(r =>
r.InterfaceName === interfaceName &&
r.ModelStructName === modelStructName
);
if (row) {
row.InjectValue = validValue;
}
});
input.addEventListener('change', (e) => {
const interfaceName = e.target.dataset.interface;
const modelStructName = e.target.dataset.struct;
const value = e.target.value;
// 检查接口类型
const isArray = this.isInterfaceArray(interfaceName, modelStructName);
// 验证格式
if (!this.validateInjectValue(value, isArray, interfaceName, modelStructName)) {
e.target.value = '';
// 清空表格数据中的注入值
const row = this.tableData.find(r =>
r.InterfaceName === interfaceName &&
r.ModelStructName === modelStructName
);
if (row) {
row.InjectValue = '';
}
return;
}
// 更新表格数据
const row = this.tableData.find(r =>
r.InterfaceName === interfaceName &&
r.ModelStructName === modelStructName
);
if (row) {
row.InjectValue = value;
}
});
// 为按钮绑定事件
const deleteButton = tr.querySelector('.action-button.delete');
deleteButton.addEventListener('click', () => {
this.handleDelete(item.InterfaceName, item.ModelStructName);
});
const plotButton = tr.querySelector('.action-button.plot');
plotButton.addEventListener('click', () => {
this.handlePlot(item.InterfaceName, item.ModelStructName);
});
const injectButton = tr.querySelector('.action-button.inject-once');
injectButton.addEventListener('click', async () => {
// 检查监控状态
const statusIndicator = this.shadowRoot.getElementById('statusIndicator');
if (!statusIndicator || !statusIndicator.classList.contains('active')) {
return;
}
const row = this.tableData.find(r =>
r.InterfaceName === item.InterfaceName &&
r.ModelStructName === item.ModelStructName
);
if (!row) {
return;
}
if (!row.InjectValue || row.InjectValue.trim() === '') {
alert('请先输入注入值');
return;
}
// 检查接口类型和注入值格式
const isArray = this.isInterfaceArray(item.InterfaceName, item.ModelStructName);
if (!this.validateInjectValue(row.InjectValue, isArray, item.InterfaceName, item.ModelStructName)) {
alert(isArray ? '请输入正确格式的数组数据(用逗号分隔的数字)' : '请输入单个数字');
return;
}
try {
// 构造接口数据
const interfaceData = {
[item.InterfaceName]: row.InjectValue
};
// 调用注入接口
const response = await fetch('/api/data-monitor/inject', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
structName: item.ModelStructName,
interfaceNameAndData: JSON.stringify(interfaceData)
})
});
const result = await response.json();
if (!result.success) {
throw new Error(result.message);
}
} catch (error) {
console.error('注入失败:', error);
alert(`注入失败: ${error.message}`);
}
});
tbody.appendChild(tr);
}
}
@ -915,6 +1046,29 @@ class DataMonitor extends HTMLElement {
overflow-y: auto;
}
.inject-input {
width: 100%;
padding: 4px 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.inject-input:focus {
border-color: #1890ff;
outline: none;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.inject-input.invalid {
border-color: #ff4d4f;
}
.inject-input.invalid:focus {
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.2);
}
.floating-chart-window {
position: fixed;
width: 400px;
@ -1034,22 +1188,31 @@ class DataMonitor extends HTMLElement {
</tr>
</thead>
<tbody>
${this.tableData.map(row => `
<tr>
<td title="${row.InterfaceName}">${row.InterfaceName}</td>
<td title="${row.ModelStructName}">${row.ModelStructName}</td>
<td class="data-cell" title="${this.formatMonitorData(row.monitorData)}">${this.formatMonitorData(row.monitorData)}</td>
<td title="${row.InjectValue || '-'}">${row.InjectValue || '-'}</td>
<td>
<div class="action-buttons">
<button class="action-button plot" data-interface="${row.InterfaceName}" data-struct="${row.ModelStructName}">绘图</button>
<button class="action-button">注入一次</button>
<button class="action-button">连续注入</button>
<button class="action-button delete" data-interface="${row.InterfaceName}" data-struct="${row.ModelStructName}">删除</button>
</div>
</td>
</tr>
`).join('')}
${this.tableData.map(row => {
return `
<tr>
<td title="${row.InterfaceName}">${row.InterfaceName}</td>
<td title="${row.ModelStructName}">${row.ModelStructName}</td>
<td class="data-cell" title="${this.formatMonitorData(row.monitorData)}">${this.formatMonitorData(row.monitorData)}</td>
<td>
<input type="text"
class="inject-input"
value="${row.InjectValue || ''}"
placeholder="输入注入值"
data-interface="${row.InterfaceName}"
data-struct="${row.ModelStructName}">
</td>
<td>
<div class="action-buttons">
<button class="action-button plot" data-interface="${row.InterfaceName}" data-struct="${row.ModelStructName}">绘图</button>
<button class="action-button inject-once" data-interface="${row.InterfaceName}" data-struct="${row.ModelStructName}">注入一次</button>
<button class="action-button inject-continuous" data-interface="${row.InterfaceName}" data-struct="${row.ModelStructName}">连续注入</button>
<button class="action-button delete" data-interface="${row.InterfaceName}" data-struct="${row.ModelStructName}">删除</button>
</div>
</td>
</tr>
`;
}).join('')}
</tbody>
</table>
</div>
@ -1057,6 +1220,8 @@ class DataMonitor extends HTMLElement {
</div>
`;
const injectInputs = this.shadowRoot.querySelectorAll('.inject-input');
// 搜索框事件
const searchInput = this.shadowRoot.querySelector('.search-input');
if (searchInput) {
@ -1106,6 +1271,136 @@ class DataMonitor extends HTMLElement {
});
});
// 添加注入值输入框的事件处理
injectInputs.forEach(input => {
// 添加输入验证
input.addEventListener('input', (e) => {
const value = e.target.value;
const interfaceName = e.target.dataset.interface;
const modelStructName = e.target.dataset.struct;
// 检查接口类型
const isArray = this.isInterfaceArray(interfaceName, modelStructName);
// 只允许数字、小数点、负号和逗号
const validValue = value.replace(/[^0-9.,-]/g, '');
if (value !== validValue) {
e.target.value = validValue;
}
// 验证格式
const isValid = this.validateInjectValue(validValue, isArray, interfaceName, modelStructName);
e.target.classList.toggle('invalid', !isValid);
// 实时更新表格数据
const row = this.tableData.find(r =>
r.InterfaceName === interfaceName &&
r.ModelStructName === modelStructName
);
if (row) {
row.InjectValue = validValue;
}
});
input.addEventListener('change', (e) => {
const interfaceName = e.target.dataset.interface;
const modelStructName = e.target.dataset.struct;
const value = e.target.value;
// 检查接口类型
const isArray = this.isInterfaceArray(interfaceName, modelStructName);
// 验证格式
if (!this.validateInjectValue(value, isArray, interfaceName, modelStructName)) {
e.target.value = '';
// 清空表格数据中的注入值
const row = this.tableData.find(r =>
r.InterfaceName === interfaceName &&
r.ModelStructName === modelStructName
);
if (row) {
row.InjectValue = '';
}
return;
}
// 更新表格数据
const row = this.tableData.find(r =>
r.InterfaceName === interfaceName &&
r.ModelStructName === modelStructName
);
if (row) {
row.InjectValue = value;
}
});
});
// 添加注入一次按钮的事件委托
table.addEventListener('click', async (e) => {
const injectButton = e.target.closest('.action-button.inject-once');
if (injectButton) {
// 检查监控状态
const statusIndicator = this.shadowRoot.getElementById('statusIndicator');
if (!statusIndicator || !statusIndicator.classList.contains('active')) {
return;
}
const interfaceName = injectButton.dataset.interface;
const modelStructName = injectButton.dataset.struct;
// 获取对应的注入值
const row = this.tableData.find(r =>
r.InterfaceName === interfaceName &&
r.ModelStructName === modelStructName
);
if (!row) {
console.error('未找到对应的行数据');
return;
}
if (!row.InjectValue || row.InjectValue.trim() === '') {
alert('请先输入注入值');
return;
}
// 检查接口类型和注入值格式
const isArray = this.isInterfaceArray(interfaceName, modelStructName);
if (!this.validateInjectValue(row.InjectValue, isArray, interfaceName, modelStructName)) {
alert(isArray ? '请输入正确格式的数组数据(用逗号分隔的数字)' : '请输入单个数字');
return;
}
try {
// 构造接口数据
const interfaceData = {
[interfaceName]: row.InjectValue
};
// 调用注入接口
const response = await fetch('/api/data-monitor/inject', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
structName: modelStructName,
interfaceNameAndData: JSON.stringify(interfaceData)
})
});
const result = await response.json();
if (!result.success) {
throw new Error(result.message);
}
} catch (error) {
console.error('注入失败:', error);
alert(`注入失败: ${error.message}`);
}
}
});
// 添加删除按钮的事件委托
table.addEventListener('click', (e) => {
const deleteButton = e.target.closest('.action-button.delete');
@ -1127,6 +1422,69 @@ class DataMonitor extends HTMLElement {
});
}
/**
* @description 验证注入值的格式
* @param {string} value - 要验证的值
* @param {boolean} isArray - 是否为数组类型
* @param {string} interfaceName - 接口名称
* @param {string} modelStructName - 结构体名称
* @returns {boolean} 是否有效
*/
validateInjectValue(value, isArray = false, interfaceName = '', modelStructName = '') {
if (!value) return true; // 空值视为有效
// 检查是否包含非法字符
if (/[^0-9.,-]/.test(value)) return false;
// 检查逗号分隔的数字
const numbers = value.split(',');
// 如果不是数组类型,不应该有逗号
if (!isArray && numbers.length > 1) return false;
// 验证每个数字的格式
for (const num of numbers) {
if (num && !/^-?\d*\.?\d*$/.test(num)) return false;
}
// 如果是数组类型,验证数组大小
if (isArray && interfaceName && modelStructName) {
const interfaceInfo = this.interfaces.find(item =>
item.InterfaceName === interfaceName &&
item.ModelStructName === modelStructName
);
if (interfaceInfo) {
const size1 = interfaceInfo.InterfaceArraySize_1 || 0;
const size2 = interfaceInfo.InterfaceArraySize_2 || 0;
const expectedSize = size2 <= 1 ? size1 : size1 * size2;
if (numbers.length !== expectedSize) {
return false;
}
}
}
return true;
}
/**
* @description 检查接口是否为数组类型
* @param {string} interfaceName - 接口名称
* @param {string} modelStructName - 结构体名称
* @returns {boolean} 是否为数组类型
*/
isInterfaceArray(interfaceName, modelStructName) {
// 从接口数据中查找对应的接口信息
const interfaceInfo = this.interfaces.find(item =>
item.InterfaceName === interfaceName &&
item.ModelStructName === modelStructName
);
// 如果找到接口信息,检查是否为数组类型
return interfaceInfo ? interfaceInfo.InterfaceIsArray : false;
}
/**
* @description 处理删除按钮点击事件
* @param {string} interfaceName - 接口名称