/// <summary> /// Reads the data from the TCP port and verifies the target acknowledged the message. Target acknowledges /// the message sent from the application when no data is sent back from the target (i.e. a command was sent) /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 ReceiveTargetAcknowledge() { // Allocate memory for the acknowledge byte[] rxMessage = new Byte[1]; Int32 bytesRead = 0; while (bytesRead == 0) { // Read the TCP socket bytesRead = ReceiveMessage(rxMessage, 0); if (bytesRead < 0) { return(bytesRead); } if (bytesRead == 1) { // Verify ACK received if (rxMessage[0] != ProtocolPTU.PTU_ACK) { m_TCPError = ProtocolPTU.Errors.AckNotReceieved; return(-1); } } else if (bytesRead > 1) { // too many bytes read m_TCPError = ProtocolPTU.Errors.ExcessiveBytesReceived; return(-1); } } return(0); }
/// <summary> /// This method is responsible for reading all available chars that are in the serial port /// sent by the embedded PTU. All characters are copied to the <paramref name="rxMessage"/> /// starting at the <paramref name="bufferOffset"/>. This feature allows multiple calls to /// this method until the entire message is received. /// </summary> /// <param name="rxMessage">buffer where the received message is stored</param> /// <param name="bufferOffset">offset into the receive buffer</param> /// <returns>less than 0 if any failure occurs; number of bytes read if successful</returns> private Int32 ReceiveMessage(Byte[] rxMessage, Int32 bufferOffset) { try { // Reset all variables m_BytesRead = 0; m_AsyncExceptionThrown = false; // Start the asynchronous receive m_Client.Client.BeginReceive(rxMessage, bufferOffset, rxMessage.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null); // AutoResetEvent signaled as soon as data is received Boolean rxSuccessful = m_ReceiveDone.WaitOne(SOCKET_ACTIVE_WAIT_TIME_MS); // Check for a message timeout if (!rxSuccessful) { m_TCPError = ProtocolPTU.Errors.ReceiveMessage; m_ExceptionMessage = "RX Message Timeout"; return(-1); } // Check if asynchronous receive function threw an exception else if (m_AsyncExceptionThrown) { return(-1); } } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.ReceiveMessage; m_ExceptionMessage = e.Message; return(-1); } return(m_BytesRead); }
/// <summary> /// Flushes the serial port transmit buffer /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> private Int32 FlushTxBuffer() { try { m_SerialPort.DiscardOutBuffer(); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.SerialBufferFlush; m_ExceptionMessage = e.Message; return(-1); } return(0); }
/// <summary> /// Sends a message to the target via the serial port /// </summary> /// <param name="txMessage">the byte array to be sent to the embedded PTU</param> /// <returns>less than 0 if any failure occurs; number of bytes sent if successful</returns> private Int32 TransmitMessage(Byte[] txMessage) { // Send the entire message to the serial port try { m_SerialPort.Write(txMessage, 0, txMessage.Length); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.TransmitMessage; m_ExceptionMessage = e.Message; return(-1); } return(txMessage.Length); }
/// <summary> /// Closes the serial port /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 Close() { try { m_SerialPort.Close(); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.Close; m_ExceptionMessage = e.Message; return(-1); } return(0); }
/// <summary> /// Receives a message from the target. It is assumed that the target sends a message with the first 2 bytes /// indicating the size of the message. /// </summary> /// <param name="rxMessage">array where the received message will be stored</param> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 ReceiveTargetDataPacket(Byte[] rxMessage) { // Verify the target responds with a SOM first Int32 errorCode = ReceiveStartOfMessage(); if (errorCode < 0) { return(errorCode); } Int32 bytesRead = 0; Int32 totalBytesRead = 0; // When the first 2 bytes are read, the received message size will be calculated UInt16 messageSize = UInt16.MaxValue; while (totalBytesRead != messageSize) { // read the serial receive buffer bytesRead = ReceiveMessage(rxMessage, totalBytesRead); if (bytesRead < 0) { return(bytesRead); } // adjust the index into the receive buffer in case the entire message wasn't received totalBytesRead += bytesRead; if ((totalBytesRead >= 2) && (messageSize == UInt16.MaxValue)) { // 1st 2 bytes of the message is the message length messageSize = BitConverter.ToUInt16(rxMessage, 0); if (IsTargetBigEndian()) { messageSize = Utils.ReverseByteOrder(messageSize); } } // Verify the expected receive message length isn't too long if (totalBytesRead > messageSize) { // too many bytes read m_SerialError = ProtocolPTU.Errors.ExcessiveBytesReceived; FlushRxBuffer(); return(-1); } } return(0); }
/// <summary> /// Send a message to the embedded PTU target. The SOM is sent and then waits for an echo from the target. /// The message is then sent and an echo that is identical to the message sent is verified. /// </summary> /// <param name="txMessage">the message to be sent to the target</param> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 SendMessageToTarget(byte[] txMessage) { // Transmit the message to the target Int32 errorCode = TransmitMessage(txMessage); if (errorCode < 0) { return(errorCode); } // Create a buffer for the receive message which should be identical to the // message just sent Byte[] rxMessage = new Byte[txMessage.Length]; // wait for the target logic to echo back the exact message just sent by the application // (NOTE: this is different than TCP which doesn't expect an echo) Int32 bytesRead = 0; Int32 totalBytesRead = 0; while (totalBytesRead != rxMessage.Length) { bytesRead = ReceiveMessage(rxMessage, totalBytesRead); if (bytesRead < 0) { return(bytesRead); } totalBytesRead += bytesRead; if (totalBytesRead > rxMessage.Length) { // too many bytes read m_SerialError = ProtocolPTU.Errors.ExcessiveBytesReceived; FlushRxBuffer(); return(-1); } } // This compares the contents of the 2 arrays if (txMessage.SequenceEqual(rxMessage) == false) { // log error m_SerialError = ProtocolPTU.Errors.MessageEcho; return(-1); } return(0); }
/// <summary> /// Closes the TCP connection gracefully by issuing a shutdown which effectively disables sends /// and receives on the socket and then closes the socket (issues a [FIN,ACK]). /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 Close() { // TODO need to kill asynchronous threads when application closes because task lingers in task manager when closing // via "x" on form try { m_Client.Client.Shutdown(SocketShutdown.Both); m_Client.Client.Dispose(); m_Client.Close(); } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.Close; m_ExceptionMessage = e.Message; return(-1); } return(0); }
/// <summary> /// This callback is invoked when data is received on the socket. /// </summary> /// <param name="ar">Required parameter for asynchronous callback; contains info about the socket</param> private void ReceiveCallback(IAsyncResult ar) { try { // Read data from the remote device. m_BytesRead = m_Client.Client.EndReceive(ar); } catch (Exception e) { m_AsyncExceptionThrown = true; m_ExceptionMessage = e.Message; m_TCPError = ProtocolPTU.Errors.ReceiveMessageAsync; } finally { // Signal that bytes have been received or an exception thrown m_ReceiveDone.Set(); } }
/// <summary> /// This callback is invoked when data is transmitted on the socket. /// </summary> /// <param name="ar">Required parameter for asynchronous callback; contains info about the socket</param> private void TransmitCallback(IAsyncResult ar) { try { // Complete sending the data to the remote device. m_BytesSent = m_Client.Client.EndSend(ar); } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.TransmitMessageAsync; m_AsyncExceptionThrown = true; m_ExceptionMessage = e.Message; } finally { // Signal that bytes have been sent or an exception thrown m_TransmitDone.Set(); } }
/// <summary> /// Receives the Start of Message byte from the target /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> private Int32 ReceiveStartOfMessage() { // Allocate memory for the received byte byte[] startOfMessage = new byte[1]; Int32 bytesRead = 0; while (bytesRead == 0) { bytesRead = ReceiveMessage(startOfMessage, 0); // Verify a valid call was made to ReceiveMessage() if (bytesRead < 0) { return(bytesRead); } // Verify only 1 byte was read; if so save the byte if (bytesRead == 1) { // Verify a valid SOM if ((startOfMessage[0] == ProtocolPTU.THE_SOM) || (startOfMessage[0] == ProtocolPTU.TARGET_BIG_ENDIAN_SOM)) { m_TargetStartOfMessage = startOfMessage[0]; } else { m_TargetStartOfMessage = 0; m_SerialError = ProtocolPTU.Errors.InvalidSOM; return(-1); } } else if (bytesRead > 1) { // too many bytes read m_SerialError = ProtocolPTU.Errors.ExcessiveBytesReceived; FlushRxBuffer(); return(-1); } } return(0); }
/// <summary> /// Sends a message to the target via the serial port /// </summary> /// <param name="txMessage">the byte array to be sent to the embedded PTU</param> /// <returns>less than 0 if any failure occurs; number of bytes sent if successful</returns> private Int32 TransmitMessage(Byte[] txMessage) { #if DAS // Send the entire message to the serial port try { m_SerialPort.Write(txMessage, 0, txMessage.Length); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.TransmitMessage; m_ExceptionMessage = e.Message; return(-1); } #else // Send the entire message to the serial port try { int i = 0; while (i < txMessage.Length) { m_SerialPort.Write(txMessage, i, 1); i++; #if DAS if ((i % 16) == 0) { Thread.Sleep(50); } #endif } } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.TransmitMessage; m_ExceptionMessage = e.Message; return(-1); } #endif return(txMessage.Length); }
/// <summary> /// This method is invoked after a successful TCP connection (3 way handshake) with the embedded PTU /// is established. It sets a flag to inform the connection was established. /// </summary> /// <param name="result">delegate parameter required; UNUSED</param> private void ConnectCallback(IAsyncResult result) { try { // Retrieve the socket from the state object. Socket client = (Socket)result.AsyncState; // Complete the connection. client.EndConnect(result); } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.ConnectionErrorAsync; m_AsyncExceptionThrown = true; m_ExceptionMessage = e.Message; } finally { // Signal that the connection has been made or an exception thrown m_ConnectDone.Set(); } }
/// <summary> /// Sends a message to the embedded TCP server target from the TCP client /// </summary> /// <param name="txMessage">the byte array to be sent to the embedded PTU</param> /// <returns>less than 0 if any failure occurs; number of bytes sent if successful</returns> private Int32 TransmitMessage(Byte[] txMessage) { // Send the entire message to the serial port try { // Reset all of the variables prior to starting an Asynchronous send m_BytesSent = -1; m_AsyncExceptionThrown = false; // Start the transmit, the callback will set the AutoResetEvent when complete. NOTE: Asynchronous transmit // and receive calls are required because the PTU will "hang" (i.e. the main form thread will block) if someone // pulls the cable if synchronous calls were used. m_Client.Client.BeginSend(txMessage, 0, txMessage.Length, 0, new AsyncCallback(TransmitCallback), null); Boolean txSuccessful = m_TransmitDone.WaitOne(SOCKET_ACTIVE_WAIT_TIME_MS); // Verify a successful transmit if (!txSuccessful) { // It wasn't log this a timeout error m_TCPError = ProtocolPTU.Errors.TransmitMessage; m_ExceptionMessage = "TX Message Timeout"; return(-1); } // Determine if an exception was thrown in the callback else if (m_AsyncExceptionThrown) { return(-1); } } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.TransmitMessage; m_ExceptionMessage = e.Message; return(-1); } return(m_BytesSent); }
/// <summary> /// Reads the serial port and verifies the target acknowledged the message. Target acknowledges /// the message sent from the application when no data is sent back from the target (i.e. a command was sent) /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 ReceiveTargetAcknowledge() { // Allocate memory for the acknowledge byte[] rxMessage = new Byte[1]; Int32 bytesRead = 0; while (bytesRead == 0) { // Read the serial port bytesRead = ReceiveMessage(rxMessage, 0); if (bytesRead < 0) { // bytesRead in this case is an error code return(bytesRead); } if (bytesRead == 1) { // Verify ACK received if (rxMessage[0] != ProtocolPTU.PTU_ACK) { m_SerialError = ProtocolPTU.Errors.AckNotReceieved; return(-1); } } else if (bytesRead > 1) { // too many bytes read m_SerialError = ProtocolPTU.Errors.ExcessiveBytesReceived; FlushRxBuffer(); return(-1); } } return(0); }
/// <summary> /// This method is responsible for reading all available chars that are in the serial port /// sent by the embedded PTU. All characters are copied to the <paramref name="rxMessage"/> /// starting at the <paramref name="bufferOffset"/>. This feature allows multiple calls to /// this method until the entire message is received. /// </summary> /// <param name="rxMessage">buffer where the received message is stored</param> /// <param name="bufferOffset">offset into the receive buffer</param> /// <returns>less than 0 if any failure occurs; number of bytes read if successful</returns> private Int32 ReceiveMessage(Byte[] rxMessage, Int32 bufferOffset) { Int32 bytesRead = 0; try { bytesRead = m_SerialPort.Read(rxMessage, bufferOffset, rxMessage.Length - bufferOffset); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.ReceiveMessage; m_ExceptionMessage = e.Message; return(-1); } // Should always read bytes when this function is invoked, otherwise the buffer may be full if (bytesRead == 0) { m_ExceptionMessage = "Zero bytes read when expecting bytes"; return(-1); } return(bytesRead); }
/// <summary> /// This function attempts to open a new connection with an embedded PTU. /// </summary> /// <param name="commaDelimitedOptions">valid URL or valid IP address</param> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 Open(String commaDelimitedOptions) { String url; IPAddress [] ipAddress; // There are no other options supported except passing the URL (could be IP address or // IPTCOM address) url = commaDelimitedOptions; // Any PTU object can only support 1 TCP client; ensures Open() is called only once per created object if (m_Client != null) { m_TCPError = ProtocolPTU.Errors.ClientPreviouslyCreated; return(-1); } // Create the TCPClient object m_Client = new TcpClient(); // Attempt to resolve the URL to an IP address try { ipAddress = Dns.GetHostAddresses(url); } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.InvalidURL; m_ExceptionMessage = e.Message; return(-1); } // Verify at least one IP address is resolved if (ipAddress.Length == 0) { m_TCPError = ProtocolPTU.Errors.InvalidURL; return(-1); } // Scan through all of the resolved IP addresses and try connecting to the first // IP v4 address in the list; ignore the rest IPAddress ipv4Addr = null; foreach (IPAddress addr in ipAddress) { if (addr.AddressFamily == AddressFamily.InterNetwork) { ipv4Addr = addr; break; } } // Can't resolve URL into IPv4 Address if (ipv4Addr == null) { m_TCPError = ProtocolPTU.Errors.UnresolvableURL; return(-1); } // Try to establish a connection with the embedded PTU. This establishes the 3 way handshake with the // target try { m_AsyncExceptionThrown = false; // This is an asynchronous (non--blocking) TCP connection attempt. If a successful connection // occurs, the ConnectCallback() method will be invoked and m_Connected will be made true m_Client.BeginConnect(ipv4Addr, PTU_SERVER_SOCKET, new AsyncCallback(ConnectCallback), m_Client.Client); // Allow time for the connection to establish, if the connection is made and m_ConnectDone is signaled // in the callback, this function wakes up and moves so no extra time is wasted. bool connected = m_ConnectDone.WaitOne(SOCKET_ACTIVE_WAIT_TIME_MS); if (!connected) { // It wasn't log this a timeout error m_TCPError = ProtocolPTU.Errors.ConnectionError; m_ExceptionMessage = "Connection Timeout"; return(-1); } // Determine if an exception was thrown in the callback else if (m_AsyncExceptionThrown) { return(-1); } } catch (SocketException e) { m_TCPError = ProtocolPTU.Errors.ConnectionError; m_ExceptionMessage = e.Message; return(-1); } // Connection to PTU server was successful return(0); }
/// <summary> /// Reads the serial port and verifies the target acknowledged the message. Target acknowledges /// the message sent from the application when no data is sent back from the target (i.e. a command was sent) /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 ReceiveTargetAcknowledge() { // Allocate memory for the acknowledge byte[] rxMessage = new Byte[1]; Int32 bytesRead = 0; while (bytesRead == 0) { // Read the serial port bytesRead = ReceiveMessage(rxMessage, 0); if (bytesRead < 0) { // bytesRead in this case is an error code return bytesRead; } if (bytesRead == 1) { // Verify ACK received if (rxMessage[0] != ProtocolPTU.PTU_ACK) { m_SerialError = ProtocolPTU.Errors.AckNotReceieved; return -1; } } else if (bytesRead > 1) { // too many bytes read m_SerialError = ProtocolPTU.Errors.ExcessiveBytesReceived; FlushRxBuffer(); return -1; } } return 0; }
/// <summary> /// Sends a message to the embedded TCP server target from the TCP client /// </summary> /// <param name="txMessage">the byte array to be sent to the embedded PTU</param> /// <returns>less than 0 if any failure occurs; number of bytes sent if successful</returns> private Int32 TransmitMessage(Byte[] txMessage) { // Send the entire message to the serial port try { // Reset all of the variables prior to starting an Asynchronous send m_BytesSent = -1; m_AsyncExceptionThrown = false; // Start the transmit, the callback will set the AutoResetEvent when complete. NOTE: Asynchronous transmit // and receive calls are required because the PTU will "hang" (i.e. the main form thread will block) if someone // pulls the cable if synchronous calls were used. m_Client.Client.BeginSend(txMessage, 0, txMessage.Length, 0, new AsyncCallback(TransmitCallback), null); Boolean txSuccessful = m_TransmitDone.WaitOne(SOCKET_ACTIVE_WAIT_TIME_MS); // Verify a successful transmit if (!txSuccessful) { // It wasn't log this a timeout error m_TCPError = ProtocolPTU.Errors.TransmitMessage; m_ExceptionMessage = "TX Message Timeout"; return -1; } // Determine if an exception was thrown in the callback else if (m_AsyncExceptionThrown) { return -1; } } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.TransmitMessage; m_ExceptionMessage = e.Message; return -1; } return m_BytesSent; }
/// <summary> /// This method is responsible for reading all available chars that are in the serial port /// sent by the embedded PTU. All characters are copied to the <paramref name="rxMessage"/> /// starting at the <paramref name="bufferOffset"/>. This feature allows multiple calls to /// this method until the entire message is received. /// </summary> /// <param name="rxMessage">buffer where the received message is stored</param> /// <param name="bufferOffset">offset into the receive buffer</param> /// <returns>less than 0 if any failure occurs; number of bytes read if successful</returns> private Int32 ReceiveMessage(Byte[] rxMessage, Int32 bufferOffset) { Int32 bytesRead = 0; try { bytesRead = m_SerialPort.Read(rxMessage, bufferOffset, rxMessage.Length - bufferOffset); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.ReceiveMessage; m_ExceptionMessage = e.Message; return -1; } return bytesRead; }
/// <summary> /// Opens the desired serial port based on the comma delimited string passed as the argument. The first argument /// is the COM port (e.g. COM1). The 2nd argument in the string is the baud rate (e.g. 19200). The 3rd argument is /// the parity (e.g. none, odd, or even). The 4th argument is the number of data bits (e.g. 8). The 5th argument is /// the number of stop bits (e.g. 1). /// </summary> /// <param name="commaDelimitedOptions">COM port settings - "COMX,BaudRate,Parity,DataBits,StopBits"</param> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 Open(string commaDelimitedOptions) { String[] options; options = commaDelimitedOptions.Split(','); // verify 5 comma delimited arguments if (options.Length != 5) { m_SerialError = ProtocolPTU.Errors.OptionsLengthIncorrect; return(-1); } // Save the port name (e.g. COM1) String portName = options[0]; Int32 baudRate; try { baudRate = Convert.ToInt32(options[1]); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.BaudRateConversion; m_ExceptionMessage = e.Message; return(-1); } // Default to no parity check Parity parity = Parity.None; switch (options[2].ToLower()) { case "odd": parity = Parity.Odd; break; case "even": parity = Parity.Even; break; default: break; } // Set the number of data bits Int32 dataBits; try { dataBits = Convert.ToInt32(options[3]); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.DataBitsConversion; m_ExceptionMessage = e.Message; return(-1); } // Default to 1 stop bit StopBits stopBits = StopBits.One; switch (options[4].ToLower()) { case "0": stopBits = StopBits.None; break; case "2": stopBits = StopBits.Two; break; default: break; } // Open the serial port try { m_SerialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits); m_SerialPort.Open(); m_SerialPort.ReadTimeout = m_ReadTimeout; } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.OpenSerialPort; m_ExceptionMessage = e.Message; return(-1); } // Flush the receive buffer Int32 errorCode = FlushRxBuffer(); if (errorCode < 0) { return(errorCode); } // Flush the transmit buffer errorCode = FlushTxBuffer(); if (errorCode < 0) { return(errorCode); } return(0); }
/// <summary> /// Receives a message from the target. It is assumed that the target sends a message with the first 2 bytes /// indicating the size of the message. /// </summary> /// <param name="rxMessage">array where the received message will be stored</param> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 ReceiveTargetDataPacket(Byte[] rxMessage) { // Verify the target responds with a SOM first Int32 errorCode = ReceiveStartOfMessage(); if (errorCode < 0) { return errorCode; } Int32 bytesRead = 0; Int32 totalBytesRead = 0; // When the first 2 bytes are read, the received message size will be calculated UInt16 messageSize = UInt16.MaxValue; while (totalBytesRead != messageSize) { // read the TCP receive buffer bytesRead = ReceiveMessage(rxMessage, totalBytesRead); if (bytesRead < 0) { return bytesRead; } // adjust the index into the receive buffer in case the entire message wasn't received totalBytesRead += bytesRead; if ((totalBytesRead >= 2) && (messageSize == UInt16.MaxValue)) { // 1st 2 bytes of the message is the message length messageSize = BitConverter.ToUInt16(rxMessage, 0); if (IsTargetBigEndian()) { messageSize = Utils.ReverseByteOrder(messageSize); } } // Verify the expected receive message length isn't too long if (totalBytesRead > messageSize) { // too many bytes read m_TCPError = ProtocolPTU.Errors.ExcessiveBytesReceived; return -1; } } return 0; }
/// <summary> /// Reads the data from the TCP port and verifies the target acknowledged the message. Target acknowledges /// the message sent from the application when no data is sent back from the target (i.e. a command was sent) /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 ReceiveTargetAcknowledge() { // Allocate memory for the acknowledge byte[] rxMessage = new Byte[1]; Int32 bytesRead = 0; while (bytesRead == 0) { // Read the TCP socket bytesRead = ReceiveMessage(rxMessage, 0); if (bytesRead < 0) { return bytesRead; } if (bytesRead == 1) { // Verify ACK received if (rxMessage[0] != ProtocolPTU.PTU_ACK) { m_TCPError = ProtocolPTU.Errors.AckNotReceieved; return -1; } } else if (bytesRead > 1) { // too many bytes read m_TCPError = ProtocolPTU.Errors.ExcessiveBytesReceived; return -1; } } return 0; }
/// <summary> /// This function attempts to open a new connection with an embedded PTU. /// </summary> /// <param name="commaDelimitedOptions">valid URL or valid IP address</param> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 Open(String commaDelimitedOptions) { String url; IPAddress []ipAddress; // There are no other options supported except passing the URL (could be IP address or // IPTCOM address) url = commaDelimitedOptions; // Any PTU object can only support 1 TCP client; ensures Open() is called only once per created object if (m_Client != null) { m_TCPError = ProtocolPTU.Errors.ClientPreviouslyCreated; return -1; } // Create the TCPClient object m_Client = new TcpClient(); // Attempt to resolve the URL to an IP address try { ipAddress = Dns.GetHostAddresses(url); } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.InvalidURL; m_ExceptionMessage = e.Message; return -1; } // Verify at least one IP address is resolved if (ipAddress.Length == 0) { m_TCPError = ProtocolPTU.Errors.InvalidURL; return -1; } // Scan through all of the resolved IP addresses and try connecting to the first // IP v4 address in the list; ignore the rest IPAddress ipv4Addr = null; foreach (IPAddress addr in ipAddress) { if (addr.AddressFamily == AddressFamily.InterNetwork) { ipv4Addr = addr; break; } } // Can't resolve URL into IPv4 Address if (ipv4Addr == null) { m_TCPError = ProtocolPTU.Errors.UnresolvableURL; return -1; } // Try to establish a connection with the embedded PTU. This establishes the 3 way handshake with the // target try { m_AsyncExceptionThrown = false; // This is an asynchronous (non--blocking) TCP connection attempt. If a successful connection // occurs, the ConnectCallback() method will be invoked and m_Connected will be made true m_Client.BeginConnect(ipv4Addr, PTU_SERVER_SOCKET, new AsyncCallback(ConnectCallback), m_Client.Client); // Allow time for the connection to establish, if the connection is made and m_ConnectDone is signaled // in the callback, this function wakes up and moves so no extra time is wasted. bool connected = m_ConnectDone.WaitOne(SOCKET_ACTIVE_WAIT_TIME_MS); if (!connected) { // It wasn't log this a timeout error m_TCPError = ProtocolPTU.Errors.ConnectionError; m_ExceptionMessage = "Connection Timeout"; return -1; } // Determine if an exception was thrown in the callback else if (m_AsyncExceptionThrown) { return -1; } } catch (SocketException e) { m_TCPError = ProtocolPTU.Errors.ConnectionError; m_ExceptionMessage = e.Message; return -1; } // Connection to PTU server was successful return 0; }
/// <summary> /// Send a message to the embedded PTU target. The SOM is sent and then waits for an echo from the target. /// The message is then sent and an echo that is identical to the message sent is verified. /// </summary> /// <param name="txMessage">the message to be sent to the target</param> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 SendMessageToTarget(byte[] txMessage) { // Transmit the message to the target Int32 errorCode = TransmitMessage(txMessage); if (errorCode < 0) { return errorCode; } // Create a buffer for the receive message which should be identical to the // message just sent Byte[] rxMessage = new Byte[txMessage.Length]; // wait for the target logic to echo back the exact message just sent by the application // (NOTE: this is different than TCP which doesn't expect an echo) Int32 bytesRead = 0; Int32 totalBytesRead = 0; while (totalBytesRead != rxMessage.Length) { bytesRead = ReceiveMessage(rxMessage, totalBytesRead); if (bytesRead < 0) { return bytesRead; } totalBytesRead += bytesRead; if (totalBytesRead > rxMessage.Length) { // too many bytes read m_SerialError = ProtocolPTU.Errors.ExcessiveBytesReceived; FlushRxBuffer(); return -1; } } // This compares the contents of the 2 arrays if (txMessage.SequenceEqual(rxMessage) == false) { // log error m_SerialError = ProtocolPTU.Errors.MessageEcho; return -1; } return 0; }
/// <summary> /// Sends a message to the target via the serial port /// </summary> /// <param name="txMessage">the byte array to be sent to the embedded PTU</param> /// <returns>less than 0 if any failure occurs; number of bytes sent if successful</returns> private Int32 TransmitMessage(Byte[] txMessage) { // Send the entire message to the serial port try { m_SerialPort.Write(txMessage, 0, txMessage.Length); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.TransmitMessage; m_ExceptionMessage = e.Message; return -1; } return txMessage.Length; }
/// <summary> /// Opens the desired serial port based on the comma delimited string passed as the argument. The first argument /// is the COM port (e.g. COM1). The 2nd argument in the string is the baud rate (e.g. 19200). The 3rd argument is /// the parity (e.g. none, odd, or even). The 4th argument is the number of data bits (e.g. 8). The 5th argument is /// the number of stop bits (e.g. 1). /// </summary> /// <param name="commaDelimitedOptions">COM port settings - "COMX,BaudRate,Parity,DataBits,StopBits"</param> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 Open(string commaDelimitedOptions) { String[] options; options = commaDelimitedOptions.Split(','); // verify 5 comma delimited arguments if (options.Length != 5) { m_SerialError = ProtocolPTU.Errors.OptionsLengthIncorrect; return -1; } // Save the port name (e.g. COM1) String portName = options[0]; Int32 baudRate; try { baudRate = Convert.ToInt32(options[1]); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.BaudRateConversion; m_ExceptionMessage = e.Message; return -1; } // Default to no parity check Parity parity = Parity.None; switch (options[2].ToLower()) { case "odd": parity = Parity.Odd; break; case "even": parity = Parity.Even; break; default: break; } // Set the number of data bits Int32 dataBits; try { dataBits = Convert.ToInt32(options[3]); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.DataBitsConversion; m_ExceptionMessage = e.Message; return -1; } // Default to 1 stop bit StopBits stopBits = StopBits.One; switch (options[4].ToLower()) { case "0": stopBits = StopBits.None; break; case "2": stopBits = StopBits.Two; break; default: break; } // Open the serial port try { m_SerialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits); m_SerialPort.Open(); m_SerialPort.ReadTimeout = m_ReadTimeout; } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.OpenSerialPort; m_ExceptionMessage = e.Message; return -1; } // Flush the receive buffer Int32 errorCode = FlushRxBuffer(); if (errorCode < 0) { return errorCode; } // Flush the transmit buffer errorCode = FlushTxBuffer(); if (errorCode < 0) { return errorCode; } return 0; }
/// <summary> /// Closes the serial port /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 Close() { try { m_SerialPort.Close(); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.Close; m_ExceptionMessage = e.Message; return -1; } return 0; }
/// <summary> /// This method is responsible for reading all available chars that are in the serial port /// sent by the embedded PTU. All characters are copied to the <paramref name="rxMessage"/> /// starting at the <paramref name="bufferOffset"/>. This feature allows multiple calls to /// this method until the entire message is received. /// </summary> /// <param name="rxMessage">buffer where the received message is stored</param> /// <param name="bufferOffset">offset into the receive buffer</param> /// <returns>less than 0 if any failure occurs; number of bytes read if successful</returns> private Int32 ReceiveMessage(Byte[] rxMessage, Int32 bufferOffset) { try { // Reset all variables m_BytesRead = 0; m_AsyncExceptionThrown = false; // Start the asynchronous receive m_Client.Client.BeginReceive(rxMessage, bufferOffset, rxMessage.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null); // AutoResetEvent signaled as soon as data is received Boolean rxSuccessful = m_ReceiveDone.WaitOne(SOCKET_ACTIVE_WAIT_TIME_MS); // Check for a message timeout if (!rxSuccessful) { m_TCPError = ProtocolPTU.Errors.ReceiveMessage; m_ExceptionMessage = "RX Message Timeout"; return -1; } // Check if asynchronous receive function threw an exception else if (m_AsyncExceptionThrown) { return -1; } } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.ReceiveMessage; m_ExceptionMessage = e.Message; return -1; } return m_BytesRead; }
/// <summary> /// Receives the Start Of Message (SOM) from the embedded the embedded PTU /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> private Int32 ReceiveStartOfMessage() { // Allocate memory for the received byte byte[] startOfMessage = new byte[1]; Int32 bytesRead = 0; while (bytesRead == 0) { bytesRead = ReceiveMessage(startOfMessage, 0); // Verify a valid call was made to ReceiveMessage() if (bytesRead < 0) { return bytesRead; } // Verify only 1 byte was read; if so save the byte if (bytesRead == 1) { if ((startOfMessage[0] == ProtocolPTU.THE_SOM) || (startOfMessage[0] == ProtocolPTU.TARGET_BIG_ENDIAN_SOM)) { m_TargetStartOfMessage = startOfMessage[0]; } else { m_TargetStartOfMessage = 0; m_TCPError = ProtocolPTU.Errors.InvalidSOM; return -1; } } else if (bytesRead > 1) { // too many bytes read m_TCPError = ProtocolPTU.Errors.ExcessiveBytesReceived; return -1; } } return 0; }
/// <summary> /// Closes the TCP connection gracefully by issuing a shutdown which effectively disables sends /// and receives on the socket and then closes the socket (issues a [FIN,ACK]). /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> public Int32 Close() { // TODO need to kill asynchronous threads when application closes because task lingers in task manager when closing // via "x" on form try { m_Client.Client.Shutdown(SocketShutdown.Both); m_Client.Client.Dispose(); m_Client.Close(); } catch (Exception e) { m_TCPError = ProtocolPTU.Errors.Close; m_ExceptionMessage = e.Message; return -1; } return 0; }
/// <summary> /// Flushes the serial port transmit buffer /// </summary> /// <returns>less than 0 if any failure occurs; greater than or equal to 0 if successful</returns> private Int32 FlushTxBuffer() { try { m_SerialPort.DiscardOutBuffer(); } catch (Exception e) { m_SerialError = ProtocolPTU.Errors.SerialBufferFlush; m_ExceptionMessage = e.Message; return -1; } return 0; }