/// <summary> /// Sends the given telegram. /// If necessary additional information like a checksum can be inserted here. /// </summary> /// <param name="buffer">Buffer containing the data.</param> /// <param name="telegramLength">Length of the telegram in bytes.</param> public void SendTelegram(byte[] buffer, short telegramLength) { var crc = ModbusUtils.CalcCrc(buffer, telegramLength - 2); buffer[telegramLength - 2] = (byte)(crc & 0x00ff); buffer[telegramLength - 1] = (byte)((crc & 0xff00) >> 8); // ticks and _NextSend are multiples of 100 ns var dt = _nextSend - DateTime.Now.Ticks; if (dt > 0) { Thread.Sleep(Math.Max(1, (int)dt / 10000)); } // clear buffers _serial.DiscardInBuffer(); _serial.DiscardOutBuffer(); // next send is 3.5 chars after the end of this telegram _nextSend = DateTime.Now.Ticks + (telegramLength * 2 + 7) * _halfCharLength; #if NETMF try { _serial.Write(buffer, 0, telegramLength); // make sure all bytes are sent out _serial.Flush(); } catch (ArgumentException) { // happens some times -> Close -> Open -> try again try { _serial.Close(); } catch { // ignored } _serial.Open(); _serial.Write(buffer, 0, telegramLength); _serial.Flush(); } #else _serial.Write(buffer, 0, telegramLength); #endif }
/// <summary> /// Parses a telegram received by ReceiveTelegram. /// </summary> /// <param name="buffer">Buffer containing the data.</param> /// <param name="telegramLength">Total length of the telegram in bytes.</param> /// <param name="isResponse">true if the telegram is a response telegram; false if the telegram is a request telegram.</param> /// <param name="telegramContext"> /// If isResponse == true: pass the telegramContext returned by CreateTelegram from the request. /// If isResponse == false: returns the telegramContext from the received request. It must pe passed to the CreateTelegram method for the response. /// </param> /// <param name="address">Returns the device address.</param> /// <param name="fkt">Returns the function code.</param> /// <param name="dataPos">Returns the offset in buffer of the function code specific data.</param> /// <param name="dataLength">Returns the length of the function code specific data.</param> /// <returns>Returns true if this is the matching response according to the telegramContext; else false. If isResponse == false this method should return always true.</returns> public bool ParseTelegram(byte[] buffer, short telegramLength, bool isResponse, ref object telegramContext, out byte address, out byte fkt, out short dataPos, out short dataLength) { if (telegramLength < 4) { throw new ModbusException(ModbusErrorCode.ResponseTooShort); } var crc = ModbusUtils.CalcCrc(buffer, telegramLength - 2); if (buffer[telegramLength - 2] != (byte)(crc & 0x00ff) || buffer[telegramLength - 1] != (byte)((crc & 0xff00) >> 8)) { throw new ModbusException(ModbusErrorCode.CrcError); } address = buffer[0]; fkt = buffer[1]; dataPos = 2; dataLength = (short)(telegramLength - 4); return(true); }
/// <summary> /// Sends the given telegram. /// If necessary additional information like a checksum can be inserted here. /// </summary> /// <param name="buffer">Buffer containing the data.</param> /// <param name="telegramLength">Length of the telegram in bytes.</param> public void SendTelegram(byte[] buffer, short telegramLength) { var crc = ModbusUtils.CalcCrc(buffer, telegramLength - 2); buffer[telegramLength - 2] = (byte)(crc & 0x00ff); buffer[telegramLength - 1] = (byte)((crc & 0xff00) >> 8); // ticks and _NextSend are multiples of 100 ns var dt = this.nextSend - DateTime.Now.Ticks; if (dt > 0) { Thread.Sleep(Math.Max(1, (int)dt / 10000)); } // clear buffers this.serial.ClearReadBuffer(); this.serial.ClearWriteBuffer(); // next send is 3.5 chars after the end of this telegram this.nextSend = DateTime.Now.Ticks + (telegramLength * 2 + 7) * this.halfCharLength; this.serial.Write(buffer, 0, telegramLength); }
/// <summary> /// Waits and receives a telegram. /// </summary> /// <param name="buffer">Buffer to write data into.</param> /// <param name="desiredDataLength">Desired length of the function code specific data in bytes. -1 if length is unknown.</param> /// <param name="timeout">Timeout in milliseconds to wait for the telegram.</param> /// <param name="telegramLength">Returns the total length of the telegram in bytes.</param> /// <returns>Returns true if the telegram was received successfully; false on timeout.</returns> public bool ReceiveTelegram(byte[] buffer, short desiredDataLength, int timeout, out short telegramLength) { short desiredLength; if (desiredDataLength >= 0) { desiredLength = (short)(desiredDataLength + 4); if (desiredLength > buffer.Length) { throw new ArgumentException(string.Concat("buffer size (", buffer.Length, ") must be at least 4 byte larger than desiredDataLength (", desiredDataLength, ")")); } } else { desiredLength = -1; } var n = 0; var tOut = DateTime.Now.AddMilliseconds(timeout); long nextRead = 0; var errorChecked = false; while (true) { //if ((desiredLength > 0 || n == 0) && DateTime.Now > tOut) if (DateTime.Now > tOut) { break; } if (this.serial.BytesToRead > 0) { if (desiredLength > 0) { n += this.serial.Read(buffer, n, desiredLength - n); } else { n += this.serial.Read(buffer, n, buffer.Length - n); } // a delay of more than 1.5 chars means end of telegram /////, but since this is a extreme short time, we extend it by factor 2 nextRead = DateTime.Now.Ticks + 6 * this.halfCharLength; } if (!errorChecked && n >= 2) { errorChecked = true; if ((buffer[1] & 0x80) != 0) { // modbus error, so desired length is 5 desiredLength = 5; } } if (desiredLength > 0 && n == desiredLength) { telegramLength = (short)n; return(true); } if (desiredLength <= 0 && n >= 2 && DateTime.Now.Ticks > nextRead && this.serial.BytesToRead == 0) { var crc = ModbusUtils.CalcCrc(buffer, n - 2); if (buffer[n - 2] != (byte)(crc & 0x00ff) || buffer[n - 1] != (byte)((crc & 0xff00) >> 8)) { // read a little bit longer Thread.Sleep(1); nextRead = DateTime.Now.Ticks + 6 * this.halfCharLength; } else { telegramLength = (short)n; return(true); } } } telegramLength = 0; return(false); }