Files
Leakage-Control/leakage_system/usr/app/app_leakage.c
guanyejian 118727b491 update:main V0.005.0、hmi V1.4
main
1、MODBUS_TCP增加3个port口,总计4个port口供主机读取数据;
2、修复历史报警记录靠前条数不是最新的报警信息BUG;
3、修复MODBUS_TCP写寄存器时解析数据的异常BUG;

hmi
1、MODBUS_TCP设置界面增加3个port口,总计4个port口进行设置;
2026-06-05 17:48:48 +08:00

443 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "app_leakage.h"
#include <string.h>
#include "bsp_w25q.h"
#include "bsp_buzzer.h"
#include "bsp_relay.h"
#include "bsp_DS1302.h"
static void history_clear_all(void);
static u8 history_read_record(u32 record_index, app_leakage_history_alarm_t *record);
static void history_init(void);
static void history_save_metadata(void);
static void app_leakage_init(void);
/*历史报警缓冲区*/
uint8_t sector_buf[2][W25Q32_SECTOR_SIZE];
app_leakage_t leakage =
{
.region_num = 0,
.sub_device_num = 0,
.init = app_leakage_init,
.task = app_leakage_task,
.class_update = app_leakage_region_classify,
};
app_leakage_t *p_leakage = &leakage;
app_hitory_t history =
{
.read_history = history_read_record,
.clean_history = history_clear_all,
.init_history = history_init};
static void app_leakage_init(void)
{
// for(int i = 0; i < APP_LEAKAGE_SUB_DEVICE_NUM; i++)
// {
// memset(leakage.sub_device_data[i].ch_data, 0,
// sizeof(leakage.sub_device_data[i].ch_data));
// }
app_leakage_region_classify();
}
/*区域分类,将同一区域名的设备划分到一起*/
void app_leakage_region_classify(void)
{
u16 i, j;
u8 add_region_flag;
/*数量及相关数据清零*/
p_leakage->region_num = 0;
p_leakage->sub_device_num = 0;
memset(p_leakage->region_data, 0, sizeof(p_leakage->region_data));
/*遍历子系统*/
for (i = 0; i < APP_LEAKAGE_SUB_DEVICE_NUM; i++)
{
add_region_flag = 1; /*添加新区域*/
/*设备使能*/
if (ENABLE == p_leakage->sub_device_data[i].flash_data.state)
{
p_leakage->sub_device_num++; /*子系统总数量++*/
/********************************************区域划分******************************************************/
/*遍历区域*/
for (j = 0; j < APP_LEAKAGE_SUB_DEVICE_NUM; j++)
{
if (0 == memcmp(p_leakage->region_data[j].name, p_leakage->sub_device_data[i].flash_data.region_name, APP_LEAKAGE_STRING_NANE_LEN)) /*名称相同*/
{
/*添加子设备*/
p_leakage->region_data[j].sub_device_index[p_leakage->region_data[j].sub_device_num] = i; /*绑定子设备索引*/
p_leakage->region_data[j].sub_device_num++; /*区域中子系统数据++*/
add_region_flag = 0; /*不添加新区域*/
break;
}
}
/*没有找到相同名称*/
if (add_region_flag) /*添加新区域*/
{
/*复制名称*/
memcpy(p_leakage->region_data[p_leakage->region_num].name,
p_leakage->sub_device_data[i].flash_data.region_name,
APP_LEAKAGE_STRING_NANE_LEN);
// 第一个设备索引为0
p_leakage->region_data[p_leakage->region_num].sub_device_index[0] = i;
p_leakage->region_data[p_leakage->region_num].sub_device_num = 1;
p_leakage->region_num++;
}
}
}
}
/*控制 声 光 继电器 报警*/
static void app_leakage_alarm_contorl(void)
{
if (0 != p_leakage->alarm_state)
{
buzzer.set.on();
relay.set(BSP_RELAY_CH_ERROR_STATE, USR_ON);
}
else
{
buzzer.set.off();
relay.set(BSP_RELAY_CH_ERROR_STATE, USR_OFF);
}
/*漏液状态*/
if (p_leakage->alarm_state & APP_LEAKAGE_SUB_DEVICE_STATE_LEAKAGE)
{
relay.set(BSP_RELAY_CH_LEAKAGE, USR_ON);
}
else
{
relay.set(BSP_RELAY_CH_LEAKAGE, USR_OFF);
}
/*断带状态*/
if (p_leakage->alarm_state & APP_LEAKAGE_SUB_DEVICE_STATE_OPEN)
{
relay.set(BSP_RELAY_CH_OPEN, USR_ON);
}
else
{
relay.set(BSP_RELAY_CH_OPEN, USR_OFF);
}
/*通讯超时*/
if (p_leakage->alarm_state & APP_LEAKAGE_SUB_DEVICE_STATE_TIME_OUT)
{
relay.set(BSP_RELAY_CH_COMMINCAION, USR_ON);
}
else
{
relay.set(BSP_RELAY_CH_COMMINCAION, USR_OFF);
}
}
/*异常状态设备数量统计*/
void app_leakage_task(void)
{
static u16 prev_ch_state[APP_LEAKAGE_SUB_DEVICE_NUM][APP_LEAKAGE_SUB_DEVICE_CH_NUM] = {0};
u16 i, j, k, sub_device_index;
/* 初始化区域异常统计 */
for (i = 0; i < p_leakage->region_num; i++)
{
p_leakage->region_data[i].leakage_num = 0;
p_leakage->region_data[i].open_num = 0;
p_leakage->region_data[i].time_out_num = 0;
}
p_leakage->alarm_state = 0;
/* 检测状态变化并统计异常数量 */
for (i = 0; i < p_leakage->region_num; i++)
{
for (j = 0; j < p_leakage->region_data[i].sub_device_num; j++)
{
sub_device_index = p_leakage->region_data[i].sub_device_index[j];
/* 检查设备是否启用 */
if (p_leakage->sub_device_data[sub_device_index].flash_data.state != ENABLE)
{
continue;
}
/* 检查设备是否屏蔽 */
if (p_leakage->sub_device_data[sub_device_index].shield != UNBLOCKED)
{
continue;
}
/*历史报警存储*/
for (k = 0; k < APP_LEAKAGE_SUB_DEVICE_USE_CH_NUM; k++)
{
u16 current_state = p_leakage->sub_device_data[sub_device_index].ch_data[k].state;
u16 prev_state = prev_ch_state[sub_device_index][k];
u16 leak_distance = p_leakage->sub_device_data[sub_device_index].ch_data[k].distance;
/* 检测状态变化并记录历史报警 */
if ((current_state & APP_LEAKAGE_SUB_DEVICE_STATE_LEAKAGE) &&
!(prev_state & APP_LEAKAGE_SUB_DEVICE_STATE_LEAKAGE))
{
/* 漏液报警开始 - 记录历史报警 */
history_add_alarm_record(i, sub_device_index, k, APP_LEAKAGE_SUB_DEVICE_STATE_LEAKAGE, leak_distance);
}
if ((current_state & APP_LEAKAGE_SUB_DEVICE_STATE_OPEN) &&
!(prev_state & APP_LEAKAGE_SUB_DEVICE_STATE_OPEN))
{
/* 断带报警开始 - 记录历史报警 */
history_add_alarm_record(i, sub_device_index, k, APP_LEAKAGE_SUB_DEVICE_STATE_OPEN, 0);
}
if ((current_state & APP_LEAKAGE_SUB_DEVICE_STATE_TIME_OUT) &&
!(prev_state & APP_LEAKAGE_SUB_DEVICE_STATE_TIME_OUT))
{
if (k > 0 && (p_leakage->sub_device_data[sub_device_index].ch_data[0].state & APP_LEAKAGE_SUB_DEVICE_STATE_TIME_OUT))
{
/*第一个通道超时,剩余通道不存储超时报警*/
}
else
{
/* 通讯超时报警开始 - 记录历史报警 */
history_add_alarm_record(i, sub_device_index, k, APP_LEAKAGE_SUB_DEVICE_STATE_TIME_OUT, 0);
}
}
/* 更新历史状态 */
prev_ch_state[sub_device_index][k] = current_state;
}
/* 统计区域异常设备数量 - 按设备统计 */
for (k = 0; k < APP_LEAKAGE_SUB_DEVICE_USE_CH_NUM; k++)
{
u16 current_state = p_leakage->sub_device_data[sub_device_index].ch_data[k].state;
if (current_state & APP_LEAKAGE_SUB_DEVICE_STATE_TIME_OUT)
{
p_leakage->region_data[i].time_out_num++;
p_leakage->alarm_state |= APP_LEAKAGE_SUB_DEVICE_STATE_TIME_OUT;
break;
}
else if (current_state & APP_LEAKAGE_SUB_DEVICE_STATE_OPEN)
{
p_leakage->region_data[i].open_num++;
p_leakage->alarm_state |= APP_LEAKAGE_SUB_DEVICE_STATE_OPEN;
}
else if (current_state & APP_LEAKAGE_SUB_DEVICE_STATE_LEAKAGE)
{
p_leakage->region_data[i].leakage_num++;
p_leakage->alarm_state |= APP_LEAKAGE_SUB_DEVICE_STATE_LEAKAGE;
}
}
}
}
/*报警控制 声光报警*/
app_leakage_alarm_contorl();
}
/* 获取当前时间 */
static void get_current_time(u8 *time_buffer)
{
/* 年: 2字节 (例如: 2024 -> 0x07 0xE8) */
uint16_t year = 2000 + DS1302.Time.Year; /* RTC年份通常从2000开始 */
time_buffer[0] = (year >> 8) & 0xFF; /* 高字节 */
time_buffer[1] = year & 0xFF; /* 低字节 */
time_buffer[2] = DS1302.Time.Month; /* 月 */
time_buffer[3] = DS1302.Time.Day; /* 日 */
time_buffer[4] = DS1302.Time.Hour; /* 时 */
time_buffer[5] = DS1302.Time.Minute; /* 分 */
time_buffer[6] = DS1302.Time.Second; /* 秒 */
}
/* 从Flash读取历史报警元数据 */
static void history_read_metadata(void)
{
app_leakage_history_metadata_t temp_metadata;
w25q32.read(W25Q32_HISTORY_ALARM_METADATA_ADDR,
(uint8_t *)&temp_metadata,
sizeof(app_leakage_history_metadata_t));
if (temp_metadata.total_records <= temp_metadata.max_records &&
temp_metadata.write_index < temp_metadata.max_records)
{
/* 数据有效,复制到全局变量 */
memcpy(&leakage.history_metadata, &temp_metadata, sizeof(app_leakage_history_metadata_t));
}
else
{
/* 数据无效,初始化 */
memset(&leakage.history_metadata, 0, sizeof(app_leakage_history_metadata_t));
leakage.history_metadata.max_records = MAX_HISTORY_ALARM_RECORDS;
/* 保存到Flash */
history_save_metadata();
}
}
/* 保存历史报警元数据到Flash */
static void history_save_metadata(void)
{
/* 擦除元数据扇区 */
w25q32_sector_erase(W25Q32_HISTORY_ALARM_METADATA_ADDR);
/* 写入元数据 */
w25q32.write(W25Q32_HISTORY_ALARM_METADATA_ADDR,
(uint8_t *)&leakage.history_metadata,
sizeof(app_leakage_history_metadata_t));
}
/* 计算记录在Flash中的地址 */
static uint32_t history_calc_record_addr(u32 record_index)
{
return W25Q32_HISTORY_ALARM_DATA_ADDR +
(record_index * HISTORY_ALARM_RECORD_SIZE);
}
/* 获取记录所在的扇区地址 */
static uint32_t history_calc_sector_addr(u32 record_index)
{
uint32_t record_addr = history_calc_record_addr(record_index);
return record_addr & ~(W25Q32_SECTOR_SIZE - 1); /* 4K对齐 */
}
/* 添加历史报警记录 */
void history_add_alarm_record(u8 region_idx, u8 device_idx, u8 channel, u16 alarm_type, u16 leak_distance)
{
app_leakage_history_alarm_t new_alarm;
uint32_t write_addr;
/* 填充报警记录 */
memset(&new_alarm, 0, sizeof(app_leakage_history_alarm_t));
/* 区域名 */
if (region_idx < leakage.region_num)
{
memcpy(new_alarm.region_name, leakage.region_data[region_idx].name,
APP_LEAKAGE_STRING_NANE_LEN);
}
/* 设备ID和名称 */
if (device_idx < APP_LEAKAGE_SUB_DEVICE_NUM)
{
new_alarm.device_id = leakage.sub_device_data[device_idx].flash_data.modbus_id;
memcpy(new_alarm.device_name, leakage.sub_device_data[device_idx].flash_data.device_name,
APP_LEAKAGE_STRING_NANE_LEN);
}
/* 报警类型、通道和漏液距离 */
new_alarm.alarm_type = alarm_type;
new_alarm.channel = channel;
new_alarm.leak_distance = leak_distance;
/* 开始时间 */
get_current_time(new_alarm.start_time);
/* 计算写入地址 */
write_addr = history_calc_record_addr(leakage.history_metadata.write_index);
/* 计算该记录可能跨越的扇区范围最多2个扇区 */
uint32_t start_sector = write_addr & ~(W25Q32_SECTOR_SIZE - 1);
uint32_t end_addr = write_addr + HISTORY_ALARM_RECORD_SIZE - 1;
uint32_t end_sector = end_addr & ~(W25Q32_SECTOR_SIZE - 1);
uint32_t num_sectors = (end_sector - start_sector) / W25Q32_SECTOR_SIZE + 1;
/* 缓冲区最多两个扇区每个扇区4KB */
uint32_t sectors[2] = {start_sector, (num_sectors > 1) ? end_sector : 0};
/* 1. 读取所有涉及的扇区到RAM */
for (uint32_t i = 0; i < num_sectors; i++)
{
w25q32.read(sectors[i], sector_buf[i], W25Q32_SECTOR_SIZE);
}
/* 2. 擦除这些扇区 */
for (uint32_t i = 0; i < num_sectors; i++)
{
w25q32_sector_erase(sectors[i]);
}
/* 3. 在RAM中更新新记录的内容 */
uint32_t offset_in_start = write_addr - start_sector;
uint32_t first_part_len = (num_sectors == 1) ? HISTORY_ALARM_RECORD_SIZE : (W25Q32_SECTOR_SIZE - offset_in_start);
memcpy(sector_buf[0] + offset_in_start, &new_alarm, first_part_len);
if (num_sectors > 1)
{
uint32_t second_part_len = HISTORY_ALARM_RECORD_SIZE - first_part_len;
memcpy(sector_buf[1], (uint8_t *)&new_alarm + first_part_len, second_part_len);
}
/* 4. 将修改后的缓冲区写回Flash */
for (uint32_t i = 0; i < num_sectors; i++)
{
w25q32.write(sectors[i], sector_buf[i], W25Q32_SECTOR_SIZE);
}
/* 5. 更新元数据(环形队列) */
leakage.history_metadata.write_index++;
if (leakage.history_metadata.write_index >= leakage.history_metadata.max_records)
{
leakage.history_metadata.write_index = 0;
}
if (leakage.history_metadata.total_records < leakage.history_metadata.max_records)
{
leakage.history_metadata.total_records++;
}
/* 保存元数据到Flash */
history_save_metadata();
}
/* 读取历史报警记录 */
static u8 history_read_record(u32 record_index, app_leakage_history_alarm_t *record)
{
if (record_index >= leakage.history_metadata.total_records)
{
return 0; /* 记录索引无效 */
}
/* 计算实际存储索引(考虑循环队列) */
uint32_t actual_index;
if (leakage.history_metadata.total_records == leakage.history_metadata.max_records)
{
/* 缓冲区已满,计算相对索引 */
actual_index = (leakage.history_metadata.write_index + record_index) %
leakage.history_metadata.max_records;
}
else
{
/* 缓冲区未满,直接读取 */
actual_index = record_index;
}
uint32_t read_addr = history_calc_record_addr(actual_index);
w25q32.read(read_addr, (uint8_t *)record, HISTORY_ALARM_RECORD_SIZE);
return 1;
}
/* 清空所有历史报警记录 */
static void history_clear_all(void)
{
/* 重置元数据 */
memset(&leakage.history_metadata, 0, sizeof(app_leakage_history_metadata_t));
leakage.history_metadata.max_records = MAX_HISTORY_ALARM_RECORDS;
/* 保存元数据 */
history_save_metadata();
/* 擦除所有数据扇区(可选) */
for (uint32_t i = 0; i < HISTORY_ALARM_SECTORS_NEEDED; i++)
{
uint32_t sector_addr = W25Q32_HISTORY_ALARM_DATA_ADDR + i * W25Q32_SECTOR_SIZE;
w25q32_sector_erase(sector_addr);
}
}
/* 初始化历史报警模块 */
static void history_init(void)
{
/* 读取元数据 */
history_read_metadata();
}