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 ...@@ -5,11 +5,13 @@ from fastapi.responses import JSONResponse
from app.api.data_management import router as data_management_router 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.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.package_management import router as package_management_router
from app.api.train_management import router as train_management_router from app.api.train_management import router as train_management_router
from app.database import Base, engine from app.database import Base, engine
from app.models import Category, DataFile, DataPackage, DataPackageFile # noqa: F401 from app.models import Category, DataFile, DataPackage, DataPackageFile # noqa: F401
from app.models.eval_management import EvalRecord # 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.models.train_management import SavedModel, TrainTask # noqa: F401
from app.utils.response import error_response, success_response from app.utils.response import error_response, success_response
...@@ -60,6 +62,7 @@ def create_app() -> FastAPI: ...@@ -60,6 +62,7 @@ def create_app() -> FastAPI:
app.include_router(package_management_router, prefix='/api/packages', tags=['数据包管理']) app.include_router(package_management_router, prefix='/api/packages', tags=['数据包管理'])
app.include_router(train_management_router, prefix='/api/train', tags=['模型训练']) app.include_router(train_management_router, prefix='/api/train', tags=['模型训练'])
app.include_router(eval_management_router, prefix='/api/eval', tags=['模型评估']) app.include_router(eval_management_router, prefix='/api/eval', tags=['模型评估'])
app.include_router(monitor_router, prefix='/api/monitor', tags=['实时监控'])
return app return app
......
This diff is collapsed.
from app.models.data_management import Category, DataFile, DataPackage, DataPackageFile, DataQualityConfig from app.models.data_management import Category, DataFile, DataPackage, DataPackageFile, DataQualityConfig
from app.models.eval_management import EvalRecord from app.models.eval_management import EvalRecord
from app.models.monitor import MonitorDataPoint, MonitorExperiment
from app.models.train_management import SavedModel, TrainTask 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): ...@@ -61,6 +61,8 @@ class DataPackage(Base):
clean_rules: Mapped[dict | None] = mapped_column(JSON, nullable=True, comment='野值清洗规则') 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_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, 含)') 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')) created_at: Mapped[str] = mapped_column(TIMESTAMP, nullable=False, server_default=text('CURRENT_TIMESTAMP'))
updated_at: Mapped[str] = mapped_column( updated_at: Mapped[str] = mapped_column(
TIMESTAMP, 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 typing import Any
from sqlalchemy import func from sqlalchemy import func
...@@ -8,6 +12,8 @@ from app.services.data_management_service import DataManagementService ...@@ -8,6 +12,8 @@ from app.services.data_management_service import DataManagementService
class PackageManagementService: class PackageManagementService:
# CSV 存储目录与数据文件相同
_PACKAGES_SUBDIR = 'packages'
def __init__(self) -> None: def __init__(self) -> None:
self._dm = DataManagementService() self._dm = DataManagementService()
...@@ -184,18 +190,14 @@ class PackageManagementService: ...@@ -184,18 +190,14 @@ class PackageManagementService:
file_map = {f.id: f for f in files} 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) # 始终执行完整合并,以便写入磁盘 CSV
if needs_full_merge: merged = self._merge_records(
result = self._merge_records( file_ids, file_map, limit=None,
file_ids, file_map, limit=None, clean_rules=clean_rules,
clean_rules=clean_rules, row_start=row_start, row_end=row_end,
row_start=row_start, row_end=row_end, )
) total_count = merged['count']
total_count = result['count'] stored_rules: dict | None = clean_rules if (clean_rules and clean_rules.get('enabled')) else None
stored_rules: dict | None = clean_rules
else:
total_count = sum(f.data_count for f in files)
stored_rules = None
pkg = DataPackage( pkg = DataPackage(
name=name, name=name,
...@@ -207,7 +209,16 @@ class PackageManagementService: ...@@ -207,7 +209,16 @@ class PackageManagementService:
row_end=row_end, row_end=row_end,
) )
session.add(pkg) 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): for idx, fid in enumerate(file_ids):
pf = DataPackageFile(package_id=pkg.id, file_id=fid, sort_order=idx) pf = DataPackageFile(package_id=pkg.id, file_id=fid, sort_order=idx)
...@@ -223,9 +234,15 @@ class PackageManagementService: ...@@ -223,9 +234,15 @@ class PackageManagementService:
pkg = session.query(DataPackage).filter(DataPackage.id == db_id).first() pkg = session.query(DataPackage).filter(DataPackage.id == db_id).first()
if not pkg: if not pkg:
raise ValueError('数据包不存在') raise ValueError('数据包不存在')
stored_name = pkg.stored_name
session.query(DataPackageFile).filter(DataPackageFile.package_id == db_id).delete() session.query(DataPackageFile).filter(DataPackageFile.package_id == db_id).delete()
session.delete(pkg) session.delete(pkg)
session.commit() 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]: def get_package_records(self, package_id: str, limit: int = 500) -> dict[str, Any]:
db_id = self._parse_int_id(package_id, '数据包ID') db_id = self._parse_int_id(package_id, '数据包ID')
...@@ -369,10 +386,27 @@ class PackageManagementService: ...@@ -369,10 +386,27 @@ class PackageManagementService:
'data_count': pkg.data_count, 'data_count': pkg.data_count,
'row_start': pkg.row_start, 'row_start': pkg.row_start,
'row_end': pkg.row_end, '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 '', '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 '', '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 @staticmethod
def _parse_int_id(value: str, label: str = 'ID') -> int: def _parse_int_id(value: str, label: str = 'ID') -> int:
try: try:
......
...@@ -7,4 +7,5 @@ PyMySQL ...@@ -7,4 +7,5 @@ PyMySQL
openpyxl openpyxl
xlrd xlrd
numpy numpy
scipy
torch 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 @@ ...@@ -5,9 +5,9 @@
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>热实验温度控制系统</title> <title>热实验温度控制系统</title>
<script type="module" crossorigin src="/assets/index-8e_Vyf7m.js"></script> <script type="module" crossorigin src="/assets/index-BuprtPJP.js"></script>
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-D1RKUtCV.js"> <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-BeNqMkde.js">
<link rel="modulepreload" crossorigin href="/assets/es-XoFJL7_H.js"> <link rel="modulepreload" crossorigin href="/assets/vue-router-DixiVDJx.js">
<link rel="stylesheet" crossorigin href="/assets/index-pfQxyObg.css"> <link rel="stylesheet" crossorigin href="/assets/index-pfQxyObg.css">
</head> </head>
<body> <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({ ...@@ -15,14 +15,17 @@ const router = createRouter({
{ {
path: '/realtime-monitor', path: '/realtime-monitor',
name: 'realtime-monitor', name: 'realtime-monitor',
component: () => import('@/components/common/UnderConstruction.vue'), component: () => import('@/views/RealtimeMonitor/index.vue'),
props: { title: '实时监控' }, },
{
path: '/realtime-monitor/:id',
name: 'realtime-monitor-detail',
component: () => import('@/views/RealtimeMonitor/components/ExperimentDetail.vue'),
}, },
{ {
path: '/history-data', path: '/history-data',
name: 'history-data', name: 'history-data',
component: () => import('@/components/common/UnderConstruction.vue'), component: () => import('@/views/HistoryData/index.vue'),
props: { title: '历史数据' },
}, },
{ {
path: '/model-training', 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