【问题标题】:.NET CF 3.5 Forms talking to sync-only network device.NET CF 3.5 Forms 与仅同步网络设备对话
【发布时间】:2012-06-20 22:27:32
【问题描述】:

好的,是时候在这里问我的第一个问题了......

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

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

有点像这样,在“驱动程序”界面中:

public Response ExecuteCommand(Command command);

public event DeviceMessageReceivedEvent;

public Queue DeviceMessages;

还有一个便利的应用程序外观,但它基本上只是 ExecuteCommand 调用的精美包装,所以我将在此处省略。

问题:

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

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

CF 不提供 BeginInvoke 和 EndInvoke,但我想这些很容易重新实现?

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

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

...?

我想我在这里寻找一些关于一般设计原则的头脑风暴。我从来都不是一个 UI 人,主要是在做系统编程,从来没有比我能避免的更多的 .NET。异步编程的概念很清楚,只是不知道如何在这种情况下应用它们。

所以.. 任何意见表示赞赏,还有书籍推荐等...

【问题讨论】:

    标签: .net winforms multithreading asynchronous compact-framework


    【解决方案1】:

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

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

    最低层 - 通信总线:

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

    下一层 - API (?) 层

    设备监视器

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

    消息管理器

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

    服务层

    • 使用和抽象 API 层
    • 向应用程序公开对象和事件(直接和通过 REST)
    • 是否进行数据聚合(平均值、电池监控等)
    • 包含业务规则/逻辑

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

    【讨论】:

    • 那里肯定有一些相似之处。我什至正在考虑创建类似于您今天的 TxThread 的东西。谢谢您的意见。此外,如果你是我认为的 ctacke,你的作品对我来说已经非常有价值。谢谢你。
    • 小更新:我正在积极解决这个问题,并最终会正确地“回答”我自己的问题,以造福其他人。从您的抽象描述中,我猜您正在做类似 modbus/tcp 的事情。相信我,这更糟糕,该协议是专有的,并且在各种方式和地方都被破坏了。很多时间都花在了这个和其他领域,所以我不能把大部分时间花在提出好的架构上。从您发布的内容中得出的模式是有意义的,但我需要做明确的异步工作。我刚刚记下了一些伪代码,明天下班后再发。
    • 我现在正在做 zigbee,但是是的,我过去做过 modbus。如果你的工作更糟,那么我很同情你。
    • 抱歉没有更新,我很忙。但是,刚刚发现这篇非常有用的文章:msdn.microsoft.com/en-us/magazine/cc163427.aspx#S1 如果您对 .NET 线程比较陌生,那么这样的材料非常宝贵...
    【解决方案2】:

    我最终得到了一堆特定的 IAsyncResult Begin/End 调用,以及其他有趣的东西,导致解决方案不是很漂亮或不太适合单核单管道 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
                }
            }
        }
    }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-20
      • 1970-01-01
      • 1970-01-01
      • 2011-01-09
      相关资源
      最近更新 更多