General Mealy state machine + frame counting + recovery history state

preface

Obviously, this semester is very tense, but there is no course selection - only one or two courses can be selected for a certain type, there are few courses, and I don't know where to inform academic lectures. We can only do our own things and draw a little sense of achievement from the boring life. I haven't typed the code for months. My tenosynovitis is almost over! Unfortunately, it will get worse soon! The plan of hiking by mountain bike failed. I rode for a day and ran 100 kilometers in the main road, highway, forest path, field path and urban path under the 36 degree sun. Gaode map gave me the wrong way! It's not enough to rely on passion alone. If I plan to do well or have someone to accompany me, things will be simpler!

Mealy state machine

Each state of the state machine has N lines to other states, M lines to the current state, and the relationship between states in the Mealy state machine

All transformations rely on input, and there are W input events.

Information required by the state machine

  • N States
  • M lines

    Information of each line: current status, event, next status, action and other information

  • W events

Dynamic array

The line between States is the transition rule.

The status ID can be set to 0 ~ status number - 1.

The event ID can be set to 0 ~ number of events - 1.

However, there is more information about rules and they are put into the structure. The pointers of the rules are stored in the dynamic array, so as to save null

Room!!!

Dynamic array data structure

#define bol uint8_t
#define true 1
#define false 0
#define unint size_t

/*Dynamic array*/
struct dynamic_array {
	void** arr;                //Pointer array, each element is a pointer
	unint size;                //Number of current elements in dynamic array
	unint capacity;            //Current capacity of dynamic array
};

Implementation of dynamic array method

/*
effect: Create an initialized dynamic data object
output: Dynamic array pointer
*/
struct dynamic_array* 
	da_create() {
	struct dynamic_array* da = (struct dynamic_array*)malloc(sizeof(struct dynamic_array));
	if (da == NULL) {
		printf("[da_create] allocation of dynamic array has failed\n");
		return NULL;
	}
	da->capacity = 4;
	da->size = 0;
	da->arr = (void**)malloc(sizeof(void*) * da->capacity);
	return da;
}

/*
effect: Add elements to the dynamic array. The length of the dynamic array is size. Use elements from 0 to size-1
input : _da Dynamic array pointer
		_data Data pointer
*/
void 
da_push(struct dynamic_array* _da, void* _data) {
	/*security check*/
	if (_da == NULL) {
		printf("[da_push] dynamic array is null\n");
		return;
	}

	//The space is not enough. It is full. You need to expand the memory
	if (_da->capacity == _da->size) { 
		//The growth rate was 2 times in the early stage and 1.5 times in the later stage
		unint temp = (unint)(_da->capacity <= 128 ? (_da->capacity * 2) : (_da->capacity * 1.5));
		
		//Apply for a larger space
		void** new_space = (void**)malloc(sizeof(void*) * temp);
		
		//Check whether the space is allocated successfully
		if (new_space == NULL) {
			printf("[da_push] allocation of new space has failed\n");
			return;
		}

		//success
		_da->capacity = temp;

		//Copy data to new space
		memcpy(new_space, _da->arr, _da->capacity * sizeof(void*));

		//Free up old memory
		free(_da->arr);

		//Update space
		_da->arr = new_space;
	}

	//Add new element
	_da->arr[_da->size++] = _data;
}

/*
effect: Delete the last element and return
input : _da Dynamic array pointer
output: Pointer to the last element
*/
void* 
da_pop(struct dynamic_array* _da) {
	/*security check*/
	if (_da == NULL) {
		printf("[da_pop] dynamic array is null\n");
		return NULL;
	}
	if (_da->size == 0) {
		printf("[da_pop] size of dynamic array is zero\n");
		return NULL;
	}

	return _da->arr[--_da->size];
}

/*
effect: Find elements by location
input : _da Dynamic array pointer
		_position Position of element pointer
output: _position Pointer to the element of the location
*/
void* 
da_at(const struct dynamic_array* _da, unint _position) {
	/*security check*/
	if (_da == NULL) {
		printf("[da_at] dynamic array is null\n");
		return NULL;
	}
	if (_position >= _da->size) {
		printf("[da_at] position is illegal\n");
		return NULL;
	}

	return _da->arr[_position];
}

