2

好了,時間讓我問我的第一個問題就在這裏.......NET CF 3.5窗體交談以僅同步網絡設備

我創造了許多意見(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,但我想這些會很簡單,重新實現?

我應該創建一個靜態視圖中心模型嗎?這甚至會簡化事宜嗎?

我應該創建一個額外的「inbetween」線程來處理model/view和driver之間的狀態轉移嗎?這甚至會簡化事宜嗎?

...?

我想我在這裏尋找一些關於一般設計原則的頭腦風暴。我從來沒有太多的用戶界面人員,大多數人一直在做系統編程,從來沒有更多的.NET比我可以避免。異步編程的概念非常清晰,而不是如何在這種情況下應用它們。

所以..任何輸入讚賞,也書推薦等...

回答

1

聲音的方式,方式,太多像我目前正在研究的項目。我有硬件,我一次只能發送一個命令。這些命令可能會超時,或者可能會等到接收器喚醒。命令作爲回覆進入,或作爲未經請求的數據消息進入。

我沒有什麼實際創建一些名字幾層,我只是做了現在

最底層 - 通信總線:

  • 知道如何發送和接收。
  • 知道如何將輸入數據解析爲「幀」,但沒有更多(它知道分隔符和校驗和)。這實際上是在OpenNETCF.IO串行庫的緩衝線程中完成的。
  • 放解析幀到隊列中,以儘量減少接收器線程忙狀態
  • 擁有工作線程手錶RX隊列和調度幀
  • 自曝一個簡單的「發送」接口(發送在上面的層的處理)

接着層 - (?)API層

設備監視器

  • 誰知設備存在什麼目的
  • 知道,當我上次從他們
  • 從通信總線接收幀聽取並把它們放在一個隊列(以便不阻塞接收機)
  • 知道如何更好地剖析框架
  • 提高消息的特定事件
  • 發送消息(從消息處理器檢索),當一個節點上線時
  • 具有兩個工作線程:
    • TxThread通過從闕 消息處理程序
    • RxThread接收拉動處理「一個且僅一個輸出消息」的問題手錶輸入幀隊列,解析並調度那些消息

消息管理器

  • 知道什麼消息需要去什麼設備
  • 知道每個消息的狀態(未發送,等待響應,超時等)
  • 允許過濾「發送」,「超時」等。項

服務層

  • 消耗和抽象API層
  • 自曝對象和事件到應用程序(直接和通過REST)
  • 是否數據聚集(平均值,電池監控,等)
  • 包含業務規則/邏輯

這裏的關鍵是我沒有明確實現任何異步API,儘管庫中的幾乎所有內容都是異步行爲。 UI只是查找傳入內容的事件(INotifyPropertyChanged豐富)。對於傳出,它執行類似於「請求將引腳1置高」,這是非阻塞的。服務層執行一些檢查來驗證遠程端實際發生的操作,但UI只是查看「pin狀態」屬性,以及實際更改爲高時(如服務層中驗證),它會更新。

+0

肯定有一些similiarities那裏。我甚至正在考慮今天創建類似於TxThread的東西。謝謝您的意見。另外,如果你是我認爲你的ctacke,你的着作對我來說已經非常有價值。謝謝你。 –

+0

小小更新:我正在積極研究這個問題,並最終將「正確回答」我自己的問題,以便讓其他人獲益。從你的抽象描述中我猜你正在做類似modbus/tcp的事情。相信我,這更糟,協議是專有的,並以各種方式和地點破碎。很多時間都會進入這個領域以及其他領域,所以我不能投入大量時間來構建良好的架構。從你發佈的內容派生出的模式是有道理的,但我需要做明確的異步內容。我剛剛記下了一些僞代碼,並將在明天下班後再次發佈。 –

+0

此刻我正在做zigbee,但是,我過去做過modbus。如果你的工作更糟糕,那麼你有我的同情心。 – ctacke

0

我最終結束了一堆特定的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 
      } 
     } 
    } 
} 
}