D'abord un avertissement - Je suis nouveau à WF et j'ai beel jouer avec WF4 seulement pendant 3 jours - si des excuses sur des questions bénignes ou boiteux que je pourrais avoir ...StateMachine dans WF4
Je suis en train de implémenter WF4 State Machine dans mon projet. Voici quelques informations de fond:
- je UI WinForm comme application principale qui peut générer différentes commandes à une pile de mise en réseau
- Je couche d'interface comme une DLL qui reçoit des événements à partir d'une pile réseau
- Le La machine d'état doit implémenter une machine d'état de softphone quelque part dans la couche intermédiaire parallèle à la couche d'interface. L'état du téléphone change en fonction des événements réseau et des actions de l'utilisateur. Certaines actions de l'utilisateur provoquent simplement des commandes de mise en réseau et certaines peuvent affecter directement la machine d'état.
- Ma couche d'interface implémente un objet Phone donc j'ai fait de cet objet un hôte de la machine d'état. Je passe mon principal objet d'interface WinSipItf à l'objet de téléphone et effectuez les opérations suivantes (vie de l'objet WinSipItf dans un autre espace de noms 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);
}
}
Téléphone état initial est initial et il a déclencheur automatique/nul avec un état cible PhoneIdle. OnEntrace à l'état initial J'ai besoin d'exécuter quelques enregistrements de téléphone avec la pile de réseau - enregistrer un rappel (opération de synchronisation) et enregistrer avec le gestionnaire de pile réseau (async). Toute cette fonctionnalité réside dans le code non géré de niveau inférieur auquel j'accède via les API construites dans ma couche Interface. Après un enregistrement réussi, je reçois un rappel dans la couche Interface et déclenche un événement. Par conséquent mon inscription acitivité ressemble à ça:
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(this.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
}
Comme vous pouvez le voir dans le code comme je fais cals: phone.WinSipItf.DoSipRegister
est-il valide? Je pense que toutes les machines d'état s'exécutent sur un thread différent de celui que l'objet WinSipItf est construit ... Redondant pour dire que je n'arrive à rien avec ça ... Je ne peux même pas faire une pause dans Execute ou CacheMetadata. Je ne sais pas quoi faire à ce stade. Il vaut la peine de mentionner que mon diagramme xaml est assez construit mais j'essayais de mettre en œuvre uniquement l'état initial et sa transition en état inactif en construisant cette activité personnalisée ...
Peut-être que je shouold ont commencé à partir d'une question simple: je Je reçois la "Valeur pour un argument d'activité requis" winSipItf "n'a pas été fourni" d'avertissement lorsque je dépose mon activité de Registre sur l'état Entrée d'initial. J'ai vérifié tout le code et je ne vois pas pourquoi. Qu'est-ce que je rate?