跳转到内容
RailWise KB已发布

自动化监测脚本模板

RailWise自动化监测脚本标准化代码模板,支持定时数据采集、自动处理、报告生成与预警推送的完整自动化工作流,实现无人值守的监测运维。

复核 2026-07-09入门公开可引用RailWise 技术团队
template-doc

本模板提供自动化监测运维的完整脚本框架,支持:

  • 定时采集:按设定频率自动采集监测数据
  • 自动处理:数据清洗、计算、预警检测自动执行
  • 报告生成:自动生成日报、周报、月报
  • 预警推送:自动检测预警并推送通知
  • 状态监控:系统健康检查、日志记录、异常恢复
  • 数据归档:自动归档历史数据、清理过期数据
场景 频率 触发条件 执行内容
自动化全站仪监测 1-4小时 定时 采集→处理→预警→报告
传感器数据接入 分钟级 实时/定时 采集→入库→检测
日终数据处理 每日 凌晨2:00 汇总→计算→归档
周报自动生成 每周 周一8:00 汇总→分析→报告
数据清理归档 每月 每月1日 归档→清理→备份
  • 输入:定时触发信号、配置文件、数据库连接
  • 输出:监测数据、报告文件、通知消息、日志记录
  • 中间产物:临时数据、处理状态、执行日志

