应用——智能配电箱监控系统

智能配电箱监控系统开发笔记

一、项目概述

这是一个基于多线程邮箱通信机制的智能配电箱监控系统,实现了以下功能:

  1. 多线程通信:使用自定义邮箱系统进行线程间通信

  2. 数据库存储:使用SQLite实时存储传感器数据

  3. 报警功能:实时监测电压、电流、温度等参数,触发报警

  4. 图形显示:使用FrameBuffer在屏幕上显示监控界面

二、核心技术原理

2.1 线程邮箱通信机制

复制代码
// 核心数据结构
typedef struct {
    char name_of_sender[32];  // 发送者名称
    char data[256];           // 数据内容
} MAIL_DATA;

typedef struct {
    // 邮箱系统相关数据结构
} MBS;

// 核心API函数
MBS* create_mail_box_system();                 // 创建邮箱系统
void register_to_mail_system(MBS* mbs, const char* name, void* (*func)(void*)); // 注册线程
int send_msg(MBS* mbs, const char* to_name, MAIL_DATA* data);  // 发送消息
int recv_msg(MBS* mbs, MAIL_DATA* data);       // 接收消息
void wait_all_end(MBS* mbs);                   // 等待所有线程结束
void destroy_mail_box_system(MBS* mbs);        // 销毁邮箱系统

2.2 工作流程

复制代码
数据线程(data_th) → 生成传感器数据
    ↓ (通过邮箱发送)
显示线程(show_th) → 在屏幕上显示数据
    ↓ (通过邮箱发送)
数据库 → 保存历史数据
    ↓ (检查阈值)
报警系统 → 触发报警记录

三、系统架构设计

3.1 线程设计

1. 显示线程 (show_th)

功能 :接收传感器数据并显示在屏幕上
特点

  • 使用FrameBuffer直接写屏

  • 支持中文字符显示

  • 实时更新监控界面

2. 数据线程 (data_th)

功能 :生成模拟传感器数据
特点

  • 模拟真实电压、电流、温度波动

  • 支持随机时间间隔

  • 自动触发报警检测

3.2 数据库设计

数据表结构
复制代码
-- 传感器数据表
CREATE TABLE sensor_data (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    time DATETIME DEFAULT CURRENT_TIMESTAMP,
    voltage REAL,      -- 电压值 (单位: V)
    current REAL,      -- 电流值 (单位: A)
    temperature REAL   -- 温度值 (单位: °C)
);

-- 报警记录表
CREATE TABLE alarm_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    time DATETIME DEFAULT CURRENT_TIMESTAMP,
    alarm_type TEXT,   -- 报警类型: '过压'/'欠压'/'过流'/'高温'
    value REAL,        -- 实际测量值
    threshold REAL     -- 报警阈值
);

四、最终代码实现

cpp 复制代码
#include "framebuffer.h"
#include "head.h"
#include "linkque.h"
#include "list.h"
#include "mailbox.h"
#include "utf.h"
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>     // 信号头文件
#include <sqlite3.h>    // 数据库头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

// ========== 全局变量 ==========
MBS *g_mbs;            // 邮箱系统句柄
int g_running = 1;     // 程序运行标志
sqlite3 *g_db = NULL;  // 数据库句柄

// ========== 信号处理 ==========
void signal_handler(int sig) {
    printf("\n收到停止信号,正在退出...\n");
    g_running = 0;
}

// ========== 数据库初始化 ==========
void init_database() {
    int rc = sqlite3_open("sensor.db", &g_db);
    if (rc != SQLITE_OK) {
        printf("数据库打开失败\n");
        return;
    }

    // 创建两个表:传感器数据表和报警记录表
    char *sql =
        // 第一个表:传感器数据表
        "CREATE TABLE IF NOT EXISTS sensor_data ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT,"         // 第1列:自动编号(1,2,3...)
        "time DATETIME DEFAULT CURRENT_TIMESTAMP,"      // 第2列:自动记录时间
        "voltage REAL,"                                 // 第3列:电压值(小数)
        "current REAL,"                                 // 第4列:电流值(小数)
        "temperature REAL"                              // 第5列:温度值(小数)
        ");"

        // 第二个表:报警记录表
        "CREATE TABLE IF NOT EXISTS alarm_log ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT,"         // 第1列:自动编号
        "time DATETIME DEFAULT CURRENT_TIMESTAMP,"      // 第2列:报警时间
        "alarm_type TEXT,"                              // 第3列:报警类型(文字,如"高温")
        "value REAL,"                                   // 第4列:实际测量值(如实际温度45)
        "threshold REAL"                                // 第5列:报警阈值(如温度阈值40)
        ");";

    char *err_msg = 0;
    rc = sqlite3_exec(g_db, sql, 0, 0, &err_msg);
    if (rc != SQLITE_OK) {
        printf("创建表失败: %s\n", err_msg);
        sqlite3_free(err_msg);
    }
}

