agent and scotty 2.0.2 experiences and sample code

Peter J M Polkinghorne (Peter.Polkinghorne@gec-hrc.co.uk)
Tue, 14 Nov 1995 15:33:43 +0000 (GMT)

These comments are the result of using the features new to scotty 2.0.2 for
implementing agent - in particular tables with the RowStatus convention.
I include some sample code so:
a) others may avoid some pain and grief
b) others can point out the bugs :-)

Firstly comments:

a) the CHECK bindings should take place before the result values are put the
response PDU - this allows for the notInService vs notReady distinction.

b) automation for create/rollback would be nice - that is on rollback unbind
the instance so created - this would eliminate some of the code in the example.

c) for a lot of table processing you need to know what the old value was, this
is particularly true for RowStatus. While scotty automaticly saves this value
for you, it is not accessible.

d) alternatively may be table processing is just too tough to automate?

e) because when evaluating bindings the error value from the script is used,
debugging the bind script is hard - may be I am missing a TCL trick to debug
these scripts?

f) I came across a bug in getnext processing: when a table entry is created
with createAndWait, it may well have "holes" in it. So when I had a table

entry thus:
scalar.0
table
entry
entry.index not-accessible
entry.val read-create - not present
entry.result read-only - default there

getnexts on:
scalar.0 failed - noSuchName
table failed - noSuchName
entry failed - noSuchName
entry.index OK - gave entry.result
entry.val OK - gave entry.result

However if there was another whole row there, then the getnexts all behaved
correctly. A cursory examination of the tree traversal code gave me no obvious
reason for this.

Here is the skeleton code for a table (called table!):

This table has a simple integer index, a value (Val) to be set and a readonly
result (Res) derived from the Val when row becomes active. The table() array
is used to record status of row.

# procedure to create a default row
proc table_Create { sess idx } {
global table

# set bindings - with suitable defaults
$sess instance tableRes.$idx tableRes($idx)

# set associated variable
set table($idx) creation
}

# procedure to destroy a row - note careful unsetting since not all entries
# may be present
proc table_Destroy { idx } {

after idle "catch {unset tableVal($idx)}"
after idle "catch {unset tableRes($idx)}"
after idle "catch {unset tableRowStatus($idx)}"
after idle "catch {unset table($idx)}"
}

$sagent bind tableRowStatus create {
set idx "%i"
switch "%v" {
createAndGo {
table_Create "%S" $idx
%S instance tableRowStatus.$idx tableRowStatus($idx) active
break
}
createAndWait {
table_Create "%S" $idx
# assume will have favourable outcome
%S instance tableRowStatus.$idx tableRowStatus($idx) notInService
break
}
destroy {
# basicly ignore -- but do not set a value or instance binding either.
break
}
default {
error inconsistentValue
}
}
}

$sagent bind tableVal create {
set idx "%i"

# check value
if { [OkVal "%v"] } {
# allow default processing - Rollback uses this simple flag to see if unbind
set tableVal_C($idx) created

} else {
error inconsistentValue
}
}

$sagent bind tableVal set {

if { [OkVal "%v"] } {
# let usual processing set value

} else {
error inconsistentValue
}
}

$sagent bind tableRowStatus set {
set idx "%i"

switch "%v" {
destroy -
notInService -
active {
# probably OK - but record old value - since we need this
if { $table($idx) == "" } {
set table($idx) $tableRowStatus($idx)

} elseif { $table($idx) == "creation" } {
# should we complain about setting RowStatus more than once?

}
}
default {
error inconsistentValue
}
}
}

$sagent bind tableVal check {
set idx "%i"

# check for the RowStatus existence - not compulsory to do this check.
if { [catch {set tableRowStatus($idx)} ] } {
error inconsistentValue
}
}

$sagent bind tableRowStatus check {
set idx "%i"

# whether Val column there - and potentially other read-create columns
set noVal [catch {set tableVal($idx)} ]

switch "%v" {
active -
notInService -
createAndGo {
if { $noVal } {
error inconsistentValue
}
}
createAndWait {
if { $noVal } {
# note this will not be returned to user alas
set tableRowStatus($idx) notReady
}
}
destroy {
# do not care
}
}
}

$sagent bind tableVal commit {
set idx "%i"

# remove create flag
catch {unset tableVal_C($idx)}

# if RowStatus notReady - change to notInService
if { $tableRowStatus($idx) == "notReady" } {
set tableRowStatus($idx) notInService
}
}

$sagent bind tableVal rollback {
set idx "%i"

# remove create flag and possibly value - let scotty handle value changes
if { ![catch {unset tableVal_C($idx)}] } {
after idle "catch {unset tableVal($idx)}"
}
}

$sagent bind tableRowStatus commit {
set idx "%i"

if { $tableRowStatus($idx) == "destroy" } {
table_Destroy $idx

} elseif { $tableRowStatus($idx) == "active" && \
$tableRowStatus($idx) != $table($idx) } {

# if become active carry out what ever required by row semantics
}

# clear saved status
set table($idx) ""

}

$sagent bind tableRowStatus rollback {
set idx "%i"

if { $table($idx) == "creation" } {

# Roll back for creation implies deleting all associated table entry
# this may be too drastic - but lets be simple
table_Destroy $idx

} else {
# clear saved status - let scotty handle value rollbacks
set table($idx) ""

}
}