"""
RailWise 自动化监测脚本引擎
支持定时任务、工作流编排、异常恢复与状态监控
作者: RailWise技术部
版本: 1.0.0
"""
import json
import logging
import signal
import sys
import time
import traceback
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime, timedelta, timezone
from enum import Enum
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
import schedule
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.interval import IntervalTrigger
# 配置日志
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler("automation.log", encoding="utf-8")
]
)
logger = logging.getLogger("RailWise.AutomationEngine")
class TaskStatus(Enum):
"""任务状态枚举"""
PENDING = "pending" # 待执行
RUNNING = "running" # 执行中
SUCCESS = "success" # 成功
FAILED = "failed" # 失败
SKIPPED = "skipped" # 跳过
TIMEOUT = "timeout" # 超时
class TaskPriority(Enum):
"""任务优先级"""
CRITICAL = 1 # 关键任务(如预警推送)
HIGH = 2 # 高优先级(如数据采集)
NORMAL = 3 # 普通优先级(如报告生成)
LOW = 4 # 低优先级(如数据清理)
@dataclass
class TaskConfig:
"""任务配置"""
task_id: str
task_name: str
task_type: str
schedule: str # cron表达式或间隔描述
priority: TaskPriority = TaskPriority.NORMAL
timeout_seconds: int = 3600
max_retries: int = 3
retry_delay_seconds: int = 300
enabled: bool = True
dependencies: List[str] = field(default_factory=list) # 依赖任务ID
parameters: Dict[str, Any] = field(default_factory=dict)
@dataclass
class TaskResult:
"""任务执行结果"""
task_id: str
status: TaskStatus
start_time: Optional[datetime] = None
end_time: Optional[datetime] = None
duration_ms: float = 0.0
output: Any = None
error_message: Optional[str] = None
retry_count: int = 0
@dataclass
class AutomationConfig:
"""自动化配置"""
project_id: str
project_name: str
log_dir: Path = field(default_factory=lambda: Path("./logs"))
data_dir: Path = field(default_factory=lambda: Path("./data"))
report_dir: Path = field(default_factory=lambda: Path("./reports"))
archive_dir: Path = field(default_factory=lambda: Path("./archive"))
enable_health_check: bool = True
health_check_interval_seconds: int = 60
max_concurrent_tasks: int = 5
shutdown_timeout_seconds: int = 30
class AutomationTask(ABC):
"""自动化任务抽象基类"""
def __init__(self, config: TaskConfig):
self.config = config
self.logger = logging.getLogger(f"Task.{config.task_id}")
self.current_result: Optional[TaskResult] = None
@abstractmethod
def execute(self, **kwargs) -> Any:
"""执行任务"""
pass
def pre_execute(self) -> bool:
"""执行前检查"""
self.logger.info(f"任务 {self.config.task_id} 开始执行")
return True
def post_execute(self, result: Any) -> Any:
"""执行后处理"""
self.logger.info(f"任务 {self.config.task_id} 执行完成")
return result
def on_error(self, error: Exception) -> None:
"""错误处理"""
self.logger.error(f"任务 {self.config.task_id} 执行失败: {error}")
self.logger.error(traceback.format_exc())
def run(self, **kwargs) -> TaskResult:
"""运行任务(包含重试逻辑)"""
result = TaskResult(task_id=self.config.task_id, status=TaskStatus.PENDING)
for attempt in range(self.config.max_retries + 1):
try:
# 执行前检查
if not self.pre_execute():
result.status = TaskStatus.SKIPPED
result.error_message = "执行前检查失败"
return result
# 记录开始时间
result.start_time = datetime.now(timezone.utc)
result.status = TaskStatus.RUNNING
result.retry_count = attempt
# 执行任务
output = self.execute(**kwargs)
# 执行后处理
output = self.post_execute(output)
# 记录完成
result.end_time = datetime.now(timezone.utc)
result.duration_ms = (result.end_time - result.start_time).total_seconds() * 1000
result.status = TaskStatus.SUCCESS
result.output = output
self.logger.info(f"任务 {self.config.task_id} 成功完成,耗时: {result.duration_ms:.2f}ms")
break
except Exception as e:
self.on_error(e)
result.error_message = str(e)
if attempt < self.config.max_retries:
self.logger.warning(f"任务 {self.config.task_id}{attempt + 1} 次失败,{self.config.retry_delay_seconds}秒后重试")
time.sleep(self.config.retry_delay_seconds)
else:
result.status = TaskStatus.FAILED
result.end_time = datetime.now(timezone.utc)
self.logger.error(f"任务 {self.config.task_id} 最终失败")
self.current_result = result
return result
class DataCollectionTask(AutomationTask):
"""数据采集任务"""
def __init__(self, config: TaskConfig, instrument_config: Dict[str, Any]):
super().__init__(config)
self.instrument_config = instrument_config
def execute(self, **kwargs) -> Any:
"""执行数据采集"""
instrument_type = self.instrument_config.get("type", "total_station")
instrument_id = self.instrument_config.get("id", "unknown")
self.logger.info(f"开始从 {instrument_id} ({instrument_type}) 采集数据")
# 模拟数据采集(实际项目中调用仪器SDK或API)
collected_data = self._collect_from_instrument(instrument_type, instrument_id)
self.logger.info(f"采集完成: {len(collected_data)} 条记录")
return collected_data
def _collect_from_instrument(self, instrument_type: str, instrument_id: str) -> List[Dict]:
"""从仪器采集数据"""
# 实际项目中根据仪器类型调用不同的采集方法
# 这里模拟返回数据
import random
from datetime import datetime
data = []
for i in range(5): # 模拟5个测点
data.append({
"record_id": f"{instrument_id}_{datetime.now().strftime('%Y%m%d%H%M%S')}_{i}",
"instrument_id": instrument_id,
"point_id": f"JC-{i+1:02d}",
"observation_time": datetime.now(timezone.utc).isoformat(),
"value_x": round(random.uniform(-0.5, 0.5), 2),
"value_y": round(random.uniform(-0.5, 0.5), 2),
"value_z": round(random.uniform(-0.1, 0.1), 2),
})
return data
class DataProcessingTask(AutomationTask):
"""数据处理任务"""
def __init__(self, config: TaskConfig, processing_config: Dict[str, Any]):
super().__init__(config)
self.processing_config = processing_config
def execute(self, **kwargs) -> Any:
"""执行数据处理"""
data = kwargs.get("data", [])
if not data:
self.logger.warning("无数据需要处理")
return []
self.logger.info(f"开始处理 {len(data)} 条记录")
# 1. 数据清洗
cleaned_data = self._clean_data(data)
# 2. 计算指标
calculated_data = self._calculate_metrics(cleaned_data)
# 3. 预警检测
alert_data = self._detect_alerts(calculated_data)
self.logger.info(f"处理完成: 清洗 {len(cleaned_data)} 条, 预警 {len([d for d in alert_data if d.get('alert_level') != '正常'])} 条")
return alert_data
def _clean_data(self, data: List[Dict]) -> List[Dict]:
"""清洗数据"""
cleaned = []
for record in data:
# 检查必要字段
if all(k in record for k in ["point_id", "observation_time"]):
# 数值精度处理
for key in ["value_x", "value_y", "value_z"]:
if key in record and record[key] is not None:
record[key] = round(record[key], 2)
cleaned.append(record)
return cleaned
def _calculate_metrics(self, data: List[Dict]) -> List[Dict]:
"""计算监测指标"""
for record in data:
# 计算累计位移(简化版)
values = [record.get("value_x", 0), record.get("value_y", 0), record.get("value_z", 0)]
record["value_d"] = round(sum(v**2 for v in values) ** 0.5, 2)
# 计算变化速率(需要历史数据,这里简化)
record["change_rate"] = 0.0
return data
def _detect_alerts(self, data: List[Dict]) -> List[Dict]:
"""检测预警"""
thresholds = self.processing_config.get("thresholds", {})
for record in data:
value_d = record.get("value_d", 0)
# 判断预警等级
if value_d >= thresholds.get("red", 10):
record["alert_level"] = "红色预警"
elif value_d >= thresholds.get("orange", 8):
record["alert_level"] = "橙色预警"
elif value_d >= thresholds.get("yellow", 5):
record["alert_level"] = "黄色预警"
else:
record["alert_level"] = "正常"
return data
class ReportGenerationTask(AutomationTask):
"""报告生成任务"""
def __init__(self, config: TaskConfig, report_config: Dict[str, Any]):
super().__init__(config)
self.report_config = report_config
def execute(self, **kwargs) -> Any:
"""执行报告生成"""
report_type = self.report_config.get("type", "daily")
self.logger.info(f"开始生成 {report_type} 报告")
# 模拟报告生成
report_data = {
"report_type": report_type,
"report_date": datetime.now(timezone.utc).isoformat(),
"project_id": self.report_config.get("project_id", "unknown"),
"total_points": 45,
"active_points": 43,
"alert_count": 2,
"summary": "监测数据正常,2个测点黄色预警"
}
# 保存报告
report_path = self._save_report(report_data)
self.logger.info(f"报告已生成: {report_path}")
return report_path
def _save_report(self, report_data: Dict) -> Path:
"""保存报告"""
report_dir = Path(self.report_config.get("output_dir", "./reports"))
report_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
report_path = report_dir / f"report_{report_data['report_type']}_{timestamp}.json"
with open(report_path, 'w', encoding='utf-8') as f:
json.dump(report_data, f, ensure_ascii=False, indent=2)
return report_path
class AlertNotificationTask(AutomationTask):
"""预警通知任务"""
def __init__(self, config: TaskConfig, notification_config: Dict[str, Any]):
super().__init__(config)
self.notification_config = notification_config
def execute(self, **kwargs) -> Any:
"""执行预警通知"""
alerts = kwargs.get("alerts", [])
if not alerts:
self.logger.info("无预警需要通知")
return []
self.logger.info(f"开始发送 {len(alerts)} 条预警通知")
sent_notifications = []
for alert in alerts:
# 模拟发送通知
notification = self._send_notification(alert)
sent_notifications.append(notification)
self.logger.info(f"预警通知发送完成: {len(sent_notifications)} 条")
return sent_notifications
def _send_notification(self, alert: Dict) -> Dict:
"""发送单条通知"""
# 实际项目中调用通知服务
return {
"alert_id": alert.get("record_id"),
"alert_level": alert.get("alert_level"),
"sent_at": datetime.now(timezone.utc).isoformat(),
"channels": ["dingtalk", "email"],
"status": "sent"
}
class DataArchivingTask(AutomationTask):
"""数据归档任务"""
def __init__(self, config: TaskConfig, archive_config: Dict[str, Any]):
super().__init__(config)
self.archive_config = archive_config
def execute(self, **kwargs) -> Any:
"""执行数据归档"""
retention_days = self.archive_config.get("retention_days", 365)
archive_dir = Path(self.archive_config.get("archive_dir", "./archive"))
self.logger.info(f"开始归档超过 {retention_days} 天的数据")
# 计算归档截止日期
cutoff_date = datetime.now(timezone.utc) - timedelta(days=retention_days)
# 模拟归档操作
archived_count = 0
# 实际项目中从数据库查询并归档旧数据
self.logger.info(f"归档完成: {archived_count} 条记录")
return {"archived_count": archived_count, "cutoff_date": cutoff_date.isoformat()}
class AutomationEngine:
"""自动化引擎"""
def __init__(self, config: AutomationConfig):
self.config = config
self.logger = logging.getLogger("AutomationEngine")
self.scheduler = BackgroundScheduler()
self.tasks: Dict[str, AutomationTask] = {}
self.task_results: Dict[str, List[TaskResult]] = {}
self.running = False
# 确保目录存在
self.config.log_dir.mkdir(parents=True, exist_ok=True)
self.config.data_dir.mkdir(parents=True, exist_ok=True)
self.config.report_dir.mkdir(parents=True, exist_ok=True)
self.config.archive_dir.mkdir(parents=True, exist_ok=True)
# 注册信号处理
signal.signal(signal.SIGTERM, self._signal_handler)
signal.signal(signal.SIGINT, self._signal_handler)
def register_task(self, task: AutomationTask):
"""注册任务"""
self.tasks[task.config.task_id] = task
self.task_results[task.config.task_id] = []
self.logger.info(f"任务已注册: {task.config.task_id} ({task.config.task_name})")
def schedule_task(self, task_id: str):
"""调度任务"""
task = self.tasks.get(task_id)
if not task:
self.logger.error(f"任务未找到: {task_id}")
return
if not task.config.enabled:
self.logger.info(f"任务已禁用: {task_id}")
return
# 解析调度表达式
schedule_str = task.config.schedule
if schedule_str.startswith("cron:"):
# cron表达式
cron_expr = schedule_str.replace("cron:", "")
parts = cron_expr.split()
if len(parts) == 5:
trigger = CronTrigger(
minute=parts[0],
hour=parts[1],
day=parts[2],
month=parts[3],
day_of_week=parts[4]
)
else:
self.logger.error(f"无效的cron表达式: {cron_expr}")
return
elif schedule_str.startswith("interval:"):
# 间隔表达式
interval_str = schedule_str.replace("interval:", "")
# 解析如 "4h", "30m", "1d"
value = int(interval_str[:-1])
unit = interval_str[-1]
if unit == 'm':
trigger = IntervalTrigger(minutes=value)
elif unit == 'h':
trigger = IntervalTrigger(hours=value)
elif unit == 'd':
trigger = IntervalTrigger(days=value)
else:
self.logger.error(f"无效的间隔单位: {unit}")
return
else:
self.logger.error(f"无效的调度表达式: {schedule_str}")
return
# 添加任务到调度器
self.scheduler.add_job(
self._execute_task,
trigger=trigger,
args=[task_id],
id=task_id,
name=task.config.task_name,
max_instances=1, # 同一任务同时只能执行一个实例
misfire_grace_time=300 # 错过执行后5分钟内仍可执行
)
self.logger.info(f"任务已调度: {task_id} ({schedule_str})")
def _execute_task(self, task_id: str):
"""执行任务"""
task = self.tasks.get(task_id)
if not task:
return
self.logger.info(f"开始执行任务: {task_id}")
# 检查依赖
for dep_id in task.config.dependencies:
dep_results = self.task_results.get(dep_id, [])
if not dep_results or dep_results[-1].status != TaskStatus.SUCCESS:
self.logger.warning(f"任务 {task_id} 依赖 {dep_id} 未满足,跳过执行")
return
# 执行任务
result = task.run()
# 记录结果
self.task_results[task_id].append(result)
# 保存结果到日志
self._save_task_result(result)
def _save_task_result(self, result: TaskResult):
"""保存任务结果"""
result_file = self.config.log_dir / f"{result.task_id}_results.json"
results = []
if result_file.exists():
with open(result_file, 'r', encoding='utf-8') as f:
results = json.load(f)
results.append({
"task_id": result.task_id,
"status": result.status.value,
"start_time": result.start_time.isoformat() if result.start_time else None,
"end_time": result.end_time.isoformat() if result.end_time else None,
"duration_ms": result.duration_ms,
"error_message": result.error_message,
"retry_count": result.retry_count
})
with open(result_file, 'w', encoding='utf-8') as f:
json.dump(results, f, ensure_ascii=False, indent=2)
def start(self):
"""启动自动化引擎"""
self.logger.info("启动自动化引擎")
# 调度所有已注册任务
for task_id in self.tasks:
self.schedule_task(task_id)
# 启动调度器
self.scheduler.start()
self.running = True
self.logger.info("自动化引擎已启动")
def stop(self):
"""停止自动化引擎"""
self.logger.info("停止自动化引擎")
self.running = False
# 关闭调度器
self.scheduler.shutdown(wait=True)
self.logger.info("自动化引擎已停止")
def _signal_handler(self, signum, frame):
"""信号处理"""
self.logger.info(f"收到信号 {signum},准备关闭")
self.stop()
sys.exit(0)
def get_task_status(self, task_id: str) -> Optional[TaskResult]:
"""获取任务状态"""
results = self.task_results.get(task_id, [])
return results[-1] if results else None
def get_all_status(self) -> Dict[str, TaskResult]:
"""获取所有任务状态"""
return {task_id: self.get_task_status(task_id) for task_id in self.tasks}
def run_task_now(self, task_id: str) -> TaskResult:
"""立即执行任务"""
self._execute_task(task_id)
return self.get_task_status(task_id)
"""工作流编排模块 - 将多个任务组合为工作流"""
from typing import List, Dict, Any
class Workflow:
"""工作流"""
def __init__(self, workflow_id: str, workflow_name: str):
self.workflow_id = workflow_id
self.workflow_name = workflow_name
self.steps: List[Dict[str, Any]] = []
self.logger = logging.getLogger(f"Workflow.{workflow_id}")
def add_step(
self,
task: AutomationTask,
input_from: Optional[str] = None,
condition: Optional[Callable] = None
):
"""添加工作流步骤"""
self.steps.append({
"task": task,
"input_from": input_from,
"condition": condition
})
def execute(self, initial_data: Any = None) -> Dict[str, Any]:
"""执行工作流"""
self.logger.info(f"开始执行工作流: {self.workflow_name}")
results = {}
current_data = initial_data
for i, step in enumerate(self.steps):
task = step["task"]
input_from = step["input_from"]
condition = step["condition"]
# 检查条件
if condition and not condition(current_data):
self.logger.info(f"步骤 {i+1} 条件不满足,跳过")
continue
# 准备输入数据
if input_from and input_from in results:
task_input = {"data": results[input_from].output}
else:
task_input = {"data": current_data}
# 执行任务
self.logger.info(f"执行步骤 {i+1}/{len(self.steps)}: {task.config.task_name}")
result = task.run(**task_input)
results[task.config.task_id] = result
current_data = result.output
# 如果任务失败,中断工作流
if result.status == TaskStatus.FAILED:
self.logger.error(f"工作流在步骤 {i+1} 失败")
break
self.logger.info(f"工作流执行完成: {self.workflow_name}")
return results
# 预定义工作流模板
def create_monitoring_workflow(
engine: AutomationEngine,
project_id: str,
instrument_config: Dict[str, Any],
processing_config: Dict[str, Any],
report_config: Dict[str, Any],
notification_config: Dict[str, Any]
) -> Workflow:
"""
创建标准监测工作流
流程: 数据采集 -> 数据处理 -> 预警通知 -> 报告生成
"""
workflow = Workflow(
workflow_id=f"monitoring_{project_id}",
workflow_name=f"{project_id} 自动化监测工作流"
)
# 1. 数据采集
collection_task = DataCollectionTask(
TaskConfig(
task_id="data_collection",
task_name="数据采集",
task_type="collection",
schedule="interval:4h",
priority=TaskPriority.HIGH
),
instrument_config
)
# 2. 数据处理
processing_task = DataProcessingTask(
TaskConfig(
task_id="data_processing",
task_name="数据处理",
task_type="processing",
schedule="interval:4h",
priority=TaskPriority.HIGH,
dependencies=["data_collection"]
),
processing_config
)
# 3. 预警通知(只在有预警时执行)
alert_task = AlertNotificationTask(
TaskConfig(
task_id="alert_notification",
task_name="预警通知",
task_type="notification",
schedule="interval:4h",
priority=TaskPriority.CRITICAL,
dependencies=["data_processing"]
),
notification_config
)
# 4. 报告生成
report_task = ReportGenerationTask(
TaskConfig(
task_id="report_generation",
task_name="报告生成",
task_type="report",
schedule="cron:0 8 * * *", # 每天8:00
priority=TaskPriority.NORMAL,
dependencies=["data_processing"]
),
report_config
)
# 添加步骤
workflow.add_step(collection_task)
workflow.add_step(processing_task, input_from="data_collection")
workflow.add_step(
alert_task,
input_from="data_processing",
condition=lambda data: any(d.get("alert_level") != "正常" for d in (data or []))
)
workflow.add_step(report_task, input_from="data_processing")
return workflow

