Monitoring teploty: Sériová komunikace s hardwarem v .NET

Jednou z velmi užitečných vlastností platformy .NET je, že se neomezuje jenom na jeden typ aplikací. Ve stejném jazyce, vývojovém prostředí a stejnými technologiemi lze psát aplikace webové (Web Forms), desktopové (Windows Forms) i pro kapesní počítače (Compact Framework).

Ač to na první pohled nemusí být zřejmé, tato vlastnost je velice výhodná i pro čistokrevného webového programátora. Webové rozhraní prostě má svoje omezení a není vhodné pro všechny typy úkolů. Lze-li očekávat, že klient bude vybaven .NET Frameworkem, je možné řadu těchto omezení vyřešit napsáním jednoduché aplikace, která požadované operace provede. Se svým webovým protějškem pak může komunikovat například přes web service rozhraní.

Microsoft .NET 2.0 (Whidbey), toho času dostupný jako Beta 2, přináší řadu novinek, a to i pro vývoj desktopových aplikací. Rozhodl jsem se napsat v něm aplikaci, která bude schopna zobrazit aktuální teplotu, přičemž informace o ní bude čerpat jednak z veřejně dostupné webové služby, jednak z teplotního čidla připojeného na sériový port.

Použité technologie

  • Komunikace s hardwarovým zařízením přes COM port a protokol RS-232. Obsluha sériových portů je novinkou ve verzi 2.0.
  • Komunikace s webovou službou třetí strany.
  • Využití application frameworku, tedy funkcí Whidbey pro snazší vývoj desktopových aplikací.
  • Využití settings frameworku, nástroje pro snadnou práci s konfigurací.

Teplotní čidlo TM

Jako lokální teplotní čidlo jsem použil modul "TM" od firmy Papouch s. r. o. (viz papouch.com). Stejný výrobce nabízí i celou řadu dalších čidel (nejen teplotních), včetně připojení na USB nebo Ethernet, ale pro naše účely bohatě postačí toto nejjednodušší a nejlacinější.

Komunikační protokol a proof-of-concept

Zapojuje se na sériový port a funguje tak, že po připojení (s parametry 9600-8-N-1) a nahození signálu DTR změří teplotu a pošle ji na výstup jako jednoduchý ASCII řetězec (např. +025.5C). Proof-of-concept kód pro načtení aktuální teploty do numerické proměnné vypadá takto:

' Connect to serial port
Dim Port As New System.IO.Ports.SerialPort()
Port.PortName = "COM1"
Port.BaudRate = 9600
Port.DataBits = 8
Port.StopBits = IO.Ports.StopBits.One
Port.Parity = IO.Ports.Parity.None
Port.ReadTimeout = 1000
Port.DtrEnable = True
Port.Open()
' Get current value
Dim S As String = Port.ReadTo("C")
Dim T As Single = Single.Parse(S.Substring(0, 6), _
                  New System.Globalization.CultureInfo("en-US"))
MsgBox(String.Format("Current temperature is: {0:N2} °C", T))
Port.Close()
Port.Dispose()

Webová služba GlobalWeather

Vzhledem k významu počasí pro leteckou dopravu jsou ideálním zdrojem informací o počasí letecké systémy. Přístup do nich lze získat příkladně pomocí webové slyžby GlobalWeather, jejíž popis najdete na http://live.capescience.com/GlobalWeather/. Tato služba je zároveň důkazem interoperability technologie Web Services, protože neběží na platformě .NET, ale lze ji bez problémů z .NETu využívat.

Informace o počasí (nejen teplotě) je k dispozici pro každé mezinárodní letiště. Pro ČR lze využít následující:

  • Praha (kód LKPR)
  • Karlovy Vary (kód LKKV)
  • Brno (kód LKTB)
  • Holešov (kód LKHO)
  • Ostrava (kód LKMT)

Chcete-li tuto službu využívat, přidejte si Web reference na adresu http://live.capescience.com/wsdl/GlobalWeather.wsdl a nazvěte ji GlobalWeather. Tato webová služba vystavuje dvě třídy, které nás budou zajímat, a to StationInfo a GlobalWeather.

