A simple FSM in Vala

一个使用 Vala 语言实现的简单的有限状态机。

FsmState – 状态机状态基类

/*
 ============================================================================
 Name        : hev-fsm-state.vala
 Author      : Heiher <admin @heiher.info>
 Version     : 0.0.1
 Copyright   : Copyright (C) 2012 everyone.
 Description : 
 ============================================================================
 */
 
using GLib;
 
namespace Hev {
 
	public class FsmState<g ,H> : Object {
 
		public virtual signal void enter(G obj, H input) {
			message("enter");
		}
 
		public virtual signal void process(G obj, H input) {
			message("process");
		}
 
		public virtual signal void leave(G obj, H input) {
			message("leave");
		}
	}
}
</g></admin>

此类定义了三个信号 enter, process, leave 及虚默认实现。enter 信号在切换入此状态时触发,process 信号在进入信号触发后触发,leave 信号为离开此状态时触发。基类中的默认信号处理函数仅仅打印出一个 message。通常从此基类派生子类并重载三个信号的默认处理函数实现特定的状态逻辑。

FsmStateOne – 状态机状态类实例

/*
 ============================================================================
 Name        : hev-fsm-state-one.vala
 Author      : Heiher <admin@heiher.info>
 Version     : 0.0.1
 Copyright   : Copyright (C) 2012 everyone.
 Description : 
 ============================================================================
 */
 
namespace Hev {
 
	public class FsmStateOne<G,H> : FsmState<G,H> {
 
		public override void enter(G obj, H input) {
			message("enter");
		}
 
		public override void process(G obj, H input) {
			message("process");
		}
 
		public override void leave(G obj, H input) {
			message("leave");
		}
	}
}

此类重载了基类中三个信号处理函数,仍然只打印出 message,按此方法可以派生出 FsmStateTwo, FsmStateThree 等等……

Fsm – 状态机类

/*
 ============================================================================
 Name        : hev-fsm.vala
 Author      : Heiher <admin@heiher.info>
 Version     : 0.0.1
 Copyright   : Copyright (C) 2012 everyone.
 Description : 
 ============================================================================
 */
 
namespace Hev {
 
	public class Fsm<G,H> : Object {
 
		private HashTable<Type?,FsmState<G,H>> states = null;
		private HashTable<FsmState<G,H>?,HashTable<H?,FsmState<G,H>>> rules = null;
		private FsmState<G,H> curr_state = null;
		private unowned G _obj = null;
 
		public G obj {
			get { return _obj; }
		}
 
		private static uint type_hash(Type? key) {
			return (uint)key;
		}
 
		private static bool type_equal(Type? a, Type? b) {
			return (a == b) ? true : false;
		}
 
		private static uint h_hash(H? key) {
			return (uint)key;
		}
 
		private static bool h_equal(H? a, H? b) {
			return (a == b) ? true : false;
		}
 
		public Fsm(G obj) {
			this._obj = obj;
			states = new HashTable<Type?,FsmState<G,H>>(type_hash, type_equal);
			rules = new HashTable<FsmState<G,H>?,
				  HashTable<H?,FsmState<G,H>>>(direct_hash, direct_equal);
		}
 
		public void add_state(Type type) {
			var state = Object.new(type) as FsmState<G,H>;
			var rule = new HashTable<H?,FsmState<G,H>>(h_hash, h_equal);
 
			states.remove(type);
			states.insert(type, state);
 
			rules.remove(state);
			rules.insert(state, rule);
		}
 
		public void add_state_translate_rule(Type curr_state,
					H input, Type new_state) {
			var _curr_state = states.lookup(curr_state);
			var _new_state = states.lookup(new_state);
			if((null!=_curr_state) && (null!=_new_state)) {
				var rule = rules.lookup(_curr_state);
				if(null != rule) {
					rule.remove(input);
					rule.insert(input, _new_state);
				}
			}
		}
 
		public void set_start_state(Type type, H input) {
			var state = states.lookup(type);
			translate_state(state, input);
		}
 
		public void input(H input) {
			var rule = rules.lookup(curr_state);
			if(null != rule) {
				var new_state = rule.lookup(input);
				translate_state(new_state, input);
			}
		}
 
		private void translate_state(FsmState<G,H>? state, H input) {
			if(null != state) {
				if(state == curr_state) {
					curr_state.process(obj, input);
				} else {
					if(null != curr_state)
					  curr_state.leave(obj, input);
 
					curr_state = state;
					curr_state.enter(obj, input);
					curr_state.process(obj, input);
				}
			}
		}
	}
}

Fsm 类用于管理状态(FsmState)和维护一个状态转移表,并处理外部输入触发状态转换,触发涉及状态类的对应信号。

Test – 测试类

/*
 ============================================================================
 Name        : hev-test.vala
 Author      : Heiher <admin@heiher.info>
 Version     : 0.0.1
 Copyright   : Copyright (C) 2012 everyone.
 Description : 
 ============================================================================
 */
 
namespace Hev {
 
	public class Test : Object {
 
		private Fsm<Test,uint> fsm = null;
 
		public Test() {
			fsm = new Fsm<Test,uint>(this);
			var one = typeof(FsmStateOne);
			var two = typeof(FsmStateTwo);
			var three = typeof(FsmStateThree);
 
			fsm.add_state(one);
			fsm.add_state(two);
			fsm.add_state(three);
 
			fsm.add_state_translate_rule(one, 1, one);
			fsm.add_state_translate_rule(one, 2, two);
			fsm.add_state_translate_rule(one, 3, three);
			fsm.add_state_translate_rule(two, 1, one);
			fsm.add_state_translate_rule(two, 2, two);
			fsm.add_state_translate_rule(two, 3, three);
			fsm.add_state_translate_rule(three, 1, one);
			fsm.add_state_translate_rule(three, 2, two);
			fsm.add_state_translate_rule(three, 3, three);
 
			fsm.set_start_state(one, 0);
		}
 
		public void run() {
			Timeout.add_seconds(1, () => {
					var rand = new Rand();
					fsm.input(rand.int_range(1, 4));
					return true;
				});
		}
 
		public static int main(string[] args) {
			var main_loop = new MainLoop();
			var test = new Test();
 
			test.run();
			main_loop.run();
 
			return 0;
		}
	}
}

下载、编译、执行

git clone git://gitcafe.com/heiher/hev-fsm.git
cd hev-fsm
make
bin/hev-fsm

Over!

Leave a Reply

Your email address will not be published. Required fields are marked *