参数名 类型 必填 默认值 说明
task_id str - 任务唯一标识
task_name str - 任务名称
task_type str - 任务类型
schedule str - 调度表达式(cron:或interval:)
priority TaskPriority NORMAL 任务优先级
timeout_seconds int 3600 任务超时时间(秒)
max_retries int 3 最大重试次数
retry_delay_seconds int 300 重试延迟(秒)
enabled bool True 是否启用
dependencies List[str] [] 依赖任务ID列表
parameters Dict {} 任务参数

3.2 AutomationConfig 自动化配置参数

Section titled “3.2 AutomationConfig 自动化配置参数”
参数名 类型 必填 默认值 说明
project_id str - 项目编号
project_name str - 项目名称
log_dir Path ./logs 日志目录
data_dir Path ./data 数据目录
report_dir Path ./reports 报告目录
archive_dir Path ./archive 归档目录
enable_health_check bool True 启用健康检查
health_check_interval_seconds int 60 健康检查间隔(秒)
max_concurrent_tasks int 5 最大并发任务数
shutdown_timeout_seconds int 30 关闭超时(秒)
格式 示例 说明
cron:分 时 日 月 周 cron:0 8 * * * 每天8:00
cron:0 */4 * * * cron:0 */4 * * * 每4小时
interval:4h interval:4h 每4小时
interval:30m interval:30m 每30分钟
interval:1d interval:1d 每天

