Эта статья показывает, как записывать и читать данные от устройства, подключенного к последовательному порту (COM-порт) из приложения на языке C# в среде .NET. В этой статье мы создадим class RS485Server для работы с нашим газоанализатором по интерфейсу RS485. В недалеком прошлом для работы с Serial Port в среде .Net 1.1, мы должны были использовать либо Windows API, либо использовать управление из сторонних библиотек. В среде .Net 2.0 (и в более поздних версиях .NET) компания Microsoft добавила поддержку последовательного порта включением класса SerialPort как части пространства имен System.IO.Ports. Реализация класса RS485Server сделана очень прямо и очевидно. Для начала опишем используемые классом переменные:
C# Code:
SerialPort _serialPort; // Статус клиента private bool client_running; private int loc_req; // Статус приемника private string returnData = ""; private bool start_ok = false; private bool parse_ok = false;
На следующем этапе реализуем функцию Connect, отвечающую за открытие нашего com порта:
C# Code:
public bool Connect(string Name, int Speed) { client_running = false; Speed, Parity.None, 8, StopBits.One); _serialPort.Handshake = Handshake.None; _serialPort.WriteTimeout = 50; _serialPort.ReadTimeout = 50; try { _serialPort.Open(); } catch (Exception ex) { Console.WriteLine("ERROR: " + ex.ToString() + "MESSAGE " + ex.Message); } if (_serialPort.IsOpen) { client_running = true; } else { MessageBox.Show( "Не могу открыть порт!", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); } return client_running; }
Как видите функция получилась достаточно простой. В качестве аргументов она принимает имя com порта и требуюмую скорость. Для определения существующих com портов удобно пользоваться следующей конструкцией:
C# Code:
string[] portnames = System.IO.Ports.SerialPort.GetPortNames(); domainUpDown1.Items.Clear(); foreach (string port in portnames) { domainUpDown1.Text = port; domainUpDown1.Items.Add(port); }
Для приема данных нам нужно создать обработчик события EventHandler для "SerialDataReceivedEventHandler". В качестве этого приемника будет выступать метод Receiver:
C# Code:
public void Receiver(object sender, SerialDataReceivedEventArgs e) { if (client_running == true) { if (_serialPort.IsOpen) { SerialPort sp = (SerialPort)sender; int buferSize = sp.BytesToRead; for (int i = 0; i < buferSize; ++i) { // читаем по одному байту char bt = (char)sp.ReadByte(); if (bt == '{' && start_ok == false) { start_ok = true; returnData = ""; } if (start_ok == true) { returnData = returnData + bt.ToString(); } if (bt == '}' && start_ok == true) { start_ok = false; parse_ok = true; } } if (parse_ok == true) { parse_ok = false; Console.WriteLine("RS485 --> " + returnData.ToString()); try { if (loc_req == 1)//GET Device { Ans_Device newDevice = JsonConvert.DeserializeObject(returnData); Form1.myForm.Device1.DrawDeviceData(newDevice); newDevice = null; } } catch (Exception ex) { Console.WriteLine("ERROR: " + ex.ToString() + "MESSAGE " + ex.Message); } } } } }
Мы уже писали про использование формата JSON для общения наших устройств с серверным ПО. По этому в потоке происходит чтение данных с нашего com порта, и поиск данных заключенных в фигурные скобки { }. Далее для десериализации принятых данных используется компонент JsonConvert Class. Использовать его достаточно просто, для парсинга принятых данных, необходимо создать класс, соответствующий нашему запросу:
C# Code:
//Класс ответа девайса public class Ans_Device { public string ANSWER { get; set; } public string NAME { get; set; } public int SENSORS { get; set; } public byte RS485_ADRESS { get; set; } public int[] IP_ADDRESS { get; set; } public int[] NETMASK { get; set; } public int[] GATEWAY { get; set; } }
Для десеарилизации наших данных достаточно создать экземпляр класса Ans_Device с нашими десериализованными данными:
C# Code:
Ans_Device newDevice = JsonConvert.DeserializeObject(returnData);
Для завершения создания нашего класса осталось реализовать два простых метода. Первый, это метод Send, отправляющий данные в com порт:
C# Code:
public void Send(string datagram, int request) { loc_req = request; _serialPort.Write(datagram.ToString()); Console.WriteLine("RS485 <-- " + datagram.ToString()); }
Второй, это метод Close, закрывающий наш порт, если он открыт:
C# Code:
public void DisConnect() { try { if (_serialPort.IsOpen) { client_running = false; Thread.Sleep(500); _serialPort.Close(); } } catch (Exception ex) { Console.WriteLine("ERROR: " + ex.ToString() + "MESSAGE " + ex.Message); } }
Ниже можно наблюдать видео работы получившегося класса.