Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Serial Port and asynchronous event help need

52 views
Skip to first unread message

CKL

unread,
Dec 8, 2009, 6:47:28 PM12/8/09
to
Hi tcl gurus

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
}
}

dave.j...@googlemail.com

unread,
Dec 9, 2009, 4:46:45 AM12/9/09
to
On Dec 8, 11:47 pm, CKL <christian.klugesh...@gmail.com> wrote:
> Hi tcl gurus
>
> There is now 2 weeks that I try to find a solution to my problem
>
> 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.
>

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

CKL

unread,
Dec 9, 2009, 1:00:26 PM12/9/09
to
On 9 déc, 10:46, "dave.joub...@googlemail.com"

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


dave.j...@googlemail.com

unread,
Dec 9, 2009, 1:41:05 PM12/9/09
to
Hi 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} {

Uwe Klein

unread,
Dec 9, 2009, 1:44:26 PM12/9/09
to
CKL wrote:
> On 9 d�c, 10:46, "dave.joub...@googlemail.com"

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

dave.j...@googlemail.com

unread,
Dec 9, 2009, 1:58:41 PM12/9/09
to
Sorry, too used to vi, and hit tab and cr....

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

dave.j...@googlemail.com

unread,
Dec 9, 2009, 2:07:26 PM12/9/09
to
On Dec 9, 6:44 pm, Uwe Klein <uwe_klein_habertw...@t-online.de> wrote:
>
> Have you looked into expect?
>         look into the exp_background command.
>
> uwe

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

CKL

unread,
Dec 9, 2009, 3:38:20 PM12/9/09
to
On 9 déc, 20:07, "dave.joub...@googlemail.com"

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


Uwe Klein

unread,
Dec 9, 2009, 5:07:40 PM12/9/09
to
CKL wrote:
> 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

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

CKL

unread,
Dec 9, 2009, 6:21:40 PM12/9/09
to

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

Uwe Klein

unread,
Dec 10, 2009, 4:40:14 AM12/10/09
to
CKL wrote:
> 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

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

CKL

unread,
Dec 10, 2009, 11:40:11 AM12/10/09
to

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

Uwe Klein

unread,
Dec 10, 2009, 12:01:05 PM12/10/09
to
CKL wrote:

> On 10 d�c, 10:40, Uwe Klein <uwe_klein_habertw...@t-online.de> wrote:
>
>>CKL wrote:
>>
>>>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
>>
>>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,
expect is a tcl extension.

> 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

CKL

unread,
Dec 10, 2009, 3:40:24 PM12/10/09
to
On 10 déc, 18:01, Uwe Klein <uwe_klein_habertw...@t-online.de> wrote:
> CKL wrote:
> 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

CKL

unread,
Dec 11, 2009, 4:49:44 AM12/11/09
to
> I use tcl in the mingw environment:http://wiki.tcl.tk/14828so 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

Uwe Klein

unread,
Dec 11, 2009, 5:14:45 AM12/11/09
to
CKL wrote:
> 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

Sorry Linux only and I never used tcl wrappers.
Lets see what others have access to.

uwe

hae

unread,
Dec 11, 2009, 7:38:38 AM12/11/09
to
> > I use tcl in the mingw environment:http://wiki.tcl.tk/14828soI 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

ActiveTcl has a recent version. Though you have to install it with
teacup.

http://community.activestate.com/faq/where-is-expect

Ruediger

CKL

unread,
Dec 11, 2009, 10:22:58 AM12/11/09
to

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

CKL

unread,
Dec 11, 2009, 11:13:27 AM12/11/09
to

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

CKL

unread,
Dec 11, 2009, 4:26:28 PM12/11/09
to
> Will try this evening- Masquer le texte des messages précédents -
>
> - Afficher le texte des messages précédents -

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

CKL

unread,
Dec 12, 2009, 6:40:18 PM12/12/09
to

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

0 new messages