jbr May 2, 2010
The Delta Tau PMAC (programmable multi axis controller) is a high end programmable motion controller for machine tools and robotics. I have been programming PMACs for about 10 years. Usually I access them from C, but recently I've had occasion to connect directly from Tcl.
Here is a simple library that I wrote up last week to make this possible. You'll need TCP and recv.
You'll also need to review the PMAC Ethernet protocol documentation, this library is a pretty direct mapping of the available commands. The only "fancy" usage here is pmac:command which provides a command reply pair and pmac:mapmem which reads a memory buffer from the pmac and immediately calls binary scan to map the data buffer into Tcl variables that need to be fully qualified names.
I think that this code provides a very concise example of interacting with a binary protocol in Tcl.
enjoy,
set Host2PMAC 0x40
set PMAC2Host 0xC0
set PMAC_SENDLINE 0xB0
set PMAC_GETLINE 0xB1
set PMAC_FLUSH 0xB3
set PMAC_GETMEM 0xB4
set PMAC_SETMEM 0xB5
set PMAC_READY 0xC2
set PMAC_GETBUFFER 0xC5
set PMAC_WRITEBUFFER 0xC6
proc pmac:connect { pmac } {
set sock [socket $pmac 1025]
fconfigure $sock -encoding binary
return $sock
}
proc pmac:flush { sock } {
pmac:packet $sock $::Host2PMAC $::PMAC_FLUSH
read $sock 1
}
proc pmac:wait { sock } {
set x 0
while { 1 } {
pmac:packet $sock $::PMAC2Host $::PMAC_READY 0 2
binary scan [read $sock 2] c x
if { $x } { break }
after 10
}
}
proc pmac:getbuf { sock } {
pmac:packet $sock $::PMAC2Host $::PMAC_GETBUFFER 0 1400
recv $sock
}
proc pmac:command { sock data } {
pmac:packet $sock $::Host2PMAC $::PMAC_SENDLINE 0 [string length $data] $data
binary scan [read $sock 1] c reply
pmac:wait $sock
string map { \r " " } [string range [pmac:getbuf $sock] 0 end-2]
}
proc pmac:wbuff { sock data here } {
pmac:packet $sock $::Host2PMAC $::PMAC_WRITEBUFFER 0 [string length $data] $data
binary scan [read $sock 4] scc line code error
if { $error == 0x80 } {
error "pmac:write : error $code at line [expr $here + $line]"
}
}
proc pmac:write { sock Data } {
set data {}
set Here 0
set here 0
foreach line [split $Data \n] {
if { [string length $data] + [string length $line] + 1 > 1000 } {
pmac:wbuff $sock $data $Here
incr Here $here
set data {}
set here 0
}
append data $line "\0"
}
pmac:wbuff $sock $data $Here
}
proc pmac:setmem { sock start length } {
pmac:packet $sock $::Host2PMAC $::PMAC_SETMEM $start $offset $data
}
proc pmac:packet { sock type requ { offset 0 } { size 0 } { data {} } } {
puts -nonewline $sock [binary format ccSSS $type $requ $offset 0 $size]
puts -nonewline $sock $data
flush $sock
}
proc pmac:getmem { sock start length } {
pmac:packet $sock $::PMAC2Host $::PMAC_GETMEM $start $length
recv $sock
}
proc pmac:mapmem { sock start length scan vars } {
set block [pmac:getmem $sock $start $length]
binary scan $block $scan {*}$vars]
}