أحد الحاجات اللى هتفرق فى تنظيم الكود هى Finite State Machine: FSM
ودى درسنا أمثلة عملية كتيرة ليها فى مواد الDigital Design و الCommunication وال Control وغيرها.
أثناء مرحلة تصميم الكود SWE.2 اوفى كل Component جوة SWE.3 بنحتاج نكسر مجموعة الحاجات المرتبطة ببعض لشوية States بحيث أقدر أحدد مجموعة مهام تتنفذ جوة ال State دى.
زى مثلًا فى تقسيم Software Modes، والسوفتوير هينفذ ايه فى كل Mode منهم.
وطبعًا فيه طرق كتيرة ممكن تنفذ بيها State Machine فى الكود.
أسهل طريقة هي باستخدام switch statement.
كل case جوه الـswitch بيتحول لState اللى من بأضيف جواها الFunctions اللى محتاج إنها تتنفذ.
الطريقة دي مناسبة جدًا لحل مشاكل تصميم كتير.
لكن لما تيجي تستخدمها في مشروع Event-driven أو Multithreaded،
بيبقى صعب أو بيسبب مشاكل مع أى خطأ فى التصميم، وبيكون الDebugging فيها مش سهل.
مثال عملي بسيط جدًا:
التحكم في موتور
نبدأ تشغيل الموتور.
نوقف الموتور.
نغير سرعته.
الأحداث-Events اللي هنوفرها للـClient Software:
Set Speed: يشغل الموتور بسرعة معينة.
Halt: يوقف الموتور.
الأحداث دي بتسمح إننا:
نشغل الموتور على أي سرعة.
نغير سرعة موتور شغال بالفعل.
أو نوقفه تمامًا.
مع مراعاة أنا كنت فين ورايح لفين!!
الحالات (States)
الأحداث -> Events
مش هي الحالات -> States
الحالات هنا هي الخطوات اللي بتتعامل مع الأحداث.
Idle: الموتور واقف.
- مفيش حاجة تتعمل.
Start: تشغيل الموتور من وضع التوقف.
- شغل الباور.
Change Speed: تغيير سرعة موتور شغال.
- عدل السرعة.
Stop: إيقاف الموتور.
- افصل الباور.
- ارجع لحالة Idle.
مفهوم Current State
كل State Machine ليها مفهوم "Current State".
في أي لحظة، بنكون في حالة واحدة بس.
يعني الحالة اللي الماكينة واقفة فيها دلوقتي.
وده بيحدد ايه الStates اللى ينفع أتحرك ليها بعد كده.
الرسم التوضيحي: State Diagram
مثال على الانتقالات:
نمشى مع الأسهم:
- لو Event "SetSpeed" جه والموتور في حالة Idle → ينتقل لحالة Start.
- لو نفس Event "SetSpeed" جه والموتور في حالة Start → ينتقل لحالة ChangeSpeed.
مش كل الانتقالات مسموحة:
مثلًا: ماينفعش ننتقل من ChangeSpeed لـIdle مباشرة.
لازم الأول نعدي على Stop ونوضح هل الانتقال من Stop ل Initial هايكون ازاى.
---
نشوف كود توضيحى بسيط عشان نوضح الفكرة..
لاحظ كويس التعامل مع كل state وكمان التعامل مع الانتقالات الخطأ.
وفى الmain فيه كود اختبار عشان نجرب الانتقالات الصحيحة والغلط.
وده أحد المصادر اللطيفة:
#include <stdio.h>
// Define motor states
typedef enum {
STATE_IDLE,
STATE_STOP,
STATE_START,
STATE_CHANGE_SPEED
} MotorState;
// Define event types
typedef enum {
EVENT_NONE,
EVENT_SET_SPEED,
EVENT_HALT,
EVENT_GET_READY
} EventType;
// Event struct with name
typedef struct {
EventType type;
const char *name;
} MotorEvent;
// Helper: convert state enum to string
const char* state_to_string(MotorState state) {
switch (state) {
case STATE_IDLE: return "IDLE";
case STATE_STOP: return "STOP";
case STATE_START: return "START";
case STATE_CHANGE_SPEED: return "CHANGE_SPEED";
default: return "UNKNOWN";
}
}
// State machine function (single return)
MotorState motor_state_machine(MotorState current, MotorEvent event) {
MotorState next = current; // default: stay in same state
switch (current) {
case STATE_IDLE:
if (event.type == EVENT_SET_SPEED) {
printf("Event: %s | Transition: Idle -> Start\n", event.name);
next = STATE_START;
} else {
printf("Event: %s | Invalid action from state IDLE\n", event.name);
}
break;
case STATE_STOP:
if (event.type == EVENT_GET_READY) {
printf("Event: %s | Transition: Stop -> Idle\n", event.name);
next = STATE_IDLE;
} else {
printf("Event: %s | Invalid action from state STOP\n", event.name);
}
break;
case STATE_START:
if (event.type == EVENT_SET_SPEED) {
printf("Event: %s | Transition: Start -> ChangeSpeed\n", event.name);
next = STATE_CHANGE_SPEED;
} else if (event.type == EVENT_HALT) {
printf("Event: %s | Transition: Start -> Stop\n", event.name);
next = STATE_STOP;
} else {
printf("Event: %s | Invalid action from state START\n", event.name);
}
break;
case STATE_CHANGE_SPEED:
if (event.type == EVENT_SET_SPEED) {
printf("Event: %s | Transition: ChangeSpeed -> ChangeSpeed (stay)\n", event.name);
next = STATE_CHANGE_SPEED;
} else if (event.type == EVENT_HALT) {
printf("Event: %s | Transition: ChangeSpeed -> Stop\n", event.name);
next = STATE_STOP;
} else {
printf("Event: %s | Invalid action from state CHANGE_SPEED\n", event.name);
}
break;
default:
printf("Event: %s | Invalid action from UNKNOWN state\n", event.name);
break;
}
return next; // single return
}
// Dummy test code
int main() {
MotorState state = STATE_IDLE;
// Define test sequence with event names
MotorEvent events[] = {
{EVENT_SET_SPEED, "SetSpeed"}, // Idle -> Start
{EVENT_GET_READY, "GetReady"}, // INVALID from Start
{EVENT_SET_SPEED, "SetSpeed"}, // Start -> ChangeSpeed
{EVENT_GET_READY, "GetReady"}, // INVALID from ChangeSpeed
{EVENT_HALT, "Halt"}, // ChangeSpeed -> Stop
{EVENT_SET_SPEED, "SetSpeed"}, // INVALID from Stop
{EVENT_GET_READY, "GetReady"} // Stop -> Idle
};
int num_events = sizeof(events) / sizeof(events[0]);
for (int i = 0; i < num_events; i++) {
state = motor_state_machine(state, events[i]);
printf("Current State: %s\n\n", state_to_string(state));
}
return 0;
}