|
Please don't use such a Big subject line.
|
|
|
|
|
This is my third attempt to leave this question. The first time I submitted (without keeping a copy of the text - Grrr!) the submission apparently failed and I was left staring at a white screen. The second time was similar, though I did keep a copy.
I am writing a Class to monitor and manage a WiFi-enabled HVAC thermostat which communicates using UDP packets. It sends out a "status report" containing various data every 10 seconds, which I am able to receive (using an asynchronous thread) and decode with no difficulty. Because the thermostat and its WiFi module are relatively slow and single-threaded, I have set the Class up so that settings changes are processed in its main thread and the resulting commands are sent out synchronously from the receiving thread after the status report is received and processed. This is where I have run into som problems.
It appears that if I do not insert pauses (using Thread.Sleep) before and/or between using .Send to transmit each command packet, some packets do not get transmitted (confirmed by using a packet sniffer on the network). In addition, the first packet of at least some combinations of commands appears not to be sent if I do not include an Application.DoEvents call before the pause that precedes it. Putting in breakpoints and/or stepping through the Class code abolishes all misbehavior! This worries me a) because I don't like behavior I don't understand, and b) because I had planned to make the Class into a Class Library, which appears to preclude the use of Application Methods.
The current version of the code is as shown below. Does anyone have any ideas as to what may be going on?
Private Sub ReceiveMessages()
Try
Dim receiveBytes As [Byte]() = TCudpClient.Receive(RecEndpoint)
' Some error checking code omitted
Process_Heartbeat(receiveBytes)
If NewSettings Then ' There are some queued commands
UpDating = True ' Set a flag to prevent new commands being added while we are processing existing ones
Send_Commands() ' See below
UpDating = False
NewSettings = False
End If
ThreadReceive = New System.Threading.Thread(AddressOf ReceiveMessages)
ThreadReceive.Start()
Catch ex As System.Threading.ThreadAbortException
' this generally means that the thread was deliberately aborted, so we can ignore it
Catch e As Exception
If Not ShuttingDown Then Throw New ApplicationException("Problem receiving data from Thermostat" & vbCrLf, e)
End Try
End Sub
Private Sub Send_Commands()
Static blInitDone As Boolean
If Not blInitDone Then
TCudpClient.Connect(IPAddress.Parse(IPText), COMMANDPORT) ' presumably only need to do this once
'It appears that the first message sent after the Connect command sometimes fails to be transmitted,
'so we make sure that it gets sent twice
For Each tc In ThermostatCommands ' find the first available command
If tc.Pending Then
Application.DoEvents()
System.Threading.Thread.Sleep(100)
SendMessage(tc.ComBuff) ' See below
Exit For
End If
Next
blInitDone = True
Else
End If
For Each tc In ThermostatCommands
If tc.Pending Then
Application.DoEvents()
System.Threading.Thread.Sleep(100)
SendMessage(tc.ComBuff) ' See below
tc.ComBuff = Nothing
tc.Pending = False
End If
Next
End Sub
Private Sub SendMessage(ByVal bytArray() As Byte)
Dim intReturn As Integer
intReturn = TCudpClient.Send(bytArray, bytArray.Length)
If intReturn <> bytArray.Length Then
Throw New ApplicationException("Problem sending command to Thermostat")
End If
End Sub
modified on Thursday, September 9, 2010 4:47 PM
|
|
|
|
|
Hi Peter,
1.
the blank pages are a known problem, the powers that be are working hard to get it fixed.
2.
I think you have some threading problems. Here are two things I don't like at all:
2a.
Sub ReceiveMessages() is creating and launching a thread that executes ReceiveMessages(). So you seem to want the code to run repetitively, however instead of using a loop, you create a new thread all the time?
2b.
Application.DoEvents() is evil; when used, AFAIK it should be called only by the main thread, not by any other thread. And I don't think you need it at all.
3.
It is not obvious to me how you intend on using all the code shown, as it consists of subs only. If you need further help, I suggest you explain it a bit more and indicate how/where/when something gets called (e.g. ReceiveMessages() is called from the main form's constructor; or from a button_clicked handler; ...)
Hope this helps.
|
|
|
|
|
2a I am following a model published elsewhere, though the logic did make sense to me. The .Receive call needs to be in an independent thread - it is first activated from initialisation code. When ReceiveMessages is invoked on its thread, the thread stalls (for up to 10 seconds) until the UDPClient.Receive call returns. It then does the necessary processing and ends after invoking a new copy of itself on a new thread. Only one ReceiveMessages thread is in existence at a time (except for the very short time after the reinvocation and before the invoking thread terminates), or at least, that is what I assumed. I am not sure why you feel that using a loop would be better, though I also don't think it would be worse.
2b You are not the only one who thinks that Application.DoEvents is evil, but it seems to be the only thing that produces consistent behavior in this code. I dont know why this should be so, and I would be delighted to replace it with something cleaner that also worked - that was what I was hoping that someone would come up with.
3) This is a moderately large and complex Class (because the Thermostat has lots of bells and whistles!). I should have made it clear that the initial Threading call to ReceiveMessages was from the Class constructor, but I thought that the code which I included, which is all that runs in the problem thread, showed how I had things set up without being excessively long and confusing.
|
|
|
|
|
OK, here are some thoughts:
1.
I'm inclined to use less code; so I'll try and get rid of some. Simpler is better, assuming it works.
2.
I would replace your current Sub ReceiveMessage() by something along these lines:
Private Sub ReceiveMessages()
Try
ThreadReceive = New System.Threading.Thread(AddressOf ReceiverMethod)
ThreadReceive.Start()
Catch ex As System.Threading.ThreadAbortException
' this generally means that the thread was deliberately aborted, so we can ignore it
Catch e As Exception
If Not ShuttingDown Then Throw New ApplicationException("Problem receiving data from Thermostat" & vbCrLf, e)
End Try
End Sub
Private Sub ReceiverMethod()
While Not ShuttingDown
Try
Dim receiveBytes As [Byte]() = TCudpClient.Receive(RecEndpoint)
' Some error checking code omitted
Process_Heartbeat(receiveBytes)
If NewSettings Then ' There are some queued commands
UpDating = True ' Set a flag to prevent new commands being added while we are processing existing ones
Send_Commands() ' See below
UpDating = False
NewSettings = False
End If
Catch ex As System.Threading.ThreadAbortException
' this generally means that the thread was deliberately aborted, so we can ignore it
Catch e As Exception
If Not ShuttingDown Then Throw New ApplicationException("Problem receiving data from Thermostat" & vbCrLf, e)
End Try
End While
End Sub
This makes sure only one extra thread is used, and every execution of the original code is by that one thread (including the very first, which wasn't true at all in the original code). The original with a new thread every so often is expensive on system resources, and has the risk of two of those threads running at the same time, confusing you and making you add code to fix things that could have been avoided.
Note: some details may need fixing (e.g. what exceptions to catch, and how to deal with them)
[ADDED]
Remark: make sure the while loop is blocking somehow; it should not just sit there spinning like mad, wasting lots of CPU cycles; if there is no way to make it block (i.e. wait on something), insert a time delay using Thread.Sleep
[/ADDED]
3.
Just remove the DoEvent stuff.
4.
If you want to send the same thing twice, no need to have two for loops. This should do:
For Each tc In ThermostatCommands ' find the first available command
If tc.Pending Then
System.Threading.Thread.Sleep(100)
SendMessage(tc.ComBuff) ' See below
System.Threading.Thread.Sleep(100)
SendMessage(tc.ComBuff) ' See below
tc.ComBuff = Nothing
tc.Pending = False
Exit For
End If
Next
5.
Not sure what ThermostatCommands is, and why you need a pending flag and why you clear the ComBuff; I would be inclined to create new command objects and stuff them in a Queue. Then the sender does not need a for loop at all, it just pulls the head of the queue and sends it.
6.
I'm pretty sure debugging such application is mind boggling if you create a lot of threads, then try to single-step and watch in some IDE or debugger. I tackle those apps differently, by adding logging code,
making sure each log line gets a time stamp. This article[^] gives the philosophy and some code snippets (in C#, could be done in VB.NET equally well).
7.
After some refactoring along above lines, I would not be surprised if some of your comments became obsolete, I mean the ones about the first message sometimes failing, and possibly also about having to send things twice.
Good luck.
|
|
|
|
|
Thanks!
The short version is that your suggestions about reorganising the Threading certainly simplified things and may well have been 90% responsible for solving the problem (I'll come back to the 10%, below)
The longer version:
1. Agreed!
2. Done, with very minor modifications. The While loop blocks "automatically" on the UDPClient.Receive call, which doesn't return until the next Status Report packet arrives, 10 seconds later.
3. Done (and in one other place in the Class)
4. It was actually only the first packet sent after the (initial and only) UDPClient.Connect call that seemed to have problems - only that packet was sent twice by my original code (the first For loop has an early exit). Your code sends every packet twice, which was not my intention!
5. Threads (up until recently) weren't my thing. Data structures are! There are (IMHO) good reasons for the way I am doing this.
6. I have resorted to this sort of approach once or twice (more when I was writing real-time data acquisition and control stuff), but haven't found it useful in general programming often enough to put the "hooks" for it in my code on a routine basis.
7. It appears from further experimentation with the modified code that you do need delays between at least some calls to UDPClient Methods, and that I wasn't leaving quite enough time between them in some cases. Sleeping for 200 msec seems to work reliably for my situation - 100 msec is not always enough. I think that it is possible that the Application.DoEvents calls were "working" simply because they were wasting more time, and the poor threading code may also have been only a contributing factor to the problems.
|
|
|
|
|
I'm glad you got on top of it, well mostly anyway.
5.
I've done lots of real-time stuff, even with queues. One very neat trick is this:
- create two queues, lets call them FULL and EMPTY; FULL will represent the functional queue, EMPTY will be the store for messages currently not in use;
- create N messages, put all of them in the EMPTY queue;
- have the producer get a message from the EMPTY queue, fill it, and put it in the FULL queue;
- have the consumer get a message from the FULL queue, execute it, and put it in the EMPTY queue.
The net result is you don't need object creation/destruction once things have been initialized; and you get an automatic dataflow-limited behavior, as either queue will never hold more than N items.
It works just great, provided all messages have the same type (or size).
6.
Same here. In embedded systems, you typically want to observe without halting everything, hence logging fits the bill.
What remains to be done IMO is figuring out why you need your delays. Is it related to the reaction time of your thermostat? (i.e. is it deaf while processing an earlier command or transmitting a result?) Timestamped logging should help out here as well.
|
|
|
|
|
5. In this case, all the messages are not the same size (the message header length is constant, but a message can contain 0, 1, 2, or an arbitrary (2 < n <= 26) number of additional bytes of data). Also, for a number of reasons related to the device and application, I want to be able to identify and (if necessary) modify a pending command, if another command of the same "class" is issued before the first one is executed, which may be up to 10 seconds later.
The thermostat is relatively slow (in computer terms), and can only do one thing at a time, but I don't think that this can be causing the need for the delays. If I don't put them in, some of the UPD packets are never sent. It is as if the outgoing UDP port needs time to "clear" before it will send a new message.
|
|
|
|
|
I need help how i can add differen control in datagirdview in a singel column.
Like
ProperName showvaule
testName show value in combox
testName2 show value in text
testName3 show value in combox
|
|
|
|
|
This is going to be a huge pain to do. The DGV was designed to have a single display control and edit control in any one column. What you would have to do is create your own custom DatGridViewColumn classes that determines which edit control to display based on some criteria. You can find an example of creating a custom column here[^]. Note that doing this requires three seperate classes.
|
|
|
|
|
hi members
I want to retrieve the application path of program which is currently running in task manager, please help me.
|
|
|
|
|
System.Diagnostics.Process.GetProcessesByName will give you access to the process.
Call .MainModule.FileName will return the full path to the exe.
|
|
|
|
|
To Get path of running Application listen in TaskManager you can use Given Code
Take a List box and a Button and write given code in click event of button.
foreach (Process p in Process.GetProcesses())
{
string procFile;
try
{
procFile = p.Modules[0].FileName;
}
catch (Win32Exception ex)
{
procFile = ex.Message;
}
listBox1.Items.Add(string.Format("Process {0}: {1}", p.ProcessName, procFile));
}
|
|
|
|
|
Hello coders,
I've been wondering this for some time, but haven't been able to find an answer to: How does DoEvents actually work (technically seen)?
The way I look at it, you have this picture describing your single-threaded application:
[ Your code ]
[ Translation to .NET events ]
[ Message Pump ]
With basically messages coming in from Windows at the Message Pump level, then advancing up until it reaches your code in terms of Events. Your code runs etcetera, and whenever execution 'pauses' (e.g. disappears when debugging), the message pump gets execution again and handles messages. Somewhere parallel is the garbage collector, asynchronously cleaning up your mess in collect 'n reap batches. Am I at least right about this picture (if not, then my question might not make sense)?
The actual question then is: how does DoEvents, called from your code, manage to fall back into the Message Pump? I'd think you need to re-enter the calling function, so basically advance through the stack back to the caller, without losing all the context allowing you to move up to your original point of execution. That means that a simple call to the message pump 'function' would not be the way to, because then it would end up a top of the stack.
One solution to this I was thinking of was to use something like SendMessage(WM_USER_IDLE, ...) to get back into the message pump, but is that even possible/allowed? Does anyone know the answer, because I am REALLY curious
Thanks for reading my rant, and I'll hope you have some answers,
Richard
|
|
|
|
|
There is no such thing as "the message pump", i.e. you can have as many pumps as you like.
Example: when you show a dialog, it has its own message pump, which processes messages related to the dialog and ignores the others.
DoEvents() looks like yet another message pump, which processes everything until there are no more messages, then returns. And it is pretty dangerous to call DoEvents() from within an event handler, as it may cause that very handler to be called again, i.e. it may re-enter which you probably did not intend nor anticipate.
|
|
|
|
|
So it really is another message pump (quite like the original) atop of the call stack? For some reason, I felt it couldn't be that, but don't know exactly why. Probably because this new message pump would have to have the same behaviour as the original. I guess it's possible in an environment like .NET (or VB runtime before that).
Thanks for your answer.
|
|
|
|
|
Luc Pattyn wrote: you can have as many pumps as you like.
Correct.
Luc Pattyn wrote: a dialog, it has its own message pump
In general not correct. However, modal dialogs usually have their own message pump.
Time you enjoy wasting is not wasted time - Bertrand Russel
|
|
|
|
|
that is what I meant, to me a dialog is a modal window and a modal dialog a tautology.
|
|
|
|
|
Windows under the hood is still basicaly a non-preemptive multitasking OS, meaning that on a given processor core, individual threads are allocated execution time slices by the system one at a time. During your thread's slice of time, your application has the core and can execute as long as it sees fit, returning to the system when you are done. In such an OS for an application to "play nice" with others, if you running a long execution, you would intersperce your code with DoEvents (especially if you have a long running loop, you put a DoEvents in the loop) to allow the OS to have a timeslice to perform it's housekeeping routines, send pending messages, etc.. In other words, DoEvents allows your code to return execution to the OS for a timeslice then execution returns to you where you left off. This is especially handy if you are coding a Windows Forms app that updates it's screen elements as your code is running (example a progress bar). If you don't allow the OS a timeslice to send messages to your program to re-draw, your window will seem to freeze untill your long running process is finished and execution is returned to the OS (your app's screen won't redraw).
Kevin Rucker, Application Programmer
QSS Group, Inc.
United States Coast Guard OSC
Kevin.D.Rucker@uscg.mil
"Programming is an art form that fights back." -- Chad Hower
|
|
|
|
|
Spectre_001 wrote: Windows under the hood is still basicaly a non-preemptive multitasking OS
I don't agree. The Windows kernel is event driven, is priority based, is pre-emptive, has fairness implemented, and contains some protection against CPU monopolizing processes. There is no need to call DoEvents(), or anything else, at all to get things to work properly. Here is a simple test: create a process that contains a long computation and have it optionally generate output while computing; now run a number of instances and watch how each of them is proceeding.
Windows is not a real-time OS, which means it is not guaranteeing much on latency; however it does not need the user's cooperation to provide a good interactive user experience. There are ways to make an app behave badly, one of them would be to execute long or blocking stuff on a WinApp's main thread; and there are ways to make an app fail, one of them is calling Application.DoEvents() causing code to get re-entered while it wasn't designed for re-entrance to start with.
BTW: none of the misbehavior mentioned (block GUI thread, call DoEvents) would negatively affect other processes.
|
|
|
|
|
I thought this question was going to be about the access function (does the same kind of thing)
*shudders at recollection of coding in access*
---Guy H ---
|
|
|
|
|
Guy Harwood wrote: *shudders at recollection of coding in access*
Ahh the heady days of my youth spent coding VBA... I can barely remember what it was like to end a line of code without a semicolon and have a hard time doing it when I wander into VBA to trick out a macro in Excel for a co-worker these days. Makes me feel old to think of how long ago that was (mid 90's).
Mike Devenney
|
|
|
|
|
Reflector is your friend:
public static void DoEvents()
{
ThreadContext.FromCurrent().RunMessageLoop(2, null);
}
If you keep poking deeper, you see that it's just running a message loop to handle any messages that might have been sent.
|
|
|
|
|
I beleive DoEvents call moves the pointer on the call stack to where the Windows Messages are, saving the current point and then returning to it after the windows messages are processed.
Of course not this over simplified but I beleive the logic works this way...
|
|
|
|
|
That is not correct. The Windows Message queue is not "on" the stack. No weird call stack operations are performed.
For .Net Application.Run() and Application.DoEvents() are somewhat similar in that they both call a message pump. A message pump could be very simply be:
MessagePump()
{
while ( true )
{
Message msg = GetMessage();
DispatchMessage( msg );
if ( msg.Msg == WM_QUIT ) break;
}
}
The code behind DispatchMessage will eventually call your events, e.g. when a button is clicked.
If you then do a form.ShowDialog() it will re-enter the message pump function (recursively, the previous call just stays on the call stack).
This is why showing a message box in a timer event handler will result in stacking up message boxes until resources are exhausted or a stack overflow occurs.
The call stack will look something like this (looking at it top-down) :
Application.Run
MessagePump
DispatchMessage
... framework stuff ...
Timer1.TimerEvent
SomeForm.ShowDialog
Application.DoEvents
MessagePump
DispatchMessage
... framework stuff ...
Timer1.TimerEvent
SomeForm.ShowDialog <-- if you indeed do this call then this will repeat until...
modified on Wednesday, September 8, 2010 2:36 PM
|
|
|
|
|