智能家居状态管理系统:数据库设计与服务器通信实现

1. 数据库设计

本系统使用 SQLite 数据库来存储用户数据和设备状态信息。数据库包含两个表:

  • **users 表:**存储用户信息,包括用户ID、用户名、密码等。
CREATE TABLE IF NOT EXISTS users (
    uid INTEGER PRIMARY KEY AUTOINCREMENT,
    username varchar(10), 
    passwd varchar(10)
);
  • **Status 表:**存储智能家居设备状态信息,包括设备名称、状态、值、模式等,并与 users 表关联,通过 uid 字段关联用户。
CREATE TABLE IF NOT EXISTS Status (
    sid INTEGER PRIMARY KEY AUTOINCREMENT,
    uid INTEGER ,
    device_name varchar(10),
    device_state varchar(10),
    value varchar(10),
    mode varchar(10),
    FOREIGN KEY (uid) REFERENCES users (uid)
);

2. 服务器实现

服务器使用 C 语言实现,负责接收客户端请求,查询数据库获取设备状态信息,并根据状态生成建议返回给客户端。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <sqlite3.h>

#define MAX_BUFFER_SIZE 1024

typedef struct {
    int uid;
    char device_name[10];
    char device_state[10];
    char value[10];
    char mode[10];
} Status;

typedef struct {
    int sockfd;
    struct sockaddr_in client_addr;
    socklen_t client_addr_len;
    sqlite3 *db;
} ServerContext;

void handleClientRequest(ServerContext *context) {
    char buffer[MAX_BUFFER_SIZE];
    int userid;

    // 接收客户端发送的 userid
    ssize_t recvSize = recv(context->sockfd, &userid, sizeof(int), 0);
    if (recvSize == -1) {
        perror("userid 接受失败\nrecv");
        return;
    }
    printf("客户端已连接\n");

    // 查询数据库获取设备状态信息
    char sql[100];
    snprintf(sql, sizeof(sql), "SELECT * FROM Status WHERE uid = %d", userid);
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(context->db, sql, -1, &stmt, NULL);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "无法执行的语句: %s\n", sqlite3_errmsg(context->db));
        return;
    }

    Status status;
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        status.uid = sqlite3_column_int(stmt, 1);
        strncpy(status.device_name, sqlite3_column_text(stmt, 2), sizeof(status.device_name));
        strncpy(status.device_state, sqlite3_column_text(stmt, 3), sizeof(status.device_state));
        strncpy(status.value, sqlite3_column_text(stmt, 4), sizeof(status.value));
        strncpy(status.mode, sqlite3_column_text(stmt, 5), sizeof(status.mode));
    }
    sqlite3_finalize(stmt);

    // 分析设备状态并生成建议
    char suggestion[MAX_BUFFER_SIZE];
    if (strcmp(status.device_name, "空调") == 0) {
        int temperature = atoi(status.value);
        if (temperature < 24) {
            snprintf(suggestion, sizeof(suggestion), "空调温度过低,建议提高温度至 26℃");
        } else {
            snprintf(suggestion, sizeof(suggestion), "空调温度正常");
        }
    } else if (strcmp(status.device_name, "加湿器") == 0) {
        int humidity = atoi(status.value);
        if (humidity < 40 || humidity > 70) {
            snprintf(suggestion, sizeof(suggestion), "加湿器湿度过高或过低,建议调整加湿器湿度");
        } else {
            snprintf(suggestion, sizeof(suggestion), "加湿器湿度正常");
        }
    } else {
        snprintf(suggestion, sizeof(suggestion), "设备状态未知");
    }

    // 向客户端发送建议
    ssize_t sendSize = send(context->sockfd, suggestion, strlen(suggestion), 0);
    if (sendSize == -1) {
        perror("发送失败\nsend");
        return;
    }
}

int main() {
    printf("正在连接中…\n");
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("连接失败\nsocket");
        return 1;
    }

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(12345);

    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("连接失败\nbind");
        close(sockfd);
        return 1;
    }

    if (listen(sockfd, 5) == -1) {
        perror("连接失败\nlisten");
        close(sockfd);
        return 1;
    }

    sqlite3 *db;
    int rc = sqlite3_open("/mnt/g/Qt/Client/Smarthome_Client/database/database.db", &db);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "数据库打开失败: %s\n", sqlite3_errmsg(db));
        return 1;
    }

    ServerContext context;
    context.sockfd = sockfd;
    context.client_addr_len = sizeof(context.client_addr);
    context.db = db;

    fd_set readfds;
    int maxfd = sockfd;

    while (1) {
        FD_ZERO(&readfds);
        FD_SET(sockfd, &readfds);

        int activity = select(maxfd + 1, &readfds, NULL, NULL, NULL);
        if (activity == -1) {
            perror("select");
            break;
        }

        if (FD_ISSET(sockfd, &readfds)) {
            int clientSockfd = accept(sockfd, (struct sockaddr *)&context.client_addr, &context.client_addr_len);
            if (clientSockfd == -1) {
                perror("套接字接收失败\naccept");
                break;
            }

            context.sockfd = clientSockfd;
            handleClientRequest(&context);

            close(clientSockfd);
        }
    }

    sqlite3_close(db);
    close(sockfd);
    printf("服务器已关闭\n");

    return 0;
}

3. 客户端实现

客户端使用 Qt 框架实现,负责与服务器建立连接,发送用户ID,接收服务器返回的建议并显示。

#include "procession.h"
#include "ui_procession.h"
#include <QAbstractSocket>
#include <QDebug>

