return array from called function

engr.ahayee

Member
Joined
Aug 10, 2021
Messages
16
Programming Experience
Beginner
Hi everyone.
I am trying to read data from serial port and want to do some processing.
I have defined a function which should return the received data
function:
testing_array = ReadHoldingRegisters(byte.Parse(txtSlaveAddr.Text), int.Parse(txtStartAddr.Text), int.Parse(txtLength.Text));

following is the complete code
program:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;

namespace _485_Communication
{
    public partial class Form1 : Form
    {
        public SerialPort MBPort = new SerialPort();
        bool ConnectedStatus = false;

        byte[] testing_array;

        public Form1()
        {
            InitializeComponent();
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            MBPort = new SerialPort(txtCommPort.Text);
            MBPort.BaudRate = int.Parse(cbBaudRate.Text);
            if (cbParity.Text == "None") MBPort.Parity = System.IO.Ports.Parity.None;
            else if (cbParity.Text == "Even") MBPort.Parity = System.IO.Ports.Parity.Even;
            else if (cbParity.Text == "Odd") MBPort.Parity = System.IO.Ports.Parity.Odd;

            try
            {
                if (MBPort.IsOpen) MBPort.Close();
                MBPort.Open();

                lblStatus.Text = "Connected";
                lblStatus.ForeColor = Color.Green;
                ConnectedStatus = true;
            }
            catch (Exception ex)
            {
                lblStatus.Text = "Error!" + ex.ToString();
            }
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            MBPort.Close();
            lblStatus.Text = "Disconnected";
            lblStatus.ForeColor = Color.Red;
            ConnectedStatus = false;
        }

        private void btnRead_Click(object sender, EventArgs e)
        {
            if(ConnectedStatus)
            {
                try
                {
                    testing_array = ReadHoldingRegisters(byte.Parse(txtSlaveAddr.Text), int.Parse(txtStartAddr.Text), int.Parse(txtLength.Text));
                }
                catch(Exception ex)
                {
                    lblStatus.Text = "Error!" + ex.ToString();
                }
            }
        }

        public byte[] ReadHoldingRegisters(byte SlaveAddr, int StartAddr, int Length)
        {
            byte[] frame = new byte[8];
            byte[] return_data = new byte[2];

            frame[0] = SlaveAddr;
            frame[1] = 3;
            frame[2] = (byte)(StartAddr >> 8);
            frame[3] = (byte)StartAddr;
            frame[4] = (byte)(Length >> 8);
            frame[5] = (byte)Length;

            byte[] CRC = CalculateCRC(frame);
            frame[frame.Length - 2] = CRC[0];
            frame[frame.Length - 1] = CRC[1];

            try
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
                {
                    MBPort.Write(frame, 0, frame.Length);
                    Thread.Sleep(100);
                    if (MBPort.BytesToRead >= 5)
                    {
                        byte[] bufferReceiver = new byte[MBPort.BytesToRead];
                        MBPort.Read(bufferReceiver, 0, MBPort.BytesToRead);
                        MBPort.DiscardInBuffer();

                        byte[] data = new byte[bufferReceiver.Length - 5];
                        Array.Copy(bufferReceiver, 3, data, 0, data.Length);
                    }

                }));
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return return_data;
        }

        public byte[] CalculateCRC(byte[] data)
        {
            ushort CRCFull = 0xFFFF;
            byte CRCHigh = 0xFF, CRCLow = 0xFF;
            char CRCLSB;
            byte[] CRC = new byte[2];
            for (int i = 0; i < (data.Length) - 2; i++)
            {
                CRCFull = (ushort)(CRCFull ^ data[i]);

                for (int j = 0; j < 8; j++)
                {
                    CRCLSB = (char)(CRCFull & 0x0001);
                    CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);

                    if (CRCLSB == 1)
                        CRCFull = (ushort)(CRCFull ^ 0xA001);
                }
            }
            CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
            CRC[0] = CRCLow = (byte)(CRCFull & 0xFF);
            return CRC;
        }
    }
}

I am getting data from serial port as I mentioned in the following picture.
received data.png


my question is how I return this data to testing_array?
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,190
Location
Sydney, Australia
Programming Experience
10+
You don't. You are queueing a work item on the thread pool and that will get done when the system feel s like it. If you want to actually wait until the data is available and return it then you should just execute the code right there, not via the ThreadPool. If you want to be able to execute code asynchronously but still return the result of a function then you should look into using Tasks and possibly async/await. That's a whole topic so it's probably not appropriate to just provide code to copy and paste.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
4,029
Location
Chesapeake, VA
Programming Experience
10+
There's four things that you need to do:
1) Pass in some kind of data transfer object to be filled in/updated to the callback you created on line 91, by adding a second parameter to QueueUserWorkItem(). See documentation.
2) Inside your callback, copy the test data into your data transfer object. Likely lines 101-102 can be changed to to directly allocate the array copy into your object.
3) Setup a mechanism in your ReadHoldingRegisters() so that the callback thread can notify the running thread that it got the data. Usually a ManualResetEvent is the easiest way to accomplish this in an beginner friendly way. Again see the same code in the documentation I linked to in step 1 above.
4) Before line 112, wait for the event in your method. When the wait is done, then reallocate your current return_data to be big enough to not only hold the two bytes you already have in there, but to also the test data that you wanted to return. Obviously copy the two bytes as well as the test data into this new array.

Be warned that waiting can/will lock up your main UI thread. Depending on the speed and reliability of your device communication, this may or may not be acceptable. It looks like you are already trying to workaround this by queuing the work into a thread pool, but you may need to do more. An alternative is to consider using the more modern async/await.
 

engr.ahayee

Member
Joined
Aug 10, 2021
Messages
16
Programming Experience
Beginner
can I use this kind of routine for receiving serial data?
function:
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            dataIN = serialPort1.ReadExisting();
            this.Invoke(new EventHandler(showData));
        }

        private void showData(object sender, EventArgs e)
        {
            for (i = 0; i < dataIN.Length; i++)
            {
                received_Data[i] = Convert.ToByte(dataIN[i]);
            }

            foreach (byte b in received_Data)
            {
                Response_data.AppendFormat("{0:X2}", b);
                Response_data.AppendFormat(" ", b);
            }

            txtDataIN.Text = Response_data.ToString();
        }
 
Top Bottom