Files
STM32F4-Base/IEC60870-Slave/.build/command-comments/IEC104_Command.c.before
T
STF cd42b4d61d
CI / L0 静态门禁 (pre-commit) (push) Failing after 2m51s
CI / L0.5 主机侧单测 (MSVC) (push) Successful in 18s
添加遥控业务代码
2026-10-30 20:03:19 -07:00

928 lines
43 KiB
Plaintext

/*
* IEC104单点/双点遥控(45/46)。
* 一个事务槽保存工作事件及必要回复,独立短应答使用固定池;全局序号决定提交先后。
* 应用显式Accept启动动作,Report_Result结束设备期限,Complete结束返回信息阶段。
* FINISHED和发送收尾分别完成后才释放事务;取消不回滚设备,迟到事件不能复用新事务。
*/
#include "IEC104_Command.h"
#include "IEC104_TcpServer_Config.h"
#include "IEC104_Net.h"
#include <string.h>
#ifndef IEC104_COMMAND_CAPACITY
#define IEC104_COMMAND_CAPACITY IEC104_TCP_SERVER_CAPACITY
#endif
#ifndef IEC104_COMMAND_REPLY_CAPACITY
#define IEC104_COMMAND_REPLY_CAPACITY 4U
#endif
_Static_assert(IEC104_COMMAND_CAPACITY > 0U, "Command needs instance storage");
_Static_assert(IEC104_COMMAND_REPLY_CAPACITY > 0U &&
IEC104_COMMAND_REPLY_CAPACITY < UINT32_MAX / 2U, "Command needs bounded reply storage");
typedef struct {
IEC104_SlaveEventIdentity_t identity;
uint8_t request_bytes[10], reply_bytes[10];
IEC104_Station_t *station;
uint64_t transaction_id, receive_serial, accepted_ms, timer_start_ms;
uint64_t event_serial, accepted_event_serial, select_event_serial, execute_event_serial;
uint64_t confirm_serial, term_serial, reply_serial;
uint8_t phase, event_type, finish_reason, result, confirm_cot, reply_cot;
bool occupied, event_taken, ever_work_delivered, finish_pending;
bool select_delivered, execute_delivered, selected;
bool confirm_negative, confirm_try, send_waiting, return_waiting;
bool confirm_submitted, completed, term_submitted, select_effect, execute_effect;
} CommandTransaction;
typedef struct {
IEC104_SlaveEventIdentity_t identity;
uint8_t bytes[10];
uint64_t serial;
uint8_t cot;
bool occupied, send_waiting;
} CommandReply;
struct IEC104_Command {
IEC104_Slave_t *slave;
IEC104_SlaveEventIdentity_t session;
IEC104_CommandConfig_t config;
IEC104_CommandStatus_t status;
CommandTransaction transaction;
CommandReply replies[IEC104_COMMAND_REPLY_CAPACITY];
uint64_t serial, next_transaction, next_event, next_reply, last_generation;
bool in_use, in_action;
};
static IEC104_Command_t command_pool[IEC104_COMMAND_CAPACITY];
static uint64_t next_instance;
/* 先验证静态池归属,再解引用。 */
static bool Owned(const IEC104_Command_t *self)
{
unsigned i;
for (i = 0U; i < IEC104_COMMAND_CAPACITY; ++i) {
if (self == &command_pool[i]) { return self->in_use; }
}
return false;
}
static uint32_t Read_IOA(const uint8_t *bytes)
{
return (uint32_t)bytes[6] | ((uint32_t)bytes[7] << 8) | ((uint32_t)bytes[8] << 16);
}
static uint16_t Read_CA(const uint8_t *bytes)
{
return (uint16_t)((uint16_t)bytes[4] | ((uint16_t)bytes[5] << 8));
}
/* 所有事件及动作使用模块保留的请求重建,不能信任回传event中的可修改request。 */
static void Copy_Request(const CommandTransaction *task, IEC104_CommandRequest_t *request)
{
memset(request, 0, sizeof(*request));
request->generation = task->identity.generation;
request->receive_serial = task->receive_serial;
request->station_id = IEC104_Station_Get_Id(task->station);
request->common_address = Read_CA(task->request_bytes);
request->originator_address = task->request_bytes[3];
request->type_id = task->request_bytes[0];
request->ioa = Read_IOA(task->request_bytes);
request->is_select = (task->request_bytes[9] & 0x80U) != 0U;
request->selected = task->selected;
request->state = task->request_bytes[9] & (request->type_id == 45U ? 1U : 3U);
request->qualifier = (task->request_bytes[9] >> 2) & 31U;
request->accepted_ms = task->accepted_ms;
}
static unsigned Pending_Replies(const IEC104_Command_t *self)
{
const CommandTransaction *task = &self->transaction;
unsigned i, count = (task->confirm_serial != 0U ? 1U : 0U) +
(task->term_serial != 0U ? 1U : 0U) + (task->reply_serial != 0U ? 1U : 0U);
for (i = 0U; i < IEC104_COMMAND_REPLY_CAPACITY; ++i) {
if (self->replies[i].occupied) { ++count; }
}
return count;
}
/* FINISHED消费与本事务回复交接是两个独立条件;独立短应答不借用事务内存。 */
static void Reap(IEC104_Command_t *self)
{
CommandTransaction *task = &self->transaction;
if (!task->occupied || task->phase != IEC104_COMMAND_PHASE_ENDED || task->finish_pending ||
task->confirm_serial != 0U || task->term_serial != 0U || task->reply_serial != 0U) { return; }
(void)IEC104_Slave_Release_Station(self->slave, task->station);
memset(task, 0, sizeof(*task));
}
/* 第一次终结冻结原因并移除未读工作事件。后续STOP/Cancel只可清回复,不能重发FINISHED。 */
static void Finish(IEC104_Command_t *self, IEC104_CommandFinishReason_t reason)
{
CommandTransaction *task = &self->transaction;
if (!task->occupied || task->phase == IEC104_COMMAND_PHASE_ENDED) { Reap(self); return; }
task->phase = IEC104_COMMAND_PHASE_ENDED;
task->finish_reason = (uint8_t)reason;
task->event_type = IEC104_COMMAND_FINISHED;
task->event_taken = false;
task->finish_pending = task->ever_work_delivered;
task->return_waiting = false;
/* 创建事务时为最多两个工作事件和一个FINISHED检查了序号余量。 */
if (task->finish_pending) { task->event_serial = ++self->next_event; }
switch (reason) {
case IEC104_COMMAND_FINISH_COMPLETED: ++self->status.completed; break;
case IEC104_COMMAND_FINISH_EXECUTION_FAILED: ++self->status.execution_failed; break;
case IEC104_COMMAND_FINISH_ACTION_FAILED: ++self->status.action_failed; break;
case IEC104_COMMAND_FINISH_MISMATCH: ++self->status.mismatches; break;
case IEC104_COMMAND_FINISH_DEACTIVATED: ++self->status.deactivated; break;
case IEC104_COMMAND_FINISH_SELECT_TIMEOUT: ++self->status.select_timeouts; break;
case IEC104_COMMAND_FINISH_DECISION_TIMEOUT: ++self->status.decision_timeouts; break;
case IEC104_COMMAND_FINISH_EXECUTE_TIMEOUT: ++self->status.execute_timeouts; break;
case IEC104_COMMAND_FINISH_CANCELLED:
case IEC104_COMMAND_FINISH_STOPPED:
case IEC104_COMMAND_FINISH_DISCONNECTED:
case IEC104_COMMAND_FINISH_CONNECTION_CHANGED:
case IEC104_COMMAND_FINISH_SEND_ERROR:
case IEC104_COMMAND_FINISH_SERVICE_ERROR: ++self->status.cancelled; break;
default: break;
}
Reap(self);
}
static void Cancel_All(IEC104_Command_t *self, IEC104_CommandFinishReason_t reason)
{
CommandTransaction *task = &self->transaction;
task->confirm_serial = task->term_serial = task->reply_serial = 0U;
task->send_waiting = false;
memset(self->replies, 0, sizeof(self->replies));
Finish(self, reason);
}
/* 动作/提交门禁,不用于消费Handle_Event本身,否则FIFO中尚有通知时无法读空。 */
static IEC104_SlaveRequestState_t Check_Session(IEC104_Command_t *self)
{
IEC104_SlaveRequestState_t state = IEC104_Slave_Check_Request(self->slave, &self->session);
self->status.events_pending = state == IEC104_SLAVE_REQUEST_WAIT_EVENTS;
if (state == IEC104_SLAVE_REQUEST_ERROR) {
Cancel_All(self, IEC104_COMMAND_FINISH_SERVICE_ERROR);
self->status.active = false;
} else if (state != IEC104_SLAVE_REQUEST_WAIT_EVENTS &&
(!self->status.active || state != IEC104_SLAVE_REQUEST_READY)) {
Cancel_All(self, IEC104_COMMAND_FINISH_CONNECTION_CHANGED);
self->status.active = false;
state = IEC104_SLAVE_REQUEST_STALE;
}
return state;
}
/* 期限关闭返回0;动作回调中事件已消费,不再计算决定期限。 */
static uint32_t Timer_Limit(const IEC104_Command_t *self)
{
const CommandTransaction *task = &self->transaction;
if (!task->occupied || self->in_action) { return 0U; }
switch (task->phase) {
case IEC104_COMMAND_PHASE_SELECT_WAIT:
case IEC104_COMMAND_PHASE_EXECUTE_WAIT: return self->config.decision_timeout_ms;
case IEC104_COMMAND_PHASE_SELECTED: return self->config.select_timeout_ms;
case IEC104_COMMAND_PHASE_EXECUTING:
return task->result == 0U ? self->config.execute_timeout_ms : 0U;
default: return 0U;
}
}
static uint32_t Remaining(const CommandTransaction *task, uint32_t limit, uint64_t now)
{
if (now < task->timer_start_ms || now - task->timer_start_ms >= limit) { return 0U; }
return limit - (uint32_t)(now - task->timer_start_ms);
}
/* 0关闭经过时长限制,不允许正在等待的状态依赖倒退的单调时间继续执行。 */
static bool Clock_Reversed(const IEC104_Command_t *self, uint64_t now)
{
const CommandTransaction *task = &self->transaction;
return !self->in_action && task->occupied && now < task->timer_start_ms &&
(task->phase == IEC104_COMMAND_PHASE_SELECT_WAIT || task->phase == IEC104_COMMAND_PHASE_SELECTED ||
task->phase == IEC104_COMMAND_PHASE_EXECUTE_WAIT ||
(task->phase == IEC104_COMMAND_PHASE_EXECUTING && task->result == 0U));
}
/* 0是未排队哨兵;耗尽时终结,绝不回绕到可能与旧排队身份相同的序号。 */
static uint64_t Reply_Serial(IEC104_Command_t *self)
{
if (self->next_reply == UINT64_MAX) {
Cancel_All(self, IEC104_COMMAND_FINISH_SERVICE_ERROR);
self->status.active = false;
return 0U;
}
return ++self->next_reply;
}
static bool Set_Confirm(IEC104_Command_t *self, uint8_t cause, bool negative, bool try_send)
{
CommandTransaction *task = &self->transaction;
uint64_t serial = Reply_Serial(self);
if (serial == 0U) { return false; }
task->confirm_serial = serial;
task->confirm_cot = cause;
task->confirm_negative = negative;
task->confirm_try = try_send;
task->confirm_submitted = false;
task->send_waiting = false;
return true;
}
/* 只在已处理会话门禁的调用中使用。I帧在FIFO受理时也必须先检查,避免过期选择复活。 */
static void Expire(IEC104_Command_t *self, uint64_t now)
{
CommandTransaction *task = &self->transaction;
uint32_t limit = Timer_Limit(self);
bool reversed = Clock_Reversed(self, now);
if (!reversed && (limit == 0U || Remaining(task, limit, now) != 0U)) { return; }
if (reversed) { ++self->status.clock_errors; }
switch (task->phase) {
case IEC104_COMMAND_PHASE_SELECT_WAIT:
case IEC104_COMMAND_PHASE_EXECUTE_WAIT:
if (Set_Confirm(self, IEC60870_ASDU_COT_ACTIVATION_CON, true, false)) {
Finish(self, IEC104_COMMAND_FINISH_DECISION_TIMEOUT);
}
break;
case IEC104_COMMAND_PHASE_SELECTED:
task->confirm_serial = 0U; /* 只能撤回模块尚持有的确认。 */
Finish(self, IEC104_COMMAND_FINISH_SELECT_TIMEOUT);
break;
case IEC104_COMMAND_PHASE_EXECUTING:
/* 动作已启动,不能因结果超时把原肯定确认改成否定。 */
Finish(self, IEC104_COMMAND_FINISH_EXECUTE_TIMEOUT);
break;
default: break;
}
}
/* 提交入口可能接收STOP,ERROR只能在同连接健康且通知待处理时解释为等待。 */
static bool Defer_For_Stop(IEC104_Command_t *self, const IEC104_SlaveEventIdentity_t *identity,
IEC104_TcpSubmitResult_t submitted)
{
IEC104_TcpServerStatus_t status;
if (submitted != IEC104_TCP_SUBMIT_ERROR || identity->slave != self->slave ||
identity->instance_id != self->session.instance_id || identity->generation != self->session.generation ||
IEC104_Slave_Get_Status(self->slave, &status, NULL) != IEC104_TCP_OK ||
status.generation != identity->generation || !status.connected || !status.connection_status_valid ||
!status.asdu_queue_status_valid || status.connection.phase != IEC104_TCP_SESSION_ACTIVE ||
status.connection.fault != IEC104_TCP_FAULT_NONE || status.connection.data_transfer_started ||
status.pending_event_count == 0U) { return false; }
self->status.events_pending = true;
return true;
}
/* 三种事务内回复和独立回复共享一个序号域,不另建可溢出的指针FIFO。 */
enum { COMMAND_HEAD_NONE, COMMAND_HEAD_CONFIRM, COMMAND_HEAD_TERM, COMMAND_HEAD_REPLY, COMMAND_HEAD_SHORT };
static unsigned Reply_Head(const IEC104_Command_t *self, unsigned *slot)
{
const CommandTransaction *task = &self->transaction;
uint64_t first = 0U;
unsigned head = COMMAND_HEAD_NONE, i;
if (task->confirm_serial != 0U) { first = task->confirm_serial; head = COMMAND_HEAD_CONFIRM; }
if (task->term_serial != 0U && (first == 0U || task->term_serial < first)) {
first = task->term_serial; head = COMMAND_HEAD_TERM;
}
if (task->reply_serial != 0U && (first == 0U || task->reply_serial < first)) {
first = task->reply_serial; head = COMMAND_HEAD_REPLY;
}
for (i = 0U; i < IEC104_COMMAND_REPLY_CAPACITY; ++i) {
if (self->replies[i].occupied && (first == 0U || self->replies[i].serial < first)) {
first = self->replies[i].serial; head = COMMAND_HEAD_SHORT; *slot = i;
}
}
return head;
}
/* Complete关闭返回阶段后,确认和可选TERM均交接才向应用报告协议完成。 */
static void Complete_If_Sent(IEC104_Command_t *self)
{
CommandTransaction *task = &self->transaction;
if (task->occupied && task->phase == IEC104_COMMAND_PHASE_EXECUTING && task->completed &&
task->confirm_submitted && task->confirm_serial == 0U && task->term_serial == 0U) {
Finish(self, task->result == IEC104_COMMAND_RESULT_SUCCEEDED ?
IEC104_COMMAND_FINISH_COMPLETED : IEC104_COMMAND_FINISH_EXECUTION_FAILED);
}
Reap(self);
}
/* 只提交一个队首。1有进展,0让出,-1组包/服务错误;发送故障已清理后返回0。 */
static int Submit_Head(IEC104_Command_t *self)
{
CommandTransaction *task = &self->transaction;
const IEC104_SlaveEventIdentity_t *identity;
const uint8_t *source;
uint8_t bytes[10], cause;
IEC60870_Asdu_t asdu;
IEC104_TcpSubmitResult_t submitted;
IEC104_SlaveRequestState_t state = Check_Session(self);
unsigned slot = 0U, head;
bool negative, try_send;
if (state == IEC104_SLAVE_REQUEST_ERROR) { return -1; }
if (state != IEC104_SLAVE_REQUEST_READY) { return 0; }
head = Reply_Head(self, &slot);
if (head == COMMAND_HEAD_NONE) { return 0; }
identity = &task->identity;
source = task->request_bytes;
cause = IEC60870_ASDU_COT_ACTIVATION_TERMINATION;
negative = false;
try_send = true;
if (head == COMMAND_HEAD_CONFIRM) {
cause = task->confirm_cot; negative = task->confirm_negative; try_send = task->confirm_try;
} else if (head == COMMAND_HEAD_REPLY) {
source = task->reply_bytes; cause = task->reply_cot;
negative = cause != IEC60870_ASDU_COT_DEACTIVATION_CON; try_send = false;
} else if (head == COMMAND_HEAD_SHORT) {
identity = &self->replies[slot].identity;
source = self->replies[slot].bytes; cause = self->replies[slot].cot;
negative = true; try_send = false;
}
memcpy(bytes, source, sizeof(bytes));
if (IEC60870_Asdu_Create_From_Buffer(&asdu, IEC104_Slave_Get_Application_Layer_Parameters(self->slave),
bytes, sizeof(bytes)) != IEC60870_ASDU_OK ||
IEC60870_Asdu_Set_COT(&asdu, cause) != IEC60870_ASDU_OK ||
IEC60870_Asdu_Set_Negative(&asdu, negative) != IEC60870_ASDU_OK) {
Cancel_All(self, IEC104_COMMAND_FINISH_SERVICE_ERROR);
return -1;
}
submitted = try_send ? IEC104_Slave_Try_Send_Response(self->slave, identity, &asdu) :
IEC104_Slave_Send_Response(self->slave, identity, &asdu);
if (submitted == IEC104_TCP_SUBMIT_OK) {
++self->status.submitted;
if (negative) { ++self->status.negative_submitted; }
if (head == COMMAND_HEAD_SHORT) { memset(&self->replies[slot], 0, sizeof(self->replies[slot])); }
else {
task->send_waiting = false;
if (head == COMMAND_HEAD_CONFIRM) { task->confirm_serial = 0U; task->confirm_submitted = true; }
else if (head == COMMAND_HEAD_TERM) { task->term_serial = 0U; task->term_submitted = true; }
else { task->reply_serial = 0U; }
}
Complete_If_Sent(self);
return 1;
}
if (submitted == IEC104_TCP_SUBMIT_WAIT) {
if (head == COMMAND_HEAD_SHORT) { self->replies[slot].send_waiting = true; }
else { task->send_waiting = true; }
++self->status.waits;
return 0;
}
if (Defer_For_Stop(self, identity, submitted)) { return 0; }
Cancel_All(self, IEC104_COMMAND_FINISH_SEND_ERROR);
self->status.active = false;
return 0;
}
/* 新短回复不能抢占活动事务的确认存储;池满丢新,不覆盖旧回复,也不断开连接。 */
static bool Short_Reply(IEC104_Command_t *self, const IEC104_SlaveEventIdentity_t *identity,
const uint8_t *data, uint8_t cause)
{
unsigned slot;
bool empty = Pending_Replies(self) == 0U;
uint64_t serial;
for (slot = 0U; slot < IEC104_COMMAND_REPLY_CAPACITY; ++slot) {
if (!self->replies[slot].occupied) { break; }
}
if (slot == IEC104_COMMAND_REPLY_CAPACITY) {
++self->status.overloads;
++self->status.dropped_requests;
return true;
}
serial = Reply_Serial(self);
if (serial == 0U) { return false; }
self->replies[slot].identity = *identity;
memcpy(self->replies[slot].bytes, data, sizeof(self->replies[slot].bytes));
self->replies[slot].bytes[1] = 1U;
self->replies[slot].serial = serial;
self->replies[slot].cot = cause;
self->replies[slot].occupied = true;
self->replies[slot].send_waiting = false;
return !empty || Submit_Head(self) >= 0;
}
/* 替换当前工作请求,仅匹配执行可从选择走到这里。事务身份和选择资源标志保持不变。 */
static void Work_Event(IEC104_Command_t *self, const uint8_t *data, uint64_t receive_serial,
uint64_t now, bool selected)
{
CommandTransaction *task = &self->transaction;
memcpy(task->request_bytes, data, sizeof(task->request_bytes));
task->request_bytes[1] = 1U;
task->receive_serial = receive_serial;
task->accepted_ms = task->timer_start_ms = now;
task->event_type = (data[9] & 0x80U) != 0U ? IEC104_COMMAND_SELECT : IEC104_COMMAND_EXECUTE;
task->phase = task->event_type == IEC104_COMMAND_SELECT ?
IEC104_COMMAND_PHASE_SELECT_WAIT : IEC104_COMMAND_PHASE_EXECUTE_WAIT;
task->event_serial = ++self->next_event;
task->accepted_event_serial = 0U;
if (task->event_type == IEC104_COMMAND_SELECT) { task->select_event_serial = task->event_serial; }
else { task->execute_event_serial = task->event_serial; }
task->event_taken = false;
task->selected = selected;
task->confirm_submitted = false;
task->send_waiting = false;
++self->status.accepted;
}
/* 撤销和不匹配使用本事务预留的短回复存储,不依赖独立池空闲。
* 原工作请求不覆盖,FINISHED仍能标识需要清理的选择/输出。 */
static bool End_With_Reply(IEC104_Command_t *self, const uint8_t *data, uint8_t cause,
IEC104_CommandFinishReason_t reason)
{
CommandTransaction *task = &self->transaction;
uint64_t serial = Reply_Serial(self);
if (serial == 0U) { return false; }
task->confirm_serial = task->term_serial = 0U;
memcpy(task->reply_bytes, data, sizeof(task->reply_bytes));
task->reply_bytes[1] = 1U;
task->reply_cot = cause;
task->reply_serial = serial;
task->send_waiting = false;
Finish(self, reason);
return Submit_Head(self) >= 0;
}
/* 按COT、CA、IOA、对象的顺序判错。完整10字节仍可规范回显,截短或多对象不能伪造回显。 */
static IEC104_CommandEventResult_t Request(IEC104_Command_t *self, const IEC104_SlaveEvent_t *event,
const IEC104_SlaveEventIdentity_t *identity)
{
CommandTransaction *task = &self->transaction;
IEC60870_AsduInfo_t info;
IEC104_Station_t *station;
const uint8_t *data;
uint16_t length;
uint8_t cause = 0U, state;
uint32_t ioa;
uint64_t now;
bool same_point, is_select;
if (!self->status.active || event->asdu_result != IEC60870_ASDU_OK ||
IEC60870_Asdu_Get_Data(&event->asdu, &data, &length) != IEC60870_ASDU_OK || length == 0U ||
(data[0] != 45U && data[0] != 46U) ||
(self->config.type_mask & IEC104_COMMAND_TYPE_BIT(data[0])) == 0U ||
!IEC60870_Parse_ASDU_Header(event->asdu.parameters, data, length, &info)) {
return IEC104_COMMAND_EVENT_IGNORED;
}
now = IEC104_Net_Now_Ms();
Expire(self, now);
if (info.object_count != 1U || info.information_objects_len < 4U) {
++self->status.rejected;
return IEC104_COMMAND_EVENT_HANDLED;
}
ioa = Read_IOA(data);
station = IEC104_Slave_Get_Station(self->slave, info.common_address);
state = data[9] & 3U;
is_select = (data[9] & 0x80U) != 0U;
if (info.cause_of_transmission != IEC60870_ASDU_COT_ACTIVATION &&
info.cause_of_transmission != IEC60870_ASDU_COT_DEACTIVATION) {
cause = IEC60870_ASDU_COT_UNKNOWN_CAUSE;
} else if (station == NULL) { cause = IEC60870_ASDU_COT_UNKNOWN_COMMON_ADDRESS; }
else if (ioa == 0U) { cause = IEC60870_ASDU_COT_UNKNOWN_IOA; }
else if (info.is_sequence || length != 10U || info.is_negative_confirm || info.is_test ||
(data[0] == 45U ? (data[9] & 2U) != 0U : (state == 0U || state == 3U))) {
cause = info.cause_of_transmission == IEC60870_ASDU_COT_DEACTIVATION ?
IEC60870_ASDU_COT_DEACTIVATION_CON : IEC60870_ASDU_COT_ACTIVATION_CON;
}
if (cause != 0U) {
++self->status.rejected;
return Short_Reply(self, identity, data, cause) ? IEC104_COMMAND_EVENT_HANDLED : IEC104_COMMAND_EVENT_ERROR;
}
same_point = task->occupied && task->identity.generation == identity->generation &&
IEC104_Station_Get_Id(task->station) == IEC104_Station_Get_Id(station) &&
Read_IOA(task->request_bytes) == ioa;
if (info.cause_of_transmission == IEC60870_ASDU_COT_DEACTIVATION) {
if (same_point && task->request_bytes[0] == data[0] && task->request_bytes[3] == data[3] &&
(task->phase == IEC104_COMMAND_PHASE_SELECT_WAIT || task->phase == IEC104_COMMAND_PHASE_SELECTED ||
task->phase == IEC104_COMMAND_PHASE_EXECUTE_WAIT)) {
return End_With_Reply(self, data, IEC60870_ASDU_COT_DEACTIVATION_CON,
IEC104_COMMAND_FINISH_DEACTIVATED) ? IEC104_COMMAND_EVENT_HANDLED : IEC104_COMMAND_EVENT_ERROR;
}
++self->status.rejected;
return Short_Reply(self, identity, data, IEC60870_ASDU_COT_DEACTIVATION_CON) ?
IEC104_COMMAND_EVENT_HANDLED : IEC104_COMMAND_EVENT_ERROR;
}
/* 选择确认尚未交接时一律忙拒绝;确认交接后同点执行先匹配,其余新激活不影响选择。 */
if (same_point && !is_select && task->phase == IEC104_COMMAND_PHASE_SELECTED && task->confirm_submitted) {
if (task->request_bytes[0] != data[0] || task->request_bytes[3] != data[3] ||
(task->request_bytes[9] & 0x7FU) != (data[9] & 0x7FU)) {
++self->status.rejected;
return End_With_Reply(self, data, IEC60870_ASDU_COT_ACTIVATION_CON,
IEC104_COMMAND_FINISH_MISMATCH) ? IEC104_COMMAND_EVENT_HANDLED : IEC104_COMMAND_EVENT_ERROR;
}
Work_Event(self, data, event->transport.data.i_frame.receive_serial, now, true);
return IEC104_COMMAND_EVENT_HANDLED;
}
if (task->occupied || (!is_select && (self->config.select_required_mask & IEC104_COMMAND_TYPE_BIT(data[0])) != 0U)) {
++self->status.rejected;
if (task->occupied) { ++self->status.busy_rejected; }
return Short_Reply(self, identity, data, IEC60870_ASDU_COT_ACTIVATION_CON) ?
IEC104_COMMAND_EVENT_HANDLED : IEC104_COMMAND_EVENT_ERROR;
}
if (self->next_transaction == UINT64_MAX || self->next_event > UINT64_MAX - 3U) {
++self->status.overloads;
++self->status.dropped_requests;
return IEC104_COMMAND_EVENT_HANDLED;
}
if (IEC104_Slave_Acquire_Station(self->slave, station) != IEC104_STATION_OK) { return IEC104_COMMAND_EVENT_ERROR; }
memset(task, 0, sizeof(*task));
task->occupied = true;
task->identity = *identity;
task->station = station;
task->transaction_id = ++self->next_transaction;
Work_Event(self, data, event->transport.data.i_frame.receive_serial, now, false);
return IEC104_COMMAND_EVENT_HANDLED;
}
void IEC104_Command_Config_Default(IEC104_CommandConfig_t *config)
{
if (config == NULL) { return; }
memset(config, 0, sizeof(*config));
config->type_mask = IEC104_COMMAND_TYPE_BIT(45U) | IEC104_COMMAND_TYPE_BIT(46U);
config->select_required_mask = config->type_mask;
config->select_timeout_ms = 30000U;
config->decision_timeout_ms = 15000U;
config->execute_timeout_ms = 60000U;
config->send_act_term = true;
}
IEC104_Command_t *IEC104_Command_Create(IEC104_Slave_t *slave, const IEC104_CommandConfig_t *config)
{
const uint16_t supported = IEC104_COMMAND_TYPE_BIT(45U) | IEC104_COMMAND_TYPE_BIT(46U);
IEC104_Command_t *free_slot = NULL;
unsigned i;
if (config == NULL || config->type_mask == 0U || (config->type_mask & (uint16_t)~supported) != 0U ||
(config->select_required_mask & (uint16_t)~config->type_mask) != 0U || config->select_timeout_ms == 0U ||
next_instance == UINT64_MAX || IEC104_Slave_Get_Application_Layer_Parameters(slave) == NULL) { return NULL; }
for (i = 0U; i < IEC104_COMMAND_CAPACITY; ++i) {
if (command_pool[i].in_use && command_pool[i].slave == slave) { return NULL; }
if (!command_pool[i].in_use && free_slot == NULL) { free_slot = &command_pool[i]; }
}
if (free_slot == NULL) { return NULL; }
memset(free_slot, 0, sizeof(*free_slot));
free_slot->slave = slave;
free_slot->config = *config;
free_slot->serial = ++next_instance;
free_slot->in_use = true;
return free_slot;
}
bool IEC104_Command_Destroy(IEC104_Command_t *self)
{
if (!Owned(self) || self->in_action || self->transaction.occupied || Pending_Replies(self) != 0U) { return false; }
memset(self, 0, sizeof(*self));
return true;
}
void IEC104_Command_Cancel(IEC104_Command_t *self)
{
if (Owned(self) && !self->in_action) { Cancel_All(self, IEC104_COMMAND_FINISH_CANCELLED); }
}
/* 状态通知先处理,不能统一套Check_Request;后续尚未出队的STOP不应阻挡当前通知消费。 */
IEC104_CommandEventResult_t IEC104_Command_Handle_Event(IEC104_Command_t *self,
const IEC104_SlaveEvent_t *event)
{
IEC104_SlaveEventIdentity_t identity;
if (!Owned(self) || self->in_action ||
!IEC104_Slave_Get_Event_Identity(self->slave, event, &identity) ||
(self->session.instance_id != 0U && self->session.instance_id != identity.instance_id)) {
return IEC104_COMMAND_EVENT_ERROR;
}
self->session.slave = identity.slave;
self->session.instance_id = identity.instance_id;
if (event->transport.type == IEC104_TCP_EVENT_CONNECTED) {
if (identity.generation <= self->last_generation) { return IEC104_COMMAND_EVENT_IGNORED; }
Cancel_All(self, IEC104_COMMAND_FINISH_CONNECTION_CHANGED);
self->session.generation = self->last_generation = identity.generation;
self->status.active = false;
return IEC104_COMMAND_EVENT_HANDLED;
}
if (identity.generation != self->session.generation || identity.generation == 0U) {
return IEC104_COMMAND_EVENT_IGNORED;
}
switch (event->transport.type) {
case IEC104_TCP_EVENT_ACTIVATED:
self->status.active = true;
return IEC104_COMMAND_EVENT_HANDLED;
case IEC104_TCP_EVENT_DEACTIVATED:
Cancel_All(self, IEC104_COMMAND_FINISH_STOPPED);
self->status.active = false;
return IEC104_COMMAND_EVENT_HANDLED;
case IEC104_TCP_EVENT_DISCONNECTED:
Cancel_All(self, IEC104_COMMAND_FINISH_DISCONNECTED);
self->session.generation = 0U;
self->status.active = false;
return IEC104_COMMAND_EVENT_HANDLED;
case IEC104_TCP_EVENT_I_FRAME: return Request(self, event, &identity);
default: return IEC104_COMMAND_EVENT_IGNORED;
}
}
bool IEC104_Command_Process(IEC104_Command_t *self)
{
unsigned step;
if (!Owned(self) || self->in_action) { return false; }
for (step = 0U; step < 2U * (1U + IEC104_COMMAND_REPLY_CAPACITY); ++step) {
IEC104_SlaveRequestState_t state = Check_Session(self);
int submitted;
if (state == IEC104_SLAVE_REQUEST_ERROR) { return false; }
if (state != IEC104_SLAVE_REQUEST_READY) { return true; }
Expire(self, IEC104_Net_Now_Ms());
Complete_If_Sent(self);
submitted = Submit_Head(self);
if (submitted <= 0) { return submitted == 0; }
}
return true;
}
IEC104_CommandNextResult_t IEC104_Command_NextEvent(IEC104_Command_t *self, IEC104_CommandEvent_t *event)
{
CommandTransaction *task;
IEC104_CommandEvent_t result;
IEC104_SlaveRequestState_t state = IEC104_SLAVE_REQUEST_READY;
if (!Owned(self) || self->in_action || event == NULL) { return IEC104_COMMAND_NEXT_ERROR; }
task = &self->transaction;
/* 已存在的FINISHED不受离线或未读通知门禁阻挡;它只清理已结束的协议交互。 */
if (!task->finish_pending) {
state = Check_Session(self);
if (state == IEC104_SLAVE_REQUEST_READY) { Expire(self, IEC104_Net_Now_Ms()); }
}
if (!task->finish_pending && state == IEC104_SLAVE_REQUEST_ERROR) { return IEC104_COMMAND_NEXT_ERROR; }
if (!task->occupied || (!task->finish_pending &&
(state != IEC104_SLAVE_REQUEST_READY || task->event_taken ||
(task->phase != IEC104_COMMAND_PHASE_SELECT_WAIT && task->phase != IEC104_COMMAND_PHASE_EXECUTE_WAIT)))) {
return IEC104_COMMAND_NEXT_EMPTY;
}
memset(&result, 0, sizeof(result));
result.type = (IEC104_CommandEventType_t)task->event_type;
result.instance_id = self->serial;
result.transaction_id = task->transaction_id;
result.event_serial = task->event_serial;
Copy_Request(task, &result.request);
if (task->finish_pending) {
result.reason = (IEC104_CommandFinishReason_t)task->finish_reason;
result.select_effect = task->select_effect;
result.execute_effect = task->execute_effect;
task->finish_pending = false;
} else {
task->ever_work_delivered = true;
if (result.type == IEC104_COMMAND_SELECT) { task->select_delivered = true; }
else { task->execute_delivered = true; }
}
task->event_taken = true;
*event = result;
Reap(self);
return IEC104_COMMAND_NEXT_READY;
}
/* 校验应用交互的独立身份;Slave身份只解决会话归属,不能代替事务及工作事件序号。 */
static bool Current_Event(const IEC104_Command_t *self, const IEC104_CommandEvent_t *event)
{
const CommandTransaction *task = &self->transaction;
return event != NULL && task->occupied && event->instance_id == self->serial &&
event->transaction_id == task->transaction_id && event->event_serial == task->event_serial &&
event->type == task->event_type && task->event_taken;
}
/* work=true用于未消费的SELECT/EXECUTE;false用于启动成功后可多次提交返回信息的EXECUTE令牌。 */
static bool Reply_Stage(const IEC104_Command_t *self, const IEC104_CommandEvent_t *event, bool work)
{
const CommandTransaction *task = &self->transaction;
if (!Current_Event(self, event)) { return false; }
if (work) {
return task->accepted_event_serial == 0U &&
((event->type == IEC104_COMMAND_SELECT && task->phase == IEC104_COMMAND_PHASE_SELECT_WAIT) ||
(event->type == IEC104_COMMAND_EXECUTE && task->phase == IEC104_COMMAND_PHASE_EXECUTE_WAIT));
}
return event->type == IEC104_COMMAND_EXECUTE && task->phase == IEC104_COMMAND_PHASE_EXECUTING &&
task->accepted_event_serial == event->event_serial && task->execute_effect;
}
static IEC104_CommandReplyResult_t Check_Reply(IEC104_Command_t *self,
const IEC104_CommandEvent_t *event, bool work)
{
IEC104_SlaveRequestState_t state;
if (!Owned(self) || self->in_action || event == NULL) { return IEC104_COMMAND_REPLY_ERROR; }
if (!Reply_Stage(self, event, work)) { return IEC104_COMMAND_REPLY_STALE; }
state = Check_Session(self);
if (state == IEC104_SLAVE_REQUEST_ERROR) { return IEC104_COMMAND_REPLY_ERROR; }
if (state == IEC104_SLAVE_REQUEST_WAIT_EVENTS) { return IEC104_COMMAND_REPLY_WAIT; }
if (state != IEC104_SLAVE_REQUEST_READY) { return IEC104_COMMAND_REPLY_STALE; }
Expire(self, IEC104_Net_Now_Ms());
return Reply_Stage(self, event, work) ? IEC104_COMMAND_REPLY_OK : IEC104_COMMAND_REPLY_STALE;
}
IEC104_CommandReplyResult_t IEC104_Command_Accept(IEC104_Command_t *self,
const IEC104_CommandEvent_t *event, IEC104_CommandAction_t action, void *context)
{
CommandTransaction *task;
IEC104_CommandRequest_t request;
IEC104_CommandActionResult_t action_result;
IEC104_CommandReplyResult_t checked;
bool selecting;
if (event != NULL && event->type == IEC104_COMMAND_EXECUTE && action == NULL) {
return IEC104_COMMAND_REPLY_ERROR;
}
checked = Check_Reply(self, event, true);
if (checked != IEC104_COMMAND_REPLY_OK) { return checked; }
task = &self->transaction;
selecting = task->event_type == IEC104_COMMAND_SELECT;
/* 确认资源和序号在动作前准备。事务自己的回复不会被独立短回复洪泛挤占。 */
if (!Set_Confirm(self, IEC60870_ASDU_COT_ACTIVATION_CON, false, !selecting)) {
return IEC104_COMMAND_REPLY_ERROR;
}
Copy_Request(task, &request);
task->accepted_event_serial = task->event_serial;
self->in_action = true;
action_result = action == NULL ? IEC104_COMMAND_ACTION_STARTED : action(context, &request);
self->in_action = false;
/* 非法动作返回值按可能有副作用的失败处理;已消费的动作绝不重试。 */
if (action_result != IEC104_COMMAND_ACTION_STARTED && action_result != IEC104_COMMAND_ACTION_NOT_STARTED) {
action_result = IEC104_COMMAND_ACTION_FAILED;
}
if (selecting) { task->select_effect = action != NULL && action_result != IEC104_COMMAND_ACTION_NOT_STARTED; }
else { task->execute_effect = action_result != IEC104_COMMAND_ACTION_NOT_STARTED; }
if (action_result == IEC104_COMMAND_ACTION_STARTED) {
task->phase = selecting ? IEC104_COMMAND_PHASE_SELECTED : IEC104_COMMAND_PHASE_EXECUTING;
task->timer_start_ms = IEC104_Net_Now_Ms();
if (selecting) { ++self->status.selected; }
else { ++self->status.executed; }
} else {
task->confirm_negative = true;
task->confirm_try = !selecting && action_result == IEC104_COMMAND_ACTION_FAILED;
if (action_result == IEC104_COMMAND_ACTION_NOT_STARTED) {
++self->status.app_rejected;
Finish(self, IEC104_COMMAND_FINISH_REJECTED);
} else { Finish(self, IEC104_COMMAND_FINISH_ACTION_FAILED); }
}
/* 提交可能接收STOP或产生故障;OK仍只描述动作交互已消费,应用继续读取FINISHED。 */
(void)Submit_Head(self);
return IEC104_COMMAND_REPLY_OK;
}
IEC104_CommandReplyResult_t IEC104_Command_Reject(IEC104_Command_t *self,
const IEC104_CommandEvent_t *event, IEC104_CommandRejectCause_t cause)
{
IEC104_CommandReplyResult_t checked;
if (cause != IEC104_COMMAND_REJECT_NEGATIVE && cause != IEC104_COMMAND_REJECT_UNKNOWN_IOA) {
return IEC104_COMMAND_REPLY_ERROR;
}
checked = Check_Reply(self, event, true);
if (checked != IEC104_COMMAND_REPLY_OK) { return checked; }
if (!Set_Confirm(self, cause == IEC104_COMMAND_REJECT_UNKNOWN_IOA ?
IEC60870_ASDU_COT_UNKNOWN_IOA : IEC60870_ASDU_COT_ACTIVATION_CON, true, false)) {
return IEC104_COMMAND_REPLY_ERROR;
}
self->transaction.accepted_event_serial = self->transaction.event_serial;
++self->status.app_rejected;
Finish(self, IEC104_COMMAND_FINISH_REJECTED);
(void)Submit_Head(self);
return IEC104_COMMAND_REPLY_OK;
}
IEC104_CommandReplyResult_t IEC104_Command_Report_Result(IEC104_Command_t *self,
const IEC104_CommandEvent_t *event, IEC104_CommandResult_t result)
{
IEC104_CommandReplyResult_t checked;
if (result != IEC104_COMMAND_RESULT_SUCCEEDED && result != IEC104_COMMAND_RESULT_FAILED) {
return IEC104_COMMAND_REPLY_ERROR;
}
checked = Check_Reply(self, event, false);
if (checked != IEC104_COMMAND_REPLY_OK) { return checked; }
if (self->transaction.result != 0U || self->transaction.completed) { return IEC104_COMMAND_REPLY_ERROR; }
self->transaction.result = (uint8_t)result;
return IEC104_COMMAND_REPLY_OK;
}
IEC104_CommandReplyResult_t IEC104_Command_Submit_Return_Info(IEC104_Command_t *self,
const IEC104_CommandEvent_t *event, const IEC60870_Asdu_t *asdu)
{
CommandTransaction *task;
IEC60870_AsduInfo_t info;
const uint8_t *data;
uint16_t length;
IEC104_TcpSubmitResult_t submitted;
IEC104_CommandReplyResult_t checked = Check_Reply(self, event, false);
if (checked != IEC104_COMMAND_REPLY_OK) { return checked; }
task = &self->transaction;
if (task->completed || asdu == NULL || !IEC60870_AppLayerParameters_Valid(asdu->parameters) ||
asdu->parameters->sizeOfCOT != 2 || asdu->parameters->sizeOfCA != 2 || asdu->parameters->sizeOfIOA != 3 ||
asdu->parameters->maxSizeOfASDU > IEC104_MAX_ASDU_LEN ||
IEC60870_Asdu_Get_Data(asdu, &data, &length) != IEC60870_ASDU_OK || length > IEC104_MAX_ASDU_LEN ||
IEC60870_Asdu_Parse(IEC104_Slave_Get_Application_Layer_Parameters(self->slave), data, length, &info) !=
IEC60870_ASDU_OK ||
!((info.type_id >= 1U && info.type_id <= 21U) || (info.type_id >= 30U && info.type_id <= 40U)) ||
info.object_count == 0U ||
(info.cause_of_transmission != IEC60870_ASDU_COT_RETURN_INFO_REMOTE &&
info.cause_of_transmission != IEC60870_ASDU_COT_SPONTANEOUS) ||
info.is_negative_confirm || info.is_test != ((task->request_bytes[2] >> 7) & 1U) ||
info.common_address != Read_CA(task->request_bytes)) { return IEC104_COMMAND_REPLY_ERROR; }
/* 底层Try只知道传输队列;这里还必须检查模块内更早的独立/事务应答。 */
if (!task->confirm_submitted || Pending_Replies(self) != 0U) { return IEC104_COMMAND_REPLY_WAIT; }
submitted = IEC104_Slave_Try_Send_Response(self->slave, &task->identity, asdu);
if (submitted == IEC104_TCP_SUBMIT_WAIT) {
task->return_waiting = true;
++self->status.waits;
return IEC104_COMMAND_REPLY_WAIT;
}
if (submitted == IEC104_TCP_SUBMIT_OK) {
task->return_waiting = false;
++self->status.submitted;
++self->status.return_info_submitted;
return IEC104_COMMAND_REPLY_OK;
}
if (Defer_For_Stop(self, &task->identity, submitted)) { return IEC104_COMMAND_REPLY_WAIT; }
Cancel_All(self, IEC104_COMMAND_FINISH_SEND_ERROR);
self->status.active = false;
return IEC104_COMMAND_REPLY_STALE;
}
IEC104_CommandReplyResult_t IEC104_Command_Complete(IEC104_Command_t *self,
const IEC104_CommandEvent_t *event)
{
CommandTransaction *task;
IEC104_CommandReplyResult_t checked = Check_Reply(self, event, false);
if (checked != IEC104_COMMAND_REPLY_OK) { return checked; }
task = &self->transaction;
if (task->result == 0U || task->completed) { return IEC104_COMMAND_REPLY_ERROR; }
if (self->config.send_act_term &&
(task->result == IEC104_COMMAND_RESULT_SUCCEEDED || self->config.act_term_on_failure)) {
uint64_t serial = Reply_Serial(self);
if (serial == 0U) { return IEC104_COMMAND_REPLY_ERROR; }
task->term_serial = serial;
}
task->completed = true;
task->return_waiting = false;
/* 无TERM且确认已交接可立即结束;否则Process只重试协议收尾,不要求应用重复Complete。 */
Complete_If_Sent(self);
return IEC104_COMMAND_REPLY_OK;
}
bool IEC104_Command_Get_Transaction(const IEC104_Command_t *self,
const IEC104_CommandEvent_t *event, IEC104_CommandTransactionInfo_t *info)
{
const CommandTransaction *task;
IEC104_CommandTransactionInfo_t result;
bool delivered;
if (!Owned(self) || event == NULL || info == NULL) { return false; }
task = &self->transaction;
if (!task->occupied || event->instance_id != self->serial || event->transaction_id != task->transaction_id) {
return false;
}
delivered = (event->type == IEC104_COMMAND_SELECT && task->select_delivered &&
event->event_serial == task->select_event_serial) ||
(event->type == IEC104_COMMAND_EXECUTE && task->execute_delivered &&
event->event_serial == task->execute_event_serial) ||
(event->type == IEC104_COMMAND_FINISHED && task->event_type == IEC104_COMMAND_FINISHED &&
task->event_taken && event->event_serial == task->event_serial);
if (!delivered) { return false; }
memset(&result, 0, sizeof(result));
result.active = task->phase != IEC104_COMMAND_PHASE_ENDED;
result.phase = (IEC104_CommandPhase_t)task->phase;
result.confirm_submitted = task->confirm_submitted;
result.result_reported = task->result != 0U;
result.completed = task->completed;
result.term_submitted = task->term_submitted;
*info = result;
return true;
}
bool IEC104_Command_Get_Status(const IEC104_Command_t *self, IEC104_CommandStatus_t *status)
{
const CommandTransaction *task;
IEC104_CommandStatus_t result;
IEC104_SlaveRequestState_t state;
uint32_t limit;
uint64_t now;
unsigned slot = 0U, head;
bool waiting;
if (!Owned(self) || status == NULL) { return false; }
state = IEC104_Slave_Check_Request(self->slave, &self->session);
if (state == IEC104_SLAVE_REQUEST_ERROR) { return false; }
task = &self->transaction;
result = self->status;
result.generation = self->session.generation;
result.events_pending = state == IEC104_SLAVE_REQUEST_WAIT_EVENTS;
result.busy = task->occupied;
result.pending_replies = Pending_Replies(self);
result.pending_events = task->occupied && (task->finish_pending ||
(!task->event_taken && (task->phase == IEC104_COMMAND_PHASE_SELECT_WAIT ||
task->phase == IEC104_COMMAND_PHASE_EXECUTE_WAIT))) ? 1U : 0U;
limit = Timer_Limit(self);
now = IEC104_Net_Now_Ms();
result.timer_active = limit != 0U;
result.timeout_remaining_ms = limit != 0U ? Remaining(task, limit, now) : 0U;
result.need_process = false;
head = Reply_Head(self, &slot);
waiting = head == COMMAND_HEAD_SHORT ? self->replies[slot].send_waiting : task->send_waiting;
if (!self->in_action && !result.events_pending) {
if (state != IEC104_SLAVE_REQUEST_READY || !result.active) {
result.need_process = result.pending_replies != 0U ||
(task->occupied && task->phase != IEC104_COMMAND_PHASE_ENDED);
} else {
result.need_process = Clock_Reversed(self, now) ||
(result.timer_active && result.timeout_remaining_ms == 0U) ||
(head != COMMAND_HEAD_NONE && !waiting);
}
}
*status = result;
return true;
}