आइए सामान्य रूप से पदानुक्रमित राज्य मशीनों और विशेष रूप से SObjectizer-5 में उनके समर्थन के बारे में बात करने की कोशिश करें

परिमित राज्य मशीनें शायद प्रोग्रामिंग में सबसे मौलिक और व्यापक रूप से उपयोग की जाने वाली अवधारणाओं में से एक हैं। परिमित राज्य मशीनों (केए) का उपयोग सक्रिय रूप से कई लागू नच में किया जाता है। विशेष रूप से, एपीसीएस और दूरसंचार के रूप में ऐसे निशानों में, जिसके साथ सौदा करना संभव था, अंतरिक्ष यान हर कदम पर थोड़ा कम पाया जाता है।

इसलिए, इस लेख में हम अंतरिक्ष यान के बारे में बात करने की कोशिश करेंगे, मुख्य रूप से पदानुक्रमित परिमित राज्य मशीनों और उनकी उन्नत क्षमताओं के बारे में। और SObjectizer-5 में अंतरिक्ष यान के लिए समर्थन के बारे में थोड़ा बताएं, C ++ के लिए "अभिनेता" रूपरेखा। उन दो कुछ में से एक जो खुले, मुक्त, क्रॉस-प्लेटफॉर्म और अभी भी जीवित हैं।

यहां तक ​​कि अगर आप SObjectizer में रुचि नहीं रखते हैं, लेकिन आपने पदानुक्रमित परिमित राज्य मशीनों के बारे में कभी नहीं सुना है या राज्यों या राज्य के इतिहास के लिए इनपुट / आउटपुट हैंडलर के रूप में अंतरिक्ष यान की ऐसी उन्नत विशेषताएं कितनी उपयोगी हैं, तो आप बिल्ली को देखने के लिए इच्छुक हो सकते हैं और लेख का कम से कम पहला भाग पढ़ें।

परिमित राज्य मशीनों के बारे में सामान्य शब्द


हम ऑटोमेटा के विषय पर लेख में एक पूर्ण शैक्षिक कार्यक्रम का संचालन करने की कोशिश नहीं करेंगे और इस तरह के परिमित राज्य मशीनों के रूप में । पाठक को इन प्रकार की संस्थाओं की कम से कम बुनियादी समझ होनी चाहिए।

उन्नत परिमित राज्य मशीनें और उनकी क्षमताएं


अंतरिक्ष यान में कई "उन्नत" विशेषताएं हैं जो कार्यक्रम में अंतरिक्ष यान की उपयोगिता को बहुत बढ़ाती हैं। आइए इन "उन्नत" विशेषताओं पर एक नज़र डालें।

डिस्क्लेमर: अगर पाठक यूएमएल के राज्य आरेखों से अच्छी तरह से परिचित है, तो उसे यहां अपने लिए कुछ नया नहीं मिलेगा।

पदानुक्रमित राज्य मशीनें


शायद सबसे महत्वपूर्ण और मूल्यवान अवसर राज्यों के पदानुक्रम / घोंसले के शिकार का संगठन है। चूँकि यह ठीक-ठीक राज्यों को एक दूसरे में डालने की क्षमता है जो अंतरिक्ष यान की जटिलता बढ़ने पर राज्य से राज्य में होने वाले संक्रमणों की संख्या के "विस्फोट" को समाप्त करता है।

उदाहरणों से दिखाने के लिए इसे शब्दों में समझाना अधिक कठिन है। इसलिए, आइए कल्पना करें कि हमारे पास स्क्रीन पर एक infokiosk है जिसमें पहली बार एक स्वागत योग्य संदेश प्रदर्शित किया गया है। उपयोगकर्ता "सेवा" आइटम का चयन कर सकता है और उसे उन सेवाओं का चयन करने के लिए अनुभाग में जाना चाहिए जिनकी उसे आवश्यकता है। या वह "व्यक्तिगत खाता" आइटम का चयन कर सकता है और अपने व्यक्तिगत डेटा और सेवाओं के साथ काम करने पर अनुभाग पर जा सकता है। या वह सहायता अनुभाग का चयन कर सकता है। अब तक, सब कुछ सरल प्रतीत होता है और निम्नलिखित राज्य आरेख (जितना संभव हो उतना सरल) द्वारा प्रस्तुत किया जा सकता है:



लेकिन यह सुनिश्चित करने की कोशिश करें कि "रद्द करें" बटन पर क्लिक करके, उपयोगकर्ता स्वागत पृष्ठ के साथ किसी भी अनुभाग से प्रारंभ पृष्ठ पर वापस आ सकता है:



योजना जटिल हो रही है, लेकिन अभी भी नियंत्रण में है। हालाँकि, याद रखें कि "सेवा" अनुभाग में हमारे पास कई और उप-खंड हो सकते हैं, उदाहरण के लिए, "लोकप्रिय सेवाएँ", "नई सेवाएँ" और "पूर्ण सूची"। और इनमें से प्रत्येक सेक्शन से आपको स्टार्ट पेज पर भी लौटना होगा। हमारा साधारण अंतरिक्ष यान अधिक कठिन होता जा रहा है:



लेकिन यह सब से बहुत दूर है। हमने अभी तक "बैक" बटन को ध्यान में नहीं रखा है, जिसके द्वारा हमें पिछले अनुभाग पर वापस जाने की आवश्यकता है। आइए "बैक" बटन पर एक प्रतिक्रिया जोड़ें और देखें कि हमें क्या मिलता है:



