pageError.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. // error.js - 示值误差测试相关功能
  2. // 图表相关变量
  3. let errorCtx = null;
  4. let errorChartInstance = null;
  5. function initializeErrorPage() {
  6. // 生成示值误差标签页内容
  7. generateErrorTabContent();
  8. // 加载示值误差数据
  9. loadErrorData();
  10. }
  11. // 生成示值误差标签页内容
  12. function generateErrorTabContent() {
  13. console.log('generateErrorTabContent');
  14. const errorTab = document.createElement('div');
  15. errorTab.id = 'error';
  16. errorTab.className = 'tab-content';
  17. // 创建内容容器
  18. const contentContainer = document.createElement('div');
  19. contentContainer.className = 'content-container';
  20. // 创建输入部分
  21. const inputSection = document.createElement('div');
  22. inputSection.className = 'input-section';
  23. inputSection.style.width = '100%';
  24. // 创建误差容器
  25. const errorContainer = document.createElement('div');
  26. errorContainer.className = 'error-container';
  27. errorContainer.id = 'error-inputs';
  28. // 添加按钮
  29. const addInputBtn = document.createElement('div');
  30. addInputBtn.className = 'add-input-btn';
  31. addInputBtn.onclick = addErrorPoint;
  32. const addInputSpan = document.createElement('span');
  33. addInputSpan.textContent = '+ 添加测试点';
  34. addInputBtn.appendChild(addInputSpan);
  35. // 创建不确定度结果部分
  36. const uncertaintyResults = document.createElement('div');
  37. uncertaintyResults.className = 'uncertainty-results';
  38. uncertaintyResults.style.marginTop = '1rem';
  39. uncertaintyResults.style.padding = '1rem';
  40. uncertaintyResults.style.border = '1px solid #eee';
  41. uncertaintyResults.style.borderRadius = '4px';
  42. // 添加标题
  43. const uncertaintyTitle = document.createElement('h3');
  44. uncertaintyTitle.style.marginTop = '0';
  45. uncertaintyTitle.textContent = '示值误差校准结果不确定度';
  46. uncertaintyResults.appendChild(uncertaintyTitle);
  47. // 创建结果显示
  48. const results = document.createElement('div');
  49. results.className = 'results';
  50. // 添加结果项
  51. const resultItems = [
  52. { label: 'A类不确定度(uA):', id: 'uncertainty-a' },
  53. { label: 'B类不确定度(uB):', id: 'uncertainty-b' },
  54. { label: 'C类不确定度(uc):', id: 'uncertainty-c' },
  55. { label: '扩展不确定度(U):', id: 'uncertainty-u' }
  56. ];
  57. resultItems.forEach(item => {
  58. const resultItem = document.createElement('div');
  59. resultItem.className = 'result-item';
  60. resultItem.innerHTML = `${item.label} <span id="${item.id}">-</span>`;
  61. results.appendChild(resultItem);
  62. });
  63. uncertaintyResults.appendChild(results);
  64. // 创建图表容器
  65. const chartContainer = document.createElement('div');
  66. chartContainer.className = 'chart-container';
  67. const canvas = document.createElement('canvas');
  68. canvas.id = 'errorChart';
  69. chartContainer.appendChild(canvas);
  70. errorCtx = canvas.getContext('2d');
  71. // 组装输入部分
  72. inputSection.appendChild(errorContainer);
  73. inputSection.appendChild(addInputBtn);
  74. inputSection.appendChild(uncertaintyResults);
  75. inputSection.appendChild(chartContainer);
  76. // 组装整个内容
  77. contentContainer.appendChild(inputSection);
  78. errorTab.appendChild(contentContainer);
  79. if (document.getElementById('container')) {
  80. console.log('appendChild errorTab');
  81. document.getElementById('container').appendChild(errorTab);
  82. }
  83. // 添加初始示值误差测试点(因为已移除所有默认测试点)
  84. if (document.querySelectorAll('#error-inputs .error-point-container').length === 0) {
  85. addErrorPoint();
  86. }
  87. }
  88. // 示值误差测试点容器创建
  89. function createErrorPointContainer(pointValue = '') {
  90. const container = document.createElement('div');
  91. container.className = 'error-point-container';
  92. container.style.marginBottom = '1rem';
  93. container.style.padding = '1rem';
  94. container.style.border = '1px solid #eee';
  95. container.style.borderRadius = '4px';
  96. // 测试点输入
  97. const pointRow = document.createElement('div');
  98. pointRow.style.display = 'flex';
  99. pointRow.style.alignItems = 'center';
  100. pointRow.style.marginBottom = '0.5rem';
  101. const pointLabel = document.createElement('label');
  102. pointLabel.textContent = '测试点:';
  103. pointLabel.style.marginRight = '0.5rem';
  104. pointLabel.style.fontWeight = 'bold';
  105. const pointInput = document.createElement('input');
  106. pointInput.type = 'number';
  107. pointInput.className = 'error-point-input';
  108. pointInput.placeholder = '测试点';
  109. pointInput.value = pointValue;
  110. pointInput.setAttribute('data-original', pointValue);
  111. pointInput.style.flex = '1';
  112. pointInput.style.marginRight = '0.5rem';
  113. pointInput.oninput = function () {
  114. updateErrorPoint(this);
  115. // 保存示值误差数据
  116. saveErrorData();
  117. };
  118. const deleteBtn = document.createElement('span');
  119. deleteBtn.className = 'delete-btn';
  120. deleteBtn.innerHTML = '×';
  121. deleteBtn.onclick = function () {
  122. container.parentNode.removeChild(container);
  123. calculateError();
  124. // 保存误差数据
  125. saveErrorData();
  126. };
  127. pointRow.appendChild(pointLabel);
  128. pointRow.appendChild(pointInput);
  129. pointRow.appendChild(deleteBtn);
  130. // 输入值部分
  131. const inputsContainer = document.createElement('div');
  132. inputsContainer.className = 'error-inputs';
  133. inputsContainer.style.display = 'flex';
  134. inputsContainer.style.flexWrap = 'wrap';
  135. inputsContainer.style.gap = '0.5rem';
  136. inputsContainer.style.marginBottom = '0.5rem';
  137. // 添加3个默认输入框
  138. for (let i = 0; i < 3; i++) {
  139. const input = document.createElement('input');
  140. input.type = 'number';
  141. input.className = 'error-input';
  142. input.placeholder = `输入值`;
  143. input.setAttribute('data-point', pointValue);
  144. input.style.flex = '1';
  145. input.style.minWidth = '100px';
  146. input.oninput = function () {
  147. calculateError();
  148. // 保存误差数据到localStorage
  149. saveErrorData();
  150. };
  151. inputsContainer.appendChild(input);
  152. }
  153. // 结果显示部分
  154. const resultsRow = document.createElement('div');
  155. resultsRow.style.display = 'flex';
  156. resultsRow.style.flexDirection = 'column';
  157. const avgContainer = document.createElement('div');
  158. avgContainer.style.flex = '1';
  159. const avgLabel = document.createElement('span');
  160. avgLabel.textContent = '平均值:';
  161. avgLabel.style.fontWeight = 'bold';
  162. const avgSpan = document.createElement('span');
  163. avgSpan.className = 'error-avg';
  164. avgSpan.setAttribute('data-point', pointValue);
  165. avgSpan.textContent = '-';
  166. avgContainer.appendChild(avgLabel);
  167. avgContainer.appendChild(avgSpan);
  168. const errorContainer = document.createElement('div');
  169. errorContainer.style.flex = '1';
  170. const errorLabel = document.createElement('span');
  171. errorLabel.textContent = '示值误差:';
  172. errorLabel.style.fontWeight = 'bold';
  173. const errorSpan = document.createElement('span');
  174. errorSpan.className = 'error-value';
  175. errorSpan.setAttribute('data-point', pointValue);
  176. errorSpan.textContent = '-';
  177. errorContainer.appendChild(errorLabel);
  178. errorContainer.appendChild(errorSpan);
  179. resultsRow.appendChild(avgContainer);
  180. resultsRow.appendChild(errorContainer);
  181. container.appendChild(pointRow);
  182. container.appendChild(inputsContainer);
  183. container.appendChild(resultsRow);
  184. return container;
  185. }
  186. // 示值误差测试点添加
  187. function addErrorPoint() {
  188. const container = document.getElementById('error-inputs');
  189. const pointContainer = createErrorPointContainer();
  190. container.appendChild(pointContainer);
  191. }
  192. // 示值误差测试点更新
  193. function updateErrorPoint(input) {
  194. const pointContainer = input.closest('.error-point-container');
  195. const originalValue = input.getAttribute('data-original');
  196. const newValue = input.value;
  197. // 如果测试点值发生变化,更新所有相关元素的data-point属性
  198. if (originalValue !== newValue) {
  199. input.setAttribute('data-original', newValue);
  200. pointContainer.querySelectorAll('.error-input').forEach(input => {
  201. input.setAttribute('data-point', newValue);
  202. });
  203. pointContainer.querySelector('.error-avg').setAttribute('data-point', newValue);
  204. pointContainer.querySelector('.error-value').setAttribute('data-point', newValue);
  205. }
  206. calculateError();
  207. saveErrorData();
  208. }
  209. // 清除示值误差数据
  210. function clearErrorData() {
  211. // 清除示值误差数据
  212. const errorContainer = document.getElementById('error-inputs');
  213. while (errorContainer.firstChild) {
  214. errorContainer.removeChild(errorContainer.firstChild);
  215. }
  216. addErrorPoint();
  217. // 重置图表
  218. if (errorChartInstance) {
  219. errorChartInstance.destroy();
  220. errorChartInstance = null;
  221. }
  222. }
  223. // 示值误差数据计算
  224. function calculateError() {
  225. const pointContainers = document.querySelectorAll('.error-point-container');
  226. const errorData = [];
  227. pointContainers.forEach(container => {
  228. const pointInput = container.querySelector('.error-point-input');
  229. const inputs = container.querySelectorAll('.error-input');
  230. const avgSpan = container.querySelector('.error-avg');
  231. const errorSpan = container.querySelector('.error-value');
  232. const pointValue = parseFloat(pointInput.value);
  233. const values = Array.from(inputs)
  234. .map(input => parseFloat(input.value))
  235. .filter(value => !isNaN(value));
  236. if (!isNaN(pointValue) && values.length > 0) {
  237. // 计算平均值
  238. const avg = values.reduce((sum, val) => sum + val, 0) / values.length;
  239. // 计算示值误差
  240. const error = avg - pointValue;
  241. // 更新显示
  242. avgSpan.textContent = avg.toFixed(6);
  243. errorSpan.textContent = error.toFixed(6);
  244. // 收集数据用于图表和不确定度计算
  245. errorData.push({
  246. point: pointValue,
  247. avg: avg,
  248. error: error
  249. });
  250. } else {
  251. avgSpan.textContent = '-';
  252. errorSpan.textContent = '-';
  253. }
  254. });
  255. // 计算不确定度
  256. calculateUncertainty(errorData);
  257. // 保存误差数据
  258. saveErrorData();
  259. // 更新图表
  260. updateErrorChart(errorData);
  261. return errorData;
  262. }
  263. // 示值误差不确定度计算
  264. function calculateUncertainty(errorData) {
  265. if (errorData.length === 0) {
  266. // 清空不确定度显示
  267. document.getElementById('uncertainty-a').textContent = '-';
  268. document.getElementById('uncertainty-b').textContent = '-';
  269. document.getElementById('uncertainty-c').textContent = '-';
  270. document.getElementById('uncertainty-u').textContent = '-';
  271. return;
  272. }
  273. // 计算A类不确定度(重复测量的标准偏差)
  274. let sumSquaredDeviations = 0;
  275. let totalMeasurements = 0;
  276. document.querySelectorAll('.error-point-container').forEach(container => {
  277. const inputs = container.querySelectorAll('.error-input');
  278. const values = Array.from(inputs)
  279. .map(input => parseFloat(input.value))
  280. .filter(value => !isNaN(value));
  281. if (values.length > 0) {
  282. const avg = values.reduce((sum, val) => sum + val, 0) / values.length;
  283. values.forEach(value => {
  284. sumSquaredDeviations += Math.pow(value - avg, 2);
  285. });
  286. totalMeasurements += values.length;
  287. }
  288. });
  289. // 计算A类不确定度
  290. const uA = totalMeasurements > 1 ? Math.sqrt(sumSquaredDeviations / (totalMeasurements * (totalMeasurements - 1))) : 0;
  291. // 假设B类不确定度(仪器分辨率引起的不确定度)
  292. // 这里假设分辨率为0.001,则B类不确定度为0.001/√3
  293. const resolution = 0.001;
  294. const uB = resolution / Math.sqrt(3);
  295. // 合成不确定度
  296. const uC = Math.sqrt(Math.pow(uA, 2) + Math.pow(uB, 2));
  297. // 扩展不确定度(k=2,约95%置信水平)
  298. const U = 2 * uC;
  299. // 更新显示
  300. document.getElementById('uncertainty-a').textContent = uA.toExponential(3);
  301. document.getElementById('uncertainty-b').textContent = uB.toExponential(3);
  302. document.getElementById('uncertainty-c').textContent = uC.toExponential(3);
  303. document.getElementById('uncertainty-u').textContent = U.toExponential(3);
  304. }
  305. // 示值误差图表更新
  306. function updateErrorChart(data) {
  307. if (data.length === 0) {
  308. if (errorChartInstance) {
  309. errorChartInstance.data.labels = [];
  310. errorChartInstance.data.datasets[0].data = [];
  311. errorChartInstance.update();
  312. }
  313. return;
  314. }
  315. // 按测试点值排序
  316. data.sort((a, b) => a.point - b.point);
  317. // 提取标签和误差值
  318. const labels = data.map(item => item.point);
  319. const errorValues = data.map(item => item.error);
  320. // 使用通用图表创建函数
  321. errorChartInstance = createOrUpdateChart(errorChartInstance, errorCtx, labels, errorValues, '示值误差');
  322. }
  323. // 示值误差数据保存
  324. function saveErrorData() {
  325. // 收集输入数据
  326. const inputs = Array.from(document.querySelectorAll('.error-point-container')).map(container => {
  327. const pointInput = container.querySelector('.error-point-input');
  328. const inputs = container.querySelectorAll('.error-input');
  329. const avgSpan = container.querySelector('.error-avg');
  330. const errorSpan = container.querySelector('.error-value');
  331. return {
  332. point: pointInput.value,
  333. value: Array.from(inputs).map(input => input.value),
  334. avg: avgSpan.innerText,
  335. error: errorSpan.innerText
  336. };
  337. });
  338. // 收集结果数据
  339. const results = {
  340. uncertainty_a: document.getElementById('uncertainty-a').textContent,
  341. uncertainty_b: document.getElementById('uncertainty-b').textContent,
  342. uncertainty_c: document.getElementById('uncertainty-c').textContent,
  343. uncertainty_u: document.getElementById('uncertainty-u').textContent
  344. };
  345. const data = {
  346. inputs: inputs,
  347. results: results
  348. };
  349. const dataName = 'errorData';
  350. console.log(dataName, data);
  351. setLocalStorage(dataName, JSON.stringify(data));
  352. }
  353. // 示值误差数据加载
  354. function loadErrorData() {
  355. const savedErrorData = getLocalStorage('errorData');
  356. if (savedErrorData) {
  357. try {
  358. const data = JSON.parse(savedErrorData);
  359. // 清除现有的测试点
  360. const errorContainer = document.getElementById('error-inputs');
  361. while (errorContainer.firstChild) {
  362. errorContainer.removeChild(errorContainer.firstChild);
  363. }
  364. // 添加保存的测试点
  365. if (data.inputs && data.inputs.length > 0) {
  366. data.inputs.forEach((item, index) => {
  367. const point = typeof item === 'object' ? item.point : '';
  368. const value = typeof item === 'object' ? item.value : '';
  369. const pointContainer = createErrorPointContainer(item.point);
  370. errorContainer.appendChild(pointContainer);
  371. // 填充测试点
  372. pointContainer.querySelector('.error-point-input').value = point;
  373. // 填充输入值
  374. const inputs = pointContainer.querySelectorAll('.error-input');
  375. value.forEach((value, index) => {
  376. if (index < inputs.length) {
  377. inputs[index].value = value;
  378. }
  379. });
  380. });
  381. }
  382. // 计算结果
  383. calculateError();
  384. } catch (e) {
  385. console.error('加载示值误差数据失败:', e);
  386. }
  387. }
  388. }