/* * IEC104单点/双点遥控(45/46)。 * 一个事务槽保存工作事件及必要回复,独立短应答使用固定池;全局序号决定提交先后。 * 应用显式Accept启动动作,Report_Result结束设备期限,Complete结束返回信息阶段。 * FINISHED和发送收尾分别完成后才释放事务;取消不回滚设备,迟到事件不能复用新事务。 */ #include "IEC104_Command.h" #include "IEC104_TcpServer_Config.h" #include "IEC104_Net.h" #include #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; }