question

melonNG-6986 avatar image
0 Votes"
melonNG-6986 asked SandervandeVelde42 commented

Why my program sometimes not work only at startup?

Here is the code of my dotnet console App :
using System;
using System.IO.Ports;
using System.Linq;
using System.Threading.Tasks;

 namespace ConsoleApp1
 {
     internal class Program
     {
         private static SerialPort SP1 = new SerialPort("/dev/ttyUSB0", 57600, Parity.None, 8, StopBits.One);
         private static SerialPort SP2 = new SerialPort("/dev/ttyUSB1", 115200, Parity.None, 8, StopBits.One);        
         static void Main(string[] args)
         {
             SP1.DataReceived += SP_DataReceived;
             try
             {
                 SP1.Open();
                 SP2.Open();
             }
             catch(Exception ex){ 
                 Console.WriteLine(ex.Message);
             }
             Task.Run(() => {
                 while (true)
                 {
                     if (!SP1.IsOpen)
                     {
                         try
                         {
                             SP1.Open();
                         }
                         catch (Exception ex)
                         {
                             Console.WriteLine(ex.Message);
                         }
                     }
                     if (!SP2.IsOpen)
                     {
                         try
                         {
                             SP2.Open();
                         }
                         catch (Exception ex)
                         {
                             Console.WriteLine(ex.Message);
                         }
                     }
                     Task.Delay(1000).Wait();
                 }
             });
             Console.WriteLine("Started");
             Console.ReadKey();
         }
    
      static List<byte> ByteList = new List<byte>();
     private static void SP_DataReceived(object sender, SerialDataReceivedEventArgs e)
     {
         byte[] buf = new byte[SP1.BytesToRead];
         Console.WriteLine("DATA RECEIVED!");
         SP1!.Read(buf, 0, buf.Length);
         foreach (Byte b in buf)
         {
             ByteList.Add(b);
         }
         int StartIndex = -1;
         int EndIndex = -1;
         for (int i = 0; i < ByteList.Count; i++)
         {
             if (ByteList[i] == 2)
             {
                 StartIndex = i;
             }
             else if (ByteList[i] == 13 && ByteList.Count-1 > i&&ByteList[i+1]==10)
             {
                 EndIndex = i + 1;
                 break;
             }
         }
         List<byte> NewByteList = new List<byte>();
         if (StartIndex != -1 && EndIndex != -1)
         {                
             for (int i = StartIndex+1; i <= EndIndex-2; i++)
             {
                 NewByteList.Add(ByteList[i]);
             }
         }
         ByteList.RemoveRange(StartIndex, EndIndex - StartIndex+1);
         var Result = Encoding.UTF8.GetString(NewByteList.ToArray()).Split(" ").Last().Replace(Environment.NewLine, "");            
         if (!string.IsNullOrEmpty(Result))
         {
             Console.WriteLine(Result);
             if (SP2.IsOpen)
             {
                 SP2.Write(Result+Environment.NewLine);
             }
             else {
                 Console.WriteLine("SP2 is not opened");
             }
         }
     }
     }
 }

The program is running on raspberry with raspberry os. It is to get data from serial port A, then convert and transfer it to serial port B.

And it runs well if I run it by dotnet ConsoleApp1.dll

Now I want it run when the raspberry startup. So I create a service like this:
[Unit]
Description=Example .NET App

[Service]
WorkingDirectory=/home/pi/deployment-location
ExecStart=/home/pi/.dotnet/dotnet /home/pi/deployment-location/ConsoleApp1.dll
Restart=always
RestartSec=3
SyslogIdentifier=dotnet-example
User=pi
Environment=ASPNETCORE_ENVIRONMENT=Development

[Install]
WantedBy=multi-user.target

In spite, the program runs when startup. However, it met a strange problem. The program never works while startup. However, this problem never occurs while it running with "dotnet ConsoleApp1.dll"

