تمامًا مثل السنجاب، وهو حيوان صغير ورشيق وذكي ومتنبه ولطيف ، تهدف مؤسسة السنجاب إلى توفير تطبيق آلة حالة Java خفيف الوزن ومرن للغاية وقابل للتوسيع وقابل للتشخيص وسهل الاستخدام وآمن للاستخدام في المؤسسات.
فيما يلي مخطط جهاز الحالة الذي يصف تغيير حالة ماكينة الصراف الآلي:
يمكن العثور على نموذج التعليمات البرمجية في الحزمة "org.squirrelframework.foundation.fsm.atm" .
تم نشر squirrel-foundation في المستودع المركزي المخضرم، لذلك تحتاج فقط إلى إضافة التبعية التالية إلى ملف pom.xml.
أحدث نسخة تم إصدارها:
<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.10</version>
</dependency>
أحدث نسخة لقطة:
<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.11-SNAPSHOT</version>
</dependency>
لتجربة وظائف آلة حالة السنجاب بسرعة، يرجى إنشاء مشروع مخضرم وتضمين تبعية أساس السنجاب بشكل صحيح. ثم قم فقط بتشغيل نموذج التعليمات البرمجية التالي.
public class QuickStartSample {
// 1. Define State Machine Event
enum FSMEvent {
ToA , ToB , ToC , ToD
}
// 2. Define State Machine Class
@ StateMachineParameters ( stateType = String . class , eventType = FSMEvent . class , contextType = Integer . class )
static class StateMachineSample extends AbstractUntypedStateMachine {
protected void fromAToB ( String from , String to , FSMEvent event , Integer context ) {
System . out . println ( "Transition from '" + from + "' to '" + to + "' on event '" + event +
"' with context '" + context + "'." );
}
protected void ontoB ( String from , String to , FSMEvent event , Integer context ) {
System . out . println ( "Entry State ' " + to + " ' ." );
}
}
public static void main ( String [] args ) {
// 3. Build State Transitions
UntypedStateMachineBuilder builder = StateMachineBuilderFactory . create ( StateMachineSample . class );
builder . externalTransition (). from ( "A" ). to ( "B" ). on ( FSMEvent . ToB ). callMethod ( "fromAToB" );
builder . onEntry ( "B" ). callMethod ( "ontoB" );
// 4. Use State Machine
UntypedStateMachine fsm = builder . newStateMachine ( "A" );
fsm . fire ( FSMEvent . ToB , 10 );
System . out . println ( "Current state is " + fsm . getCurrentState ());
}
}
قد يكون لديك الآن العديد من الأسئلة حول نموذج التعليمات البرمجية، يرجى التحلي بالصبر. سيجيب دليل المستخدم التالي على معظم أسئلتك. ولكن قبل الدخول في التفاصيل، يتطلب الأمر أن يكون لديك فهم أساسي لمفاهيم آلة الحالة. هذه المواد جيدة لفهم مفاهيم آلة الحالة. [مخططات حالة الآلة] [آلة حالة كيو تي]
يدعم squirrel-foundation كلاً من واجهة برمجة التطبيقات (API) بطلاقة والطريقة التصريحية للإعلان عن جهاز الحالة، كما يمكّن المستخدم من تحديد أساليب العمل بطريقة مباشرة.
تأخذ واجهة StateMachine أربعة معلمات نوع عامة.
منشئ آلة الدولة
من أجل إنشاء آلة حالة، يحتاج المستخدم إلى إنشاء منشئ آلة الحالة أولاً. على سبيل المثال:
StateMachineBuilder < MyStateMachine , MyState , MyEvent , MyContext > builder =
StateMachineBuilderFactory . create ( MyStateMachine . class , MyState . class , MyEvent . class , MyContext . class );
يأخذ منشئ آلة الحالة المعلمات التي هي نوع آلة الحالة (T)، والحالة (S)، والحدث (E)، والسياق (C).
واجهة برمجة التطبيقات بطلاقة
بعد إنشاء أداة إنشاء آلة الحالة، يمكننا استخدام واجهة برمجة التطبيقات (API) بطلاقة لتحديد الحالة/الانتقال/الإجراء الخاص بجهاز الحالة.
builder . externalTransition (). from ( MyState . A ). to ( MyState . B ). on ( MyEvent . GoToB );
يتم إنشاء انتقال خارجي بين الحالة "A" إلى الحالة "B" ويتم تشغيله عند الحدث المستلم "GoToB".
builder . internalTransition ( TransitionPriority . HIGH ). within ( MyState . A ). on ( MyEvent . WithinA ). perform ( myAction );
يتم إنشاء انتقال داخلي مع تعيين الأولوية على عالية داخل الحالة "A" في الحدث "WithinA" الذي يؤدي "myAction". ويعني الانتقال الداخلي أنه بعد اكتمال الانتقال، لا يتم الخروج أو الدخول إلى أي حالة. يتم استخدام أولوية النقل لتجاوز الانتقال الأصلي عند تمديد آلة الحالة.
builder . externalTransition (). from ( MyState . C ). to ( MyState . D ). on ( MyEvent . GoToD ). when (
new Condition < MyContext >() {
@ Override
public boolean isSatisfied ( MyContext context ) {
return context != null && context . getValue ()> 80 ;
}
@ Override
public String name () {
return "MyCondition" ;
}
}). callMethod ( "myInternalTransitionCall" );
يتم إنشاء انتقال مشروط من الحالة "C" إلى الحالة "D" في الحدث "GoToD" عندما يفي السياق الخارجي بتقييد الشرط، ثم يستدعي أسلوب الإجراء "myInternalTransitionCall". يمكن للمستخدم أيضًا استخدام MVEL (لغة تعبير قوية) لوصف الحالة بالطريقة التالية.
builder . externalTransition (). from ( MyState . C ). to ( MyState . D ). on ( MyEvent . GoToD ). whenMvel (
"MyCondition:::(context!=null && context.getValue()>80)" ). callMethod ( "myInternalTransitionCall" );
ملاحظة: يتم استخدام الأحرف ':::' للفصل بين اسم الشرط وتعبير الشرط. "السياق" هو نقطة المتغير المحددة مسبقًا لكائن السياق الحالي.
builder . onEntry ( MyState . A ). perform ( Lists . newArrayList ( action1 , action2 ))
يتم تعريف قائمة إجراءات إدخال الحالة في نموذج التعليمات البرمجية أعلاه.
طريقة استدعاء الإجراء
يمكن للمستخدم تحديد إجراءات مجهولة أثناء تحديد التحولات أو دخول/خروج الحالة. ومع ذلك، سوف يكون رمز الإجراء متناثرًا في العديد من الأماكن مما قد يجعل من الصعب الحفاظ على الكود. علاوة على ذلك، لا يمكن للمستخدم الآخر تجاوز الإجراءات. لذلك يدعم squirrel-foundation أيضًا تحديد إجراء استدعاء أسلوب آلة الحالة والذي يأتي مع فئة آلة الحالة نفسها.
StateMachineBuilder <...> builder = StateMachineBuilderFactory . create (
MyStateMachine . class , MyState . class , MyEvent . class , MyContext . class );
builder . externalTransition (). from ( A ). to ( B ). on ( toB ). callMethod ( "fromAToB" );
// All transition action method stays with state machine class
public class MyStateMachine <...> extends AbstractStateMachine <...> {
protected void fromAToB ( MyState from , MyState to , MyEvent event , MyContext context ) {
// this method will be called during transition from "A" to "B" on event "toB"
// the action method parameters types and order should match
...
}
}
علاوة على ذلك، يدعم squirrel-foundation أيضًا تحديد إجراءات استدعاء الأسلوب بطريقة الاتفاقية عبر التكوين . في الأساس، هذا يعني أنه إذا كانت الطريقة المعلنة في آلة الحالة تفي باصطلاح التسمية والمعلمات، فسيتم إضافتها إلى قائمة إجراءات النقل وسيتم استدعاؤها أيضًا في مرحلة معينة. على سبيل المثال
protected void transitFromAToBOnGoToB ( MyState from , MyState to , MyEvent event , MyContext context )
ستتم إضافة الطريقة المسماة باسم TransitFrom[SourceStateName]To[TargetStateName]On[EventName] والمعلمات كـ [MyState, MyState, MyEvent, MyContext] إلى قائمة إجراءات النقل "A-(GoToB)->B". عند الانتقال من الحالة "A" إلى الحالة "B" في الحدث "GoToB"، سيتم استدعاء هذه الطريقة.
protected void transitFromAnyToBOnGoToB ( MyState from , MyState to , MyEvent event , MyContext context )
TransitFromAnyTo[TargetStateName]On[EventName] سيتم استدعاء الطريقة عند النقل من أي حالة إلى الحالة 'B' في الحدث 'GoToB'.
protected void exitA ( MyState from , MyState to , MyEvent event , MyContext context )
Exit[StateName] سيتم استدعاء الطريقة عند الخروج من الحالة 'A'. لذا فإن الإدخال[StateName] و beforeExitAny / afterExitAny و beforeEntryAny / afterEntryAny .
أنماط التسمية المدعومة الأخرى:
transitFrom[fromStateName]To[toStateName]On[eventName]When[conditionName]
transitFrom[fromStateName]To[toStateName]On[eventName]
transitFromAnyTo[toStateName]On[eventName]
transitFrom[fromStateName]ToAnyOn[eventName]
transitFrom[fromStateName]To[toStateName]
on[eventName]
توفر اصطلاحات الطريقة المذكورة أعلاه أيضًا وظائف تشبه AOP ، والتي توفر إمكانية تمديد مرنة مدمجة لآلة الحالة السنجابية بأي تفصيل. لمزيد من المعلومات، يرجى الرجوع إلى حالة الاختبار " org.squirrelframework.foundation.fsm.ExtensionMethodCallTest ". منذ 0.3.1، هناك طريقة أخرى لتحديد طرق الامتداد المشابهة لـ AOP والتي تتم من خلال واجهة برمجة التطبيقات (API) بطلاقة (شكرًا لاقتراح vittali)، على سبيل المثال
// since 0.3.1
// the same effect as add method transitFromAnyToCOnToC in your state machine
builder . transit (). fromAny (). to ( "C" ). on ( "ToC" ). callMethod ( "fromAnyToC" );
// the same effect as add method transitFromBToAnyOnToC in your state machine
builder . transit (). from ( "B" ). toAny (). on ( "ToC" ). callMethod ( "fromBToAny" );
// the same effect as add method transitFromBToAny in your state machine
builder . transit (). from ( "B" ). toAny (). onAny (). callMethod ( "fromBToAny" );
أو من خلال الشرح التعريفي، على سبيل المثال
// since 0.3.1
@ Transitions ({
@ Transit ( from = "B" , to = "E" , on = "*" , callMethod = "fromBToEOnAny" ),
@ Transit ( from = "*" , to = "E" , on = "ToE" , callMethod = "fromAnyToEOnToE" )
})
ملحوظة : سيتم إرفاق طرق العمل هذه بالانتقالات المطابقة والموجودة بالفعل ولكن ليس لإنشاء أي انتقالات جديدة. منذ 0.3.4، يمكن أيضًا تعريف التحولات المتعددة مرة واحدة في كل مرة باستخدام واجهة برمجة التطبيقات التالية، على سبيل المثال
// transitions(A->B@A2B=>a2b, A->C@A2C=>a2c, A->D@A2D) will be defined at once
builder . transitions (). from ( State . _A ). toAmong ( State . B , State . C , State . D ).
onEach ( Event . A2B , Event . A2C , Event . A2D ). callMethod ( "a2b|a2c|_" );
// transitions(A->_A@A2ANY=>DecisionMaker, _A->A@ANY2A) will be defined at once
builder . localTransitions (). between ( State . A ). and ( State . _A ).
onMutual ( Event . A2ANY , Event . ANY2A ).
perform ( Lists . newArrayList ( new DecisionMaker ( "SomeLocalState" ), null ) );
يمكن العثور على مزيد من المعلومات في org.squirrelframework.foundation.fsm.samples.DecisionStateSampleTest ؛
الشرح التصريحي
يتم أيضًا توفير طريقة تعريفية لتعريف آلة الحالة وتوسيعها أيضًا. هنا مثال.
@ States ({
@ State ( name = "A" , entryCallMethod = "entryStateA" , exitCallMethod = "exitStateA" ),
@ State ( name = "B" , entryCallMethod = "entryStateB" , exitCallMethod = "exitStateB" )
})
@ Transitions ({
@ Transit ( from = "A" , to = "B" , on = "GoToB" , callMethod = "stateAToStateBOnGotoB" ),
@ Transit ( from = "A" , to = "A" , on = "WithinA" , callMethod = "stateAToStateAOnWithinA" , type = TransitionType . INTERNAL )
})
interface MyStateMachine extends StateMachine < MyStateMachine , MyState , MyEvent , MyContext > {
void entryStateA ( MyState from , MyState to , MyEvent event , MyContext context );
void stateAToStateBOnGotoB ( MyState from , MyState to , MyEvent event , MyContext context )
void stateAToStateAOnWithinA ( MyState from , MyState to , MyEvent event , MyContext context )
void exitStateA ( MyState from , MyState to , MyEvent event , MyContext context );
...
}
يمكن تعريف التعليق التوضيحي في كل من فئة التنفيذ لجهاز الحالة أو أي واجهة سيتم تنفيذ جهاز الحالة فيها. يمكن أيضًا استخدامه ممزوجًا بواجهة برمجة التطبيقات (API) بطلاقة، مما يعني أنه يمكن أيضًا توسيع جهاز الحالة المحدد في واجهة برمجة التطبيقات (API) بطلاقة من خلال هذه التعليقات التوضيحية. (شيء واحد قد تحتاج إلى ملاحظته، وهو أن الطريقة المحددة داخل الواجهة يجب أن تكون عامة، مما يعني أيضًا أن تنفيذ إجراء استدعاء الطريقة سيكون عامًا للمتصل.)
المحولات
من أجل الإعلان عن الحالة والحدث داخل @State و @Transit ، يحتاج المستخدم إلى تنفيذ المحولات المقابلة لنوع الحالة (S) والحدث (E). يجب أن يقوم التحويل بتطبيق واجهة Converter<T>، والتي تقوم بتحويل الحالة/الحدث إلى/من السلسلة.
public interface Converter < T > extends SquirrelComponent {
/**
* Convert object to string.
* @param obj converted object
* @return string description of object
*/
String convertToString ( T obj );
/**
* Convert string to object.
* @param name name of the object
* @return converted object
*/
T convertFromString ( String name );
}
ثم قم بتسجيل هذه المحولات في ConverterProvider . على سبيل المثال
ConverterProvider . INSTANCE . register ( MyEvent . class , new MyEventConverter ());
ConverterProvider . INSTANCE . register ( MyState . class , new MyStateConverter ());
ملاحظة: إذا كنت تستخدم واجهة برمجة التطبيقات (API) بطلاقة فقط لتعريف آلة الحالة، فلن تكون هناك حاجة إلى تنفيذ المحولات المقابلة. وأيضًا إذا كانت فئة الحدث أو الحالة من نوع سلسلة أو تعداد، فلن تحتاج إلى تنفيذ محول أو تسجيله بشكل صريح في معظم الحالات.
مثيل آلة الحالة الجديدة
بعد أن يحدد المستخدم سلوك آلة الحالة، يمكن للمستخدم إنشاء مثيل آلة حالة جديد من خلال المُنشئ. لاحظ أنه بمجرد إنشاء مثيل آلة الحالة من المنشئ، لا يمكن استخدام المنشئ لتحديد أي عنصر جديد في آلة الحالة بعد الآن.
T newStateMachine ( S initialStateId , Object ... extraParams );
لإنشاء مثيل جهاز حالة جديد من منشئ جهاز الحالة، تحتاج إلى تمرير المعلمات التالية.
initialStateId
: عند البدء، الحالة الأولية لجهاز الحالة.
extraParams
: المعلمات الإضافية اللازمة لإنشاء مثيل جهاز حالة جديد. اضبط على "كائن جديد [0]" لعدم الحاجة إلى معلمات إضافية.
أ. إذا مرر المستخدم معلمات إضافية أثناء إنشاء مثيل جهاز حالة جديد، فيرجى التأكد من أن StateMachineBuilderFactory قام أيضًا بتحديد نوع المعلمات الإضافية عند إنشاء منشئ جهاز الحالة. وإلا، سيتم تجاهل المعلمة الإضافية. ب. يمكن تمرير المعلمات الإضافية إلى مثيل جهاز الحالة بطريقتين. أحدهما من خلال مُنشئ آلة الحالة مما يعني أن المستخدم بحاجة إلى تحديد مُنشئ بنفس نوع المعلمات وترتيبها لمثيل آلة الحالة. هناك طريقة أخرى وهي تحديد طريقة تسمى postConstruct
وأيضًا بنفس نوع المعلمات وترتيبها.
إذا لم تكن هناك حاجة لتمرير أي معلمات إضافية إلى جهاز الحالة، فيمكن للمستخدم ببساطة الاتصال T newStateMachine(S initialStateId)
لإنشاء مثيل جهاز حالة جديد.
آلة حالة جديدة من منشئ آلة الدولة. (في هذه الحالة، لا يلزم تمرير أي معلمات إضافية.)
MyStateMachine stateMachine = builder . newStateMachine ( MyState . Initial );
تحريك التحولات
بعد إنشاء آلة الحالة، يمكن للمستخدم إطلاق الأحداث مع السياق لبدء الانتقال داخل آلة الحالة. على سبيل المثال
stateMachine . fire ( MyEvent . Prepare , new MyContext ( "Testing" ));
آلة الدولة غير المكتوبة
من أجل تبسيط استخدام جهاز الحالة، وتجنب وجود عدد كبير جدًا من الأنواع العامة (مثل StateMachine<T, S, E, C>) التي قد تجعل قراءة التعليمات البرمجية صعبة في بعض الحالات، ولكنها لا تزال تحتفظ بجزء مهم من ميزة أمان النوع في إجراء النقل التنفيذ، تم تنفيذ UntypedStateMachine لهذا الغرض.
enum TestEvent {
toA , toB , toC , toD
}
@ Transitions ({
@ Transit ( from = "A" , to = "B" , on = "toB" , callMethod = "fromAToB" ),
@ Transit ( from = "B" , to = "C" , on = "toC" ),
@ Transit ( from = "C" , to = "D" , on = "toD" )
})
@ StateMachineParameters ( stateType = String . class , eventType = TestEvent . class , contextType = Integer . class )
class UntypedStateMachineSample extends AbstractUntypedStateMachine {
// No need to specify constructor anymore since 0.2.9
// protected UntypedStateMachineSample(ImmutableUntypedState initialState,
// Map<Object, ImmutableUntypedState> states) {
// super(initialState, states);
// }
protected void fromAToB ( String from , String to , TestEvent event , Integer context ) {
// transition action still type safe ...
}
protected void transitFromDToAOntoA ( String from , String to , TestEvent event , Integer context ) {
// transition action still type safe ...
}
}
UntypedStateMachineBuilder builder = StateMachineBuilderFactory . create (
UntypedStateMachineSample . class );
// state machine builder not type safe anymore
builder . externalTransition (). from ( "D" ). to ( "A" ). on ( TestEvent . toA );
UntypedStateMachine fsm = builder . newStateMachine ( "A" );
لإنشاء UntypedStateMachine، يحتاج المستخدم إلى إنشاء UntypedStateMachineBuilder من خلال StateMachineBuilderFactory أولاً. يأخذ StateMachineBuilderFactory معلمة واحدة فقط وهي نوع فئة آلة الحالة لإنشاء UntypedStateMachineBuilder. يتم استخدام @StateMachineParameters للإعلان عن أنواع المعلمات العامة لجهاز الحالة. AbstractUntypedStateMachine هي الفئة الأساسية لأي جهاز حالة غير مكتوب.
آلة الدولة غير الحساسة للسياق
في بعض الأحيان، لا يهتم انتقال الحالة بالسياق، مما يعني أن التحول يتم تحديده في الغالب حسب الحدث فقط. في هذه الحالة، يمكن للمستخدم استخدام آلة الحالة غير الحساسة للسياق لتبسيط معلمات استدعاء الطريقة. إن إعلان أن آلة الحالة غير حساسة للسياق أمر بسيط للغاية. يحتاج المستخدم فقط إلى إضافة تعليق توضيحي @ContextInsensitive في فئة تنفيذ آلة الحالة. بعد ذلك، يمكن تجاهل معلمة السياق في قائمة معلمات طريقة النقل. على سبيل المثال
@ ContextInsensitive
public class ATMStateMachine extends AbstractStateMachine < ATMStateMachine , ATMState , String , Void > {
// no need to add context parameter here anymore
public void transitFromIdleToLoadingOnConnected ( ATMState from , ATMState to , String event ) {
...
}
public void entryLoading ( ATMState from , ATMState to , String event ) {
...
}
}
معالجة استثناءات الانتقال
عند حدوث استثناء أثناء انتقال الحالة، سيتم إلغاء قائمة الإجراءات المنفذة وستدخل آلة الحالة في حالة الخطأ، مما يعني أن مثيل آلة الحالة لا يمكنه معالجة الحدث بعد الآن. إذا استمر المستخدم في إطلاق الحدث إلى مثيل جهاز الحالة، فسيتم طرح IllegalStateException. سيتم تغليف كل الاستثناءات التي حدثت أثناء مرحلة الانتقال بما في ذلك تنفيذ الإجراء واستدعاء المستمع الخارجي في TransitionException (استثناء لم يتم التحقق منه). في الوقت الحالي، تعد استراتيجية معالجة الاستثناء الافتراضية بسيطة وغير مهذبة من خلال الاستمرار في التخلص من الاستثناء، راجع طريقة AbstractStateMachine.afterTransitionCausedException.
protected void afterTransitionCausedException (...) { throw e ; }
إذا كان من الممكن استرداد جهاز الحالة من هذا الاستثناء، فيمكن للمستخدم تمديد طريقة afterTransitionCausedException وإضافة منطق الاسترداد المقابل في هذه الطريقة. لا تنس إعادة حالة جهاز الحالة إلى وضعها الطبيعي في النهاية. على سبيل المثال
@ Override
protected void afterTransitionCausedException ( Object fromState , Object toState , Object event , Object context ) {
Throwable targeException = getLastException (). getTargetException ();
// recover from IllegalArgumentException thrown out from state 'A' to 'B' caused by event 'ToB'
if ( targeException instanceof IllegalArgumentException &&
fromState . equals ( "A" ) && toState . equals ( "B" ) && event . equals ( "ToB" )) {
// do some error clean up job here
// ...
// after recovered from this exception, reset the state machine status back to normal
setStatus ( StateMachineStatus . IDLE );
} else if (...) {
// recover from other exception ...
} else {
super . afterTransitionCausedException ( fromState , toState , event , context );
}
}
تعريف الحالة الهرمية
قد تحتوي الحالة الهرمية على حالة متداخلة. قد يكون لدى الحالات الفرعية نفسها أطفال متداخلون وقد يستمر التداخل إلى أي عمق. عندما تكون الحالة الهرمية نشطة، تكون حالة واحدة فقط من الحالات التابعة لها نشطة. يمكن تعريف الحالة الهرمية من خلال واجهة برمجة التطبيقات (API) أو التعليق التوضيحي.
void defineSequentialStatesOn ( S parentStateId , S ... childStateIds );
يعرّف builder.defineSequentialStatesOn(State.A, State.BinA, StateCinA) حالتين فرعيتين "BinA" و"CinA" ضمن الحالة الأصلية "A"، وستكون الحالة الفرعية المحددة الأولى أيضًا هي الحالة الأولية للحالة الهرمية "A" . يمكن أيضًا تعريف نفس الحالة الهرمية من خلال التعليقات التوضيحية، على سبيل المثال
@ States ({
@ State ( name = "A" , entryMethodCall = "entryA" , exitMethodCall = "exitA" ),
@ State ( parent = "A" , name = "BinA" , entryMethodCall = "entryBinA" , exitMethodCall = "exitBinA" , initialState = true ),
@ State ( parent = "A" , name = "CinA" , entryMethodCall = "entryCinA" , exitMethodCall = "exitCinA" )
})
تعريف الدولة الموازية
تتضمن الحالة الموازية مجموعة من الحالات الفرعية التي تكون نشطة في نفس الوقت عندما يكون العنصر الأصلي نشطًا. يمكن تعريف الحالة الموازية من خلال واجهة برمجة التطبيقات (API) أو كليهما. على سبيل المثال
// defines two region states "RegionState1" and "RegionState2" under parent parallel state "Root"
builder . defineParallelStatesOn ( MyState . Root , MyState . RegionState1 , MyState . RegionState2 );
builder . defineSequentialStatesOn ( MyState . RegionState1 , MyState . State11 , MyState . State12 );
builder . externalTransition (). from ( MyState . State11 ). to ( MyState . State12 ). on ( MyEvent . Event1 );
builder . defineSequentialStatesOn ( MyState . RegionState2 , MyState . State21 , MyState . State22 );
builder . externalTransition (). from ( MyState . State21 ). to ( MyState . State22 ). on ( MyEvent . Event2 );
أو
@ States ({
@ State ( name = "Root" , entryCallMethod = "enterRoot" , exitCallMethod = "exitRoot" , compositeType = StateCompositeType . PARALLEL ),
@ State ( parent = "Root" , name = "RegionState1" , entryCallMethod = "enterRegionState1" , exitCallMethod = "exitRegionState1" ),
@ State ( parent = "Root" , name = "RegionState2" , entryCallMethod = "enterRegionState2" , exitCallMethod = "exitRegionState2" )
})
للحصول على الحالات الفرعية الحالية للدولة الموازية
stateMachine . getSubStatesOn ( MyState . Root ); // return list of current sub states of parallel state
عندما تصل جميع الحالات المتوازية إلى الحالة النهائية، سيتم إطلاق حدث سياق النهاية .
تحديد حدث السياق
حدث السياق يعني أن الحدث المحدد من قبل المستخدم له سياق محدد مسبقًا في جهاز الحالة. حددت مؤسسة السنجاب ثلاثة أنواع من أحداث السياق لحالة استخدام مختلفة. حدث البدء/الإنهاء : سيتم استخدام الحدث الذي تم الإعلان عنه كحدث بدء/إنهاء عند بدء/إنهاء جهاز الحالة. لذلك يمكن للمستخدم التمييز بين مشغل الإجراء الذي تم استدعاؤه، على سبيل المثال، عندما تبدأ آلة الحالة وتدخل حالتها الأولية، يمكن للمستخدم التمييز بين إجراء إدخال الحالة الذي تم استدعاؤه بواسطة حدث البداية. حدث النهاية : عندما تصل جميع الحالات المتوازية إلى الحالة النهائية، سيتم إطلاق حدث النهاية تلقائيًا. يمكن للمستخدم تحديد الانتقال التالي بناءً على حدث النهاية. لتحديد حدث السياق، لدى المستخدم اتجاهين، واجهة برمجة تطبيقات للتعليق التوضيحي أو المنشئ.
@ ContextEvent ( finishEvent = "Finish" )
static class ParallelStateMachine extends AbstractStateMachine <...> {
}
أو
StateMachineBuilder <...> builder = StateMachineBuilderFactory . create (...);
...
builder . defineFinishEvent ( HEvent . Start );
builder . defineTerminateEvent ( HEvent . Terminate );
builder . defineStartEvent ( HEvent . Finish );
استخدام حالات التاريخ لحفظ الحالة الحالية واستعادتها
تسمح حالة التاريخ الزائفة لجهاز الحالة بتذكر تكوين حالته. سيؤدي الانتقال الذي يتخذ حالة السجل كهدف له إلى إرجاع آلة الحالة إلى هذا التكوين المسجل. إذا كان "نوع" السجل "سطحيًا"، فيجب على معالج آلة الحالة تسجيل الأطفال النشطين المباشرين لوالده قبل إجراء أي انتقال يخرج من الأصل. إذا كان "نوع" السجل "عميقًا"، فيجب على معالج آلة الحالة تسجيل جميع الأحفاد النشطين للأصل قبل إجراء أي انتقال يخرج من الأصل. يتم دعم كل من واجهة برمجة التطبيقات (API) والتعليق التوضيحي لتحديد نوع سجل الحالة. على سبيل المثال
// defined history type of state "A" as "deep"
builder . defineSequentialStatesOn ( MyState . A , HistoryType . DEEP , MyState . A1 , MyState . A2 )
أو
@ State ( parent = "A" , name = "A1" , entryCallMethod = "enterA1" , exitCallMethod = "exitA1" , historyType = HistoryType . DEEP )
ملاحظة: قبل الإصدار 0.3.7، يحتاج المستخدم إلى تحديد "HistoryType.DEEP" لكل مستوى من مستويات الحالة التاريخية، وهو أمر غير مناسب تمامًا. (شكرًا لشركة Voskuijlen لتوفير الحل للمشكلة 33). الآن يقوم المستخدم فقط بتعريف "HistoryType.DEEP" في المستوى الأعلى للحالة التاريخية، وسيتم تذكر كافة المعلومات التاريخية التي تذكرها.
أنواع الانتقال
وفقا لمواصفات UML، قد يكون الانتقال أحد هذه الأنواع الثلاثة:
- الانتقال الداخلي يعني أن الانتقال، إذا تم تشغيله، يحدث دون الخروج من الحالة المصدر أو الدخول إليها (أي أنه لا يتسبب في تغيير الحالة). وهذا يعني أنه لن يتم الاحتجاج بشرط الدخول أو الخروج لدولة المصدر. يمكن إجراء انتقال داخلي حتى لو كان StateMachine موجودًا في منطقة واحدة أو أكثر متداخلة داخل الولاية المرتبطة.
- يشير الانتقال المحلي إلى أن الانتقال، في حالة تشغيله، لن يخرج من الحالة المركبة (المصدر)، ولكنه سيخرج ويعيد الدخول إلى أي حالة داخل الحالة المركبة الموجودة في تكوين الحالة الحالية.
- الانتقال الخارجي يعني ضمناً أن الانتقال، في حالة تفعيله، سيخرج من الحالة المركبة (المصدر).
يدعم squirrel-foundation كلاً من واجهة برمجة التطبيقات (API) والتعليقات التوضيحية للإعلان عن جميع أنواع التحولات، على سبيل المثال
builder . externalTransition (). from ( MyState . A ). to ( MyState . B ). on ( MyEvent . A2B );
builder . internalTransition (). within ( MyState . A ). on ( MyEvent . innerA );
builder . localTransition (). from ( MyState . A ). to ( MyState . CinA ). on ( MyEvent . intoC )
أو
@ Transitions ({
@ Transition ( from = "A" , to = "B" , on = "A2B" ), //default value of transition type is EXTERNAL
@ Transition ( from = "A" , on = "innerA" , type = TransitionType . INTERNAL ),
@ Transition ( from = "A" , to = "CinA" , on = "intoC" , type = TransitionType . LOCAL ),
})
إرسال حدث تعدد الأشكال
خلال دورة حياة آلة الحالة، سيتم إطلاق أحداث مختلفة، على سبيل المثال
State Machine Lifecycle Events
|--StateMachineEvent /* Base event of all state machine event */
|--StartEvent /* Fired when state machine started */
|--TerminateEvent /* Fired when state machine terminated */
|--TransitionEvent /* Base event of all transition event */
|--TransitionBeginEvent /* Fired when transition began */
|--TransitionCompleteEvent /* Fired when transition completed */
|--TransitionExceptionEvent /* Fired when transition threw exception */
|--TransitionDeclinedEvent /* Fired when transition declined */
|--TransitionEndEvent /* Fired when transition end no matter declined or complete */
يمكن للمستخدم إضافة مستمع للاستماع إلى StateMachineEvent، مما يعني أن جميع الأحداث التي تم إطلاقها أثناء دورة حياة جهاز الحالة سيتم التقاطها بواسطة هذا المستمع، على سبيل المثال،
stateMachine . addStateMachineListener ( new StateMachineListener <...>() {
@ Override
public void stateMachineEvent ( StateMachineEvent <...> event ) {
// ...
}
});
ويمكن للمستخدم أيضًا إضافة مستمع للاستماع إلى TransitionEvent من خلال StateMachine.addTransitionListener، مما يعني أن جميع الأحداث التي يتم تشغيلها أثناء كل انتقال للحالة بما في ذلك TransitionBeginEvent وTransitionCompleteEvent وTransitionEndEvent سيتم اكتشافها بواسطة هذا المستمع. أو يمكن للمستخدم إضافة مستمع محدد، على سبيل المثال TransitionDeclinedListener للاستماع إلى TransitionDeclinedEvent عند رفض طلب النقل.
مستمع الحدث التعريفي
تؤدي إضافة مستمع الأحداث أعلاه إلى جهاز الحالة إلى إزعاج المستخدم في بعض الأحيان، كما أن الكثير من الأنواع العامة تجعل قراءة التعليمات البرمجية قبيحة. لتبسيط استخدام جهاز الحالة، والأهم من ذلك توفير تكامل غير جراحي، يوفر squirrel-foundation طريقة تعريفية لإضافة مستمع الحدث من خلال التعليقات التوضيحية التالية، على سبيل المثال
static class ExternalModule {
@ OnTransitionEnd
@ ListenerOrder ( 10 ) // Since 0.3.1 ListenerOrder can be used to insure listener invoked orderly
public void transitionEnd () {
// method annotated with TransitionEnd will be invoked when transition end...
// the method must be public and return nothing
}
@ OnTransitionBegin
public void transitionBegin ( TestEvent event ) {
// method annotated with TransitionBegin will be invoked when transition begin...
}
// 'event'(E), 'from'(S), 'to'(S), 'context'(C) and 'stateMachine'(T) can be used in MVEL scripts
@ OnTransitionBegin ( when = "event.name().equals( " toB " )" )
public void transitionBeginConditional () {
// method will be invoked when transition begin while transition caused by event "toB"
}
@ OnTransitionComplete
public void transitionComplete ( String from , String to , TestEvent event , Integer context ) {
// method annotated with TransitionComplete will be invoked when transition complete...
}
@ OnTransitionDecline
public void transitionDeclined ( String from , TestEvent event , Integer context ) {
// method annotated with TransitionDecline will be invoked when transition declined...
}
@ OnBeforeActionExecuted
public void onBeforeActionExecuted ( Object sourceState , Object targetState ,
Object event , Object context , int [] mOfN , Action <?, ?, ?,?> action ) {
// method annotated with OnAfterActionExecuted will be invoked before action invoked
}
@ OnAfterActionExecuted
public void onAfterActionExecuted ( Object sourceState , Object targetState ,
Object event , Object context , int [] mOfN , Action <?, ?, ?,?> action ) {
// method annotated with OnAfterActionExecuted will be invoked after action invoked
}
@ OnActionExecException
public void onActionExecException ( Action <?, ?, ?,?> action , TransitionException e ) {
// method annotated with OnActionExecException will be invoked when action thrown exception
}
}
ExternalModule externalModule = new ExternalModule ();
fsm . addDeclarativeListener ( externalModule );
...
fsm . removeDeclarativeListener ( externalModule );
من خلال القيام بذلك، لا يحتاج كود الوحدة الخارجية إلى تنفيذ أي واجهة مستمع لجهاز الحالة. قم فقط بإضافة القليل من التعليقات التوضيحية على الأساليب التي سيتم ربطها أثناء مرحلة الانتقال. معلمات الطريقة آمنة أيضًا، وسيتم استنتاجها تلقائيًا لمطابقة الحدث المقابل. وهذا نهج جيد لفصل المخاوف . يمكن للمستخدم العثور على نموذج للاستخدام في org.squirrelframework.foundation.fsm.StateMachineLogger .
طرق تمديد الانتقال
يحتوي كل حدث انتقالي أيضًا على طريقة تمديد مقابلة في فئة AbstractStateMachine والتي يُسمح بتوسيعها في فئة تنفيذ جهاز حالة العميل.
protected void afterTransitionCausedException ( Exception e , S fromState , S toState , E event , C context ) {
}
protected void beforeTransitionBegin ( S fromState , E event , C context ) {
}
protected void afterTransitionCompleted ( S fromState , S toState , E event , C context ) {
}
protected void afterTransitionEnd ( S fromState , S toState , E event , C context ) {
}
protected void afterTransitionDeclined ( S fromState , E event , C context ) {
}
protected void beforeActionInvoked ( S fromState , S toState , E event , C context ) {
}
عادةً، يمكن للمستخدم ربط منطق معالجة أعمالك بطرق الامتداد هذه أثناء كل انتقال للحالة، بينما يعمل مستمع الأحداث المتنوع كحدود لنظام التحكم القائم على آلة الحالة، والذي يمكنه التفاعل مع الوحدات الخارجية (مثل واجهة المستخدم والتدقيق وESB وما إلى ذلك) ). على سبيل المثال، يمكن للمستخدم توسيع الطريقة afterTransitionCausedException لتنظيف البيئة عند حدوث استثناء أثناء النقل، وكذلك إخطار وحدة واجهة المستخدم لعرض رسالة خطأ من خلال TransitionExceptionEvent.
العمل المرجح
يمكن للمستخدم تحديد وزن الإجراء لضبط ترتيب تنفيذ الإجراء. يتم ترتيب الإجراءات أثناء دخول/خروج الحالة وانتقال الحالة بترتيب تصاعدي وفقًا لقيمة وزنها. وزن العمل هو 0 بشكل افتراضي. لدى المستخدم طريقتان لضبط وزن العمل.
أحدهما هو إلحاق رقم الوزن باسم الطريقة وفصله بـ ":".
// define state entry action 'goEntryD' weight -150
@ State ( name = "D" , entryCallMethod = "goEntryD:-150" )
// define transition action 'goAToC1' weight +150
@ Transit ( from = "A" , to = "C" , on = "ToC" , callMethod = "goAToC1:+150" )
هناك طريقة أخرى وهي تجاوز طريقة الوزن لفئة العمل، على سبيل المثال
Action <...> newAction = new Action <...>() {
...
@ Override
public int weight () {
return 100 ;
}
}
تدعم مؤسسة السنجاب أيضًا الطريقة التقليدية للإعلان عن وزن الحركة. سيتم تعيين وزن إجراء استدعاء الطريقة الذي يبدأ اسمه بـ " قبل " على 100، لذا، كما يبدأ الاسم بـ " بعد " سيتم تعيينه على -100. بشكل عام، هذا يعني أنه سيتم استدعاء اسم أسلوب الإجراء الذي يبدأ بـ "قبل" في البداية، بينما سيتم استدعاء اسم أسلوب الإجراء الذي يبدأ بـ "بعد" أخيرًا. "method1:ignore" تعني أنه لن يتم استدعاء الطريقة 1.
لمزيد من المعلومات، يرجى الرجوع إلى حالة الاختبار ' org.squirrelframework.foundation.fsm.WeightedActionTest '؛
التنفيذ غير المتزامن
يمكن استخدام التعليق التوضيحي AsyncExecute في إجراء استدعاء الأسلوب ومستمع الحدث التعريفي للإشارة إلى أنه سيتم تنفيذ هذا الإجراء أو مستمع الحدث بشكل غير متزامن، على سبيل المثال تحديد طريقة الإجراء التي تم استدعاؤها بشكل غير متزامن:
@ ContextInsensitive
@ StateMachineParameters ( stateType = String . class , eventType = String . class , contextType = Void . class )
public class ConcurrentSimpleStateMachine extends AbstractUntypedStateMachine {
// No need to specify constructor anymore since 0.2.9
// protected ConcurrentSimpleStateMachine(ImmutableUntypedState initialState,
// Map<Object, ImmutableUntypedState> states) {
// super(initialState, states);
// }
@ AsyncExecute
protected void fromAToB ( String from , String to , String event ) {
// this action method will be invoked asynchronously
}
}
تعريف الحدث المرسل بشكل غير متزامن:
public class DeclarativeListener {
@ OnTransitionBegin
@ AsyncExecute
public void onTransitionBegin (...) {
// transition begin event will be dispatched asynchronously to this listener method
}
}
سيتم إرسال مهمة التنفيذ غير المتزامنة إلى ExecutorService . يمكن للمستخدم تسجيل مثيل تنفيذ ExecutorService الخاص بك من خلال SquirrelSingletonProvider ، على سبيل المثال
ExecutorService executorService = Executors . newFixedThreadPool ( 1 );
SquirrelSingletonProvider . getInstance (). register ( ExecutorService . class , executorService );
إذا لم يتم تسجيل أي مثيل ExecutorService، فسيوفر SquirrelConfiguration مثيلًا افتراضيًا.
حالة آلة ما بعد المعالج
يمكن للمستخدم تسجيل معالج ما بعد لنوع معين من أجهزة الحالة من أجل إضافة منطق عملية ما بعد بعد إنشاء جهاز الحالة، على سبيل المثال
// 1 User defined a state machine interface
interface MyStateMachine extends StateMachine < MyStateMachine , MyState , MyEvent , MyContext > {
. . .
}
// 2 Both MyStateMachineImpl and MyStateMachineImplEx are implemented MyStateMachine
class MyStateMachineImpl implements MyStateMachine {
. . .
}
class MyStateMachineImplEx implements MyStateMachine {
. . .
}
// 3 User define a state machine post processor
MyStateMachinePostProcessor implements SquirrelPostProcessor < MyStateMachine > {
void postProcess ( MyStateMachine component ) {
. . .
}
}
// 4 User register state machine post process
SquirrelPostProcessorProvider . getInstance (). register ( MyStateMachine . class , MyStateMachinePostProcessor . class );
في هذه الحالة، عندما يقوم المستخدم بإنشاء مثيل MyStateMachineImpl وMyStateMachineImplEx، سيتم استدعاء معالج النشر المسجل MyStateMachinePostProcessor للقيام ببعض الأعمال.
تصدير آلة الدولة
يمكن استخدام SCXMLVisitor لتصدير تعريف جهاز الحالة في مستند [SCXML] 2.
SCXMLVisitor visitor = SquirrelProvider . getInstance (). newInstance ( SCXMLVisitor . class );
stateMachine . accept ( visitor );
visitor . convertSCXMLFile ( "MyStateMachine" , true );
راجع للشغل، يمكن للمستخدم أيضًا الاتصال بـ StateMachine.exportXMLDefinition(true) لتصدير تعريف XML الجميل. يمكن استخدام DotVisitor لإنشاء مخطط الحالة الذي يمكن عرضه بواسطة [GraphViz] 3.
DotVisitor visitor = SquirrelProvider . getInstance (). newInstance ( DotVisitor . class );
stateMachine . accept ( visitor );
visitor . convertDotFile ( "SnakeStateMachine" );
استيراد آلة الدولة
يمكن استخدام UntypedStateMachineImporter لاستيراد تعريف مشابه لـ SCXML لجهاز الحالة والذي تم تصديره بواسطة SCXMLVisitor أو تعريف الكتابة اليدوية. سيقوم UntypedStateMachineImporter ببناء UntypedStateMachineBuilder وفقًا للتعريف الذي يمكن استخدامه لاحقًا لإنشاء مثيلات آلة الحالة.
UntypedStateMachineBuilder builder = new UntypedStateMachineImporter (). importDefinition ( scxmlDef );
ATMStateMachine stateMachine = builder . newAnyStateMachine ( ATMState . Idle );
ملاحظة: قدم UntypedStateMachineImporter نمط XML لتعريف جهاز الحالة تمامًا مثل واجهة برمجة تطبيقات منشئ جهاز الحالة أو التعليقات التوضيحية. تعريف SCXML المشابه لا يساوي SCXML القياسي.
حفظ/تحميل بيانات آلة الحالة
يمكن للمستخدم حفظ بيانات جهاز الحالة عندما يكون جهاز الحالة في حالة الخمول.
StateMachineData . Reader < MyStateMachine , MyState , MyEvent , MyContext >
savedData = stateMachine . dumpSavedData ();
ويمكن للمستخدم أيضًا تحميل البيانات المحفوظة أعلاه في جهاز حالة آخر تم إنهاء حالته أو تهيئته للتو.
newStateMachineInstance . loadSavedData ( savedData );
ملاحظة : يمكن إجراء تسلسل لبيانات جهاز الحالة إلى/إلغاء تسلسلها من سلسلة Base64 المشفرة بمساعدة فئة ObjectSerializableSupport .
تكوين آلة الدولة
عند إنشاء مثيل جهاز حالة جديد، يمكن للمستخدم تكوين سلوكه من خلال StateMachineConfiguration ، على سبيل المثال
UntypedStateMachine fsm = builder . newUntypedStateMachine ( "a" ,
StateMachineConfiguration . create (). enableAutoStart ( false )
. setIdProvider ( IdProvider . UUIDProvider . getInstance ()),
new Object [ 0 ]); // since 0.3.0
fsm . fire ( TestEvent . toA );
يتم استخدام نموذج التعليمات البرمجية أعلاه لإنشاء مثيل جهاز الحالة باستخدام UUID كمعرف له وتعطيل وظيفة البدء التلقائي. يمكن أيضًا تعيين StateMachineConfigure على منشئ جهاز الحالة، مما يعني أن جميع مثيلات جهاز الحالة التي تم إنشاؤها بواسطة builder.newStateMachine(S initialStateId)
أو builder.newStateMachine(S initialStateId, Object... extraParams)
ستستخدم هذا التكوين.
تشخيص آلة الدولة
يتم استخدام StateMachineLogger لمراقبة الحالة الداخلية لجهاز الحالة، مثل أداء التنفيذ وتسلسل استدعاء الإجراء وتقدم الانتقال وما إلى ذلك، على سبيل المثال
StateMachine <?,?,?,?> stateMachine = builder . newStateMachine ( HState . A );
StateMachineLogger fsmLogger = new StateMachineLogger ( stateMachine );
fsmLogger . startLogging ();
...
stateMachine . fire ( HEvent . B2A , 1 );
...
fsmLogger . terminateLogging ();
-------------------------------------------------------------------------------------------
Console Log :
HierachicalStateMachine : Transition from "B2a" on "B2A" with context "1" begin .
Before execute method call action "leftB2a" ( 1 of 6 ).
Before execute method call action "exitB2" ( 2 of 6 ).
...
Before execute method call action "entryA1" ( 6 of 6 ).
HierachicalStateMachine : Transition from "B2a" to "A1" on "B2A" complete which took 2 ms .
...
نظرًا لأنه يمكن استخدام مُسجل جهاز الحالة v0.3.0 بطريقة أكثر سهولة عن طريق تعيين StateMachineConfiguration فقط لتمكين وضع التصحيح، على سبيل المثال
StateMachine<?,?,?,?> stateMachine = builder.newStateMachine(HState.A,
StateMachineConfiguration.create().enableDebugMode(true),
new Object[0]);
يمكن استخدام StateMachinePerformanceMonitor لمراقبة معلومات أداء تنفيذ جهاز الحالة، بما في ذلك إجمالي عدد مرات الانتقال، ومتوسط الوقت المستهلك للانتقال، وما إلى ذلك، على سبيل المثال
final UntypedStateMachine fsm = builder . newStateMachine ( "D" );
final StateMachinePerformanceMonitor performanceMonitor =
new StateMachinePerformanceMonitor ( "Sample State Machine Performance Info" );
fsm . addDeclarativeListener ( performanceMonitor );
for ( int i = 0 ; i < 10000 ; i ++) {
fsm . fire ( FSMEvent . ToA , 10 );
fsm . fire ( FSMEvent . ToB , 10 );
fsm . fire ( FSMEvent . ToC , 10 );
fsm . fire ( FSMEvent . ToD , 10 );
}
fsm . removeDeclarativeListener ( performanceMonitor );
System . out . println ( performanceMonitor . getPerfModel ());
-------------------------------------------------------------------------------------------
Console Log :
========================== Sample State Machine Performance Info ==========================
Total Transition Invoked : 40000
Total Transition Failed : 0
Total Transition Declained : 0
Average Transition Comsumed : 0.0004 ms
Transition Key Invoked Times Average Time Max Time Min Time
C --{ ToD , 10 }-> D 10000 0.0007 ms 5 ms 0 ms
B --{ ToC , 10 }-> C 10000 0.0001 ms 1 ms 0 ms
D --{ ToA , 10 }-> A 10000 0.0009 ms 7 ms 0 ms
A --{ ToB , 10 }-> B 10000 0.0000 ms 1 ms 0 ms
Total Action Invoked : 40000
Total Action Failed : 0
Average Action Execution Comsumed : 0.0000 ms
Action Key Invoked Times Average Time Max Time Min Time
instan ... Test$1 40000 0.0000 ms 1 ms 0 ms
========================== Sample State Machine Performance Info ==========================
ستؤدي إضافة @LogExecTime في طريقة الإجراء إلى تسجيل الخروج من وقت تنفيذ الطريقة. وأيضًا إضافة @LogExecTime في فئة آلة الحالة سوف يسجل الخروج من كل وقت تنفيذ طريقة الإجراء. على سبيل المثال، سيتم تسجيل الخروج من وقت تنفيذ أسلوب العبورFromAToBOnGoToB .
@ LogExecTime
protected void transitFromAToBOnGoToB ( MyState from , MyState to , MyEvent event , MyContext context )
حالة توقيت
الحالة الموقوتة هي حالة يمكنها تأخير حدث محدد أو تشغيله بشكل دوري بعد إدخال الحالة. سيتم إرسال المهمة المحددة بوقت إلى خدمة SchededExecutorService . يمكن للمستخدم تسجيل مثيل تنفيذchededExecutorService الخاص بك من خلال SquirrelSingletonProvider ، على سبيل المثال
ScheduledExecutorService scheduler = Executors . newScheduledThreadPool ( 1 );
SquirrelSingletonProvider . getInstance (). register ( ScheduledExecutorService . class , scheduler );
إذا لم يتم تسجيل أي مثيل لـScheduledExecutorService، فسيوفر SquirrelConfiguration مثيلًا افتراضيًا. بعد ذلك، يمكن تعريف الحالة الموقوتة بواسطة منشئ آلة الحالة، على سبيل المثال
// after 50ms delay fire event "FIRST" every 100ms with null context
builder . defineTimedState ( "A" , 50 , 100 , "FIRST" , null );
builder . internalTransition (). within ( "A" ). on ( "FIRST" );
ملاحظة : تأكد من ضرورة تحديد الحالة الموقوتة قبل وصف التحولات أو إجراءات الدخول/الخروج. سيتم اعتبار الوقت الزمني الأقل من أو يساوي 0 منفذًا مرة واحدة فقط بعد التأخر الأولي .
الحالة المرتبطة (ما يسمى بحالة الآلة الفرعية)
تحدد الحالة المرتبطة إدراج مواصفات جهاز حالة الآلة الفرعية. تسمى آلة الحالة التي تحتوي على الحالة المرتبطة بآلة الحالة المحتوية. قد تكون نفس آلة الحالة آلة صغيرة أكثر من مرة في سياق آلة حالة واحدة تحتوي على.
الحالة المرتبطة تعادل لغويًا الحالة المركبة. مناطق آلة حالة الآلة الفرعية هي مناطق الحالة المركبة. يتم تعريف إجراءات الدخول والخروج والسلوك والانتقالات الداخلية كجزء من الحالة. حالة الآلة الفرعية هي آلية تحلل تسمح بتحليل السلوكيات الشائعة وإعادة استخدامها. يمكن تعريف الحالة المرتبطة باتباع نموذج التعليمات البرمجية.
builderOfTestStateMachine . definedLinkedState ( LState . A , builderOfLinkedStateMachine , LState . A1 );
دعم جي إم إكس
منذ 0.3.3، يمكن للمستخدم مراقبة مثيل جهاز الحالة عن بعد (على سبيل المثال، الحالة الحالية والاسم) وتعديل التكوينات (على سبيل المثال، تبديل عمليات التسجيل/تبديل مراقبة الأداء/حدث الحريق عن بعد) في وقت التشغيل. ستكون جميع معلومات مثيلات جهاز الحالة ضمن نطاق "org.squirrelframework". يوضح نموذج التعليمات البرمجية التالي كيفية تمكين دعم JMX.
UntypedStateMachineBuilder builder = StateMachineBuilderFactory . create (...);
builder . setStateMachineConfiguration ( StateMachineConfiguration . create (). enableRemoteMonitor ( true ));
ملاحظة : تم إهمال دعم ميزة JMX منذ 0.3.9-SNAPSHOT.
انظر ملف الأمثلة.
راجع ملف ملاحظات الإصدار.
للحصول على آخر التحديثات، تابع حسابي على تويتر @hhe11 أو +HeHenry
للمناقشات أو الأسئلة يرجى الانضمام إلى مجموعة آلة حالة السنجاب
لأي مشكلة أو شرط، يرجى تقديم قضية
إذا كنت تستخدم رمز Squirrel State Machine في تطبيقك، سأكون ممتنًا إذا أبلغت المؤلف عنه (البريد الإلكتروني: [email protected]) على النحو التالي :
الموضوع: نص إشعار استخدام جهاز حالة السنجاب: أستخدم جهاز حالة السنجاب <lib_version> في <project_name> - http://link_to_project. أنا [السماح | لا تسمح] بذكر مشروعي في قسم "من يستخدم Squirrel State Machine" على GitHub.