Compare commits
No commits in common. "main" and "code-example" have entirely different histories.
main
...
code-examp
54
FSW.py
54
FSW.py
@ -1,54 +0,0 @@
|
||||
# python script created by FSW: 24:11:2020 04:53:44
|
||||
import visa
|
||||
def write_command(instrument, command) :
|
||||
instrument.write(command)
|
||||
return process_system_error(instrument)
|
||||
def write_query(instrument, command) :
|
||||
buffer = instrument.query(command)
|
||||
bSuccess = process_system_error(instrument)
|
||||
return bSuccess, buffer
|
||||
def process_system_error(instrument) :
|
||||
bSuccess = True
|
||||
EsrErrorMask = 0x3C
|
||||
if ((get_esr(instrument) & EsrErrorMask) != 0) :
|
||||
print(instrument.query(":SYST:ERR?"))
|
||||
instrument.write("*CLS")
|
||||
bSuccess = False
|
||||
return bSuccess
|
||||
def get_esr(instrument) :
|
||||
esr = instrument.query("*ESR?")
|
||||
return int(esr)
|
||||
VisaResourceManager = visa.ResourceManager()
|
||||
#set center freQ
|
||||
fre = '200e6'
|
||||
descr = '_harm_dis_meas'
|
||||
#location on FSW
|
||||
tar_loc = 'd:\\murmur\\'
|
||||
# connect to analyzer
|
||||
Analyzer = VisaResourceManager.open_resource("TCPIP::192.168.1.43::inst0::INSTR")
|
||||
print(Analyzer.query("*IDN?"))
|
||||
success = write_command( Analyzer, "*CLS" )
|
||||
success = write_command( Analyzer, ":SYST:DISP:UPD ON" )
|
||||
success = write_command( Analyzer, ":INIT:CONT OFF" )
|
||||
#success = write_command( Analyzer, ":DISP:WIND:SUBW:TRAC:Y:SCAL:RLEV 10" )
|
||||
success = write_command( Analyzer, ":SENS:FREQ:CENT " + fre )
|
||||
success = write_command( Analyzer, ":CALC:MARK:FUNC:HARM:STAT ON" )
|
||||
success = write_command( Analyzer, ":INIT:CONT ON" )
|
||||
success = write_command( Analyzer, ":FORM:DEXP:DSEP POIN" )
|
||||
success = write_command( Analyzer, ":FORM:DEXP:FORM CSV" )
|
||||
success = write_command( Analyzer, ":FORM:DEXP:HEAD OFF" )
|
||||
success = write_command( Analyzer, ":FORM:DEXP:TRAC ALL" )
|
||||
#success = write_command( Analyzer, ":MMEM:STOR1:TRAC 1,'d:\\" + fre + descr +".CSV'" )
|
||||
#
|
||||
success = write_command( Analyzer, ":MMEM:STOR1:TRAC 1,'" + tar_loc + fre + descr +".CSV'" )
|
||||
success, data = write_query(Analyzer, f"MMEM:DATA? '{tar_loc}screenshot.png'")
|
||||
with open("local_screenshot.png", "wb") as f:
|
||||
f.write(data)
|
||||
success = write_command( Analyzer, ":SENS:FREQ:MODE SWE" )
|
||||
# back to local mode
|
||||
success = write_command(Analyzer, "@LOC")
|
||||
if success:
|
||||
print("Done.\r\nFile's location is {}{}{}.CSV.\r\n".format(tar_loc,fre,descr))
|
||||
# cleanup
|
||||
Analyzer.close()
|
||||
VisaResourceManager.close()
|
74
README.md
74
README.md
@ -1,74 +0,0 @@
|
||||
# 功能
|
||||
采集板具备深度计、加速度计和陀螺仪,可采集相关数据并以`*.BIN`格式存储,可通过工具格式化输出为`*.CSV`,支持Excel或文本编辑器查看、编辑。
|
||||
|
||||
# 采集状态检查
|
||||
|
||||
调试口有调试串口(TTL/115200-8-1-N),具备查看采集状态的功能。
|
||||
|
||||
采集状态有误时会不断重启,正常状态则会不断刷新采集的数据。
|
||||
|
||||
如遇采集状态有误,则需要给水密桶`断电、稍等、重新上电`尝试恢复。
|
||||
|
||||
# 采集数据格式
|
||||
原始采集的数据文件后缀为`.BIN`,以结构体`info_t`小端序存储。
|
||||
```c
|
||||
// MPU传感器数据结构
|
||||
typedef struct {
|
||||
int16_t ax; // 加速度计 X轴
|
||||
int16_t ay; // 加速度计 Y轴
|
||||
int16_t az; // 加速度计 Z轴
|
||||
int16_t gx; // 陀螺仪 X轴
|
||||
int16_t gy; // 陀螺仪 Y轴
|
||||
int16_t gz; // 陀螺仪 Z轴
|
||||
} sensor_data_t;
|
||||
|
||||
// 完整的数据记录结构
|
||||
#pragma pack(1)
|
||||
typedef struct {
|
||||
uint8_t y; // 年(相对值,需要加上2000)
|
||||
uint8_t month; // 月
|
||||
uint8_t d; // 日
|
||||
uint8_t h; // 时
|
||||
uint8_t m; // 分
|
||||
uint8_t s; // 秒
|
||||
uint16_t ms; // 毫秒
|
||||
int16_t deepth; // 深度
|
||||
sensor_data_t data; // MPU传感器数据
|
||||
} info_t;
|
||||
```
|
||||
# 导出采集数据
|
||||
|
||||
调试口连有一A口USB接头,插入电脑后**稍作等待**,系统会多出一个盘符,双击打开可浏览采集的数据文件,其以日期为文件名,形如`20241112.BIN`。
|
||||
|
||||
选中采集文件后**用鼠标拖入**本地磁盘某位置即可。
|
||||
|
||||
# 解析采集数据
|
||||
|
||||
`WIN+R`输入cmd后按回车键运行命令行,在命令行中输入`decode.exe`回车,会提示具体的使用方法,如:
|
||||
|
||||
```
|
||||
用法:
|
||||
decode.exe -f <文件名> [-o <输出文件名>]
|
||||
选项:
|
||||
-f <文件名> 指定输入文件
|
||||
-o <文件名> 指定输出文件(可选,默认输出到与输入同名的.csv文件)
|
||||
-h 显示帮助信息
|
||||
|
||||
示例:
|
||||
decode.exe -f 20241112.BIN
|
||||
decode.exe -f 20241112.BIN -o output.csv
|
||||
```
|
||||
|
||||
运行结果如:
|
||||
|
||||
```cmd
|
||||
D:\Users\murmur\Desktop\test>decode.exe -f 20241112.BIN
|
||||
系统类型: Windows
|
||||
系统字节序: 小端序
|
||||
共解析 46980 条记录
|
||||
```
|
||||
|
||||
# 异常处理
|
||||
|
||||
可通过转换后文件内的日期数据判断解析是否正确,如不正确请重新导出。
|
||||
|
@ -1,315 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>输入框自动全选和放大提示</title>
|
||||
<style>
|
||||
/* 基本样式 */
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 200px;
|
||||
padding: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: 'Consolas';
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
button {
|
||||
padding: 10px 15px;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
/* 提示数字样式 */
|
||||
.number-tip {
|
||||
position: absolute;
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
font-family: 'Consolas';
|
||||
color: #333;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s ease, transform 0.5s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.number-tip.hide {
|
||||
opacity: 0;
|
||||
transform: scale(0.5);
|
||||
}
|
||||
|
||||
/* 颜色样式 */
|
||||
.color-0 {
|
||||
color: #153fea;
|
||||
}
|
||||
|
||||
.color-1 {
|
||||
color: #15f00e;
|
||||
}
|
||||
|
||||
.color-2 {
|
||||
color: #6963688a;
|
||||
}
|
||||
|
||||
/* 错误状态样式 */
|
||||
.number-tip.error {
|
||||
background: rgba(255, 200, 200, 0.9);
|
||||
border-color: #ff0000;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
label {
|
||||
/* display: block; */
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="form-group">
|
||||
<label for="number1">数字 1:</label>
|
||||
<input type="text" id="number1" placeholder="请输入数字 1">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="number2">数字 2:</label>
|
||||
<input type="text" id="number2" placeholder="请输入数字 2">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="number3">数字 3:</label>
|
||||
<input type="text" id="number3" placeholder="请输入数字 3">
|
||||
</div>
|
||||
|
||||
<button id="focusNumber1">激活数字 1 输入框</button>
|
||||
<button id="focusNumber2">激活数字 2 输入框</button>
|
||||
<button id="focusNumber3">激活数字 3 输入框</button>
|
||||
|
||||
<div id="numberTip" class="number-tip hide"></div>
|
||||
|
||||
<script>
|
||||
// 获取所有的输入框和提示元素
|
||||
const inputBoxes = document.querySelectorAll('input[type="text"]');
|
||||
const numberTip = document.getElementById('numberTip');
|
||||
|
||||
// 按钮点击事件
|
||||
document.getElementById('focusNumber1').addEventListener('click', () => {
|
||||
document.getElementById('number1').focus();
|
||||
});
|
||||
|
||||
document.getElementById('focusNumber2').addEventListener('click', () => {
|
||||
document.getElementById('number2').focus();
|
||||
});
|
||||
|
||||
document.getElementById('focusNumber3').addEventListener('click', () => {
|
||||
document.getElementById('number3').focus();
|
||||
});
|
||||
|
||||
// 添加输入范围配置
|
||||
const inputRanges = {
|
||||
0: { min: 0, max: 100 }, // 第一个输入框:0-100
|
||||
1: { min: 100, max: 1000 }, // 第二个输入框:100-1000
|
||||
2: { min: 1000, max: 10000 } // 第三个输入框:1000-10000
|
||||
};
|
||||
|
||||
// 为所有输入框添加事件监听
|
||||
inputBoxes.forEach((inputBox, index) => {
|
||||
// 输入框获取焦点时
|
||||
inputBox.addEventListener('focus', (event) => {
|
||||
event.target.select();
|
||||
const value = event.target.value;
|
||||
if (value !== '') {
|
||||
const rawValue = value.replace(/\s/g, '');
|
||||
showNumberTip(formatNumberWithColor(rawValue), inputBox, index);
|
||||
}
|
||||
});
|
||||
|
||||
inputBox.addEventListener('input', (event) => {
|
||||
const value = event.target.value;
|
||||
if (value !== '') {
|
||||
const rawValue = value.replace(/\s/g, '');
|
||||
const numValue = parseFloat(rawValue);
|
||||
const range = inputRanges[index];
|
||||
|
||||
// 只检查最大值,允许临时小于最小值
|
||||
if (!isNaN(numValue) && numValue <= range.max) {
|
||||
// 保存有效值
|
||||
event.target.value = formatNumber(rawValue);
|
||||
event.target.dataset.lastValidValue = event.target.value;
|
||||
// 显示放大提示
|
||||
showNumberTip(formatNumberWithColor(rawValue), inputBox, index, false);
|
||||
} else if (!isNaN(numValue) && numValue > range.max) {
|
||||
// 如果超出最大值,显示错误提示
|
||||
showNumberTip(
|
||||
`超出最大值 ${range.max}`,
|
||||
inputBox,
|
||||
index,
|
||||
true
|
||||
);
|
||||
// 还原到上一个有效值或清空
|
||||
if (event.target.dataset.lastValidValue) {
|
||||
event.target.value = event.target.dataset.lastValidValue;
|
||||
} else {
|
||||
event.target.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 输入框失去焦点时
|
||||
inputBox.addEventListener('blur', (event) => {
|
||||
const value = event.target.value;
|
||||
let finalValue = value;
|
||||
|
||||
// 处理小数点
|
||||
if (value.includes('.')) {
|
||||
const [integerPart, decimalPart] = value.split('.');
|
||||
if (!decimalPart || decimalPart.replace(/[^\d]/g, '').length === 0) {
|
||||
finalValue = integerPart;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查最小值
|
||||
if (finalValue !== '') {
|
||||
const numValue = parseFloat(finalValue.replace(/\s/g, ''));
|
||||
const range = inputRanges[index];
|
||||
|
||||
if (!isNaN(numValue)) {
|
||||
if (numValue < range.min) {
|
||||
// 如果小于最小值,显示错误提示
|
||||
showNumberTip(
|
||||
`不能小于最小值 ${range.min}`,
|
||||
inputBox,
|
||||
index,
|
||||
true
|
||||
);
|
||||
// 设置为最小值
|
||||
event.target.value = range.min;
|
||||
} else if (numValue <= range.max) {
|
||||
// 值在有效范围内
|
||||
event.target.value = finalValue;
|
||||
// 隐藏提示
|
||||
numberTip.classList.add('hide');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// 修改数字格式化函数,移除输入框的颜色格式化
|
||||
function formatNumber(value) {
|
||||
// 分割整数部分和小数部分
|
||||
const [integerPart, decimalPart] = value.split('.');
|
||||
|
||||
// 格式化整数部分,只添加空格,不添加颜色
|
||||
const formattedInteger = integerPart
|
||||
.replace(/[^\d]/g, '')
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
|
||||
|
||||
// 格式化小数部分,只添加空格,不添加颜色
|
||||
if (decimalPart !== undefined) {
|
||||
const formattedDecimal = decimalPart
|
||||
.replace(/[^\d]/g, '')
|
||||
.replace(/(\d{3})/g, '$1 ')
|
||||
.trim();
|
||||
return `${formattedInteger}.${formattedDecimal}`;
|
||||
}
|
||||
|
||||
return formattedInteger;
|
||||
}
|
||||
|
||||
// 修改彩色格式化函数
|
||||
function formatNumberWithColor(value) {
|
||||
if (!value.includes('.')) {
|
||||
// 如果没有小数点,从右向左添加颜色
|
||||
return value
|
||||
.replace(/[^\d]/g, '')
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ' ')
|
||||
.split(' ')
|
||||
.map((group, index, array) => `<span class="color-${(array.length - 1 - index) % 3}">${group}</span>`)
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
// 有小数点的情况
|
||||
const [integerPart, decimalPart] = value.split('.');
|
||||
|
||||
// 整数部分:从右向左添加颜色
|
||||
const coloredInteger = integerPart
|
||||
.replace(/[^\d]/g, '')
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ' ')
|
||||
.split(' ')
|
||||
.map((group, index, array) => `<span class="color-${(array.length - 1 - index) % 3}">${group}</span>`)
|
||||
.join(' ');
|
||||
|
||||
// 小数部分:从左向右添加颜色
|
||||
const coloredDecimal = decimalPart
|
||||
.replace(/[^\d]/g, '')
|
||||
.replace(/(\d{3})/g, '$1 ')
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map((group, index) => `<span class="color-${index % 3}">${group}</span>`)
|
||||
.join(' ');
|
||||
|
||||
return `${coloredInteger}.${coloredDecimal}`;
|
||||
}
|
||||
|
||||
// 添加一个变量来存储定时器
|
||||
let hideTimer;
|
||||
|
||||
// 更新显示提示的函数
|
||||
function showNumberTip(value, inputBox, index, isError = false) {
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer);
|
||||
}
|
||||
|
||||
numberTip.innerHTML = value;
|
||||
numberTip.classList.toggle('error', isError);
|
||||
|
||||
const rect = inputBox.getBoundingClientRect();
|
||||
const tipRect = numberTip.getBoundingClientRect();
|
||||
|
||||
let left = rect.left;
|
||||
let top = rect.bottom;
|
||||
if (top + tipRect.height > window.innerHeight) {
|
||||
top = rect.top - tipRect.height;
|
||||
}
|
||||
|
||||
numberTip.style.left = `${left}px`;
|
||||
numberTip.style.top = `${top}px`;
|
||||
numberTip.classList.remove('hide');
|
||||
|
||||
hideTimer = setTimeout(() => {
|
||||
numberTip.classList.add('hide');
|
||||
}, 6000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,176 +0,0 @@
|
||||
import visa
|
||||
from typing import Optional, Tuple, Union
|
||||
import os
|
||||
from datetime import datetime
|
||||
import tempfile
|
||||
|
||||
class InstrumentInterface:
|
||||
def __init__(self, resource_string: str):
|
||||
"""
|
||||
初始化仪器接口
|
||||
Args:
|
||||
resource_string: VISA资源字符串,例如 "TCPIP::192.168.1.43::inst0::INSTR"
|
||||
"""
|
||||
self.rm = visa.ResourceManager()
|
||||
self.instrument = self.rm.open_resource(resource_string)
|
||||
self.instrument.timeout = 30000 # 30秒超时
|
||||
|
||||
# 查询仪器信息以确定系统类型
|
||||
success, idn = self.query("*IDN?")
|
||||
if success:
|
||||
self.instrument_info = idn
|
||||
# 根据IDN确定仪器类型和路径
|
||||
if "R&S" in idn:
|
||||
self.temp_path = "/tmp/" # R&S通常基于Linux
|
||||
elif "Keysight" in idn or "Agilent" in idn:
|
||||
self.temp_path = "C:/temp/" # Keysight通常基于Windows
|
||||
else:
|
||||
# 默认使用/tmp/,因为大多数仪器基于Linux
|
||||
self.temp_path = "/tmp/"
|
||||
else:
|
||||
raise ConnectionError("Failed to identify instrument")
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
def write(self, command: str) -> bool:
|
||||
"""发送SCPI命令"""
|
||||
try:
|
||||
self.instrument.write(command)
|
||||
return self._check_error()
|
||||
except Exception as e:
|
||||
print(f"Error writing command {command}: {str(e)}")
|
||||
return False
|
||||
|
||||
def query(self, command: str) -> Tuple[bool, str]:
|
||||
"""查询SCPI命令"""
|
||||
try:
|
||||
response = self.instrument.query(command)
|
||||
success = self._check_error()
|
||||
return success, response.strip()
|
||||
except Exception as e:
|
||||
print(f"Error querying {command}: {str(e)}")
|
||||
return False, ""
|
||||
|
||||
def _check_error(self) -> bool:
|
||||
"""检查仪器错误"""
|
||||
try:
|
||||
errors = self.instrument.query("SYST:ERR?")
|
||||
if "No error" in errors:
|
||||
return True
|
||||
print(f"Instrument error: {errors}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Error checking system errors: {str(e)}")
|
||||
return False
|
||||
|
||||
def _transfer_file_to_local(self,
|
||||
instrument_path: str,
|
||||
local_path: str,
|
||||
delete_source: bool = True) -> bool:
|
||||
"""
|
||||
从仪器传输文件到本地
|
||||
Args:
|
||||
instrument_path: 仪器上的文件路径
|
||||
local_path: 本地保存路径
|
||||
delete_source: 传输后是否删除仪器上的源文件
|
||||
Returns:
|
||||
bool: 操作是否成功
|
||||
"""
|
||||
try:
|
||||
# 确保本地目录存在
|
||||
os.makedirs(os.path.dirname(os.path.abspath(local_path)), exist_ok=True)
|
||||
|
||||
# 读取仪器文件数据
|
||||
success, data = self.query(f"MMEM:DATA? '{instrument_path}'")
|
||||
if not success:
|
||||
return False
|
||||
|
||||
# 保存到本地
|
||||
with open(local_path, 'wb') as f:
|
||||
f.write(data)
|
||||
|
||||
# 清理仪器上的源文件(如果需要)
|
||||
if delete_source:
|
||||
self.write(f"MMEM:DEL '{instrument_path}'")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error transferring file: {str(e)}")
|
||||
return False
|
||||
|
||||
def screenshot_to_file(self,
|
||||
local_path: str,
|
||||
format: str = "PNG") -> bool:
|
||||
"""
|
||||
保存仪器截图到本地文件
|
||||
"""
|
||||
try:
|
||||
# 生成临时文件名
|
||||
temp_filename = f"screenshot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{format.lower()}"
|
||||
temp_filepath = os.path.join(self.temp_path, temp_filename).replace('\\', '/')
|
||||
|
||||
# 配置并执行截图
|
||||
self.write(f":HCOP:DEV:LANG {format}")
|
||||
self.write(f":MMEM:NAME '{temp_filepath}'")
|
||||
self.write(":HCOP:IMM") # 执行截图
|
||||
|
||||
# 传输文件到本地
|
||||
return self._transfer_file_to_local(temp_filepath, local_path)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error saving screenshot: {str(e)}")
|
||||
return False
|
||||
|
||||
def data_to_file(self,
|
||||
local_path: str,
|
||||
trace_number: int = 1,
|
||||
format: str = "CSV") -> bool:
|
||||
"""
|
||||
保存轨迹数据到本地文件
|
||||
"""
|
||||
try:
|
||||
# 生成临时文件名
|
||||
temp_filename = f"trace_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{format.lower()}"
|
||||
temp_filepath = os.path.join(self.temp_path, temp_filename).replace('\\', '/')
|
||||
|
||||
# 配置数据导出格式
|
||||
self.write(":FORM:DEXP:DSEP POIN")
|
||||
self.write(f":FORM:DEXP:FORM {format}")
|
||||
self.write(":FORM:DEXP:HEAD OFF")
|
||||
self.write(":FORM:DEXP:TRAC ALL")
|
||||
|
||||
# 保存到仪器临时文件
|
||||
self.write(f":MMEM:STOR{trace_number}:TRAC {trace_number},'{temp_filepath}'")
|
||||
|
||||
# 传输文件到本地
|
||||
return self._transfer_file_to_local(temp_filepath, local_path)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error saving trace data: {str(e)}")
|
||||
return False
|
||||
|
||||
def file_to_local(self,
|
||||
instrument_path: str,
|
||||
local_path: str,
|
||||
delete_source: bool = False) -> bool:
|
||||
"""
|
||||
从仪器复制任意文件到本地
|
||||
Args:
|
||||
instrument_path: 仪器上的文件路径
|
||||
local_path: 本地保存路径
|
||||
delete_source: 是否删除仪器上的源文件
|
||||
"""
|
||||
return self._transfer_file_to_local(instrument_path, local_path, delete_source)
|
||||
|
||||
def close(self):
|
||||
"""关闭连接"""
|
||||
try:
|
||||
self.instrument.close()
|
||||
self.rm.close()
|
||||
except Exception as e:
|
||||
print(f"Error closing connection: {str(e)}")
|
@ -1,153 +0,0 @@
|
||||
// C++ script created by FSW: 04:12:2024 14:23:36
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <visa.h>
|
||||
|
||||
// forward declarations
|
||||
static bool write_command(const ViSession& handle, const std::string& command);
|
||||
static bool write_query(const ViSession& handle, const std::string& query, ViPBuf result_buffer, const ViUInt32 buffer_size, ViPUInt32 read_count = nullptr);
|
||||
static bool process_system_error(const ViSession& handle);
|
||||
static unsigned char get_esr(const ViSession& handle);
|
||||
|
||||
|
||||
static ViSession g_analyzer;
|
||||
|
||||
// assign your own variables
|
||||
static ViByte g_result[1000000];
|
||||
|
||||
|
||||
static bool write_command( const ViSession& handle, const std::string& command )
|
||||
{
|
||||
ViStatus status = viWrite(handle, (ViBuf)command.c_str(), command.length(), VI_NULL);
|
||||
|
||||
// return true if successful
|
||||
return ((status >= VI_SUCCESS) && process_system_error(handle));
|
||||
}
|
||||
|
||||
static bool write_query( const ViSession& handle, const std::string& query, ViPBuf result_buffer, const ViUInt32 buffer_size, ViPUInt32 read_count )
|
||||
{
|
||||
ViStatus status = viWrite(handle, (ViBuf)query.c_str(), query.length(), VI_NULL);
|
||||
if (status >= VI_SUCCESS)
|
||||
{
|
||||
status = viRead(handle, result_buffer, buffer_size, read_count);
|
||||
}
|
||||
// return true if successful
|
||||
return ((status >= VI_SUCCESS) && process_system_error(handle));
|
||||
}
|
||||
|
||||
static bool process_system_error(const ViSession& handle)
|
||||
{
|
||||
bool success = true;
|
||||
const unsigned char EsrErrorMask = 0x3C;
|
||||
|
||||
if ((get_esr(handle) & EsrErrorMask) != 0)
|
||||
{
|
||||
char err_buf[256];
|
||||
ViUInt32 readCount;
|
||||
viWrite(handle, (ViBuf)":SYST:ERR?", 10, VI_NULL);
|
||||
viRead(handle, (ViPBuf)err_buf, sizeof(err_buf), &readCount);
|
||||
err_buf[readCount] = '\0';
|
||||
std::cout << err_buf << std::endl;
|
||||
viWrite(handle, (ViBuf)"*CLS", 4, VI_NULL);
|
||||
success = false;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static unsigned char get_esr(const ViSession& handle)
|
||||
{
|
||||
char esr_buf[16];
|
||||
ViUInt32 readCount;
|
||||
viWrite(handle, (ViBuf)"*ESR?", 5, VI_NULL);
|
||||
viRead(handle, (ViPBuf)esr_buf, sizeof(esr_buf), &readCount);
|
||||
esr_buf[readCount] = '\0';
|
||||
return (static_cast<unsigned char>(std::stoi(std::string(esr_buf))));
|
||||
}
|
||||
|
||||
|
||||
int main ()
|
||||
{
|
||||
ViSession defaultRM;
|
||||
bool success = true;
|
||||
|
||||
// create a VISA session and return a handle to it
|
||||
viOpenDefaultRM(&defaultRM);
|
||||
// create a VISA session to the connected device and return a handle to it
|
||||
viOpen(defaultRM, (ViRsrc)"TCPIP::169.254.177.77::inst0::INSTR", VI_NULL, VI_NULL, &g_analyzer);
|
||||
|
||||
success = write_command( g_analyzer, "*RST" );
|
||||
|
||||
success = write_command( g_analyzer, "*CLS" );
|
||||
|
||||
success = write_command( g_analyzer, ":SYST:DISP:UPD ON" );
|
||||
|
||||
success = write_command( g_analyzer, ":INIT:CONT OFF" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:FREQ:STAR 1000000000" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:FREQ:STOP 2000000000" );
|
||||
|
||||
success = write_command( g_analyzer, ":DISP:WIND:TRAC:Y:SCAL:RLEV -30" );
|
||||
|
||||
success = write_command( g_analyzer, ":DISP:WIND:TRAC:Y:SCAL:RLEV 30" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:BAND:RES:AUTO OFF" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:BAND:RES:AUTO ON" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:BAND:VID:AUTO OFF" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:BAND:VID:AUTO ON" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:FREQ:MODE SWE" );
|
||||
|
||||
success = write_command( g_analyzer, ":DISP:WIND1:SIZE LARG" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC1:MARK1:FUNC:STR:STAT ON" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC1:MARK1:FUNC:STR:STAT OFF" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:MARK:FUNC:POW:SEL CN" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:POW:ACH:BAND:CHAN 300000" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:POW:ACH:PRES CN" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:MARK1:STAT ON" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:MARK1:X 1200000000" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:MARK:FUNC:POW:SEL CN" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:POW:ACH:PRES CN" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:POW:ACH:PRES CN" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:MARK:FUNC:POW:SEL CN0" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:MARK:FUNC:POW:SEL CN" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:FREQ:SPAN 1000000" );
|
||||
|
||||
success = write_command( g_analyzer, ":SENS:FREQ:SPAN 1000000000" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:MARK1:STAT ON" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:MARK1:X 1200000000" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:DELT2:STAT ON" );
|
||||
|
||||
success = write_command( g_analyzer, ":CALC:DELT2:X 1600000000" );
|
||||
|
||||
success = write_command( g_analyzer, ":INIT:SPUR" );
|
||||
|
||||
success = write_command( g_analyzer, ":MMEM:NAME 'C:\R_S\Instr\user\FSW_ScreenShot_2024-12-04_14-22-12.PNG'" );
|
||||
|
||||
success = write_command( g_analyzer, ":HCOP:IMM1" );
|
||||
|
||||
viClose(g_analyzer);
|
||||
viClose(defaultRM);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
*RST
|
||||
*CLS
|
||||
:SYST:DISP:UPD ON
|
||||
:INIT:CONT OFF
|
||||
:SENS:FREQ:STAR 1000000000
|
||||
:SENS:FREQ:STOP 2000000000
|
||||
:DISP:WIND:TRAC:Y:SCAL:RLEV 0
|
||||
:DISP:WIND:TRAC:Y:SCAL:RLEV 0
|
||||
:SENS:BAND:RES:AUTO OFF
|
||||
:SENS:BAND:RES:AUTO ON
|
||||
:SENS:BAND:VID:AUTO OFF
|
||||
:SENS:BAND:VID:AUTO ON
|
||||
:SENS:FREQ:MODE SWE
|
||||
:DISP:WIND1:SIZE LARG
|
||||
:CALC1:MARK1:FUNC:STR:STAT ON
|
||||
:CALC1:MARK1:FUNC:STR:STAT OFF
|
||||
:CALC:MARK:FUNC:POW:SEL CN
|
||||
:SENS:POW:ACH:BAND:CHAN 300000
|
||||
:SENS:POW:ACH:PRES CN
|
||||
:CALC:MARK1:STAT ON
|
||||
:CALC:MARK1:X 1200000000
|
||||
:CALC:MARK:FUNC:POW:SEL CN
|
||||
:SENS:POW:ACH:PRES CN
|
||||
:SENS:POW:ACH:PRES CN
|
||||
:CALC:MARK:FUNC:POW:SEL CN0
|
||||
:CALC:MARK:FUNC:POW:SEL CN
|
||||
:SENS:FREQ:SPAN 1000000
|
||||
:SENS:FREQ:SPAN 1000000000
|
||||
:CALC:MARK1:STAT ON
|
||||
:CALC:MARK1:X 1200000000
|
||||
:CALC:DELT2:STAT ON
|
||||
:CALC:DELT2:X 1600000000
|
||||
:INIT:SPUR
|
||||
:MMEM:NAME 'C:\R_S\Instr\user\FSW_ScreenShot_2024-12-04_14-22-12.PNG'
|
||||
:HCOP:IMM1
|
@ -1,109 +0,0 @@
|
||||
# python script created by FSW: 04:12:2024 14:23:54
|
||||
|
||||
import pyvisa as visa
|
||||
|
||||
def write_command(instrument, command) :
|
||||
instrument.write(command)
|
||||
return process_system_error(instrument)
|
||||
|
||||
def write_query(instrument, command) :
|
||||
buffer = instrument.query(command)
|
||||
bSuccess = process_system_error(instrument)
|
||||
return bSuccess, buffer
|
||||
|
||||
def process_system_error(instrument) :
|
||||
bSuccess = True
|
||||
EsrErrorMask = 0x3C
|
||||
if ((get_esr(instrument) & EsrErrorMask) != 0) :
|
||||
print(instrument.query(":SYST:ERR?"))
|
||||
instrument.write("*CLS")
|
||||
bSuccess = False
|
||||
return bSuccess
|
||||
|
||||
def get_esr(instrument) :
|
||||
esr = instrument.query("*ESR?")
|
||||
return int(esr)
|
||||
|
||||
|
||||
VisaResourceManager = visa.ResourceManager()
|
||||
|
||||
# connect to analyzer
|
||||
Analyzer = VisaResourceManager.open_resource("TCPIP::169.254.177.77::inst0::INSTR")
|
||||
|
||||
success = write_command( Analyzer, "*RST" )
|
||||
|
||||
success = write_command( Analyzer, "*CLS" )
|
||||
|
||||
success = write_command( Analyzer, ":SYST:DISP:UPD ON" )
|
||||
|
||||
success = write_command( Analyzer, ":INIT:CONT OFF" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:FREQ:STAR 1000000000" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:FREQ:STOP 2000000000" )
|
||||
|
||||
success = write_command( Analyzer, ":DISP:WIND:TRAC:Y:SCAL:RLEV -30" )
|
||||
|
||||
success = write_command( Analyzer, ":DISP:WIND:TRAC:Y:SCAL:RLEV 30" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:BAND:RES:AUTO OFF" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:BAND:RES:AUTO ON" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:BAND:VID:AUTO OFF" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:BAND:VID:AUTO ON" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:FREQ:MODE SWE" )
|
||||
|
||||
success = write_command( Analyzer, ":DISP:WIND1:SIZE LARG" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC1:MARK1:FUNC:STR:STAT ON" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC1:MARK1:FUNC:STR:STAT OFF" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:MARK:FUNC:POW:SEL CN" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:POW:ACH:BAND:CHAN 300000" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:POW:ACH:PRES CN" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:MARK1:STAT ON" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:MARK1:X 1200000000" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:MARK:FUNC:POW:SEL CN" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:POW:ACH:PRES CN" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:POW:ACH:PRES CN" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:MARK:FUNC:POW:SEL CN0" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:MARK:FUNC:POW:SEL CN" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:FREQ:SPAN 1000000" )
|
||||
|
||||
success = write_command( Analyzer, ":SENS:FREQ:SPAN 1000000000" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:MARK1:STAT ON" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:MARK1:X 1200000000" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:DELT2:STAT ON" )
|
||||
|
||||
success = write_command( Analyzer, ":CALC:DELT2:X 1600000000" )
|
||||
|
||||
success = write_command( Analyzer, ":INIT:SPUR" )
|
||||
|
||||
success = write_command( Analyzer, ":MMEM:NAME 'C:\R_S\Instr\user\FSW_ScreenShot_2024-12-04_14-22-12.PNG'" )
|
||||
|
||||
success = write_command( Analyzer, ":HCOP:IMM1" )
|
||||
|
||||
|
||||
# back to local mode
|
||||
success = write_command(Analyzer, "@LOC")
|
||||
|
||||
# cleanup
|
||||
Analyzer.close()
|
||||
VisaResourceManager.close()
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
":SENS:FREQ:STAR": "1000000000",
|
||||
":SENS:FREQ:STOP": "2000000000",
|
||||
":DISP:WIND:TRAC:Y:SCAL:RLEV": "0",
|
||||
":SENS:POW:ACH:BAND:CHAN": "300000",
|
||||
":CALC:MARK1:X": "1200000000",
|
||||
":SENS:FREQ:SPAN": "1000000000",
|
||||
":CALC:DELT2:X": "1600000000"
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
":SENS:FREQ:STAR": "\u8d77\u59cb\u9891\u7387",
|
||||
":SENS:FREQ:STOP": " \u7ec8\u6b62\u9891\u7387",
|
||||
":DISP:WIND:TRAC:Y:SCAL:RLEV": " \u53c2\u8003\u7535\u5e73",
|
||||
":SENS:POW:ACH:BAND:CHAN": " \u4fe1\u9053\u5e26\u5bbd",
|
||||
":CALC:MARK1:X": " MARK\u70b91",
|
||||
":SENS:FREQ:SPAN": " \u9891\u7387\u533a\u95f4\uff08\u5bbd\u5ea6\uff09",
|
||||
":CALC:DELT2:X": " MARK\u70b91\u504f\u79fb"
|
||||
}
|
@ -1,271 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SCPI命令参数编辑器</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.command-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
.command-row {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
height: inherit;
|
||||
/* margin: 10px 0; */
|
||||
padding: 10px ;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex: 1 1 30%;
|
||||
flex-direction: column;
|
||||
/* width: calc(30% - 20px); */
|
||||
|
||||
}
|
||||
.command-row::before {
|
||||
content: attr(data-index);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 25%;
|
||||
transform: translate(-50%,-50%);
|
||||
font-size: 40px;
|
||||
color: rgba(0, 0, 0, 0.1);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
.comment-input {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
border: none;
|
||||
background: none;
|
||||
font-size: 14px;
|
||||
cursor: text;
|
||||
}
|
||||
.command-label {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
font-family: monospace;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.value-input {
|
||||
width: auto;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
#update-btn {
|
||||
width: 50%;
|
||||
margin-top: 10px;
|
||||
padding: 5px 10px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#update-btn:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
#reset-btn {
|
||||
width: 50%;
|
||||
margin-top: 10px;
|
||||
padding: 5px 10px;
|
||||
background-color: #f44336;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#reset-btn:hover {
|
||||
background-color: #e53935;
|
||||
}
|
||||
.highlight {
|
||||
background-color: yellow; /* 高亮更新后的值 */
|
||||
}
|
||||
/* 添加 toast 样式 */
|
||||
.toast {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #333;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.toast.success {
|
||||
background-color: #4CAF50;
|
||||
}
|
||||
|
||||
.toast.error {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
.command-row.active {
|
||||
background-color: rgba(173, 216, 230, 0.7) !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SCPI命令参数编辑器</h1>
|
||||
<div class="command-container" id="commands-container"></div>
|
||||
<div class="button-container">
|
||||
<button id="reset-btn">复位</button>
|
||||
<button id="update-btn">更新</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let commandsData = [];
|
||||
|
||||
async function loadCommands() {
|
||||
const container = document.getElementById('commands-container');
|
||||
container.innerHTML = '';
|
||||
const response = await fetch('commands.json');
|
||||
commandsData = await response.json();
|
||||
const commentsResponse = await fetch('comments.json');
|
||||
const comments = await commentsResponse.json();
|
||||
|
||||
|
||||
commandsData.forEach(({ command, value }, index) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'command-row';
|
||||
row.setAttribute('data-index', index + 1);
|
||||
|
||||
const commentDisplay = document.createElement('span');
|
||||
commentDisplay.className = 'comment-input';
|
||||
commentDisplay.textContent = comments[command] || '';
|
||||
commentDisplay.ondblclick = () => {
|
||||
const commentInput = document.createElement('input');
|
||||
commentInput.type = 'text';
|
||||
commentInput.value = commentDisplay.textContent;
|
||||
commentInput.onblur = async () => {
|
||||
comments[command] = commentInput.value;
|
||||
commentDisplay.textContent = commentInput.value;
|
||||
row.replaceChild(commentDisplay, commentInput);
|
||||
await fetch('/save_comments', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(comments)
|
||||
});
|
||||
};
|
||||
row.replaceChild(commentInput, commentDisplay);
|
||||
commentInput.focus();
|
||||
};
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.className = 'command-label';
|
||||
label.textContent = command;
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.className = 'value-input';
|
||||
// 仅支持输入数字
|
||||
input.type = 'number';
|
||||
input.value = value;
|
||||
input.onfocus = () => {
|
||||
row.classList.add('active');
|
||||
};
|
||||
input.onblur = () => {
|
||||
row.classList.remove('active');
|
||||
};
|
||||
|
||||
// 与原值比较,变化则高亮
|
||||
input.oninput = () => {
|
||||
if (input.value !== value) {
|
||||
input.classList.add('highlight');
|
||||
}
|
||||
else {
|
||||
input.classList.remove('highlight');
|
||||
}
|
||||
};
|
||||
|
||||
row.appendChild(commentDisplay);
|
||||
row.appendChild(label);
|
||||
row.appendChild(input);
|
||||
container.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
function showToast(message, type = 'success') {
|
||||
// 移除现有的 toast
|
||||
const existingToast = document.querySelector('.toast');
|
||||
if (existingToast) {
|
||||
existingToast.remove();
|
||||
}
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast ${type}`;
|
||||
toast.textContent = message;
|
||||
document.body.appendChild(toast);
|
||||
|
||||
// 触发重排以显示动画
|
||||
setTimeout(() => toast.classList.add('show'), 10);
|
||||
|
||||
// 3秒后自动消失
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
document.getElementById('update-btn').onclick = async () => {
|
||||
const updates = commandsData.map(({ command }, index) => ({
|
||||
command: command,
|
||||
value: document.querySelectorAll('.value-input')[index].value
|
||||
}));
|
||||
|
||||
// 根据回应判断是否更新成功
|
||||
const response = await fetch('/update_command', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(updates)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
showToast('更新成功!');
|
||||
loadCommands(); // 重新加载命令
|
||||
} else {
|
||||
showToast('更新失败!', 'error');
|
||||
}
|
||||
} else {
|
||||
showToast('网络错误,无法更新!', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('reset-btn').onclick = () => {
|
||||
loadCommands(); // 重新加载命令以复位
|
||||
};
|
||||
|
||||
window.onload = loadCommands;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,58 +0,0 @@
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
|
||||
def parse_inp_file(file_path):
|
||||
commands_dict = []
|
||||
comments_dict = {}
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
for line in lines:
|
||||
parts = line.strip().split(' ')
|
||||
if len(parts) == 2: # 只处理有一个参数的指令
|
||||
command, value = parts
|
||||
# 检查参数是否为数字(包括科学计数法)
|
||||
if re.match(r'^-?\d+\.?\d*([eE][-+]?\d+)?$', value):
|
||||
commands_dict.append((command, value)) # 记录指令和参数的元组
|
||||
|
||||
# 读取注释文件
|
||||
comments_file_path = './comments.json'
|
||||
if not os.path.exists(comments_file_path):
|
||||
with open(comments_file_path, 'w') as f:
|
||||
comments_dict = {key: " " for key, _ in commands_dict}
|
||||
json.dump(comments_dict, f)
|
||||
else:
|
||||
with open(comments_file_path, 'r') as f:
|
||||
comments_dict = json.load(f)
|
||||
|
||||
# 保存为JSON文件
|
||||
with open('./commands.json', 'w') as f:
|
||||
json.dump([{ "command": cmd, "value": val } for cmd, val in commands_dict], f, indent=2) # 保存所有指令
|
||||
|
||||
return commands_dict, comments_dict
|
||||
|
||||
def save_comments(comments_dict):
|
||||
with open('./comments.json', 'w') as f:
|
||||
json.dump(comments_dict, f, indent=2)
|
||||
|
||||
def update_inp_file(file_path, command, new_values):
|
||||
with open(file_path, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
updated_lines = []
|
||||
new_values_set = set(new_values) # 使用集合以避免重复值
|
||||
for line in lines:
|
||||
parts = line.strip().split(' ')
|
||||
if len(parts) == 2 and parts[0] == command:
|
||||
# 如果当前行的指令在新值中,则更新为新值
|
||||
if parts[1] in new_values_set:
|
||||
updated_lines.append(f"{command} {parts[1]}\n")
|
||||
else:
|
||||
updated_lines.append(line) # 保留原行
|
||||
else:
|
||||
updated_lines.append(line)
|
||||
|
||||
with open(file_path, 'w') as f:
|
||||
f.writelines(updated_lines)
|
@ -1,70 +0,0 @@
|
||||
from flask import Flask, request, jsonify, send_from_directory
|
||||
from parse_inp import parse_inp_file, update_inp_file, save_comments
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# 确保在启动时解析inp文件
|
||||
INP_FILE_PATH = os.path.abspath('File.inp')
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
commands, comments = parse_inp_file(INP_FILE_PATH)
|
||||
return send_from_directory(os.path.abspath('./'), 'index.html')
|
||||
|
||||
@app.route('/commands.json')
|
||||
def get_commands():
|
||||
return send_from_directory(os.path.abspath('./'), 'commands.json')
|
||||
|
||||
@app.route('/update_command', methods=['POST'])
|
||||
def update_command():
|
||||
data = request.json
|
||||
try:
|
||||
# 读取当前的指令和参数
|
||||
with open(INP_FILE_PATH, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# 创建一个列表来存储更新后的指令
|
||||
updated_lines = []
|
||||
|
||||
# 遍历现有的指令行
|
||||
for line in lines:
|
||||
parts = line.strip().split(' ')
|
||||
if len(parts) == 2: # 只处理有一个参数的指令
|
||||
command, value = parts
|
||||
# 检查是否需要更新该指令
|
||||
for item in data:
|
||||
if item['command'] == command:
|
||||
# 如果指令匹配,使用新的值
|
||||
updated_lines.append(f"{command} {item['value']}\n")
|
||||
# 匹配后应删除data中对应的指令及参数
|
||||
data.remove(item)
|
||||
break
|
||||
else:
|
||||
# 如果没有匹配,保留原行
|
||||
updated_lines.append(line)
|
||||
|
||||
# 将更新后的指令写回文件
|
||||
with open(INP_FILE_PATH, 'w') as f:
|
||||
f.writelines(updated_lines)
|
||||
|
||||
# 更新后重新解析文件
|
||||
parse_inp_file(INP_FILE_PATH)
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
@app.route('/save_comments', methods=['POST'])
|
||||
def save_comments_route():
|
||||
comments = request.json
|
||||
save_comments(comments)
|
||||
return jsonify({'success': True})
|
||||
|
||||
@app.route('/comments.json')
|
||||
def get_comments():
|
||||
return send_from_directory(os.path.abspath('./'), 'comments.json')
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 启动时先解析一次文件
|
||||
parse_inp_file(INP_FILE_PATH)
|
||||
app.run(debug=True)
|
Loading…
Reference in New Issue
Block a user