And here is the result when I input "systemctl status ConsoleApp1.service":
● ConsoleApp1.service - Example .NET Web API App running on Ubuntu
Loaded: loaded (/etc/systemd/system/ConsoleApp1.service; enabled; vendor pres
Active: activating (auto-restart) (Result: signal) since Fri 2021-11-05 03:40
Process: 1204 ExecStart=/home/pi/.dotnet/dotnet /home/pi/deployment-location/C
Main PID: 1204 (code=killed, signal=ABRT)

Someone tell me to use journalctl -f -u ConsoleApp1.service to find the problem and soon I found this:
Nov 08 07:22:42 raspberrypi dotnet-example[1412]: System.InvalidOperationException: Cannot read keys when either application does not have a console or when console input has been redirected. Try Console.Read.
Nov 08 07:22:42 raspberrypi dotnet-example[1412]: at System.ConsolePal.ReadKey(Boolean intercept)
Nov 08 07:22:42 raspberrypi dotnet-example[1412]: at System.Console.ReadKey()
Nov 08 07:22:42 raspberrypi dotnet-example[1412]: at ConsoleApp1.Program.Main(String[] args) in C:\Users\mywatermelon\Desktop\ConsoleApp1\ConsoleApp1\Program.cs:line 57


It seems because it does not support console.Read. How can I solve this? Thank you.

dotnet-aspnet-core-generaldotnet-clidotnet-iot
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

AgaveJoe avatar image
1 Vote"
AgaveJoe answered SandervandeVelde42 commented

The design splits a message received on SP1 by a space. The code grabs the last element of the split string and sends it to SP2. Missing data is expected due to this design. Do not assume the serial port buffer always has one perfectly framed message in the receive buffer.

On a side note, there is a loop executing on another thread every second effectively wasting clock cycles which does not make a lot of sense. Remove this code or explain the need for this code.

You have not explained how the code is supposed to work but I assume SP1 (receiver) removes newlines from received messages then sends the processed messages to SP2 (sender).

I would buffer the message in SP1 until there is a complete message or maybe the buffer reaches a certain size or maybe write messages to a stack. Write a loop or design an event that sends completed messages to SP2 for writing. Basically, your code is too simplistic to function in a production environment.

· 7
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

The looping is just for Hot swap of the serial port.

The program is very easy, just as you see, receive data from SP1, split the data by the space, get the last one of the split, remove the newlines and send to the SP2. The monitor for the device of the SP2 will display what SP2 received. That's all.

Buffering the message is a good idea for me. Thank you for telling me about this. However, it seems can't solve my problem even though I buffered the message.

0 Votes 0 ·

Hello @melonNG-6986 ,

I agree with @AgaveJoe. You must add incoming messages to a buffer and each time decide if there is a complete message in the buffer. If not, wait for more messages to be received.

Remove a complete message from the start of the buffer.

You can give an exception if the buffer keeps growing without producing messages.

You can also test if there are multiple messages found at once in the buffer.

0 Votes 0 ·

I modified my code on this topic. After I tried it, the problem was still here.

0 Votes 0 ·

I have updated my code by receiving buffer. However, the problem is still here.

0 Votes 0 ·

Someone said the problem is the Console.ReadKey(); , for it can not block the program continue to running while in the background.

0 Votes 0 ·

Please have a look at the update of my question. I have found the problem, it seems the startup of Linux does not support Console.Read. Would you please help me to solve this?

0 Votes 0 ·

Hello @melonNG-6986 ,

you could easily change this console logic into an infinite loop with a sleep in it:

0 Votes 0 ·
SandervandeVelde42 avatar image
0 Votes"
SandervandeVelde42 answered SandervandeVelde42 commented

Hello @melonNG-6986 ,

as I understand, the code works fine now if your run it from the prompt.

Then, the problem lays in the daemon.

Can you check if your serial port access is successful?

In general, in Linux access to serial ports needs elevated rights.

· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Please have a look at the update of my question. I have found the problem, it seems the startup of Linux does not support Console.Read. Would you please help me to solve this?

0 Votes 0 ·

Hello @melonNG-6986 ,

That readkey is just holding the main thread from closing the application.

you could easily change this console logic into an infinite loop with a sleep in it:

     while (true)
     {
         // optionally: some message showing you reached this point
         Th read.Sl eep(1000);
     }

Add a 'using System.Threading;' if needed.

Because this is a 'dead end' in the code you better think about how to restart the logic.

0 Votes 0 ·