有限状态机和状态机之间的信号传递

14

针对电信领域中状态机开发、执行和消息/信号传递的本地语言(无FSM生成工具)支持的建议。这些状态机可能非常复杂。

我已经考虑了Erlang,但希望得到反馈、建议、教程指针、特别是基于Java的框架的替代方案。也许Scala?

仅限开源。我不需要与UML或正则表达式相关的解决方案。

由于这是为实现电信协议而做的,因此FSMs可能不是简单的。有许多状态、许多转换、基于信号,输入约束/保护。动态实例化将是一个加分项。选择语句是不可行的,它很快嵌套成无法使用的形式。它只比if/else好一点。

我更喜欢不依赖图形设计;格式FSM描述应该是人类可读/可编辑/可管理的。

--

我决定专注于C++的基于Actor的解决方案

例如,Theron框架提供了一个起点http://theron.ashtonmason.net/,为避免在基于FSM的事件处理程序中使用选择语句,这个C++ FSM模板框架看起来很有用http://satsky.spb.ru/articles/fsm/fsmEng.php


你的要求是什么?还有你的限制条件是什么? - Daniel C. Sobral
5
我现在被一个飞行意大利面怪物的形象所困扰。消息传递可以通过面条般的触手实现。 - Paul Beesley
6个回答

10

这个特定的应用程序,电信协议实现,是Erlang设计的初衷。Erlang最初在爱立信的应用是电话交换机,并且最早的商业产品是支持各种电信协议的ATM交换机。

OTP有一个标准行为来实现FSMs,称为gen_fsm。在一些OTP文档中介绍了它在非平凡FSM中的使用示例。

OSERL是Erlang中开源的SMPP实现,展示了如何使用gen_fsm实现电信协议。一个好的示例可以看看gen_esme_session

虽然我不能直接给你代码,但我知道有不少Erlang公司销售电信相关产品:CorelatusSynapseMotivity等。


8
我认为应该摒弃使用switch语句......它们最终会导致维护成本增大。您可以使用状态模式来实现有限状态机吗?根据您的实际实现,您可以使用角色(如果有多个FSM协作-嗯...这可能吗?)。角色的好处在于传递消息的框架已经存在。
使用状态的示例是:
trait State {
  def changeState(message: Any): State
}

trait FSM extends Actor {
  var state: State

  def processMessage(message: Any) {
    state = state.changeState(message)
  }

  override def act() {
    loop {
      react {
        case m: Any => processMessage(m)
      }
    }
  }
}

这是非常基础的代码,但由于我不知道更多的要求,所以这是我能想到的最多的。状态的优点是每个状态都是在一个类中自包含的。


2
就记录而言,Akka FSM 是一个不错的解决方案。 - ron

4
我不同意有人认为有限状态机(FSM)容易实现。这种看法非常短视,表明其要么不了解替代方案,要么缺乏处理复杂状态机的经验。
根本问题在于状态机图形很显然,但FSM代码并不是那么简单易懂的。一旦你超过十几个状态和大量的转换,FSM代码就变得丑陋难懂。
有一些工具可以让你绘制状态机,并生成Java代码。但我不知道是否有任何开源工具可以做到这一点。
现在回到Erlang/Scala上,Scala也有Actor和消息传递功能,而且基于JVM,因此在你的约束条件下,它可能比Erlang更好。
Scala也有DFA/NFA库,虽然它不是特别好用。它支持将任意正则表达式(即字面量不必是字符)转换为DFA/NFA。
下面会贴出使用它的一些代码。在这段代码中,我们创建了一个FSM,它将接受任何单词列表的任意前缀的顺序组合,目的是查找没有预定义键位的菜单选项。
import scala.util.regexp._
import scala.util.automata._

// The goal of this object below is to create a class, MyChar, which will
// be the domain of the tokens used for transitions in the DFA. They could
// be integers, enumerations or even a set of case classes and objects. For
// this particular code, it's just Char.
object MyLang extends WordExp {
  type _regexpT = RegExp
  type _labelT = MyChar

  case class MyChar(c:Char) extends Label
}

// We now need to import the types we defined, as well as any classes we
// created extending Label.    
import MyLang._

