Hi - I’m new to Xojo and still getting my feet wet, though I have several years of programming experience in C++ and assembly.
For a project I’m working on I want to have Timer scanning the serial ports for a particular connected device, then a second timer that gets kicked off after sending a query on each port that acts as a timeout while waiting for a response.
The first timer’s working fine, as are the serial connections, but for some reason the second timer is not. (That is - the second timer works when enabled directly, but when enabling from within the first timer it never seems to trigger). Here’s a simplified loop that’s inside the Action event handler of the first timer:
TextArea1.AppendText(“Begin!”+Chr(13))
responseTimer.Mode = Timer.ModeSingle
Do
App.SleepCurrentThread(5)
Loop until timerDone
TextArea1.AppendText(“End”+Chr(13))
In the Action event handler for responseTimer I set
timerDone=True
Any idea why responseTimer is never getting triggered?
Remove your Do…Loop Until loop altogether. Set your responseTimer mode then call your TextArea1.AppendText(“End”+Chr(13)) from the Action Event of the timer. Don’t use “timerDone” at all. You are confusing the issue with too many loops.
Also, better to use Xojo’s “endOfLine” than rely on Chr(13) to be valid on all platforms
Timer action events are executed one at a time on the same thread, meaning the second Timer must wait for the first Timer to fully exit its Action event.
[quote=412545:@Jesse Rosen]Do
App.SleepCurrentThread(5)
Loop until timerDone[/quote]
While the first timer’s action event is looping, it blocks all other system and UI. No other Timer can fire, no mouse clicks can be responded to, no keyboard entry, etc.
Thanks for the replies - I’ve got a better sense of how the timer is implemented now. So I think I’ve got a way that works for my current project. But in general is there a good pattern in Xojo for doing synchronous communication over a serial port with a timeout in the case of a break in communication?
Subclass the Serial for your device and add a Timeout property as Integer with a default that makes sense. Add a Timer property that gets initialized in the Constructor with an AddHandler, and torn down in the Destructor.
Override the Write method to start the Timer after super.Write and the DataAvailable event to stop the Timer when appropriate (the incoming data is complete). The Timer itself will raise a TimedOut event if allowed to fire.
MyDevice As Serial
Property Timeout As Integer = 10
Private Property TimeoutTimer As Timer
Event DataAvailable()
Event TimedOut()
Sub Constructor()
TimeoutTimer = new Timer
TimeoutTimer.Mode = 0
AddHandler TimeoutTimer.Action, WeakAddressOf TimeoutTimer_Action
End Sub
Sub Destructor()
if TimeoutTimer isa object then
TimeoutTimer.Mode = 0
RemoveHandler TimeoutTimer.Action, WeakAddressOf TimeoutTimer_Action
TimeoutTimer = nil
end if
End Sub
Sub TimeoutTimer_Action (sender As Timer)
#pragma unused sender
RaiseEvent TimedOut
End Sub
Sub Write (data As String)
super.Write( data )
TimeoutTimer.Period = Timeout * 1000
TimeoutTimer.Mode = 1
End Sub
On DataAvailable()
// Check to make sure it's complete, then
if itIsComplete then
TimeoutTimer.Mode = 0
RaiseEvent DataAvailable()
end if
End
@Kem Tekinay Thanks- that helped a lot. (I’ve been busy with a hardware project and only got back to this today). I did something similar to what you suggested and it works. After spending much of the last decade writing embedded code in severely memory-constrained systems it’s taking a while to adjust to the paradigms of this kind of event-driven programming!