الألة ذات الحالات المحددة Finite-state machine

هو مفهوم يطبق لبناء أي شيء ينتقل بين عدة حالات

سواء هذا الشيء آلة ميكانيكية أو كهربائية أو أي شيء يخضع لهذا المفهوم

قبل أن أبدأ الفكرة سأوضح هذا المفهوم من خلال أمثلة في حياتنا.

إشارة المرور الضوئية تنتقل بين عدة حالات محددة ( أحمر - برتقالي - أخضر - أخضر وبرتقالي معا (وأحيانا يكون أخضر وامض) )

الإشارة في لحظة ما يجب أن تكون في إحدى هذه الحالات تتنقل بينها وفق تسلسل معين بشروط معينة قد تكون فواصل زمنية أو أحداث معينة

طبعا يوجد حالات استثنائية وهي وضع الإطفاء ويكون نتيجة استثناء مثل عدم وجود كهرباء أو خلل في الدارة

كمثال آخر:

المصعد الكهربائي يكون في عدة حالات كأن يكون متوقف في طابق ما وقد يكون في حالة صعود أو هبوط أو حالة توقف استثنائي نتيجة طاريء

الشيء الذي يخضع لهذا المفهوم نسميه آلة Finite-state machine

http://en.wikipedia.org/wiki/Finite-state_machine

وكل حالة من هذه الحالات تدعى حالة State ولكون الحالات محددة من هنا سميت الآلة ذات الحالات المحددة

الآن يوجد شيء اسمه انتقال Transaction وهو عملية الانتقال من حالة حالية إلى حالة جديدة

ومن هنا يمكن تعريف عدد من الانتقالات المسموحة بين حالة وأخرى و في حالة لم يتم تعريف انتقال بين حالة وأخرى فهذا يعني أنه لا يمكن أن تأتي الحالة الثانية بعد الحالة الأولى نهائيا

كمثال :

لا يمكن انتقال المصعد من الهبوط إلى الصعود مباشرة وإنما يجب أن ينتقل إلى إحدى حالات التوقف ومن ثم ينتقل للصعود

كذلك يوجد مفهوم اسمه أحداث الانتقال Trigger بمعنى أنه عند تعريف انتقال بين حالة ما وحالة ثانية يجب تحديد الحدث الذي عندما يحصل يجب تنفيذ عملية الانتقال

كمثال :

لو عندنا مصباح فإن له حالتان مستقرتان إطفاء أو إشعال

و الانتقالات هي اثنتان من الإشعال إلى الإطفاء ومن الإطفاء للإشعال

الآن عندما يكون في إحدى الحالات لا يمكن أن ينتقل إلى الحالة الأخرى إلى إذا حدث أمر ما وهو توصيل التيار الكهربائي من خلال الضغط على زر التشغيل أو أي شيء يوصل التيار

إذا وصول التيار يعتبر حدث للانتقال الأول أي من الإطفاء إلى الإشعال

وانقطاع التيار يعتبر حدث للانتقال من الثانية إلى الأولى

كما يمكن تحديد شروط لتنفيذ الانتقال Condition

مثلا :

عندما يكون المصعد متوقف في الطابق 4 ثم يتم الضغط على زر الصعود من الطابق الأرضي وهنا يكون قد حصل حدث الانتقال من حالة توقف إلى حالة نزول

حينها ينتقل

ولكن هناك شرط أن لا يكون في حالة توقف لتنزيل ركاب وقد طلبه شخص من طابق أعلى

هنا لن يتحقق الانتقال إلى النزول لأنه يوجد أولوية للطلبات ويجب أن ينتقل لحالة صعود أولا

كما يوجد شيء اسمه ToDo وهي مهام يجب تنفيذها بحيث يمكن ربطها بالحدث أو أنك تجعل هذه المهمة مرتبطة بالحالة الآنية أو تربطها بعملية الانتقال

بمعنى أنه إما أن يتم تنفيذ مهمة عند وقوع الحدث أو أن تكون المهمة جزء من الحالة بحيث عندما يتم الانتقال إليها يتم تنفيذها أو يتم تنفيذها مع عملية الانتقال

كمثال :

فتح باب المصعد عندما ينتقل من حالة صعود أو نزول إلى حالة توقف

أو إظهار رقم الطابق كلما وصل إلى طابق ما

أو ربط إشارة المارة بإشارة المرور بحيث عندما تكون حمراء تكون تلك خضراء والعكس بالعكس

بعد كل هذا التوضيح للمفهوم سأتطرق لفكرة وهي تطبيقها في البرمجة.

وسأضرب مثال :

عندما يكون لدينا واجهة إدخال بيانات والتي سنعتبرها هي الآلة Machine

يمكن تحديد حالات State مثل ( عرض - جديد - تعديل )

أما الحذف فلا يعتبر حالة لأنك تقوم بالحذف فإما يحذف أو يتراجع وإن أحببت يمكن جعلها حالة إذا كان سيتعلق بها أمور مرحلية قبل الحذف النهائي.

