/// <summary> /// Obsługa komunikatów z klasy MODBUS. /// </summary> /// <param name="arg"></param> private void OnMODBUSCommunicate(MODBUSCommunicateEventArgs arg) { if (InvokeRequired) { // Aktualizacja kontrolki z innego wątku. MODBUS.MODBUSCommunicateDelegate d = new MODBUS.MODBUSCommunicateDelegate(OnMODBUSCommunicate); Invoke(d, new object[] { arg }); return; } switch (arg.Type) { case MODBUSCommunicateType.FrameSent: uxSentHexBox.ByteProvider = new DynamicByteProvider(arg.Frame); break; case MODBUSCommunicateType.FrameReceived: uxReceivedHexBox.ByteProvider = new DynamicByteProvider(arg.Frame); break; case MODBUSCommunicateType.TextReceived: uxReceivedTextBox.Text = arg.Text; break; case MODBUSCommunicateType.ErrorOccured: ShowMessage(arg.Text); break; case MODBUSCommunicateType.TextRequest: // Reakcja na rozkaz nr 2 - odesłanie tekstu. MODBUS.Instance.SendMessage((byte)uxStationAddress.Value, 2, uxMessageTextBox.Text); break; default: break; } }
/// <summary> /// Przetwarza otrzymaną ramkę. /// </summary> /// <param name="frame"></param> private void ProcessFrame(List<byte> frame) { if (frame.Count == 0) return; List<byte> data; // Dane otrzymane. if (_generalTransimssionMode == GeneralTransmissionModeEnum.ASCII) { data = ConvertASCIIToBytes(frame); // Nic nie robimy jeśli nie zgadza się suma kontrolna. if (!CheckLRC(data)) return; } else { // RTU data = frame; // Nic nie robimy jeśli nie zgadza się suma kontrolna. if (!CheckCRC(data)) return; } // Jeśli jesteśmy slavem to sprawdzamy czy ramka jest do nas. if (Type == StationTypeEnum.SLAVE && data[0] != StationAddress && data[0] != 0) return; // Akcje dla slave'a. if (Type == StationTypeEnum.SLAVE) { // Informacja o otrzymanej ramce. MODBUSCommunicateEventArgs arg = new MODBUSCommunicateEventArgs(MODBUSCommunicateType.FrameReceived); arg.Frame = frame.ToArray(); Communicate(arg); // Tekst ze stacji Master do Slave if (data[1] == 1) { // Jeśli to nie broadcast to potwierdzenie wykonania rozkazu. if(data[0] != 0) SendMessage(StationAddress, 1, ""); List<byte> messageBytes = _generalTransimssionMode == GeneralTransmissionModeEnum.ASCII ? data.GetRange(2, data.Count - 3) : data.GetRange(2, data.Count - 4); // Przetworzenie do tekstu string message = System.Text.Encoding.ASCII.GetString(messageBytes.ToArray()); arg = new MODBUSCommunicateEventArgs(MODBUSCommunicateType.TextReceived); arg.Text = message; Communicate(arg); } // Tekst ze stajcji Slave do Master if (data[1] == 2 && data[0] != 0) { arg = new MODBUSCommunicateEventArgs(MODBUSCommunicateType.TextRequest); Communicate(arg); } } // Akcje dla Mastera. if (Type == StationTypeEnum.MASTER) { _transactionTimeout.Stop(); _retransmittedCount = 0; _transactionInProgress = false; Console.WriteLine("Otrzymano potwierdzenie wykonania rozkazu {0}", data[1]); // Informacja o otrzymanej ramce. MODBUSCommunicateEventArgs arg = new MODBUSCommunicateEventArgs(MODBUSCommunicateType.FrameReceived); arg.Frame = frame.ToArray(); Communicate(arg); // Odczytanie tekstu if (data[1] == 2) { List<byte> messageBytes = _generalTransimssionMode == GeneralTransmissionModeEnum.ASCII ? data.GetRange(2, data.Count - 3) : data.GetRange(2, data.Count - 4); // Przetworzenie do tekstu string message = System.Text.Encoding.ASCII.GetString(messageBytes.ToArray()); arg = new MODBUSCommunicateEventArgs(MODBUSCommunicateType.TextReceived); arg.Text = message; Communicate(arg); } } }
/// <summary> /// Reakcja na komunikaty z RS232. /// </summary> /// <param name="arg"></param> private void OnRS232Communicate(RS232CommunicateEventArgs arg) { switch (arg.Type) { case CommunicateType.ErrorOccured: MODBUSCommunicateEventArgs modbusArg = new MODBUSCommunicateEventArgs(MODBUSCommunicateType.ErrorOccured); modbusArg.Text = arg.ErrorMessage; break; case CommunicateType.BinaryDataReceived: _intercharInterval.Stop(); // Unieważnienie danych z powodu nieciągłości ramki. if (_discardInBuffer) { _discardInBuffer = false; _inBuffer.Clear(); } // Dodanie danych do bufora wejściowego i sprawdzenie czy jest tam ramka ASCII dla trybu ASCII. _inBuffer.AddRange(arg.BinaryData); if(_generalTransimssionMode == GeneralTransmissionModeEnum.ASCII && CheckForASCIIFrameInBuffer()) { int start = _inBuffer.IndexOf((byte)':'); int end = _inBuffer.IndexOf(0x0A); List<byte> frame = _inBuffer.GetRange(start, end - start + 1); _inBuffer.RemoveRange(start, end - start + 1); ProcessFrame(frame); } // Zresetowanie timera dla trybu RTU if (_generalTransimssionMode == GeneralTransmissionModeEnum.RTU) { _RTUTimer.Stop(); _RTUTimer.Start(); } // Zapewnienie ciągłości ramki _intercharInterval.Start(); break; default: break; } }
/// <summary> /// Wysyła odpowiednią ramkę z wiadomością, rozkazem i adresem docelowym. /// </summary> /// <param name="destAddress"></param> /// <param name="instructionCode"></param> /// <param name="message"></param> public void SendMessage(byte destAddress, byte instructionCode, string message) { // Jeśli korzystamy z rozkazu nr 2 to nie ma sensu wysyłać dodatkowych danych // do slave'a. if (instructionCode == 2 && Type == StationTypeEnum.MASTER) message = ""; byte[] frame = new byte[] {}; // Utworzenie ramki switch (_generalTransimssionMode) { case GeneralTransmissionModeEnum.ASCII: frame = CreateASCIIFrame(destAddress, instructionCode, message); break; case GeneralTransmissionModeEnum.RTU: frame = CreateRTUFrame(destAddress, instructionCode, message); break; } // Zapisanie utworzonej ramki w razie potrzeby retransmisji. _lastFrame = frame; { // Poinformowanie o wysłanej ramce. MODBUSCommunicateEventArgs arg = new MODBUSCommunicateEventArgs(MODBUSCommunicateType.FrameSent); arg.Frame = frame; Communicate(arg); } if (!RS232.Instance.SendBinary(frame)) { MODBUSCommunicateEventArgs arg = new MODBUSCommunicateEventArgs(MODBUSCommunicateType.ErrorOccured); arg.Text = "Nie udało się wysłać ramki - błąd zapisu do portu."; Communicate(arg); return; } if (Type == StationTypeEnum.MASTER && destAddress != 0) { _transactionInProgress = true; _transactionTimeout.Start(); } }