/*
effect: Find elements by value
input : _da Dynamic array pointer
		_judge Returns whether the data in the element pointer is a required custom function
output: _judge Function returns a pointer to the element corresponding to true
*/
void* 
da_find(const struct dynamic_array* _da, bol(*_judge)(void*)) {
	/*security check*/
	if (_da == NULL) {
		printf("[da_find] dynamic array is null\n");
		return NULL;
	}

	for (unint i = 0;i<_da->size;++i) {
		if (_judge(_da->arr[i]) == true) return _da->arr[i];
	}

	printf("[da_find] can not find the expected data\n");
	return NULL;
}

/*
effect: Print data for dynamic array elements
input : _da Dynamic array pointer
		_print Custom function to print data in element pointer
*/
void 
da_print(const struct dynamic_array* _da, void(*_print)(void*)) {
	/*security check*/
	if (_da == NULL) {
		printf("[da_print] dynamic array is null\n");
		return ;
	}

	printf("array print procedure begin>\n");
	for (unint i = 0;i < _da->size; ++i) {
		printf("[%5d]",i);
		_print(_da->arr[i]);
		printf("\n");
	}
	printf("array print procedure ended!\n");
}

/*
effect: Delete elements based on location
input : _da Dynamic array pointer
		_position The location of the element to be deleted
*/
void 
da_del(struct dynamic_array* _da, unint _position) {
	/*security check*/
	if (_da == NULL) {
		printf("[da_del] dynamic array is null\n");
		return ;
	}
	if (_position >= _da->size) {
		printf("[da_del] position is illegal\n");
		return;
	}
	if (_da->size == 0) {
		printf("[da_remove] dynamic array is empty\n");
		return;
	}

	//Move the following element forward one
	for (unint i = _position;i < _da->size - 1; ++i) {
		_da->arr[i] = _da->arr[i + 1];
	}

	--_da->size;
}

/*
effect: Delete element based on value
input : _da Dynamic array pointer
		_judge Returns whether the data in the element pointer is a user-defined function to be deleted
*/
void 
da_remove(struct dynamic_array* _da, bol(*_judge)(void*)) {
	/*security check*/
	if (_da == NULL) {
		printf("[da_remove] dynamic array is null\n");
		return;
	}
	if (_da->size == 0) {
		printf("[da_remove] dynamic array is empty\n");
		return;
	}

	da_remove_recursion(_da, _judge,0);
}

/*
effect: A recursive delete function that deletes elements based on their values
input : _da Dynamic array pointer
		_judge Returns whether the data in the element pointer is a user-defined function to be deleted
		_start Within a recursive function, the position at which traversal begins
*/
static void 
da_remove_recursion(struct dynamic_array* _da, bol(*_judge)(void*), unint _start) {
	if (_da->size == 0) {
		printf("[da_remove_recursion] dynamic array is empty\n");
		return;
	}

	//Check of start position
	if (_start >= _da->size) {
		printf("[da_remove_recursion] start in the illegal position\n");
		return;
	}

	for (unint i = _start;i<_da->size; ++i) {
		if (_judge(_da->arr[i]) == true) {
			da_del(_da, i);

			//Not the last element, continue recursion
			if (i < _da->size) {
				da_remove_recursion(_da,_judge, i);
			}
			break;
		}
	}
}

/*
effect: Clear the elements in the dynamic array and reserve the memory of the dynamic array
input : _da Dynamic array pointer
*/
void 
da_clear(struct dynamic_array* _da) {
	/*security check*/
	if (_da == NULL) {
		printf("[da_clear] dynamic array is null\n");
		return;
	}

	_da->size = 0;//  ###%%%%%%Adding a new element pointer overwrites the previous pointer
}

/*
effect: Free dynamic array memory and empty_ da, note: it doesn't matter whether the element memory
input : _da Dynamic array pointer
*/
void 
da_free(struct dynamic_array* _da) {
	/*security check*/
	if (_da == NULL) {
		printf("[da_free] dynamic array is null\n");
		return;
	}

	if (_da->arr != NULL) free(_da->arr);
	_da->capacity = 0;
	_da->size = 0;
	_da = NULL;
	free(_da);
}


Dynamic array test