// ========== 保存传感器数据到数据库 ==========
void save_to_database(float voltage, float current, float temp) {
    char sql[256];
    snprintf(sql, sizeof(sql),
             "INSERT INTO sensor_data (voltage, current, temperature) "
             "VALUES (%.2f, %.2f, %.2f);",
             voltage, current, temp);
    // 执行插入命令
    sqlite3_exec(g_db, sql, 0, 0, NULL);
}

// ========== 保存报警记录到数据库 ==========
void save_alarm_to_database(const char *alarm_type, float value,
                            float threshold) {
    char sql[256];
    snprintf(sql, sizeof(sql),
             "INSERT INTO alarm_log (alarm_type, value, threshold) "
             "VALUES ('%s', %.2f, %.2f);",
             alarm_type, value, threshold);

    sqlite3_exec(g_db, sql, 0, 0, NULL);
}

// ========== 显示线程函数 ==========
void *show_th(void *arg) {
    MAIL_DATA data; // 创建一个邮件变量
    
    // 初始化UTF-8字库
    UTF8_INFO utf8_info;
    bzero(&utf8_info, sizeof(UTF8_INFO));
    strcpy(utf8_info.path, ZIKUK_FILE_BIG);
    utf8_info.width = 32;
    utf8_info.height = 32;
    init_utf8(&utf8_info);
    
    // 初始化FrameBuffer
    int fb_fd = init_fb("/dev/fb0");
    if (fb_fd == -1) {
        printf("FrameBuffer初始化失败\n");
        uninit_utf8(&utf8_info);  // 清理字库
        return NULL;
    }
    
    while (g_running) {
        bzero(&data, sizeof(MAIL_DATA)); // 清空邮件内容
        
        // 接收邮件,ret=0表示有邮件,ret=1表示没邮件
        int ret = recv_msg(g_mbs, &data);
        if (1 == ret) {
            usleep(100000); // 100毫秒,不要用sleep(1)
            continue;
        }
        
        // 显示位置(每次重置)
        int x_pos = 50;
        int y_pos = 50;
        int line_height = 40;
        
        // 清屏为黑色
        draw_clear(0x000000);
        
        // 显示标题
        draw_utf8_str(&utf8_info, x_pos, y_pos, "智能配电箱监控系统", 
                      0xFF0000, 0x000000);
        y_pos += line_height;
        
        // 显示传感器数据
        draw_utf8_str(&utf8_info, x_pos, y_pos, data.data, 
                      0xFFFFFF, 0x000000);
        y_pos += line_height;

        // 显示时间(绿色字)
        time_t now = time(NULL);
        struct tm *tm_info = localtime(&now);
        char time_str[32];
        strftime(time_str, sizeof(time_str), "时间: %Y-%m-%d %H:%M:%S", tm_info);
        draw_utf8_str(&utf8_info, x_pos, y_pos, time_str, 
                      0x00FF00, 0x0000FF);
        
        // 绘制背景图片
        draw_bmp(0, 0, "./res/1.bmp", 800, 600);
    }
    
    // 清理显示资源
    if (fb_fd != -1) {
        uninit_utf8(&utf8_info);
        uninit_fb(fb_fd);
    }
    
    printf("显示线程退出\n");
    return NULL;
}

// ========== 数据生成线程函数 ==========
void *data_th(void *arg) {
    // 设置随机种子
    srand(time(NULL));
    
    MAIL_DATA data;
    int data_count = 0;
    
    while (g_running) {
        // 生成传感器数据(模拟真实波动)
        float voltage = 220.0 + (rand() % 21 - 10);   // 210-230V
        float current = 5.0 + (rand() % 31) * 0.1;    // 5-8A
        float temperature = 25.0 + (rand() % 21);     // 25-45°C

        // 保存到数据库
        save_to_database(voltage, current, temperature);
        
        // 检查报警条件
        int has_alarm = 0;
        
        // 电压报警检查
        if (voltage > 235.0) {
            save_alarm_to_database("过压", voltage, 235.0);
            has_alarm = 1;
            printf("⚠️  过压报警: %.1fV > 235.0V\n", voltage);
        }
        if (voltage < 200.0) {
            save_alarm_to_database("欠压", voltage, 200.0);
            has_alarm = 1;
            printf("⚠️  欠压报警: %.1fV < 200.0V\n", voltage);
        }
        
        // 电流报警检查
        if (current > 10.0) {
            save_alarm_to_database("过流", current, 10.0);
            has_alarm = 1;
            printf("⚠️  过流报警: %.1fA > 10.0A\n", current);
        }
        
        // 温度报警检查
        if (temperature > 60.0) {
            save_alarm_to_database("高温", temperature, 60.0);
            has_alarm = 1;
            printf("⚠️  高温报警: %.1f°C > 60.0°C\n", temperature);
        }
        
        // 发送给显示线程
        bzero(&data, sizeof(data)); // 清空邮件内容
        sprintf(data.data, "电压:%.1fV 电流:%.1fA 温度:%.1f°C", 
                voltage, current, temperature);
        
        // 设置发送者名称
        strcpy(data.name_of_sender, "data");
        
        // 发送给show线程
        send_msg(g_mbs, "show", &data);
        
        // 随机等待0-2秒
        sleep(rand() % 3);
        
        data_count++;
        if (data_count % 10 == 0) {
            printf("✅ 已生成 %d 条传感器数据\n", data_count);
        }
    }
    
    printf("数据线程退出\n");
    return NULL;
}