from datetime import datetime, timezone
from pathlib import Path
from template_automation_script import (
AutomationEngine, AutomationConfig, TaskConfig, TaskPriority,
DataCollectionTask, DataProcessingTask, AlertNotificationTask,
ReportGenerationTask, DataArchivingTask
)
# 1. 配置自动化引擎
config = AutomationConfig(
project_id="RW-2024-001",
project_name="绕城高速管廊工程1标段",
log_dir=Path("./logs"),
data_dir=Path("./data"),
report_dir=Path("./reports"),
archive_dir=Path("./archive")
)
engine = AutomationEngine(config)
# 2. 配置仪器
instrument_config = {
"type": "total_station",
"id": "TS-001",
"brand": "Leica",
"model": "TS16",
"connection": {
"type": "tcp",
"host": "192.168.1.100",
"port": 8080
}
}
# 3. 配置处理参数
processing_config = {
"thresholds": {
"yellow": 5.0,
"orange": 8.0,
"red": 10.0
},
"baseline_date": "2024-01-01T00:00:00Z"
}
# 4. 配置报告
report_config = {
"type": "daily",
"project_id": "RW-2024-001",
"output_dir": "./reports",
"template": "daily_report_template"
}
# 5. 配置通知
notification_config = {
"channels": ["dingtalk", "email"],
"recipients": ["monitoring@railwise.com"],
"templates": {
"yellow": "yellow_alert_template",
"orange": "orange_alert_template",
"red": "red_alert_template"
}
}
# 6. 创建并注册任务
# 6.1 数据采集任务(每4小时)
collection_task = DataCollectionTask(
TaskConfig(
task_id="data_collection",
task_name="全站仪数据采集",
task_type="collection",
schedule="interval:4h",
priority=TaskPriority.HIGH
),
instrument_config
)
engine.register_task(collection_task)
# 6.2 数据处理任务(每4小时,依赖采集)
processing_task = DataProcessingTask(
TaskConfig(
task_id="data_processing",
task_name="监测数据处理",
task_type="processing",
schedule="interval:4h",
priority=TaskPriority.HIGH,
dependencies=["data_collection"]
),
processing_config
)
engine.register_task(processing_task)
# 6.3 预警通知任务(每4小时,依赖处理)
alert_task = AlertNotificationTask(
TaskConfig(
task_id="alert_notification",
task_name="预警通知推送",
task_type="notification",
schedule="interval:4h",
priority=TaskPriority.CRITICAL,
dependencies=["data_processing"]
),
notification_config
)
engine.register_task(alert_task)
# 6.4 日报生成任务(每天8:00,依赖处理)
report_task = ReportGenerationTask(
TaskConfig(
task_id="daily_report",
task_name="监测日报生成",
task_type="report",
schedule="cron:0 8 * * *",
priority=TaskPriority.NORMAL,
dependencies=["data_processing"]
),
report_config
)
engine.register_task(report_task)
# 6.5 数据归档任务(每月1日2:00)
archive_task = DataArchivingTask(
TaskConfig(
task_id="data_archive",
task_name="历史数据归档",
task_type="archive",
schedule="cron:0 2 1 * *",
priority=TaskPriority.LOW
),
{"retention_days": 365, "archive_dir": "./archive"}
)
engine.register_task(archive_task)
# 7. 启动引擎
engine.start()
# 8. 查看任务状态
print("任务状态:")
for task_id, status in engine.get_all_status().items():
if status:
print(f" {task_id}: {status.status.value} (耗时: {status.duration_ms:.2f}ms)")
# 9. 手动触发任务
# result = engine.run_task_now("data_collection")
# 10. 保持运行
try:
while engine.running:
time.sleep(1)
except KeyboardInterrupt:
engine.stop()
from template_automation_script import (
AutomationEngine, AutomationConfig, create_monitoring_workflow
)
# 创建引擎
config = AutomationConfig(
project_id="RW-2024-001",
project_name="绕城高速管廊工程1标段"
)
engine = AutomationEngine(config)
# 创建标准监测工作流
workflow = create_monitoring_workflow(
engine=engine,
project_id="RW-2024-001",
instrument_config=instrument_config,
processing_config=processing_config,
report_config=report_config,
notification_config=notification_config
)
# 执行工作流
results = workflow.execute()
# 查看结果
for task_id, result in results.items():
print(f"{task_id}: {result.status.value}")
from template_automation_script import AutomationTask, TaskConfig, TaskResult, TaskStatus
class CustomHealthCheckTask(AutomationTask):
"""自定义健康检查任务"""
def __init__(self, config: TaskConfig, check_items: List[str]):
super().__init__(config)
self.check_items = check_items
def execute(self, **kwargs) -> Any:
"""执行健康检查"""
health_status = {}
for item in self.check_items:
# 模拟检查
health_status[item] = {
"status": "healthy",
"last_check": datetime.now(timezone.utc).isoformat()
}
return health_status
# 注册自定义任务
health_task = CustomHealthCheckTask(
TaskConfig(
task_id="health_check",
task_name="系统健康检查",
task_type="health",
schedule="interval:5m",
priority=TaskPriority.HIGH
),
check_items=["database", "network", "disk_space", "memory"]
)
engine.register_task(health_task)