// We also need an instance (singleton, in this case) of WordBerrySethi,
// which will convert the regular expression into an automatum. Notice the
// language being used is MyLang.    
object MyBerrySethi extends WordBerrySethi {
  override val lang = MyLang
}

// Last, a function which takes an input in the language we defined,
// and traverses the DFA, returning whether we are at a sink state or
// not. For other uses it will probably make more sense to test against
// both sink states and final states.
def matchDet(pat: DetWordAutom[MyChar], seq: Seq[Char]): Boolean =
  !pat.isSink((0 /: seq) ((state, c) => pat.next(state, MyChar(c))))

// This converts a regular expression to a DFA, with using an intermediary NFA    
def compile(pat: MyLang._regexpT) = 
  new SubsetConstruction(MyBerrySethi.automatonFrom(pat, 100000)).determinize

// Defines a "?" function, since it isn't provided by the library
def Quest(rs: _regexpT*) = Alt(Eps, Sequ(rs: _*)) // Quest(pat) = Eps|pat = (pat)?


// And now, the algorithm proper. It splits the string into words
// converts each character into Letter[MyChar[Char]],
// produce the regular expression desired for each word using Quest and Sequ,
// then the final regular expression by using Sequ with each subexpression.
def words(s : String) = s.split("\\W+")
def wordToRegex(w : String) : Seq[MyLang._regexpT] = w.map(c => Letter(MyChar(c)))
def wordRegex(w : String) = Quest(wordToRegex(w) reduceRight ((a,b) => Sequ(a, Quest(b))))
def phraseRegex(s : String) = Sequ(words(s).map(w => wordRegex(w)) : _*)

// This takes a list of strings, produce a DFA for each, and returns a list of
// of tuples formed by DFA and string.
def regexList(l : List[String]) = l.map(s => compile(phraseRegex(s)) -> s)

// The main function takes a list of strings, and returns a function that will
// traverse each DFA, and return all strings associated with DFAs that did not
// end up in a sink state.
def regexSearcher(l : List[String]) = {
  val r = regexList(l)
  (s : String) => r.filter(t => matchDet(t._1, s)).map(_._2)
}

有限状态机的“机械结构”很容易实现(在Erlang中肯定是这样),但“逻辑转换”更多地取决于图形的复杂性。 - jldupont
我发现了这个可能会感兴趣的东西。这是一个基于Eclipse插件的框架,用于生成Java中的FSMs:http://unimod.sourceforge.net - user172783

0

状态模式(使用Java枚举)是我们在电信应用中使用的模式,但我们使用小型有限状态机:

public class Controller{
    private State itsState = State.IDLE;

    public void setState(State aState){
        itsState = aState;
    }

    public void action1(){
        itsState.action1(this);
    }

    public void action2(){
        itsState.action2(this);
    }

    public void doAction1(){
        // code
    }

    public void doAction2(){
        // code
    }
}

public enum State{
    IDLE{
        @Override
        public void action1(Controller aCtx){
            aCtx.doAction1();
            aCtx.setState(State.STATE1);
        }
    },

    STATE1{
        @Override
        public void action2(Controller aCtx){
            aCtx.doAction2();
            aCtx.setState(State.IDLE);
        }
    },

    public void action1(Controller aCtx){
        throw new IllegalStateException();
    }

    public void action2(Controller aCtx){
        throw new IllegalStateException();
    }
}

确实,正如您所提到的,对于小型状态机来说,这确实可以是一种可行的解决方案。但是随着状态数量和事件数量的增加,可能性会变得非常复杂,此时枚举解决方案就不太理想了。首先,由于所有的操作代码都在单个文件中,所以它不能很好地组织;其次,由于枚举本身是无状态的,所以也不能很好地支持状态机。 - Narendra Pathai

0

我几乎想不出有哪种语言实现FSM是不重要的。也许this one

...
if (currentState == STATE0 && event == EVENT0) return STATE1;
if (currentState == STATE1 && event == EVENT0) return STATE2;
...

-1

有 case 语句的任何语言都应该很容易实现FSM。你选择的语言应该基于有限状态机需要做什么。

例如,您指出需要在电信开发中执行此操作并提到消息。我会查看支持分布式消息传递的系统/语言。Erlang 可以做到这一点,我相信几乎所有其他常见语言都通过语言的 API /库支持此功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接