BCU 工程消息队列使用梳理
工程中仅使用 1 条 POSIX 消息队列
/calibration_queue,用于将 HMI 标定指令和 EMS 控制指令跨进程传递给 ASW(Simulink 模型进程)。
一、全局架构
┌──────────┐ RS485 ┌──────────┐ mq_send() ┌──────────────┐ mq_receive() ┌──────────┐
│ HMI │──────────►│ rtu.c │─────────────►│ /calibration │──────────────►│ ASW.cpp │
│ (触摸屏) │ │ (生产者) │ │ _queue │ │ (消费者) │
└──────────┘ └──────────┘ └──────────────┘ └──────────┘
│ │
│ mq_send() ▼
┌─────┴────────┐ Simulink 输入结构体
│iec104_adapter│ (SOXInputBus / SSM_inputSig)
│ (生产者2) │
└──────────────┘
▲
IEC 60870-5-104
│
EMS 调度
两个生产者,一个消费者,一个消息队列。
二、队列定义
名称与属性(global.h:38-69)
#define CALIBRATION_MQ_NAME "/calibration_queue"
#define MQ_SIZE 20 // 队列最大消息数
typedef struct {
unsigned int point_id; // 标定点位 ID
int64_t value; // 标定值
uint16_t flag; // 标定标志
float factor; // 系数
char description[128]; // 描述
char uint[32]; // 单位
} calibration_notify_t;
全局句柄(global.c:63)
mqd_t calibration_mq_fd = -1; // 初始无效,mq_init() 后赋值
三、生命周期管理(rtu.c)
3.1 创建 — mq_init()(rtu.c:3540-3558)
void mq_init(void) {
struct mq_attr attr;
attr.mq_flags = O_NONBLOCK; // 非阻塞发送
attr.mq_maxmsg = MQ_SIZE; // 最多 20 条
attr.mq_msgsize = sizeof(calibration_notify_t); // 每条固定大小
attr.mq_curmsgs = 0;
calibration_mq_fd = mq_open(CALIBRATION_MQ_NAME,
O_CREAT | O_RDWR | O_NONBLOCK, 0666, &attr);
}
由 rtu.c 在 main() 中调用。因为 rtu 进程启动最早,负责创建队列。
3.2 销毁 — 进程退出时(rtu.c:3942-3945)
if (calibration_mq_fd != -1) {
mq_close(calibration_mq_fd); // 关闭描述符
mq_unlink(CALIBRATION_MQ_NAME); // 从内核删除队列
}
四、生产者 1 — rtu.c(HMI 标定触发)
位于 handle_modbus_write() 中(rtu.c:2796-2815)。当 HMI 通过串口2(Modbus RTU)写入标定点位时触发。
触发条件:HMI 修改了点位值且 point_id 在标定范围内(如 20660~20674,10590 等)。
if (calibration_mq_fd != -1) {
calibration_notify_t notify = {0};
notify.point_id = pt->point_id; // 标定点位 ID
notify.value = pt->current_value; // HMI 设置的新值
notify.flag = 1;
notify.factor = pt->factor;
strncpy(notify.description, pt->description, ...);
strncpy(notify.uint, pt->uint, ...);
mq_send(calibration_mq_fd, (const char*)¬ify, sizeof(notify), 0);
}
发送模式:非阻塞(O_NONBLOCK),队列满时立即返回失败。
五、生产者 2 — iec104_adapter.c(EMS 调度指令)
位于 IEC 60870-5-104 从站回调中(iec104_adapter.c:421-433)。当 EMS 主站通过 104 协议下发充放电指令时触发。
extern mqd_t calibration_mq_fd; // 引用 rtu.c 中的全局句柄
// 收到 EMS 的充放电指令(IOA=10590)
if (calibration_mq_fd != (mqd_t)-1) {
calibration_notify_t notify = {0};
notify.point_id = 10590;
notify.value = value; // EMS 指令值
notify.flag = 1;
notify.factor = 1.0f;
strcpy(notify.description, "EMS充放电指令");
mq_send(calibration_mq_fd, (const char*)¬ify, sizeof(notify), 0);
}
两个生产者共用同一个
calibration_mq_fd句柄和同一个队列,通过point_id区分来源。
六、消费者 — ASW.cpp(Simulink 模型进程)
6.1 打开队列(ASW.cpp:200)
// 只读 + 阻塞模式(等待消息时挂起线程,不占 CPU)
g_calib_mq_fd = mq_open(CALIBRATION_MQ_NAME, O_RDONLY);
注意:ASW 是只读打开(
O_RDONLY),不创建队列(无O_CREAT)。
这意味着 rtu 必须先启动创建队列,ASW 后启动才能成功打开。
6.2 阻塞接收 + 分拣(ASW.cpp:198-306)
static void* calibration_receiver_thread(void* arg) {
g_calib_mq_fd = mq_open(CALIBRATION_MQ_NAME, O_RDONLY);
calibration_notify_t msg;
while (g_calib_running.load()) {
ssize_t n = mq_receive(g_calib_mq_fd, (char*)&msg, sizeof(msg), nullptr);
// 阻塞等待,有消息才返回
switch (msg.point_id) {
// ── SOX 标定 ──
case 20668: SOXInputBus_str.SOCCalValue = msg.value;
SOXInputBus_str.SOCCalFlg = 1; break;
case 20669: SOXInputBus_str.SOHCalValue = msg.value;
SOXInputBus_str.SOHCalFlg = 1; break;
case 20671: /* 累计充电安时标定 */ break;
case 20672: /* 累计放电安时标定 */ break;
case 20673: /* 累计充电能量标定 */ break;
case 20674: /* 累计放电能量标定 */ break;
// ── SSM 标定(继电器强制控制)──
case 20660: /* 强制负极吸合 */ break;
case 20661: /* 强制正极吸合 */ break;
case 20662: /* 强制预充吸合 */ break;
case 20663: /* 强制断路器吸合 */ break;
case 20664: /* 系统模式控制 */ break;
case 20665: /* 一键合闸分闸 */ break;
// ── EMS 控制指令 ──
case 10590: SSM_inputSig_str.EMSCmd = msg.value;
SSM_inputSig_str.EMSCmdFlg = 1; break;
default: /* 未处理的 point_id */ break;
}
}
mq_close(g_calib_mq_fd);
}
接收模式:阻塞(默认),无消息时线程挂起,不消耗 CPU。
七、两种打开模式对比
| rtu.c(生产者) | ASW.cpp(消费者) | |
|---|---|---|
| 打开方式 | O_CREAT | O_RDWR | O_NONBLOCK | O_RDONLY |
| 发送/接收 | mq_send 非阻塞 | mq_receive 阻塞 |
| 队列满/空 | 满时立即返回 -1 | 空时挂起线程,有消息才唤醒 |
| 角色 | 创建者 + 写入者 | 只读消费者 |
八、完整生命周期
rtu 进程启动
│
├─ mq_init()
│ └─ mq_open(O_CREAT) ← 创建 /calibration_queue
│
├─ 运行时:
│ │
│ ├─ HMI 写入标定点位 → mq_send()
│ │
│ └─ [iec104_adapter] EMS 指令 → mq_send()
│
└─ 退出时:
└─ mq_close() + mq_unlink() ← 删除队列
────────────────────────────────────────────
ASW 进程启动(rtu 之后)
│
├─ pthread_create(calibration_receiver_thread)
│ └─ mq_open(O_RDONLY) ← 打开已有队列
│ └─ while: mq_receive() (阻塞) → 分拣赋值
│
└─ 退出时:
└─ mq_close()
九、涉及的 API 汇总
| API | 调用位置 | 功能 |
|---|---|---|
mq_open | rtu.c (创建) / ASW.cpp (打开) | 创建或打开消息队列 |
mq_send | rtu.c + iec104_adapter.c | 发送标定通知 |
mq_receive | ASW.cpp | 阻塞接收标定通知 |
mq_close | rtu.c + ASW.cpp | 关闭队列描述符 |
mq_unlink | rtu.c | 从内核删除队列 |
920

被折叠的 条评论
为什么被折叠?



