{"id":705,"date":"2011-01-31T23:43:00","date_gmt":"2011-01-31T22:43:00","guid":{"rendered":"http:\/\/netrix.org.pl\/?p=705"},"modified":"2011-02-01T00:28:04","modified_gmt":"2011-01-31T23:28:04","slug":"serialport-i-nasluchiwanie-w-c","status":"publish","type":"post","link":"https:\/\/netrix.org.pl\/index.php\/2011\/01\/31\/serialport-i-nasluchiwanie-w-c\/","title":{"rendered":"SerialPort i nas\u0142uchiwanie w C#"},"content":{"rendered":"<p>W poprzedniej notce zaprezentowa\u0142em prosty emulator Modbus, kt\u00f3ry niedawno napisa\u0142em. W tej notce postaram si\u0119 napisa\u0107 w jaki spos\u00f3b zaimplementowa\u0142em obs\u0142ug\u0119 portu szeregowego COM oraz nas\u0142uchiwanie nieblokuj\u0105ce aplikacj\u0119.<\/p>\n<p>Zaczynaj\u0105c od pocz\u0105tku, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.io.ports.serialport%28v=VS.80%29.aspx\">SerialPort<\/a> jest klas\u0105 z .NET Framework, kt\u00f3ra umo\u017cliwia obs\u0142ug\u0119 port\u00f3w szeregowych. Na pocz\u0105tek przyk\u0142ad, w jaki spos\u00f3b pobra\u0107 list\u0119 dost\u0119pnych w komputerze port\u00f3w:<\/p>\n<pre lang=\"csharp\">string[] ports = SerialPort.GetPortNames();<\/pre>\n<p>I tyle, tablica string\u00f3w zawiera nazwy dost\u0119pnych port\u00f3w. Wa\u017cniejszym krokiem jest ustanowienie po\u0142\u0105czenia (tu przyk\u0142adowe dane):<\/p>\n<pre lang=\"csharp\">SerialPort sp = new SerialPort();\r\nsp.ReadTimeout = 1000;\r\nsp.WriteTimeout = 1000;\r\nsp.PortName = \"COM1\"\t\/\/ Jeden z wylistowanych port\u00f3w\r\nsp.BaudRate = 115200;\r\nsp.DataBits = 8\r\nsp.Parity = Parity.None;\r\nsp.StopBits = StopBits.One\r\nsp.Encoding = Encoding.BigEndianUnicode;\r\n\r\ntry\r\n{\r\n    sp.Open();\r\n}\r\ncatch (Exception err)\r\n{\r\n    \/* ... *\/\r\n}<\/pre>\n<p>Skoro port zosta\u0142 otwarty mo\u017cna ju\u017c co\u015b do niego wys\u0142a\u0107:<\/p>\n<pre lang=\"csharp\">byte[] data = \/* jakie\u015b dane *\/\r\nsp.Write(data, 0, data.Length);<\/pre>\n<p>Odbieranie danych wygl\u0105da analogicznie:<\/p>\n<pre lang=\"csharp\">byte[] data = null;\r\nif (sp.BytesToRead > 0)\r\n{\r\n    data = new byte[sp.BytesToRead];\r\n    sp.Read(data, 0, data.Length);\r\n}<\/pre>\n<p>Troszk\u0119 wi\u0119kszym problemem jest sprawienie, by aplikacja oczekiwa\u0142a na nowe dane, a w razie czego je obs\u0142u\u017cy\u0142a, nie powoduj\u0105c jednocze\u015bnie zastoju. Do tego celu nale\u017cy u\u017cy\u0107 w\u0105tk\u00f3w, a dok\u0142adniej jednego, w kt\u00f3rym nale\u017cy wywo\u0142a\u0107 tak\u0105 funkcj\u0119:<\/p>\n<pre lang=\"csharp\">private boolean bReceiving = false;\r\n\r\npublic void Receive()\r\n{\r\n    byte[] data;\r\n    while (bReceiving)\r\n    {\r\n        if (sp.BytesToRead > 0)\r\n        {\r\n            data = new byte[sp.BytesToRead];\r\n            sp.Read(data, 0, data.Length);\r\n            receiveHandler.DataReceivedEvent(data);\r\n        }\r\n\r\n        Thread.Sleep(50); \/\/ \u017beby nie zu\u017cywa\u0107 niepotrzebnie zasob\u00f3w\r\n    }\r\n}<\/pre>\n<p>Funkcja ta, jak wida\u0107 zawiera p\u0119tle, kt\u00f3ra sprawdza czy istniej\u0105 dane do odczytu, je\u015bli tak to je odczytuje i przekazuje dalej do metody, kt\u00f3ra z nich skorzysta. Kod wywo\u0142ania w\u0105tku oraz interfejs receiveHandler jest nast\u0119puj\u0105cy:<\/p>\n<pre lang=\"csharp\">interface IDataReceiveListener\r\n{\r\n    void DataReceivedEvent(byte[] data);\r\n}\r\n\r\nThread transmissionThread = null;\r\n\r\nvoid Run()\r\n{\r\n    bReceiving = true;\r\n    transmissionThread = new Thread(() => { Receive(); });\r\n    transmissionThread.Start();\r\n}<\/pre>\n<p>Teraz najwa\u017cniejsze, poniewa\u017c korzystam w tym kodzie z dw\u00f3ch w\u0105tk\u00f3w (g\u0142\u00f3wnego + dodatkowego), w tym miejscu nale\u017cy zwr\u00f3ci\u0107 szczeg\u00f3ln\u0105 uwag\u0119 na dost\u0119p do danych prze oba w\u0105tki. W mojej aplikacji za\u0142o\u017cy\u0142em, \u017ce drugi w\u0105tek obs\u0142uguje obiekt SerialPort (mimo \u017ce tworz\u0119 go w pierwszym w\u0105tku), zatem dost\u0119p do tego obiektu z w\u0105tku g\u0142\u00f3wnego (tutaj w przypadku wysy\u0142ania) musi by\u0107 opatrzony sekcj\u0105 krytyczn\u0105:<\/p>\n<pre lang=\"csharp\">Thread.BeginCriticalRegion();\r\n\r\nsp.Write(data, 0, data.Length);\r\n\r\nThread.EndCriticalRegion();<\/pre>\n<p>Natomiast jest jeszcze przypadek gdy w\u0105tek drugi odbiera dane i odwo\u0142uje si\u0119 do obiekt\u00f3w z w\u0105tku pierwszego. W tym przypadku nale\u017cy skorzysta\u0107 funkcji <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/zyzhdc6b.aspx\">Invoke()<\/a> klasy Form, kt\u00f3ra przyjmuje obiekt typu delegate, a jego zadaniem jest wywo\u0142anie odpowiedniej funkcji z w\u0105tku pierwszego. Kod kt\u00f3ry to robi jest nast\u0119puj\u0105cy:<\/p>\n<pre lang=\"csharp\">private delegate void ProcessRequestDelegate(byte[] data);\r\n\r\n\/* funkcja interfejsu IDataReceiveListener *\/\r\npublic void DataReceivedEvent(byte[] data)\r\n{\r\n    this.Invoke(new ProcessRequestDelegate(this.ProcessRequest), new object[] { data });\r\n}\r\n\r\nprivate void ProcessRequest(byte[] data)\r\n{\r\n    \/\/ some stuff\r\n}<\/pre>\n<p>I to wszystko na ten temat. Jak wida\u0107 pisanie aplikacji okienkowych w C# jest banalnie proste.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>W poprzedniej notce zaprezentowa\u0142em prosty emulator Modbus, kt\u00f3ry niedawno napisa\u0142em. W tej notce postaram si\u0119 napisa\u0107 w jaki spos\u00f3b zaimplementowa\u0142em obs\u0142ug\u0119 portu szeregowego COM oraz nas\u0142uchiwanie nieblokuj\u0105ce aplikacj\u0119. Zaczynaj\u0105c od pocz\u0105tku, SerialPort jest klas\u0105 z .NET Framework, kt\u00f3ra umo\u017cliwia obs\u0142ug\u0119 port\u00f3w szeregowych. Na pocz\u0105tek przyk\u0142ad, w jaki spos\u00f3b pobra\u0107 list\u0119 dost\u0119pnych w komputerze port\u00f3w: string[] [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[123],"tags":[132,185,131,124],"_links":{"self":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/705"}],"collection":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/comments?post=705"}],"version-history":[{"count":19,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/705\/revisions"}],"predecessor-version":[{"id":724,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/posts\/705\/revisions\/724"}],"wp:attachment":[{"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/media?parent=705"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/categories?post=705"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/netrix.org.pl\/index.php\/wp-json\/wp\/v2\/tags?post=705"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}