Сначала заявление об отказе - я новичок в WF, и я играю с WF4 только 3 дня - так что извиняюсь за любые мягкие или хромые вопросы, которые у меня могут возникнуть ...
Я пытаюсь внедрить WF4 State Machine в моем проекте. Вот некоторая справочная информация:
- У меня WinForm UI в качестве основного приложения, которое может генерировать различные команды для сетевого стека
- У меня есть интерфейсный слой в виде библиотеки DLL, которая получает события из сетевого стека
- Конечный автомат должен реализовать конечный автомат программного телефона где-то на промежуточном уровне, параллельном уровню интерфейса. Состояние телефона меняется в зависимости от сетевых событий и действий пользователя. Некоторые действия пользователя вызывают сетевые команды, а некоторые могут напрямую влиять на конечный автомат.
- Мой уровень интерфейса реализует объект Phone, поэтому я сделал этот объект хостом конечного автомата. Я передаю свой основной интерфейсный объект WinSipItf в телефонный объект и делаю следующее (объект WinSipItf находится в другом пространстве имен WinSipNetLib):
Код:
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);
}
}
Начальное состояние телефона - «Начальное» и имеет автоматический / нулевой триггер с целевым состоянием PhoneIdle. От OnEntrace до начального состояния Мне нужно выполнить несколько регистраций Phone с помощью сетевого стека - зарегистрировать обратный вызов (операция синхронизации) и зарегистрироваться в диспетчере сетевого стека (async). Вся эта функциональность живет на неуправляемом коде более низкого уровня, к которому я обращаюсь через встроенные API на моем уровне интерфейса. После успешной регистрации я получаю обратный вызов на уровне интерфейса и запускаю событие. Поэтому моя регистрационная активность выглядит так:
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
}
Как видно из кода, я делаю cals вроде: phone.WinSipItf.DoSipRegister
Это действительно? Я думаю, что весь конечный автомат выполняется в потоке, отличном от того, в котором создан объект WinSipItf ...
Избыточно сказать, что я ничего не получу с этим ... Я не могу даже сломаться в Execute или CacheMetadata. Не уверен, что делать на этом этапе.
Стоит отметить, что моя диаграмма xaml довольно сложна, но я пытался реализовать только Начальное состояние и его переход в состояние ожидания, создавая это пользовательское действие ...
Может быть, я должен начать с простого вопроса: я получаю предупреждение «Значение для требуемого аргумента действия« winSipItf »не был предоставлен», когда я сбрасываю свою активность в Регистре при входе в исходное состояние. Я проверил весь код, и я не понимаю, почему. Чего мне не хватает?