Commit d7a073ab authored by luwei's avatar luwei

实时检测、历史记录完善

parent 3faede64
from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel, Field
from app.services.monitor_service import MonitorService
from app.utils.response import success_response
router = APIRouter()
service = MonitorService()
# ── 请求 Schema ───────────────────────────────────────────────────────────────
class MPCParamsSchema(BaseModel):
P: int = Field(default=20, ge=5, le=100, description='预测时域')
M: int = Field(default=5, ge=1, le=20, description='控制时域')
Q: float = Field(default=10.0, gt=0, description='跟踪误差权重')
R: float = Field(default=0.1, gt=0, description='控制增量权重')
alpha: float = Field(default=0.8, gt=0, lt=1, description='参考轨迹柔化系数')
u_min: float = Field(default=0.0, description='电流下限(A)')
u_max: float = Field(default=10.0, description='电流上限(A)')
du_min: float = Field(default=-2.0, description='电流增量下限(A/步)')
du_max: float = Field(default=2.0, description='电流增量上限(A/步)')
y_min: float = Field(default=-50.0, description='温度约束下限(°C)')
y_max: float = Field(default=200.0, description='温度约束上限(°C)')
correction_gain: float = Field(default=0.5, ge=0, le=1, description='反馈校正增益')
class CreateExperimentRequest(BaseModel):
name: str = Field(min_length=1, max_length=255)
model_id: int
package_id: int
target_temp: float = Field(description='目标温度(°C)')
sampling_interval: float = Field(default=1.0, gt=0, le=3600, description='采样周期(秒)')
mpc_params: MPCParamsSchema = Field(default_factory=MPCParamsSchema)
# ── 元数据接口 ────────────────────────────────────────────────────────────────
@router.get('/models')
def list_models():
return success_response(data=service.list_models())
@router.get('/packages')
def list_packages():
return success_response(data=service.list_packages())
# ── 试验 CRUD ─────────────────────────────────────────────────────────────────
@router.get('/experiments')
def list_experiments():
return success_response(data=service.list_experiments())
@router.post('/experiments')
def create_experiment(req: CreateExperimentRequest):
try:
exp = service.create_experiment(
name=req.name.strip(),
model_id=req.model_id,
package_id=req.package_id,
target_temp=req.target_temp,
sampling_interval=req.sampling_interval,
mpc_params=req.mpc_params.model_dump(),
)
return success_response(data=exp, message='试验创建成功')
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
@router.get('/experiments/{exp_id}')
def get_experiment(exp_id: int):
try:
return success_response(data=service.get_experiment(exp_id))
except ValueError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
@router.delete('/experiments/{exp_id}')
def delete_experiment(exp_id: int):
try:
service.delete_experiment(exp_id)
return success_response(message='试验已删除')
except ValueError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
# ── 控制操作 ──────────────────────────────────────────────────────────────────
@router.post('/experiments/{exp_id}/start')
def start_experiment(exp_id: int):
try:
service.start_experiment(exp_id)
return success_response(message='试验已启动')
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
@router.post('/experiments/{exp_id}/stop')
def stop_experiment(exp_id: int):
try:
service.stop_experiment(exp_id)
return success_response(message='试验已停止')
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
# ── 数据 / 报告 / 导出 ────────────────────────────────────────────────────────
@router.get('/experiments/{exp_id}/data')
def get_data_points(exp_id: int, from_step: int = Query(default=0, ge=0)):
return success_response(data=service.get_data_points(exp_id, from_step))
@router.get('/experiments/{exp_id}/report')
def get_report(exp_id: int):
try:
return success_response(data=service.get_report(exp_id))
except ValueError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
@router.post('/experiments/{exp_id}/export')
def export_to_history(exp_id: int):
try:
result = service.export_to_history(exp_id)
return success_response(data=result, message='已导出到历史数据')
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
# ── 历史数据 ──────────────────────────────────────────────────────────────────
@router.get('/history')
def list_history():
return success_response(data=service.list_history_experiments())
......@@ -5,11 +5,13 @@ from fastapi.responses import JSONResponse
from app.api.data_management import router as data_management_router
from app.api.eval_management import router as eval_management_router
from app.api.monitor import router as monitor_router
from app.api.package_management import router as package_management_router
from app.api.train_management import router as train_management_router
from app.database import Base, engine
from app.models import Category, DataFile, DataPackage, DataPackageFile # noqa: F401
from app.models.eval_management import EvalRecord # noqa: F401
from app.models.monitor import MonitorDataPoint, MonitorExperiment # noqa: F401
from app.models.train_management import SavedModel, TrainTask # noqa: F401
from app.utils.response import error_response, success_response
......@@ -60,6 +62,7 @@ def create_app() -> FastAPI:
app.include_router(package_management_router, prefix='/api/packages', tags=['数据包管理'])
app.include_router(train_management_router, prefix='/api/train', tags=['模型训练'])
app.include_router(eval_management_router, prefix='/api/eval', tags=['模型评估'])
app.include_router(monitor_router, prefix='/api/monitor', tags=['实时监控'])
return app
......
This diff is collapsed.
from app.models.data_management import Category, DataFile, DataPackage, DataPackageFile, DataQualityConfig
from app.models.eval_management import EvalRecord
from app.models.monitor import MonitorDataPoint, MonitorExperiment
from app.models.train_management import SavedModel, TrainTask
__all__ = ['Category', 'DataFile', 'DataPackage', 'DataPackageFile', 'DataQualityConfig', 'TrainTask', 'SavedModel', 'EvalRecord']
__all__ = [
'Category', 'DataFile', 'DataPackage', 'DataPackageFile', 'DataQualityConfig',
'TrainTask', 'SavedModel', 'EvalRecord',
'MonitorExperiment', 'MonitorDataPoint',
]
......@@ -61,6 +61,8 @@ class DataPackage(Base):
clean_rules: Mapped[dict | None] = mapped_column(JSON, nullable=True, comment='野值清洗规则')
row_start: Mapped[int | None] = mapped_column(Integer, nullable=True, comment='数据行起始行号(1-based, 含)')
row_end: Mapped[int | None] = mapped_column(Integer, nullable=True, comment='数据行结束行号(1-based, 含)')
stored_name: Mapped[str | None] = mapped_column(String(255), nullable=True, unique=True, comment='合并后CSV存储文件名')
file_path: Mapped[str | None] = mapped_column(String(255), nullable=True, comment='合并后CSV路径')
created_at: Mapped[str] = mapped_column(TIMESTAMP, nullable=False, server_default=text('CURRENT_TIMESTAMP'))
updated_at: Mapped[str] = mapped_column(
TIMESTAMP,
......
from sqlalchemy import BIGINT, FLOAT, TIMESTAMP, Enum, Index, Integer, JSON, String, Text, text
from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base
class MonitorExperiment(Base):
__tablename__ = 'monitor_experiments'
__table_args__ = (
Index('idx_exp_status', 'status'),
{'mysql_comment': '实时控制试验表'},
)
id: Mapped[int] = mapped_column(BIGINT, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(255), nullable=False, comment='试验名称')
model_id: Mapped[int] = mapped_column(BIGINT, nullable=False, comment='模型ID')
model_name: Mapped[str] = mapped_column(String(255), nullable=False, comment='模型名称')
package_id: Mapped[int] = mapped_column(BIGINT, nullable=False, comment='初始数据包ID')
package_name: Mapped[str] = mapped_column(String(255), nullable=False, comment='数据包名称')
target_temp: Mapped[float] = mapped_column(FLOAT, nullable=False, comment='目标温度(°C)')
mpc_params: Mapped[dict] = mapped_column(JSON, nullable=False, comment='MPC参数')
status: Mapped[str] = mapped_column(
Enum('idle', 'running', 'stopped', name='exp_status_enum'),
nullable=False,
server_default=text("'idle'"),
comment='试验状态: idle/running/stopped',
)
total_steps: Mapped[int] = mapped_column(
Integer, nullable=False, server_default=text('0'), comment='已采集步数'
)
start_time: Mapped[str | None] = mapped_column(TIMESTAMP, nullable=True, comment='开始时间')
stop_time: Mapped[str | None] = mapped_column(TIMESTAMP, nullable=True, comment='停止时间')
error_msg: Mapped[str | None] = mapped_column(Text, nullable=True, comment='错误信息')
sampling_interval: Mapped[float] = mapped_column(
FLOAT, nullable=False, server_default=text('1.0'), comment='采样周期(秒)'
)
exported: Mapped[int] = mapped_column(
Integer, nullable=False, server_default=text('0'), comment='是否已导出到历史数据'
)
created_at: Mapped[str] = mapped_column(
TIMESTAMP, nullable=False, server_default=text('CURRENT_TIMESTAMP')
)
class MonitorDataPoint(Base):
__tablename__ = 'monitor_data_points'
__table_args__ = (
Index('idx_dp_exp_step', 'experiment_id', 'step_idx'),
{'mysql_comment': '试验数据点表'},
)
id: Mapped[int] = mapped_column(BIGINT, primary_key=True, autoincrement=True)
experiment_id: Mapped[int] = mapped_column(BIGINT, nullable=False, comment='试验ID')
step_idx: Mapped[int] = mapped_column(Integer, nullable=False, comment='步骤序号')
actual_temp: Mapped[float] = mapped_column(FLOAT, nullable=False, comment='实际温度(°C)')
reference_temp: Mapped[float] = mapped_column(FLOAT, nullable=False, comment='参考轨迹温度(°C)')
current_output: Mapped[float] = mapped_column(FLOAT, nullable=False, comment='电流输出(A)')
created_at: Mapped[str] = mapped_column(
TIMESTAMP, nullable=False, server_default=text('CURRENT_TIMESTAMP')
)
This diff is collapsed.
import csv
import io
from datetime import datetime
from pathlib import Path
from typing import Any
from sqlalchemy import func
......@@ -8,6 +12,8 @@ from app.services.data_management_service import DataManagementService
class PackageManagementService:
# CSV 存储目录与数据文件相同
_PACKAGES_SUBDIR = 'packages'
def __init__(self) -> None:
self._dm = DataManagementService()
......@@ -184,18 +190,14 @@ class PackageManagementService:
file_map = {f.id: f for f in files}
needs_full_merge = bool((clean_rules and clean_rules.get('enabled')) or row_start is not None or row_end is not None)
if needs_full_merge:
result = self._merge_records(
file_ids, file_map, limit=None,
clean_rules=clean_rules,
row_start=row_start, row_end=row_end,
)
total_count = result['count']
stored_rules: dict | None = clean_rules
else:
total_count = sum(f.data_count for f in files)
stored_rules = None
# 始终执行完整合并,以便写入磁盘 CSV
merged = self._merge_records(
file_ids, file_map, limit=None,
clean_rules=clean_rules,
row_start=row_start, row_end=row_end,
)
total_count = merged['count']
stored_rules: dict | None = clean_rules if (clean_rules and clean_rules.get('enabled')) else None
pkg = DataPackage(
name=name,
......@@ -207,7 +209,16 @@ class PackageManagementService:
row_end=row_end,
)
session.add(pkg)
session.flush()
session.flush() # 获取 pkg.id
# 写入合并 CSV 文件
pkg_dir = self._dm._upload_dir / 'packages'
pkg_dir.mkdir(parents=True, exist_ok=True)
ts = datetime.now().strftime('%Y%m%d%H%M%S%f')
stored_name = f'pkg_{pkg.id}_{ts}.csv'
self._write_records_csv(merged['records'], pkg_dir / stored_name)
pkg.stored_name = stored_name
pkg.file_path = f'/app/uploads/packages/{stored_name}'
for idx, fid in enumerate(file_ids):
pf = DataPackageFile(package_id=pkg.id, file_id=fid, sort_order=idx)
......@@ -223,9 +234,15 @@ class PackageManagementService:
pkg = session.query(DataPackage).filter(DataPackage.id == db_id).first()
if not pkg:
raise ValueError('数据包不存在')
stored_name = pkg.stored_name
session.query(DataPackageFile).filter(DataPackageFile.package_id == db_id).delete()
session.delete(pkg)
session.commit()
# 删除磁盘上的合并 CSV 文件
if stored_name:
pkg_file = self._dm._upload_dir / 'packages' / stored_name
if pkg_file.exists():
pkg_file.unlink()
def get_package_records(self, package_id: str, limit: int = 500) -> dict[str, Any]:
db_id = self._parse_int_id(package_id, '数据包ID')
......@@ -369,10 +386,27 @@ class PackageManagementService:
'data_count': pkg.data_count,
'row_start': pkg.row_start,
'row_end': pkg.row_end,
'stored_name': pkg.stored_name,
'file_path': pkg.file_path,
'created_at': pkg.created_at.strftime('%Y-%m-%d %H:%M:%S') if pkg.created_at else '',
'updated_at': pkg.updated_at.strftime('%Y-%m-%d %H:%M:%S') if pkg.updated_at else '',
}
@staticmethod
def _write_records_csv(records: list[dict[str, Any]], dest: Path) -> None:
"""将合并后的记录写入 CSV 文件(UTF-8 BOM,带英文表头)。"""
with dest.open('w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerow(['time', 'current', 'voltage', 'set_temperature', 'actual_temperature'])
for r in records:
writer.writerow([
r.get('time', ''),
r.get('current', ''),
r.get('voltage', ''),
r.get('set_temperature', ''),
r.get('actual_temperature', ''),
])
@staticmethod
def _parse_int_id(value: str, label: str = 'ID') -> int:
try:
......
......@@ -7,4 +7,5 @@ PyMySQL
openpyxl
xlrd
numpy
scipy
torch
\ No newline at end of file
time,current,voltage,set_temperature,actual_temperature
2026/1/22 9:04,0.0,0.09,19.06,19.06
2026/1/22 9:05,0.0,0.09,19.06,19.053
2026/1/22 9:05,0.0,0.09,19.06,19.047
2026/1/22 9:05,0.0,0.09,19.06,19.072
2026/1/22 9:05,0.0,0.09,19.06,19.061
2026/1/22 9:05,0.0,0.09,19.06,19.082
2026/1/22 9:05,0.0,0.09,19.06,19.064
2026/1/22 9:05,0.0,0.09,19.06,19.062
2026/1/22 9:05,0.0,0.09,19.06,19.059
2026/1/22 9:05,0.0,0.09,19.06,19.067
time,current,voltage,set_temperature,actual_temperature
2026/1/22 9:15,0.0,0.09,19.346,19.359
2026/1/22 9:16,0.0,0.09,19.346,19.328
2026/1/22 9:16,0.0,0.09,19.346,19.351
2026/1/22 9:16,0.0,0.09,19.346,19.331
2026/1/22 9:16,0.0,0.09,19.346,19.33
2026/1/22 9:16,0.0,0.09,19.346,19.334
2026/1/22 9:16,0.0,0.09,19.322,19.322
2026/1/22 9:16,0.0,0.09,19.322,19.335
2026/1/22 9:16,0.0,0.09,19.322,19.352
2026/1/22 9:16,0.0,0.09,19.322,19.321
2026/1/22 14:16,0.0,0.09,-10.327,-10.857
2026/1/22 14:16,0.0,0.09,-9.797,-11.068
2026/1/22 14:16,0.018,0.09,-9.267,-11.31
2026/1/22 14:16,0.035,0.15,-8.737,-11.534
2026/1/22 14:16,0.059,0.25,-8.207,-11.771
2026/1/22 14:17,0.089,0.37,-7.677,-12.012
2026/1/22 14:17,0.125,0.52,-7.147,-12.23
2026/1/22 14:17,0.17,0.7,-6.617,-12.481
2026/1/22 14:17,0.221,0.92,-6.087,-12.709
2026/1/22 14:17,0.282,1.17,-5.557,-12.958
2026/1/22 14:17,0.35,1.46,-5.027,-13.189
2026/1/22 14:17,0.427,1.79,-4.497,-13.42
2026/1/22 14:17,0.516,2.2,-3.967,-13.672
2026/1/22 14:17,0.612,2.66,-3.437,-13.893
2026/1/22 14:17,0.72,3.23,-2.907,-14.132
2026/1/22 14:18,0.838,3.92,-2.377,-14.364
2026/1/22 14:18,0.967,4.82,-1.847,-14.596
2026/1/22 14:18,1.109,6.02,-1.317,-14.836
2026/1/22 14:18,1.263,7.74,-0.787,-15.075
2026/1/22 14:18,1.427,10.4,-0.257,-15.295
2026/1/22 14:18,1.603,14.75,0.273,-15.513
2026/1/22 14:18,1.786,21.29,0.803,-15.686
2026/1/22 14:18,1.97,29.05,1.333,-15.777
2026/1/22 14:18,2.127,35.57,1.863,-15.627
2026/1/22 14:18,2.249,40.6,2.393,-15.208
2026/1/22 14:19,2.327,44.06,2.923,-14.487
2026/1/22 14:19,2.376,46.15,3.453,-13.601
2026/1/22 14:19,2.395,47.21,3.983,-12.548
2026/1/22 14:19,2.4,47.59,4.513,-11.441
2026/1/22 14:19,2.402,47.79,5.043,-10.355
2026/1/22 14:19,2.399,47.79,5.562253173,-9.273
2026/1/22 14:19,2.397,47.78,6.063479747,-8.235
2026/1/22 14:19,2.391,47.68,6.547305539,-7.215
2026/1/22 14:19,2.387,47.59,7.014334642,-6.24
2026/1/22 14:19,2.371,47.21,7.465150176,-5.229
2026/1/22 14:20,2.358,46.81,7.900315018,-4.272
2026/1/22 14:20,2.345,46.4,8.320372505,-3.348
2026/1/22 14:20,2.321,45.74,8.725847108,-2.397
2026/1/22 14:20,2.3,45.07,9.117245094,-1.496
2026/1/22 14:20,2.278,44.38,9.495055151,-0.625
2026/1/22 14:20,2.255,43.64,9.859749003,0.224
2026/1/22 14:20,2.236,43.02,10.211782,1.006
2026/1/22 14:20,2.223,42.57,10.55159368,1.729
2026/1/22 14:20,2.212,42.22,10.87960832,2.405
2026/1/22 14:20,2.197,41.79,11.19623547,3.09
2026/1/22 14:21,2.183,41.34,11.50187047,3.742
2026/1/22 14:21,2.17,40.95,11.79689492,4.357
2026/1/22 14:21,2.153,40.45,12.08167718,4.983
2026/1/22 14:21,2.144,40.11,12.35657284,5.536
2026/1/22 14:21,2.127,39.64,12.6219251,6.114
2026/1/22 14:21,2.112,39.17,12.87806529,6.661
2026/1/22 14:21,2.104,38.86,13.12531321,7.149
2026/1/22 14:21,2.097,38.64,13.36397758,7.608
2026/1/22 14:21,2.089,38.42,13.59435638,8.055
2026/1/22 14:21,2.076,38.05,13.81673726,8.524
2026/1/22 14:22,2.068,37.77,14.03139787,8.944
2026/1/22 14:22,2.056,37.51,14.23860624,9.373
2026/1/22 14:22,2.053,37.34,14.43862108,9.734
2026/1/22 14:22,2.045,37.14,14.63169213,10.113
2026/1/22 14:22,2.034,36.82,14.81806044,10.499
2026/1/22 14:22,2.029,36.63,14.99795872,10.828
2026/1/22 14:22,2.024,36.47,15.17161158,11.154
2026/1/22 14:22,2.02,36.34,15.33923584,11.457
2026/1/22 14:22,2.015,36.21,15.50104079,11.758
2026/1/22 14:22,2.01,36.05,15.65722845,12.053
2026/1/22 14:23,2.002,35.83,15.80799383,12.353
2026/1/22 14:23,1.995,35.61,15.95352519,12.639
2026/1/22 14:23,1.993,35.51,16.09400422,12.883
2026/1/22 14:23,1.989,35.4,16.22960633,13.133
2026/1/22 14:23,1.984,35.26,16.36050082,13.38
2026/1/22 14:23,1.98,35.12,16.48685112,13.616
2026/1/22 14:23,1.977,35.02,16.608815,13.834
2026/1/22 14:23,1.973,34.9,16.72654473,14.056
2026/1/22 14:23,1.97,34.8,16.84018731,14.261
2026/1/22 14:23,1.966,34.69,16.94988463,14.464
2026/1/22 14:24,1.962,34.56,17.05577365,14.667
2026/1/22 14:24,1.961,34.52,17.15798658,14.835
2026/1/22 14:24,1.956,34.38,17.25665105,15.035
2026/1/22 14:24,1.954,34.3,17.35189025,15.199
2026/1/22 14:24,1.949,34.17,17.44382308,15.383
2026/1/22 14:24,1.948,34.12,17.53256434,15.529
2026/1/22 14:24,1.947,34.09,17.61822482,15.676
2026/1/22 14:24,1.947,34.08,17.70091147,15.81
2026/1/22 14:24,1.944,34.01,17.78072755,15.963
2026/1/22 14:24,1.943,33.96,17.8577727,16.096
2026/1/22 14:25,1.945,33.99,17.93214311,16.206
2026/1/22 14:25,1.942,33.94,18.00393166,16.345
2026/1/22 14:25,1.94,33.88,18.07322796,16.47
2026/1/22 14:25,1.941,33.88,18.14011855,16.577
2026/1/22 14:25,1.938,33.81,18.20468693,16.709
2026/1/22 14:25,1.937,33.77,18.26701374,16.816
.uc-page[data-v-d5068297]{background:var(--bg-page);place-items:center;height:100%;display:grid}.uc-card[data-v-d5068297]{background:var(--bg-white);border:1px solid var(--border-color);width:min(480px,100%);box-shadow:var(--shadow-card);text-align:center;border-radius:4px;padding:40px 32px}.uc-card h2[data-v-d5068297]{color:var(--text-primary);margin:0 0 12px;font-size:16px;font-weight:600}.uc-card p[data-v-d5068297]{color:var(--text-secondary);margin:0;font-size:13px;line-height:1.6}
import{Ht as e,I as t,d as n,m as r,t as i}from"./_plugin-vue_export-helper-D1RKUtCV.js";var a={class:`uc-page`},o={class:`uc-card`},s=i({__name:`UnderConstruction`,props:{title:{type:String,default:`页面建设中`}},setup(i){return(s,c)=>(t(),r(`section`,a,[n(`div`,o,[n(`h2`,null,e(i.title),1),c[0]||=n(`p`,null,`该模块正在建设中,可先使用“数据管理”完成数据上传与查看。`,-1)])]))}},[[`__scopeId`,`data-v-d5068297`]]);export{s as default};
\ No newline at end of file
......@@ -5,9 +5,9 @@
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>热实验温度控制系统</title>
<script type="module" crossorigin src="/assets/index-8e_Vyf7m.js"></script>
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-D1RKUtCV.js">
<link rel="modulepreload" crossorigin href="/assets/es-XoFJL7_H.js">
<script type="module" crossorigin src="/assets/index-BuprtPJP.js"></script>
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-BeNqMkde.js">
<link rel="modulepreload" crossorigin href="/assets/vue-router-DixiVDJx.js">
<link rel="stylesheet" crossorigin href="/assets/index-pfQxyObg.css">
</head>
<body>
......
import request from '@/utils/request'
export function getHistoryList() {
return request.get('/monitor/history')
}
export function getHistoryDetail(expId) {
return request.get(`/monitor/experiments/${expId}/report`)
}
import request from '@/utils/request'
// ── 元数据 ────────────────────────────────────────────────────────────────────
export function getMonitorModels() {
return request.get('/monitor/models')
}
export function getMonitorPackages() {
return request.get('/monitor/packages')
}
// ── 试验 CRUD ─────────────────────────────────────────────────────────────────
export function getExperiments() {
return request.get('/monitor/experiments')
}
export function createExperiment(payload) {
return request.post('/monitor/experiments', payload)
}
export function getExperiment(expId) {
return request.get(`/monitor/experiments/${expId}`)
}
export function deleteExperiment(expId) {
return request.delete(`/monitor/experiments/${expId}`)
}
// ── 控制操作 ──────────────────────────────────────────────────────────────────
export function startExperiment(expId) {
return request.post(`/monitor/experiments/${expId}/start`)
}
export function stopExperiment(expId) {
return request.post(`/monitor/experiments/${expId}/stop`)
}
// ── 数据 / 报告 / 导出 ────────────────────────────────────────────────────────
export function getDataPoints(expId, fromStep = 0) {
return request.get(`/monitor/experiments/${expId}/data`, { params: { from_step: fromStep } })
}
export function getReport(expId) {
return request.get(`/monitor/experiments/${expId}/report`)
}
export function exportToHistory(expId) {
return request.post(`/monitor/experiments/${expId}/export`)
}
......@@ -15,14 +15,17 @@ const router = createRouter({
{
path: '/realtime-monitor',
name: 'realtime-monitor',
component: () => import('@/components/common/UnderConstruction.vue'),
props: { title: '实时监控' },
component: () => import('@/views/RealtimeMonitor/index.vue'),
},
{
path: '/realtime-monitor/:id',
name: 'realtime-monitor-detail',
component: () => import('@/views/RealtimeMonitor/components/ExperimentDetail.vue'),
},
{
path: '/history-data',
name: 'history-data',
component: () => import('@/components/common/UnderConstruction.vue'),
props: { title: '历史数据' },
component: () => import('@/views/HistoryData/index.vue'),
},
{
path: '/model-training',
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment