.NET CF 3.5表单与仅同步网络设备通信

时间:2012-06-18 21:43:13

标签: .net winforms multithreading asynchronous compact-framework

好的,我可以在这里问我的第一个问题......

我创建了许多视图(CF 3.5用户控件,使用OpenNETCF.IoC和resco控件)和导航控制器。

我还为一个非常好奇的专有网络设备创建了一个抽象层(“驱动程序”)。命令只能在设备上同步执行,但我使用读取器线程来侦听来自设备的消息(可能随时出现,或者响应同步命令)。这些消息可以在事件中分派或排队。

在“驱动程序”界面中这样排序:

public Response ExecuteCommand(Command command);< =这是同步通话。有些命令可能会运行几分钟。

public event DeviceMessageReceivedEvent;< =如果这是订阅的,则不会直接回复对上述方法的调用的传入消息将在事件args中调度。这将从读者线程调用。

public Queue DeviceMessages;< =如果未订阅该活动,此类消息将发送到此处。

还有一个方便的应用程序外观,但它基本上只是ExecuteCommand调用的一个花哨的包装器,所以我将在这里省略它。

问题:

我如何最好地将在事件和/或队列中接收的数据连接到视图,以及如何最好地在ExecuteCommand方法上实现异步包装 - 由于限制,该方法必须同步运行该设备的网络协议(是的,它确实非常糟糕。)

在阅读了很多关于使用.NET CF 3.5 WinForms的异步编程后,我不知道如何继续。在第一个演示中,我经常使用DataBindings但是这导致了在需要时没有明显的方法来使用Control.Invoke的问题。我还通过与UI线程同步执行所有命令并使用匿名线程而不是使用事件来轮询队列中的消息来解决异步执行问题。我非常希望改变,原因很明显。

CF不提供BeginInvoke和EndInvoke,但我认为那些很简单,可以重新实现吗?

我应该创建一个静态的以视图为中心的模型吗?这会简化问题吗?

我应该创建一个额外的“inbetween”线程来处理模型/视图和驱动程序之间的状态转移吗?这会简化问题吗?

...

我想我正在寻找一些关于一般设计原则的头脑风暴。我从来都不是一个UI用户,大多数都在做系统编程,而且从来没有超过我能避免的.NET。异步编程的概念非常清楚,而不是如何在这种情况下应用它们。

所以..赞赏任何意见,也推荐书籍推荐......

2 个答案:

答案 0 :(得分:1)

声音方式,方式,方式太像我正在进行的项目。我有硬件,我一次只能发送一个命令。这些命令可能会超时,或者可能只等到接收器唤醒。命令以回复或未经请求的数据消息形式出现。

我所做的是实际创建一些图层,其中包含一些我现在正在编写的名称

最低层 - 通讯总线:

  • 知道如何发送和接收。
  • 知道如何将传入的数据解析为“帧”,但仅此而已(它知道分隔符和校验和)。这实际上是在OpenNETCF.IO串行库中的缓冲线程中完成的。
  • 将已解析的帧放入队列以最小化接收器线程忙状态
  • 具有监视Rx队列并调度帧的工作线程
  • 公开一个简单的“发送”界面(发送在上面的图层中处理)

下一层 - API(?)图层

设备监视器

  • 知道最终设备存在
  • 知道我上次听到他们的消息
  • 从通信总线接收帧并将它们放入队列(以便不阻塞接收器)
  • 知道如何更好地解析帧
  • 引发特定于消息的事件
  • 在节点上线时发送消息(从消息处理程序中检索)
  • 有两个工作线程:
    • TxThread通过拉出来处理“一个且唯一一个外发消息”问题 消息处理程序
    • RxThread接收监视传入帧队列,解析和分派这些消息

消息管理器

  • 知道哪些消息需要转到哪些设备
  • 知道每条消息的状态(未发送,等待响应,超时等)
  • 允许过滤“发送”,“超时”等项目

服务层

  • 使用和抽象API层
  • 将对象和事件暴露给应用程序(直接和通过REST)
  • 数据汇总(平均值,电池监控等)
  • 包含业务规则/逻辑

这里的要点是我没有显式实现任何异步API,尽管库中的几乎所有内容都是异步行为。 UI只是为传入的东西查找事件(INotifyPropertyChanged abounds)。对于传出,它执行类似“请求将引脚1设置为高”,这是非阻塞的。服务层执行一些检查以验证远程端实际发生的操作,但UI只是查看“引脚状态”属性,当它实际变为高时(在服务层中验证),它会更新。

答案 1 :(得分:0)