struct test_data {
	char c;
};
void test_print(void* _data) {
	struct test_data* d = (struct test_data*)_data;
	printf("%c", d->c);
}
bol test_judge(void* _data) {
	struct test_data* d = (struct test_data*)_data;
	return d->c == 'q' ? true : false;
}
void test_dynamic_aay() {	
		
	struct dynamic_array* a = da_create();
	struct test_data a0, a1, a2, a3, a4;

	a0.c = 'w';
	a1.c = 'Z';
	a2.c = 'Z';
	a3.c = 'q';
	a4.c = 'Z';

	da_push(a, &a0);
	da_push(a, &a1);
	da_push(a, &a2);
	da_push(a, &a3);
	da_push(a, &a4);
	
	printf("size = %d\n", a->size);
	da_print(a, test_print);
		
	//da_remove(a, test_judge);
	
	//struct test_data* after = da_pop(a);
	//printf("after = %c\n", after->c);
	//after = da_pop(a);
	//printf("after = %c\n", after->c);
	//after = da_pop(a);
	//printf("after = %c\n", after->c);
	//after = da_pop(a);
	//printf("after = %c\n", after->c);
	//after = da_pop(a);
	//printf("after = %c\n", after->c);
	//after = da_pop(a);
	printf("after = %c\n", after->c);

	/*struct test_data* v = da_at(a, 0);
	printf("v = %c\n", v->c);
	v = da_find(a, test_judge);
	printf("v = %c\n", v->c);*/

	da_clear(a);
	da_push(a, &a4);
	da_push(a, &a0);

	printf("size = %lu\n", a->size);
	da_print(a, test_print);	
}

test result

State machine

State machine data structure

#define state_ machine_ history_ Depth 20 / / status history




/*
	Enter the conversion rule after the event,
	Find the current state and matching events, and update the "current state" of the state machine,
	Perform actions.
*/


/*State machine rule XN   */
struct state_machine_rule {
	unint cur_state;												//current state
	unint event;												    //An event that jumps from the current state to the next state
	unint next_state;												//Next status
	void (*action)(unint _event);                                   //The action after the state transitions to the next state

	/*Additional information*/
	void* note;													    //Record some information of each status
};



/*State machine X1   */
struct state_machine {
	/*definition*/
	unint num_of_state;											   //Number of States, state ID 0 ~ number of States - 1
	unint num_of_event;											   //Number of events, event ID 0 ~ number of events - 1

	/*crux*/
	unint cur_state;											  //current state
	struct dynamic_array* rules;								  //Rule array of rules

	/*Additional features*/
	unint timer;												  //Timer, the number of time slices or (step) frames that the current state lasts
	bol isbegin;												  //Whether it is the start state. Non zero is true and zero is false	
	unint stack[state_machine_history_depth];					  //Historical status record	
	unint stack_pointer;										  //The head is the bottom of the stack_ The pointer is the one at the top of the stack, and the number of elements is stack_pointer
																  Points to the current state, which is not saved in the stack
};

Implementation of state machine method

/*
effect: Create an initialized state machine pointer
output: State machine pointer
*/
struct state_machine*
	sm_create() {
	struct state_machine* sm = (struct state_machine*)malloc(sizeof(struct state_machine));
	if (sm == NULL) {
		printf("[sm_create] allocation of state machine has failed\n");
		return NULL;
	}

	/*The actual initialization is handed over to the user*/
	sm->num_of_event = 0;
	sm->num_of_state = 0;
	sm->cur_state = 0; 
	
	sm->isbegin = true;										//Before the second event, it is the start state (that is, the second state, because the first state is inactive)
	sm->stack_pointer = 0;									//No element
	sm->timer = 0;											//Timer, which starts from the status update - that is, before the action - to the next status update
	sm->rules = da_create();							    //Create action array for rule

	return sm;
}

/*
effect: Initialization status, events, current status
input : _sm State machine pointer
		_num_of_state Number of States
		_num_of_event Number of events
		_cur_state current state
*/
void
sm_init(struct state_machine* _sm, unint _num_of_state, unint _num_of_event, unint _cur_state) {
	/*security check*/
	if (_sm == NULL) {
		printf("[sm_init] state machine is null\n");
		return;
	}

	_sm->cur_state = _cur_state;
	_sm->num_of_state = _num_of_state;
	_sm->num_of_event = _num_of_event;
}

