1. 程式人生 > >《連載 | 物聯網框架ServerSuperIO教程》- 3.裝置驅動介紹

《連載 | 物聯網框架ServerSuperIO教程》- 3.裝置驅動介紹

3.1    概述

     定位ServerSuperIO(SSIO)為物聯網通訊框架,就是因為這個框架是以“裝置”(驅動)為核心構建,“裝置”是泛指感測器、下位機、PC機等各類資料來源,資料來源有自己的通訊協議或資料傳輸格式;ServerSuperIO並不是以IO通道為核心構建的框架,但是ServerSuperIO有很好的通訊能力,完全可以部署在服務端,並且支援多個服務例項,以及可以在Linux下執行。

3.2    介面定義

   ServerSuperIO的“裝置”統一介面定義為IRunDevice,這是在框架內部執行、排程、與IO通道協作的唯一介面。程式碼定義如下:

public interface IRunDevice: IServerProvider,IVirtualDevice
    {
        #region 函式介面

        /// <summary>
        /// 初始化裝置,載入裝置驅動的頭一件事就是初始化裝置
        /// </summary>
        /// <param name="devid"></param>
        void Initialize(string devid);

        /// <summary>
        /// 儲存原始的byte資料
        /// </summary>
        /// <param name="data"></param>
        void SaveBytes(byte[] data, string desc);
        
        /// <summary>
        /// 獲得傳送資料的命令,如果命令快取中沒有命令,則呼叫獲得實時資料函式
        /// </summary>
        /// <returns></returns>
        byte[] GetSendBytes();

        /// <summary>
        /// 如果當前命令快取沒有命令,則呼叫該函式,一般返回獲得裝置的實時資料命令,
        /// </summary>
        /// <returns></returns>
        byte[] GetConstantCommand();

        /// <summary>
        /// 傳送IO資料介面
        /// </summary>
        /// <param name="io"></param>
        /// <param name="senddata"></param>
        int Send(IChannel io, byte[] senddata);

        /// <summary>
        /// 讀取IO資料介面
        /// </summary>
        /// <param name="io"></param>
        /// <returns></returns>
        byte[] Receive(IChannel io);

        /// <summary>
        /// 接收資料資訊,帶過濾器
        /// </summary>
        /// <param name="io"></param>
        /// <param name="receiveFilter"></param>
        /// <returns></returns>
        IList<byte[]> Receive(IChannel io, IReceiveFilter receiveFilter);

        /// <summary>
        /// 同步執行裝置(IO)
        /// </summary>
        /// <param name="io">io例項物件</param>
        void Run(IChannel io);

        /// <summary>
        /// 同步執行裝置(byte[])
        /// </summary>
        /// <param name="key"></param>
        /// <param name="channel"></param>
        /// <param name="revData">接收到的資料</param>
        void Run(string key, IChannel channel, IRequestInfo ri);

        /// <summary>
        /// 如果通訊正常,這個函式負責處理資料
        /// </summary>
        /// <param name="info"></param>
        void Communicate(IRequestInfo info);

        /// <summary>
        /// 通訊中斷,未接收到資料
        /// </summary>
        void CommunicateInterrupt(IRequestInfo info);

        /// <summary>
        /// 通訊的資料錯誤或受到干擾
        /// </summary>
        void CommunicateError(IRequestInfo info);

        /// <summary>
        /// 通訊未知,預設狀態(一般不用)
        /// </summary>
        void CommunicateNone();

        /// <summary>
        /// 檢測通訊狀態
        /// </summary>
        /// <param name="revdata"></param>
        /// <returns></returns>
        CommunicateState CheckCommunicateState(byte[] revdata);

        /// <summary>
        /// 報警介面函式
        /// </summary>
        void Alert();

        /// <summary>
        /// 儲存解析後的資料
        /// </summary>
        void Save();

        /// <summary>
        /// 展示
        /// </summary>
        void Show();

        /// <summary>
        /// 當通訊例項為NULL的時候,呼叫該函式
        /// </summary>
        void UnknownIO();

        /// <summary>
        /// 通訊狀態改變
        /// </summary>
        /// <param name="comState">改變後的狀態</param>
        void CommunicateStateChanged(CommunicateState comState);

        /// <summary>
        /// 通道狀態改變
        /// </summary>
        /// <param name="channelState"></param>
        void ChannelStateChanged(ChannelState channelState);


        /// <summary>
        /// 當軟體關閉的時間,響應裝置退出操作
        /// </summary>
        void Exit();

        /// <summary>
        /// 刪除裝置的響應介面函式
        /// </summary>
        void Delete();

        /// <summary>
        /// 可以自定義返資料物件,用於與其他元件互動
        /// </summary>
        /// <returns></returns>
        object GetObject();

        /// <summary>
        /// 裝置定時器,響應定時任務
        /// </summary>
        void OnRunTimer();

        /// <summary>
        /// 顯示上下文選單
        /// </summary>
        void ShowContextMenu();

        /// <summary>
        /// 顯示IO監視器的窗體
        /// </summary>
        void ShowMonitorDialog();

        /// <summary>
        /// 在IO監視器上顯示byte[]資料
        /// </summary>
        /// <param name="data"></param>
        /// <param name="desc"></param>
        void ShowMonitorData(byte[] data, string desc);
        #endregion

        #region 屬性介面
        /// <summary>
        /// 預設程式集ID,用於儲存臨時物件
        /// </summary>
        object Tag { set; get; }

        /// <summary>
        /// 同步物件,用於IO互拆
        /// </summary>
        object SyncLock { get; }

        /// <summary>
        /// 實時資料持久介面
        /// </summary>
        IDeviceDynamic DeviceDynamic { get; }

        /// <summary>
        /// 裝置引數持久介面
        /// </summary>
        IDeviceParameter DeviceParameter { get; }

        /// <summary>
        /// 協議驅動
        /// </summary>
        IProtocolDriver Protocol { get; }

        /// <summary>
        /// 是否開啟時鐘,標識是否呼叫OnRunTimer介面函式。
        /// </summary>
        bool IsRunTimer { set; get;}

        /// <summary>
        /// 時鐘間隔值,標識定時呼叫DeviceTimer介面函式的週期
        /// </summary>
        int RunTimerInterval { set; get; }

        /// <summary>
        /// 裝置的型別
        /// </summary>
        DeviceType DeviceType { get;  }

        /// <summary>
        /// 裝置編號
        /// </summary>
        string ModelNumber { get;}

        /// <summary>
        /// 裝置執行許可權級別,如果執行級別高的話,則優先發送和接收資料。
        /// </summary>
        DevicePriority DevicePriority { get;set;}

        /// <summary>
        /// 裝置的通訊型別
        /// </summary>
        CommunicateType CommunicateType { get;set;}

        /// <summary>
        /// 標識是否執行裝置,如果為false,呼叫執行裝置介面時直接返回
        /// </summary>
        bool IsRunDevice{ get;set;}

        /// <summary>
        /// 是否釋放資源
        /// </summary>
        bool IsDisposed { get; }

        /// <summary>
        /// 顯示檢視
        /// </summary>
        Control DeviceGraphics { get;}
        #endregion

        #region 事件介面

        /// <summary>
        /// 傳送資料事件
        /// </summary>
        event SendDataHandler SendData;

        /// <summary>
        /// 傳送資料事件,對SendDataHandler事件的封裝
        /// </summary>
        /// <param name="senddata"></param>
        void OnSendData(byte[] senddata);

        /// <summary>
        /// 裝置日誌輸出事件
        /// </summary>
        event DeviceRuningLogHandler DeviceRuningLog;

        /// <summary>
        /// 執行監視器顯示日誌事件,對DeviceRuningLogHandler事件的封裝
        /// </summary>
        void OnDeviceRuningLog(string statetext);

        /// <summary>
        /// 串列埠引數改變事件
        /// </summary>
        event ComParameterExchangeHandler ComParameterExchange;

        /// <summary>
        /// 串列埠引數改變事件,對COMParameterExchangeHandler事件的封裝
        /// </summary>
        void OnComParameterExchange(int oldcom, int oldbaud, int newcom, int newbaud);

        /// <summary>
        /// 裝置資料物件改變事件
        /// </summary>
        event DeviceObjectChangedHandler DeviceObjectChanged;
        /// <summary>
        /// 資料驅動事件,對DeviceObjectChangedHandler事件的封裝
        /// </summary>
        void OnDeviceObjectChanged(object obj);

        ///// <summary>
        ///// 刪除裝置事件
        ///// </summary>
        //event DeleteDeviceCompletedHandler DeleteDeviceCompleted;
        ///// <summary>
        ///// 刪除裝置事件,對DeleteDeviceHandler事件的封裝
        ///// </summary>
        //void OnDeleteDeviceCompleted();
        #endregion
}

