This commit is contained in:
2025-10-09 09:43:34 +08:00
parent e6141689c1
commit 5d1a4d04f2
4 changed files with 179 additions and 59 deletions

38
mcp/data/id_rsa.txt Normal file
View File

@@ -0,0 +1,38 @@
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

56
mcp/server.py Normal file
View File

@@ -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 模式

19
softBV/out/Br.csv Normal file
View File

@@ -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
1 filename formula conductivity(e-3S/m)
2 1080005.cif Cs3Li2Br5 21.23
3 1147619.cif Li3YBr6 20.00
4 1147621.cif Li3InBr6 7.23
5 1211043.cif LiFeBr4 11.60
6 1222492.cif Li3ErBr6 29.61
7 1222679.cif Li2MnBr4 9.68
8 2033990.cif CsLi2Br3 9.71
9 22967.cif Li2FeBr4 11.21
10 23057.cif CsLiBr2 5.12
11 2763849.cif Cs3Li2Br5 121.34
12 28237.cif RbLiBr2 3.46
13 28250.cif Li2MnBr4 12.25
14 28326.cif LiGaBr4 1.56
15 28327.cif LiGaBr3 8.32
16 28829.cif Li2ZnBr4 15.71
17 37873.cif Li3ErBr6 10.90
18 580554.cif CsLi3Br4 38.40
19 606680.cif CsLi2Br3 20.63

View File

@@ -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 = <value>' 格式的行。
"""
# 正则表达式解释:
# ^ - 匹配行首
# \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]} <cif_folder_name>")
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: