/// <summary> /// Constructor for synchronized FSM /// </summary> /// <param name="synchronized"></param> public Fsm( bool synchronized ) : base(synchronized) { classInfo = (FsmClassInfo)FsmClassInfo.fsmClassInfoMap[GetType()]; if ( classInfo == null ) { InitializeFsmClassInfo(); } Debug.Assert( classInfo != null ); }
/// <summary> /// Initializes the class info for an FSM. It is called when /// the construcor is called the first time. This can not be done /// in static constructor since derived class must already exist. /// Constructing this information at startup time saves a lot of execution /// time while dispatching events! /// </summary> private void InitializeFsmClassInfo() { Type typeFsm = GetType(); Debug.Assert( classInfo == null ); classInfo = new FsmClassInfo(); classInfo.name = typeFsm.Name; FsmClassInfo.fsmClassInfoMap[typeFsm] = classInfo; // Check, if client want to use attributes for defining state var and handler Object[] fsmCodingAttributes = typeFsm.GetCustomAttributes(typeof(FsmCodingAttribute),true); Debug.Assert( fsmCodingAttributes.Length <= 1 ); if ( fsmCodingAttributes.Length > 0 ) { FsmCodingAttribute attr = (FsmCodingAttribute)fsmCodingAttributes[0]; classInfo.fsmCodingType = attr.CodingType; } // get state member variable if available MemberInfo[] stateFields; if ( classInfo.fsmCodingType == ECodingType.Automatic ) { // No attribute needed, just take the member with the name "state" stateFields = typeFsm.GetMember( "state", MemberTypes.Field, BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic); } else { // Find a enum member with the attribute "FsmState" stateFields = typeFsm.FindMembers(MemberTypes.Field, BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic, new MemberFilter(HasFieldAttributeOfType), typeof(FsmStateAttribute)); // Generate a GetCurrentState method for this FSM class // Note: don't know how to implement .... } // Get all info from state valiable and prepare state info FieldInfo stateField; FieldInfo[] StateEnums; Hashtable stringToStateMap = new Hashtable(); if ( stateFields.Length > 0 ) { classInfo.hasStates = true; // Get transition handlers and state handlers // This FSM has a state field, get the filed info of it. // Fill a string map with the enumeration value names for faster lookup Debug.Assert( stateFields.Length == 1 ); stateField = (FieldInfo)stateFields[0]; classInfo.stateEnumType = stateField.FieldType; Debug.Assert(classInfo.stateEnumType.IsSubclassOf(typeof(System.Enum))); StateEnums = classInfo.stateEnumType.GetFields( BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic); classInfo.stateInfoArray = new StateInfo[StateEnums.Length]; foreach ( FieldInfo fieldInfo in StateEnums ) { int val = (int)fieldInfo.GetValue(null); StateInfo stateInfo = new StateInfo(); classInfo.stateInfoArray[val] = stateInfo; Debug.Assert(classInfo.stateInfoArray[val] == stateInfo); stateInfo.name = fieldInfo.Name; stateInfo.transitions = new Hashtable(); stringToStateMap.Add(fieldInfo.Name, stateInfo ); } } // Get all methods which are candidates for any kind of handlers MemberInfo[] handlers = typeFsm.FindMembers(MemberTypes.Method, BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic, new MemberFilter(IsValidFsmHandler), null); // Loop over all these methods foreach ( MemberInfo mi in handlers ) { MethodInfo meth = (MethodInfo)mi; StateInfo stateInfo = null; if ( classInfo.fsmCodingType == ECodingType.Automatic ) { // check if it is a state or transition handler bool bIsStateOrTransitionHandler = false; int separatorPos = meth.Name.IndexOf("_"); if ( separatorPos >= 0 ) { string prefix = meth.Name.Substring(0, separatorPos); stateInfo = (StateInfo)stringToStateMap[prefix]; if ( stateInfo != null ) { // It is a state or transition handler bIsStateOrTransitionHandler = true; } } if ( bIsStateOrTransitionHandler ) { if ( meth.Name.EndsWith("_EntryState") ) { Debug.Assert(meth.GetParameters().Length == 2); Debug.Assert(meth.GetParameters()[0].ParameterType == typeof(FsmEvent)); Debug.Assert(meth.GetParameters()[1].ParameterType == classInfo.stateEnumType); stateInfo.entryMethod = meth; } else if ( meth.Name.EndsWith("_ExitState") ) { Debug.Assert(meth.GetParameters().Length == 1); Debug.Assert(meth.GetParameters()[0].ParameterType == typeof(FsmEvent)); stateInfo.exitMethod = meth; } else if ( meth.GetParameters().Length == 1 ) { if ( meth.GetParameters()[0].ParameterType == typeof(FsmEvent) ) { // it is a default transition stateInfo.defaultTransitionMethod = meth; } else if ( meth.GetParameters()[0].ParameterType.IsSubclassOf(typeof(FsmEvent)) ) { // it is a transition Type eventParamType = meth.GetParameters()[0].ParameterType; Debug.Assert( stateInfo.transitions[eventParamType] == null ); stateInfo.transitions[eventParamType] = meth; } else { // Do nothing, it is not a FSM method } } else { // Do nothing, it is not a FSM method } } else { if ( meth.GetParameters().Length == 1 && meth.GetParameters()[0].ParameterType.IsSubclassOf(typeof(FsmEvent)) ) { // Its an event handler Type eventParamType = meth.GetParameters()[0].ParameterType; // add [FsmNoHandler] to failing method when asserting here! Debug.Assert( classInfo.eventHandlers[eventParamType] == null ); classInfo.eventHandlers[eventParamType] = meth; } else { // Do nothing, it is not a FSM method } } } else { // NOT Automatic, with attributes object[] attribs; // Is it a transition handler ? attribs = mi.GetCustomAttributes(typeof(FsmTransitionHandlerAttribute), false); if ( attribs.Length > 0 ) { // yes, it is a transition handler, assign it to state info FsmTransitionHandlerAttribute attrib = (FsmTransitionHandlerAttribute)attribs[0]; stateInfo = (StateInfo)stringToStateMap[attrib.FromState]; Debug.Assert( stateInfo != null ); Type eventParamType = meth.GetParameters()[0].ParameterType; // Is it the default transition handler ? if ( eventParamType == typeof(FsmEvent) ) { // Yes, store it stateInfo.defaultTransitionMethod = meth; } else { // It is a normal transiton handler Debug.Assert( eventParamType.IsSubclassOf(typeof(FsmEvent)) ); Debug.Assert( stateInfo.transitions[eventParamType] == null ); stateInfo.transitions[eventParamType] = meth; } } else { attribs = mi.GetCustomAttributes(typeof(FsmStateHandlerAttribute), false); if ( attribs.Length > 0 ) { // yes, it is a state handler FsmStateHandlerAttribute attrib = (FsmStateHandlerAttribute)attribs[0]; stateInfo = (StateInfo)stringToStateMap[attrib.State]; Debug.Assert( stateInfo != null ); if ( attrib.HandlerType == EStateHandlerType.Entry ) { Debug.Assert( meth.GetParameters().Length == 2 ); Debug.Assert( meth.GetParameters()[0].ParameterType == typeof(FsmEvent)); Debug.Assert( meth.GetParameters()[1].ParameterType == classInfo.stateEnumType); Debug.Assert( stateInfo.entryMethod == null ); stateInfo.entryMethod = meth; } else if ( attrib.HandlerType == EStateHandlerType.Exit ) { Debug.Assert( meth.GetParameters().Length == 1 ); Debug.Assert( meth.GetParameters()[0].ParameterType == typeof(FsmEvent)); Debug.Assert( stateInfo.exitMethod == null ); stateInfo.exitMethod = meth; } else { Trace.WriteLine( "Unexpected State Handler attribute value" ); Debug.Assert( false ); } } else { // it is neither a transition nor a state handler // Is it a event handler attribs = mi.GetCustomAttributes(typeof(FsmEventHandlerAttribute), false); if ( attribs.Length > 0 ) { // yes, it is an event handler FsmEventHandlerAttribute attrib = (FsmEventHandlerAttribute)attribs[0]; Type eventParamType = meth.GetParameters()[0].ParameterType; Debug.Assert( classInfo.eventHandlers[eventParamType] == null ); Debug.Assert( eventParamType.IsSubclassOf(typeof(FsmEvent)) ); classInfo.eventHandlers[eventParamType] = meth; } } } } } }