Solved

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

Posted on 2016-11-21
4
27 Views
Last Modified: 2016-11-28
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
Comment
Question by:Nathan Vanderwyst
  • 3
4 Comments
 
LVL 32

Assisted Solution

by:ste5an
ste5an earned 500 total points
ID: 41897893
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
 

Author Comment

by:Nathan Vanderwyst
ID: 41899526
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
 

Accepted Solution

by:
Nathan Vanderwyst earned 0 total points
ID: 41899599
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
 

Author Closing Comment

by:Nathan Vanderwyst
ID: 41903943
I found out what the issue was by doing my own research.
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Recently, I was assigned the task of performing a hardware refresh in the datacenter. The previous Windows 2008 systems were connected to the SAN via fiber channel HBA’s and among other thing, had PowerPath installed in order to provide sufficient f…
What to do when Windows Update is not working correctly? What tools can I use to detect the cause of the malfunction problem? What does this numeric error code mean? These and other questions that you have been asking in the past are answered here (…
In this Micro Tutorial viewers will learn how to restore their server from Bare Metal Backup image created with Windows Server Backup feature. As an example Windows 2012R2 is used.
In this Micro Tutorial viewers will learn how to restore single file or folder from Bare Metal backup image of their system. Tutorial shows how to restore files and folders from system backup. Often it is not needed to restore entire system when onl…

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now