हां, अब हम असली मस्ती की राह देखते हैं। लेकिन हमने "मेरा खाता" और "सहायता" अनुभागों में उप-वर्गों पर विचार भी नहीं किया है ... यदि हम शुरू करते हैं, तो लगभग तुरंत हमारे सरल, सबसे पहले, अंतरिक्ष यान कुछ अकल्पनीय में बदल जाएगा।

यहां राज्यों का घोंसला बनाना हमारी सहायता के लिए आता है। आइए कल्पना करें कि हमारे पास केवल दो शीर्ष-स्तरीय राज्य हैं: वेलकमस्क्रीन और उपयोगकर्ता चयन। हमारे सभी अनुभाग (अर्थात, "सेवा", "मेरा खाता" और "सहायता") उपयोगकर्ता राज्य में "नेस्टेड" होंगे। आप कह सकते हैं कि ServicesScreen, ProfileScreen और HelpScreen राज्य UserSelection के बच्चे होंगे। और जब से वे बच्चे हैं, वे अपने माता-पिता से कुछ संकेतों की प्रतिक्रिया को विरासत में लेंगे। इसलिए, हम उपयोगकर्ता के चयन में रद्द करें बटन की प्रतिक्रिया को परिभाषित कर सकते हैं। लेकिन हमें सभी सहायक सबस्टेशनों में इस प्रतिक्रिया को निर्धारित करने की आवश्यकता नहीं है। क्या हमारे अंतरिक्ष यान को अधिक संक्षिप्त और समझने योग्य बनाता है:



यहाँ आप नोट कर सकते हैं कि "रद्द करें" और "वापस" के लिए प्रतिक्रिया हमने UserSelection में परिभाषित की है। और रद्द करें बटन की यह प्रतिक्रिया अपवाद उप-राज्यों के बिना सभी के लिए काम करती है (अभी तक एक अन्य समग्र सेवा उप-राज्य सहित)। लेकिन ServicesSelection उप-राज्य में, बैक बटन की प्रतिक्रिया पहले से ही अलग है - रिटर्न वेल्कमस्क्रीन में नहीं है, लेकिन ServicesScreen में।

सीए जो राज्यों के पदानुक्रम / नेस्टिंग का उपयोग करते हैं, उन्हें पदानुक्रमित परिमित राज्य मशीन (ICA) कहा जाता है।

राज्य से प्रवेश / निकास के लिए प्रतिक्रिया


एक बहुत ही उपयोगी विशेषता एक विशेष राज्य में प्रवेश करने के लिए एक प्रतिक्रिया प्रदान करने की क्षमता है, साथ ही एक राज्य से बाहर निकलने की प्रतिक्रिया है। तो, एक infokiosk के साथ उपरोक्त उदाहरण में, आप प्रत्येक स्थिति में प्रवेश करने के लिए एक हैंडलर को लटका सकते हैं जो कि infokiosk स्क्रीन की सामग्री को बदल देगा।

पिछले उदाहरण को थोड़ा विस्तारित किया जा सकता है। मान लीजिए कि हमारे पास वेलकमस्क्रीन में दो विकल्प हैं: ब्राइटवेलकॉमस्क्रीन, जिसमें स्क्रीन को सामान्य रूप से हाइलाइट किया जाएगा, और डार्कवेलकॉमस्क्रीन, जिसमें स्क्रीन की चमक कम हो जाएगी। हम एक DarkWelcomScreen प्रविष्टि हैंडलर बना सकते हैं जो स्क्रीन को मंद कर देगा। और एक DarkWelcomScreen निकास हैंडलर जो सामान्य चमक को बहाल करेगा।



एक निर्धारित समय के बाद राज्य का स्वत: परिवर्तन


कभी-कभी, अंतरिक्ष यान को किसी विशेष अवस्था में रहने के लिए सीमित करना आवश्यक हो सकता है। इसलिए, ऊपर दिए गए उदाहरण में, हम उस समय को सीमित कर सकते हैं जब हमारा ICA BrightWelcomScreen स्थिति में एक मिनट तक रहता है। जैसे ही मिनट समाप्त होता है, ICA स्वचालित रूप से DarkWelcomScreen राज्य में बदल जाता है।

अंतरिक्ष यान का इतिहास


आईसीए की एक और बहुत ही उपयोगी विशेषता अंतरिक्ष यान की स्थिति का इतिहास है।

आइए कल्पना करें कि हमारे पास इस तरह के कुछ सार आईसीए हैं:



यह हमारा ICA TopLevelState1 से TopLevelState2 तक जा सकता है और इसके विपरीत। लेकिन TopLevelState1 के अंदर कई नेस्टेड स्टेट्स हैं। यदि ICA बस TopLevelState2 से TopLevelState1 की ओर बढ़ता है, तो दो राज्य तुरंत सक्रिय हो जाते हैं: TopLevelState1 और NestedState1। NestedState1 सक्रिय है क्योंकि यह TopLevelState1 राज्य का प्रारंभिक विकल्प है।

अब कल्पना करें कि आगे हमारे ICA ने NestedState1 से NestedState2 में अपना राज्य बदल दिया। NestedState2 के अंदर, SubState InternalState1 को सक्रिय किया गया था (क्योंकि यह NestedState2 का प्रारंभिक विकल्प है)। और InternalState1 से हम InternalState2 में गए। इस प्रकार, हमारे पास एक साथ निम्नलिखित राज्य सक्रिय हैं: TopLevelState1, NestedState2 और InternalState2। और यहां हम TopLevelState2 पर जाते हैं (यानी हम आम तौर पर TopLevelState1 छोड़ देते हैं)।

