[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

notify with a status?

Posted on 2011-10-14
10
Medium Priority
?
270 Views
Last Modified: 2012-05-12
I know you can notify with
event.WaitOne();

Open in new window

What if my receiver wants to know a little more than just a ping? I can be notified that a message has arrived. What if I also want to be notified with an integer that summarizes the message?

I thought of leaving the integer in a global place for receiver to fetch, but there's no promise the number they fetch hasn't been written over by the time they fetch it. I'd like a little more direct delivery to the receiver.
0
Comment
Question by:deleyd
  • 6
  • 4
10 Comments
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 36971885
Wrap the thread, ping, and Wait() in a CLASS so that you can create multiple instances and store data about each one individually.
0
 

Author Comment

by:deleyd
ID: 36972471
Not quite sure what is meant. I send a message and expect a reply. Simple in Procedural oriented language: send message, read reply.  A bit harder in Event-Driven language. I send a message. Someone else reads replies. I need to know if reply is what we expected or if there was a problem.

1. a sender that sends a message and wants to know if the reply that comes in is the expected reply

2. a watcher waiting for a message to arrive

3. watcher is notified of event "incoming reply has arrived

4. watcher retrieves incoming reply

5. watcher parses reply

6. watcher would now like to notify someone about this reply to their message, so they can determine if this is the expected reply or if there was a problem

0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 36973285
I have no idea what you're doing so here's a made-up example: Idle-Mind-512432.flv
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;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private delegate void ProcessReply(string reply);

        private void button1_Click(object sender, EventArgs e)
        {
            if (this.textBox1.Text.Trim().Length > 0)
            {
                SomeDevice sd = new SomeDevice();
                sd.Reply += new SomeDevice.ReplySignature(sd_Reply);
                sd.SendMessage(textBox1.Text);
                textBox1.Clear();
                textBox1.Focus();
            }
        }

        void sd_Reply(string reply)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new ProcessReply(sd_Reply), new Object[] { reply });
            }
            else
            {
                listBox1.Items.Add(reply);
            }
        }

    }

    public class SomeDevice
    {

        private string message;
        private static Random R = new Random();
        private System.Threading.Thread T = null;
        private System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);
        
        public delegate void ReplySignature(string reply);
        public event ReplySignature Reply;

        public void SendMessage(string message)
        {
            if (T == null)
            {
                this.message = message;
                this.mre.Reset();
                this.T = new System.Threading.Thread(new System.Threading.ThreadStart(this.WaitForReply));
                this.T.Start();

                System.Threading.Thread Respond = new System.Threading.Thread(new System.Threading.ThreadStart(this.RandomResponseTime));
                Respond.Start();
            }
        }

        private void WaitForReply()
        {
            this.mre.WaitOne();

            if (this.Reply != null)
            {
                // Reply with the initial message backwards
                string reply = "";
                for (int i = this.message.Length - 1; i >= 0; i--)
                {
                    reply = reply + message.Substring(i, 1);
                }

                this.Reply(reply);
            }

            this.T = null;
        }

        private void RandomResponseTime()
        {
            System.Threading.Thread.Sleep(SomeDevice.R.Next(3000, 10001)); // three to ten second response time
            mre.Set();
        }

    }

}

Open in new window

0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:deleyd
ID: 36974791
So SomeDevice sends a message with sd.SendMessage, then we start a thread which waits for a reply, and when we get a reply we display it.

Where would we insert a check to make sure the reply matches up with what was sent? (i.e. the reply is what we expected and not an error message. If it is an error message, how do we trace that error message back to the message we sent which generated that error message reply?)

Where would we insert a timeout in case we never get a reply?
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 36974817
Good questions.

I would modify the event signature so that it also passes out a reference to the instance of SomeDevice that is the source of the event:

    public delegate void ReplySignature(SomeDevice sender, string reply);

Then, when you raise the event, you pass out "this" as the first parameter:

    this.Reply(this, reply);

Back in the Form, we'll have a reference to the instance of SomeDevice:

        private delegate void ProcessReply(SomeDevice sender, string reply);

        void sd_Reply(SomeDevice sender, string reply)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new ProcessReply(sd_Reply), new Object[] { sender, reply });
            }
            else
            {
               // ... here we can use "sender" to make decisions ...
                if (sender.VVV == WWW && sender.XXX = YYY)
                {

                }

                listBox1.Items.Add(reply);
            }
        }

You'll need to publicly expose the properties in SomeDevice that you want to get to.  In my example "message" was private so you can't get to it.  You'd probably want to make that a public readonly property.  

Additionally, you could store the "reply" in SomeDevice as a public readonly property too.

The "timeout" feature can be implemented in SomeDevice as well by starting another thread that simply sleeps for the timeout duration and then cancels the "send" thread with Abort(); or uses a more elegant mechanism with WaitAll().  You can make the SomeDevice class raise a different custom TimedOut() event and also create another public readonly property to indicate success/failure.
0
 

Author Comment

by:deleyd
ID: 36976086
Thank you very good fun and educational to play with.