/*
effect: Add state to state machine
		The status element id is the position of the element in the array
		Status information includes status name, status action function and notes
input : _sm State machine pointer
		_cur_state Current status of the rule
		_event Rule events
		_next_state Next status of the rule
		_action The function of the status, passing in the event that causes the status update
		_note A message for this status
*/
void
sm_add(struct state_machine* _sm, unint _cur_state, unint _event, unint _next_state, void(*_action)(unint), void* _note) {
	/*security check*/
	if (_sm == NULL) {
		printf("[sm_add] state machine is null\n");
		return;
	}
	if (_cur_state >= _sm->num_of_state || 
		_next_state >= _sm->num_of_state ||
		_event >= _sm->num_of_event) {
		printf("[sm_add] _cur_state or _next_state or _event has out of range\n");
		return;
	}
	
	struct state_machine_rule* r = (struct state_machine_rule*)malloc(sizeof(struct state_machine_rule));
	if (r == NULL) {
		printf("[sm_add] allocation of rule of state machine has failed\n");
		return NULL;
	}
	r->cur_state = _cur_state;
	r->event = _event;
	r->next_state = _next_state;
	r->action = _action;
	r->note = _note;
	da_push(_sm->rules, r);
}

/*
effect: Select rules according to input events to perform state transition and action call
input : _sm State machine pointer
		_event event
*/
void
sm_trans(struct state_machine* _sm, unint _event) {
	/*security check*/
	if (_sm == NULL) {
		printf("[sm_trans] state machine is null\n");
		return;
	}
	if (_event >= _sm->num_of_event) {
		printf("[sm_trans] _event has out of range\n");
		return;
	}

	//Traversal rule
	for (unint i = 0; i < _sm->rules->size; ++i) {
		struct state_machine_rule* r = (struct state_machine_rule*)_sm->rules->arr[i];
		if (r->cur_state == _sm->cur_state) {
			if (r->event == _event) {
				//Record history status
				//to do something


				//Update status
				_sm->cur_state = r->next_state;
	
			
				//Reset timer
				_sm->timer = 0;

				//Call action
				r->action(_event);

				//Update state machine start flag
				if(_sm->isbegin == true) _sm->isbegin = false;
				
				//
				return;
			}
		}
	}

	//Current status dwell time plus one / / split time by input event
	++_sm->timer;
}

/*
effect: Running state machine
input : _sm State machine pointer
		_event_array Event array, the number of elements (event ID) is not necessarily equal to the number of state machine events num_event
		_size_of_event_array Number of event array elements
*/
void
sm_run(struct state_machine* _sm, unint* _event_array, unint _size_of_event_array) {
	/*security check*/
	if (_sm == NULL) {
		printf("[sm_run] state machine is null\n");
		return;
	}

	//Traversal event array
	printf("[sm_run] event array size = %d\n", _size_of_event_array);
	for (unint i = 0;i< _size_of_event_array; ++i) {
		//Print current status and events
		printf("[sm_run] cur_state = %3d, event = %3d\n",_sm->cur_state,_event_array[i]);

		//state transition 
		sm_trans(_sm, _event_array[i]);
	}
}

/*
effect: a
input : _sm State machine pointer
		_event event
output: The number of frames (time slices) that the last state lasted
*/
unint
sm_exec(struct state_machine* _sm, unint _event) {
	/*security check*/
	if (_sm == NULL) {
		printf("[sm_exec] state machine is null\n");
		return;
	}
	if (_event >= _sm->num_of_event) {
		printf("[sm_exec] _event has out of range\n");
		return;
	}

	//Print current status and events
	printf("[sm_exec] cur_state = %3d, event = %3d\n", _sm->cur_state, _event);

	//state transition 
	sm_trans(_sm, _event);
}

State machine test 1 - event array

Prepare four states, four events and four functions, initialize the current state to 1, and enter the running state of the event array

Machine!!!

void fun1(unint _event) {
	printf("[fun1] event = %d\n",_event);
}

void fun2(unint _event) {
	printf("[fun2] event = %d\n", _event);
}