3.3    二次開發,常用介面

    RunDevice抽象類繼承自IRunDevice介面,本質上來講,二次開發只需要繼承RunDevice抽象類就可以了。RunDevice已經完成了裝置驅動在ServerSuperIO框架下基本的條件。那麼在繼承RunDevice抽象類的時候,所需要二次開發的工作很小,只需要關注協議和處理資料業務本身,對於框架的內部執行機制可以配置、排程機制內部自動處理。

    二次開發常用的介面,專案示意如下圖:

     這是一個按協議規則完成實時接收檔案資料的裝置驅動,ReceiveFileDriver繼承自RunDevice抽象類,是裝置驅動的核心介面;Protocol是自定義協議介面,包括髮送資料協議和接收資料協議,例項化後在ReceiveFileDriver介面的IProtocolDriver屬性中返回;不光要有協議介面,在協議裡邊還要有命令,那麼Command就是協議中的自定義命令,這個協議命令規定了傳送資料和接收解析資料,協議命令不僅僅包括一個,根據業務需要,裝置驅動可以包含多個協議命令,以完成與實體硬體的互動;Parameter自定義引數介面,每個裝置物件本身的引數不一樣,例項化後在ReceiveFileDriver介面的IDeviceParameter屬性中返回;Dynamic是自定義實時資料臨時快取介面,可以把解析後的實時資料臨時儲存在這個物件裡,,例項化後在ReceiveFileDriver介面的IDeviceDynamic屬性中返回,這個物件不是必須要實現的,ServerSuperIO內部並沒有直接引用。

     以上是從專案角度大體闡述了需要寫哪幾類程式碼,ReceiveFileDriver裝置驅動需要二次開發寫的程式碼,如下示意圖:

1)        Initialize:初始化裝置驅動介面,在這裡可以初始化協議驅動、裝置引數和裝置的實時資料等物件,以及完成一些其他的操作。

2)        DeviceDynamic:實時資料物件介面屬性,不是必須實現。

3)        DeviceParameter:引數資料物件介面屬性,必須實現。

4)        Protocol:協議驅動介面,很關鍵,必須實現。

5)        DeviceType:裝置型別,一般指普通裝置,虛擬裝置適用於特殊情況,必須實現。

6)        ModelNumber:裝置驅動的唯一編號,一般必須實現。

7)        GetConstantCommand:固定返回傳送資料的介面,一般返回讀實時資料命令,在SendCache裡沒有命令的時候,會呼叫該介面,必須實現。

8)        Communicate:通訊正常,會把資料返回到這個介面,影響通訊狀態為Protocol介面中的CheckData函式,校驗接收到資料的完整性。

9)        CommunicateInterrupt:通訊中斷代表沒有接收到任何資料,會呼叫這個介面,影響通訊狀態為Protocol介面中的CheckData函式,校驗接收到資料的完整性。

10)    CommunicateError:通訊干擾代表接收到資料,但是沒有校驗正確,會呼叫這個介面,影響通訊狀態為Protocol介面中的CheckData函式,校驗接收到資料的完整性。

11)    CommunicateNone:通訊未知,代表該裝置驅動對應的IO通道COM沒有開啟或NET沒有有效連線。

12)    UnknownIO:未知IO通道,COM沒有開啟或NET沒有有效連線。

13)    CommunicateStateChanged:每次通訊狀態改變後會呼叫這個介面函式。

14)    ChannelStateChanged:IO通道狀態改變後會呼叫這個介面函式,COM是開啟或關閉,NET是連線或斷開。

15)    Save:儲存資料函式介面,裝置驅動執行到生命週期最後會呼叫這個介面,以完成對資料的儲存。

16)    Alert:報警函式介面,資料處理完成後,對資料進行判斷,以完成報警功能。

17)    Show:顯示資料函式介面,資料處理完成後,對資料進行顯示,是SIO保留下來的介面函式,以後擴充套件使用。

18)    Exit:當裝置驅動退出時呼叫該函式介面,例如:宿主程式退出時。

19)    Delete:刪除裝置驅動呼叫該函式介面,是SIO保留下來的介面函式,適用於有介面UI的應用場景,以後擴充套件使用。

20)    DeviceGraphics:檢視介面,是SIO保留下來的介面函式,以後擴充套件使用。

21)    GetObject:獲得資料物件,以後打算為服務介面供資料支援,現在沒有實際用處。

22)    ShowContextMenu:顯示上下文選單,是SIO保留下來的介面函式,以後擴充套件使用。