सक्रिय TopLevelState2 बन जाता है। जिसके बाद हम TopLevelState1 पर लौटना चाहते हैं। यह TopLevelState1 में है, और TopLevelState1 में किसी विशेष स्थान पर नहीं है।

तो, TopLevelState2 से हम TopLevelState1 पर जाते हैं और हम कहाँ पहुँचते हैं?

यदि TopLevelState1 का कोई इतिहास नहीं है, तो हम TopLevelState1 और NestedState1 पर आएँगे (चूंकि NestedState1 TopLevelState1 का प्रारंभिक विकल्प है)। यानी TopLevelState2 के अंदर होने वाले बदलावों के बारे में पूरी कहानी, जो TopLevelState2 को छोड़ने से पहले हुई थी, पूरी तरह से खो गई थी।

यदि TopLevelState1 में एक तथाकथित है उथला इतिहास, तब जब TopLevelState2 से TopLevelState1 तक लौटते हुए हम NestedState2 और InternalState1 में पहुंचते हैं। हम NestedState2 में आते हैं क्योंकि यह TopLevelState1 की स्थिति के इतिहास में दर्ज है। और हम InternalState1 को प्राप्त करते हैं क्योंकि यह NestedState2 के लिए एक शुरुआत है। यह पता चला है कि TopLevelState1 के लिए सतही इतिहास में, जानकारी केवल पहले स्तर के सबस्टेट्स के बारे में संग्रहीत की जाती है। इन सबस्टेशनों में एम्बेडेड राज्यों का इतिहास संरक्षित नहीं है।

लेकिन अगर TopLevelState1 का गहरा इतिहास है, तो जब हम TopLevelState2 से TopLevelState1 पर लौटते हैं तो हम NestedState2 और InternalState2 में पहुंच जाते हैं। क्योंकि एक गहरे इतिहास में, सक्रिय सबस्टेट्स के बारे में पूरी जानकारी संग्रहीत की जाती है, उनकी गहराई की परवाह किए बिना।

रूढ़िवादी राज्य


अब तक, हमने आईसीए की जांच की है जिसमें केवल एक सबस्टेशन राज्य के अंदर सक्रिय हो सकता है। लेकिन कई बार ऐसी स्थितियां हो सकती हैं जब ICA के किसी विशेष राज्य में एक साथ कई सक्रिय सबस्टेशन होने चाहिए। इस तरह के सबस्टेशन को ऑर्थोगोनल स्टेट्स कहा जाता है।

एक क्लासिक उदाहरण जो ओर्थोगोनल राज्यों को प्रदर्शित करता है वह परिचित कंप्यूटर कीबोर्ड और इसके न्यूमॉक, कैप्सलॉक और स्क्रॉललॉक मोड हैं। हम कह सकते हैं कि NumLock / CapsLock / ScrollLock के साथ काम करना सक्रिय राज्य के अंदर रूढ़िवादी सबस्टेशन द्वारा वर्णित है:



सब कुछ आप परिमित राज्य मशीनों के बारे में जानना चाहते थे, लेकिन ...


सामान्य तौर पर, डेविड हरेल: स्टेटचर्ट्स: ए विजुअल फॉर्मेलिज्म फॉर कॉम्प्लेक्स सिस्टम (1987) से राज्य आरेखों के लिए औपचारिक संकेतन पर एक मूलभूत लेख है।

वहां, विभिन्न स्थितियों का सामना करना पड़ सकता है जब एक साधारण इलेक्ट्रॉनिक घड़ी को नियंत्रित करने के उदाहरण का उपयोग करके परिमित राज्य मशीनों के साथ काम किया जाता है। यदि किसी ने इसे नहीं पढ़ा है, तो मैं इसकी अत्यधिक अनुशंसा करता हूं। मूल रूप से, हरेल ने जो कुछ भी वर्णित किया, वह यूएमएल संकेतन में चला गया। लेकिन जब आप यूएमएल से राज्य आरेखों का विवरण पढ़ते हैं, तो आपको हमेशा यह समझ में नहीं आता है कि आपको क्या, क्यों और कब इसकी आवश्यकता है। लेकिन हरेल के लेख में, प्रस्तुति सरल स्थितियों से अधिक जटिल लोगों तक जाती है। और आप उन सभी शक्ति से बेहतर जानते हैं जो राज्य मशीनों को अपने आप में छिपा लेती हैं।

SObjectizer में परिमित राज्य मशीनें


इसके अलावा हम SObjectizer और इसकी बारीकियों के बारे में बात करेंगे। यदि आप नीचे दिए गए उदाहरणों को काफी नहीं समझते हैं, तो यह SObjectizer के बारे में अधिक जानने के लिए समझ में आ सकता है। उदाहरण के लिए, SObjecizer और कई बाद के लोगों के बारे में हमारे समीक्षा लेख से, जो SObjectizer के पाठकों का परिचय देते हैं, सरल से जटिल ( पहला लेख, दूसरा और तीसरा )।

SObjectizer में एजेंट राज्य मशीन हैं