2024-07-08 08:00:00,123 - AutomationEngine - INFO - 启动自动化引擎
2024-07-08 08:00:00,125 - AutomationEngine - INFO - 任务已调度: data_collection (interval:4h)
2024-07-08 08:00:00,126 - AutomationEngine - INFO - 任务已调度: data_processing (interval:4h)
2024-07-08 08:00:00,127 - AutomationEngine - INFO - 任务已调度: alert_notification (interval:4h)
2024-07-08 08:00:00,128 - AutomationEngine - INFO - 任务已调度: daily_report (cron:0 8 * * *)
2024-07-08 08:00:00,129 - AutomationEngine - INFO - 任务已调度: data_archive (cron:0 2 1 * *)
2024-07-08 08:00:00,130 - AutomationEngine - INFO - 自动化引擎已启动
2024-07-08 08:00:00,200 - Task.data_collection - INFO - 任务 data_collection 开始执行
2024-07-08 08:00:00,201 - Task.data_collection - INFO - 开始从 TS-001 (total_station) 采集数据
2024-07-08 08:00:05,456 - Task.data_collection - INFO - 采集完成: 5 条记录
2024-07-08 08:00:05,457 - Task.data_collection - INFO - 任务 data_collection 成功完成,耗时: 5257.00ms
2024-07-08 08:00:05,500 - Task.data_processing - INFO - 任务 data_processing 开始执行
2024-07-08 08:00:05,501 - Task.data_processing - INFO - 开始处理 5 条记录
2024-07-08 08:00:06,123 - Task.data_processing - INFO - 处理完成: 清洗 5 条, 预警 0 条
2024-07-08 08:00:06,124 - Task.data_processing - INFO - 任务 data_processing 成功完成,耗时: 624.00ms
2024-07-08 08:00:06,200 - Task.alert_notification - INFO - 无预警需要通知
[
{
"task_id": "data_collection",
"status": "success",
"start_time": "2024-07-08T08:00:00.200000+00:00",
"end_time": "2024-07-08T08:00:05.456000+00:00",
"duration_ms": 5257.0,
"error_message": null,
"retry_count": 0
},
{
"task_id": "data_processing",
"status": "success",
"start_time": "2024-07-08T08:00:05.500000+00:00",
"end_time": "2024-07-08T08:00:06.124000+00:00",
"duration_ms": 624.0,
"error_message": null,
"retry_count": 0
}
]
{
"data_collection": TaskResult(
task_id="data_collection",
status=TaskStatus.SUCCESS,
output=[{...}, {...}, ...]
),
"data_processing": TaskResult(
task_id="data_processing",
status=TaskStatus.SUCCESS,
output=[{...}, {...}, ...]
),
"alert_notification": TaskResult(
task_id="alert_notification",
status=TaskStatus.SKIPPED, # 无预警,跳过
output=[]
),
"daily_report": TaskResult(
task_id="daily_report",
status=TaskStatus.SUCCESS,
output=Path("reports/report_daily_20240708_080000.json")
)
}

