Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 282
  • Last Modified:

C# System.IO.SerialPort Not Working But Does With Hyperterminal on Windows Server 2012 R2

I do not know why this code is not working on Windows Server 2012 R2.  When the device is connected to the serial port and I use Hyperterminal the data comes streaming in.  When I close Hyperterminal and turn on my program nothing happens.  I know my program opens the port because I log it to a custom event log.  Also, while my program is running and I try to run Hyperterminal using that same port it says the port is in use so I know I the program has the port open.

I have the same settings that I use for Hyperterminal (BaudRate, DataBits, etc.) so I have ruled that out.   I have read articles like this one that says there is some kind of issue with System.IO.SerialPort so use BaseStream, but I don't know if this is correct way to go: (Sparks Engineering).

Can someone please help?  Thank you.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Reflection;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Text.RegularExpressions;

namespace SecurityDoor
{
    public class SerialPortActions : IDisposable
    {

        //private SerialPort _serialPort = new SerialPort();
        private SerialPort _serialPort;
        private int _baudRate = 4800;
        private int _dataBits = 8;
        private Handshake _handshake = Handshake.None;
        private Parity _parity = Parity.None;
        private string _portName = "COM2";
        private StopBits _stopBits = StopBits.One;
        private string tString = string.Empty; //Holds data received until we get a terminator. 
        private byte _terminator = 0x0A; //Line feed byte in this case EOT (ASCII 10). 

        private DBActions dbActions = new DBActions();
        private EventLog eventLog2 = new EventLog();
        private Boolean _IsOpen = false;
        private string strComp2 = "";

        public Boolean IsOpen { get { return _IsOpen; } set { _IsOpen = value; } }

        public SerialPortActions()
        {
            eventLog2.Source = "SecurityDoorSource";
            eventLog2.Log = "SecurityDoorLog";
        }

        ~SerialPortActions()
        {
            Dispose(false);
        }

        public bool Open()
        {
            try
            {
                // Check if serial port is in use
                if (_serialPort != null && _serialPort.IsOpen)
                {
                    _IsOpen = false;
                    throw new System.InvalidOperationException("Port already open.");
                }
                else
                {
                    _serialPort = new SerialPort();
                    _serialPort.BaudRate = _baudRate;
                    _serialPort.DataBits = _dataBits;
                    _serialPort.Handshake = _handshake;
                    _serialPort.Parity = _parity;
                    _serialPort.PortName = _portName;
                    _serialPort.StopBits = _stopBits;
                    _serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
                    _serialPort.Open();
                    _IsOpen = true;
                };
            }
            catch (Exception e)
            {
                eventLog2.WriteEntry("Fail open serial port. " + e, EventLogEntryType.Error, 200);
                return false;
            }
            eventLog2.WriteEntry("Success open serial port.", EventLogEntryType.Information, 201);
            return true;
        }

        public bool Close()
        {
            try
            {
                _serialPort.Close();
                _serialPort.Dispose();
            }
            catch (Exception e)
            {
                eventLog2.WriteEntry("Fail close serial port. " + e, EventLogEntryType.Error, 210);
                return false;
            }
            eventLog2.WriteEntry("Success close serial port.", EventLogEntryType.Information, 211);
            return true;
        }

        public void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            byte[] buffer = new byte[_serialPort.ReadBufferSize]; //Initialize a buffer to hold the received data 
            int bytesRead = _serialPort.Read(buffer, 0, buffer.Length); //There is no accurate method for checking how many bytes are read unless you check the return from the Read method 
            tString += Encoding.ASCII.GetString(buffer, 0, bytesRead); //For the example assume the data we are received is ASCII data.

            if (tString.IndexOf((char)_terminator) > -1) //Check if string contains the terminator  
            {
                string workingString = tString.Substring(0, tString.IndexOf((char)_terminator)); //If tString does contain terminator we cannot assume that it is the last character received 
                workingString = TrimNonAscii(workingString);
                tString = tString.Substring(tString.IndexOf((char)_terminator)+1);  //Remove the data up to the terminator from tString 

                //Do something with workingString 
                if (IsStoreable(workingString))
                    dbActions.Insert(workingString);
            }
        }

        private bool IsStoreable(string strComp1)
        {
            //Check to see if string meets requirements for storage
            if (string.IsNullOrEmpty(strComp1.Trim()))
                return false;
            if (strComp1.Length < 70 | strComp1.IndexOf("-", 0) == 0)
                return false;

            if (strComp1.Substring(strComp1.Length-18).Substring(0,17) == strComp2)
                return false;
            strComp2 = strComp1.Substring(strComp1.Length - 18).Substring(0, 17);
            return true;
        }


