Chapter 20, "IPC with send" discusses TclRobots in detail. TclRobots.pm allows you to write Robot Control Programs in Perl instead of Tcl.
$TclRobots::VERSION = '2.1';
package TclRobots;
# This module implements a thin API that interfaces Perl with tclrobots
# version 2, written by Tom Poindexter. This means that you can write
# RCPs (Robot Control Programs) in your favorite language - Perl - and
# do battle with all the existing Tcl RCPs.
#
# This module is rather wierd - you're never supposed to use it!
# Instead, it's used when tclrobots runs an instance of perl, at which
# time this module is loaded and begins execution on behalf of your
# RCP. It creates the main window of the required dimensions and at
# the proper location on the display, and adds all the widgets, text,
# and images
#
# When instructed by tclrobots, this module then loads your Perl RCP
# (via require, so be sure your code returns a TRUE value!), and the
# contest begins. From that point on, incoming tclrobot messages are
# dispatched to Perl emulation handlers, and Perl RCP commands are
# converted to Tcl syntax and sent to tclrobots - the communication is
# via Tk::send() and Tk::Receive().
#
# Stephen.O.Lidie@Lehigh.EDU, 1999/05/07.
# Stephen.O.Lidie@Lehigh.EDU, 2000/04/13, for Perl 5.6.0.
use Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/after alert cannon damage dputs drive dsp heat loc_x loc_y
scanner speed team_declare team_get team_send tick update/;
use File::Basename;
use Tk;
use Tk qw/after catch/;
use Tk::widgets qw/Dialog/;
use subs qw/_arrowshape__configure_widgets__customize_window_
_disable_rcp__insult_rcp__destroy_rcp__load_rcp_
_see_variable__set_variables__setup_window__start_rcp_/;
use vars qw/$_after_ $_alert_on_ $_debug $_dl_ $_fc_ $_fl_ $_fs_ $_mw_
$_ping_proc_ $_rcp_filename_ $_resume_ $_robot_ $_start_ $_step_
$_tclrobots_/;
use strict;
##############################################################################
#
# Note, we run tainted so that send() and receive() work. Grab command line
# arguments:
#
# perl5 -Tw -I. -MTclRobots /dev/null RCP.ptr_2462 \
# WidthxHeigh+X+Y rob2 tclrobots ./RCP.ptr
#
##############################################################################
return 1 if $ENV{TCLROBOTS_RCP_CHECK}; # if checking RCP syntax
$ENV{'HOME'} = '/tmp';
$_mw_ = MainWindow->new;
$_mw_->withdraw;
$ARGV[0] =~ /(.*)/; # robot's Tcl name
$_mw_->appname($1);
$_mw_->title($1);
$ARGV[1] =~ /(.*)/; # window geometry
$_mw_->geometry($1);
$ARGV[2] =~ /(.*)/; # robot's handle
$_robot_ = $1;
$ARGV[3] =~ /(.*)/; # tclrobot's name
$_tclrobots_ = $1;
$ARGV[4] =~ /(.*)/; # RCP filename
$_rcp_filename_ = $1;
$_mw_->deiconify;
MainLoop;
##############################################################################
#
# Robot Control Program commands available to your Perl controlware. For
# the most part, they simply invoke Tcl subroutines in tclrobots. We also
# handle single stepping in Debug mode.
#
##############################################################################
{
local $^W = 0;
eval 'sub after {$_mw_->after(@_)}';
}
sub alert {
my($code_ref) = @_;
$_ping_proc_ = $code_ref;
if (defined $code_ref) {
$_alert_on_ = 1;
} else {
$_alert_on_ = 0;
}
}
sub cannon {
my($deg, $range) = @_;
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_cannon $_robot_ $deg $range");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub damage {
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_damage $_robot_");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub dputs {
my(@args) = @_;
$_resume_ = 0;
Tk::catch {
$_dl_->insert('end', join(' ', @args));
$_dl_->yview('end'); $_mw_->update;
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
$_mw_->update;
}
sub drive {
my($deg, $speed) = @_;
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_drive $_robot_ $deg $speed");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub dsp {
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_dsp $_robot_");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
my(@dsp) = split(' ', $val);
return @dsp;
}
sub heat {
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_heat $_robot_");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
my(@heat) = split(' ', $val);
return @heat;
}
sub loc_x {
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_loc_x $_robot_");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub loc_y {
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_loc_y $_robot_");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub scanner {
my($deg, $res) = @_;
$_mw_->after(100);
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_scanner $_robot_ $deg $res");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub speed {
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_speed $_robot_");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub team_declare {
my($tname) = @_;
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_team_declare $_robot_ $tname");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub team_get {
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_team_get $_robot_");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
my @teams;
foreach my $team (&SplitString($val)) {
my($dsp, $data) = split ' ', $team;
push @teams, [$dsp, $data];
}
return @teams;
}
sub team_send {
my($args) = @_;
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_team_send $_robot_ \"$args\"");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub tick {
$_mw_->after(100);
$_mw_->update;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_tick $_robot_");
};
$_mw_->waitVariable(\$_resume_) if $_debug and $_step_;
&_ping_check_;
$_mw_->update;
return $val;
}
sub update {
$_mw_->update;
}
##############################################################################
#
# Tcl -> Perl handlers.
#
##############################################################################
sub Tk::Receive {
# Accept Tcl strings from tclrobots and invoke
# Perl/Tk emulation code.
my($mw) = shift; # main window
$_ = shift; # Tcl command
return 2 if /expr 1\+1/;
return if /(Knuth|^rename)/m;
CASE:
{
/setup window/m and do {_setup_window_; last CASE};
/create|configure/ and do {_customize_window_ $_; last CASE};
/set _start_ 0/ and do {_load_rcp_; last CASE};
/set _start_ 1/ and do {_start_rcp_; last CASE};
/^proc after/ and do {_disable_rcp_; last CASE};
/\.d\.l insert/ and do {_insult_rcp_ $_; last CASE};
/^_a_\d+ 0 _e_\d+/ and do {_destroy_rcp_; last CASE};
/^set/ and do {_set_variables_ $_; last CASE};
/^format/ and do {return _see_variable_ $_;};
print STDERR "UNHANDLED cmd=$_!\n";
} # casend
} # end Tk::Receive
sub _arrowshape_ {
my($cmd) = @_;
my($cs, $ar) = $cmd =~ /\.f\.. create (.*) (-arrowshape.*)/;
my(@cs) = split(' ', $cs);
$ar =~ /"(\d+) (\d+) (\d+)/;
my $as = [$1, $2, $3];
$_fc_->create(@cs, -arrowshape => $as);
}
sub _configure_widgets_ {
my($cmd) = @_;
my($w) = $cmd =~ /\.f\.l/ ? $_fl_ : $_fs_;
my($cs) = $cmd =~ /configure (.*)/;
$cs =~ s/(;.*)//;
my(@cs) = split(' ', $cs);
$w->configure(@cs);
$w->update;
}
sub _customize_window_ {
$_ = $_[0];
/configure/ and do {_configure_widgets_ $_; return};
/create/ and do {_arrowshape_ $_; return};
}
sub _destroy_rcp_ {
$_mw_->after(1 => $_mw_->destroy);
}
sub _disable_rcp_ {
{
local $^W = 0;
eval 'sub after {}';
eval 'sub _ping_check_ {
while (1) {
$_mw_->update;
$_mw_->after(100);
}
}';
}
}
sub _insult_rcp_ {
my($cmd) = @_;
my($text) = $cmd =~ /insert end(.*)?;\.d\.l/;
$_mw_->after(1 => sub {
$text =~ s/\\//g;
$_dl_->insert('end', $text);
$_dl_->yview('end');
$_mw_->update;
$_mw_->waitWindow;
});
}
sub _load_rcp_ {
$_start_ = 0;
$_mw_->after(100 => sub {
$_mw_->waitVariable(\$_start_);
eval "require \"$_rcp_filename_\"";
if ($@) {
my $bn = basename $_rcp_filename_;
my $d = $_mw_->Dialog(
-title => $_mw_->appname,
-text => "$@\nYour RCP failed to compile. To perform a
syntax " .
"check, do:\n\nTCLROBOTS_RCP_CHECK=1 perl -MTclRobots $bn",
-font => 'fixed');
$d->Subwidget('message')->configure(-wraplength => '8i');
$d->Show;
$d->destroy;
}
});
}
sub _see_variable_ {
my($expression) = @_; # including leading $
$expression = substr $expression, 8;
{
no strict;
# Perl bug: I want eval "$expression";
# So for now, assume a scalar var name.
$$expression;
}
}
sub _setup_window_ {
# Setup the RCP's debug and damage window.
my $f = $_mw_->Frame;
$f->pack(qw/-side top -fill x -ipady 5/);
$_fc_ = $f->Canvas(qw/-width 20 -height 16/);
$_fl_ = $f->Label(qw/-relief sunken -width 30 -text/ =>
"(loading robot code..)");
$_fs_ = $f->Label(qw/-relief sunken -width 5 -text/ => "0%");
$_fc_->pack(qw/-side left/);
$_fs_->pack(qw/-side right/);
$_fl_->pack(qw/-side left -expand 1 -fill both/);
$_dl_ = $_mw_->Scrolled('Listbox', qw/-relief sunken -scrollbars se/);
$_dl_->pack(qw/-side left -expand 1 -fill both/);
$_mw_->minsize(100, 70);
$_mw_->update;
$_resume_ = 0;
$_step_ = 0;
}
sub _set_variables_ {
my($cmd) = @_;
foreach (split /;/, $cmd) {
my($set, $var, $val) = /(set)\s+(\S+)\s+(.*)/;
{no strict; eval {$$var = $val}}
}
}
sub _start_rcp_ {
$_mw_->after(100 => sub {$_start_ = 1});
}
##############################################################################
#
# Auxiliary routines.
#
##############################################################################
$_ping_proc_ = '';
$_alert_on_ = 0;
sub _ping_check_ {
return unless $_alert_on_;
my $val = Tk::catch {
$_mw_->send($_tclrobots_, "do_ping $_robot_");
};
Tk::catch {&$_ping_proc_($val)} if $val != 0;
}
sub SplitString {
# Swiped from Tk800.015 distribution - a weak attempt to
# turn a Tcl LOL into a Perl LOL.
local $_ = shift;
my (@arr, $tmp);
while (/\{([^{}]*)\}|((?:[^\s\\]|\\.)+)/gs) {
if (defined $1) {
push @arr, $1;
} else {
$tmp = $2 ;
$tmp =~ s/\\([\s\\])/$1/g;
push @arr, $tmp;
}
}
return @arr;
} # end SplitString
1;
Copyright © 2002 O'Reilly & Associates. All rights reserved.