Device IO in use flag
Now the last thing I want to add is some sort of a lock flag that indicates "Device IO in use" so that no one can send a 2nd message to the device until the device responds to the 1st message and I am satisfied with the response.

Because this device is temperamental. If I send a 2nd message to it before it replies to the 1st message, it gets all upset and hangs.

Also, there's one case where the device will reply twice, first with a NAK reply, then 20 seconds later with its own "timeout" reply.

So if I get a NAK reply to a certain command I need to prevent other messages from being sent until we get that "timeout" reply, or again the device will hang.



Device got unplugged
And again I'll later need to throw in my own timers to deal with what to do if we don't hear back from the device after a reasonable amount of time (device came unplugged), we then need to initiate recovery procedure to reestablish communication with device:

1)Tell use to plug in the device

2)close port which will throw an exception complaining the port isn't open, but I have to do this because if I simply try to open the port I'll get an exception complaining the port is already open

3) search COM ports for device. (Currently a loop with a pause. Fancier would be to get notified when device gets plugged in.)

3) open port

4) send a test ping to device to confirm we have reestablished communication

5) resume whatever I was doing
(This in itself could be a bit of work to determine what was I doing before this interruption. With procedural code my "state" is determined by where I am in the code, but with this event driven paradigm I'm not sure, maybe I need to set a bunch of global parameters that keep track of what "state" I'm in, what I've already done and what I still need to do.)
0
 
LVL 86

Accepted Solution

by:
Mike Tomlinson earned 2000 total points
ID: 36976143
You can use a Monitor to synchronize the sending of messages from multiple SomeDevice instances:
http://msdn.microsoft.com/en-us/library/system.threading.monitor(VS.71).aspx

Make a static Object so that it is shared across all instances:  
public class SomeDevice
    {

        // ...
        private static Object LockDevice = new Object();

        private void WaitForReply()
        {
            Monitor.Enter(SomeDevice.LockDevice); // <-- code will STOP here if another instance currently has the device "locked"

            // ... Send the Message here ...
      
            // Now Wait for the Reply:
            this.mre.WaitOne();

            if (this.Reply != null)
            {
                // Reply with the initial message backwards
                string reply = "";
                for (int i = this.message.Length - 1; i >= 0; i--)
                {
                    reply = reply + message.Substring(i, 1);
                }

                this.Reply(this, reply);
            }

            this.T = null;

            Monitor.Exit(SomeDevice.LockDevice); // <-- release the lock so other instances can communicate
        }

    }

Open in new window


So the instances won't be able to proceed until the one with the lock calls Monitor.Exit().  At that point, one of the waiting threads will acquire the lock and move forward.  In the simplified example above, Enter() and Exit() are being called in the same method, WaitForReply().  It is possible, however, to place that Exit() call somewhere else.  This could facilitate the more complex scenario where you need to wait for that second reply before releasing the lock.  You need to be careful, though, as failure to properly think through how and when the lock is acquired/released can result in all the threads becoming deadlocked and stuck.

Check out the documentation here for other thread synchronization techniques:
http://msdn.microsoft.com/en-us/library/fxy8dte8(VS.71).aspx
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 36976154
For the timeouts, previously I suggested using another thread that sleeps and then cancels the other thread.  That is still a viable approach...though some might say it is not elegant to kill another thread.

A different approach is to pass a time interval to the WaitOne() call that waits for the reply:
http://msdn.microsoft.com/en-us/library/aa332440(VS.71).aspx

When a reply is received you can set a flag to indicate as such.  Then, when the WaitOne() call returns, you can check the flag to determine if a reply was actually received or if it returned because the timeout duration was reached.
0
 

Author Closing Comment

by:deleyd
ID: 36977048
Thank you very much for all the knowledge. I didn't know one could put the monitor.Exit in another method. I thought monitor got wrapped around a code block and too bad if that's not when you want to release the lock.

It's brought up a number of little nuance questions I'll start another thread on.
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 36977075
It gets complicated...I think the Exit() needs to be running in the same thread as the Enter().  So you could start in one method and then jump to another (you'd be in the same thread still), or if an event is running in the same thread that would work.  Have to really know the flow of the app and where things come from.

That's why I posted the link to the other thread synchronization techniques so you can see your other options and pick the one(s) that fit your needs best.  =)
0

Featured Post

Efficient way to get backups off site to Azure

This user guide provides instructions on how to deploy and configure both a StoneFly Scale Out NAS Enterprise Cloud Drive virtual machine and Veeam Cloud Connect in the Microsoft Azure Cloud.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Many of us here at EE write code. Many of us write exceptional code; just as many of us write exception-prone code. As we all should know, exceptions are a mechanism for handling errors which are typically out of our control. From database errors, t…
More often than not, we developers are confronted with a need: a need to make some kind of magic happen via code. Whether it is for a client, for the boss, or for our own personal projects, the need must be satisfied. Most of the time, the Framework…
Is your data getting by on basic protection measures? In today’s climate of debilitating malware and ransomware—like WannaCry—that may not be enough. You need to establish more than basics, like a recovery plan that protects both data and endpoints.…
Loops Section Overview
Suggested Courses

834 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question