Skip to content

Commit

Permalink
Merge pull request #8 from steelbreeze/development
Browse files Browse the repository at this point in the history
Fix orthogonal region & external transition bug
  • Loading branch information
David Mesquita-Morris committed Mar 1, 2015
2 parents 814384e + d87e781 commit ee86e10
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Welcome to state.cs

The current stable release is 5.1.2.
The current stable release is 5.1.3.

If you're using state.cs I'd love to hear about it; please e-mail me at [email protected]

Expand Down
7 changes: 7 additions & 0 deletions src/Element.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ internal void Reset() {
this.Enter = null;
}

/// <summary>
/// Tests the element to determine if it is part of the current active state confuguration
/// </summary>
/// <param name="context">The state machine context.</param>
/// <returns>True if the element is active.</returns>
internal protected abstract Boolean IsActive( IContext<TContext> context );

internal virtual void BootstrapElement( Boolean deepHistoryAbove ) {
#if DEBUG
this.Leave += ( message, context, history ) => Console.WriteLine( "{0} leave {1}", context, this.QualifiedName );
Expand Down
9 changes: 9 additions & 0 deletions src/Region.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ public Region( String name, State<TContext> parent )
parent.Add( this );
}

/// <summary>
/// Tests the Region to determine if it is part of the current active state confuguration
/// </summary>
/// <param name="context">The state machine context.</param>
/// <returns>True if the element is active.</returns>
internal protected override Boolean IsActive( IContext<TContext> context ) {
return this.Parent.IsActive( context );
}

internal void Add( Vertex<TContext> vertex ) {
Trace.Assert( vertex != null, "Cannot add a null vertex" );
Trace.Assert( this.vertices.Where( v => v.Name == vertex.Name ).Count() == 0, "Vertices must have a unique name within the scope of their parent Region" );
Expand Down
14 changes: 12 additions & 2 deletions src/State.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ public State( String name, Region<TContext> parent )
// Constructor used by FinalState
internal State( String name, Region<TContext> parent, Func<Transition<TContext>[], Object, TContext, Transition<TContext>> selector ) : base( name, parent, selector ) { }

/// <summary>
/// Tests the state to determine if it is part of the current active state confuguration
/// </summary>
/// <param name="context">The state machine context.</param>
/// <returns>True if the element is active.</returns>
internal protected override Boolean IsActive( IContext<TContext> context ) {
return base.IsActive( context ) && context[ this.Region ] == this;
}

/// <summary>
/// Sets optional exit behavior that is called when leaving the State.
/// </summary>
Expand Down Expand Up @@ -301,8 +310,9 @@ internal override Boolean Evaluate( Object message, TContext context ) {

if( this.IsComposite )
for( int i = 0, l = this.regions.Length; i < l; ++i )
if( this.regions[ i ].Evaluate( message, context ) )
processed = true;
if( this.IsActive( context ) == true )
if( this.regions[ i ].Evaluate( message, context ) )
processed = true;

if( !processed )
processed = base.Evaluate( message, context );
Expand Down
9 changes: 9 additions & 0 deletions src/StateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ public StateMachine( String name )
this.Root = this;
}

/// <summary>
/// Tests the StateMachine to determine if it is part of the current active state confuguration
/// </summary>
/// <param name="context">The state machine context.</param>
/// <returns>True if the element is active.</returns>
internal protected override Boolean IsActive( IContext<TContext> context ) {
return true;
}

internal void Add( Region<TContext> region ) {
if( this.regions == null )
this.regions = new Region<TContext>[ 1 ] { region };
Expand Down
9 changes: 9 additions & 0 deletions src/Vertex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ internal Vertex( String name, Region<TContext> parent, Func<Transition<TContext>
parent.Add( this );
}

/// <summary>
/// Tests the vertex to determine if it is part of the current active state confuguration
/// </summary>
/// <param name="context">The state machine context.</param>
/// <returns>True if the element is active.</returns>
internal protected override Boolean IsActive( IContext<TContext> context ) {
return this.Parent.IsActive( context );
}

/// <summary>
/// Creates a new transition from this Vertex.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions tests/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static void Main( String[] args )
// user test cases
Users.Muximise1.Test();
Users.Brice1.Test();
Users.P3pp3r.Test();
}
}
}
43 changes: 16 additions & 27 deletions tests/Users/Muximise1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,9 @@
using System;
using System.Diagnostics;