Zjištění informací o lokaci

Ke zjištění informací o lokaci jest možno využít metodu getStation třídy StationInfo. Tato třída obsahuje ještě další zajímavé metody, jimiž jest možno prohledávat seznam stanic. Budete-li chtít, můžete s jejich použitím rozšířit aplikaci tak, aby umožňovala např. výběr ze seznamu či prohledávání.

V této fázi se spokojím s tím, že pomocí webové služby získám pro danou lokaci její název a zemi, kde je umístěna. V okně nastavení (FormServiceOptions) tak identifikuji, zda byl zadán platný kód letiště:

Dim StationWS As New GlobalWeather.StationInfo()
Dim Station As GlobalWeather.Station = StationWS.getStation(Me.TextBoxWebLocationCode.Text)
If Station Is Nothing Then
    MsgBox("Location not found - check code", MsgBoxStyle.Exclamation)
Else
    MsgBox(String.Format("{0} is {1}, {2}", Me.TextBoxWebLocationCode.Text, Station.name, Station.country), MsgBoxStyle.Information)
End If
StationWS.Dispose()

Zjištění aktuální meteo situace na lokaci

Chceme-li na dané lokaci zjistit aktuální meteorologickou situaci, jejíž součástí je i teplota, použijete k tomu metodu getWeatherReport třídy GlobalWeather. Ta vrátí informace o lokaci a její meteorologické situaci:

Dim WeatherWS As New GlobalWeather.GlobalWeather()
Dim Report As GlobalWeather.WeatherReport = WeatherWS.getWeatherReport(My.Settings.ServiceLocationCode)
If Not Report Is Nothing Then
    Me.LabelService.Text = String.Format("{0:N1}°C", Report.temperature.ambient)
    Me.GroupBoxService.Text = String.Format("{0} ({1})", Report.station.name, Report.station.country)
    Me.LabelUpdateService.Text = Report.timestamp.Value.ToShortDateString() & " " & Report.timestamp.Value.ToShortTimeString()
End If
WeatherWS.Dispose()

Application Framework

Okno vlastností projektu (ukáže se po dvojkliknutí na "My Project" v Solution Exploreru) je ve Whidbey mnohem zajímavější, než tomu bylo v předchozích verzích. Zastavme se pro tento ukamžik u záložky "Application":

Screenshot 

Po povolení Enable Application Framework máte možnost pro svou aplikaci jednoduše nastavit způsob, jakým se celkově má chovat. Po klepnutí na View Application Events se vám otevře soubor ApplicationEvents.vb (standardně skrytý ve složce My Project), ve kterém můžete definovat event handlery pro události týkající se celé aplikace. Připomíná to mechanismus Global Application Class (alias global.asax) známý z webových ASP.NET aplikací.

Můžete například reagovat na neošetřenou výjimku a pokud ve vaší aplikaci nastane, nějak ji vyřešit - například zobrazit chybové hlášení a vygenerovat log:

