# 定时任务管理
**本文档引用文件**
- [DialTimerTask.cpp](file://cpp/Views/DialTimerTask.cpp)
- [DialTimerTask.h](file://h/DialTimerTask.h)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp)
## 目录
1. [定时任务管理概述](#定时任务管理概述)
2. [DialTimerTask对话框UI组件与交互逻辑](#dialtimertask对话框ui组件与交互逻辑)
3. [CTaskDataOper类技术实现细节](#ctaskdataoper类技术实现细节)
4. [定时器触发机制与任务状态机](#定时器触发机制与任务状态机)
5. [任务与设备通信协议集成](#任务与设备通信协议集成)
6. [任务执行失败处理机制](#任务执行失败处理机制)
## 定时任务管理概述
定时任务管理功能实现了任务的创建、时间规划、状态监控和执行触发的全流程控制。系统通过数据库表`task_timer`存储定时任务信息,结合系统时间进行任务触发判断。定时任务的核心流程包括:用户通过DialTimerTask对话框配置任务参数,系统将任务信息存储到数据库,定时器服务根据当前系统时间与任务计划时间的比较来触发任务执行。任务执行过程中,系统会更新任务状态,记录执行日志,并在失败时执行重试策略。
**Section sources**
- [DialTimerTask.cpp](file://cpp/Views/DialTimerTask.cpp#L1-L238)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L4426-L4455)
## DialTimerTask对话框UI组件与交互逻辑
DialTimerTask对话框提供了完整的定时任务配置界面,其UI组件布局和用户交互逻辑设计如下:
对话框主要包含以下UI组件:
- **IDC_DATETIMEPICKER_DATE**: 日期选择器,用于选择任务执行日期
- **IDC_DATETIMEPICKER_TIME**: 时间选择器,用于选择任务执行时间
- **IDC_COMBO_TASK_NAME**: 下拉组合框,用于选择要执行的任务名称
- **IDC_EDIT_PLC_ID**: 编辑框,用于输入PLC设备ID
- **IDC_LIST_TIMER_TASK**: 列表控件,显示已配置的定时任务列表,包含ID、任务名称、执行时间和PLC ID四列
- **IDC_BUTTON_ADD**: 添加按钮,用于将当前配置添加到任务列表
- **IDC_BUTTON_DEL**: 删除按钮,用于从任务列表中删除选中的任务
- **IDC_BUTTON_SAVE**: 保存按钮,用于保存所有配置的定时任务
用户交互逻辑如下:
1. 用户首先在下拉框中选择一个任务,然后输入PLC ID
2. 通过日期和时间选择器设置任务执行时间
3. 点击"添加"按钮,系统会验证输入的有效性(确保选择了任务、输入了PLC ID、时间不早于当前时间),然后将任务添加到列表中
4. 任务名称会自动生成为"原任务名_YYYYMMDDHHMM"格式,确保唯一性
5. 用户可以多次添加任务,形成任务队列
6. 点击"删除"按钮可移除选中的任务
7. 最后点击"保存"按钮,系统会先删除标记为删除的任务,然后保存新的定时任务到数据库
```mermaid
flowchart TD
Start([开始配置定时任务]) --> SelectTask["选择任务"]
SelectTask --> InputPLCID["输入PLC ID"]
InputPLCID --> SetDateTime["设置日期和时间"]
SetDateTime --> ValidateInput["验证输入有效性"]
ValidateInput --> InputValid{"输入有效?"}
InputValid --> |否| ShowError["显示错误信息"]
InputValid --> |是| AddToList["添加到任务列表"]
AddToList --> Continue{"继续添加?"}
Continue --> |是| SelectTask
Continue --> |否| SaveTasks["保存所有任务"]
SaveTasks --> End([完成定时任务配置])
ShowError --> End
```
**Diagram sources**
- [DialTimerTask.cpp](file://cpp/Views/DialTimerTask.cpp#L79-L145)
- [DialTimerTask.h](file://h/DialTimerTask.h#L49-L51)
**Section sources**
- [DialTimerTask.cpp](file://cpp/Views/DialTimerTask.cpp#L1-L238)
- [DialTimerTask.h](file://h/DialTimerTask.h#L47-L60)
## CTaskDataOper类技术实现细节
CTaskDataOper类提供了定时任务管理的核心数据操作方法,主要包括QueryTimerTaskValiad和AddTimerTask等关键方法。
### QueryTimerTaskValiad方法
`QueryTimerTaskValiad`方法用于查询有效的定时任务,其技术实现细节如下:
- 方法接收一个`std::vector`类型的引用参数`vtTimerTd`用于返回查询结果,以及一个`SYSTEMTIME`类型的`sysCurTime`参数表示当前系统时间
- 方法首先清空结果容器,然后构建SQL查询语句,从`task_timer`表中查询执行时间大于当前时间的所有任务
- SQL查询条件为:`RunDate > 当前日期` 或 (`RunDate = 当前日期` 且 `RunTime > 当前时间`)
- 查询结果按执行日期和时间排序,确保最先执行的任务排在前面
- 方法将查询到的任务信息填充到`STTimerTask`结构体中,并添加到结果向量中
### AddTimerTask方法
`AddTimerTask`方法用于添加新的定时任务,其技术实现细节如下:
- 方法接收任务ID、任务名称、执行时间和PLC ID作为参数
- 方法首先检查任务是否已存在,如果存在则更新,否则插入新记录
- 使用事务处理确保数据一致性,在插入过程中如果发生错误会回滚事务
- 任务ID采用特殊编码方式:通过将任务ID与0x80000000进行按位或操作来标记为定时任务,恢复时通过与0x7FFFFFFF进行按位与操作来获取原始ID
- 方法返回布尔值表示操作是否成功
```mermaid
classDiagram
class CTaskDataOper {
+QueryTimerTaskValiad(vtTimerTd, sysCurTime) void
+AddTimerTask(dwTdID, strTaskName, strRunTime, strPlcID) bool
+DeleteTimerTask(vtDleTimerTask) bool
-m_pRecTdData Recordset*
-m_iTdType int
-m_bIsOpenTdData bool
}
class STTimerTask {
+uiTaskID DWORD
+strTaskName CString
+strRunTime CString
+strPlcID CString
}
CTaskDataOper --> STTimerTask : "包含"
```
**Diagram sources**
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L4426-L4440)
- [DialTimerTask.h](file://h/DialTimerTask.h#L49-L51)
**Section sources**
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L205-L2734)
- [DialTimerTask.h](file://h/DialTimerTask.h#L47-L60)
## 定时器触发机制与任务状态机
定时任务的触发机制基于系统时间的比较和任务状态的转换,形成了一个完整的工作流程。
### 定时器触发机制
定时器触发机制的核心是`CompareTdTimer`函数,其实现逻辑如下:
- 函数接收当前系统时间和任务计划时间作为参数
- 将两个时间格式化为"YYYY-MM-DD HH:MM"格式的字符串
- 使用`strcmp`函数比较两个时间字符串
- 如果系统时间大于任务时间,说明任务已超时,记录超时日志
- 返回比较结果,用于判断是否应该执行任务
任务执行流程如下:
1. 系统定期调用`QueryTimerTaskValiad`方法获取所有待执行的任务
2. 对每个任务调用`CompareTdTimer`进行时间比较
3. 对于需要执行的任务,启动任务执行线程
4. 在执行前更新任务的测试日期和时间字段
5. 按照预设流程执行任务测量
### 任务状态机转换逻辑
定时任务执行过程中的状态机转换逻辑如下:
- **EN_TIMER_PROC_PLC_POWERON**: PLC开机状态,发送开机指令并等待响应
- **EN_TIMER_PROC_ROLLCALL**: 设备点名状态,对设备进行点名操作
- **EN_TIMER_PROC_CFG_TASK**: 任务配置状态,向设备发送任务参数
- **EN_TIMER_PROC_MEASU_DATA**: 数据测量状态,执行实际测量操作
- **EN_TIMER_PROC_MEASU_SUSPEND**: 测量暂停状态,用户暂停测量
- **EN_TIMER_PROC_MEASU_CONTINUE**: 测量继续状态,用户继续测量
- **EN_TIMER_PROC_MEASU_FIN**: 测量完成状态,任务正常结束
- **EN_TIMER_PROC_MEASU_TRMI**: 测量终止状态,任务异常终止
状态转换遵循严格的顺序,每个状态完成后自动进入下一个状态。系统还支持暂停和继续功能,允许用户在测量过程中暂停并稍后继续。
```mermaid
stateDiagram-v2
[*] --> PLC开机
PLC开机 --> 设备点名 : 开机成功
设备点名 --> 任务配置 : 点名成功
任务配置 --> 数据测量 : 配置成功
数据测量 --> 测量完成 : 全部测点完成
数据测量 --> 测量暂停 : 用户暂停
测量暂停 --> 数据测量 : 用户继续
数据测量 --> 测量终止 : 发生严重错误
测量暂停 --> 测量终止 : 用户取消
note right of 数据测量
包含重试机制:
- 网络超时重试
- 数据错误重试
- 最多重试90000次
end note
```
**Diagram sources**
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp#L815-L838)
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp#L626-L781)
**Section sources**
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp#L1-L1002)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L4426-L4440)
## 任务与设备通信协议集成
定时任务与设备通信协议的集成通过一系列标准化的通信流程实现,确保任务能够正确地在远程设备上执行。
### 通信协议集成方式
系统通过以下方式实现定时任务与设备通信协议的集成:
1. **PLC控制协议**: 使用专门的PLC控制命令与设备通信,包括开机、关机、状态查询等操作
2. **任务参数传输**: 将任务参数序列化后通过网络发送到设备端,参数包括测量周期、阵列类型、电缆布局等
3. **测量数据接收**: 设备完成测量后,将数据打包发送回主机,主机解析并存储到数据库
4. **状态同步**: 实时同步设备状态,包括告警状态、测量进度等
通信流程如下:
1. 建立与PLC设备的连接
2. 发送开机指令并等待响应
3. 执行设备点名,确认设备在线
4. 发送任务配置参数
5. 启动测量流程
6. 接收并处理测量数据
7. 关闭连接
### 通信数据结构
关键通信数据结构包括:
- **STMeasuTaskParam**: 测量任务参数结构体,包含测量周期、测试类型、阵列类型等信息
- **STSigSndDataInfo**: 信号发送数据信息,包含测点编号、电极位置等
- **STMeasuBasicDataRes**: 测量基本数据响应,包含电流、电压、电阻率等测量结果
- **STRemPlcDataInfo**: 远程PLC数据信息,包含PLC状态和控制结果
系统在发送数据前会将主机字节序转换为网络字节序(使用`htonl`、`tcp_htonf`等函数),接收数据时再转换回主机字节序(使用`ntohl`、`tcp_ntohf`等函数),确保跨平台兼容性。
```mermaid
sequenceDiagram
participant 主机 as "主机系统"
participant 网络 as "网络通信"
participant 设备 as "远程设备"
主机->>网络 : 发送PLC开机指令
网络->>设备 : 接收开机指令
设备-->>网络 : 返回开机响应
网络-->>主机 : 接收开机响应
主机->>网络 : 发送设备点名指令
网络->>设备 : 接收点名指令
设备-->>网络 : 返回点名响应
网络-->>主机 : 接收点名响应
主机->>网络 : 发送任务配置参数
网络->>设备 : 接收配置参数
设备-->>网络 : 返回配置响应
网络-->>主机 : 接收配置响应
主机->>网络 : 发送测量指令
网络->>设备 : 执行测量
设备-->>网络 : 返回测量数据
网络-->>主机 : 接收测量数据
主机->>主机 : 处理并存储数据
```
**Diagram sources**
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp#L685-L794)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L2713-L2734)
**Section sources**
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp#L485-L598)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L2713-L2734)
## 任务执行失败处理机制
系统实现了完善的任务执行失败处理机制,包括重试策略和日志记录,确保任务的可靠执行。
### 重试策略
系统采用智能重试策略处理任务执行失败:
- **重试条件**: 当遇到网络超时、数据错误、数据乱序等情况时触发重试
- **重试次数**: 最多重试90000次,通过`MAX_TIMER_REPEAT_SND_CNT`常量定义
- **重试间隔**: 单个测点的最大超时时间为60秒,给予网络充分的恢复时间
- **重试逻辑**: 在`NotifyResFunc`回调函数中处理重试逻辑,根据操作结果决定是否重试
重试流程如下:
1. 发送测量请求
2. 等待响应(最长60秒)
3. 如果超时或收到错误响应,增加重试计数
4. 如果重试计数未达到上限,重新发送请求
5. 如果重试计数达到上限,终止任务并记录错误
### 日志记录机制
系统实现了全面的日志记录机制:
- **操作日志**: 记录任务的每个关键操作,包括开始执行、状态转换、完成等
- **错误日志**: 详细记录各种错误情况,包括网络错误、数据库错误、设备告警等
- **性能日志**: 记录任务执行的时间信息,用于性能分析
- **调试日志**: 在开发和调试阶段提供详细的执行信息
日志记录的关键点包括:
- 在任务开始执行时记录"准备执行定时任务"日志
- 在时间比较发现超时时记录"定时任务超时"日志
- 在重试时记录"正在重试测量"日志
- 在遇到严重告警时记录告警信息
- 在数据库操作失败时记录详细的错误描述
日志通过`CFileOperTools::GetInstance()->WriteComLog()`方法写入日志文件,确保所有重要事件都有迹可循。
```mermaid
flowchart TD
Start([开始任务执行]) --> SendRequest["发送测量请求"]
SendRequest --> WaitForResponse["等待响应(60秒)"]
WaitForResponse --> ResponseReceived{"收到响应?"}
ResponseReceived --> |否| Timeout["超时"]
ResponseReceived --> |是| CheckData["检查数据有效性"]
CheckData --> DataValid{"数据有效?"}
DataValid --> |否| DataError["数据错误"]
DataValid --> |是| ProcessData["处理测量数据"]
ProcessData --> UpdateDB["更新数据库"]
UpdateDB --> Success["任务成功完成"]
Timeout --> Retry{"重试次数 < 90000?"}
DataError --> Retry
Retry --> |是| IncrementRetry["增加重试计数"]
IncrementRetry --> SendRequest
Retry --> |否| Fail["任务执行失败"]
Success --> End([结束])
Fail --> End
```
**Diagram sources**
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp#L326-L364)
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp#L832-L834)
**Section sources**
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp#L308-L380)
- [DialTimerTaskMeasu.cpp](file://cpp/Views/DialTimerTaskMeasu.cpp#L815-L838)