There is now 2 weeks that I try to find a solution to my problem
The problem is relative simple to understand.
My PC have to control a serial device.
But The PC has to be the Master and the device a Slave.
So there is a Handshaking mechanism
I use, to simulate the serial port, the free "Virtual Serial Ports
Emulator" : http://www.eterlogic.com/Products.VSPE.html
I virtually connect com1 and com2, to simulate a loopback
Run the below tcl code, and use "Tera Term" terminal emulator, to
enter characters, and to read it on my shell
In the example below, to allow to enter chars in the terminal the
delay and timeout are long enough to play manually with the program
So in the example
Every 5 seconds
Check to see is something is available on the serial port:
For this
* Enter in FreeRunningRead Mode
* write "RTR" (Request To Read message) on serial port
* Read the answer message from the device.
if message is Acknowledged by AKR (Acknowledge Read) message, exit
mode, and enter in "Waiting" mode
or
* Wait maximum 2s for an answer. If delay passed, issue by displaying
"Timeout message", exit mode, and enter in "Waiting" mode
FreeRunningRead Mode
PC Device
---------------- RTR ---------------> o
<-------------- Message -------------
<-------------- AKR ----------------
This is easy.
The problem is now, that during all this time, there can be an
asynchronous event, which could interfere.
This event will try to write a data message to the serial port, and
must be acknowledged by AKW
ForcedWrite Mode
PC Device
--------------- Message ------------->
<--------------- AKW ----------------
And here is the issue.
Before to write the message, I must be sure that I'm outside:
FreeRunningRead Mode
Trying to play with after, vwait in all the direction, I pass now more
then 2 weeks to find a solution, but Without success :-((
It seems that my approach seems not to be good.
Please, if someone have a little bit time to analyse my program.
Thanks in advance
set chid [open com1: r+]
fconfigure $chid -mode "9600,n,8,1" -blocking 0 -buffering none -
translation binary
fileevent $chid readable [list serial_receiver $chid]
set mode Waiting
set MessageRead ""
set LoopTime 5000
set Timeout 2000
set cnt 0
proc serial_receiver { chan } {
global mode
global MessageRead
global FreeRunningRead_Result
global ForcedWrite_Result
set data [read $chan]
append MessageRead $data
if {$mode == "ForcedWrite"} {
if { [string range $MessageRead end-2 end] == "AKW"} {
set ForcedWrite_Result AKW
}
} elseif {$mode == "FreeRunningRead"} {
if { [string range $MessageRead end-2 end] == "AKR"} {
set FreeRunningRead_Result AKR
}
}
}
proc FreeRunningRead { chid } {
global mode
global MessageRead
global Timeout
global FreeRunningRead_Result
global ForcedWrite_Result
set mode "FreeRunningRead"
puts "Start FreeRunningRead"
puts -nonewline $chid "RTR"
puts -nonewline $chid \x0D
puts -nonewline $chid \x0A
set MessageRead ""
after $Timeout { set FreeRunningRead_Result TimeOut }
vwait FreeRunningRead_Result
switch $FreeRunningRead_Result {
"TimeOut" {
puts " FreeRunningRead TimeOut"
}
"AKR" {
puts " FreeRunningRead = $MessageRead"
}
}
puts "Stop FreeRunningRead"
set mode "Waiting"
}
proc ForcedWrite { chid } {
global mode
global MessageRead
global Timeout
global FreeRunningRead_Result
global ForcedWrite_Result
global cnt
if {$mode == "FreeRunningRead"} {
vwait $FreeRunningRead_Result
}
set mode "ForcedWrite"
puts "Start ForcedWrite"
set cnt [expr $cnt + 1]
puts -nonewline $chid "Message Write $cnt"
puts -nonewline $chid \x0D
puts -nonewline $chid \x0A
set MessageRead ""
after $Timeout { set ForcedWrite_Result TimeOut }
vwait ForcedWrite_Result
switch $ForcedWrite_Result {
"TimeOut" {
puts " ForcedWrite TimeOut"
}
"AKW" {
puts " ForcedWrite = $MessageRead"
}
}
puts "Stop ForcedWrite"
set mode "Waiting"
}
wm protocol . WM_DELETE_WINDOW { exit }
button .b1 -text Quit -command {exit}
button .b2 -text Write -command {ForcedWrite $chid }
pack .b1
pack .b2
set HS_Loop 0
while {1} {
if {$mode == "Waiting" } {
after $LoopTime { set HS_Loop 1 }
vwait HS_Loop
FreeRunningRead $chid
} else {
after $LoopTime { set HS_Loop 1 }
vwait HS_Loop
}
}
Looking at your code, I think a re-design is called for.
There are 2 design criteria that your code does not meet, that are
characteristic of a working design:
1) Instead of waiting on multiple different variables, you should wait
on the fewest number of possible variables, preferably only one. That
way every routine can (a) check what the current state is (b) you can
never end up in 'two states at once' (c) any routine that changes the
state can do so globally
2) Since you have expected events and unexpected events, you should:
a) always only 'block' on expected events, never on unexpected events.
b) buffer all your un-expected events (while waiting for expected
events) by putting them in a queue.
Only then handle the unexpected events queue when you are no longer
waiting for expected events.
Provided you follow (1) religiously, (2) can be made more complicated
by pushing and popping state on a stack.
Dave
Hi Dave
Thanks for the proposed criteria
I will try to follow your rules, and remodel completely the program.
Nevertheless, I think that the second point: "buffer all your un-
expected events" will not be easy.
I have no idea for the moment, how to store these events ??
Will be a hard think and not evident, and will see what I will propose
in the next 2 weeks :-)
In all the case, I suspected, when I wrote the message, that I would
have to take the problem in an different way.
Ok Lets go .....
Thanks for now Dave
Regards
Christian
On Dec 9, 6:00 pm, CKL <christian.klugesh...@gmail.com> wrote:
>
> Nevertheless, I think that the second point: "buffer all your un-
> expected events" will not be easy.
> I have no idea for the moment, how to store these events ??
>
>
> Christian
The obvious way may be the easiest:
set todoBuffCount 0
.
.
.
While
if {$mustBuffer} {
Have you looked into expect?
look into the exp_background command.
Couple of years ago I had to "connect" to lab scales that could
be commanded but also emmited loadcell values in second intervals
( actually programmable )
I matched all input from the scales in the background
evaluating incoming messages ( value or cmd reply )
setting state information in elements of an array variable.
All outgoing messages were queued and "timed" via state info in the same array.
uwe
On Dec 9, 6:41 pm, "dave.joub...@googlemail.com"
While blocked events pending
# Waiting for incoming packet
vwait ...
if {$mustBufferThisMessage} {
set todoBuff($totdoBuffCount) $message
incr todoBuffCount
} else {
# expected event .....
}
}
# All expected events done
if {$todoBuffCount > 0} {
for {set todoBuffPointer 0} ......
handle $todoBuff($totdoBuffPointer)
}
}
set todoBuffCount 0
Could as worst case send you my actual code for this particular
problem, but then you need to get your head around a lot of app
specific detail, which may obscure the big picture.
Dave
Yup, if you get the overall design right, then expect may be a good
way to write it.
If however your app can go away and do other things and get async
message during that time, then it could be a bit tricky.
Dave
Hi,
Thanks for these information
Now just some precision relative to what I want to do.
I don't know if you understood really my problem
The difficulty, is
* When clicking on an button, launch an action (unexpected events)
** to write data to the Device
** Limit the answer by a timeout threshold (some ms) in case of
issue
I need
* vwait: to wait the timeout, during the time the data is read
For this (unexpected events) it is not mandatory to buffer future
action.
I want only block all new action, until the write task (unexpected
event) has finished
Overhead this (unexpected event)
* I wanted to make the necessary task (expected events) each 5
seconds, with
** Ask the device to see if it has something to return to the PC
** Limit the answer by a timeout threshold (some ms) in case of
issue
I need
* vwait: to control when the task have to be run (each 5 seconds) to
avoid that the CPU of my PC will be 100% used
* vwait: to wait the timeout, during the time the data is read
We see that I have to use at minimum 3 vwaits, meaning that I don't
know how to do this with "ONE" as you proposed Dave
The tcl code above, try to do this, but .... don't work as I want :-((
Christian
I assume you will use the package expect
you write a
proc repeat {delay script} {
if 1 $script
after $delay repeat $delay $script
}
repeat 5000 {dev_trigger ...}
proc dev_trigger ... {
....
incr ::globalstate(typeA,message,cnt)
lappend ::globalstate(eventq) outgoing
after $trigger_timeout lappend ::globalstate(eventq) outgoing_timeout
}
# ok 1 vwait fixed
exp_background \
$typeamessage {
do_typea_Thing
incr ::globalstate(typeA,message,cnt)
lappend ::globalstate(eventq) typeA
} $typebmessage {
do_TypeB_Thing
incr ::globalstate(typeB,message,cnt)
lappend ::globalstate(eventq) typeB
} eof {
puts stderr "aijeehh, you've killed it"
}
any GUI interaction changes something in ::globalstate
# ok 2. vwait fixed
while true {
vwait ::globalstate ;# this is the place were all the background stuff
;# is done as events roll in.
set currq ::globalstate(eventq) ; set ::globalstate(incoming) {}
foreach item $currq {
switch -- $item \
... {
} exit {
# cleanup and leave programm
}
}
}
# ok vwait 3 is actually done
There are certainly other ways to do it
but if you have one single item that is event triggered ( gui here )
the remaining functionality should heed that and work to similar
priciples.
uwe
Thanks for your proposition Uwe.
Nevertheless, if it could be possible, I would like not use
immediately Expect.
Don't misunderstood, I really really appreciate want you proposed me,
and I thank you for the time you took to answer to my quest.
Why, I would not use Expect for the moment and find a solution thanks
to pure tcl language.
(Actual choice for the moment)
I use freewrap to "compile" my tcl code (which is relative large) and
I prefer not add (for the moment) another package, which perhaps could
add some other difficulties
Uwe, I will perhaps change my opinion in some days, but for now,
please let me the "hope" to find another solution
Again, I really really appreciate your help
Regards
Christian
PS: please apologizes
If you want to stay in pure tcl you would have to
emulate [exp_background] with [filevent].
(i.e. assembling an input stream from successive reads
and interpret the content )
uwe
I think that in all the cases, it will be a hard job.
* Pass to expect --> compile the source code, learn the language,
adapt it to my requirements, hope that it will work with freewrap,
hope that it will do what I want, and hope that I will find the
solution
* Stay in tcl, rearrange completely my code, and expect a solution
It's damage that my code that I have written cannot be easily re-
arranged
I hopped, that relative to my code, there were only a little
adaptation to full fill my requirements..
Nevertheless I think it is not the case, and I'm little disappointed
to trash all my work :-(
Christian
> adapt it to my requirements, hope that it will work with freewrap,
does freewrap work in the ActiveState installation? ( never used tcl2bin wrappers)
> hope that it will do what I want, and hope that I will find the
> solution
> * Stay in tcl, rearrange completely my code, and expect a solution
>
> It's damage that my code that I have written cannot be easily re-
> arranged
> I hopped, that relative to my code, there were only a little
> adaptation to full fill my requirements..
>
> Nevertheless I think it is not the case, and I'm little disappointed
> to trash all my work :-(
My "driver" for a Precisa ( http://www.precisa.ch/ ) Scale
is about 800lines/ 5kbyte tcl code.
uwe
I use tcl in the mingw environment: http://wiki.tcl.tk/14828 so I have
to compile also expect.
Then, there will be no problem to wrap it with freewrapper
An overview of my project: http://papsi.origo.ethz.ch/wiki/screenshots
IS-Papsi is written in tcl, and will only be used to configure the
dspic, and to move the stepper in case CNC controller is not used.
Christian
Uwe,
There is perhaps another solution to me to use freewrap and expect
The only thing that I need is a compiled version of expect:
Respectively following files.
/Tcl/lib/expect/expect.tap
/Tcl/lib/expect/expect.tcl
/Tcl/lib/expect/injector.dll
/Tcl/lib/expect/pkgIndex.tcl
/Tcl/lib/expect/expect543.dll
I tried to find these files, but I didn't find them
Have you perhaps a way how to recover these files
Thanks
Christian
to get expect:
I find that
Sorry Linux only and I never used tcl wrappers.
Lets see what others have access to.
uwe
ActiveTcl has a recent version. Though you have to install it with
teacup.
http://community.activestate.com/faq/where-is-expect
Ruediger
Hi Ruediger,
I found effectively this information on Activetcl, but I didn't
understand how to recover this package...
This "Teacup", seems not to be easy to understand !
Will have to read how it works :-(
This is the risk when you meet an issue. In my case I was almost near
to release my version, and now, I have to dive back..
You start on the issue, but you don't know where you will land :-)
CKL
I'm on the work for the moment, and there is proxy to cross.
Didn't work with teacup install Expect --http-proxy....
Will try this evening
Just for information:
Work with:
teacup install Expect
I now will try to understand what is behind this expect, and relative
to your information, try to resolve my issue.
When I will find the solution, will come back
Christian
Hi All,
Finally, I think I have found the solution to my Problem.
Nevertheless I use the rules of Dave, where unexpected tasks should
never been blocked.
Decreasing also the TriggerTime, it was relatively easy to adapt my
program without a lot of changes.
Relative to your proposition to resolve my issue with Expect, I
apologise, but although I didn't pass a lot of time to understand
Expect, I didn't understand the concept of Expect, nor how it could
help me with my issue.
A lot of people speaks about Expect, and use it.
Nevertheless I read also some articles, explaining that since some tcl
versions, the concept of Expect is today completely integrated in tcl,
and are no more required for telnet, ftp, ... tasks
In all the case there is something that I didn't integrated with
Expect
Now for all your help
Thanks
And below My program
set chid [open com1: r+]
fconfigure $chid -mode "9600,n,8,1" -blocking 0 -buffering none -
translation binary
fileevent $chid readable [list serial_receiver $chid]
set mode Waiting
set TriggerTime 50
set TimeOutDelay 2000
set ForcedWriteBuff() ""
set todoForcedWriteBuffCount 1
set nexttodoForcedWriteBuffCount 1
set FreeRunningReadCnt 1
set FreeRunningReadCntThres 100
set MessageRead ""
proc serial_receiver { chan } {
global mode
global MessageRead
global SerialMsg_Event
set data [read $chan]
append MessageRead $data
puts $MessageRead
if {$mode == "ForcedWrite"} {
if { [string range $MessageRead end-2 end] == "AKW"} {
set SerialMsg_Event AKW
}
} elseif {$mode == "FreeRunningRead"} {
if { [string range $MessageRead end-2 end] == "AKR"} {
set SerialMsg_Event AKR
}
}
}
proc FreeRunningRead { } {
global chid
global mode
global MessageRead
global TimeOutDelay
global SerialMsg_Event
set mode "FreeRunningRead"
puts "Start FreeRunningRead"
puts -nonewline $chid "RTR"
puts -nonewline $chid \x0D
puts -nonewline $chid \x0A
set MessageRead ""
after $TimeOutDelay { set SerialMsg_Event TimeOut }
vwait SerialMsg_Event
switch $SerialMsg_Event {
"TimeOut" {
puts " FreeRunningRead TimeOut"
}
"AKR" {
puts " FreeRunningRead = $MessageRead"
}
}
puts " Stop FreeRunningRead"
set mode "Waiting"
}
proc ForcedWrite { cmd } {
global chid
global mode
global MessageRead
global TimeOutDelay
global SerialMsg_Event
set mode "ForcedWrite"
puts "Start ForcedWrite"
puts -nonewline $chid "$cmd "
puts -nonewline $chid \x0D
puts -nonewline $chid \x0A
set MessageRead ""
after $TimeOutDelay { set SerialMsg_Event TimeOut }
vwait SerialMsg_Event
switch $SerialMsg_Event {
"TimeOut" {
puts " ForcedWrite TimeOut for cmd = $cmd"
}
"AKW" {
puts " ForcedWrite = $MessageRead for cmd = $cmd"
}
}
puts " Stop ForcedWrite"
set mode "Waiting"
}
wm protocol . WM_DELETE_WINDOW { exit }
button .b1 -text Quit -command { exit }
button .b2 -text Write -command {
global ForcedWriteBuff
global todoForcedWriteBuffCount
global nexttodoForcedWriteBuffCount
# All Job has been Done, we can reset the counter
if { $nexttodoForcedWriteBuffCount == $todoForcedWriteBuffCount }
{
set todoForcedWriteBuffCount 1
set nexttodoForcedWriteBuffCount 1
}
set ForcedWriteBuff($todoForcedWriteBuffCount) "UP"
puts " -ForcedWriteBuff($todoForcedWriteBuffCount) =
$ForcedWriteBuff($todoForcedWriteBuffCount)"
incr todoForcedWriteBuffCount
}
pack .b1
pack .b2
# Loop
proc repeat {delay script} {
if 1 $script
after $delay repeat $delay $script
}
# Run at priority the unexpected event
proc exec_expected_unexpected_tasks {} {
global ForcedWriteBuff
global todoForcedWriteBuffCount
global nexttodoForcedWriteBuffCount
global FreeRunningReadCnt
global FreeRunningReadCntThres
while {$nexttodoForcedWriteBuffCount < $todoForcedWriteBuffCount}
{
ForcedWrite "$ForcedWriteBuff($nexttodoForcedWriteBuffCount)
$nexttodoForcedWriteBuffCount"
incr nexttodoForcedWriteBuffCount
}
incr FreeRunningReadCnt
if {$FreeRunningReadCnt == $FreeRunningReadCntThres } {
FreeRunningRead
set FreeRunningReadCnt 1
}
}
# Run each TriggerTime
repeat $TriggerTime exec_expected_unexpected_tasks