1

Problem Statement

I'm writing a C# console program that takes binary data coming in through Serial COM1, converting each byte into ASCII hex, and then outputting those HEX characters out through Serial COM2.

I would like this program to run in the command shell continuously but also exit gracefully (so that system resources like the COM ports are freed). The catch is that there is no time-out on either inbound/outbound serial data stream (I'd like for the program to run for weeks-months at a time). So how would such a program exit when the user wants it to quit?

The ability to connect to and disconnect from a serial communication session is a standard feature of most terminal emulators and I wanted to know how developers implement such a solution.

Initial Solutions

  1. Have the program read from a text file and recognize commands like "quit" from it. The user simply creates a text file, writes "quit" and the next time the application gets around to reading the file, it knows to quit! I don't like this solution because I'll be chided for creating an archaic UI.
  2. Have the application read from a third COM port and process the data stream as commands. It quits when it sees "quit" in the ReadBuffer.
  3. Create a second program (process) that invokes the above program as a process. Then, have the second process handle user i/o from stdin in another console window. When the user enters "quit", the second process should kill the first process. (I've never worked with multiple processes so I don't know what function calls or libraries to look at.
  4. Make the program multi-threaded. Have a thread of execution for reading in bytes, another to do the conversion, another to write out the converted, and another to handle input from stdin.

Comments

I'm not sure what solution to invest time in. Currently, I'm looking into (4).

  • 3
  • The PInvoke mechanisms to deal with the console closing should be OK. Just be advised that unless you are running as a service, your app may not work if your machine goes to sleep or you log out. – Berin Loritsch Nov 22 '17 at 16:28
  • 5
    You also have the option of creating a Windows Service: docs.microsoft.com/en-us/dotnet/framework/windows-services/… which will receive events when it is time to shut down. This also avoids the problem where auto-logout policies due to inactivity stop your port forwarder from working. You have a couple more options. – Berin Loritsch Nov 22 '17 at 16:31
  • 1
    @BerinLoritsch: sounds like a good answer. A service has the advantage there is already an UI to start, pause and stop the program - the UI from the operating system. No need to reinvent the wheel. – Doc Brown Nov 22 '17 at 16:57
  • You can do everything described in (4) inside a single thread (even without interrupt handlers, but why not use them?). Not saying you shouldn’t use multiple threads. But you don’t need to. – Konrad Rudolph Jan 23 '18 at 15:38
2

There's nothing in your problem statement to suggest that you require explicit multithreading. What I don't know about your C# application is if you are writing a Net Core app that is designed to be cross platform or not. That can limit what options you have.

Windows Only

Using the article here you use PInvoke to register for the SetConsoleCtrlHandler(...) Win32 callback. That callback will let you know if your app received any of the events that would close your typical console app (Ctrl+C, Break, Close the console, logoff, shutdown). That lets you handle unexpected closing more gracefully.

You can create a Windows Service to enable your application to run even if someone is not logged in. This has the advantage of being able to use the Services control panel to start/stop your app.

Cross Platform

If you need to handle Linux/Mac as well as Windows, then using PInvoke is pointless since that is platform specific. However you can adopt a hybrid approach inspired by the Apache Tomcat developers.

They created a control interface on a separate TCP port that would allow an external tool to send a control message like shut down, restart, etc. For Windows, they had a Windows service that would handle starting the console app (Tomcat is a Java based web server you can run from your console) and sending the appropriate control messages when it was time to shut down. For Linux/Mac, they just had a script that did essentially the same thing that tied in to the daemon/service system.

Personal Experience

I created a separate console data processing app that was controlled a Windows service. This particular data processing app was used to monitor sensors and perform some fancy calculus so we could pinpoint problems. The data processing app sent the results to a semi-real time monitoring UI. The app had to work without stop for as long as the machine did. By creating a Windows Service that simply was there to control the data processing app, we could easily spin up a new instance if the old instance died for an unexpected reason. We could also monitor the app's memory usage and other health criteria.

One of the things we did in the console app was to make sure that there were no leftover resources being consumed by an instance that didn't shut down cleanly. It never happened in real life, but the importance of this process made us design for that case.

Control Protocol

You can use a simple control protocol based on UDP. C#'s socket API has asynchronous read/write which is why you don't need explicit multithreading. A lot of that is handled for you.

You just have to decide on the message format. If you use a control program, you can keep a reference to the running Process to monitor it's health and determine if it is running. Useful commands:

  • Hang Up -- you can shut down now
  • Reset -- go back to start up conditions (i.e. close your ports and reopen them)
  • Heartbeat -- if you need to manage a process remotely
1

Simply look for the SIGTERM platform equivalent, and handle that

Under Linux things are pretty simple, as the COM port is just another file descriptor and you can do all sorts of weird things with it like passing it to another process or have it close when the program closes (does that anyway)

On other platforms the resources might not be freed when the program exits abruptly, but you can test an established approach to managing RS-232, in C#

In different settings there's a crash handler process monitoring the main process, and can restart it or send an error log etc., but this doesn't help here that much

Other than that,

  • Options 1 and 3: COM port can genuinely send a 'quit' sequence to be logged
  • Option 2 is usually FTP (separate data/control connections)
  • Option 4 (as with option 2) is MT and doesn't really help

All in all, it sounds like you're writing a virtual serial port driver

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.