Namespace My
    Class MyApplication
        Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
            If MsgBox("The follofing exception occured:" & vbCrLf & e.Exception.Message & vbCrLf & "Do you want to create log file?", MsgBoxStyle.Critical Or MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
                Dim LogFileName As String = My.Computer.FileSystem.GetTempFileName()
                My.Computer.FileSystem.WriteAllText(LogFileName, e.Exception.ToString(), False)
                System.Diagnostics.Process.Start("notepad.exe", LogFileName)
            End If
            e.ExitApplication = True
        End Sub
    End Class
End Namespace

Application Settings

Téměř každá aplikace má nějaká nastavení, která jest třeba někde ukládat. Obvykle v nějakém konfiguračním souboru, případně v registrech. Což s sebou nese nutnost se o tyto údaje starat a validovat je. Whidbey disponuje technologií, která tyto operace provádí transparentně a vystavuje je pro programátora ve formě strongly-typed třídy, dostupné jako My.Settings.

Předtím, než začneme experimentovat s nastaveními, doporučuji ještě na záložce Application klepnout na tlačítko Assembly Information a nastavit metadata týkající se aplikace. V předchozí verzi se zapisovaly do souboru AssemblyInfo.vb (ten je přítomen stále, ale je generován automaticky a skryt v adresář My Project), nyní je na to i GUI.

Screenshot 

Konfigurace totiž rozeznává dva druhy parametrů: per user a per application. Ty aplikační jsou uloženy klasicky v souboru jménoassembly.config, ale uživatelské jsou uloženy do souboru v uživatelově profilu. Přesná cesta k němu se generuje právě na základě shora uvedených metadat.

Po dokončení nezbytných příprav jest nám vrhnouti se na genervání vlastních nastavení. Za tímto účelem se v okně My Project přepneme na záložku Settings, kdežtě můžeme určovat konfigurační proměnné, jejich typy a defaultní hodnoty:

Screenshot 

Milé je, že jako typ konfigurační proměnné je možno použít cokoliv, nejenom primitivní typy jako String nebo Integer. Příkladem budiž Enum typy použité pro uložení nastavení sériového portu (např. System.IO.Ports.StopBits a další). Načítání se pak v kódu děje způsobem, jež je až urážlivě jednoduchý:

Dim Port As New System.IO.Ports.SerialPort()
Port.PortName = My.Settings.SensorPortName
Port.BaudRate = My.Settings.SensorBaudRate
Port.DataBits = My.Settings.SensorDataBits
Port.StopBits = My.Settings.SensorStopBits
Port.Parity = My.Settings.SensorParity
Port.ReadTimeout = My.Settings.SensorTimeout
Port.DtrEnable = True
Port.Open()

Pokud se jedná o nastavení s user scope, je možno stejným způsobem hodnoty nejenom číst, ale i nastavovat (po dokončení nastavování zavolejte My.Settings.Save()). Nastavení s application scope nelze za běhu aplikace měnit.

Ikonka v system tray a "Baloon tip" notifikace

Screenshot

Komponenta NotifyIcon, reprezentující ikonu v system tray (lokace známá též jako "vedle hodin") byla k dispozici již v předchozích verzích .NET Frameworku. Její Whidbey verze obsahuje též podporu pro "Baloon tip" - notifikaci nově používanou ve Windows XP.

V souvislosti s NotifyIcon umí aplikace tři věci:

  • Po minimalizaci se "schová" do ikonky.
  • Po klepnutí na ikonku zobrazí baloon tip s aktuálními údaji o teplotě.
  • Po klepnutí na ikonku pravým tlačítkem zobrazí okno aplikace.

Stará se o to následující kód:

Private Sub FormMain_Resize(ByVal sender As Object, _
            ByVal e As System.EventArgs) Handles Me.Resize
If Me.WindowState = FormWindowState.Minimized Then Me.Visible = False Me.TrayIcon.Visible = True End If End Sub
Private Sub TrayIcon_MouseClick(ByVal sender As Object, _ ByVal e As System.Windows.Forms.MouseEventArgs) _ Handles TrayIcon.MouseClick If e.Button = Windows.Forms.MouseButtons.Right Then Me.Visible = True Me.TrayIcon.Visible = False Me.WindowState = FormWindowState.Normal Else Dim Message As String = String.Format("{0}: {1}\n{2}: {3}", _ Me.GroupBoxSensor.Text, Me.LabelSensor.Text, _ Me.GroupBoxService.Text, Me.LabelService.Text) Me.TrayIcon.ShowBalloonTip(5, "Current temperature", _ Message.Replace("\n", vbCrLf), ToolTipIcon.Info) End If End Sub

Závěrem

Toto je funkční aplikace, která využívá některé nové možnosti, dostupné v Microsoft .NET Beta 2. K jejímu spuštění potřebujete Whidbey beta 2, projekt je pro VS.NET, ale měl by být kompatibilní i s Visual Basic Expressem.

Pracuji na obsáhlejším projektu, který bude umět centralizovaně monitorovat teplotu ve více bodech, včetně síťové komunikace a využití pokročilejších papouščích čidel. Dočkáte se ho během několika týdnů.

  • Altairis
  • Nemesis
  • Microsoft MVP
  • IIS
  • ASP.NET