我最终得到了一堆特定的IAsyncResult开始/结束调用,以及其他有趣的东西,导致解决方案不是非常漂亮或非常适合单核心单管道CPU。但从那时起,我在一个新项目中重新审视了这个问题。在这里,我创建了一个简单的包装器,它允许我轻松地触发并忘记CF中的异步调用。它可能并不完美,但它在我需要它做的事情上表现非常出色。

请注意,这仅适用于UI内容。

新架构现在包括通用连接层(通过公共接口的串行和tcp),特定于设备的API层(在此级别上几乎没有共同点的几个设备),特定于设备的表示控制器,通用表示模型(一旦抽象,它们实际上有很多共同点),并且在视图之上,由演示控制器通过表示模型间接控制。对于所有设备(设备特定功能),视图并不完全相同,但是演示控制器控制视图中可见/加载的内容。

但不用多说,

调用示例:

        AsyncCallback callback = delegate(IAsyncResult result)
                                     {
                                         IView2MainContainer.StopWaiting();
                                         try
                                         {
                                             AsyncHelper.EndInvoke(result);
                                         }
                                         catch (Exception ex)
                                         {
                                             RescoMessageBoxHelper.ShowOK(
                                                 string.Format(StringResources.Message_FirmwareUpdateError,
                                                               ex.Message), MessageBoxIcon.Hand);
                                             return;
                                         }
                                         RescoMessageBoxHelper.ShowOK(StringResources.Message_FirmwareUpdateComplete, MessageBoxIcon.Asterisk);
                                     };

        AsyncHelper.BeginInvoke(
            callback,
            delegate
                {
                    IView2MainContainer.StartWaiting(StringResources.Message_WaitFirmwareUpdate);
                    presenterModel.CurrentDevice.UpdatePrinterFirmware(firmwareDataBuffer);
                }
            );

实施:

using System; 
using System.Collections.Generic;
using System.Threading;

namespace Allen.IView2.Common
{
  public static class AsyncHelper
  {
    private static readonly object resultsLock = new object();
    private static volatile List<AsyncResultImpl> results = new List<AsyncResultImpl>();

    public static void EndInvoke(IAsyncResult result)
    {
        if (!(result is AsyncResultImpl))
            throw new InvalidOperationException("The async result was of an unknown type.");

        lock (resultsLock)
        {
            if (!results.Contains((AsyncResultImpl) result))
            {
                throw new InvalidOperationException("The async result was unknown");
            }
            results.Remove((AsyncResultImpl) result);
        }
        ((AsyncResultImpl) result).AsyncWaitHandle.WaitOne();

        Exception ex = ((AsyncResultImpl) result).Exception;
        ((AsyncResultImpl) result).Dispose();

        if (ex != null)
        {
            throw ex;
        }
    }

    public static void BeginInvoke(AsyncCallback callback, Action action)
    {
        var result = new AsyncResultImpl(callback, null);

        lock (resultsLock)
        {
            results.Add(result);
        }

        ThreadPool.QueueUserWorkItem(delegate
                                         {
                                             try
                                             {
                                                 action.Invoke();
                                             }
                                             catch (Exception ex)
                                             {
                                                 result.Complete(ex);
                                                 return;
                                             }
                                             result.Complete();
                                         });
    }
}
}

using System;
using System.Threading;

namespace Allen.IView2.Common
{
  public class AsyncResultImpl : IAsyncResult, IDisposable
  {
    private AsyncCallback callback;
    private Exception exception;
    private object state;
    private ManualResetEvent waitHandle;

    public AsyncResultImpl(AsyncCallback callback, object state)
    {
        this.callback = callback;
        this.state = state;
        waitHandle = new ManualResetEvent(false);
    }

    public ManualResetEvent AsyncWaitHandle
    {
        get { return waitHandle; }
    }

    public Exception Exception
    {
        get { return exception; }
    }

    public object AsyncState
    {
        get { return state; }
    }

    WaitHandle IAsyncResult.AsyncWaitHandle
    {
        get { return AsyncWaitHandle; }
    }

    public bool CompletedSynchronously
    {
        get { return false; }
    }

    public bool IsCompleted
    {
        get { return waitHandle.WaitOne(0, false); }
    }

    public void Dispose() // lazy dispose
    {
        if (null != waitHandle)
        {
            waitHandle.Close();
            waitHandle = null;
            state = null;
            callback = null;
        }
    }

    public void Complete()
    {
        Complete(null);
    }

    public void Complete(Exception exception)
    {
        this.exception = exception;
        waitHandle.Set();
        if (null != callback)
            if (callback.GetInvocationList().Length > 1)
                throw new InvalidOperationException("A callback must not be multicast.");
        if (null != callback)
        {
            try
            {
                callback(this);
            }
            catch
            {
// nom nom
            }
        }
    }
}
}