from template_automation_script import AutomationTask, TaskConfig
class DatabaseBackupTask(AutomationTask):
"""数据库备份任务"""
def __init__(self, config: TaskConfig, db_config: Dict[str, Any]):
super().__init__(config)
self.db_config = db_config
def execute(self, **kwargs) -> Any:
"""执行数据库备份"""
import subprocess
db_name = self.db_config.get("database", "monitoring")
backup_path = self.db_config.get("backup_dir", "./backups")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = f"{backup_path}/{db_name}_{timestamp}.sql"
# 执行备份命令
cmd = [
"mysqldump",
"-h", self.db_config.get("host", "localhost"),
"-u", self.db_config.get("user", "root"),
"-p" + self.db_config.get("password", ""),
db_name
]
with open(backup_file, 'w') as f:
subprocess.run(cmd, stdout=f, check=True)
return {"backup_file": backup_file, "size": Path(backup_file).stat().st_size}
# 注册
backup_task = DatabaseBackupTask(
TaskConfig(
task_id="db_backup",
task_name="数据库备份",
task_type="backup",
schedule="cron:0 3 * * *", # 每天3:00
priority=TaskPriority.LOW
),
db_config={"database": "railwise_monitoring", "host": "localhost", "user": "backup_user"}
)
engine.register_task(backup_task)
# 保存为 automation_service.py
import sys
import time
from template_automation_script import AutomationEngine, AutomationConfig
def main():
"""主函数"""
config = AutomationConfig(
project_id="RW-2024-001",
project_name="绕城高速管廊工程1标段"
)
engine = AutomationEngine(config)
# 注册任务...
# engine.register_task(...)
# 启动
engine.start()
# 保持运行
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
engine.stop()
if __name__ == "__main__":
main()

