agent and scotty 2.0.2 experiences and sample code

Peter J M Polkinghorne Peter.Polkinghorne at gec-hrc.co.uk
Tue Nov 14 16:33:43 CET 1995



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) ""

	}
    }




More information about the Tkined mailing list