Procession::Procession(int userid,QWidget *parent) : 
    QWidget(parent),
    ui(new Ui::Procession),
    userid(userid)
{
    ui->setupUi(this);
    processionWidget();
    m_socket = new QTcpSocket(this);
    connect(ui->connectBtn, &QPushButton::clicked, this, &Procession::on_connectBtn_clicked);
    connect(m_socket, &QTcpSocket::readyRead, this, &Procession::readyRead);
    connect(m_socket, &QTcpSocket::connected, this, &Procession::connected);
    connect(m_socket, &QTcpSocket::disconnected,this,&Procession::disconnected);
    connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &Procession::displayError);
}

Procession::~Procession()
{
    delete ui;
}

void Procession::processionWidget()
{
    setWindowTitle("服务器通信");
    setAutoFillBackground(true);
    QPalette palette=this->palette();
    QPixmap pixmap(":/user/image/image/net.jpg");
    palette.setBrush(QPalette::Window, QBrush(pixmap));
    setPalette(palette);
    setFixedSize(600,400);
}


void Procession::connected()
{
    ui->message->append("连接成功");
    // 发送 userid 给服务器
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out << this->userid;
    if (m_socket->write(block) == -1) {
        ui->message->append("发送 userid 失败");
        m_socket->close();
    }
}

void Procession::disconnected()
{
    ui->message->append("连接失败");
    m_socket->close();
}

void Procession::readyRead()
{
    QByteArray data = m_socket->readAll();
    if (data.isEmpty()) {
        ui->message->append("读取的数据为空");
        return;
    }
    // 解析服务器返回的数据
    QDataStream in(&data, QIODevice::ReadOnly);
    QString suggestion;
    in >> suggestion; // 使用重载的 >> 运算符来读取数据
    if (in.status() != QDataStream::Ok) {
        ui->message->append("解析服务器返回的数据失败");
        return;
    }
    ui->message->append(suggestion);
    m_socket->close();
}

void Procession::displayError(QAbstractSocket::SocketError error)
{
    QString errorMessage;
    switch (error) {
    case QAbstractSocket::ConnectionRefusedError:
        errorMessage = "连接被拒绝";
        break;
    case QAbstractSocket::RemoteHostClosedError:
        errorMessage = "远程主机关闭";
        break;
    case QAbstractSocket::HostNotFoundError:
        errorMessage = "未找到主机";
        break;
    case QAbstractSocket::SocketTimeoutError:
        errorMessage = "连接超时";
        break;
    case QAbstractSocket::NetworkError:
        errorMessage = "网络错误";
        break;
    default:
        errorMessage = "未知错误";
        break;
    }
    ui->message->append("连接失败:" + errorMessage);
}

void Procession::on_connectBtn_clicked()
{
    if (m_socket->state() == QAbstractSocket::ConnectedState) {
        m_socket->disconnectFromHost(); // 先关闭套接字
        return;
    }
    ui->message->append("已经连接上服务器");
    QString ip = ui->IP->text();
    QString port = ui->port->text();

    if (ip.isEmpty() || port.isEmpty()) {
        ui->message->clear();
        ui->message->append("请输入有效的 IP 地址和端口号");
        return;
    }

    ui->message->clear();
    ui->message->append("正在连接中…");
    m_socket->connectToHost(ip, static_cast<quint16>(ui->port->text().toInt()));
}

4. 运行结果

  1. 服务器运行后,会监听端口 12345,等待客户端连接。
  2. 客户端运行后,输入服务器 IP 地址和端口号,点击连接按钮,尝试连接服务器。
  3. 连接成功后,客户端会发送用户 ID 给服务器,服务器会查询数据库获取用户设备状态信息,并生成建议返回给客户端。
  4. 客户端接收到服务器返回的建议后,会将其显示在界面上。

5. 代码逻辑

服务器代码逻辑

  1. 初始化 socket、绑定地址、监听端口。
  2. 打开数据库。
  3. 使用 select 函数监听 socket 连接请求。
  4. 接收客户端连接请求并创建新的 socket 连接。
  5. 接收客户端发送的用户 ID。
  6. 查询数据库获取用户设备状态信息。
  7. 分析设备状态并生成建议。
  8. 将建议发送给客户端。
  9. 关闭连接。
  10. 关闭数据库和 socket。

客户端代码逻辑

  1. 创建 socket 对象。
  2. 连接服务器。
  3. 连接成功后,发送用户 ID 给服务器。
  4. 监听服务器返回的数据。
  5. 接收服务器返回的数据并解析。
  6. 将解析后的建议显示在界面上。
  7. 关闭连接。

6. 注意事项

  1. 服务器和客户端的 IP 地址和端口号必须一致。
  2. 客户端需要安装 Qt 框架才能运行代码。
  3. 数据库文件路径需要根据实际情况进行修改。
  4. 代码中的建议生成逻辑可以根据实际情况进行调整。

7. 总结

本项目实现了一个简单的智能家居状态管理系统,可以帮助用户更好地了解和管理自己的智能家居设备。服务器通过数据库存储用户数据和设备状态信息,并根据状态提供建议,客户端则通过网络与服务器进行通信,获取设备状态建议。该系统可以作为智能家居系统的基础,可以进一步扩展功能,实现更多的功能,例如:

  • 添加新的设备类型和状态。
  • 实现设备控制功能。
  • 增加用户认证功能。
  • 使用云平台进行数据存储和备份。
  • 开发移动端应用程序。

本项目只是一个简单的示例,可以作为学习智能家居系统开发的参考。希望本项目能够帮助你更好地理解智能家居系统的开发流程。

智能家居状态管理系统:数据库设计与服务器通信实现

原文地址: http://www.cveoy.top/t/topic/fCq1 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录