namespace Steelbreeze.Behavior.StateMachines.Tests.Users
{
public class Muximise1
{
public static void Test()
{
// try
// {
namespace Steelbreeze.Behavior.StateMachines.Tests.Users {
public class Muximise1 {
public static void Test() {
var model = new StateMachine<DictionaryContext>( "muximise1" );

var initial = new PseudoState<DictionaryContext>( "initial", model );
Expand All @@ -33,33 +28,27 @@ public static void Test()
var f1 = new FinalState<DictionaryContext>( "f1", r1 );
var f2 = new FinalState<DictionaryContext>( "f2", r2 );

initial.To( ortho );
initial.To( ortho );

i1.To( s1 );
i2.To( s2 );
i1.To( s1 );
i2.To( s2 );

ortho.To( final ); // This should happen once all regions in ortho are complete?
ortho.To( final ); // This should happen once all regions in ortho are complete?

s1.To( f1 ).When<String>( c => c == "complete1" );
s2.To( f2 ).When<String>( c => c == "complete2" );
s1.To( f1 ).When<String>( c => c == "complete1" );
s2.To( f2 ).When<String>( c => c == "complete2" );

ortho.To( simple ).When<String>( c => c == "jump" );
simple.To( ortho ).When<String>( c => c == "back" );
ortho.To( simple ).When<String>( c => c == "jump" );
simple.To( ortho ).When<String>( c => c == "back" );

var instance = new DictionaryContext();
var instance = new DictionaryContext();

model.Initialise( instance );
model.Initialise( instance );

model.Evaluate( "complete1", instance );
model.Evaluate( "complete2", instance );
model.Evaluate( "complete1", instance );
model.Evaluate( "complete2", instance );

Trace.Assert( model.IsComplete( instance ) );

// }
// catch( Exception x )
// {
// Trace.Fail( x.Message );
// }
Trace.Assert( model.IsComplete( instance ) );
}
}
}
56 changes: 56 additions & 0 deletions tests/Users/P3pp3r.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* State v5 finite state machine library
* http://www.steelbreeze.net/state.cs
* Copyright (c) 2014-5 Steelbreeze Limited
* Licensed under MIT and GPL v3 licences
*/
using System;
using System.Diagnostics;

namespace Steelbreeze.Behavior.StateMachines.Tests.Users {
/// <summary>
/// Test relating to external transitions and orthogonal regions; a transition in one region renders a potential transition in another region obsolete
/// </summary>
public class P3pp3r {
public static void Test() {
var main = new StateMachine<DictionaryContext>( "p3pp3r" );
var initial = new PseudoState<DictionaryContext>( "initial", main, PseudoStateKind.Initial );
var state1 = new State<DictionaryContext>( "state1", main );
var state2 = new State<DictionaryContext>( "state2", main );

var regionA = new Region<DictionaryContext>( "regionA", state1 );
var initialA = new PseudoState<DictionaryContext>( "initialA", regionA, PseudoStateKind.Initial );
var state3 = new State<DictionaryContext>( "state3", regionA );
var state8 = new State<DictionaryContext>( "state8", regionA );

var regionB = new Region<DictionaryContext>( "regionB", state1 );
var initialB = new PseudoState<DictionaryContext>( "initialB", regionB, PseudoStateKind.Initial );
var state4 = new State<DictionaryContext>( "state4", regionB );
var state5 = new State<DictionaryContext>( "state5", regionB );

var regionBa = new Region<DictionaryContext>( "regionBa", state4 );
var initialBa = new PseudoState<DictionaryContext>( "initialBa", regionBa, PseudoStateKind.Initial );
var state6 = new State<DictionaryContext>( "state6", regionBa );

var regionBb = new Region<DictionaryContext>( "regionBb", state4 );
var initialBb = new PseudoState<DictionaryContext>( "initialBb", regionBb, PseudoStateKind.Initial );
var state7 = new State<DictionaryContext>( "state7", regionBb );

initial.To( state1 );
initialA.To( state3 );
initialB.To( state4 );
initialBa.To( state6 );
initialBb.To( state7 );

state3.To( state2 ).When<String>( c => c == "event2" );
state3.To( state8 ).When<String>( c => c == "event1" );

state7.To( state5 ).When<String>( c => c == "event2" ).Effect( () => Trace.TraceError("p3pp3r test failed") );
state7.To( state5 ).When<String>( c => c == "event1" );

var sms = new DictionaryContext();
main.Initialise( sms );

main.Evaluate( "event2", sms );
}
}
}

0 comments on commit ee86e10

Please sign in to comment.