أما الإنشاء والتعديل فلكونك ستكون في وضع يفتح لك الكونترولات ويسمع بتعديل النصوص ومن ثم تحفظ أو تلغي فيجب أن تكون حالات

الحالة الأولى (عرض) ستكون هي الافتراضية لأنك ستبدأ بها وتعود إليها

الآن نعرف الانتقالات Transaction

من العرض للجديد

ومن العرض للتعديل

من الجديد للعرض

من التعديل للعرض

أما من الجديد للتعديل مباشرة أو العكس فلا يمكن

نحدد الآن أحداث الانتقال Trigger

وهو النقر على زر جديد فيقوم بتنفيذ الانتقال من العرض للجديد

وحدث النقر على زر تعديل فيقوم بتنفيذ الانتقال من حالة العرض إلى حالة التعديل

وحدث الحفظ أو التراجع لينقلنا من الجديد إلى العرض

ونفسهما لينقلنا من التعديل للعرض

سنعرف شروط الانتقال Condition

لا يمكن الانتقال من العرض للتعديل في حال لم يكن هناك سجل محدد

سنعرف مهام مرتبطة بالحالة ToDo

كأن ينقل حالة الكونترولات من ReadOnly=true إلى ReadOnly=false ليسمح بإدخال وتعديل البيانات

و نعرف مهام مرتبطة بالأحداث كأن يقوم بعملية حفظ البيانات في القاعدة عند النقر على Save أو إلغاء العملية عند النقر على زر Cancel

كما يمكن تعريف مهام مرتبطة بالانتقال

كأن تقوم بتحديد السجل الجديد في حال انتقل من الجديد إلى العرض بعد عملية الحفظ

وهكذا

في الحقيقة لقد قمت بتطبيقها عمليا في مشروعي الذي أعمل عليه حاليا وقد قدم لي فائدة عظيمة حيث نظم لي العمل وفصل المهام و قدم حماية من الحالات الخاطئة

مثال استخدمته :

       Automator = new Automation(this.GetType().Name)
            // Normal // ======================================================================
            .AddState(new State(C.Default)).SetAsDefault(() =>
            {
                //MoveTo(10);
            })
            .AddEntry(() =>
            {
                EntityMode = EntityModes.Default;
            })

          // Create // ======================================================================
            .AddState(new State(C.Creation))
            .AddEntryConditions(() => CanCreate)
            .AddEntry(() =>
            {
                EntityMode = EntityModes.Creation;
                SelectedItemBeforCreation = SelectedItem;

                this.ActiveItem = LowerModel.Submit(GKeys.DataExt.GetEntity, Data, this.SelectedItem).Result<IEntity>();

                this.SelectedItem = null;
                PreviousSelectedIndex = SelectedIndex;
                SelectedIndex = MaxIndex + 2;
            })


          // Edit // ======================================================================
            .AddState(new State(C.Edition))
            .AddEntryConditions(() => CanEdit)
            .AddEntry(() =>
            {
                EntityMode = EntityModes.Edition;
            })


          // Delete // ======================================================================
            .AddState(new State(C.Deletion))
            .AddEntryConditions(() => CanDelete)
            .AddEntry(() =>
            {
                EntityMode = EntityModes.Deletion;
                DeleteEntity();
                Refresh();
                Automator.ReturnToState(C.Default);
            })


            .Automation

            .AddTransaction(new Transaction("Df->C", "Default", "Creation")).AddTriggers(Trigger.FromEntry("Create"))
            .AddTransaction(new Transaction("Df->E", "Default", "Edition")).AddTriggers(Trigger.FromEntry("Edit"))
            .AddTransaction(new Transaction("Df->D", "Default", "Deletion")).AddTriggers(Trigger.FromEntry("Delete"))

            .AddTransaction(new Transaction("C->Df", "Creation", "Default"))
            .AddTriggers(Trigger.FromSuccess("Save")
            .AddToDo(() =>
            {
                if(CanSave)
                {
                    Operate2(EntityModes.Creation);
                    IsDirty = false;
                    EntityMode = EntityModes.Default;
                }
            })
            ,
            Trigger.FromEntry("Cancel")
            .AddToDo(() =>
            {
                IsDirty = false;
                EntityMode = EntityModes.Default;
                //SelectedItem = PreviousSelectedItem;
                MoveTo(PreviousSelectedIndex);
            }),
            Trigger.FromExit("Close"))

            .AddTransaction(new Transaction("E->Df", "Edition", "Default"))
            .AddTriggers(Trigger.FromSuccess("Save")
            .AddToDo(() =>
            {
                if(CanSave)
                {
                    Operate2(EntityModes.Edition);
                    IsDirty = false;
                    EntityMode = EntityModes.Default;
                }
            })
            ,
            Trigger.FromEntry("Cancel")
            .AddToDo(() =>
            {
                IsDirty = false;
                EntityMode = EntityModes.Default;
                //SelectedItem = PreviousSelectedItem;
                MoveTo(PreviousSelectedIndex);
            }),
            Trigger.FromExit("Close"))

            .Automation
            ;

أخوكم : عبد الرحمن أحمد

في 09-02-2014

دمتم بحفظ الله