Skip to main content

Notifications

Announcements

No record found.

Microsoft Dynamics AX (Archived)

Cannot Add Event Handler on Serial Port add_DataReceived()

Posted on by Microsoft Employee

We are trying to connect to a truck weighing scale on a custom developed form that reads weight from scale and shows on screen in a continuous manner. 

Before I attempted implementation in X++, I did a proof of concept (poc) with a normal C# project. The basic code of connection and reading data is as follows:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using Microsoft.VisualBasic;
using System.Text.RegularExpressions;

namespace WeightScaleReader
{
    public partial class Form1 : Form
    {
        // Temp Strings
        String stor;
        String stor2;
        // String used to receive Com port data
        String buffer;
        private SerialPort _serialPort;
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {   
        }

        private void startButton_Click(object sender, EventArgs e)
        {       
            //Check if Serial Port is already open
            if (_serialPort != null && _serialPort.IsOpen)
                _serialPort.Close();
            if (_serialPort != null)
                _serialPort.Dispose();
            //Initialize serial port with hard coded settings. Later settings will be transfered to settings file
            _serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);       
            // Add event handler when ever data is received in COM port
            _serialPort.DataReceived += SerialPortOnDataReceived;
            _serialPort.Open();
            messageBox.Text = "Listening on " + _serialPort.PortName + "...\r\n";

           
        }

        private void SerialPortOnDataReceived(object sender, SerialDataReceivedEventArgs serialDataReceivedEventArgs)
        {
            try
            {
                this.buffer = this._serialPort.ReadLine();
                // printing reading to text box to see nature of data. useful for parsing received data
                messageBox.Text += buffer+"\r\n";
                this.Invoke((Delegate)new MethodInvoker(this.Display));
            }
            catch (Exception ex)
            {
                messageBox.Text += "Error " + ex.Message;
                MessageBox.Show(ex.Message);
            }
        }

        private void Display() {
            try
            {
                this.stor = buffer;
                buffer = "";
                // Parsing recieved data to extract the weight
                this.stor2 = Regex.Replace(this.stor.Trim(), "[^0-9]", "");
                comData.Text += stor2;
            }
            catch (Exception e)
            {
                messageBox.Text += " ... ERROR ... \r\n";
            }
        }
    }
}

The above code is working fine.

Next step I transferred the code with logic to a AX form as follows.

private void initilaizeComPort()
{
    try{
        if(serialPort!=null && serialPort.get_IsOpen())
        {
            serialPort.Close();
        }
        if(serialPort!=null)
        {
            serialPort.Dispose();
        }
        serialPort = new System.IO.Ports.SerialPort("COM1",9600,System.IO.Ports.Parity::None,8,System.IO.Ports.StopBits::One);
        portEventHandler = new ManagedEventHandler(this,"serialPortEventHandler");
        serialPort.add_DataReceived(portEventHandler);
        serialPort.Open();
        comPortConnected = true;
    }
    catch
    {
        comPortConnected = false;
    }
    if(comPortConnected){
        info("Com Connection Successful");

    }
    else{
        info("Com Connection Failed");
    }
}

public void serialPortEventHandler(Object object, System.IO.Ports.SerialDataReceivedEventArgs  e)
{
    System.String buffer = null;
    info("Data Received");
    buffer = serialPort.ReadLine();
    info(buffer);
    scaleReading = System.Text.RegularExpressions.Regex::Replace(buffer.Trim(),"[^0-9]","");
    info(scaleReading);
    // Setting scale reading to gauge control
    digiG.set_Text(scaleReading);
}

The initilaizeComPort method is called in the init method of the form. The connection is successful, however when data is received the AX client crashes. The code within the handler never runs.

Just to make sure that com port is sending data and I can read and parse I did the following

private void tickTime()
{
    buffer = null;
    buffer = serialPort.ReadLine();
    info(buffer);
    scaleReading = System.Text.RegularExpressions.Regex::Replace(buffer.Trim(),"[^0-9]","");
    info(scaleReading);
    digiG.set_Text(scaleReading);
    timerHandler = this.setTimeOut(identifierstr(tickTime),200,false);*/
}


I placed the code in a ticktime method which calls it self after a time out. The first call to this method is made right after the initilaizeComPort method in init method

public void init()
{
    super();
    this.initilaizeComPort();
    this.tickTime();
}


Once form was run, the reading started to come on screen changing continuously, how ever the values were delayed and sometimes in correct. This is due to the frequency in which our code is reading the data and the actual throw-put of COM port is different. This is why I feel that event handler must be used.

After some searching I understood that I have to use a delegate which will take the event hanlder as a parameter, but I do not quite understand how to do that. Also if I create a delegate in AOT how will I pass that delegate to add_DataRecieved method in serial port.

I feel the solution is simple but cannot wrap my head around it.