使用systemd管理:

/etc/systemd/system/railwise-automation.service
[Unit]
Description=RailWise Automation Service
After=network.target
[Service]
Type=simple
User=railwise
WorkingDirectory=/opt/railwise
ExecStart=/opt/railwise/venv/bin/python automation_service.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
from template_automation_script import AutomationEngine
import json
def monitor_tasks(engine: AutomationEngine):
"""监控任务状态"""
status = engine.get_all_status()
report = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"tasks": {}
}
for task_id, result in status.items():
if result:
report["tasks"][task_id] = {
"status": result.status.value,
"last_run": result.end_time.isoformat() if result.end_time else None,
"duration_ms": result.duration_ms,
"success_rate": calculate_success_rate(task_id)
}
# 保存监控报告
with open("task_monitor.json", "w") as f:
json.dump(report, f, indent=2)
def calculate_success_rate(task_id: str) -> float:
"""计算任务成功率"""
# 从结果历史计算
return 0.95 # 示例
class ResilientTask(AutomationTask):
"""弹性任务(支持异常恢复)"""
def __init__(self, config: TaskConfig):
super().__init__(config)
self.state_file = Path(f"./state/{config.task_id}_state.json")
self.state_file.parent.mkdir(parents=True, exist_ok=True)
def save_state(self, state: Dict):
"""保存状态"""
with open(self.state_file, 'w') as f:
json.dump(state, f)
def load_state(self) -> Optional[Dict]:
"""加载状态"""
if self.state_file.exists():
with open(self.state_file, 'r') as f:
return json.load(f)
return None
def execute(self, **kwargs) -> Any:
"""执行(支持恢复)"""
state = self.load_state()
if state and state.get("in_progress"):
self.logger.info("检测到未完成的任务,尝试恢复")
# 从上次中断处恢复
return self._resume_from_state(state)
# 正常执行
self.save_state({"in_progress": True, "start_time": datetime.now().isoformat()})
try:
result = self._do_work(**kwargs)
self.save_state({"in_progress": False, "completed": True})
return result
except Exception as e:
self.save_state({"in_progress": True, "error": str(e)})
raise

