;; -*- mode: Lisp; Syntax: Common-Lisp; Package: model; -*-
;;_________________________________________________________
;;
;;
;; B o i l e r m a k e r
;;
;; Copyright � 2000 by Kenny Tilton
;;
(in-package :model)
#| Intro to Semaphors, Chapter 1
It occurred to me that the rah-rah stuff will be more comprehensible
after one has actually seen what I am talking about, so here are two
simple examples, rah-rah to follow.
|#
(defmodel boiler ()
((status :semaphor t :initarg :status :accessor status :initform
nil);; vanilla semaphor
(temp :semaphor t :initarg :temp :accessor temp :initform nil)
(vent :semaphor t :initarg :vent :accessor vent :initform nil)
))
(defun example-1 ()
(md-reset) ;; resets debugging/testing specials
(let ((b (make-instance 'boiler
:temp (smv 20)
:status (sm? (if (< (^temp self) 100) ;; #1 (footnote)
:on
:off))
:vent (sm? (ecase (^status) ;; #2
(:on :open)
(:off :closed))))))
(trc "boiler> temp:" (temp b) :status= (status b) :vent= (vent b))
;; #3
(setf (temp b) 100) ;; recalculation of 'status then 'vent all
occurs during this 'setf
(trc "boiler> temp:" (temp b) :status= (status b) :vent= (vent b))
))
#|
Output:
0> boiler> temp: 20 :STATUS= :ON
0> boiler> temp: 100 :STATUS= :OFF
Note 1: The 'sm? macro is used to define semaphoric formulas. ^temp is
a macro generated by the 'defmodel of boiler. ^macros work in concert
with
'sm? almost transparently to establish a dependency (almost--just don't
forget
the ^, which for some reason I almost never do).
Note 2: The ^macros single parameter is optional and defaults to 'self
for convenience.
Note 3: 'trc prints to the debugger window. Notice an almost incidental
charm of
Semaphors, viz, initialization of a slot based on calculations against
other slots. Sometimes these days I make a slot semaphoric simply to get
that
capability.
Now let's see how echo functions can be used...
|#
(def-sm-echo (status) () ;; parameters default to (self newValue
oldValue)
(trc "boiler" self :oldstatus= oldValue :newstatus= newValue)
;
; also call boiler API to actually turn it on or off
;
)
(def-sm-echo (vent) ()
(trc "boiler vent changing from" oldValue :to newValue)
;
; also call boiler API to actually open or close it
;
)
;
; And let's also demonstrate inter-object dependency by
; separating out the thermometer
;
(defmodel boiler2 ()
((status :semaphor t :initarg :status :accessor status :initform
nil);; vanilla semaphor
(vent :semaphor t :initarg :vent :accessor vent :initform nil)
(thermometer :initarg :thermometer :accessor thermometer :initform
nil)
))
;-------------------------
(defmodel thermometer ()
((temp :semaphor t :initarg :temp :accessor temp :initform nil)
))
(def-sm-echo (temp) ((self Thermometer) newTemp oldTemp)
(trc "thermometer temp changing from" oldTemp :to newTemp))
;--------------------------
(defun example-2 ()
(md-reset) ;; resets debugging/testing specials
(let ((b (make-instance 'Boiler2
:status (sm? (if (< (^temp (Thermometer self)) 100)
:on :off))
:vent (sm? (ecase (^status)
(:on :open)
(:off :closed)))
:thermometer (make-instance 'Thermometer
:temp (smv 20))))
)
(trc "------to 100---------")
(setf (temp (Thermometer b)) 100)
(trc "------to 101---------")
(setf (temp (Thermometer b)) 101)
(trc "------to 101---------")
(setf (temp (Thermometer b)) 101)
))
#|
Output:
0> thermometer temp changing from NIL :TO 20
0> boiler <BOILER2 BOILER2> :OLDSTATUS= NIL :NEWSTATUS= :ON
0> boiler vent changing from NIL :TO :OPEN
0> ------to 100---------
0> thermometer temp changing from 20 :TO 100
0> boiler <BOILER2 BOILER2> :OLDSTATUS= :ON :NEWSTATUS= :OFF
0> boiler vent changing from :OPEN :TO :CLOSED
0> ------to 101---------
0> thermometer temp changing from 100 :TO 101
0> ------to 101---------
|#