Search code examples
c#monosensorsmodbus

How to get back to function from timed event


Okay so I have a function called readSensor which you guessed it.. reads a sensor.

But the sensors usually take about 100ms to respond. So in the readSensor function I am basically just starting a timer.

On the timed event I read the serialport and get my response.

However this means that my response is in the onTimedEvent when I want it to be in the readSensor function..

Basically from the main form I want to be able to do this.

value = readSensor()

when at the minute all I can do is readSensor() and then I can see the response is coming back by displaying it in a messagebox once the timedEvent fires.

here is my code. (I have missed out loads of serialport setup and stuff but hopefully you can see the problem I am in)

I don't want to just wait in the function for 100ms though polling the timer as that will make my program slow..

I want to somehow get the response back to the readSensor function and then back to the form.

    using System;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.IO.Ports;
    using System.Timers;

    namespace readSensor
    {
      public partial class readSens : UserControl
      {
        public readSens()
        {
          InitializeComponent();
        }

        private System.Timers.Timer rTimer;
        SerialPort sp = new SerialPort();

        private void setupTimer()
        {
          // Create a timer with a 100ms response.
          rTimer = new System.Timers.Timer(100);
          rTimer.SynchronizingObject = this;
          // Hook up the Elapsed event for the timer.
          rTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
        }

        private void OnTimedEvent(object source, ElapsedEventArgs e)
        {
          string response = getResponse();
        }

        public string getResponse()
        {
          string status = "";
          byte[] readBuffer = new byte[255];
          if (sp.IsOpen)
          {
            if (sp.BytesToRead > 0) //there is data to read
            {
              int length = sp.BytesToRead;

              for (int i = 0; i < length; i++)
              {
                readBuffer[i] = (byte)sp.ReadByte();
                status = "pass";
                return status;
              }
            }
         }

        public void readSensor(byte addr)
        {
          if (!sp.IsOpen)
          {
            openPort();
            readSensor(addr); // calls itself again once port is opened
          }
          else if (sp.IsOpen)
          {
            rTimer.Start();
          }
          else
          {
            MessageBox.Show("Port not opened yet");
          }
        }
      }
    }

In the main form I am basically just saying

setupTimer();
readSensor(); 

on a button click.


Solution

  • Start a separate thread, then from that thread write into a queue the results back in your main thread.

    class Game1
    {
        //We declare a queue, which is like an array that we can extract and enter data easily in a FIFO (first in, first out) style list.
        Queue<string> q = new Queue<string>();
    
        public void threadStart(object obj)
        {
            //We get the result of your function, while our main function is still looping and waiting.
            string result = readInput()
            //We tell C# that the parameter we passed in, is in fact the Game1 class passed from "t.Start"
            Game1 game = (Game1)obj;
            //This puts our "result" into the queue.
            game.q.Enqueue(result);
        }
    
        public void start()
        {
            //Declares a new thread, which will run "threadStart" function.
            System.Threading.Thread t = new System.Threading.Thread(threadStart);
    
            //We start the other thread (that will run in parallel) and pass "this" as the parameter.
            t.Start(this);
    
            //We loop over and over, sleeping, whilst the other function runs at the same time. This is called "multi- threading"
            while (q.Count == 0)
            {
                System.Threading.Thread.Sleep(10);
            }
    
            //This gets the last-entered (oldest) value from the queue q.
            string result = q.Deque();
        }
    }
    

    So this sets off a thread to get the result, and then in my version, polls the queue for a while until the results come back, but in yours could do a bunch of stuff, as long as you check the queue every now and again for new data.

    Edit: Added commenting to hopefully alleviate some of your questions.