C#實現完整的防盜自制監控系統
在您的手機中通知您家中的入侵者,並拍攝他們的照片
介紹
在本文中,我將展示一些DIY東西??,用於安裝監控系統,檢測家中的入侵者,拍攝照片並通過手機通知您,必要時可以打電話給警察並提供照片以便快速識別劫匪,並提高你恢復所有被盜事物的機會。
當然,除了這個軟件,你必須提供一些硬件,但我已經在我家使用相對便宜的材料建造了這個系統,如果我們除了相機,這是安裝中最昂貴的部分。但你可以用相機做很多事情,所以它可以是一個好的和有趣的投資。
基本上,這是系統架構,包含所有參與元素:
雖然在模式中我已經代表了一些具體的子系統,但實際上我已經設計了解決方案,以便通過實現公共接口並使用依賴註入將它們與應用程序鏈接,可以獨立開發所有這些元素。不同的子系統或協議如下:
- 攝像機協議:定義與攝像機的通信。
- 存儲協議:定義文件傳輸,圖像和控制命令/響應。
- 觸發協議:啟動監控系統。
- 警報協議:將事件遠程傳達給用戶。
該解決方案使用Visual Studio 2015和4.5版的.Net Framework實現。
你可以在我的博客中找到這篇文章的更長版本網站源碼,這裏有西班牙文版本。由於此站點的文件大小限制為10MB,我不得不刪除源代碼中的大量文件,所有NuGet包,obj目錄和所有二進制文件。雖然您可以從Visual Studio還原包,但您可能無法重新編譯代碼。在這種情況下,您可以從我的網站,在上一個鏈接中下載項目的完整文件集。
硬件
讓我們回顧一下我用來構建系統的硬件。由於應用程序可以通過多種方式進行擴展,因此您可以使用自己的不同硬件選擇來安裝它。
首先是相機。我有兩個IP攝像頭,每個都有不同的協議。更便宜的是一個概念性的wifi攝像頭,價格約50€和協議NetWave cgi。另一種是專業的,具有高性能,但也是非常高的價格。這是一款采用VAPIX cgi協議的Axis相機。
為了撥打移動電話,我買了一個簡單的USB AT調制解調器,價格約為17歐元:
當然,作為觸發器,我使用的是Arduino板(約20歐元),存在探測器開關(約10歐元)和繼電器。由於存在開關適用於220V,因此將其直接連接到Arduino板是一個壞主意。因此,我已將探測器連接到12V電源,並將繼電器的電源連接到另一個開關,該電源關閉5V Arduino電源和輸入引腳之間的電路。這完全隔離了220V主電源的Arduino板(以及計算機)。
你可以輕松地建立一個繼電器電路。只需將12個電源連接到繼電器卷軸,將二極管從地線連接到12V電線,然後從Arduino側使用輸入引腳(PI)作為觸發引腳,輸出引腳(PO)用力電路開路時輸入引腳為0V,5V電源信號激活輸入引腳:
這是Arduino代碼,我使用引腳28作為輸入,24使用輸出,因為在Arduino Mega板中它們靠近5V引腳,但是你可以使用你想要的,當然。
int pin1 = 28; int pin0 = 24; void setup() { // Initialize pins pinMode(pin0, OUTPUT); digitalWrite(pin0, LOW); pinMode(pin1, INPUT); digitalWrite(pin1, LOW); Serial.begin(9600); } void loop() { int val = digitalRead(pin1); if (val == HIGH) { Serial.write(1); } delay(1000); }
最後,雖然這不是真正的硬件,但我會提到我使用過的存儲協議。我選擇Dropbox作為將照片上傳到雲端的最簡單,最便宜的方式,我還使用此媒體將移動客戶端與控制中心進行通信,使用帶有JSON格式數據的文本文件。
控制中心
在ThiefWatcher項目中,實現了中央控制應用程序。它是一個桌面MDI Windows應用程序,基本上有兩種不同的窗口類型。其中一個是控制面板,您可以在其中設置所有協議,而不是攝像機:
頂部窗格用於觸發器協議。在這裏,您可以選擇要使用的協議,提供具有相應設置的連接字符串(可以從協議到另一個不同),系統必須啟動監視模式的開始日期/時間(如果您不提供,系統啟動(中間),停止監視的結束日期/時間,您可以配置檢測到入侵者時拍攝的照片數量和照片之間的秒數(整數)。
此窗格下方是通知(警報)協議。在下拉列表右側選擇協議,您有一個測試按鈕,允許您測試此協議,而無需進行任何模擬。您還必須提供帶參數設置的字符串連接,並在協議允許數據傳輸的情況下提供可選消息。
底部窗格用於存儲協議。您有一個連接字符串來設置參數(如果有)和一個用於存儲數據的容器名稱,可以是本地文件夾,FTP文件夾,Azure blob容器名稱等。
命令按鈕從左到右依次為Start Simulacrum,它啟動或停止系統,就像檢測到入侵者一樣,因此您可以測試攝像機和存儲協議以及與客戶端的通信。在此模式下,不考慮開始和結束日期。接下來,“ 開始”按鈕啟動或停止實際監控模式。相機形式中沒有顯示圖像(假設沒有人在場)。最後,“ 保存”按鈕會在配置文件中寫入更改。
在代碼使用部分,我將評論我已實現的所有協議的連接字符串的參數。
關於攝像機協議,每個攝像機的配置都在攝像機窗口中執行,您可以使用File / New Camera ...菜單選項顯示攝像機窗口。首先,您必須為要添加的攝像機選擇正確的攝像機協議,然後,您必須提供連接數據,攝像機URL,用戶名和密碼。然後,你可以看到這樣一個窗口:
工具欄左側的第一個按鈕用於更改訪問設置,第二個按鈕用於顯示相機設置對話框,該對話框在相應的協議中實現。然後,您有一個啟動按鈕和其他停止相機的按鈕,因此您可以在配置相機時觀看圖像。攝像機ID必須是唯一的並且是必需的,因為您將使用此ID從客戶端選擇攝像機。最後兩個按鈕用於將攝像機保存在配置文件中或將其刪除。
所有這些設置都存儲在應用程序 App.config文件中。connectionStrings部分中的連接字符串,appSettings部分中的其他協議設置。還有兩個自定義部分用於存儲協議列表以及不同的攝像機及其設置。
該cameraSection islike這樣的:
<camerasSection> <cameras> <cameraData id="CAMNW" protocolName="NetWave IP camera" connectionStringName="CAMNW" /> <cameraData id="VAPIX" protocolName="VAPIX IP Camera" connectionStringName="VAPIX" /> </cameras> </camerasSection>
每個照相機是一個cameraData元件,具有一個ID屬性,protocolName與相應協議的名稱屬性,和一個的connectionStringName用於連接數據屬性:URL,userName的和密碼,存儲在一個連接字符串中的ConnectionStrings部分。
還有一個protocolsSection,包含已安裝協議的列表:
<protocolsSection> <protocols> <protocolData name="Arduino Simple Trigger" class="trigger" type="ArduinoSimpleTriggerProtocol.ArduinoTrigger, ArduinoSimpleTriggerProtocol, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <protocolData name="Lync Notifications" class="alarm" type="LyncProtocol.LyncAlarmChannel, LyncProtocol, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <protocolData name="AT Modem Notifications" class="alarm" type="ATModemProtocol.ATModemAlarmChannel, ATModemProtocol, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <protocolData name="Azure Blob Storage" class="storage" type="AzureBlobProtocol.AzureBlobManager, AzureBlobProtocol, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <protocolData name="NetWave IP camera" class="camera" type="NetWaveProtocol.NetWaveCamera, NetWaveProtocol, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <protocolData name="VAPIX IP Camera" class="camera" type="VAPIXProtocol.VAPIXCamera, VAPIXProtocol, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <protocolData name="DropBox Storage" class="storage" type="DropBoxProtocol.DropBoxStorage, DropBoxProtocol, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </protocols> </protocolsSection>
每個協議有一個名字,一類,以確定它們的用法(觸發,報警,存儲或相機)和類型與所述完整的類型,其實現協議的類。
您可以使用File / Install Protocol / s ...菜單選項向此部分添加新協議,選擇具有協議或協議實現的類庫。
客戶
無論您身在何處,應用程序都必須通知您可能的入侵,因此我將客戶端實現為移動應用程序。幾乎所有平臺快速擁有應用程序版本的最簡單方法是使用Xamarin來執行此操作,因此這是我選擇的方法。
該TWClientApp PCL(便攜式類庫)項目包含在客戶端幾乎所有的代碼。在不同平臺的具體項目中,只有代碼保存文件,將攝像頭拍攝的照片保存在手機內存中,以便您盡快將其提供給警方。
這是我的第一個移動App項目,所以它不是很復雜。這裏我沒有使用依賴註入。相反,我只實現了Dropbox存儲協議,因此,如果要使用另一個協議,則必須更改PCL庫中的代碼。此協議的優點是您可以使用Dropbox實際客戶端獲取照片,而無需使用ThiefWatcher客戶端(盡管您失去了應用程序控制功能)。
啟動客戶端應用程序時,必須按“ 連接”按鈕才能向主應用程序發送標識消息:
然後,將相機列表發送到客戶端。您可以按相應的按鈕選擇其中一個:
您可以觀看相機的當前圖像。通常,您不能等待真正的視頻流,因為上傳每個圖像可能會非常慢。中央控制實時獲取幀,但Dropbox每上傳花費最多兩秒鐘。
您可以使用按鈕啟動/停止相機,拍照或結束鬧鐘模式(在結束鬧鐘模式之前無需停止相機)。
照片顯示在底部的列表中,您可以將其保存到手機或刪除它們。
我無法測試iOS版本,因為我沒有MAC,但Windows Phone和Android Apps工作正常。
使用代碼
不同的協議接口在WatcherCommons項目的Interfaces名稱空間中定義。攝像機協議是IWatcherCamera,定義如下:
public class FrameEventArgs : EventArgs { public FrameEventArgs(Bitmap bmp) { Image = bmp; } public Bitmap Image { get; private set; } } public delegate void NewFrameEventHandler(object sender, FrameEventArgs e); public interface IWatcherCamera { event NewFrameEventHandler OnNewFrame; Size FrameSize { get; } string ConnectionString { get; set; } string UserName { get; set; } string Password { get; set; } string Uri { get; set; } int MaxFPS { get; set; } bool Status { get; } ICameraSetupManager SetupManager { get; } void Initialize(); void ShowCameraConfiguration(Form parent); void Start(); void Close(); }
- OnNewFrame:當圖像準備好發送到應用程序時觸發事件處理程序。圖像在FrameEventArgs參數的Image屬性中作為Bitmap傳遞。
- FrameSize:攝像機圖像的當前寬度和高度。
- ConnectionString:用分號分隔的字符串,用於定義攝像機訪問參數。在我實現的協議中,參數是url,userName和password,如下所示:url = http://192.168.1.20; userName = root; password = root。
- UserName,Password和Uri:與連接字符串中的相同。
- MaxFps:設置捕獲率。
- 狀態:如果攝像機正在運行,則為true。
- SetupManager:與攝像機設置對話框的界面。用於在用戶更改攝像機圖像大小時在應用程序中觸發事件,以便可以正確調整攝像機表單的大小。
- 初始化:根據需要重置內部狀態。
- ShowCameraConfiguration:顯示攝像機配置對話框。它必須不是模態的,因此如果相機正在顯示圖像,您可以觀察更改。
- 開始:開始圖像捕獲。這是在一個單獨的線程中執行的,您必須在新幀事件中與相機交互時將其考慮在內。
- 停止:停止捕獲。
該NetWave協議在實施NetWaveProtocol項目和VAPIX在協議VAPIXProtocol項目。
觸發器協議ITrigger如下:
public interface ITrigger { event EventHandler OnTriggerFired; string ConnectionString { get; set; } void Initialize(); void Start(); void Stop(); }
- OnTriggerFired:在檢測到觸發條件時觸發。
- ConnectionString:帶有配置參數的字符串。在我已經實現的協議中,在ArduinoSimpleTriggerProtocol項目中,它們是端口和波特率,如下所示:port = COM4; baudrate = 9600。請記住在Arduino代碼中設置相同的波特率。
- 初始化:根據需要重置intarnal狀態。
- 開始:開始偵聽觸發條件。這是在一個單獨的線程中完成的。
- 停止:停止聽。
通知協議IAlarmChannel也很簡單:
public interface IAlarmChannel { string ConnectionString { get; set; } string MessageText { get; set; } void Initialice(); void SendAlarm(); }
- ConnectionString:帶有配置參數的字符串。
- MessageText:如果協議允許,則發送消息。
- 初始化:重置內部狀態。
- SendAlarm:向客戶端發送通知。
我實現的協議是ATModemProtocol項目,它使用AT調制解調器撥打一個或多個電話號碼,並具有以下配置參數:
- port:連接調制解調器的COM端口。
- 波特率:設置端口波特率。
- initdelay:撥號前等待的延遲時間(以毫秒為單位)。
- number:逗號分隔的電話號碼列表。
- ringduration:掛機前的時間,以毫秒為單位。
另一個協議使用Skype或Lync通知用戶。它在LyncProtocol項目中實現。連接字符串是以分號分隔的Skype或Lync用戶地址列表。您必須在主計算機和客戶端上安裝Lync客戶端。
後者是存儲協議,此協議使用的數據在WatcherCommons類庫的Data命名空間中定義。有兩個不同的類,ControlCommand用於攝像頭命令:
[DataContract] public class ControlCommand { public const int cmdGetCameraList = 1; public const int cmdStopAlarm = 2; public ControlCommand() { } public static ControlCommand FromJSON(Stream s) { s.Position = 0; StreamReader rdr = new StreamReader(s); string str = rdr.ReadToEnd(); return JsonConvert.DeserializeObject<ControlCommand>(str); } public static void ToJSON(Stream s, ControlCommand cc) { s.Position = 0; string js = JsonConvert.SerializeObject(cc); StreamWriter wr = new StreamWriter(s); wr.Write(js); wr.Flush(); } [DataMember] public int Command { get; set; } [DataMember] public string ClientID { get; set; } }
命令以JSON格式發送和接收。在Command成員中傳遞了兩個不同的commnand,一個用於向應用程序註冊並獲取攝像機列表,另一個用於停止警報並將應用程序重置為監視模式。
該客戶端ID構件唯一地標識每個客戶端。
CameraInfo也是以JSON格式交換有關攝像機的請求和響應:
[DataContract] public class CameraInfo { public CameraInfo() { } public static List<CameraInfo> FromJSON(Stream s) { s.Position = 0; StreamReader rdr = new StreamReader(s); return JsonConvert.DeserializeObject<List<CameraInfo>>(rdr.ReadToEnd()); } public static void ToJSON(Stream s, List<CameraInfo> ci) { s.Position = 0; string js = JsonConvert.SerializeObject(ci); StreamWriter wr = new StreamWriter(s); wr.Write(js); wr.Flush(); } [DataMember] public string ID { get; set; } [DataMember] public bool Active { get; set; } [DataMember] public bool Photo { get; set; } [DataMember] public int Width { get; set; } [DataMember] public int Height { get; set; } [DataMember] public string ClientID { get; set; } }
- ID:攝像機標識符。
- 活動:相機狀態。
- 照片:用於要求相機拍照。
- 寬度和高度:相機圖像尺寸。
- ClientID:客戶端唯一標識符。
當您請求攝像機列表時,您會收到一個帶有一系列CameraInfo對象的響應,每個攝像機對應一個。
實現協議的接口是IStorageManager:
public interface IStorageManager { string ConnsecionString { get; set; } string ContainerPath { get; set; } void UploadFile(string filename, Stream s); void DownloadFile(string filename, Stream s); void DeleteFile(string filename); bool ExistsFile(string filename); IEnumerable<string> ListFiles(string model); IEnumerable<ControlCommand> GetCommands(); IEnumerable<List<CameraInfo>> GetRequests(); void SendResponse(List<CameraInfo> resp); }
- ConnectionString:帶有配置參數的字符串。
- ContainerPath:標識文件夾,blob容器名稱等。
- UploadFile:發送Stream對象中提供的文件。
- DownloadFile:在提供的Stream對象中獲取文件。
- DeleteFile:刪除文件。
- ExistsFile:測試文件是否存在。
- ListFiles:枚舉文件夾中的文件,其名稱的開頭必須與模型參數匹配 。
- GetCommands:枚舉客戶端發送的命令。
- GetRequests:枚舉客戶端發送的攝像頭請求。
- SendResponse:發送命令或攝像機請求的響應。
我已經實現了兩個存儲協議。該DropBoxProtocol項目實施與使用的協議的Dropbox。在服務器端,這只是讀取和寫入Dropbox文件夾的文件。不需要連接字符串,因為文件夾是單獨配置的。
在客戶端中,這是實現的協議。它略有不同,界面在TWClientApp項目中定義:
public interface IStorageManager { Task DownloadFile(string filename, Stream s); Task DeleteFile(string filename); Task<bool> ExistsFile(string filename); Task<List<string>> ListFiles(string model); Task SendCommand(ControlCommand cmd); Task SendRequest(List<CameraInfo> req); Task<List<CameraInfo>> GetResponse(string id); }
它是一個異步接口,成員數少於服務器端。實現並不像服務器那麽容易; 我們必須使用Dropbox API與之交互。實現在DropBoxStorage類中,並且在_accessKey常量中,您必須將安全密鑰設置為成功建立連接(在第一次編譯代碼之前不要忘記這樣做,因為沒有默認值)。
private const string _accessKey = "";
客戶端App的幾乎所有代碼都在TWClientApp項目中,在CameraPage類中。數據的交換協議是通過文件,每個文件都有一個特殊的名稱來識別它。這些是不同的文件名模式:
- 攝像機只寫一個幀文件,當客戶端讀取幀時,它刪除文件,服務器可以寫另一個。該文件是jpg圖片,名稱為<CAMERA ID> _FRAME_ <CLIENT ID> .jpg。
- 照片的名稱相似,可能有多張照片。名稱模式為:<CAMERA ID> _PHOTO_yyyyMMddHHmmss.jpg。
- 客戶端可以以JSON文本格式和名稱cmd_ <CLIENT ID> .json將命令一次發送到服務器。
- 當服務器獲取命令文件時,它會刪除該文件,因此客戶端可以發送另一個命令,並執行該命令。然後,它編寫一個名為resp_ <CLIENT ID> .json的響應文件。
- 最後,客戶端可以發送相機請求,例如拍攝照片,或以JSON格式在名為req_ <CLIENT ID> .json的文件中啟動或停止相機。服務器讀取文件,刪除它,並將請求傳遞給攝像機進行處理,然後,服務器寫入響應文件,就像命令一樣,具有攝像機狀態。
該NetWave相機協議配置對話框非常簡單,你可以閱讀更多關於此協議中我的博客。
至於VAPIX協議,它更復雜,因為它是專業相機的協議。我沒有使用包含大量控件的復雜對話框,而是實現了一個包含所有配置參數的樹視圖(它們是很多配置參數),您可以在其中選擇每個參數並更改值。您也可以在我的博客中閱讀更多相關信息。
這就是全部,享受解決方案,並感謝閱讀!
C#實現完整的防盜自制監控系統