StateMachine в WF4 - PullRequest
       2

StateMachine в WF4

2 голосов
/ 29 сентября 2011

Сначала заявление об отказе - я новичок в WF, и я играю с WF4 только 3 дня - так что извиняюсь за любые мягкие или хромые вопросы, которые у меня могут возникнуть ...

Я пытаюсь внедрить WF4 State Machine в моем проекте. Вот некоторая справочная информация:

  1. У меня WinForm UI в качестве основного приложения, которое может генерировать различные команды для сетевого стека
  2. У меня есть интерфейсный слой в виде библиотеки DLL, которая получает события из сетевого стека
  3. Конечный автомат должен реализовать конечный автомат программного телефона где-то на промежуточном уровне, параллельном уровню интерфейса. Состояние телефона меняется в зависимости от сетевых событий и действий пользователя. Некоторые действия пользователя вызывают сетевые команды, а некоторые могут напрямую влиять на конечный автомат.
  4. Мой уровень интерфейса реализует объект 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 »не был предоставлен», когда я сбрасываю свою активность в Регистре при входе в исходное состояние. Я проверил весь код, и я не понимаю, почему. Чего мне не хватает?

Ответы [ 2 ]

3 голосов
/ 29 сентября 2011

Во-первых, поскольку вы новичок в Workflow, вы, возможно, захотите провести Введение в State Machine Hands On Lab .

Второе - у меня есть пример приложения, которое демонстрирует, как поддерживать обратные вызовы (да, рабочий процесс выполняется в другом потоке) Windows Workflow Foundation (WF4) - Обратные вызовы операций и обработчики событий .

0 голосов
/ 29 сентября 2011

Вы не добавили определение рабочего процесса, но ошибка «Значение для требуемого аргумента действия« winSipItf »не была предоставлена», по-видимому, является результатом того, что аргумент winSipItf для действия «Регистр» не был установлен. Он помечен как обязательный, поэтому его необходимо установить с помощью выражения VB в рабочем процессе. Вы предоставляете аргумент с именем winSipItf для рабочего процесса, но он привязан к аргументу worklfow и не распространяется на содержащиеся действия с тем же именем аргумента.

...