शुरुआत से ही SObjectizer में एजेंट स्पष्ट राज्यों वाली राज्य मशीनें थीं। भले ही एजेंट के डेवलपर ने अपने एजेंट वर्ग में अपने स्वयं के राज्यों में से किसी का वर्णन नहीं किया, फिर भी एजेंट के पास डिफ़ॉल्ट राज्य था, जिसका उपयोग डिफ़ॉल्ट रूप से किया गया था। उदाहरण के लिए, यदि किसी डेवलपर ने ऐसा तुच्छ एजेंट बनाया है:
class simple_demo final : public so_5::agent_t { public: //   ,       . struct how_are_you final : public so_5::signal_t {}; //   ,     . struct quit final : public so_5::signal_t {}; // ..   ,      . simple_demo(context_t ctx) : so_5::agent_t{std::move(ctx)} { so_subscribe_self() .event<how_are_you>([]{ std::cout << "I'm fine!" << std::endl; }) .event<quit>([this]{ so_deregister_agent_coop_normally(); }); } }; 

तब उन्हें यह भी संदेह नहीं हो सकता है कि वास्तव में उनके द्वारा बनाए गए सभी सदस्यता डिफ़ॉल्ट राज्य के लिए हैं। लेकिन अगर डेवलपर एजेंट के साथ अपने राज्यों को जोड़ता है, तो आपको पहले से ही एजेंट को सही स्थिति में ठीक से हस्ताक्षर करने के बारे में सोचना होगा। यहां, ऊपर दिखाए गए एजेंट के एक साधारण (और, हमेशा की तरह) गलत संशोधन का कहना है:
 class simple_demo final : public so_5::agent_t { // ,  ,   . state_t st_free{this}; // ,  ,   . state_t st_busy{this}; public: //   ,       . struct how_are_you final : public so_5::signal_t {}; //   ,     . struct quit final : public so_5::signal_t {}; // ..   ,      . simple_demo(context_t ctx) : so_5::agent_t{std::move(ctx)} { so_subscribe_self() .event<quit>([this]{ so_deregister_agent_coop_normally(); }); //   how_are_you   ,    . st_free.event([]{ std::cout << "I'm free" << std::endl; }); st_busy.event([]{ std::cout << "I'm busy" << std::endl; }); //     st_free. this >>= st_free; } }; 

हम how_are_you सिग्नल के लिए दो अलग-अलग हैंडलर सेट करते हैं, प्रत्येक अपने राज्य के लिए।

और इस simple_demo एजेंट संशोधन में त्रुटि यह है कि st_free या st_busy में होने के कारण एजेंट बिल्कुल भी छोड़ने का जवाब नहीं देगा, क्योंकि हमने डिफ़ॉल्ट राज्य में सदस्यता छोड़ दी, लेकिन st_free और st_busy के लिए संगत सदस्यता नहीं बनाई। इस समस्या को ठीक करने का एक सरल और स्पष्ट तरीका यह है कि st_free और st_busy में उपयुक्त सदस्यताएँ जोड़ें:
  simple_demo(context_t ctx) : so_5::agent_t{std::move(ctx)} { //   how_are_you   ,    . st_free .event([]{ std::cout << "I'm free" << std::endl; }) .event<quit>([this]{ so_deregister_agent_coop_normally(); }); st_busy .event([]{ std::cout << "I'm busy" << std::endl; }) .event<quit>([this]{ so_deregister_agent_coop_normally(); }); //     st_free. this >>= st_free; } 

सच है, यह विधि कॉपी-पेस्ट की स्मैक है, जो अच्छा नहीं है। आप st_free और st_busy के लिए एक आम अभिभावक राज्य में प्रवेश करके कॉपी-पेस्ट से छुटकारा पा सकते हैं:
 class simple_demo final : public so_5::agent_t { //      . state_t st_basic{this}; // ,  ,   . //      st_basic. state_t st_free{initial_substate_of{st_basic}}; // ,  ,   . //     st_basic. state_t st_busy{substate_of{st_basic}}; public: //   ,       . struct how_are_you final : public so_5::signal_t {}; //   ,     . struct quit final : public so_5::signal_t {}; // ..   ,      . simple_demo(context_t ctx) : so_5::agent_t{std::move(ctx)} { //   quit   st_basic    //  ""  . st_basic.event<quit>([this]{ so_deregister_agent_coop_normally(); }); //   how_are_you   ,    . st_free.event([]{ std::cout << "I'm free" << std::endl; }); st_busy.event([]{ std::cout << "I'm busy" << std::endl; }); //     st_free. this >>= st_free; } }; 

न्याय की खातिर, यह जोड़ा जाना चाहिए कि शुरू में SObjectizer एजेंटों में केवल सरल राज्य मशीनें हो सकती हैं। जनवरी 2016 में पदानुक्रमित अंतरिक्ष यान के लिए समर्थन अपेक्षाकृत हाल ही में दिखाई दिया।

SObjectizer एजेंट राज्य मशीनों को सीमित क्यों कर रहे हैं?


इस प्रश्न का बहुत ही सरल उत्तर है: ऐसा इसलिए हुआ है कि SObjectizer की जड़ें प्रक्रिया नियंत्रण प्रणालियों की दुनिया से विकसित होती हैं, और वहाँ परिमित राज्य मशीनों का उपयोग अक्सर किया जाता है। इसलिए, हमने यह आवश्यक समझा कि SObjectizer में एजेंट भी स्टेट मशीन हों। यह बहुत सुविधाजनक है यदि जिस एप्लिकेशन में वे आवेदन करने का प्रयास कर रहे हैं, उसके लिए CA का उपयोग किया जाता है। और डिफ़ॉल्ट स्थिति जो सभी एजेंटों के पास है, आपको सीए के बारे में सोचने की अनुमति नहीं देता है, अगर सीए के उपयोग की आवश्यकता नहीं है।

सिद्धांत रूप में, यदि आप स्वयं अभिनेता मॉडल को देखते हैं, और उन सिद्धांतों पर, जिन पर यह मॉडल बनाया गया है:

  • एक अभिनेता व्यवहार के साथ एक इकाई है;
  • अभिनेता आने वाले संदेशों का जवाब देते हैं;
  • संदेश प्राप्त करने के बाद, अभिनेता कर सकता है:
    • अन्य अभिनेताओं को एक निश्चित संख्या में संदेश भेजें;
    • कई नए अभिनेताओं का निर्माण;
    • बाद के संदेशों को संसाधित करने के लिए एक नया व्यवहार परिभाषित करें।

सरल अंतरिक्ष यान और अभिनेताओं के बीच एक मजबूत समानता मिल सकती है। आप यह भी कह सकते हैं कि अभिनेता सरल परिमित राज्य मशीन हैं।

उन्नत राज्य मशीनों की क्या विशेषताएं SObjectizer का समर्थन करती हैं?


उन्नत परिमित राज्य मशीनों की उपर्युक्त सुविधाओं में से, सॉजेबाइज़र ऑर्थोगोनल राज्यों को छोड़कर सब कुछ का समर्थन करता है। अन्य अच्छाइयों, जैसे नेस्टेड राज्य, इनपुट / आउटपुट हैंडलर, राज्य में बिताए समय पर प्रतिबंध, राज्यों के लिए इतिहास, का समर्थन किया जाता है।

ऑर्थोगोनल राज्यों के समर्थन के साथ पहली बार एक साथ विकसित नहीं हुआ। एक तरफ, SObjectizer की आंतरिक वास्तुकला का उद्देश्य एजेंट के कई स्वतंत्र और एक साथ सक्रिय राज्यों का समर्थन करना नहीं था। दूसरी ओर, इस बारे में वैचारिक सवाल हैं कि एक एजेंट जिसके पास ऑर्थोगोनल राज्य हैं, को कैसे व्यवहार करना चाहिए। इन सवालों की उलझन बहुत जटिल हो गई थी, और इस समस्या को हल करने के लिए उपयोगी निकास बहुत छोटा था। हां, और हमारे व्यवहार में, अभी तक ऐसी परिस्थितियां नहीं आई हैं जहां ऑर्थोगोनल राज्यों की आवश्यकता होती, लेकिन ऐसा करना असंभव होता, उदाहरण के लिए, एक सामान्य कामकाजी संदर्भ से जुड़े कई एजेंटों के बीच काम को विभाजित करके।

हालांकि, अगर किसी को ऑर्थोगोनल राज्यों जैसी सुविधा की आवश्यकता है, और आपके पास उन कार्यों के वास्तविक दुनिया उदाहरण हैं जहां यह मांग है, तो चलो बात करते हैं। शायद, हमारी आँखों के सामने ठोस उदाहरण होने पर, हम इस सुविधा को SObjectizer में जोड़ सकते हैं।

कोड में आईसीए की उन्नत सुविधाओं का समर्थन कैसे दिखता है


कहानी के इस भाग में, हम ICA के साथ काम करने के लिए SObjectizer-5 API पर जल्दी से जाने की कोशिश करेंगे। विवरण में गहराई तक जाने के बिना, बस इतना है कि पाठक को यह पता है कि क्या है और यह कैसा दिखता है। अधिक विस्तृत जानकारी, यदि आप चाहते हैं, तो आधिकारिक दस्तावेज में पाया जा सकता है।

नेस्टेड स्टेट्स


नेस्टेड स्टेट घोषित करने के लिए, आपको संबंधित state_t ऑब्जेक्ट के कंस्ट्रक्टर के लिए initial_substate_of या substate_of एक्सप्रेशन पास करना होगा:
 class demo : public so_5::agent_t { state_t st_parent{this}; //  . state_t st_first_child{initial_substate_of{st_parent}}; //   . //    . state_t st_second_child{substate_of{st_parent}}; //   . state_t st_third_child{substate_of{st_parent}}; //   . state_t st_first_grandchild{initial_substate_of{st_third_child}}; //    . state_t st_second_grandchild{substate_of{st_third_child]}; ... }; 

यदि राज्य S में C1, C2, ..., Cn के कई विकल्प हैं, तो उनमें से एक (और केवल एक) को initial_substate_of के रूप में चिह्नित किया जाना चाहिए। इस नियम के उल्लंघन का निदान रन-टाइम में किया जाता है।

SObjectizer-5 में राज्य के घोंसले की अधिकतम गहराई सीमित है। 5.5 संस्करणों में, ये 16 स्तर हैं। इस नियम के उल्लंघन का निदान रन-टाइम में किया जाता है।

नेस्टेड राज्यों के साथ सबसे महत्वपूर्ण चाल यह है कि जब नेस्टेड राज्यों को सक्रिय किया जाता है, तो कई राज्य एक साथ सक्रिय होते हैं। मान लीजिए कि एक अवस्था A है जिसमें B और C सम्‍मिलित है, और B के स्‍थान पर D और E सम्‍मिलित हैं:



जब राज्य ए सक्रिय होता है, तो, वास्तव में, तीन राज्य तुरंत सक्रिय हो जाते हैं: ए, एबी और एबीडी

यह तथ्य कि कई राज्य एक साथ सक्रिय हो सकते हैं, दो अभिलेखीय चीजों पर सबसे गंभीर प्रभाव पड़ता है। सबसे पहले, अगले आने वाले संदेश के लिए एक हैंडलर की खोज करें। इसलिए, केवल दिखाए गए उदाहरण में, संदेश हैंडलर को पहले ABD राज्य में खोजा जाएगा। यदि वहाँ कोई उपयुक्त हैंडलर नहीं है, तो खोज अपने मूल राज्य में जारी रहेगी, अर्थात। एबी में और पहले से ही चोट लगी है, यदि आवश्यक हो, राज्य ए में खोज जारी रहेगी।

दूसरे, कई सक्रिय राज्यों की उपस्थिति राज्यों के लिए इनपुट / आउटपुट हैंडलर के आह्वान के आदेश को प्रभावित करती है। लेकिन इस पर नीचे चर्चा की जाएगी।

राज्य I / O हैंडलर्स


एक राज्य के लिए, राज्य प्रवेश और निकास राज्य संचालकों को निर्दिष्ट किया जा सकता है। यह State_t :: on_enter और state_t :: on_exit विधियों का उपयोग करके किया जाता है। आमतौर पर, इन विधियों को so_define_agent () विधि में (या सीधे एजेंट कंस्ट्रक्टर में कहा जाता है, यदि एजेंट तुच्छ और विरासत से प्रदान नहीं किया जाता है)।
 class demo : public so_5::agent_t { state_t st_free{this}; state_t st_busy{this}; ... void so_define_agent() override { // :       , //     . st_free.on_enter([]{ ... }); st_busy.on_exit([]{ ...}); ... this >>= st_free; } ... }; 

शायद on_enter / on_exit हैंडलर के साथ सबसे कठिन क्षण नेस्टेड राज्यों के लिए उपयोग कर रहा है। आइए राज्यों ए, बी, सी, डी और ई के साथ उदाहरण पर वापस जाएं।



मान लीजिए कि प्रत्येक राज्य में एक on_enter और on_exit हैंडलर है।

एजेंट की वर्तमान स्थिति बनने दें। राज्य A, AB और ABD सक्रिय हैं एजेंट के राज्य के परिवर्तन के दौरान, A.on_enter, ABon_enter और ABDon_enter को बुलाया जाएगा। और उस क्रम में।

मान लीजिए कि ABE के लिए एक संक्रमण है। ABDon_exit और ABEon_enter को बुलाया जाएगा।

यदि हम एजेंट को एसी स्थिति में रखते हैं, तो ABEon_exit, ABon_exit, ACon_enter को बुलाया जाएगा।

यदि एजेंट, एसी की स्थिति में है, तो उसे निष्क्रिय कर दिया जाता है, तो so_evt_finish () विधि के पूरा होने के तुरंत बाद, ACon_exit और A.on_exit हैंडलर को बुलाया जाएगा।

समय सीमा


एजेंट के लिए किसी विशेष राज्य में रहने की समय सीमा राज्य_t :: time_limit पद्धति का उपयोग करके निर्धारित की जाती है। On_enter / on_exit के साथ, time_limit विधियों को आम तौर पर कहा जाता है जहां एजेंट SOBjectizer के अंदर काम करने के लिए कॉन्फ़िगर किया गया है:
 class led_indicator : public so_5::agent_t { state_t inactive{this}; state_t active{this}; ... void so_define_agent() override { //        15s. //        inactive. active.time_limit(15s, inactive); ... } ... }; 

यदि राज्य के लिए समय सीमा निर्धारित की जाती है, तो जैसे ही एजेंट इस राज्य में प्रवेश करता है, SObjectizer राज्य में बिताए समय को गिनना शुरू कर देता है। यदि एजेंट राज्य छोड़ देता है, और फिर इस स्थिति में वापस आ जाता है, तो फिर से उलटी गिनती शुरू होती है।

यदि एम्बेडेड राज्यों के लिए समय सीमा निर्धारित की जाती है, तो आपको सावधान रहने की आवश्यकता है, क्योंकि जिज्ञासु चाल संभव है:
 class demo : public so_5::agent_t { //   . state_t A{this}, B{this}; //   first . state_t C{initial_substate_of{A}}, st_D{substate_of{A}}; ... void so_define_agent() override { A.time_limit(15s, B); C.time_limit(10s, D); D.time_limit(20s, C); ... } ... }; 

मान लीजिए कि एक एजेंट राज्य में प्रवेश करता है। A और C दोनों के लिए राज्य A और C सक्रिय हैं। पहले, यह राज्य सी के लिए समाप्त हो जाएगा और एजेंट राज्य डी पर स्विच करेगा। यह राज्य डी में रहने के लिए उलटी गिनती शुरू करेगा। लेकिन ए में रहने के लिए उलटी गिनती जारी रहेगी! चूँकि C से D तक संक्रमण के दौरान एजेंट का राज्य A में बने रहना जारी रहा और C से D के लिए मजबूर संक्रमण के पाँच सेकंड बाद, एजेंट राज्य B में जाएगा।

भाग्य के लिए कहानी


डिफ़ॉल्ट रूप से, एजेंट राज्यों का इतिहास नहीं होता है। किसी राज्य के लिए इतिहास की बचत को सक्रिय करने के लिए, उथले_हिस्टोन को पास करें (राज्य का एक उथला इतिहास होगा) या deep_history (राज्य का एक गहरा इतिहास होगा) को state_t कंस्ट्रक्टर। उदाहरण के लिए:
 class demo : public so_5::agent_t { state_t A{this, shallow_history}; state_t B{this, deep_history}; ... }; 

राज्यों के लिए इतिहास एक कठिन विषय है, खासकर जब राज्यों के घोंसले की एक अच्छी गहराई का उपयोग किया जाता है और सबस्टेशनों का अपना इतिहास होता है। इसलिए, इस विषय पर अधिक संपूर्ण जानकारी के लिए, प्रलेखन का उल्लेख करना बेहतर है, प्रयोग करने के लिए। ठीक है, यह पूछने के लिए कि क्या आप इसे स्वयं नहीं समझ सकते;)

just_switch_to, transfer_to_state, दबाएं


State_t वर्ग के पास सबसे अधिक उपयोग की जाने वाली विधियां हैं जो पहले से ही ऊपर दिखाई गई हैं: इनपुट / आउटपुट हैंडलर, time_limit () को किसी राज्य में बिताए गए समय की सीमा निर्धारित करने के लिए एक संदेश, on_enter () और on_exit () पर घटनाओं की सदस्यता के लिए ईवेंट ()।

इन विधियों के साथ, जब ICA के साथ काम करते हैं, State_t वर्ग के निम्नलिखित तरीके बहुत उपयोगी होते हैं:

विधि just_switch_to (), जो उस स्थिति के लिए डिज़ाइन की गई है जब एक आने वाले संदेश की एकमात्र प्रतिक्रिया एजेंट को नए राज्य में स्थानांतरित करना है। आप लिख सकते हैं:
 some_state.just_switch_to<some_msg>(another_state); 

इसके बजाय:
 some_state.event([this](mhood_t<some_msg>) { this >>= another_state; }); 

Transfer_to_state () विधि बहुत उपयोगी है जब हमारे पास कुछ संदेश M को दो या अधिक राज्यों S1, S2, ..., Sn में उसी तरह संसाधित किया जाता है। लेकिन, अगर हम एस 2, ..., एसएन राज्यों में हैं, तो हमें पहले एस 1 पर लौटना होगा, और उसके बाद ही प्रोसेसिंग एम।

यदि यह मुश्किल लगता है, तो शायद एक कोड उदाहरण में इस स्थिति को बेहतर समझा जाएगा:
 class demo : public so_5::agent_t { state_t S1{this}, S2{this}, ..., Sn{this}; ... void actual_M_handler(mhood_t<M> cmd) {...} ... void so_define_agent() override { S1.event(&demo::actual_M_handler); ... //           S1, //      M  . S2.event([this](mhood_t<M> cmd) { this >>= S1; actual_M_handler(cmd); }); ... //      . Sn.event([this](mhood_t<M> cmd) { this >>= S1; actual_M_handler(cmd); }); } ... }; 

लेकिन S2 के लिए बहुत समान ईवेंट हैंडलर को परिभाषित करने के बजाय, ..., Sn, transfer_to_state का उपयोग करें:
 class demo : public so_5::agent_t { state_t S1{this}, S2{this}, ..., Sn{this}; ... void actual_M_handler(mhood_t<M> cmd) {...} ... void so_define_agent() override { S1.event(&demo::actual_M_handler); ... //           S1, //      M  . S2.transfer_to_state<M>(S1); ... //      . Sn.transfer_to_state<M>(Sn); } ... }; 

दबाने () विधि वर्तमान सबस्टेशन के लिए एक घटना हैंडलर खोज को दबा देती है और इसके सभी अभिभावकों को प्रतिस्थापित करती है। मान लीजिए कि हमारे पास एक मूल राज्य A है जिसमें std :: abort () संदेश M पर कहा जाता है। और बी का एक बच्चा राज्य है जिसमें एम को सुरक्षित रूप से अनदेखा किया जा सकता है। हमें M को B के स्थान पर प्रतिक्रिया का निर्धारण करना चाहिए, क्योंकि यदि हम नहीं करते हैं, तो B के लिए हैंडलर A. में मिल जाएगा। इसलिए, हमें कुछ लिखने की आवश्यकता होगी:
 void so_define_agent() override { A.event([](mhood_t<M>) { std::abort(); }); ... B.event([](mhood_t<M>) {}); //    ,      //   M   . ... } 

दमन () विधि आपको इस स्थिति को कोड में अधिक स्पष्ट रूप से और ग्राफिक रूप से लिखने की अनुमति देती है:
 void so_define_agent() override { A.event([](mhood_t<M>) { std::abort(); }); ... B.suppress<M>(); //    ,      //   M   . ... } 

बहुत सरल उदाहरण है


सोबिजाइज़र v.5.5 के मानक उदाहरणों में एक सरल उदाहरण शामिल है, ब्लिंकिंग_ल्ड , जो एक निमिष एलईडी संकेतक के संचालन को अनुकरण करता है। इस उदाहरण से एजेंट स्टेट आरेख निम्नानुसार है:



और यहाँ इस उदाहरण से पूर्ण एजेंट कोड है:
 class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ ctx } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1250}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } }; 

यहाँ, सभी वास्तविक कार्य I / O हैंडलर के अंदर ब्लिंक_ऑन स्टेट के लिए किया जाता है। ठीक है, प्लस, ब्लिंक_ऑन और ब्लिंक_ऑफ में रहने की अवधि को सीमित करता है।

बहुत सरल उदाहरण नहीं है


SObjectizer v.5.5 के मानक उदाहरणों में एक बहुत अधिक जटिल उदाहरण, इंटरकॉम_स्टेटेट भी शामिल है, जो डोरफोन पैनल के व्यवहार की नकल करता है। और इस उदाहरण में मुख्य एजेंट का राज्य आरेख कुछ इस तरह दिखता है:



सब कुछ इतना कठोर है क्योंकि यह नकल न केवल एक अपार्टमेंट को नंबर से कॉल करने का समर्थन करता है, बल्कि प्रत्येक अपार्टमेंट के लिए एक अद्वितीय गुप्त कोड के साथ-साथ एक विशेष सेवा कोड भी है। ये कोड आपको कहीं भी डायल किए बिना दरवाजे का ताला खोलने की अनुमति देते हैं।

इस उदाहरण में अभी भी दिलचस्प चीजें हैं। लेकिन विस्तार से वर्णित किया जाना बहुत बड़ा है (यहां तक ​​कि इसके लिए एक अलग लेख भी पर्याप्त नहीं हो सकता है)। इसलिए यदि आप रुचि रखते हैं कि वास्तव में आईसीएबी एसओबीजेलाइज़र में कितना जटिल है, तो आप इस उदाहरण में देख सकते हैं। और अगर कुछ स्पष्ट नहीं है, तो आप हमसे एक सवाल पूछ सकते हैं। उदाहरण के लिए, इस लेख की टिप्पणियों में।

क्या यह संभव है कि SObjectizer-5 में निर्मित अंतरिक्ष यान के लिए समर्थन का उपयोग न करें?


तो, SObjectizer-5 ने ICA के लिए बहुत ही विस्तृत समर्थित सुविधाओं के साथ अंतर्निहित समर्थन किया है। यह समर्थन, निश्चित रूप से, इसका उपयोग करने के लिए किया जाता है। विशेष रूप से, संदेश वितरण ट्रेसिंग जैसे सोबिजाइज़र के डिबगिंग तंत्र एजेंट की स्थिति से अवगत होते हैं और अपने संबंधित डिबग संदेशों में वर्तमान स्थिति प्रदर्शित करते हैं।

फिर भी, अगर डेवलपर बिल्ट-इन SObjectizer-5 टूल का उपयोग करने के लिए किसी कारण से नहीं चाहता है, तो वह ऐसा नहीं कर सकता है।

उदाहरण के लिए, आप SObjectizer state_t और उसके जैसे अन्य लोगों का उपयोग करने से इंकार कर सकते हैं क्योंकि State_t std :: string, std के एक जोड़े :: फ़ंक्शन, std जैसे कई काउंटरों के साथ एक बहुत भारी वस्तु है :: size_t, विभिन्न वस्तुओं और कुछ अन्य तिपहिया के लिए पाँच संकेत। एक साथ, यह 64-बिट लिनक्स और जीसीसी-5.5 पर, उदाहरण के लिए, प्रति स्टेट_t 160 बाइट्स देता है (इसके अलावा जो डायनामिक मेमोरी में आवंटित किया जा सकता है)।

यदि आपको आवश्यकता है, तो कहें, कि आवेदन में एक लाख एजेंट, जिनमें से प्रत्येक के पास 10 राज्य होंगे, तो SObjectizer State_t का ओवरहेड स्वीकार्य नहीं हो सकता है। इस स्थिति में, आप राज्य मशीनों के साथ काम करने के लिए कुछ अन्य तंत्र का उपयोग कर सकते हैं, मैन्युअल रूप से इस तंत्र को संदेश प्रसंस्करण सौंप सकते हैं। कुछ इस तरह:
 class external_fsm_demo : public so_5::agent_t { some_fsm_type my_fsm_; ... void so_define_agent() override { so_subscribe_self() .event([this](mhood_t<msg_one> cmd) { my_fsm_.handle(*cmd); }) .event([this](mhood_t<msg_two> cmd) { my_fsm_.handle(*cmd); }) .event([this](mhood_t<msg_three> cmd) { my_fsm_.handle(*cmd); }); ... } ... }; 

इस मामले में, आप मैन्युअल काम की मात्रा और SObjectizer के डिबगिंग तंत्र से मदद की कमी को बढ़ाकर दक्षता के लिए भुगतान कर रहे हैं। लेकिन यहां यह तय करना डेवलपर पर निर्भर है।

निष्कर्ष


यह लेख स्वैच्छिक रूप से निकला, मूल रूप से योजनाबद्ध की तुलना में बहुत अधिक है। इस जगह पर पढ़ने वाले सभी को धन्यवाद। यदि पाठकों में से एक ने लेख पर टिप्पणियों में आपकी प्रतिक्रिया को छोड़ना संभव माना, तो यह बहुत अच्छा होगा।

यदि कुछ अस्पष्ट रहता है, तो प्रश्न पूछें, हम आनंद के साथ उत्तर देंगे।

इसके अलावा, इस अवसर को लेते हुए, मैं उन लोगों का ध्यान आकर्षित करना चाहता हूं, जो SObjectizer में रुचि रखते हैं, शाखा 5.5 के ढांचे में SObjectizer के अगले संस्करण पर काम शुरू हो गया है। संक्षेप में 5.5.23 में कार्यान्वयन के लिए क्या माना जाता है, इसके बारे में यहां वर्णित हैअधिक पूरी तरह से, लेकिन अंग्रेजी में, यहांआप कार्यान्वयन के लिए प्रस्तावित किसी भी सुविधा के बारे में अपनी राय छोड़ सकते हैं, या कुछ और पेश कर सकते हैं। यानीSObjectizer के विकास को प्रभावित करने का एक वास्तविक अवसर है। इसके अलावा, v.5.5.23 की रिहाई के बाद, SObjectizer पर काम में विराम लग सकता है और 2018 में कुछ उपयोगी को शामिल करने का अगला अवसर SObjectizer संभव नहीं हो सकता है।

Source: https://habr.com/ru/post/hi423497/


All Articles