.NET Framework - Is State pattern the solution?

Asked By Anders Eriksson on 02-Jun-11 01:34 PM
I have a xml file that is a sequence. in this xml file there are
commands for a laser marking system.
Threre are two kinds of commands
MoveAxis
Document

MoveAxis is used to move the axis of the machine so that the item being
marked is under the laser.

Document is the layout file and it has to be executed(marked)

These command can be in any order and any number, but usually there is a
MoveAxis and then a Document, followed by a MoveAxis, etc

The moveAxis command is an async command so it will return directly.
When the axis has reached its position an ReachPosition event will be
triggered by the hardware.

The Document.execute is also an async command and will also return
directly. When the document has been marked an LaserEnd event will be
triggered by the hardware.

my problem is that I need to "wait" for each event before continuing
with the next command in the xml file.

I feel like this could be solved using a State Pattern or State Machine?
But I do not get how!
All samples are either so simple that I cannot rework them for this or so
complicated that I do not understand them.

Is State Pattern/State Machine (are there any differences?) the way to
go or is there a more appropriate way of solving this problem?

// Anders


Big Steel replied to Anders Eriksson on 02-Jun-11 03:46 PM
So maybe you can use a Windows Workflow with events.
Peter Duniho replied to Anders Eriksson on 02-Jun-11 08:53 PM
The problem does not sound complicated enough to warrant worrying about
specific design patterns at all.

The real issue here (as I understand it) is that you apparently have an
API that provides only async methods, with no synchronous equivalents.
So basically, you need to turn those into synchronous methods.

it is not clear from your post where the events live relative to the
object on which you are calling the method to invoke a command.  So I will
gloss over that bit and not worry about it.  I assume you can fill in
the details.

I think one of the easiest ways to address the issue would be to simply
have a queue of the commands.  Make the Action delegate references if
you like, whatever???does not matter too much.

Then just subscribe to both events, and in the event handler, signal the
consumer of the queue to peek at the next command if present and invoke
it.  When queuing a command, if the queue is empty, you can just invoke
the command immediately.

I suggested using peek to get the command; do not dequeue the command
until it is actually been completed.  This gives you a way to know
whether a command is in progress when you queue a new one.  If you would
prefer to dequeue the command right away, then just keep a flag around
that is set when there is a command in progress, so you can tell when an
empty queue means you can actually invoke the command immediately or not.

Another way to approach this, of course, would be to just wrap the async
API in something that gives you a synchronous version.  In that wrapper,
you would  have synchronous versions of your command methods, which when
called simply wait for the event to be raised signaling the completion
of the command, and only return to the caller when that event is raised.

The wrapper approach might be slightly less efficient, but it does have
the advantage of being a little simpler, and also of having a lower
memory footprint (because it does not have to maintain the queue, which
could get quite large if the latency on your hardware is much higher
than your file i/o and the list of commands is itself very large).

Pete
Anders Eriksson replied to Peter Duniho on 03-Jun-11 08:15 AM
Hello Peter,

The idea of a queue is really clever, which is probably why I had not
thought of it ;-)

All commands is performed in a COM Server, and as you guessed it only
has async functions.

I have tried, with your help, creating a sync wrapper using
lock(objWait)
{
Monitor.Wait(objWait);
}
when I have called the MoveAxis function and
lock (objWait)
{
Monitor.Pulse(objWait);
}
in the ReachPosition event.

This has worked perfectly, somehow it has stopped working!?
Now everything hangs after Monitor.Wait

Since I do not have the possibility to debug (I need a communication
card, which I do not have). I will go for the queue solution

Thank you very much for your help!

// Anders
Peter Duniho replied to Anders Eriksson on 03-Jun-11 11:55 AM
Without a proper code example, it is hard to know for sure.  But I would
guess that you are not calling the MoveAxis() method from within the
first "lock" block.  If so, then that creates a race condition that can
allow the operation for the MoveAxis() to complete before the caller has
had a chance to actually get to the statement with the call to Wait(),
and so when the Pulse() method is called, there is no thread waiting.

Pulse() can only act on threads that are already waiting; if one comes
along later and waits, it will do so indefinitely.

The correct idiom is for the initiator/caller to first acquire the lock,
_then_ within that lock to call the asynchronous method, and only after
that then to call Wait().  That ensures that the completion of the
asynchronous method cannot occur until after the initiator/caller has
actually been placed in the waiting queue for the monitor.

For example:

AsyncObjectWithEvent a = ???; // whatever
object objWait = new object();
EventHandler handler = delegate
{
lock (objWait)
{
Monitor.Pulse(objWait);
}
};

a.DoneEvent += handler;

lock (objWait)
{
a.DoAsyncMethod();

Monitor.Wait(objWait);
}

a.DoneEvent -= handler;

In the above, the monitor is not released until the calling code gets to
the Wait() call.  This means that no matter how fast the async operation
happens, and no matter what the thread scheduler does, the async
operation's completion event cannot get to the call to Pulse() until the
calling code has successfully called Wait() and released the monitor.

(In that example, I put all the logic locally in the same block of code,
but of course you can instead just subscribe to the event once
elsewhere, using a class member field for the synchronizing object
do, rather than all the setup too).

Pete