// ========== 主函数 ==========
int main(int argc, char **argv) {
    printf("=======================================\n");
    printf("   智能配电箱监控系统 v1.0\n");
    printf("=======================================\n");
    
    // 设置信号处理
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    // 初始化数据库
    printf("初始化数据库...\n");
    init_database();
    
    // 创建邮箱系统
    printf("创建邮箱系统...\n");
    g_mbs = create_mail_box_system();
    if (!g_mbs) {
        printf("创建邮箱系统失败!\n");
        return -1;
    }
    
    // 注册线程到邮箱系统
    printf("注册显示线程...\n");
    register_to_mail_system(g_mbs, "show", show_th);
    
    printf("注册数据线程...\n");
    register_to_mail_system(g_mbs, "data", data_th);
    
    printf("\n系统启动完成!\n");
    printf("运行中...按 Ctrl+C 停止\n");
    printf("=======================================\n\n");
    
    // 等待所有线程结束
    wait_all_end(g_mbs);
    
    // 清理资源
    printf("\n清理资源...\n");
    if (g_db) {
        sqlite3_close(g_db);
        printf("数据库已关闭\n");
    }
    
    destroy_mail_box_system(g_mbs);
    printf("邮箱系统已销毁\n");
    
    printf("程序安全退出\n");
    return 0;
}

五、编译和运行说明

5.1 编译命令

复制代码
# 需要链接的库:pthread, sqlite3
gcc -o smart_powerbox main.c mailbox.c linkque.c list.c framebuffer.c utf.c \
    -lpthread -lsqlite3 -lm

5.2 运行说明

复制代码
# 1. 运行程序
./smart_powerbox

# 2. 查看数据库(另一个终端)
sqlite3 sensor.db

# 3. 在sqlite3中查看数据
sqlite> SELECT * FROM sensor_data;
sqlite> SELECT * FROM alarm_log;
sqlite> .exit

六、技术要点总结

6.1 多线程通信优势

  1. 解耦:各线程独立工作,通过邮箱通信

  2. 实时性:数据生成和显示分离,互不影响

  3. 扩展性:方便添加新功能模块

6.2 数据库设计要点

  1. 时间戳:自动记录数据时间

  2. 数据类型:使用REAL存储浮点数

  3. 报警记录:单独建表,便于查询分析

6.3 显示技术要点

  1. FrameBuffer:直接操作显示缓冲区

  2. UTF-8支持:显示中文字符

  3. 定时刷新:实时更新显示内容

6.4 报警逻辑

  1. 阈值设置:根据实际需求设定

  2. 实时检测:每次生成数据时检查

  3. 记录保存:所有报警都有历史记录

七、扩展建议

7.1 功能扩展

  1. 增加配置文件:支持参数配置

  2. 添加日志系统:记录程序运行状态

  3. 支持远程访问:添加Web界面

7.2 性能优化

  1. 数据库优化:使用事务批量插入

  2. 内存管理:合理控制内存使用

  3. 线程调度:优化线程优先级

八、注意事项

  1. 权限问题:FrameBuffer操作需要root权限

  2. 资源清理:确保所有资源正确释放

  3. 错误处理:添加更完善的错误处理机制

  4. 信号安全:确保信号处理函数可重入

这个系统展示了如何将多线程、数据库、图形显示等技术综合应用,构建一个完整的嵌入式监控系统。

相关推荐
Tisfy8 小时前
网站访问耗时优化 - 从数十秒到几百毫秒的“零成本”优化过程
服务器·开发语言·性能优化·php·网站·建站
闲人编程8 小时前
消息通知系统实现:构建高可用、可扩展的企业级通知服务
java·服务器·网络·python·消息队列·异步处理·分发器
XiaoHu02078 小时前
Linux多线程(详细全解)
linux·运维·服务器·开发语言·c++·git
Xの哲學8 小时前
Linux Platform驱动深度剖析: 从设计思想到实战解析
linux·服务器·网络·算法·边缘计算
!chen8 小时前
EF Core自定义映射PostgreSQL原生函数
数据库·postgresql
逑之8 小时前
C语言笔记11:字符函数和字符串函数
c语言·笔记·算法
霖霖总总8 小时前
[小技巧14]MySQL 8.0 系统变量设置全解析:SET GLOBAL、SET PERSIST 与 SET PERSIST_ONLY 的区别与应用
数据库·mysql
逑之8 小时前
C语言笔记1:C语言常见概念
c语言·笔记·c#
马克学长8 小时前
SSM校园食堂订餐系统531p9(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·ssm 校园食堂订餐系统