        private string TrimNonAscii(string value)
        {
            string pattern = "[^ -~]*";
            Regex reg_exp = new Regex(pattern);
            return reg_exp.Replace(value, "").Trim();
        }

        // Call to release serial port
        public void Dispose()
        {
            Dispose(true);
        }

        // Part of basic design pattern for implementing Dispose
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _serialPort.DataReceived -= new SerialDataReceivedEventHandler(_serialPort_DataReceived);
            }
            // Releasing serial port (and other unmanaged objects)
            if (_serialPort != null)
            {
                if (_serialPort.IsOpen)
                    _serialPort.Close();

                _serialPort.Dispose();
            }
        }
    }
}

Open in new window

0
Nathan Vanderwyst
Asked:
Nathan Vanderwyst
  • 3
2 Solutions
 
ste5anSenior DeveloperCommented:
The problem is your concept of how a serial device works.

Serial communications is per se asynchronous. And it only transmits byte streams.

Thus when you're waiting to receive an multi-byte response (datagram) from your device, this can result in multiple DataReceived events for this one datagram. So you need to declare the buffer outside the scope of the DataReceived event and you need to collect all input from the serial port there. Then you need to inspect the entire buffer from the beginning (it's basically a queue of bytes) and search for complete datagrams. When a complete datagram is found, remove the datagram from the begin of the buffer and handle it.

So in a clean and minimzed version (no error handling, logging, disposal) it should look like:
public class SerialPortActions
{
    private const int BAUDRATE = 4800;
    private const int DATABITS = 8;
    private const Handshake HANDSHAKE = Handshake.None;
    private const char LINEFEED = (char)0x0A;
    private const Parity PARITY = Parity.None;
    private const string PORTNAME = "COM2";
    private const StopBits STOPBITS = StopBits.One;
    private string buffer;
    private SerialPort serialPort;
    public Boolean IsOpen { get; set; }

    public bool Open()
    {
        this.buffer = "";
        this.serialPort = new SerialPort();
        this.serialPort.BaudRate = BAUDRATE;
        this.serialPort.DataBits = DATABITS;
        this.serialPort.Handshake = HANDSHAKE;
        this.serialPort.Parity = PARITY;
        this.serialPort.PortName = PORTNAME;
        this.serialPort.StopBits = STOPBITS;
        this.serialPort.DataReceived += new SerialDataReceivedEventHandler(this.serialPort_DataReceived);
        this.serialPort.Open();
        this.IsOpen = true;
        return true;
    }

    public bool Close()
    {
        this.serialPort.Close();
        this.serialPort.Dispose();
        return true;
    }

    private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        this.buffer += this.serialPort.ReadExisting();
        string datagram = this.DequeueDatagram();
        while (datagram != null) {
            this.HandleDatagram(datagram);
            datagram = this.DequeueDatagram();
        }
    }

    private string DequeueDatagram()
    {
        string result = null;
        if (this.buffer.IndexOf(LINEFEED) > -1)
        {
            int linefeed = this.buffer.IndexOf(LINEFEED);
            result = this.buffer.Substring(0, linefeed);
            this.buffer = this.buffer.Substring(linefeed + 1);
        }

        return result;
    }

    private void HandleDatagram(string datagram)
    {
        //Do something
        //if (IsStoreable(datagram))
        //    dbActions.Insert(datagram);
    }
}

Open in new window

0
 
Nathan VanderwystAuthor Commented:
Thank you for the code, but the problem remains the same after implementing it.  To be more specific, the data is coming from a DSC4020 security panel that outputs data to a serial port printer.  For years I have had VB6 program reading the data from the serial port and writing it to a database.   Now that we have upgrade our network I have had to try to write a program in C# to do the same.

So, while I know the port is being opened by the C# program it is not receiving data AND the security panel thinks it is not connected and beeps endlessly.  If I terminate the program and open up hyperterminal and connect the security panel thinks it is connected once again to a printer and the data comes streaming in.  So, there is something wrong and we need another approach.

Another odd thing is that when I connect a null modem serial cable from another pc running hyperterminal to the serial port of the server running the program,  the program will read and write the data to the database.  I'm banging my head against a wall trying to understand why this doesn't work.  Thank you for your help so far, any other ideas?
0
 
Nathan VanderwystAuthor Commented:
Found the issue after reading the answer in this article.  I had to enable DTR to true because have to have handshake set to none but the panel didn't know I was ready to receive.  Somehow hyperterminal must handle that behind the scenes even though flowcontrol is set to none.  Thank you for your help, much appreciated.

serialPort.DtrEnable =true;

Open in new window

0
 
Nathan VanderwystAuthor Commented:
I found out what the issue was by doing my own research.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now