Spaces:
Sleeping
Sleeping
File size: 6,573 Bytes
62a70e4 6520ac8 865ff98 6520ac8 865ff98 6520ac8 62a70e4 6520ac8 62a70e4 6520ac8 865ff98 6520ac8 865ff98 6520ac8 865ff98 6520ac8 865ff98 6520ac8 865ff98 6520ac8 865ff98 6520ac8 62a70e4 865ff98 62a70e4 865ff98 62a70e4 865ff98 62a70e4 865ff98 62a70e4 865ff98 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
import io
import os
import traceback
import zipfile
from fastapi import FastAPI, HTTPException, status
from fastapi.concurrency import run_in_threadpool
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from opentrons.simulate import simulate, format_runlog
app = FastAPI()
class Protocol(BaseModel):
name: str
content: str
@app.get("/")
async def root():
return {"message": "Opentrons simulation API is running."}
@app.get("/protocols")
async def list_saved_protocols():
"""
storageディレクトリに保存されているプロトコルファイルの一覧を返します。
"""
storage_dir = "storage"
if not os.path.isdir(storage_dir):
# ディレクトリが存在しない場合は空のリストを返す
return {"protocols": []}
try:
# os.listdirはブロッキングI/Oなのでスレッドプールで実行
file_list = await run_in_threadpool(os.listdir, storage_dir)
return {"protocols": sorted(file_list)}
except Exception as e:
print(f"Error listing files: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An error occurred while listing the protocols."
)
@app.get("/protocols/download")
async def download_all_protocols():
"""
storageディレクトリ内のすべてのプロトコルをzipファイルにまとめてダウンロードします。
"""
storage_dir = "storage"
if not os.path.isdir(storage_dir) or not os.listdir(storage_dir):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="No protocols found to download."
)
# メモリ上でzipファイルを作成
zip_io = io.BytesIO()
with zipfile.ZipFile(zip_io, mode='w', compression=zipfile.ZIP_DEFLATED) as temp_zip:
# run_in_threadpoolの中でファイルI/Oを実行
def create_zip_in_thread():
for root, _, files in os.walk(storage_dir):
for file in files:
file_path = os.path.join(root, file)
# zipファイル内でのパスをルートからの相対パスに設定
temp_zip.write(file_path, os.path.relpath(file_path, storage_dir))
await run_in_threadpool(create_zip_in_thread)
# BytesIOのポインタを先頭に戻す
zip_io.seek(0)
return StreamingResponse(
content=zip_io,
media_type="application/zip",
headers={"Content-Disposition": "attachment; filename=protocols.zip"}
)
@app.post("/simulate")
async def simulate_protocol(protocol: Protocol):
"""
プロトコルの内容を受け取り、シミュレーションのみを実行します。
ファイルへの保存は行いません。
"""
try:
protocol_file = io.StringIO(protocol.content)
run_log, _ = await run_in_threadpool(
simulate,
protocol_file=protocol_file,
file_name=protocol.name
)
return {
"protocol_name": protocol.name,
"run_status": "success",
"run_log": format_runlog(run_log)
}
except Exception as e:
print(f"Simulation Error: {e}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail={
"message": "Failed to simulate protocol.",
"error_type": type(e).__name__,
"error_details": str(e),
"traceback": traceback.format_exc()
}
)
@app.post("/protocols")
async def save_and_simulate_protocol(protocol: Protocol):
"""
プロトコルをファイルに保存し、続けてシミュレーションを実行します。
保存とシミュレーション両方の結果を返します。
"""
# --- 1. プロトコルファイルを保存 ---
storage_dir = "storage"
os.makedirs(storage_dir, exist_ok=True)
file_path = os.path.join(storage_dir, protocol.name)
try:
# スレッドプールでファイルを保存し、実際に保存されたパスを受け取る
saved_path = await run_in_threadpool(save_protocol_file, protocol.content, file_path)
save_message = f"Protocol '{os.path.basename(saved_path)}' saved successfully."
except Exception as e:
print(f"File saving error: {e}")
# ファイル保存に失敗した場合は、ここで処理を中断してエラーを返す
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="An error occurred while saving the file."
)
# --- 2. シミュレーションを実行 ---
try:
protocol_file = io.StringIO(protocol.content)
# スレッドプールでシミュレーションを実行
run_log, _ = await run_in_threadpool(
simulate,
protocol_file=protocol_file,
file_name=protocol.name
)
# 保存とシミュレーション両方の成功を返す
return {
"save_status": "success",
"save_message": save_message,
"protocol_name": protocol.name,
"run_status": "success",
"run_log": format_runlog(run_log)
}
except Exception as e:
# ファイル保存は成功したが、シミュレーションでエラーが発生した場合
print(f"Simulation Error after save: {e}")
# 保存が成功したことを伝えつつ、シミュレーションのエラー情報を返す
return {
"save_status": "success",
"save_message": save_message,
"protocol_name": protocol.name,
"run_status": "failure",
"error_details": str(e),
"traceback": traceback.format_exc()
}
def save_protocol_file(content: str, file_path: str) -> str:
"""
指定されたパスにテキストコンテンツを保存します。
ファイル名が重複する場合は、連番を付与して新しいパスに保存します。
実際に保存したファイルのパスを返します。
"""
base, ext = os.path.splitext(file_path)
counter = 1
new_path = file_path
while os.path.exists(new_path):
new_path = f"{base}_{counter}{ext}"
counter += 1
with open(new_path, 'w') as f:
f.write(content)
return new_path |