StateMachine in WF4
First a disclaimer - I am new to WF and I've beel playing with WF4 only for 3 days - so apologies on any benign or lame questions I may have...
I am trying to implement WF4 State Machine in my project. Here is some background info:
- I have WinForm UI as a main app that can generate different commands to a networking stack
- I have Interface layer as a DLL that receives events from a network stack
- The state machine should implement a softphone state machine somewhere in the intermediate layer parallel to interface layer. The phone state change based on the networking events and on the user actions. Some user actions just cause networking commands and some may directly affect the state machine.
- My interface layer implements an object Phone so I made this object as a host of the state machine. I am passing my main interface object WinSipItf to the phone object, and do the following (WinSipItf object lives in another namespace WinSipNetLib):
Code:
public void Initialize(WinSipNetLib.WinSipItf winSipItf, string hostname, string user, string password)
{
try
{
WinSipItf = winSipItf;
var Itf = WinSipItf;
var input = new Dictionary<string, object>
{
{"winSipItf", Itf}
};
this.WorkFlowHostApp = new WorkflowApplication(new Activity1(), input)
{
Idle = this.OnWorkflowIdle,
Completed = this.OnWorkflowCompleted
};
// Setup the Tracking to output in the debug window
WorkFlowHostApp.Extensions.Add(new Tracker());
this.WorkFlowHostApp.Extensions.Add(this);
this.workflowBusy.Reset();
Console.WriteLine("Starting Workflow");
this.WorkFlowHostApp.Run();
}
catch (Exception ex)
{
Console.Write(ex);
}
}
Phone initial state is Initial and it has automatic/null trigger with a target state PhoneIdle. OnEntrace to Initial state I need to execute couple of Phone registrations with networking stack - register a callback (sync operation) and register with network stack manager (async). All this functionality lives at the lower level unmanaged code that I access through constructed APIs in my Interface layer. Upon successful registration I get a callback in the Interface layer and fire an event. Therefore my Registration Acitivity looks like that:
public sealed class Register : NativeActivity<Int32>
{
// Define an activity input argument of type string
[RequiredArgument]
public InArgument<WinSipNetLib.WinSipItf> winSipItf { get; set; }
#region Constants and Fields
private readonly Variable<NoPersistHandle> _noPersistHandle = new Variable<NoPersistHandle>();
internal const string BookmarkName = "WaitingForAccountStatusEvent";
/// <summary>
/// The transition callback.
/// </summary>
private BookmarkCallback registrationCallback;
#endregion
#region Properties
///// <summary>
///// Gets or sets PhoneTransition.
///// </summary>
//public PhoneTransition PhoneTransition { get; set; }
/// <summary>
/// Gets a value indicating whether CanInduceIdle.
/// </summary>
protected override bool CanInduceIdle
{
get
{
return true;
}
}
/// <summary>
/// Gets TransitionCallback.
/// </summary>
private BookmarkCallback RegistrationCallback
{
get
{
return this.registrationCallback ?? (this.registrationCallback = new BookmarkCallback(this.OnRegistrationCallback));
}
}
#endregion
#region Methods
/// <summary>
/// The cache metadata.
/// </summary>
/// <param name="metadata">
/// The metadata.
/// </param>
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.RequireExtension(typeof(Tracker));
metadata.RequireExtension(typeof(Phone));
metadata.RequireExtension(typeof(WaitForRegistrationExt));
// !!! and one more for GUI
// Provide a Func<T> to create the extension if it does not already exist
metadata.AddDefaultExtensionProvider(() => new WaitForRegistrationExt());
metadata.AddArgument(new RuntimeArgument("winSipItf", typeof(WinSipNetLib.WinSipItf), ArgumentDirection.In, true));
metadata.AddArgument(new RuntimeArgument("Result", typeof(int), ArgumentDirection.Out, false));
metadata.AddImplementationVariable(_noPersistHandle);
}
protected override void Execute(NativeActivityContext context)
{
// Enter a no persist zone to pin this activity to memory since we are setting up a delegate to receive a callback
var handle = _noPersistHandle.Get(context);
handle.Enter(context);
// Get Phone extension needed to call the functions in WinSipItf
Phone phone = (Phone)context.GetExtension<Phone>();
// Get (which may create) the extension
WaitForRegistrationExt regExt = context.GetExtension<WaitForRegistrationExt>();
// Add the callback
regExt.AddRegistrationCallback(winSipItf.Get(context));
bool bRet = phone.WinSipItf.RegisterDBusCallback("WinSipPhone");
// Obtain the runtime value of the Text input argument
if (bRet == false)
{
this.Result.Set(context, bRet.ToString());
return;
}
string hostname = "demo2.demo.com";
string username = "406";
string password = "123123";
string id = username + "@" + hostname;
String regUri = hostname;
if (phone.WinSipItf.DoSipRegister(id, regUri, username, password) == 0)
{
this.Result.Set(context, bRet.ToString());
return;
}
// Set a bookmark - the extension will resume when the Gizmo is fired
context.CreateBookmark(BookmarkName, RegistrationCallback);
//context.CreateBookmark(thi开发者_运维技巧s.PhoneTransition.ToString(), this.RegistrationCallback);
//// Obtain the runtime value of the Text input argument
//string text = context.GetValue(this.Text);
//Result.Set(context, string.Format("The text is {0}", text));
}
/// <summary>
/// The on transition callback.
/// </summary>
/// <param name="context">
/// The context.
/// </param>
/// <param name="bookmark">
/// The bookmark.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <exception cref="InvalidOperationException">
/// </exception>
private void OnRegistrationCallback(NativeActivityContext context, Bookmark bookmark, object value)
{
if (value is WinSipItf.MSGDATA)
{
}
////if (value is StateTransitionResult)
//{
// this.Result.Set(context, value as StateTransitionResult);
//}
//else if (value != null)
//{
// // Resumed with something else
// throw new InvalidOperationException(
// "You must resume PhoneTransition bookmarks with a result of type StateTransitionResult");
//}
// Exit the no persist zone
var handle = _noPersistHandle.Get(context);
handle.Exit(context);
}
#endregion
}
As you can see from the code I make cals like: phone.WinSipItf.DoSipRegister
Is it valid? I think all state machine is executing on a different thread from the one that the WinSipItf object is constructed...
Redundant to say that I dont get anywhere with this... I cant even break in Execute or CacheMetadata. Not sure what to do at this point.
Worth to mention that my xaml diagram is quite built but I was trying to implement only the Initial state and its transition to Idle state by constructing this custom activity...
Maybe I shouold have started from a simple question: I am getting "Value for a required activity argument 'winSipItf' was not supplied" warning when I drop my Register activity on the Entry of Initial state. I checked all the code and I dont see why. WHat am I missing?
First since you are new to Workflow you might want to do the Introduction to State Machine Hands On Lab.
Second - I have a sample application that demonstrates how to support callbacks (yes the workflow is executing on a different thread) Windows Workflow Foundation (WF4) - Activity Callbacks and Event Handlers.
You didn't add the workflow definition but the error "Value for a required activity argument 'winSipItf' was not supplied" seems to be the result of the winSipItf argument on the Register activity not being set. It is marked as required so it needs to be set using a VB expression in the workflow. You are providing an argument with the name winSipItf to the workflow but that is tied to a worklfow argument and doesn't propagate to contained activities with the same argument name.
精彩评论