add old lci module

This commit is contained in:
qing committed 2026-08-02 20:15:07 +08:00
1 parent 38a6e815f0
commit 8499631091
171 files changed
+25562

No files matched your search

+145
View File
@@ -0,0 +1,145 @@
#include "Crypto_Basic.hpp"
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <iostream>
#include <vector>
#include <stdexcept>
#define CRYPTO_CHECK(condition, message) \
if (!(condition)) { \
throw std::runtime_error(message); \
}
namespace qing {
class AES_CBC: public qing::Crypto_Basic {
public:
/* 构造函数 初始化一个随即的密钥和初始化向量 */
AES_CBC() {
generate_key_iv(key, iv);
}
AES_CBC(std::vector<unsigned char> &key, std::vector<unsigned char> &iv) {
this->key = key;
this->iv = iv;
}
std::string get_key() {
return bytes_to_hex(key);
}
std::string get_iv() {
return bytes_to_hex(iv);
}
std::string encrypt(std::string original_text) {
std::vector<unsigned char> plaintext(original_text.begin(), original_text.end());
std::vector<unsigned char> ciphertext = aes_encrypt(plaintext, key, iv);
// hex-bytes转换测试
/*std::string hex = bytes_to_hex(ciphertext);
std::vector<unsigned char> cipher2 = hex_to_bytes(hex);
print_bytes(ciphertext);
print_bytes(cipher2);*/
return bytes_to_hex(ciphertext);
}
std::string decrypt(std::string cipher_hex) {
std::vector<unsigned char> ciphertext = hex_to_bytes(cipher_hex);
std::vector<unsigned char> decrypted = aes_decrypt(ciphertext, key, iv);
return std::string(decrypted.begin(), decrypted.end());
}
static void print_bytes(std::vector<unsigned char>& bytes) {
for (unsigned char byte : bytes) {
std::cerr << std::setw(2) << static_cast<int>(byte);
}
std::cerr << std::endl;
}
private:
std::vector<unsigned char> key;
std::vector<unsigned char> iv;
// 生成随机密钥和IV
static void generate_key_iv(std::vector<unsigned char>& key,
std::vector<unsigned char>& iv) {
key.resize(32); // AES-256 需要 32 字节密钥
iv.resize(16); // AES 块大小 16 字节(128-bit)
// 生成随即密钥
CRYPTO_CHECK(RAND_bytes(key.data(), key.size()) == 1, "密钥生成失败");
// 生成随即IV
CRYPTO_CHECK(RAND_bytes(iv.data(), iv.size()) == 1, "IV生成失败");
}
// AES-256-CBC 加密
static std::vector<unsigned char> aes_encrypt(const std::vector<unsigned char>& plaintext,
const std::vector<unsigned char>& key,
const std::vector<unsigned char>& iv) {
// 创建上下文
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
CRYPTO_CHECK(ctx != nullptr, "无法创建加密上下文");
// 初始化加密操作
CRYPTO_CHECK(EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr,
key.data(), iv.data()) , "加密初始化失败");
// 输出缓冲区 (密文可能比明文长一个块)
std::vector<unsigned char> ciphertext(plaintext.size() + EVP_CIPHER_CTX_block_size(ctx));
int len = 0;
int ciphertext_len = 0;
// 提供明文数据
CRYPTO_CHECK(EVP_EncryptUpdate(ctx, ciphertext.data(), &len,
plaintext.data(), plaintext.size()) == 1, "加载更新失败");
ciphertext_len = len;
// 完成加密(处理填充)
CRYPTO_CHECK(EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len) == 1, "加密结束失败");
ciphertext_len += len;
// 调整到实际密文长度
ciphertext.resize(ciphertext_len);
// 处理上下文
EVP_CIPHER_CTX_free(ctx);
return ciphertext;
}
// AES-256-CBC
static std::vector<unsigned char> aes_decrypt(const std::vector<unsigned char>& ciphertext,
const std::vector<unsigned char>& key,
const std::vector<unsigned char>& iv) {
// 创建上下文
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
CRYPTO_CHECK(ctx != nullptr, "无法创建解密上下文");
// 初始化解密操作
CRYPTO_CHECK(EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr,
key.data(), iv.data()) == 1, "解密初始化失败");
// 输出缓冲区(明文可能比密文短,但最大为密文长度)
std::vector<unsigned char> plaintext(ciphertext.size());
int len = 0;
int plaintext_len = 0;
// 提供密文数据
CRYPTO_CHECK(EVP_DecryptUpdate(ctx, plaintext.data(), &len,
ciphertext.data(), ciphertext.size()) == 1, "解密更新失败");
plaintext_len = len;
// 完成解密(处理填充)
CRYPTO_CHECK(EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len) == 1, "解密结束失败");
plaintext_len += len;
// 调整到实际明文长度
plaintext.resize(plaintext_len);
// 清理上下文
EVP_CIPHER_CTX_free(ctx);
return plaintext;
}
};
}
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include <string>
#include <vector>
#include <stdexcept>
namespace qing {
class Crypto_Basic {
public:
class CryptoExp: public std::runtime_error {
public:
CryptoExp(const std::string& msg): std::runtime_error(msg) {
;
}
};
protected:
/* 字节码转十六进制字符串 */
static std::string bytes_to_hex(const std::vector<unsigned char>& data) {
static const char hex_chars[] = "0123456789abcdef";
std::string hex_string;
hex_string.reserve(data.size() * 2);
for (unsigned char byte: data) {
hex_string.push_back(hex_chars[byte >> 4]); // 高4位
hex_string.push_back(hex_chars[byte & 0x0F]); // 低4位
}
return hex_string;
}
/* 十六进制字符串转字节码 */
static std::vector<unsigned char> hex_to_bytes(const std::string &hex) {
// 检查长度是否为偶数
if (hex.size() % 2 != 0) {
throw std::invalid_argument("十六进制字符串长度必须为偶数");
}
std::vector<unsigned char> bytes;
bytes.reserve(hex.size() / 2);
auto char_to_nibble = [](char c) -> unsigned char {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
throw std::invalid_argument("无效的十六进制字符:" + std::string(1, c));
};
for (size_t i = 0; i < hex.size(); i += 2) {
unsigned char high = char_to_nibble(hex[i]);
unsigned char low = char_to_nibble(hex[i + 1]);
bytes.push_back((high << 4) | low);
}
return bytes;
}
};
}
+288
View File
@@ -0,0 +1,288 @@
#pragma once
#include "Crypto_Basic.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include <memory>
#include <stdexcept>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/core_names.h>
// 错误处理宏
#define HANDLE_SSL_ERROR() { \
char err_buf[512]; \
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); \
std::cerr << "SSL error: " << err_buf << std::endl; \
}
// 自定义删除器用于管理OpenSSL资源
struct EVP_PKEY_deleter {
void operator()(EVP_PKEY* pkey) const { EVP_PKEY_free(pkey); }
};
using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, EVP_PKEY_deleter>;
struct EVP_PKEY_CTX_deleter {
void operator()(EVP_PKEY_CTX* ctx) const { EVP_PKEY_CTX_free(ctx); }
};
using EVP_PKEY_CTX_ptr = std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_deleter>;
struct BIO_deleter {
void operator()(BIO* bio) const { BIO_free_all(bio); }
};
using BIO_ptr = std::unique_ptr<BIO, BIO_deleter>;
namespace qing {
class MyRSA: public qing::Crypto_Basic {
public:
class Generator {
public:
Generator() {
key = generate_rsa_key();
}
void save_pri_key(const std::string filename) {
if (!save_private_key(key.get(), filename)) {
throw Crypto_Basic::CryptoExp("保存私钥失败。");
}
}
void save_pub_key(const std::string filename) {
if(!save_public_key(key.get(), filename)) {
throw Crypto_Basic::CryptoExp("保存公钥失败。");
}
}
private:
EVP_PKEY_ptr key;
// 生成 RSA 密钥对
static EVP_PKEY_ptr generate_rsa_key(size_t key_bits = 2048) {
EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr));
if (!ctx) {
HANDLE_SSL_ERROR();
return nullptr;
}
if (EVP_PKEY_keygen_init(ctx.get()) <= 0) {
HANDLE_SSL_ERROR();
return nullptr;
}
// 设置 RSA 密钥长度
OSSL_PARAM params[] = {
OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_RSA_BITS, &key_bits),
OSSL_PARAM_END
};
if (EVP_PKEY_CTX_set_params(ctx.get(), params) <= 0) {
HANDLE_SSL_ERROR();
return nullptr;
}
EVP_PKEY* pkey = nullptr;
if (EVP_PKEY_generate(ctx.get(), &pkey) <= 0) {
HANDLE_SSL_ERROR();
return nullptr;
}
return EVP_PKEY_ptr(pkey);
}
// 保存私钥到文件
static bool save_private_key(EVP_PKEY* pkey, const std::string& filename,
const char* password = nullptr) {
BIO_ptr bio(BIO_new_file(filename.c_str(), "wb"));
if (!bio) {
std::cerr << "Failed to open file: " << filename << std::endl;
return false;
}
// 选择加密算法
const EVP_CIPHER* cipher = password ? EVP_aes_256_cbc() : nullptr;
int ret = PEM_write_bio_PrivateKey(bio.get(), pkey, cipher,
(unsigned char*) password,
password ? strlen(password) : 0,
nullptr, nullptr);
if (ret != 1) {
HANDLE_SSL_ERROR();
return false;
}
return true;
}
// 保存公钥到文件
static bool save_public_key(EVP_PKEY* pkey, const std::string& filename) {
BIO_ptr bio(BIO_new_file(filename.c_str(), "wb"));
if (!bio) {
std::cerr << "Failed ot open file: " << filename << std::endl;
return false;
}
int ret = PEM_write_bio_PUBKEY(bio.get(), pkey);
if (ret != 1) {
HANDLE_SSL_ERROR();
return false;
}
return true;
}
};
class Private_Key: Crypto_Basic {
public:
Private_Key(const std::string fullpath) {
if (!(pri_key = load_private_key(fullpath)))
throw Crypto_Basic::CryptoExp("读取私钥失败");
}
std::string Decrypt(const std::string cipher_hex) {
std::vector<unsigned char> ciphertext = hex_to_bytes(cipher_hex);
std::vector<unsigned char> decrypted = rsa_decrypt(pri_key.get(), ciphertext);
if (decrypted.empty()) throw Crypto_Basic::CryptoExp("解密失败");
return std::string(decrypted.begin(), decrypted.end());
}
private:
EVP_PKEY_ptr pri_key;
// 从文件加载私钥
EVP_PKEY_ptr load_private_key(const std::string& filename,
const char* password = nullptr) {
BIO_ptr bio(BIO_new_file(filename.c_str(), "rb"));
if (!bio) {
std::cerr << "Failed to open file: " << filename << std::endl;
return nullptr;
}
// 密码回调函数,显示转换为函数指针类型
pem_password_cb* callback = +[] (char *buf, int size, int rwflag, void * u) {
if (!u) return 0;
const char* pass = (const char*)u;
int len = strlen(pass);
if (len > size) len = size;
memcpy(buf, pass, len);
return len;
};
pem_password_cb* cb = password ? nullptr : callback;
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio.get(), nullptr, cb, (void*)password);
if (!pkey) {
HANDLE_SSL_ERROR();
return nullptr;
}
return EVP_PKEY_ptr(pkey);
}
// 使用私钥解密数据
static std::vector<unsigned char> rsa_decrypt(EVP_PKEY* private_key,
const std::vector<unsigned char>& ciphertext) {
EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new(private_key, nullptr));
if (!ctx) {
HANDLE_SSL_ERROR();
return {};
}
if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
// 使用 OAEP 填充
if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
// 获取输出缓冲区大小
size_t outlen = 0;
if (EVP_PKEY_decrypt(ctx.get(), nullptr, &outlen,
ciphertext.data(), ciphertext.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
// 执行解密
std::vector<unsigned char> plaintext(outlen);
if (EVP_PKEY_decrypt(ctx.get(), plaintext.data(), &outlen,
ciphertext.data(), ciphertext.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
plaintext.resize(outlen);
return plaintext;
}
};
class Public_Key: public Crypto_Basic {
public:
Public_Key(const std::string filename) {
if (!(pub_key = load_public_key(filename)))
throw Crypto_Basic::CryptoExp("读取公钥失败");
}
std::string Encrypt(std::string original_text) {
std::vector<unsigned char> plaintext(original_text.begin(), original_text.end());
std::vector<unsigned char> ciphertext = rsa_encrypt(pub_key.get(), plaintext);
if (ciphertext.empty()) throw Crypto_Basic::CryptoExp("加密失败");
return bytes_to_hex(ciphertext);
}
private:
EVP_PKEY_ptr pub_key;
// 从文件加载公钥
static EVP_PKEY_ptr load_public_key(const std::string& filename) {
BIO_ptr bio(BIO_new_file(filename.c_str(), "rb"));
if (!bio) {
std::cerr << "Failed to open file: " << filename << std::endl;
return nullptr;
}
EVP_PKEY* pkey = PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr);
if (!pkey) {
HANDLE_SSL_ERROR();
return nullptr;
}
return EVP_PKEY_ptr(pkey);
}
// 使用公钥加密数据
static std::vector<unsigned char> rsa_encrypt(EVP_PKEY* public_key,
const std::vector<unsigned char>& plaintext) {
EVP_PKEY_CTX_ptr ctx(EVP_PKEY_CTX_new(public_key, nullptr));
if (!ctx) {
HANDLE_SSL_ERROR();
return {};
}
if (EVP_PKEY_encrypt_init(ctx.get()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
// 使用 OAEP 填充
if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
// 获取输出缓冲区大小
size_t outlen =0 ;
if (EVP_PKEY_encrypt(ctx.get(), nullptr, &outlen,
plaintext.data(), plaintext.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
// 执行加密
std::vector<unsigned char> ciphertext(outlen);
if (EVP_PKEY_encrypt(ctx.get(), ciphertext.data(), &outlen,
plaintext.data(), plaintext.size()) <= 0) {
HANDLE_SSL_ERROR();
return {};
}
ciphertext.resize(outlen);
return ciphertext;
}
};
};
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <vector>
#include "nn/Matrix.hh"
#include "nn/ILayer.hpp"
#include "nn/ILearnable.hpp"
#include "nn/ILayerFullyConnectted.hpp"
#include "nn/ILayerConvolutional.hpp"
#include "nn/NeuralNetwork.hpp"
#include "nn/ConvLayer.hpp"
#include "nn/PoolingLayer.hpp"
#include "nn/FlattenLayer.hpp"
#include "nn/NNBuilder.hpp"
#include "nn/CNNBuilder.hpp"
#include "nn/ImageLoader.hpp"
+44
View File
@@ -0,0 +1,44 @@
#pragma once
/*
* # 安装工具
* apt install i2c-tools libi2c-dev
*
* # 扫描设备
* i2cdetect -y 1
*/
#include "cn/中文化.hpp"
/* 基本I2C */
逻辑型 I2C_检查();
空类型 I2C_初始化(常量 字符* 文件路径 = "/dev/i2c-1");
空类型 I2C_清除();
空类型 I2C_获取锁(); /* FIXME: 使用锁类 */
空类型 I2C_释放锁();
空类型 I2C_置地址(常量 无号8位整 地址);
空类型 I2C_写寄存器(无号8位整 寄存器, 无号8位整 值);
无号8位整 I2C_读寄存器(无号8位整 寄存器);
/* PCA9685 */
逻辑型 PCA9685_检查();
空类型 PCA9685_初始化();
空类型 PCA9685_设置PWM(无号8位整 通道, 无号16位整 导通值, 无号16位整 关断值);
空类型 PCA9685_调整角度(无号8位整 通道, 单精度小数 角度);
/*
* MPU6050
* 传感器数据
*/
结构体 MPU6050_传感器数据 {
有号16位整 加速度计_横, 加速度计_纵, 加速度计_竖;
有号16位整 陀螺仪_横, 陀螺仪_纵, 陀螺仪_竖;
};
逻辑型 MPU6050_检查();
空类型 MPU6050_初始化();
MPU6050_传感器数据 MPU6050_读传感器(); /* FIXME: 输入结构体引用(或指针)会更快 */
空类型 MPU6050_计算角度(常量 MPU6050_传感器数据& 数据, 单精度小数& 俯仰角, 单精度小数& 翻滚角);
/* Robot */
#include "hd/Robot.hpp"
+177
View File
@@ -0,0 +1,177 @@
//作者:简单
//修改:qing
/*
* C++17支持了中文变量名,配合define预编译指令可以将代码中文化。
*
* 在C语言中,变量名只能由字母、数字或是下划线组成。
* 由于C++语言兼容C语言,最开始的C++是不能够使用多字节变量名的。
*
* */
#ifndef __中文模式
#define __中文模式
#include <stdint.h>
/*保留字*/
#define 使用 using
#define 命名空间 namespace
#define 取别名 typedef
#define 覆盖 override
#define 取内存大小 sizeof
#define 返回 return
#define 如果 if
#define 否则 else
#define 又或者 else if
#define 真 true
#define 假 false
//#define 且 and
//#define 或 or
#define 空类型 void
#define 常量 const
#define 有符号 signed
#define 无符号 unsigned
#define 文件 FILE
#define 文件结束 EOF
#define 类 class
#define 结构体 struct
#define 枚举类型 enum
#define 对象 this
#define 抛出异常 throw
#define 新建 new
//#define 新的 new
#define 删除 delete
#define 跳出 break
#define 继续 continue
#define 判断循环 while
#define 循环判断 do
#define 循环判断尾 while
#define 分支选择 switch
#define 支系 case
#define 缺省 default
#define 计次循环 for
#define 尝试 try
#define 获取异常 catch
#define 捕获异常 catch
#define 退出 exit
#define 公开的 public
#define 受保护的 protected
#define 私有的 private
#define 外部的 extern
#define 静态的 static
#define 虚拟的 virtual
#define 有号8位整 int8_t
#define 无号8位整 uint8_t
#define 有号16位整 int16_t
#define 无号16位整 uint16_t
#define 有号32位整 int32_t
#define 无号32位整 uint32_t
#define 有号64位整 int64_t
#define 无号64位整 uint64_t
#define 空的 NULL
#define 空指针 nullptr
#define 整数 int
#define 字符 char
#define 短整数 short
#define 长型 long
#define 超长型 long long
#define 单精度小数 float
#define 双精度小数 double
#define 长度单位 size_t
#define 布尔 bool
#define 逻辑型 bool
#define 可变类型 auto
#define 自动的 auto
#define 适应 auto
#define _文件 FILE
#define _文件结束 EOF
#define _读模式 "r"
#define _写模式 "w"
#define 模板 template
#define 类型名 typename
#define 默认的 default
#define 被删除 delete
#define 入口 main
#define 主函数 main
#define 或 ||
#define 或者 ||
#define 而且 &&
#define 且 &&
#define 等于 ==
#define 大于等于 >=
#define 小于等于 <=
#define 大于 >
#define 小于 <
#define 不报错的 noexcept
#define 不转换的 explicit
#define 纯函数的 consteval
#define 预求值的 constexpr
#define _加法重载 operator+
#define _取下标重载 operator[]
#define _开始 begin
#define _结束 end
//-----------------------------------------------------------
//-----------------------------------------------------------
//基本单元
#ifdef __cplusplus
#include <iostream>
命名空间 中文标准{
类 字符串 {
公开的:
/* 初始化为空字符串 */
字符串() {
对象->s = "";
}
/* 以字面量初始化 */
字符串(常量 字符* 源) {
对象->s = 源;
}
/* 访问器 */
常量 字符* 取值() {
返回 s.c_str();
}
/* */
字符串& _加法重载(常量 字符* 乙方) {
对象->s += 乙方;
返回 *对象;
}
/* 字符串之间的连接 */
字符串& _加法重载(字符串& 乙) {
对象->s += 乙.取值();
返回 *对象;
}
私有的:
std::string s;
};
类 标准输出{
公开的:
空类型 调试输出(常量 字符* 消息) {
std::cout << 消息;
}
空类型 调试输出(字符串& 信) {
std::cout<< 信.取值();
}
};
类 标准输入{
公开的:
字符串 取字符串(){
std::string s;
std::cin >> s;
返回 字符串(s.c_str());
}
整数 取整数() {
整数 数;
std::cin >> 数;
返回 数;
}
};
}
#endif //__cplusplus
#endif //__中文模式
+129
View File
@@ -0,0 +1,129 @@
#pragma once
#include "中文化.hpp"
#include "异常.hpp"
命名空间 中文标准 {
模板 <类型名 类型1>
类 向量{
公开的:
/* 注意:当长度单位为0时,内部指针是空的 */
向量() {
对象->指针 = 空指针;
对象->容量 = 0;
}
/* 专门用于处理花括号初始化列表 */
向量(常量 std::initializer_list<类型1>& init = {}) {
对象->指针 = 新建 类型1[init.size()] {空的};
对象->容量 = init.size();
长度单位 计数 = 0;
计次循环(适应 & 东西: init) {
对象->指针[计数++] = 东西;
}
}
~向量() {
删除 对象->指针;
对象->指针 = 空指针;
对象->容量 = 0;
}
/*----------访问器----------*/
长度单位 取容量() {
返回 对象->容量;
}
布尔 是否为空() {
返回 对象->容量 等于 0;
}
/*--------运算符重载--------*/
向量& _取下标重载( 常量 长度单位 下标 ) {
如果(下标 < 0 或 下标 >= 对象->容量)
抛出异常 下标越界{"向量", 对象->容量, 下标};
返回 对象->指针[下标];
}
/*--------可范围遍历--------*/
类型1* _开始() {
返回 对象->指针;
}
类型1* _结束() {
返回 对象->指针 + 对象->容量;
}
/*----------变异器----------*/
空类型 插入元素(常量 长度单位 位置, 常量 向量& 媒介) {
/* 越界检查 */
如果(位置 < 0 或 位置 > 对象->容量)
抛出异常 下标越界{"向量元素插入", 对象->容量+1, 位置};
适应 长度 = 取容量();
适应 新的 = 新建 类型1[长度 + 1] { 空的 };
/* 插入位置之前 */
计次循环(适应 计数 = 0; 计数 < 位置; ++计数)
新的[计数] = 对象->指针[计数];
/* 执行插入 */
新的[位置] = 媒介;
/* 插入位置之后 */
计次循环(适应 计数 = 位置 + 1; 计数 <= 长度; ++计数)
新的[计数] = 对象->指针[计数 - 1];
如果(对象->指针) 删除[] 对象->指针; /* 可能为空 */
对象->指针 = 新的;
}
空类型 追加元素(常量 向量& 媒介) {
适应 长度 = 取容量();
适应 新的 = 新建 类型1[长度 +1] {空的};
计次循环(适应 计数 = 0; 计数 < 长度; ++计数)
新的[计数] = 对象->指针[计数];
新的[长度] = 媒介;
如果(对象->指针) 删除[] 指针; /* 可能为空 */
对象->指针 = 新的;
}
空类型 删除元素(常量 长度单位 位置) {
/* 越界检查 */
如果(位置 < 0 或 位置 >= 对象->容量)
抛出异常 下标越界{"向量元素插入", 对象->容量 + 1, 位置};
适应 长度 = 取容量();
适应 新的 = (长度 等于 1)
? 空指针 : 新建 类型1[长度 - 1] { 空的 };
/* 目标位置之前 */
计次循环(适应 计数 = 0; 计数 < 位置; ++计数)
新的[计数] = 对象->指针[计数];
/* 目标位置之后 */
计次循环(适应 计数 = 位置; 计数 < 长度; ++计数)
新的[计数] = 对象->指针[计数 + 1];
删除[] 对象->指针;
对象->指针 = 新的;
}
/*----------------*/
//类 迭代器{
//公开的:
// 迭代器(常量 std::vector<类型1>::iterator& it) {
// this->it = it;
// }
//私有的:
// std::vector<类型1>::iterator it;
//};
/*----------------*/
私有的:
类型1 *指针;
长度单位 容量;
};
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "中文化.hpp"
命名空间 中文标准 {
类 标准异常 : 公开的 std::exception {
公开的:
不转换的 标准异常(常量 字符* 信息): 信息(信息) {}
常量 字符* what() 常量 不报错的 覆盖 {
返回 信息;
}
私有的:
常量 字符* 信息;
};
类 下标越界 : 公开的 标准异常{
公开的:
下标越界(常量 字符 * 提示, 常量 长度单位 标准值, 常量 长度单位 目前值)
: 标准异常(提示), 标准值(标准值), 目前值(目前值) {
};
长度单位 取标准值() { 返回 标准值; }
长度单位 取目前值() { 返回 目前值; }
私有的:
长度单位 标准值, 目前值;
};
}
+97
View File
@@ -0,0 +1,97 @@
#pragma once
#include "中文化.hpp"
#include "异常.hpp"
#include "顺序表.hpp"
命名空间 中文标准 {
模板 <类型名 类型1, 长度单位 节点大小 = 64>
类 链串 {
公开的:
/* 初始化为空链串 */
链串() {
对象->长度 = 0;
对象->下一个 = 空指针;
对象->上一个 = 空指针;
对象->头 = 对象; /* 每一个子节点都记录同一个头结点 */
对象->尾 = 对象; /* 为了方便,只有头结点记录尾节点 */
}
链串 (常量 std::initializer_list<类型1>& init = {})
{
计次循环(适应 & 东西: init) {
插入(东西);
}
}
/*
* 链式析构
*
* 析构时,会递归析构所有节点。
*/
~链串() {
删除[] 对象->下一个;
}
空类型 插入(常量 类型1& 元素) {
链串 *暂时 = 对象->尾;
如果(暂时->长度 等于 节点大小)
_推();
暂时->存储[暂时->长度++] = 元素;
}
长度单位 统计() {
长度单位 总数 = 0;
链串 *暂时 = 对象->头;
判断循环(暂时 != 空指针) {
总数 += 暂时->长度;
暂时 = 暂时->下一个;
}
返回 总数;
}
/*
* 链串重组
*
* 将链串重组为顺序表W
*/
顺序表<类型1> 重组() {
顺序表<类型1> 结果(统计());
链串 *暂时 = 对象->头;
长度单位 计数 = 0;
判断循环(暂时 != 空指针) {
计次循环(适应 索引 = 0; 索引 < 暂时->长度; ++索引) {
结果[计数++] = 暂时->存储[索引];
}
暂时 = 暂时->下一个;
}
返回 结果;
}
类 链串异常: 公开的 标准异常{
公开的:
链串异常(常量 字符* 消息) : 标准异常(消息) {}
};
私有的:
类型1 存储[节点大小];
长度单位 长度; /* 存储中的元素数量 */
链串 *下一个;
链串 *上一个;
链串 *头;
链串 *尾;
空类型 _推() {
链串*新的;
尝试{ 新的 = 新建 链串(); }
捕获异常 (std::exception& exp){
抛出异常 链串异常{"链串推失败"};
}
_拼接节点(新的);
}
空类型 _拼接节点(链串* 新的) {
对象->尾->下一个 = 新的;
新的->上一个 = 对象->尾;
对象->尾 = 新的;
新的->头 = 对象;
}
};
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "中文化.hpp"
#include "异常.hpp"
命名空间 中文标准 {
模板<类型名 类型1>
类 顺序表 {
公开的:
顺序表(长度单位 大小) {
存储 = 新建 类型1[大小];
长度 = 大小;
}
~顺序表() {
删除[] 存储;
}
类型1 _取下标重载(长度单位 下标) {
如果(下标 >= 长度)
抛出异常 顺序表异常{"下标超出范围"};
返回 存储[下标];
}
类 顺序表异常: 公开的 标准异常{
公开的:
顺序表异常(常量 字符* 消息) : 标准异常(消息) {}
};
私有的:
类型1 *存储;
长度单位 长度;
};
}
+560
View File
@@ -0,0 +1,560 @@
#ifndef PMC_GUI_GTK_HPP
#define PMC_GUI_GTK_HPP
/**
* @file GTK.hpp
* @brief GTK库入口封装
*
* 提供GTK库的统一入口和简化接口,便于在pmc项目中使用GTK进行GUI开发。
*/
#include <gtk/gtk.h>
#include <string>
#include <functional>
#include <memory>
#include <vector>
namespace pmc {
namespace gui {
/**
* @class GTKApplication
* @brief GTK应用程序封装类
*
* 封装GTK应用程序的初始化和运行逻辑,提供简化的API。
*/
class GTKApplication {
public:
/**
* @brief 构造函数
* @param app_id 应用程序ID
* @param app_name 应用程序名称
*/
GTKApplication(const std::string& app_id, const std::string& app_name);
/**
* @brief 析构函数
*/
~GTKApplication();
/**
* @brief 初始化GTK应用程序
* @param argc 命令行参数数量
* @param argv 命令行参数数组
* @return 初始化是否成功
*/
bool initialize(int argc, char** argv);
/**
* @brief 运行应用程序
* @return 应用程序退出码
*/
int run();
/**
* @brief 获取GTK应用程序指针
* @return GtkApplication指针
*/
GtkApplication* get_app() const { return app_; }
/**
* @brief 设置应用程序图标
* @param icon_path 图标文件路径
* @return 设置是否成功
*/
bool set_icon(const std::string& icon_path);
/**
* @brief 设置窗口默认大小
* @param width 窗口宽度
* @param height 窗口高度
*/
void set_default_window_size(int width, int height);
private:
std::string app_id_;
std::string app_name_;
GtkApplication* app_;
int default_width_;
int default_height_;
};
/**
* @class GTKWindow
* @brief GTK窗口封装类
*
* 封装GTK窗口的创建和管理,提供简化的窗口操作接口。
*/
class GTKWindow {
public:
/**
* @brief 构造函数
* @param title 窗口标题
* @param width 窗口宽度
* @param height 窗口高度
*/
GTKWindow(const std::string& title, int width = 800, int height = 600);
/**
* @brief 析构函数
*/
~GTKWindow();
/**
* @brief 显示窗口
*/
void show();
/**
* @brief 隐藏窗口
*/
void hide();
/**
* @brief 设置窗口标题
* @param title 新标题
*/
void set_title(const std::string& title);
/**
* @brief 获取窗口标题
* @return 窗口标题
*/
std::string get_title() const;
/**
* @brief 设置窗口大小
* @param width 宽度
* @param height 高度
*/
void set_size(int width, int height);
/**
* @brief 获取窗口宽度
* @return 窗口宽度
*/
int get_width() const;
/**
* @brief 获取窗口高度
* @return 窗口高度
*/
int get_height() const;
/**
* @brief 获取GTK窗口指针
* @return GtkWindow指针
*/
GtkWindow* get_window() const { return window_; }
/**
* @brief 设置关闭回调函数
* @param callback 关闭回调函数
*/
void set_close_callback(std::function<void()> callback);
/**
* @brief 添加子控件
* @param widget 要添加的控件
*/
void add_widget(GtkWidget* widget);
/**
* @brief 设置窗口位置居中
*/
void center_on_screen();
private:
GtkWindow* window_;
GtkWidget* container_;
std::string title_;
int width_;
int height_;
std::function<void()> close_callback_;
};
/**
* @class GTKButton
* @brief GTK按钮封装类
*
* 封装GTK按钮的创建和事件处理。
*/
class GTKButton {
public:
/**
* @brief 构造函数
* @param label 按钮标签
*/
GTKButton(const std::string& label);
/**
* @brief 析构函数
*/
~GTKButton();
/**
* @brief 设置点击回调函数
* @param callback 点击回调函数
*/
void set_click_callback(std::function<void()> callback);
/**
* @brief 获取GTK按钮指针
* @return GtkButton指针
*/
GtkButton* get_button() const { return button_; }
/**
* @brief 设置按钮标签
* @param label 新标签
*/
void set_label(const std::string& label);
/**
* @brief 获取按钮标签
* @return 按钮标签
*/
std::string get_label() const;
/**
* @brief 启用按钮
*/
void enable();
/**
* @brief 禁用按钮
*/
void disable();
/**
* @brief 设置按钮工具提示
* @param tooltip 工具提示文本
*/
void set_tooltip(const std::string& tooltip);
private:
GtkButton* button_;
std::string label_;
std::function<void()> click_callback_;
};
/**
* @class GTKLabel
* @brief GTK标签封装类
*
* 封装GTK标签的创建和文本管理。
*/
class GTKLabel {
public:
/**
* @brief 构造函数
* @param text 标签文本
*/
GTKLabel(const std::string& text = "");
/**
* @brief 析构函数
*/
~GTKLabel();
/**
* @brief 设置标签文本
* @param text 新文本
*/
void set_text(const std::string& text);
/**
* @brief 获取标签文本
* @return 标签文本
*/
std::string get_text() const;
/**
* @brief 获取GTK标签指针
* @return GtkLabel指针
*/
GtkLabel* get_label() const { return label_; }
/**
* @brief 设置文本对齐方式
* @param xalign 水平对齐 (0.0=左对齐, 0.5=居中, 1.0=右对齐)
* @param yalign 垂直对齐 (0.0=顶部, 0.5=居中, 1.0=底部)
*/
void set_alignment(float xalign = 0.0f, float yalign = 0.5f);
/**
* @brief 设置文本换行
* @param wrap 是否换行
*/
void set_wrap(bool wrap);
/**
* @brief 设置选择模式
* @param selectable 是否可选择
*/
void set_selectable(bool selectable);
private:
GtkLabel* label_;
std::string text_;
};
/**
* @class GTKEntry
* @brief GTK输入框封装类
*
* 封装GTK输入框的创建和文本管理。
*/
class GTKEntry {
public:
/**
* @brief 构造函数
* @param placeholder 占位符文本
*/
GTKEntry(const std::string& placeholder = "");
/**
* @brief 析构函数
*/
~GTKEntry();
/**
* @brief 获取输入文本
* @return 输入文本
*/
std::string get_text() const;
/**
* @brief 设置输入文本
* @param text 新文本
*/
void set_text(const std::string& text);
/**
* @brief 清空输入框
*/
void clear();
/**
* @brief 获取GTK输入框指针
* @return GtkEntry指针
*/
GtkEntry* get_entry() const { return entry_; }
/**
* @brief 设置占位符文本
* @param placeholder 占位符文本
*/
void set_placeholder(const std::string& placeholder);
/**
* @brief 设置输入变化回调函数
* @param callback 输入变化回调函数
*/
void set_changed_callback(std::function<void()> callback);
/**
* @brief 设置激活回调函数(回车键)
* @param callback 激活回调函数
*/
void set_activate_callback(std::function<void()> callback);
/**
* @brief 启用输入框
*/
void enable();
/**
* @brief 禁用输入框
*/
void disable();
private:
GtkEntry* entry_;
std::string placeholder_;
std::function<void()> changed_callback_;
std::function<void()> activate_callback_;
};
/**
* @class GTKTextView
* @brief GTK文本视图封装类
*
* 封装GTK文本视图的创建和文本管理,支持多行文本编辑。
*/
class GTKTextView {
public:
/**
* @brief 构造函数
*/
GTKTextView();
/**
* @brief 析构函数
*/
~GTKTextView();
/**
* @brief 获取文本内容
* @return 文本内容
*/
std::string get_text() const;
/**
* @brief 设置文本内容
* @param text 新文本
*/
void set_text(const std::string& text);
/**
* @brief 追加文本
* @param text 要追加的文本
*/
void append_text(const std::string& text);
/**
* @brief 清空文本
*/
void clear();
/**
* @brief 获取GTK文本视图指针
* @return GtkTextView指针
*/
GtkTextView* get_text_view() const { return text_view_; }
/**
* @brief 获取文本缓冲区
* @return GtkTextBuffer指针
*/
GtkTextBuffer* get_buffer() const { return buffer_; }
/**
* @brief 设置只读模式
* @param read_only 是否只读
*/
void set_read_only(bool read_only);
/**
* @brief 设置文本变化回调函数
* @param callback 文本变化回调函数
*/
void set_changed_callback(std::function<void()> callback);
/**
* @brief 滚动到底部
*/
void scroll_to_bottom();
private:
GtkTextView* text_view_;
GtkTextBuffer* buffer_;
std::function<void()> changed_callback_;
};
/**
* @class GTKBox
* @brief GTK盒子布局容器封装类
*
* 封装GTK盒子布局容器的创建和子控件管理。
*/
class GTKBox {
public:
/**
* @brief 构造函数
* @param orientation 布局方向 (GTK_ORIENTATION_HORIZONTAL 或 GTK_ORIENTATION_VERTICAL)
* @param spacing 子控件间距
*/
GTKBox(GtkOrientation orientation = GTK_ORIENTATION_VERTICAL, int spacing = 5);
/**
* @brief 析构函数
*/
~GTKBox();
/**
* @brief 添加子控件
* @param widget 要添加的控件
* @param expand 是否扩展
* @param fill 是否填充
* @param padding 内边距
*/
void add_widget(GtkWidget* widget, bool expand = false, bool fill = false, int padding = 0);
/**
* @brief 获取GTK盒子指针
* @return GtkBox指针
*/
GtkBox* get_box() const { return box_; }
/**
* @brief 设置间距
* @param spacing 新间距
*/
void set_spacing(int spacing);
/**
* @brief 设置对齐方式
* @param align 对齐方式
*/
void set_alignment(GtkAlign align);
/**
* @brief 设置均匀分配
* @param homogeneous 是否均匀分配空间
*/
void set_homogeneous(bool homogeneous);
private:
GtkBox* box_;
GtkOrientation orientation_;
int spacing_;
};
/**
* @brief 初始化GTK库
* @param argc 命令行参数数量指针
* @param argv 命令行参数数组指针
* @return 初始化是否成功
*/
bool gtk_init(int* argc, char*** argv);
/**
* @brief 运行GTK主循环
*/
void gtk_main();
/**
* @brief 退出GTK主循环
*/
void gtk_main_quit();
/**
* @brief 显示错误对话框
* @param parent 父窗口
* @param title 对话框标题
* @param message 错误消息
*/
void show_error_dialog(GtkWindow* parent, const std::string& title, const std::string& message);
/**
* @brief 显示信息对话框
* @param parent 父窗口
* @param title 对话框标题
* @param message 信息消息
*/
void show_info_dialog(GtkWindow* parent, const std::string& title, const std::string& message);
/**
* @brief 显示确认对话框
* @param parent 父窗口
* @param title 对话框标题
* @param message 确认消息
* @return 用户是否确认
*/
bool show_confirm_dialog(GtkWindow* parent, const std::string& title, const std::string& message);
} // namespace gui
} // namespace pmc
#endif // PMC_GUI_GTK_HPP
+456
View File
@@ -0,0 +1,456 @@
#ifndef PMC_GUI_GTK_IMPL_HPP
#define PMC_GUI_GTK_IMPL_HPP
/**
* @file GTK_impl.hpp
* @brief GTK库封装实现模板
*
* 提供GTK封装类的内联实现,便于头文件包含。
*/
#include "gui/GTK.hpp"
#include <iostream>
namespace pmc {
namespace gui {
// GTKApplication 实现
inline GTKApplication::GTKApplication(const std::string& app_id, const std::string& app_name)
: app_id_(app_id), app_name_(app_name), app_(nullptr), default_width_(800), default_height_(600) {
}
inline GTKApplication::~GTKApplication() {
if (app_) {
g_object_unref(app_);
}
}
inline bool GTKApplication::initialize(int argc, char** argv) {
app_ = gtk_application_new(app_id_.c_str(), G_APPLICATION_FLAGS_NONE);
if (!app_) {
std::cerr << "Failed to create GTK application: " << app_name_ << std::endl;
return false;
}
return true;
}
inline int GTKApplication::run() {
if (!app_) {
std::cerr << "GTK application not initialized" << std::endl;
return -1;
}
return g_application_run(G_APPLICATION(app_), 0, nullptr);
}
inline bool GTKApplication::set_icon(const std::string& icon_path) {
// 实现图标设置逻辑
return true;
}
inline void GTKApplication::set_default_window_size(int width, int height) {
default_width_ = width;
default_height_ = height;
}
// GTKWindow 实现
inline GTKWindow::GTKWindow(const std::string& title, int width, int height)
: title_(title), width_(width), height_(height), window_(nullptr), container_(nullptr) {
window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
if (window_) {
gtk_window_set_title(window_, title_.c_str());
gtk_window_set_default_size(window_, width_, height_);
container_ = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add(GTK_CONTAINER(window_), container_);
}
}
inline GTKWindow::~GTKWindow() {
// GTK会自动管理窗口内存
}
inline void GTKWindow::show() {
if (window_) {
gtk_widget_show_all(GTK_WIDGET(window_));
}
}
inline void GTKWindow::hide() {
if (window_) {
gtk_widget_hide(GTK_WIDGET(window_));
}
}
inline void GTKWindow::set_title(const std::string& title) {
title_ = title;
if (window_) {
gtk_window_set_title(window_, title_.c_str());
}
}
inline std::string GTKWindow::get_title() const {
return title_;
}
inline void GTKWindow::set_size(int width, int height) {
width_ = width;
height_ = height;
if (window_) {
gtk_window_resize(window_, width_, height_);
}
}
inline int GTKWindow::get_width() const {
return width_;
}
inline int GTKWindow::get_height() const {
return height_;
}
inline void GTKWindow::set_close_callback(std::function<void()> callback) {
close_callback_ = callback;
if (window_) {
g_signal_connect(window_, "destroy", G_CALLBACK(+[](GtkWidget* widget, gpointer data) {
auto self = static_cast<GTKWindow*>(data);
if (self && self->close_callback_) {
self->close_callback_();
}
}), this);
}
}
inline void GTKWindow::add_widget(GtkWidget* widget) {
if (container_ && widget) {
gtk_container_add(GTK_CONTAINER(container_), widget);
}
}
inline void GTKWindow::center_on_screen() {
if (window_) {
gtk_window_set_position(window_, GTK_WIN_POS_CENTER);
}
}
// GTKButton 实现
inline GTKButton::GTKButton(const std::string& label)
: label_(label) {
button_ = GTK_BUTTON(gtk_button_new_with_label(label_.c_str()));
}
inline GTKButton::~GTKButton() {
// GTK会自动管理按钮内存
}
inline void GTKButton::set_click_callback(std::function<void()> callback) {
click_callback_ = callback;
if (button_) {
g_signal_connect(button_, "clicked", G_CALLBACK(+[](GtkButton* button, gpointer data) {
auto self = static_cast<GTKButton*>(data);
if (self && self->click_callback_) {
self->click_callback_();
}
}), this);
}
}
inline void GTKButton::set_label(const std::string& label) {
label_ = label;
if (button_) {
gtk_button_set_label(button_, label_.c_str());
}
}
inline std::string GTKButton::get_label() const {
return label_;
}
inline void GTKButton::enable() {
if (button_) {
gtk_widget_set_sensitive(GTK_WIDGET(button_), TRUE);
}
}
inline void GTKButton::disable() {
if (button_) {
gtk_widget_set_sensitive(GTK_WIDGET(button_), FALSE);
}
}
inline void GTKButton::set_tooltip(const std::string& tooltip) {
if (button_) {
gtk_widget_set_tooltip_text(GTK_WIDGET(button_), tooltip.c_str());
}
}
// GTKLabel 实现
inline GTKLabel::GTKLabel(const std::string& text)
: text_(text) {
label_ = GTK_LABEL(gtk_label_new(text_.c_str()));
}
inline GTKLabel::~GTKLabel() {
// GTK会自动管理标签内存
}
inline void GTKLabel::set_text(const std::string& text) {
text_ = text;
if (label_) {
gtk_label_set_text(label_, text_.c_str());
}
}
inline std::string GTKLabel::get_text() const {
return text_;
}
inline void GTKLabel::set_alignment(float xalign, float yalign) {
if (label_) {
gtk_label_set_xalign(label_, xalign);
gtk_label_set_yalign(label_, yalign);
}
}
inline void GTKLabel::set_wrap(bool wrap) {
if (label_) {
gtk_label_set_line_wrap(label_, wrap);
}
}
inline void GTKLabel::set_selectable(bool selectable) {
if (label_) {
gtk_label_set_selectable(label_, selectable);
}
}
// GTKEntry 实现
inline GTKEntry::GTKEntry(const std::string& placeholder)
: placeholder_(placeholder) {
entry_ = GTK_ENTRY(gtk_entry_new());
if (!placeholder_.empty()) {
gtk_entry_set_placeholder_text(entry_, placeholder_.c_str());
}
}
inline GTKEntry::~GTKEntry() {
// GTK会自动管理输入框内存
}
inline std::string GTKEntry::get_text() const {
if (entry_) {
const gchar* text = gtk_entry_get_text(entry_);
return text ? std::string(text) : "";
}
return "";
}
inline void GTKEntry::set_text(const std::string& text) {
if (entry_) {
gtk_entry_set_text(entry_, text.c_str());
}
}
inline void GTKEntry::clear() {
if (entry_) {
gtk_entry_set_text(entry_, "");
}
}
inline void GTKEntry::set_placeholder(const std::string& placeholder) {
placeholder_ = placeholder;
if (entry_) {
gtk_entry_set_placeholder_text(entry_, placeholder_.c_str());
}
}
inline void GTKEntry::set_changed_callback(std::function<void()> callback) {
changed_callback_ = callback;
if (entry_) {
g_signal_connect(entry_, "changed", G_CALLBACK(+[](GtkEntry* entry, gpointer data) {
auto self = static_cast<GTKEntry*>(data);
if (self && self->changed_callback_) {
self->changed_callback_();
}
}), this);
}
}
inline void GTKEntry::set_activate_callback(std::function<void()> callback) {
activate_callback_ = callback;
if (entry_) {
g_signal_connect(entry_, "activate", G_CALLBACK(+[](GtkEntry* entry, gpointer data) {
auto self = static_cast<GTKEntry*>(data);
if (self && self->activate_callback_) {
self->activate_callback_();
}
}), this);
}
}
inline void GTKEntry::enable() {
if (entry_) {
gtk_widget_set_sensitive(GTK_WIDGET(entry_), TRUE);
}
}
inline void GTKEntry::disable() {
if (entry_) {
gtk_widget_set_sensitive(GTK_WIDGET(entry_), FALSE);
}
}
// GTKTextView 实现
inline GTKTextView::GTKTextView() {
text_view_ = GTK_TEXT_VIEW(gtk_text_view_new());
buffer_ = gtk_text_view_get_buffer(text_view_);
}
inline GTKTextView::~GTKTextView() {
// GTK会自动管理文本视图内存
}
inline std::string GTKTextView::get_text() const {
if (buffer_) {
GtkTextIter start, end;
gtk_text_buffer_get_start_iter(buffer_, &start);
gtk_text_buffer_get_end_iter(buffer_, &end);
gchar* text = gtk_text_buffer_get_text(buffer_, &start, &end, FALSE);
std::string result(text ? text : "");
g_free(text);
return result;
}
return "";
}
inline void GTKTextView::set_text(const std::string& text) {
if (buffer_) {
gtk_text_buffer_set_text(buffer_, text.c_str(), -1);
}
}
inline void GTKTextView::append_text(const std::string& text) {
if (buffer_) {
GtkTextIter end;
gtk_text_buffer_get_end_iter(buffer_, &end);
gtk_text_buffer_insert(buffer_, &end, text.c_str(), -1);
}
}
inline void GTKTextView::clear() {
if (buffer_) {
gtk_text_buffer_set_text(buffer_, "", -1);
}
}
inline void GTKTextView::set_read_only(bool read_only) {
if (text_view_) {
gtk_text_view_set_editable(text_view_, !read_only);
}
}
inline void GTKTextView::set_changed_callback(std::function<void()> callback) {
changed_callback_ = callback;
if (buffer_) {
g_signal_connect(buffer_, "changed", G_CALLBACK(+[](GtkTextBuffer* buffer, gpointer data) {
auto self = static_cast<GTKTextView*>(data);
if (self && self->changed_callback_) {
self->changed_callback_();
}
}), this);
}
}
inline void GTKTextView::scroll_to_bottom() {
if (text_view_ && buffer_) {
GtkTextIter end;
gtk_text_buffer_get_end_iter(buffer_, &end);
gtk_text_view_scroll_to_iter(text_view_, &end, 0.0, FALSE, 0.0, 0.0);
}
}
// GTKBox 实现
inline GTKBox::GTKBox(GtkOrientation orientation, int spacing)
: orientation_(orientation), spacing_(spacing) {
box_ = GTK_BOX(gtk_box_new(orientation_, spacing_));
}
inline GTKBox::~GTKBox() {
// GTK会自动管理盒子内存
}
inline void GTKBox::add_widget(GtkWidget* widget, bool expand, bool fill, int padding) {
if (box_ && widget) {
gtk_box_pack_start(box_, widget, expand, fill, padding);
}
}
inline void GTKBox::set_spacing(int spacing) {
spacing_ = spacing;
if (box_) {
gtk_box_set_spacing(box_, spacing_);
}
}
inline void GTKBox::set_alignment(GtkAlign align) {
if (box_) {
gtk_widget_set_halign(GTK_WIDGET(box_), align);
gtk_widget_set_valign(GTK_WIDGET(box_), align);
}
}
inline void GTKBox::set_homogeneous(bool homogeneous) {
if (box_) {
gtk_box_set_homogeneous(box_, homogeneous);
}
}
// 全局函数实现
inline bool gtk_init(int* argc, char*** argv) {
return gtk_init_check(argc, argv);
}
inline void gtk_main() {
::gtk_main();
}
inline void gtk_main_quit() {
::gtk_main_quit();
}
inline void show_error_dialog(GtkWindow* parent, const std::string& title, const std::string& message) {
GtkWidget* dialog = gtk_message_dialog_new(parent,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"%s", message.c_str());
gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
inline void show_info_dialog(GtkWindow* parent, const std::string& title, const std::string& message) {
GtkWidget* dialog = gtk_message_dialog_new(parent,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"%s", message.c_str());
gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
inline bool show_confirm_dialog(GtkWindow* parent, const std::string& title, const std::string& message) {
GtkWidget* dialog = gtk_message_dialog_new(parent,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"%s", message.c_str());
gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return response == GTK_RESPONSE_YES;
}
} // namespace gui
} // namespace pmc
#endif // PMC_GUI_GTK_IMPL_HPP
+174
View File
@@ -0,0 +1,174 @@
# PMC GUI 模块 - GTK 封装
## 概述
本模块提供了对GTK库的C++封装,简化在pmc项目中使用GTK进行GUI开发的过程。封装提供了面向对象的接口,隐藏了GTK的C语言细节。
## 文件结构
```
include/gui/
├── GTK.hpp # 主封装头文件(类声明)
├── GTK_impl.hpp # 内联实现文件
├── gui.hpp # GUI模块主入口
├── example.hpp # 使用示例
└── README.md # 本文档
```
## 主要类
### 1. GTKApplication
封装GTK应用程序的初始化和运行。
```cpp
pmc::gui::GTKApplication app("com.example.pmc", "PMC Application");
app.initialize(argc, argv);
app.run();
```
### 2. GTKWindow
封装GTK窗口的创建和管理。
```cpp
pmc::gui::GTKWindow window("My Window", 800, 600);
window.set_close_callback([]() {
std::cout << "Window closing" << std::endl;
gtk_main_quit();
});
window.show();
```
### 3. GTKButton
封装GTK按钮的创建和事件处理。
```cpp
pmc::gui::GTKButton button("Click Me");
button.set_click_callback([]() {
std::cout << "Button clicked!" << std::endl;
});
```
### 4. GTKEntry
封装GTK输入框的创建和文本管理。
```cpp
pmc::gui::GTKEntry entry("Enter text here...");
entry.set_activate_callback([]() {
std::string text = entry.get_text();
std::cout << "Entered: " << text << std::endl;
});
```
### 5. GTKTextView
封装GTK文本视图的创建和文本管理。
```cpp
pmc::gui::GTKTextView text_view;
text_view.set_read_only(true);
text_view.append_text("Hello, World!\n");
```
### 6. GTKBox
封装GTK盒子布局容器的创建和子控件管理。
```cpp
pmc::gui::GTKBox vbox(GTK_ORIENTATION_VERTICAL, 5);
vbox.add_widget(GTK_WIDGET(button.get_button()));
vbox.add_widget(GTK_WIDGET(entry.get_entry()));
```
## 使用示例
### 简单聊天窗口
```cpp
#include "gui/example.hpp"
int main(int argc, char** argv) {
pmc::gui::example::run_simple_chat_example(argc, argv);
return 0;
}
```
### 设置窗口
```cpp
#include "gui/example.hpp"
int main(int argc, char** argv) {
pmc::gui::example::run_settings_example(argc, argv);
return 0;
}
```
## 编译要求
### 依赖项
- GTK+ 3.0 或更高版本
- C++11 或更高版本
### Ubuntu/Debian 安装依赖
```bash
sudo apt-get install libgtk-3-dev
```
### Fedora/RHEL 安装依赖
```bash
sudo dnf install gtk3-devel
```
### 编译命令示例
```bash
g++ -std=c++11 -o myapp main.cpp `pkg-config --cflags --libs gtk+-3.0`
```
## 集成到pmc项目
### 1. 在CMakeLists.txt中添加
```cmake
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
include_directories(${GTK3_INCLUDE_DIRS})
target_link_libraries(your_target ${GTK3_LIBRARIES})
```
### 2. 使用封装类
```cpp
#include "gui/GTK.hpp"
// 使用pmc::gui命名空间中的类
```
## 设计原则
1. **简化接口**:隐藏GTK的C语言细节,提供C++面向对象接口
2. **资源管理**:使用RAII原则管理GTK对象生命周期
3. **类型安全**:使用C++类型系统减少错误
4. **可扩展性**:易于添加新的封装类
5. **向后兼容**:保持与原生GTK API的互操作性
## 注意事项
1. **线程安全**:GTK不是线程安全的,所有GUI操作必须在主线程进行
2. **内存管理**:GTK使用引用计数,封装类会自动处理引用
3. **信号连接**:使用lambda表达式简化信号处理
4. **错误处理**:提供基本的错误检查和对话框支持
## 扩展指南
要添加新的GTK控件封装:
1. 在`GTK.hpp`中声明类
2. 在`GTK_impl.hpp`中实现内联方法
3. 提供构造函数、析构函数和主要方法
4. 添加信号连接支持
5. 更新示例和文档
## 许可证
本模块遵循pmc项目的整体许可证。
## 贡献
欢迎提交问题和拉取请求改进本模块。
+100
View File
@@ -0,0 +1,100 @@
#ifndef PMC_GUI_HPP
#define PMC_GUI_HPP
/**
* @file gui.hpp
* @brief pmc项目GUI模块主入口
*
* 提供GTK GUI模块的统一入口,简化GUI开发。
*/
#include "gui/GTK.hpp"
namespace pmc {
namespace gui {
/**
* @brief 初始化GUI模块
* @param argc 命令行参数数量
* @param argv 命令行参数数组
* @return 初始化是否成功
*/
bool init(int argc, char** argv);
/**
* @brief 运行GUI主循环
*/
void run();
/**
* @brief 退出GUI主循环
*/
void quit();
/**
* @brief 创建主窗口
* @param title 窗口标题
* @param width 窗口宽度
* @param height 窗口高度
* @return 窗口指针
*/
GTKWindow* create_main_window(const std::string& title = "PMC Application",
int width = 800, int height = 600);
/**
* @brief 创建聊天窗口
* @param parent 父窗口
* @return 聊天窗口指针
*/
GTKWindow* create_chat_window(GTKWindow* parent = nullptr);
/**
* @brief 创建设置窗口
* @param parent 父窗口
* @return 设置窗口指针
*/
GTKWindow* create_settings_window(GTKWindow* parent = nullptr);
/**
* @brief 显示关于对话框
* @param parent 父窗口
*/
void show_about_dialog(GTKWindow* parent);
/**
* @brief 显示状态栏
* @param parent 父窗口
* @param message 状态消息
*/
void show_status_bar(GTKWindow* parent, const std::string& message);
/**
* @brief 更新状态栏消息
* @param message 新状态消息
*/
void update_status_bar(const std::string& message);
/**
* @brief 显示进度对话框
* @param parent 父窗口
* @param title 对话框标题
* @param message 进度消息
*/
void show_progress_dialog(GTKWindow* parent, const std::string& title, const std::string& message);
/**
* @brief 更新进度对话框
* @param progress 进度值 (0.0-1.0)
* @param message 进度消息
*/
void update_progress_dialog(double progress, const std::string& message);
/**
* @brief 关闭进度对话框
*/
void close_progress_dialog();
} // namespace gui
} // namespace pmc
#endif // PMC_GUI_HPP
+12
View File
@@ -0,0 +1,12 @@
#pragma once
/* Camera */
#define cimg_display 0 /* 不显示图像,只保存 */
#include "CImg.h"
void Camera_init(int w, int h);
void Camera_close();
bool Camera_is_open();
/* 在线程安全的情况下取得一帧图像 */
cimg_library::CImg<unsigned char> get_frame_from_camera(bool& flag);
+241
View File
@@ -0,0 +1,241 @@
#pragma once
#include <cmath>
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <unistd.h>
#include "cn/中文化.hpp"
#include "Dashun_linux.hpp"
命名空间 qing{
类 伺服电机{ /* 180度舵机 */
公开的:
/* FIXME: min只能比max小 */
伺服电机(整数 通道, 单精度小数 上限=180, 单精度小数 下限=0, 逻辑型 逆向=false)
: 通道(通道), 上限(上限), 下限(下限), 逆向(逆向) {}
/* 控制舵机运行至一个角度 */
空类型 转动至(单精度小数 角度) {
角度 = std::min(上限, 角度);
角度 = std::max(下限, 角度);
PCA9685_调整角度(
通道, (逆向) ? 180 - 角度 : 角度
);
}
/* FIXME: 返回最大值 */
单精度小数 取上限() {
返回 上限;
}
私有的:
整数 通道; /* 通道编号 */
单精度小数 上限; /* 角度上限 */
单精度小数 下限; /* 角度下限 */
逻辑型 逆向; /* 是否反向 */
};
}
命名空间 qing {
类 ActParser{ /* 动作脚本解析器 */
公开的:
/* 解析标准输入中的动作脚本 */
空类型 fromStdin() {
计数 = 0;
清除所有();
解析(std::cin);
}
/* 解析文件输入流中的动作脚本 */
空类型 fromFile(const std::string& 路径) {
计数 = 0;
std::ifstream 文件(路径.c_str(), std::ios::in);
如果 (!文件.is_open())
抛出异常 std::runtime_error("打开文件失败");
清除所有();
解析(文件);
文件.close();
}
/* 解析字符串流中的动作脚本 */
空类型 fromStr(const std::string& 脚本) {
std::stringstream 流(脚本);
计数 = 0;
清除所有();
解析(流);
}
/* 执行刚刚解析出来的机器人和动作 */
空类型 play() {
如果 (机组.empty() || 矩阵.empty())
抛出异常 std::runtime_error("没动作或机组");
机器人扮演(机组, 矩阵);
}
/* 获得机器人的最大值向量, FIXME: 把逻辑写在外面 */
std::vector<float> get() {
适应 结果 = std::vector<float>();
计次循环 (适应& 项目: this->机组) {
结果.push_back(项目.取上限());
}
/* 这个数组中包含机器人每个舵机的角度上限 */
返回 结果;
}
/* 获取当前这一组动作 */
std::vector<float> now() {
如果 (计数 >= 矩阵.size())
抛出异常 std::runtime_error("now finished");
返回 矩阵[计数];
}
/* 执行下一组动作 */
空类型 next() {
如果 (计数 >= 矩阵.size())
抛出异常 std::runtime_error("没下一个动作了");
适应 临时向量 = 矩阵[计数++];
适应 临时矩阵 = std::vector<std::vector<float>> ({临时向量});
机器人扮演(机组, 临时矩阵);
}
/* 输入下一个动作执行 */
空类型 next(std::vector<float>& act) {
计数++;
auto vv = std::vector<std::vector<float>> ({act});
机器人扮演(机组, vv);
}
private:
字符 句[16] = {0}; /* FIXME: 这个栈有越界风险 */
整数 顶 = 0;
整数 计数 = 0;
std::vector<float> 数组; /* 临时使用 */
std::vector<std::vector<float>> 矩阵; /* 解析出的整套动作 */
std::vector<伺服电机> 机组; /* 机器人抽象 */
空类型 解析(std::istream& 源) { /* NOTE: 修改为非静态成员函数能够减少行数 FIXME: 改为使用C++风格的文件操作 */
清除临时();
字符 c = 0;
布尔 end_flag = true;
布尔 comment_flag = false;
布尔 obj_flag = false;
判断循环 (end_flag) { /* 255 for arm */
end_flag = (bool)源.get(c);
//std::cout<<c;
如果 ((!comment_flag && c >= '0' && c <= '9') || c == '+' || c == '-') { /* 读取数字 */
句[顶++] = c;
std::cout<<c;
}
否则 如果 (!comment_flag && c == ' ') { /* 每个角度以空格结尾 */
句[顶] = '\0';
递交舵机();
}
否则 如果 (!comment_flag && (c == '\r' || c == '\n')) { /* 忽略回车 */
;
}
否则 如果 (!comment_flag && c == '#') { /* 注释以#开头 */
comment_flag = 真;
}
否则 如果 (comment_flag && c == '\n') { /* 注释以换行结尾 */
comment_flag = 假;
}
否则 如果 (comment_flag && c != '\n') { /* 忽略注释 */
;
}
否则 如果 (!comment_flag && !obj_flag && 顶 == 0 && c == '!') { /* 构建模式 */
obj_flag = 真;
}
否则 如果 (!comment_flag && obj_flag && (c==';' || end_flag)) { /* 构建机器人 */
句[顶] = '\0';
递交舵机();
递交机组();
obj_flag = 假;
}
否则 如果 (!comment_flag && !obj_flag && (c == ';' || end_flag)) { /* 每个动作以分号结尾 */
句[顶] = '\0';
递交舵机();
递交动作();
}
否则 {
抛出异常 std::runtime_error(
"Invalid action: " + std::to_string(int(c)));
}
}
}
空类型 递交舵机() {
如果 (顶 > 0) {
单精度小数 角度 = (单精度小数)atoi(句);
数组.push_back(角度);
std::cout<<角度<<std::endl;
}
句[0] = '\0';
顶 = 0;
}
空类型 递交动作() {
如果 (!数组.empty()) {
std::cout<<"接收到动作: " << 数组.size() << std::endl;
矩阵.push_back(数组);
数组 = std::vector<float>{};
}
}
空类型 递交机组() {
如果 (!数组.empty()) {
std::cout << "接收到机组: " << 数组.size() << std::endl;
机组 = 机组工厂(数组);
数组 = std::vector<float>{};
}
}
空类型 清除临时() {
句[0] = '\0';
顶 = 0;
数组 = std::vector<float> {};
}
空类型 清除所有() {
句[0] = '\0';
顶 = 0;
数组 = std::vector<float> {};
矩阵 = std::vector<std::vector<float>> {};
机组 = std::vector<伺服电机> {};
}
/* 机器人工厂,FIXME: 这里可以做成舵机工厂就可以了, */
静态的 std::vector<伺服电机> 机组工厂(const std::vector<float>& 上限集) {
std::vector<伺服电机> 机组;
计次循环 (整数 甲=0; 甲 < 上限集.size(); ++甲)
机组.push_back(
伺服电机(甲, std::abs(上限集[甲]), 0, (上限集[甲] >= 0) ? 假 : 真));
返回 机组;
}
/* 机器人移动,输入一组舵机对象和一组目标角度 */
静态的 空类型 机器人行动(std::vector<伺服电机>& 机组, std::vector<float>& 动作) {
如果 (机组.size() + 1 != 动作.size())
抛出异常 std::runtime_error(
"动作与机组不符合, 我们有"
+ std::to_string(机组.size()) + "和" + std::to_string(动作.size())
);
/* FIXME: 随机下标以消除先后时间差 */
计次循环 (int 甲=0; 甲<机组.size(); ++甲) {
机组[甲].转动至(动作[甲]);
}
/* 最后一组数据是毫秒延时 */
预求值的 整数 缩放 = 1000;
usleep(动作[动作.size()-1] * 缩放);
}
/* 机器人整套动作 */
静态的 空类型 机器人扮演(
std::vector<伺服电机>& 机组, std::vector<std::vector<float>>& 动作集)
{
计次循环 (适应 动作: 动作集)
机器人行动(机组, 动作);
}
};
}
+206
View File
@@ -0,0 +1,206 @@
/*
* 这是cpp使用linux C手段读写串口的实现。
*
* 作者在其中加入了“沟通”手段,这种手段主要是针对COPS设备。
*
* COPS设备的通信手段主要是上位机问,下位机答,一问一答的形式。
*
* (!)还未进行对于热插拔的支持。
*
* 20250621 暂时不用了
*/
#pragma once
#include <iostream>
#include <vector>
#include <regex>
#include <chrono>
#include <thread>
#include <cstdio>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <termios.h>
#include <sys/time.h>
namespace qing {
/*
* 串口类
*
* 长连接的速度会更快
*/
class Serial {
private:
/* 串口文件描述符 */
int fd = 0;
public:
/* 输入设备文件地址,进行构造。打开后通常需要等待一下 */
Serial(const std::string& path, double wait = 2) {
fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
throw DeviceOpenErroring();
}
std::this_thread::sleep_for(std::chrono::milliseconds((long)(wait*1000))); /* 等待串口 */
}
//释放
~Serial() {
if (fd != -1) close(fd);
}
/* 设置串口 */
void Set() {
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B115200); /* 设置波特率 */
cfsetospeed(&options, B115200);
options.c_cflag |= (CLOCAL | CREAD);// 启用接收和本地模式
options.c_cflag &= ~PARENB;// 8位数据位,无校验,1位停止位
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag &= ~CRTSCTS;// 禁用硬件流控
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);// 原始输入模式
options.c_oflag &= ~OPOST;// 原始输出模式
options.c_cc[VMIN] = 0;// 设置超时 - 1秒
options.c_cc[VTIME] = 10;
tcsetattr(fd, TCSANOW, &options);
tcflush(fd, TCIOFLUSH); /* 清空缓冲区 */
}
/* 写数据 每次写之前清空缓冲区数据*/
void Write(const std::string& msg) {
tcflush(fd, TCIOFLUSH); /* 清空缓冲区 */
write(fd, msg.c_str(), msg.size());
}
/* 读数据 */
std::string CircleRead() {
char buffer[1024] = {0};
std::string ret = "";
ssize_t n;
do {
n = read(fd, buffer, sizeof(buffer));
if (n == -1 && ret == "") {
throw NoResponse();
} else if (n == -1 && ret != "") {
return ret;
} else if (n == 0) {
return ret;
} else if (n > 0) {
buffer[n] = '\0';
ret += buffer;
}
} while (true);
}
/* 读数据 */
std::string Read() {
char buffer[1024] = {0};
std::string ret = "";
ssize_t n;
n = read(fd, buffer, sizeof(buffer));
if (n == -1 && ret == "") {
throw NoResponse();
} else if (n > 0) {
buffer[n] = '\0';
ret += buffer;
}
return ret;
}
/* 发送然后获取 */
std::string Send_Wait_And_Get(const std::string& snd, double wait) {
Write(snd);
std::this_thread::sleep_for(std::chrono::milliseconds((long)(wait*1000)));
return Read();
}
/* 一问一答,尝试一定次数 */
std::string Try_Comm(const std::string& snd, double wait, int times = 10, int drop_tail = 0) {
int num = 0;
while (true) {
try {
num ++;
if (num > times) break;
std::string msg = Send_Wait_And_Get(snd, wait);
DropTail(msg, drop_tail);
return msg;
} catch (qing::Serial::Serial_Error e) {
std::cerr << e.what() << std::endl;
}
}
throw NoResponse();
}
/* 去掉字符串末尾一定数量的字符 */
static void DropTail(std::string& msg, int n) {
for (int i=0; i<n; i++) {
msg.pop_back();
}
}
/* 串口异常类 */
class Serial_Error: public std::exception {
private:
std::string message;
public:
explicit Serial_Error(const std::string& msg): message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
/* 打开设备异常 */
class DeviceOpenErroring: public Serial_Error {
public:
DeviceOpenErroring(): Serial_Error("Device Open Erroring") {}
};
/* select异常 */
class SelectErroring: public Serial_Error {
public:
SelectErroring(): Serial_Error("Select Erroring") {}
};
/* 无响应 */
class NoResponse: public Serial_Error {
public:
NoResponse(): Serial_Error(" No Response") {}
};
/* 无响应异常 */
class SerialReadError: public Serial_Error {
public:
SerialReadError(): Serial_Error("Serial Read Error") {}
};
/* 获取所有的串口 */
static std::vector<std::string> GetPorts() {
std::vector<std::string> ports;
//Linux实现
DIR *dir;
struct dirent *ent;
const std::regex serial_regex("(ttyS|ttyUSB|ttyACM)[0-9]+");
if ((dir = opendir("/dev")) != NULL) {
while((ent = readdir(dir)) != NULL) {
std::string name = ent->d_name;
if (std::regex_match(name, serial_regex)) {
ports.push_back("/dev/"+name);
}
}
closedir(dir);
}
return ports;
}
/* 以线程不安全的方式打印所有的串口 */
static void UnsafePrintPorts() {
auto v = GetPorts();
for (const auto& i: v) {
std::cout << i << std::endl;
}
}
};
}
+93
View File
@@ -0,0 +1,93 @@
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <unistd.h>
struct CpuData {
std::string cpu_name;
long user;
long nice;
long system;
long idle;
long iowait;
long irq;
long softirq;
long steal;
long guest;
long guest_nice;
long total() const {
return user + nice + system + idle + iowait + irq + softirq + steal;
}
long idle_total() const {
return idle + iowait;
}
};
class CpuMonitor {
private:
std::vector<CpuData> read_cpu_stats() {
std::vector<CpuData> stats;
std::ifstream file("/proc/stat");
std::string line;
while (std::getline(file, line)) {
if (line.substr(0, 3) == "cpu") {
std::istringstream iss(line);
CpuData data;
iss >> data.cpu_name >> data.user >> data.nice >> data.system
>> data.idle >> data.iowait >> data.irq >> data.softirq
>> data.steal >> data.guest >> data.guest_nice;
stats.push_back(data);
}
}
return stats;
}
public:
double get_cpu_usage() {
auto stats1 = read_cpu_stats();
sleep(1);
auto stats2 = read_cpu_stats();
if (stats1.empty() || stats2.empty()) {
return -1.0;
}
long total_diff = stats2[0].total() - stats1[0].total();
long idle_diff = stats2[0].idle_total() - stats1[0].idle_total();
if (total_diff == 0) {
return 0.0;
}
return 100.0 * (total_diff - idle_diff) / total_diff;
}
std::vector<double> get_per_cpu_usage() {
auto stats1 = read_cpu_stats();
sleep(1);
auto stats2 = read_cpu_stats();
std::vector<double> usages;
for (size_t i = 1; i < stats1.size() && i < stats2.size(); i++) {
long total_diff = stats2[i].total() - stats1[1].total();
long idle_diff = stats2[i].idle_total() - stats1[i].idle_total();
if (total_diff > 0) {
usages.push_back(100.0 * (total_diff - idle_diff) / total_diff);
} else {
usages.push_back(0.0);
}
}
return usages;
}
};
+14
View File
@@ -0,0 +1,14 @@
#ifndef HD_MEM_H
#define HD_MEM_H
#include <iostream>
#include <sys/sysinfo.h>
struct MemoryInfo {
long totalMemory; /* 总内存(bytes) */
long freeMemory; /* 空闲内存 */
long usedMemory; /* 已用内存 */
double usageRate; /* 使用率(%) */
};
MemoryInfo getMemoryUsageWithSysinfo();
#endif
+106
View File
@@ -0,0 +1,106 @@
#ifndef PMC_LLM_CLIENT_HPP
#define PMC_LLM_CLIENT_HPP
#include <string>
#include <vector>
#include <utility>
#include <functional>
#include <memory>
#include <boost/json.hpp>
#include "net/HttpClient.hpp"
namespace pmc::llm {
class LLMClient {
public:
LLMClient(const std::string& api_key) : api_key_(api_key) {}
// 流式请求(使用同步版本避免线程死锁)
void stream_request(const std::vector<std::pair<std::string, std::string>>& messages,
std::function<void(const std::string&)> callback) {
// 使用智能指针确保HttpClient在回调期间保持存活
auto http_client = std::make_shared<pmc::net::HttpClient>();
// 设置请求头
http_client->set_header("Authorization: Bearer " + api_key_);
http_client->set_header("Content-Type: application/json");
http_client->set_header("Accept: text/event-stream");
// 构建请求JSON
boost::json::object request;
request["model"] = "deepseek-chat";
request["stream"] = true;
// 构建消息数组,添加系统消息要求返回Markdown格式
boost::json::array messages_array;
// 添加系统消息,要求使用Markdown格式回复
boost::json::object system_message;
system_message["role"] = "system";
system_message["content"] = "请使用Markdown格式回复。使用**粗体**表示重要概念,*斜体*表示强调,`代码`表示代码片段,使用列表、标题等Markdown元素来组织内容。";
messages_array.push_back(system_message);
// 添加用户消息
for (const auto& msg : messages) {
boost::json::object message_obj;
message_obj["role"] = msg.first;
message_obj["content"] = msg.second;
messages_array.push_back(message_obj);
}
request["messages"] = messages_array;
// 转换为字符串
std::string request_body = boost::json::serialize(request);
// 创建包装回调,解析JSON并提取文本内容
auto wrapped_callback = [callback](const std::string& json_str) {
try {
// 解析JSON
boost::json::value json_value = boost::json::parse(json_str);
// 检查是否是有效的响应
if (json_value.is_object()) {
const auto& obj = json_value.as_object();
// 检查是否有choices数组
if (obj.contains("choices") && obj.at("choices").is_array()) {
const auto& choices = obj.at("choices").as_array();
if (!choices.empty() && choices[0].is_object()) {
const auto& choice = choices[0].as_object();
// 检查是否有delta对象
if (choice.contains("delta") && choice.at("delta").is_object()) {
const auto& delta = choice.at("delta").as_object();
// 检查是否有content字段
if (delta.contains("content") && delta.at("content").is_string()) {
const std::string& content = delta.at("content").as_string().c_str();
callback(content);
return;
}
}
}
}
}
// 如果JSON解析失败或没有有效内容,发送原始字符串(用于调试)
callback("[" + json_str + "]");
} catch (const std::exception& e) {
// JSON解析失败,发送原始字符串
callback("[" + json_str + "]");
}
};
// 发送POST请求 - 使用post_stream_sync方法
http_client->post_stream_sync("https://api.deepseek.com/v1/chat/completions", request_body, wrapped_callback);
}
private:
std::string api_key_;
};
} // namespace pmc::llm
#endif // PMC_LLM_CLIENT_HPP
+133
View File
@@ -0,0 +1,133 @@
#ifndef PMC_LLM_WEBSOCKET_SERVER_HPP
#define PMC_LLM_WEBSOCKET_SERVER_HPP
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <chrono>
#include <functional>
#include <vector>
#include <utility>
#include <atomic>
#include "LLMClient.hpp"
#include "net/WebSocketServer.hpp"
namespace pmc::llm {
class LLMWebSocketServer {
public:
LLMWebSocketServer(const std::string& api_key, unsigned short port = 8080)
: api_key_(api_key)
, port_(port)
, running_(false) {
// 创建WebSocket服务器
ws_server_ = std::make_unique<pmc::net::WebSocketServer>(port);
}
~LLMWebSocketServer() {
stop();
}
void start() {
if (running_) {
return;
}
std::cout << "启动LLM WebSocket服务器,端口: " << port_ << std::endl;
std::cout << "API密钥: " << (api_key_.empty() ? "未设置" : "已设置") << std::endl;
// 设置WebSocket回调
ws_server_->onConnect([this](const std::string& client_id, const std::string& remote_address) {
handle_connect(client_id, remote_address);
});
ws_server_->onMessage("/", [this](const std::string& client_id,
const std::string& message,
std::function<void(const std::string&)> send_callback) {
// 这里我们使用异步发送,所以忽略send_callback参数
handle_message(client_id, message);
});
ws_server_->onDisconnect([this](const std::string& client_id) {
handle_disconnect(client_id);
});
// 启动WebSocket服务器
ws_server_->start();
running_ = true;
std::cout << "服务器已启动,等待客户端连接..." << std::endl;
std::cout << "按Ctrl+C停止服务器" << std::endl;
}
void stop() {
if (!running_) {
return;
}
std::cout << "正在停止服务器..." << std::endl;
ws_server_->stop();
running_ = false;
}
bool is_running() const {
return running_;
}
private:
// 处理连接
void handle_connect(const std::string& client_id, const std::string& remote_address) {
std::cout << "客户端连接: " << client_id << " (" << remote_address << ")" << std::endl;
// 使用异步线程发送欢迎消息,确保客户端已准备好
std::thread([this, client_id]() {
// 等待一小段时间,确保客户端连接完全建立
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// 发送欢迎消息
ws_server_->sendToClient(client_id, "欢迎连接到LLM WebSocket服务器!");
ws_server_->sendToClient(client_id, "请发送您的消息,我将为您提供AI助手服务。");
}).detach();
}
// 处理断开连接
void handle_disconnect(const std::string& client_id) {
std::cout << "客户端断开: " << client_id << std::endl;
}
// 处理消息
void handle_message(const std::string& client_id, const std::string& message) {
std::cout << "收到来自客户端 " << client_id << " 的消息: " << message << std::endl;
// 创建LLM客户端
LLMClient llm_client(api_key_);
// 构建消息历史
std::vector<std::pair<std::string, std::string>> messages = {
{"user", message}
};
// 发送流式请求
llm_client.stream_request(messages, [this, client_id](const std::string& chunk) {
// 将每个数据块发送给客户端
ws_server_->sendToClient(client_id, chunk);
});
// 发送处理完成标记
ws_server_->sendToClient(client_id, "[处理完成]");
}
private:
std::string api_key_;
unsigned short port_;
bool running_;
std::unique_ptr<pmc::net::WebSocketServer> ws_server_;
};
} // namespace pmc::llm
#endif // PMC_LLM_WEBSOCKET_SERVER_HPP
+107
View File
@@ -0,0 +1,107 @@
/* 日志 */
#pragma once
#include <iostream>
#include <string>
#include <mutex>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <ctime>
enum class LogLevel {
DEBUG,
INFO,
WARNING,
ERROR,
CRITICAL
};
namespace qing {
/*
* 这是由Deepseek创作的,用于记录日志的类模块
* 简化版本,只输出到控制台
*
* 优化意见:改为存储日志文件
*/
class Logger {
public:
//获取日志实例(单例模式)
static Logger& getInstance() {
static Logger instance;
return instance;
}
//初始化日志系统
void init(LogLevel minLogLevel = LogLevel::INFO,
bool consoleOutput = true) {
std::lock_guard<std::mutex> lock(mutex_);
if (initialized_) {
return;
}
minLogLevel_ = minLogLevel;
consoleOutput_ = consoleOutput;
initialized_ = true;
}
// 设置最低日志级别
void setMinLogLevel(LogLevel level) {
std::lock_guard<std::mutex> lock(mutex_);
minLogLevel_ = level;
}
// 日志输出方法
void log(LogLevel level, const std::string& message) {
if (level < minLogLevel_ || !consoleOutput_) {
return;
}
auto now = std::chrono::system_clock::now();
auto in_time_t = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) % 1000;
std::tm tm;
localtime_r(&in_time_t, &tm);
std::stringstream ss;
ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S")
<< "." << std::setfill('0') << std::setw(3) << ms.count()
<< " [" << logLevelToString(level) << "] "
<< message;
std::lock_guard<std::mutex> lock(mutex_);
std::cout << ss.str() << std::endl;
}
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
private:
Logger() = default;
~Logger() = default;
std::string logLevelToString(LogLevel level) {
switch (level) {
case LogLevel::DEBUG: return "DEBUG";
case LogLevel::INFO: return "INFO";
case LogLevel::WARNING: return "WARNING";
case LogLevel::ERROR: return "ERROR";
case LogLevel::CRITICAL: return "CRITICAL";
default: return "UNKNOWN";
}
}
std::mutex mutex_;
LogLevel minLogLevel_ = LogLevel::INFO;
bool consoleOutput_ = true;
bool initialized_ = false;
};
}
#define LOG_DEBUG(message) qing::Logger::getInstance().log(LogLevel::DEBUG, message)
#define LOG_INFO(message) qing::Logger::getInstance().log(LogLevel::INFO, message)
#define LOG_WARNING(message) qing::Logger::getInstance().log(LogLevel::WARNING, message)
#define LOG_ERROR(message) qing::Logger::getInstance().log(LogLevel::ERROR, message)
#define LOG_CRITICAL(message) qing::Logger::getInstance().log(LogLevel::CRITICAL, message)
+82
View File
@@ -0,0 +1,82 @@
#pragma once
#include <httplib.h> // libcpp-httplib-dev
#include <nlohmann/json.hpp> //
#include <iostream>
#include <filesystem> //
#include <stdexcept>
namespace fs = std::filesystem;
/* 这里的类型是HTTP服务器要设置的回调函数的类型 */
using Handler = std::function<void(const httplib::Request&, httplib::Response&)>;
namespace qing {
/* NOTE: 不要删除该类,这个抽象成能够方便重构 */
class Http { /* 超文本传输协议(HTTP)服务器类 */
public:
/* 初始化各个变量 */
Http(std::string addr, int port): addr(addr), port(port) {}
/* 运行服务器,失败抛出异常 */
void run() {
//printAddr(); /* 在日志里打印更好 */
if (!svr.listen(addr.c_str(), port))
throw StartServerFailed("");
}
/* 终止服务器 */
void stop() { svr.stop(); }
/*
* 设置路由和对应的处理函数
*
* FIXME: 使用一个方法实现两个功能
*/
void get (std::string path, Handler handler) { svr.Get(path, handler); }
void post(std::string path, Handler handler) { svr.Post(path, handler); }
/*
* 打印地址
*
* FIXME: 把这个函数的具体功能放到类的外部
*/
std::string printAddr() { return std::string("pmcd now listenning on this address: http://") + addr + ":" + std::to_string(port); }
/* 获取当前执行文件目录 */
//static std::string getCurrentDirectory() {
// fs::path currentPath = fs::absolute(__FILE__);
// return currentPath.parent_path().string();
//}
// 读取文本文件的所有内容
//static std::string readFile(const std::string& filePath) {
// std::ifstream file(filePath); // 创建一个输入文件流对象,用于读取文件
// if (!file) {
// std::cerr << "无法打开文件:" << filePath << std::endl;
// return "";
// }
// std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); /* 使用迭代器读取文件内容到字符串 */
// file.close();
// return content;
//}
class Http_Error: public std::exception { /* HTTP 异常类 */
private:
std::string message;
public:
explicit Http_Error(const std::string& msg): message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
class StartServerFailed: public Http_Error { /* 开始服务失败 */
public:
StartServerFailed(const std::string& msg): Http_Error(msg) {}
};
private:
httplib::Server svr; /* 创建HTTP服务器对象(如果需要HTTPS,则使用SSLServer */
std::string addr; /* 服务地址 */
int port; /* 端口 */
};
}
+151
View File
@@ -0,0 +1,151 @@
#ifndef PMC_HTTP_CLIENT_HPP
#define PMC_HTTP_CLIENT_HPP
#include <string>
#include <functional>
#include <curl/curl.h>
#include <iostream>
namespace pmc::net {
class HttpClient {
public:
HttpClient() {
std::cout << "HttpClient构造函数: 开始初始化" << std::endl;
curl_ = curl_easy_init();
if (!curl_) {
throw std::runtime_error("Failed to initialize CURL");
}
std::cout << "HttpClient构造函数: 初始化完成" << std::endl;
}
~HttpClient() {
std::cout << "HttpClient析构函数: 开始清理" << std::endl;
if (curl_) {
curl_easy_cleanup(curl_);
}
if (headers_) {
curl_slist_free_all(headers_);
}
std::cout << "HttpClient析构函数: 清理完成" << std::endl;
}
void set_header(const std::string& header) {
std::cout << "set_header: 添加header: " << header << std::endl;
headers_ = curl_slist_append(headers_, header.c_str());
}
// 同步流式请求
void post_stream_sync(const std::string& url, const std::string& data, std::function<void(const std::string&)> callback) {
std::cout << "post_stream_sync: 开始发送流式请求到 " << url << std::endl;
if (!curl_) {
throw std::runtime_error("CURL not initialized");
}
// 重置CURL句柄
curl_easy_reset(curl_);
// 设置URL
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
// 设置POST请求
curl_easy_setopt(curl_, CURLOPT_POST, 1L);
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, data.c_str());
curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, data.size());
// 设置请求头
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_);
// 设置流式回调函数
std::string buffer;
auto data_pair = std::make_pair(&buffer, &callback);
std::cout << "post_stream_sync: 数据对创建完成" << std::endl;
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, stream_write_callback);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &data_pair);
// 设置超时和连接选项
curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 30L);
curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, 10L);
// 执行请求
std::cout << "post_stream_sync: 执行请求" << std::endl;
CURLcode res = curl_easy_perform(curl_);
std::cout << "post_stream_sync: 数据对清理完成" << std::endl;
if (res != CURLE_OK) {
std::cout << "post_stream_sync: CURL错误: " << curl_easy_strerror(res) << std::endl;
throw std::runtime_error(std::string("CURL error: ") + curl_easy_strerror(res));
}
std::cout << "post_stream_sync: 请求成功" << std::endl;
}
private:
CURL* curl_ = nullptr;
struct curl_slist* headers_ = nullptr;
static size_t stream_write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) {
// userdata指向一个std::pair<std::string*, std::function<void(const std::string&)>*>
auto* data_pair = static_cast<std::pair<std::string*, std::function<void(const std::string&)>*>*>(userdata);
if (!data_pair || !data_pair->first || !data_pair->second) {
std::cout << "stream_write_callback: 无效的用户数据" << std::endl;
return 0;
}
std::string& buffer = *(data_pair->first);
std::function<void(const std::string&)>& callback = *(data_pair->second);
size_t total_size = size * nmemb;
std::cout << "stream_write_callback: 收到 " << total_size << " 字节数据" << std::endl;
// 将数据追加到缓冲区
buffer.append(ptr, total_size);
// 打印原始数据(用于调试)
std::string raw_data(ptr, total_size);
std::cout << "stream_write_callback: 原始数据: " << raw_data << std::endl;
// 处理流式数据(按行处理Server-Sent Events格式)
size_t pos = 0;
size_t newline_pos;
while ((newline_pos = buffer.find('\n', pos)) != std::string::npos) {
std::string line = buffer.substr(pos, newline_pos - pos);
pos = newline_pos + 1;
std::cout << "stream_write_callback: 处理行: " << line << std::endl;
// 处理SSE格式的数据(以"data: "开头)
if (line.rfind("data: ", 0) == 0) {
std::string json_str = line.substr(6);
if (json_str == "[DONE]") {
std::cout << "stream_write_callback: 收到 [DONE]" << std::endl;
callback("[[流式传输完成]]");
continue;
}
// 调用回调函数处理JSON数据
std::cout << "stream_write_callback: 调用回调,数据长度: " << json_str.length() << std::endl;
callback(json_str);
} else if (!line.empty()) {
// 如果不是SSE格式,直接传递给回调(可能是错误消息)
std::cout << "stream_write_callback: 非SSE数据,直接传递: " << line << std::endl;
callback(line);
}
}
// 移除已处理的数据
if (pos > 0) {
buffer.erase(0, pos);
}
return total_size;
}
};
} // namespace pmc::net
#endif // PMC_HTTP_CLIENT_HPP
+193
View File
@@ -0,0 +1,193 @@
#ifndef HTTPSERVER_HPP
#define HTTPSERVER_HPP
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <boost/config.hpp>
#include <memory>
#include <string>
#include <functional>
#include <unordered_map>
#include <vector>
#include <iostream>
#include <thread>
#include <atomic>
namespace beast = boost::beast;
namespace http = beast::http;
namespace asio = boost::asio;
using tcp = boost::asio::ip::tcp;
namespace pmc {
namespace net {
/**
* @brief HTTP请求处理器类型定义
*
* 处理HTTP请求并返回响应
*/
using HttpRequestHandler = std::function<http::response<http::string_body>(
const http::request<http::string_body>&,
const std::unordered_map<std::string, std::string>&)>;
/**
* @brief 中间件处理器类型定义
*
* 用于处理请求链的中间件
*/
using MiddlewareHandler = std::function<bool(
const http::request<http::string_body>&,
http::response<http::string_body>&,
const std::unordered_map<std::string, std::string>&)>;
/**
* @brief HTTP服务器类(合并增强版)
*
* 基于boost::asio和boost::beast的异步HTTP服务器
* 结合了原始版本的易用性和修复版本的功能完整性
*/
class HttpServer {
public:
/**
* @brief 构造函数(监听所有接口)
* @param port 监听端口
* @param threads 工作线程数
*/
explicit HttpServer(unsigned short port, unsigned int threads = 1);
/**
* @brief 构造函数(指定监听地址)
* @param address 监听IP地址(如 "0.0.0.0" 或 "127.0.0.1")
* @param port 监听端口
* @param threads 工作线程数
*/
explicit HttpServer(const std::string& address, unsigned short port, unsigned int threads = 1);
/**
* @brief 析构函数
*/
~HttpServer();
/**
* @brief 启动服务器
*/
void start();
/**
* @brief 停止服务器
*/
void stop();
/**
* @brief 注册GET请求处理器
* @param path 请求路径
* @param handler 请求处理器
*/
void get(const std::string& path, HttpRequestHandler handler);
/**
* @brief 注册POST请求处理器
* @param path 请求路径
* @param handler 请求处理器
*/
void post(const std::string& path, HttpRequestHandler handler);
/**
* @brief 注册PUT请求处理器
* @param path 请求路径
* @param handler 请求处理器
*/
void put(const std::string& path, HttpRequestHandler handler);
/**
* @brief 注册DELETE请求处理器
* @param path 请求路径
* @param handler 请求处理器
*/
void del(const std::string& path, HttpRequestHandler handler);
/**
* @brief 注册中间件
* @param middleware 中间件处理器
*/
void use(MiddlewareHandler middleware);
/**
* @brief 设置静态文件目录
* @param path 静态文件目录路径
*/
void setStaticDirectory(const std::string& path);
/**
* @brief 获取服务器状态
* @return 服务器是否正在运行
*/
bool isRunning() const;
/**
* @brief 获取服务器端口
* @return 服务器监听端口
*/
unsigned short getPort() const;
/**
* @brief 获取服务器监听地址
* @return 服务器监听地址
*/
std::string getAddress() const;
/**
* @brief 运行服务器(阻塞调用)- 从原始版本继承
*/
void run();
/**
* @brief 显示服务器状态信息 - 从原始版本继承
*/
void showStatus() const;
private:
// 内部类声明
class Session;
class Listener;
// 服务器配置
std::string address_;
unsigned short port_;
unsigned int threads_;
std::atomic<bool> running_{false};
// Boost.Asio上下文
std::unique_ptr<asio::io_context> ioc_;
// 网络组件
std::shared_ptr<Listener> listener_;
std::vector<std::thread> worker_threads_;
// 路由表
std::unordered_map<std::string, HttpRequestHandler> get_handlers_;
std::unordered_map<std::string, HttpRequestHandler> post_handlers_;
std::unordered_map<std::string, HttpRequestHandler> put_handlers_;
std::unordered_map<std::string, HttpRequestHandler> delete_handlers_;
// 中间件链
std::vector<MiddlewareHandler> middlewares_;
// 静态文件目录
std::string static_directory_;
// 内部方法
std::unordered_map<std::string, std::string> parseQueryParams(const std::string& query);
// 请求处理
http::response<http::string_body> handleRequest(const http::request<http::string_body>& req);
};
} // namespace net
} // namespace pmc
#endif // HTTPSERVER_HPP
+63
View File
@@ -0,0 +1,63 @@
#ifndef PMC_NET_WEBSOCKET_CLIENT_HPP
#define PMC_NET_WEBSOCKET_CLIENT_HPP
#include "WebSocketClientBase.hpp"
namespace pmc {
namespace net {
/**
* @brief 基于 Boost.Beast 的异步 WebSocket 客户端(非 SSL 版本)
*/
class WebSocketClient : public WebSocketClientBase {
public:
/**
* @brief 构造函数
* @param host 服务器主机名或 IP 地址
* @param port 服务器端口号
* @param path WebSocket 路径(默认 "/")
*/
WebSocketClient(const std::string& host, const std::string& port, const std::string& path = "/");
/**
* @brief 析构函数
*/
~WebSocketClient();
// 实现 WebSocketClientBase 接口
void setMessageCallback(MessageCallback callback) override;
void setErrorCallback(ErrorCallback callback) override;
void setConnectCallback(ConnectCallback callback) override;
void setCloseCallback(CloseCallback callback) override;
bool connect(int timeout_ms = 5000) override;
void disconnect() override;
bool isConnected() const override;
bool send(const std::string& message) override;
bool sendBinary(const void* data, size_t size) override;
void setAutoReconnect(bool enable, int interval_ms = 3000) override;
void setHeartbeatInterval(int interval_ms = 30000) override;
std::string getHost() const override;
std::string getPort() const override;
std::string getPath() const override;
// SSL/TLS 相关(非 SSL 版本返回默认值)
bool isSecure() const override { return false; }
void setVerifyCertificate(bool verify) override { /* 忽略 */ }
void setCertificateFile(const std::string& cert_file) override { /* 忽略 */ }
void setPrivateKeyFile(const std::string& key_file) override { /* 忽略 */ }
void setCertificateAuthorityFile(const std::string& ca_file) override { /* 忽略 */ }
private:
// 前向声明实现类
class Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace net
} // namespace pmc
#endif // PMC_NET_WEBSOCKET_CLIENT_HPP
+61
View File
@@ -0,0 +1,61 @@
#ifndef PMC_NET_WEBSOCKET_CLIENT_BASE_HPP
#define PMC_NET_WEBSOCKET_CLIENT_BASE_HPP
#include <string>
#include <functional>
#include <memory>
namespace pmc {
namespace net {
/**
* @brief WebSocket 客户端基类
*
* 定义所有 WebSocket 客户端的公共接口
*/
class WebSocketClientBase {
public:
// 回调函数类型
using MessageCallback = std::function<void(const std::string&)>;
using ErrorCallback = std::function<void(const std::string&)>;
using ConnectCallback = std::function<void(bool)>;
using CloseCallback = std::function<void()>;
virtual ~WebSocketClientBase() = default;
// 回调函数设置
virtual void setMessageCallback(MessageCallback callback) = 0;
virtual void setErrorCallback(ErrorCallback callback) = 0;
virtual void setConnectCallback(ConnectCallback callback) = 0;
virtual void setCloseCallback(CloseCallback callback) = 0;
// 连接管理
virtual bool connect(int timeout_ms = 5000) = 0;
virtual void disconnect() = 0;
virtual bool isConnected() const = 0;
// 消息发送
virtual bool send(const std::string& message) = 0;
virtual bool sendBinary(const void* data, size_t size) = 0;
// 配置选项
virtual void setAutoReconnect(bool enable, int interval_ms = 3000) = 0;
virtual void setHeartbeatInterval(int interval_ms = 30000) = 0;
// 信息获取
virtual std::string getHost() const = 0;
virtual std::string getPort() const = 0;
virtual std::string getPath() const = 0;
// SSL/TLS 相关
virtual bool isSecure() const = 0;
virtual void setVerifyCertificate(bool verify) = 0;
virtual void setCertificateFile(const std::string& cert_file) = 0;
virtual void setPrivateKeyFile(const std::string& key_file) = 0;
virtual void setCertificateAuthorityFile(const std::string& ca_file) = 0;
};
} // namespace net
} // namespace pmc
#endif // PMC_NET_WEBSOCKET_CLIENT_BASE_HPP
+85
View File
@@ -0,0 +1,85 @@
#ifndef PMC_NET_WEBSOCKET_CLIENT_SSL_HPP
#define PMC_NET_WEBSOCKET_CLIENT_SSL_HPP
#include "WebSocketClientBase.hpp"
#include <string>
#include <memory>
namespace pmc {
namespace net {
/**
* @brief 基于 Boost.Beast 的异步 WebSocket 客户端(SSL/TLS 版本)
*
* 支持安全的 WebSocket 连接(wss://)
*/
class WebSocketClientSSL : public WebSocketClientBase {
public:
/**
* @brief 构造函数
* @param host 服务器主机名或 IP 地址
* @param port 服务器端口号(通常为 443)
* @param path WebSocket 路径(默认 "/")
*/
WebSocketClientSSL(const std::string& host, const std::string& port, const std::string& path = "/");
/**
* @brief 析构函数
*/
~WebSocketClientSSL();
// 实现 WebSocketClientBase 接口
void setMessageCallback(MessageCallback callback) override;
void setErrorCallback(ErrorCallback callback) override;
void setConnectCallback(ConnectCallback callback) override;
void setCloseCallback(CloseCallback callback) override;
bool connect(int timeout_ms = 5000) override;
void disconnect() override;
bool isConnected() const override;
bool send(const std::string& message) override;
bool sendBinary(const void* data, size_t size) override;
void setAutoReconnect(bool enable, int interval_ms = 3000) override;
void setHeartbeatInterval(int interval_ms = 30000) override;
std::string getHost() const override;
std::string getPort() const override;
std::string getPath() const override;
// SSL/TLS 相关方法
bool isSecure() const override { return true; }
void setVerifyCertificate(bool verify) override;
void setCertificateFile(const std::string& cert_file) override;
void setPrivateKeyFile(const std::string& key_file) override;
void setCertificateAuthorityFile(const std::string& ca_file) override;
/**
* @brief 设置 SSL 上下文选项
* @param options SSL 上下文选项
*/
void setSSLContextOptions(long options);
/**
* @brief 设置密码列表
* @param ciphers 密码列表字符串
*/
void setCiphers(const std::string& ciphers);
/**
* @brief 设置 SNI 主机名
* @param sni_hostname SNI 主机名
*/
void setSNIHostname(const std::string& sni_hostname);
private:
// 前向声明实现类
class Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace net
} // namespace pmc
#endif // PMC_NET_WEBSOCKET_CLIENT_SSL_HPP
+176
View File
@@ -0,0 +1,176 @@
// WebSocketServer.hpp - WebSocket服务器头文件
// 基于Boost.Beast和Boost.Asio
#ifndef PMC_NET_WEBSOCKETSERVER_HPP
#define PMC_NET_WEBSOCKETSERVER_HPP
#include <functional>
#include <string>
#include <memory>
#include <unordered_map>
#include <mutex>
#include <atomic>
#include <vector>
#include <thread>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
namespace pmc::net {
/**
* @brief WebSocket连接处理器类型定义
*
* 处理WebSocket连接建立后的消息收发
*/
using WebSocketMessageHandler = std::function<void(
const std::string& clientId,
const std::string& message,
std::function<void(const std::string&)> sendCallback)>;
/**
* @brief WebSocket连接回调类型定义
*/
using WebSocketConnectHandler = std::function<void(
const std::string& clientId,
const std::string& remoteAddress)>;
/**
* @brief WebSocket断开连接回调类型定义
*/
using WebSocketDisconnectHandler = std::function<void(
const std::string& clientId)>;
/**
* @brief WebSocket服务器类
*
* 基于Boost.Beast和Boost.Asio实现的WebSocket服务器
*/
class WebSocketServer {
public:
/**
* @brief 构造函数
* @param port 监听端口
* @param threads 线程数(默认2)
*/
WebSocketServer(unsigned short port, unsigned int threads = 2);
/**
* @brief 析构函数
*/
~WebSocketServer();
/**
* @brief 启动服务器
*/
void start();
/**
* @brief 停止服务器
*/
void stop();
/**
* @brief 注册消息处理器
* @param path 路径(用于路由)
* @param handler 消息处理器
*/
void onMessage(const std::string& path, WebSocketMessageHandler handler);
/**
* @brief 注册连接处理器
* @param handler 连接处理器
*/
void onConnect(WebSocketConnectHandler handler);
/**
* @brief 注册断开连接处理器
* @param handler 断开连接处理器
*/
void onDisconnect(WebSocketDisconnectHandler handler);
/**
* @brief 向指定客户端发送消息
* @param clientId 客户端ID
* @param message 消息内容
* @return 是否发送成功
*/
bool sendToClient(const std::string& clientId, const std::string& message);
/**
* @brief 向所有客户端广播消息
* @param message 消息内容
* @return 成功发送的客户端数量
*/
size_t broadcast(const std::string& message);
/**
* @brief 断开指定客户端连接
* @param clientId 客户端ID
*/
void disconnectClient(const std::string& clientId);
/**
* @brief 获取当前连接的客户端数量
* @return 客户端数量
*/
size_t getClientCount() const;
/**
* @brief 获取服务器是否正在运行
* @return 运行状态
*/
bool isRunning() const { return running_; }
/**
* @brief 获取服务器监听端口
* @return 端口号
*/
unsigned short getPort() const { return port_; }
private:
// 内部Session类
class Session;
// 内部Listener类
class Listener;
// 内部实现结构体
struct Impl {
boost::asio::io_context ioc_;
std::shared_ptr<Listener> listener_;
std::vector<std::thread> threads_;
};
// 服务器配置
unsigned short port_;
unsigned int threads_;
std::atomic<bool> running_{false};
// 内部实现
std::unique_ptr<Impl> impl_;
// 生成客户端ID
std::string generateClientId() const;
// 添加客户端
void addClient(const std::string& clientId, std::shared_ptr<Session> session);
// 移除客户端
void removeClient(const std::string& clientId);
// 获取客户端Session
std::shared_ptr<Session> getClient(const std::string& clientId);
// 回调函数
std::unordered_map<std::string, WebSocketMessageHandler> messageHandlers_;
WebSocketConnectHandler connectHandler_;
WebSocketDisconnectHandler disconnectHandler_;
// 客户端管理
mutable std::mutex clientsMutex_;
std::unordered_map<std::string, std::shared_ptr<Session>> clients_;
};
} // namespace pmc::net
#endif // PMC_NET_WEBSOCKETSERVER_HPP
+38
View File
@@ -0,0 +1,38 @@
/*
* 利用条件变量进行等待
*
* FIXME: deepseek 认为有一个设计缺陷:它只能用于单次等待-唤醒的场景。
* FIXME: 可能导致永久堵塞,应使用带超时的等待
* FIXME: 使用标准库的 future/promise
*/
#include <thread>
#include <atomic> /* 也许将字段flag创建为原子变量 */
#include <mutex>
#include <condition_variable>
namespace qing {
class Cv_wait { /* 用于等待的条件变量类,对同一个对象进行操作能够阻塞当前线程,或者唤醒被阻塞的线程 */
private:
std::mutex mtx{}; /* 用于控制条件变量的锁 */
std::condition_variable cv{};
bool flag = false; /* 用于解锁条件变量的标志 */
public:
inline void Wait() /* 线程进入等待 */
{
auto lock = std::unique_lock<std::mutex>(mtx);
flag = false;
cv.wait(lock, [this] { return flag; });
}
inline void WakeCv() /* 唤醒线程 */
{
{
std::lock_guard<std::mutex> lcok(mtx);
flag = true;
}
cv.notify_all();
}
};
}
+75
View File
@@ -0,0 +1,75 @@
/*
* 有限状态机(Finite State Machine,FSM)
*/
#pragma once
#include <mutex> /*上锁 使得有限状态机线程安全*/
#include <condition_variable> /*条件变量,使得子类能够等待状态的设置*/
namespace qing {
/*
* FiniteStateMachine有限状态机
*
* 每个数字代表一个状态 使用static_cast<>()之类的函数去赋值
* FIXME: 之前用过一种使用回调函数处理的方式
*/
class Fsm {
public:
/* 状态枚举类 */
enum class Stat: int {
SHUT = -1, /* 关闭 */
STOP = 0, /* 静止 */
START = 1, /* 启动 */
RUNNING = 2 /* 运行 */
/* ... */
};/*Stat*/
/* 以STOP为初始状态 */
Fsm() { set(Stat::STOP); }
enum Stat check() { /* 检查当前的状态 */
std::lock_guard<std::mutex> lk(mtx);
return stat;
} /*check*/
/*
* 在不上锁的情况下设置一个状态
* 这里不能使用锁,因为我们还要在临界区里唤醒条件变量
*/
void set_without_lock(Stat stat) { this->stat = stat; }
/* 带锁的状态设置 但是该方法不唤醒条件变量 */
void set_without_notify(Stat stat) {
std::lock_guard<std::mutex> lk(mtx);
this->stat = stat;
}
/* 带锁和条件唤醒的状态设置 */
void set(Stat stat) {
{
std::lock_guard<std::mutex> lk(mtx);
set_without_lock(stat);
ready = true;
}
cv.notify_all();
}
//protected:
void suspend() { /* 阻塞线程,等待事件唤醒 */
std::unique_lock<std::mutex> lk(mtx);
ready = false;
cv.wait(lk, [this] { return ready; });
}/*suspend */
private:
std::mutex mtx; /* 线程安全 */
bool ready = false; /* 用于条件变量的阻塞唤醒 */
std::condition_variable cv; /* 条件变量 阻塞/唤醒线程 */
enum Stat stat; /* 状态机的状态变量 */
};//Fsm
}//qing
+9
View File
@@ -0,0 +1,9 @@
#pragma once
namespace qing{
class ITask { /* 单个任务通用接口 */
public:
virtual void start() = 0; /* 任务开始 */
virtual void stop() = 0; /* 任务结束 */
virtual bool isRunning() = 0; /* 是否在跑 */
};
}
+183
View File
@@ -0,0 +1,183 @@
/* 线程
*
*
* 包含一个简易的多线程控制机制,需要共享的变量和函数声明为成员。
*
* 线程主函数包括了流程控制,要使用需要实现其中的各个事件方法
*
* 20250621: 从派生改为持有
*
* NOTE: 在对象构造完之后应该激活
*/
#pragma once
#include <thread>
#include <memory> /* 内存管理 */
#include <chrono> /* 用于线程休息 */
#include <iostream>
#include <stdexcept>
#include <functional>
#include "th/Fsm.hpp" /* 有限状态机 */
namespace qing {
class Thread;
//using f_t = void(*)(Thread&);
using f_t = std::function<void(Thread&)>;
class Thread {
public:
/* 作为可变的函数,持有回调可能比继承更好 */
Thread(f_t stop_callback, f_t wake_callback, f_t loop_callback, f_t clear_callback);
/*
* 删除复制构造函数。
*
* 因为持有线程类,不要轻举妄动。
*/
Thread(const Thread&) = delete;
/*
* 虚析构函数
*
* FIXME: 具体有什么用,忘了
*/
virtual ~Thread() {}
/*-----------------------------------*/
/*-----------------------------------*/
/*
* 检查状态的方法
*
* NOTE: 做成虚函数,万一子类有特别要求呢
*
* FIXME: 将检查器和变异器做成一个简单的回调函数
*/
virtual Fsm::Stat check() {
return fsm.check();
}
/*-----------------------------------*/
/*-----------------------------------*/
/* 让线程能够阻塞直到新的状态被赋予的方法 */
inline void suspend() {
fsm.suspend();
}
/*-----------------------------------*/
/*-----------------------------------*/
/* 状态改变的方法 - 变异器 */
/*
* 进入唤醒状态
*
* 唤醒函数不可以进行该类中的锁操作,否则会造成死锁
*/
virtual void wake() {
//{
//std::lock_guard<std::mutex> lk(mtx_th);
fsm.set(Fsm::Stat::START);
//ready = true;
//}
//cv.notify_all();
} /* wake */
/* 进入静止状态
*
* 上锁、唤醒阻塞的线程
*/
virtual void stop() {
{
std::lock_guard<std::mutex> lk(mtx_th);
fsm.set(Fsm::Stat::STOP);
ready = true;
}
/* 可能会出现多个线程在等待的情况 */
cv.notify_all();
}/* stop */
/* 进入关闭状态 */
virtual void shut() {
{
std::lock_guard<std::mutex> lk(mtx_th);
fsm.set(Fsm::Stat::SHUT);
ready = true;
}
cv.notify_all();
} /*shut*/
/* 进入运行状态 */
virtual void run() {
{
std::lock_guard<std::mutex> lk(mtx_th);
fsm.set(Fsm::Stat::RUNNING);
ready = true;
}
cv.notify_all();
}/*run*/
/*-----------------------------------*/
/*-----------------------------------*/
/*
* 激活线程,申请线程对象
*
* 该函数不能放在构造函数内,因为需要确保对象已经被构造了
*/
void Activate() {
/* 防止重复初始化
* FIXME: 抛出异常?感觉也不太合适 */
if (th) return;
/* 申请线程资源 */
th = std::make_unique<std::thread>(&Thread::main, this);
}
/*
* 尝试唤醒线程并等待它开始 据说该函数有问题可能导致异常
*/
void WaitStart() {
std::unique_lock<std::mutex> lk(mtx_th); /* 不进入临界区可能会造成死等,在唤醒过程非常短暂的情况下 */
wake(); /* 这就是为什么wake()不可以获取临界区,否则会造成死锁 */
ready = false; /* 这个临界区结束后,WakeEvent()中的run()会唤醒线程的堵塞 */
cv.wait(lk, [this] { return ready; });
}
/* 等待线程关闭 */
void WaitClose() {
shut();
if (th->joinable())
th->join();
}
/*-----------------------------------*/
/* 线程主函数 */
void main();
private:
qing::Fsm fsm{}; /* 持有状态机类型 */
std::unique_ptr<std::thread> th; /* 持有线程类型 */
std::mutex mtx_th; /* 持有锁类型 */
/*
* 这里持有一个条件变量,用于阻塞以等待线程的关闭
*
* 注意状态机中的cv是另一个变量
*/
std::condition_variable cv;
/* 辅助条件变量的逻辑型 */
bool ready;
/* 事件回调 */
std::unique_ptr<f_t> StopEvent;
std::unique_ptr<f_t> WakeEvent;
std::unique_ptr<f_t> LoopEvent;
std::unique_ptr<f_t> ClearEvent;
};
}
+93
View File
@@ -0,0 +1,93 @@
/*
* 目前是Message Queue 的发送端(不涉及创建消息队列)
*
* FIXME: 集成发送、接收和创建
*/
#pragma once
#include <iostream>
#include <cstring>
#include <memory>
#include <boost/interprocess/ipc/message_queue.hpp>
namespace qing {
namespace bip = boost::interprocess;
using sf_t = std::function<void(const char*)>; /* success */
using ef_t = std::function<void(void)>; /* error */
class Mq {
public:
enum {CREATOR=0, USER=1};
Mq(const std::string& name, int msglen, size_t msgcnt, int mode = USER) {
this->name = std::string("/") + name;
this->msglen = msglen;
this->msgcnt = msgcnt;
this->mode = mode;
switch (mode) {
case USER:
mqp = std::make_unique<bip::message_queue>(bip::open_only, this->name.c_str());
break;
case CREATOR: /* 由创建者创建消息队列 */
bip::message_queue::remove(this->name.c_str());
mqp = std::make_unique<bip::message_queue>(
bip::create_only, this->name.c_str(),
msgcnt, msglen * sizeof(char)
);
break;
}
}
/* 创建者负责清理 */
~Mq() {
if (mode == CREATOR)
bip::message_queue::remove(name.c_str());
}
/* */
Mq(const Mq&) = delete;
Mq& operator=(const Mq&) = delete;
/* 发送消息 */
void send(std::string &msg)
{
if (msg.size() + 1 > msglen) /* over the len */
throw std::length_error("Message queue Send Length over");
mqp->send(msg.c_str(), msg.size() + 1, 0); /* include \0 */
}
/* 尝试接收消息 */
void recv(sf_t success, ef_t notyet) {
auto data = std::make_unique<char[]>(msglen); /* 接收缓冲区 */
unsigned int priority; /* 优先级 */
bip::message_queue::size_type recvd_size;
try { /* try_receive()返回bool类型 */
auto r = mqp->try_receive(
data.get(), msglen*sizeof(char),
recvd_size, priority
);
if (r) /* 成功接收到数据 */
success(data.get());
else /* 没有收到数据 */
notyet();
}
catch (std::exception &exp) {
std::cerr << "exception type: " << typeid(exp).name() << std::endl;
std::cerr << "exception message: " << exp.what() << std::endl;
}
}
private:
/* */
std::unique_ptr<bip::message_queue> mqp;
std::string name;
size_t msglen=0, msgcnt=0;
int mode;
};
}
+155
View File
@@ -0,0 +1,155 @@
#pragma once
#include <string>
#include <iostream>
#include <unistd.h>
#include <fcntl.h> /* open() */
#include <sys/stat.h> /* mkfifo() */
#include <sys/select.h>
namespace qing {
class Pipe{ /* 20260309(qing): 该类不会抛出异常 */
public:
static const int CREATOR = 1;
static const int USER =2;
static const ssize_t PIPE_MAX_LEN = 2048;
Pipe(const std::string& path, int mode)
: fd(-1), isCreator(mode == CREATOR) {
std::string real_path = "/tmp/pst-";
real_path += path;
fifo_path = real_path;
if (isCreator && mkfifo(fifo_path.c_str(), 0666) == -1 && errno != EEXIST) /* wr_wr_wr */
perror("mkfifo"); /* 文件已存在是正常情况,其他错误才需要处理 */
}
~Pipe() {
/* 如果打开了文件,则关闭文件 */
if (fd != -1) close(fd);
/**********************/
/* 如果管道由该对象创建,就要负责清理 */
if (isCreator) unlink(fifo_path.c_str()); /* 创建者负责清理 */
}
Pipe(const Pipe&) = delete;
Pipe& operator=(const Pipe&) = delete;
/* 这里才是打开文件的指令 - 以读模式打开,可以非阻塞 */
bool openForRead(bool nonblock = false) {
/* 设置是否以非阻塞方式打开文件 */
int flags = O_RDONLY;
if (nonblock) {
flags |= O_NONBLOCK;
}
fd = open(fifo_path.c_str(), flags);
if (fd == -1) {
perror("open for read");
return false;
}
else {
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
if (fd > max_fd) max_fd = fd;
return true;
}
}
/* 以写模式打开文件 */
bool openForWrite(/*bool nonblock = falsei*/) {
/* 写入管道是不需要阻塞/非阻塞的? */
int flags = O_WRONLY;
//if (nonblock) {
// flags |= O_NONBLOCK;
//}
fd = open(fifo_path.c_str(), flags);
if (fd == -1) {
perror("open for write");
return false;
}
else {
FD_ZERO(&write_fds);
FD_SET(fd, &write_fds);
if (fd > max_fd) max_fd = fd;
return true;
}
}
/* 写入管道,如果失败返回-1。成功返回写入的字符数。 */
ssize_t writePipe(const std::string &data) {
if (fd == -1) {
std::cerr << "Pipe not opened for writing" << std::endl;
return -1;
}
else {
return write(fd, data.c_str(), data.size());
}
}
/* 将探测与读取解耦,使用者可以自行决定要不要阻塞 */
bool waitForRead(int timeout_sec = 0, int timeout_usec = 0) {
/* 如果文件没打开,等待是失败的 */
if (fd == -1) return false;
fd_set temp_fds = read_fds;
struct timeval timeout = {timeout_sec, timeout_usec};
int ret = select(max_fd + 1, &temp_fds, nullptr, nullptr, &timeout);
if (ret == -1) {
perror("select");
return false;
}
else {
return ret > 0 && FD_ISSET(fd, &temp_fds);
}
}
/* 读取数据到字符串,返回实际读取的字节数 */
ssize_t readPipe(std::string *output) {
if (fd == -1) {
std::cerr << "Pipe not opened for reading" << std::endl;
return -1;
}
else
{
char buffer[PIPE_MAX_LEN];
ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
if (n >0) {
buffer[n] = '\0';
*output = buffer;
}
else if (n == 0) {
/* 管道写入端关闭 */
*output = "";
}
else {
perror("read");
}
return n;
}
}
private:
std::string fifo_path;
int fd;
bool isCreator;
fd_set read_fds;
fd_set write_fds;
fd_set except_fds;
int max_fd;
};
}