void fun3(unint _event) {
	printf("[fun3] event = %d\n", _event);
}

void fun4(unint _event) {
	printf("[fun4] event = %d\n", _event);
}


int main(int _argc, char** _argv) {
	printf("---------------------------------------------------------\n");
	//test_dynamic_aay();

	struct state_machine* sm = sm_create();
	sm_init(sm, 4, 4, 1);//Initialization status, events, current status

	sm_add(sm, 0, 3, 1, fun1, NULL);
	sm_add(sm, 1, 2, 2, fun2, NULL);
	sm_add(sm, 2, 1, 3, fun3, NULL);
	sm_add(sm, 3, 0, 0, fun4, NULL);

	unint evt_arr[] = { 0,1,2,3,3,2,1,0,3,1,2,3,1,1,2,0,3,0,0 };
	sm_run(sm, evt_arr,sizeof(evt_arr)/sizeof(unint));

	printf("---------------------------------------------------------\n");
	return 0;
}

test result

State machine time frame

Timer timer records the number of time slices that the state machine stays in the current state. How to calculate the time slice?

The above is calculated by event input. Each time an event is input, it goes through a time slice. But the input of the event

Not at equal intervals! The action experience time of each state is not the same! How to define

What about the time slice of the state machine?

   \;
   \;
   \;
   \;

  1. system time. According to the specific time, you need to use multithreading to update the timer at regular intervals
  2. Loop. In systems such as game loop and screen display, some things need to be done in a loop. The order of many things in each loop is fixed and the same

   \;
   \;
   \;

I haven't written multithreading in C, so I'll use the second method!!!

   \;

//state_machine.c
void
sm_trans(struct state_machine* _sm, unint _event) {
	...
				//Print the number of last status duration slices before updating the status!!!
				printf("[sm_trans] last action duration = %d\n",_sm->timer);
	...
}				
//main.c
	srand((unsigned)time(NULL));

	for (unint i = 0; i < 20; ++i) {
		printf("%d\n", i);
		//The event has only a one-third probability of occurring in each cycle
		//After the event, only a quarter of the probability will jump to the next state
		if (rand() % 3 == 0) { 
			sm_exec(sm, rand() % 4);
		}		
		//Update counter
		++sm->timer;
	}

test result


   \;

State machine history state

The stack depth of the historical state is state_machine_history_depth

Through pointer stack_ The pointer determines the depth of the stack

I don't want to use linked lists. I only use arrays. At most, it's state_machine_history_depth so many,

I define the length as 20, and the number of state transitions can be more or less. I can't define it too much.

After 19 state transitions, the array is full, stack_pointer=20.

After another transition, the status will be recorded to 0 and overwritten from the beginning.

In this case, add another variable stack_filled,

Indicates whether the array is filled


   \;

Historical state function

Stack top pointer SP, pointing to the previous bit of the stack top element!

SP is zero, that is, there is no element, so SP cannot be zero!

Make the number of fallbacks R(recovery)

Make the stack length SD(stack depth)

   \;
   \;

1. When the stack is not full.

S P a f t e r = S P b e f o r e − R        ,      R < S P b e f o r e SP_{after}=SP_{before} - R \;\;\;,\;\; { R < SP_{before}} SPafter​=SPbefore​−R,R<SPbefore​

2. When the stack is full.