*This post is locked for comments

  • Verified answer
    Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: Cannot Add Event Handler on Serial Port add_DataReceived()

    Hi Martin.. Sorry for the late reply. I solved it :D

    I went back to my Dll solution having the following code:

    public Boolean initiateComPortConnection()
    {
    	try
    	{
    		if (_serialPort != null && _serialPort.IsOpen)
    			_serialPort.Close();
    		if (_serialPort != null)
    			_serialPort.Dispose();
    		
    		_serialPort = new SerialPort(ScaleProperties.Default.ComPort, ScaleProperties.Default.BauteRate, Parity.None, ScaleProperties.Default.DataBits, StopBits.One);       //<-- Creates new SerialPort using the name selected in the combobox
    		_serialPort.Open();     //<-- make the comport listen
    		return true;
    	}
    	catch (Exception e) {
    		return false;
    	}
    }
    public String getReading() 
    {
    	try
    	{
    		this.buffer = _serialPort.ReadLine();
    		this.stor = Regex.Replace(this.buffer.Trim(), "[^0-9]", "");
    	}
    	catch (Exception ex)
    	{
    		resultWeight = ex.Message;
    	}
    	return resultWeight;
    }


    Note I did not add DataReceived eventhandler.

    In the Ax, while initializing my form I initialize the com port using and instance of my class.

    To achieve continuous reading from port rather than using an eventhandler, I use the tickTime() method which keeps calling itself in an interval of 10 milliseconds. In each call, it calls the getReading() method from the dll which reads line from com port and returns the result.

    public void init()
    {
        super();
        this.initilaizeComPort();
        this.tickTime();
    }
    
    private void initilaizeComPort()
    {
        try
        {
            reader=  new ScaleReader.ScaleReaderClass();
            reader.initiateComPortConnection(); //Method within the dll
            comPortConnected = true;
        }
        catch
        {
            comPortConnected = false;
        }
        if(comPortConnected){
            info("Com Connection Successful");
    
        }
        else{
            info("Com Connection Failed");
        }
    }
    
    private void tickTime()
    {
        info(reader.getReading()); // getReading reads line from port and returns result
        timerHandler = this.setTimeOut(identifierstr(tickTime),10,false);
    }
    
    



    This might not be the best way to get continuous reading from a com port to Ax, but it does the trick for me.
    Hope this helps anyone out there facing the same issue.

    
    
  • Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: Cannot Add Event Handler on Serial Port add_DataReceived()

    Will give this a try and get back to you.

  • Martin Dráb Profile Picture
    Martin Dráb 230,214 Most Valuable Professional on at
    RE: Cannot Add Event Handler on Serial Port add_DataReceived()

    I used the usual approach in AX 2012 - I added the project to AOT and configured it to be deployed automatically. No need to deal with DLL files directly.

    To get the data, use properties of FileSystemEventArgs class, such as e.Name or e.FullPath. Note that I didn't bother dealing with the AccessViolationException exception mentioned in my second reply.

  • Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: Cannot Add Event Handler on Serial Port add_DataReceived()

    From what I understand you added dll containing this code in AX. Then you created an instance of MyClass in X++ code. you called Start method and on creating a new file the event handler was fired.

    I have question here. How did you retrieved the data into AX from the event object e?

  • Martin Dráb Profile Picture
    Martin Dráb 230,214 Most Valuable Professional on at
    RE: Cannot Add Event Handler on Serial Port add_DataReceived()

    There is nothing interesting in this part - it's just normal C# code for event handling:

    public class MyClass
    {
        FileSystemWatcher watcher;
    
        public void Start()
        {
            watcher = new FileSystemWatcher(@"c:\Temp");
            // Event handler subscription
            watcher.Created += new FileSystemEventHandler(OnCreated);
            watcher.EnableRaisingEvents = true;
        }
        
        // Event handler
        private void OnCreated(object sender, FileSystemEventArgs e)
        {
            // Get data from e
        }
    
        public void Stop()
        {
            if (watcher != null)
            {
                watcher.Dispose();
            }
        }
    }
  • Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: Cannot Add Event Handler on Serial Port add_DataReceived()

    Could you share the example code? I want to see how you set up the event handler and received its return values into AX.

  • Martin Dráb Profile Picture
    Martin Dráb 230,214 Most Valuable Professional on at
    RE: Cannot Add Event Handler on Serial Port add_DataReceived()

    I've tried my own example (using FileSystemWatcher) and events are raised as expected. You must have some problem in your code.

    It's worth noting, though, that direct calls from the event handler to AX proxy classes led to AccessViolationException. I've checked how Microsoft deals with events in the TFS facade and they don't do such calls from event handlers; they just collect data there and use them in the main thread.

  • Community Member Profile Picture
    Community Member Microsoft Employee on at
    RE: Cannot Add Event Handler on Serial Port add_DataReceived()

    Actually that was my initial plan. I made a class library which had comport connection method and the event handler code. I put the DLL in client bin folder then added DLL as reference in AOT. After that I used the class library and created an instance of the class. The com port connection worked but the event handler was never called. I know this because I polled on the weight variable and it was 0 all the time.

    Again I feel event handler was not properly defined.

    Is it necessary for the event handler to be static?

  • Martin Dráb Profile Picture
    Martin Dráb 230,214 Most Valuable Professional on at
    RE: Cannot Add Event Handler on Serial Port add_DataReceived()

    Maybe you know more ManagedEventHandler class than I do, but are you sure that your use is supported? I'm not very sure of that and the fact that AX crashes suggests that is doesn't have to be the case. I suggest you avoid it.

    I recommend you create a C# class library and add it to AOT (so you can easily consume it from AX). Inside the library, you can use all usual C# magic, including event registration, usings etc., and you can create an interface which is much easier to consume in Dynamics AX. You event handler method may call objects in AX through proxy classes.

    Trying to do everything in X++ may not be the best plan.

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

December Spotlight Star - Muhammad Affan

Congratulations to a top community star!

Top 10 leaders for November!

Congratulations to our November super stars!

Tips for Writing Effective Suggested Answers

Best practices for providing successful forum answers ✍️

Leaderboard

#1
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 291,280 Super User 2024 Season 2

#2
Martin Dráb Profile Picture

Martin Dráb 230,214 Most Valuable Professional

#3
nmaenpaa Profile Picture

nmaenpaa 101,156

Leaderboard

Featured topics

Product updates

Dynamics 365 release plans