A: 设置合理的超时时间,并在任务中定期检查:

def execute(self, **kwargs):
start_time = time.time()
timeout = self.config.timeout_seconds
while True:
if time.time() - start_time > timeout:
raise TimeoutError("任务执行超时")
# 执行任务...
time.sleep(1)

A: 在任务配置中设置依赖,引擎会自动检查:

TaskConfig(
task_id="dependent_task",
dependencies=["prerequisite_task"], # 依赖任务
# 如果依赖任务失败,本任务会自动跳过
)

A: 任务结果保存在日志目录:

Terminal window
cat logs/data_collection_results.json

A: 使用run_task_now方法:

result = engine.run_task_now("data_collection")
print(f"执行结果: {result.status.value}")

A: 发送SIGTERM信号或调用stop方法:

# 方法1:信号
# kill -TERM <pid>
# 方法2:代码
engine.stop()


ai_tags:
domain: ["工程监测", "自动化运维", "任务调度"]
technology: ["Python", "APScheduler", "定时任务", "工作流"]
standard: ["GB 50497", "CJJ/T 202", "JGJ 8"]
product: ["RAILWISE-TSM", "RAILWISE-CLI", "WorkWise"]
difficulty: "advanced"
role: ["运维工程师", "系统管理员", "监测工程师"]
task_type: ["自动化", "定时任务", "运维监控"]
引用与复核把知识带回真实工程判断

引用时保留页面与来源线索;涉及标准条文、阈值、频率和项目结论,请回到现行依据与责任人复核。

查看 Agent 使用规则