S P a f t e r = { S D − R + S P b e f o r e        ,        R > S P b e o f r e S D                                                        ,        R = S P b e f o r e S P b e f o r e − R                            ,        R < S P b e f o r e SP_{after} = \begin{cases} SD - R + SP_{before} \;\;\;,\;\;\;R >SP_{beofre} \\ SD \;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\; , \;\;\; R = SP_{before} \\ SP_{before} - R \;\;\;\;\;\;\;\;\;\;\;\;\;,\;\;\;R<SP_{before}\end{cases} SPafter​=⎩⎪⎨⎪⎧​SD−R+SPbefore​,R>SPbeofre​SD,R=SPbefore​SPbefore​−R,R<SPbefore​​

   \;

/*
effect: Back to how many states
input : _sm State machine pointer
		_recovery Number of fallbacks
*/
void
sm_history(struct state_machine* _sm, unint _recovery) {
	/*security check*/
	if (_sm == NULL) {
		printf("[sm_history] state machine is null\n");
		return;
	}
	//After filled, you can only fallback state at most_ machine_ history_ depth-1
	if (_recovery >= state_machine_history_depth) {
		printf("[sm_history] recovery too much\n");
		return;
	}


	if (_sm->stack_filled == false) { //The stack hasn't been filled yet
		if (_recovery >= _sm->stack_pointer) { //Cannot fall back to 0. The state does not exist at 0	
			printf("[sm_history] stack of history's state did not filled,and can not back off to expect location\n");
			return;
		}

		//Pointer fallback
		_sm->stack_pointer -= _recovery;	
	}
	else { //The stack is full
		if (_recovery > _sm->stack_pointer) {
			_sm->stack_pointer = state_machine_history_depth - _recovery + _sm->stack_pointer;
		}
		else if(_recovery == _sm->stack_pointer){ //When SP becomes 0, it becomes state_machine_history_depth
			_sm->stack_pointer = state_machine_history_depth;
		}
		else{
			_sm->stack_pointer -= _recovery;
		}
	}

	//Update status
	_sm->cur_state = _sm->stack[_sm->stack_pointer - 1];
}

   \;
Update sm_trans
   \;

/*
effect: Select rules according to input events to perform state transition and action call
input : _sm State machine pointer
		_event event
*/
void
sm_trans(struct state_machine* _sm, unint _event) {
	/*security check*/
	if (_sm == NULL) {
		printf("[sm_trans] state machine is null\n");
		return;
	}
	if (_event >= _sm->num_of_event) {
		printf("[sm_trans] _event has out of range\n");
		return;
	}

	//Traversal rule
	for (unint i = 0; i < _sm->rules->size; ++i) {
		struct state_machine_rule* r = (struct state_machine_rule*)_sm->rules->arr[i];
		if (r->cur_state == _sm->cur_state) {
			if (r->event == _event) {
				
				//Record history status
				if (_sm->stack_pointer < state_machine_history_depth) {
					_sm->stack[_sm->stack_pointer++] = _sm->cur_state;
				}
				else {
					if (_sm->stack_filled == false) _sm->stack_filled = true;

					//Overwrites the previous header element
					_sm->stack_pointer = 1;
					_sm->stack[0] = _sm->cur_state;
				}
				
				
				//Update status
				_sm->cur_state = r->next_state;


				//Print last status duration slices
				//printf("[sm_trans] last action duration = %d\n",_sm->timer);


				//Reset timer
				_sm->timer = 0;

				//Call action
				r->action(_event);

				//Update state machine start flag
				if(_sm->isbegin == true) _sm->isbegin = false;
				
				//
				return;
			}
		}
	}

	//Current status dwell time plus one / / split time by input event
	//++_sm->timer;
}

Fallback history status 1

state_machine_history_depth is defined as 6, good debugging!

	struct state_machine* sm = sm_create();
	sm_init(sm, 4, 4, 0);

	sm_add(sm, 0, 3, 1, fun1, NULL);
	sm_add(sm, 1, 2, 2, fun2, NULL);
	sm_add(sm, 2, 1, 3, fun3, NULL);
	sm_add(sm, 3, 0, 0, fun4, NULL);

	unint evt_arr[] = { 3,2,1,0};
	sm_run(sm, evt_arr,sizeof(evt_arr)/sizeof(unint));

	sm_history(sm, 2);

test result

The current state before file return is not saved in the stack. It is the position pointed by the SP!

The current state after the shift back is cur_state!

Fallback history status 2

	struct state_machine* sm = sm_create();
	sm_init(sm, 4, 4, 0);

	sm_add(sm, 0, 3, 1, fun1, NULL);
	sm_add(sm, 1, 2, 2, fun2, NULL);
	sm_add(sm, 2, 1, 3, fun3, NULL);
	sm_add(sm, 3, 0, 0, fun4, NULL);

	unint evt_arr[] = { 3,2,1,0,3,2,1,0};
	sm_run(sm, evt_arr,sizeof(evt_arr)/sizeof(unint));

	sm_history(sm, 5);

test result

Tags: C C++ data structure

Posted on Sun, 19 Sep 2021 20:49:25 -0400 by tbobker