From 5d1a4d04f26ba728608770c268578247442d6629 Mon Sep 17 00:00:00 2001 From: koko <1429659362@qq.com> Date: Thu, 9 Oct 2025 09:43:34 +0800 Subject: [PATCH] mcp --- mcp/data/id_rsa.txt | 38 +++++++++++++ mcp/server.py | 56 +++++++++++++++++++ softBV/out/Br.csv | 19 +++++++ softBV/run_softBV.py | 125 +++++++++++++++++++++++-------------------- 4 files changed, 179 insertions(+), 59 deletions(-) create mode 100644 mcp/data/id_rsa.txt create mode 100644 mcp/server.py create mode 100644 softBV/out/Br.csv diff --git a/mcp/data/id_rsa.txt b/mcp/data/id_rsa.txt new file mode 100644 index 0000000..043b558 --- /dev/null +++ b/mcp/data/id_rsa.txt @@ -0,0 +1,38 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +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 +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/mcp/server.py b/mcp/server.py new file mode 100644 index 0000000..73402e7 --- /dev/null +++ b/mcp/server.py @@ -0,0 +1,56 @@ +# server.py + +import asyncio +import asyncssh # 用于 SSH 连接的库 +from mcp.server.fastmcp import FastMCP, Context + +# --- 预设的服务器连接信息 --- +# 警告:在生产环境中,不要将密钥和密码硬编码在代码里。 +# 最好使用环境变量、配置文件或专门的密钥管理服务。 +REMOTE_HOST = '202.121.182.208' # 替换为你的服务器地址 +REMOTE_USER = 'koko125' # 替换为你的用户名 +PRIVATE_KEY_PATH = 'data/id_rsa.txt' # 替换为你的私钥文件路径 + +# 创建一个 MCP 服务器实例 +mcp = FastMCP("远程服务器工具") + + +@mcp.tool() +async def execute_remote_command(command: str, ctx: Context) -> str: + """ + 在远程服务器上执行一个shell命令并返回其输出。 + + Args: + command: 要在远程服务器上执行的命令字符串。 + """ + await ctx.info(f"准备在 {REMOTE_HOST} 上执行命令: '{command}'") + + try: + # 建立 SSH 连接 + async with asyncssh.connect(REMOTE_HOST, username=REMOTE_USER, client_keys=[PRIVATE_KEY_PATH]) as conn: + # 执行命令 + result = await conn.run(command, check=True) + + # 成功执行,返回标准输出 + output = result.stdout.strip() + await ctx.info(f"命令成功执行,返回输出。") + await ctx.debug(f"输出内容: {output}") + return output + + except asyncssh.ProcessError as e: + # 命令执行出错(例如,命令本身返回了非零退出码) + error_message = f"命令执行失败: {e.stderr.strip()}" + await ctx.error(error_message) + return error_message + + except Exception as e: + # 其他连接错误等 + error_message = f"发生未知错误: {str(e)}" + await ctx.error(error_message) + return error_message + + +# 这部分使得你可以直接运行 `python server.py` +if __name__ == "__main__": + # mcp.run() # 这是默认的 stdio 模式 + mcp.run(transport="streamable-http") # 改为 streamable-http 模式 \ No newline at end of file diff --git a/softBV/out/Br.csv b/softBV/out/Br.csv new file mode 100644 index 0000000..0b93b34 --- /dev/null +++ b/softBV/out/Br.csv @@ -0,0 +1,19 @@ +filename,formula,conductivity(e-3S/m) +1080005.cif,Cs3Li2Br5,21.23 +1147619.cif,Li3YBr6,20.00 +1147621.cif,Li3InBr6,7.23 +1211043.cif,LiFeBr4,11.60 +1222492.cif,Li3ErBr6,29.61 +1222679.cif,Li2MnBr4,9.68 +2033990.cif,CsLi2Br3,9.71 +22967.cif,Li2FeBr4,11.21 +23057.cif,CsLiBr2,5.12 +2763849.cif,Cs3Li2Br5,121.34 +28237.cif,RbLiBr2,3.46 +28250.cif,Li2MnBr4,12.25 +28326.cif,LiGaBr4,1.56 +28327.cif,LiGaBr3,8.32 +28829.cif,Li2ZnBr4,15.71 +37873.cif,Li3ErBr6,10.90 +580554.cif,CsLi3Br4,38.40 +606680.cif,CsLi2Br3,20.63 diff --git a/softBV/run_softBV.py b/softBV/run_softBV.py index 8770add..fe00467 100644 --- a/softBV/run_softBV.py +++ b/softBV/run_softBV.py @@ -3,17 +3,16 @@ import sys import subprocess import re import csv +import argparse from pathlib import Path # --- 配置区 --- # softBV.x 可执行文件的路径,请根据你的实际情况修改 -# 使用 Path.home() 来自动获取用户家目录,比 '~' 更可靠 SOFTBV_EXECUTABLE = Path.home() / "tool/softBV-GUI_linux/bin/softBV.x" # 固定的命令参数 MOBILE_ION = "Li" ION_VALENCE = "1" -SF_VALUE = "1000" # 输出CSV文件的名称 OUTPUT_CSV_FILE = "conductivity_results.csv" @@ -33,47 +32,73 @@ def check_executable(): sys.exit(1) +def get_formula_from_cif_line2(cif_path): + """ + 通过读取CIF文件的第二行来提取化学式。 + 期望的格式是 'data_FORMULA'。 + """ + try: + with open(cif_path, 'r', encoding='utf-8') as f: + lines = f.readlines() + if len(lines) > 1: + second_line = lines[1].strip() # .strip() 去除首尾空白和换行符 + if second_line.lower().startswith('data_'): + # 提取 'data_' 之后的内容作为化学式 + return second_line[5:] + else: + print(f"\n警告: 文件 {cif_path.name} 的第二行格式不正确 (不是以'data_'开头)。", file=sys.stderr) + return "FormatError" + else: + print(f"\n警告: 文件 {cif_path.name} 行数不足2行。", file=sys.stderr) + return "FileTooShort" + except Exception as e: + print(f"\n警告: 读取文件 {cif_path.name} 时发生错误: {e}", file=sys.stderr) + return "ReadError" + + def parse_conductivity(output_text): - """ - 从 softBV.x 的输出文本中解析电导率(conductivity)。 - 使用正则表达式来匹配 'MD: conductivity = ' 格式的行。 - """ - # 正则表达式解释: - # ^ - 匹配行首 - # \s* - 匹配任意数量的空白字符 - # MD: conductivity - 匹配字面字符串 - # \s*=\s* - 匹配等号,前后可以有任意空白 - # ([-\d.eE+]+) - 捕获组1: 匹配并捕获一个或多个数字、点、'e'/'E'、'+'/'-' (科学计数法) + """从 softBV.x 的输出文本中解析并格式化电导率""" pattern = re.compile(r"^\s*MD: conductivity\s*=\s*([-\d.eE+]+)", re.MULTILINE) match = pattern.search(output_text) if match: - # 返回捕获到的第一个组 (也就是数值部分) - return match.group(1) + conductivity_str = match.group(1) + try: + conductivity_val = float(conductivity_str) + # 将单位从 S/m 转换为 1e-3 S/m (mS/m), 需要乘以 1000 + conductivity_ms_m = conductivity_val * 1000 + # 格式化为两位小数的字符串 + return f"{conductivity_ms_m:.2f}" + except ValueError: + return "ValueError" else: - # 如果没有找到匹配的行,返回 None return None def main(): """主函数""" - # 1. 检查命令行参数 - if len(sys.argv) != 2: - print(f"用法: python {sys.argv[0]} ") - print(f"例如: python {sys.argv[0]} Br") - sys.exit(1) + parser = argparse.ArgumentParser( + description="运行 softBV.x 计算电导率并汇总到CSV文件。", + formatter_class=argparse.RawTextHelpFormatter + ) + parser.add_argument("folder", type=str, help="包含CIF文件的目标文件夹路径。") + parser.add_argument( + "-t", "--temperature", + type=int, + default=1000, + help="模拟温度 (K),对应 softBV.x 的最后一个参数。\n默认值: 1000" + ) + args = parser.parse_args() - target_folder = Path(sys.argv[1]) + target_folder = Path(args.folder) + temperature = str(args.temperature) - # 检查目标文件夹是否存在 if not target_folder.is_dir(): print(f"错误: 文件夹 '{target_folder}' 不存在。") sys.exit(1) - # 检查 softBV.x 是否配置正确 check_executable() - # 2. 查找所有 .cif 文件 cif_files = sorted(list(target_folder.glob("*.cif"))) if not cif_files: @@ -81,61 +106,45 @@ def main(): return print(f"在 '{target_folder}' 中找到 {len(cif_files)} 个 .cif 文件,开始处理...") + print(f"模拟温度设置为: {temperature} K") - results = [] # 用于存储 (文件名, 电导率) + results = [] - # 3. 遍历文件,执行命令并提取数据 for cif_file in cif_files: print(f" -> 正在处理: {cif_file} ...", end="", flush=True) - # 构建命令 + # 使用新的函数获取化学式 + formula = get_formula_from_cif_line2(cif_file) + command = [ - str(SOFTBV_EXECUTABLE), - "--md", - str(cif_file), - MOBILE_ION, - ION_VALENCE, - SF_VALUE + str(SOFTBV_EXECUTABLE), "--md", str(cif_file), + MOBILE_ION, ION_VALENCE, temperature ] try: - # 执行命令并捕获输出 - # capture_output=True 将 stdout 和 stderr 分别捕获 - # text=True (或 encoding='utf-8') 将输出解码为字符串 - # check=True 如果命令返回非零退出码 (表示错误),则会抛出异常 process_result = subprocess.run( - command, - capture_output=True, - text=True, - check=True, - timeout=300 # 设置一个超时时间(例如300秒),防止程序卡死 + command, capture_output=True, text=True, check=True, timeout=300 ) - - # 从标准输出中解析数据 conductivity = parse_conductivity(process_result.stdout) if conductivity is not None: - results.append((cif_file.name, conductivity)) - print(f" 成功, conductivity = {conductivity}") + results.append([cif_file.name, formula, conductivity]) + print(f" 成功, Formula: {formula}, Conductivity: {conductivity} (x10^-3 S/m)") else: print(" 失败 (无法在输出中找到conductivity)") - # 你也可以选择将错误信息记录下来 - # results.append((cif_file.name, "Error: Not Found")) + results.append([cif_file.name, formula, "NotFound"]) except subprocess.CalledProcessError as e: - # 命令执行失败 print(f" 失败 (命令执行错误)") - print(f" 错误信息: {e.stderr.strip()}") - # results.append((cif_file.name, "Error: Execution Failed")) + print(f" 错误信息: {e.stderr.strip()}", file=sys.stderr) + results.append([cif_file.name, formula, "ExecError"]) except subprocess.TimeoutExpired: print(f" 失败 (命令执行超时)") - # results.append((cif_file.name, "Error: Timeout")) + results.append([cif_file.name, formula, "Timeout"]) except Exception as e: - # 其他未知错误 - print(f" 失败 (发生未知错误: {e})") - # results.append((cif_file.name, f"Error: {e}")) + print(f" 失败 (发生未知错误: {e})", file=sys.stderr) + results.append([cif_file.name, formula, "UnknownError"]) - # 4. 将结果写入CSV文件 if not results: print("没有成功处理任何文件,不生成CSV。") return @@ -145,9 +154,7 @@ def main(): try: with open(OUTPUT_CSV_FILE, 'w', newline='', encoding='utf-8') as csvfile: writer = csv.writer(csvfile) - # 写入表头 - writer.writerow(['filename', 'conductivity']) - # 写入数据 + writer.writerow(['filename', 'formula', 'conductivity(e-3S/m)']) writer.writerows(results) print("CSV文件已成功生成!") except IOError as e: