// Abstract ------------------------------------------------------------------------------------------------------------------------------

This sample shows how a simple devices monitoring system could be written in Fizz. It is inspired by the monitoring problem discussed in "Expert Systems: Principles and Programming" by Joseph Giarratano and Gary Riley (page 578):

5 devices are monitored by multiple temperature sensors. Each of the sensors have a different 'Red Line' range and 'Guard Line' range in which the sensor's reading must stay for the device to be kept in operation. When the temperature exceed the 'Red Line' range, the device must be halted.

The sample relies on the user to trigger a slow increase or decrease of the temperature measured by any of the sensors using specific keys on the keyboard:

1,2,3,4,5 to select a given sensor
= or + (on the keypad) to get the temperature of the selected sensor to start increasing
- to get the temperature of the selected sensor to start decreasing
0 or * (on the keypad) to stop any change of temperature for the selected sensor
t to print the current temperature trend for the selected sensor
r to print the current temperature reading for the selected sensor

In the included example, we set the sensor #0 (attached to device #0) to increasing temperatures and the sensor #2 (attached to device #1) to decreasing temperatures and watch them go out of working state and then back in as we invert the trend for both sensors.

// Examples ------------------------------------------------------------------------------------------------------------------------------

?- /spy(append,sensors,device)
spy : observing sensors
spy : observing device
log : set temp for sensor 0 as increasing
spy : S sensors(0, 101.683965) := 1.00 (15.000000)
log : sensor 2 selected
spy : S sensors(0, 101.576752) := 1.00 (15.000000)
spy : S sensors(0, 100.967118) := 1.00 (15.000000)
log : set temp for sensor 2 as stable
spy : S sensors(0, 101.308985) := 1.00 (15.000000)
spy : S sensors(0, 102.084105) := 1.00 (15.000000)
spy : S sensors(0, 104.742469) := 1.00 (15.000000)
spy : S sensors(0, 106.598127) := 1.00 (15.000000)
spy : S sensors(0, 109.148667) := 1.00 (15.000000)
spy : S sensors(0, 112.191178) := 1.00 (15.000000)
spy : S sensors(0, 114.372971) := 1.00 (15.000000)
log : sensor 0 selected
spy : S sensors(0, 115.681879) := 1.00 (15.000000)
log : sensor 0 temp = 115.681879
spy : S sensors(0, 116.144503) := 1.00 (15.000000)
spy : S sensors(0, 116.797341) := 1.00 (15.000000)
spy : S sensors(0, 117.044270) := 1.00 (15.000000)
spy : S sensors(0, 119.253259) := 1.00 (15.000000)
spy : S sensors(0, 118.034160) := 1.00 (15.000000)
spy : S sensors(0, 119.317154) := 1.00 (15.000000)
spy : S sensors(0, 120.669959) := 1.00 (15.000000)
spy : S device(0, warn) := 1.00 (15.000000)
spy : S sensors(0, 121.018383) := 1.00 (15.000000)
spy : S sensors(0, 121.877841) := 1.00 (15.000000)
spy : S sensors(0, 123.226638) := 1.00 (15.000000)
spy : S sensors(0, 124.075093) := 1.00 (15.000000)
spy : S sensors(0, 125.312428) := 1.00 (15.000000)
spy : S sensors(0, 125.987162) := 1.00 (15.000000)
spy : S sensors(0, 127.267860) := 1.00 (15.000000)
spy : S sensors(0, 129.789043) := 1.00 (15.000000)
log : sensor 2 selected
spy : S sensors(0, 131.107867) := 1.00 (15.000000)
spy : S device(0, halt) := 1.00 (15.000000)
log : you can stop the trend on the sensor(s)
spy : S sensors(0, 133.214001) := 1.00 (15.000000)
log : set temp for sensor 2 as decreasing
spy : S sensors(0, 135.027244) := 1.00 (15.000000)
spy : S sensors(2, 99.234608) := 1.00 (15.000000)
spy : S sensors(0, 136.436559) := 1.00 (15.000000)
spy : S sensors(2, 99.228892) := 1.00 (15.000000)
spy : S sensors(0, 135.505916) := 1.00 (15.000000)
spy : S sensors(2, 98.995847) := 1.00 (15.000000)
log : sensor 2 temp = 98.995847
spy : S sensors(0, 136.399544) := 1.00 (15.000000)
spy : S sensors(2, 97.911749) := 1.00 (15.000000)
...
spy : S sensors(0, 139.408460) := 1.00 (15.000000)
spy : S sensors(2, 69.827354) := 1.00 (15.000000)
spy : S device(1, warn) := 1.00 (15.000000)
spy : S sensors(0, 139.867773) := 1.00 (15.000000)
spy : S sensors(2, 68.121973) := 1.00 (15.000000)
...
spy : S sensors(0, 139.282944) := 1.00 (15.000000)
spy : S sensors(2, 59.997318) := 1.00 (15.000000)
spy : S device(1, halt) := 1.00 (15.000000)
log : you can stop the trend on the sensor(s)
spy : S sensors(0, 138.947859) := 1.00 (15.000000)
spy : S sensors(2, 59.620651) := 1.00 (15.000000)
spy : S sensors(0, 138.158339) := 1.00 (15.000000)
spy : S sensors(2, 58.211917) := 1.00 (15.000000)
spy : S sensors(0, 137.390321) := 1.00 (15.000000)
spy : S sensors(2, 56.604156) := 1.00 (15.000000)
spy : S sensors(0, 136.252418) := 1.00 (15.000000)
spy : S sensors(2, 55.810378) := 1.00 (15.000000)
spy : S sensors(0, 138.436355) := 1.00 (15.000000)
spy : S sensors(2, 55.129705) := 1.00 (15.000000)
log : sensor 0 selected
spy : S sensors(0, 140) := 1.00 (15.000000)
spy : S sensors(2, 55.157148) := 1.00 (15.000000)
spy : S sensors(0, 140) := 1.00 (15.000000)
spy : S sensors(2, 54.185301) := 1.00 (15.000000)
spy : S sensors(0, 139.433864) := 1.00 (15.000000)
spy : S sensors(2, 53.325331) := 1.00 (15.000000)
log : set temp for sensor 0 as decreasing
spy : S sensors(0, 140) := 1.00 (15.000000)
spy : S sensors(2, 52.710033) := 1.00 (15.000000)
spy : S sensors(0, 138.757596) := 1.00 (15.000000)
spy : S sensors(2, 53.300451) := 1.00 (15.000000)
log : sensor 1 selected
spy : S sensors(0, 137.109581) := 1.00 (15.000000)
spy : S sensors(2, 51.592981) := 1.00 (15.000000)
log : sensor 2 selected
spy : S sensors(0, 135.982156) := 1.00 (15.000000)
spy : S sensors(2, 50) := 1.00 (15.000000)
log : set temp for sensor 2 as increasing
spy : S sensors(0, 135.879557) := 1.00 (15.000000)
spy : S sensors(2, 50.108183) := 1.00 (15.000000)
...
spy : S sensors(0, 127.966440) := 1.00 (15.000000)
spy : S sensors(2, 55.312917) := 1.00 (15.000000)
spy : S device(0, warn) := 1.00 (15.000000)
spy : S sensors(0, 126.445741) := 1.00 (15.000000)
spy : S sensors(2, 57.264923) := 1.00 (15.000000)
...
spy : S sensors(0, 120.240757) := 1.00 (15.000000)
spy : S sensors(2, 61.589817) := 1.00 (15.000000)
spy : S device(1, warn) := 1.00 (15.000000)
spy : S sensors(0, 118.443330) := 1.00 (15.000000)
spy : S sensors(2, 62.733532) := 1.00 (15.000000)
spy : S device(0, okay) := 1.00 (15.000000)
spy : S sensors(0, 116.731061) := 1.00 (15.000000)
spy : S sensors(2, 62.904042) := 1.00 (15.000000)
spy : S sensors(0, 116.126724) := 1.00 (15.000000)
spy : S sensors(2, 63.294547) := 1.00 (15.000000)
spy : S sensors(0, 113.932571) := 1.00 (15.000000)
spy : S sensors(2, 62.398282) := 1.00 (15.000000)
spy : S sensors(0, 113.319822) := 1.00 (15.000000)
spy : S sensors(2, 62.717602) := 1.00 (15.000000)
log : sensor 0 selected
spy : S sensors(0, 113.375384) := 1.00 (15.000000)
spy : S sensors(2, 63.854610) := 1.00 (15.000000)
spy : S sensors(0, 112.923171) := 1.00 (15.000000)
spy : S sensors(2, 64.299641) := 1.00 (15.000000)
log : set temp for sensor 0 as stable
spy : S sensors(2, 63.444886) := 1.00 (15.000000)
spy : S sensors(2, 64.319511) := 1.00 (15.000000)
spy : S sensors(2, 65.880719) := 1.00 (15.000000)
log : sensor 2 selected
spy : S sensors(2, 68.037126) := 1.00 (15.000000)
spy : S sensors(2, 67.985082) := 1.00 (15.000000)
spy : S sensors(2, 67.805207) := 1.00 (15.000000)
spy : S sensors(2, 69.026185) := 1.00 (15.000000)
spy : S sensors(2, 69.770006) := 1.00 (15.000000)
spy : S sensors(2, 71.996552) := 1.00 (15.000000)
spy : S device(1, okay) := 1.00 (15.000000)
spy : S sensors(2, 71.989145) := 1.00 (15.000000)
spy : S sensors(2, 72.840229) := 1.00 (15.000000)
log : set temp for sensor 2 as stable

 
// Code ----------------------------------------------------------------------------------------------------------------------------------

cycle {class = FZZCTicker, mod = 4} { // used to trigger sensor reporting

}

trend { // compute a new trend value based on some random variation of the previous reading plus the trend

    (:v,:s,:w) :- add(:v,:s,:v2), rnd.rsnd(1,:w,:v2,:s);

}

trend.step { // define the trend step for each sensors

    (0,1.0);
    (1,0.4);
    (2,0.8);
    (3,0.6);
    (4,0.3);
    (5,0.1);


}

range { // specifies the temperature range for each sensors

    (0,<40|140>);
    (1,<15|200>);
    (2,<50|140>);
    (3,<50|140>);
    (4,<50|140>);
    (5,<100|140>);

}

line { // specifies the line for each sensors

    //(sensor,redline,guardline)

    (0,<60|130>,<70|120>);
    (1,<20|180>,<40|160>);
    (2,<60|130>,<70|120>);
    (3,<60|130>,<70|120>);
    (4,<65|125>,<70|120>);
    (5,<110|130>,<115|125>);

}

line.check { // match a sensor reading to the 'guardline' & 'readline' lines

    (:v,:rl,:gl,okay) :-  rng.inc(:rl,:v), rng.inc(:gl,:v);
    (:v,:rl,:gl,warn) :- !rng.inc(:gl,:v), rng.inc(:rl,:v);
    (:v,:rl,:gl,halt) :- !rng.inc(:rl,:v);

}

sensors { // keeps track of the sensors readings and handle the console inputs

    readings = [100,100,100,100,100,100],   // last temperature reading for all the 5 sensors
    trending = [0,0,0,0,0,0],               // trending for each of the 5 sensors
    selected = 0                            // currently selected sensor

} {

    // select a sensor
    () :-   @console.keypress(:k?[<49|54>]), hush, sub(:k,49,:s), !peek(selected,:s), poke(selected,:s),
            &console.puts("log : sensor ",:s," selected");

    // stable temp
    () :-   @console.keypress(_?[lst.member([48,42])]), hush, peek(selected,:s), peek(trending,:l), lst.swap(:l,:s,0,:l2),
            poke(trending,:l2), console.puts("log : set temp for sensor ",:s," as stable");
    // lower temp
    () :-   @console.keypress(45), hush, peek(selected,:s), peek(trending,:l), #trend.step(:s,:t), mul(:t,-1,:t2),
            lst.swap(:l,:s,:t2,:l2), poke(trending,:l2), console.puts("log : set temp for sensor ",:s," as decreasing");
    // higher temp
    () :-   @console.keypress(_?[lst.member([61,43])]), hush, peek(selected,:s), peek(trending,:l), #trend.step(:s,:t),
            lst.swap(:l,:s,:t,:l2), poke(trending,:l2), console.puts("log : set temp for sensor ",:s," as increasing");

    // debug print the reading for the selected sensor
    () :-   @console.keypress(114), hush, peek(selected,:s), peek(readings,:l), lst.item(:l,:s,:v),
            console.puts("log : sensor ",:s," temp = ",:v);
    // debug print the trend for the selected sensor
    () :-   @console.keypress(116), hush, peek(selected,:s), peek(trending,:l), lst.item(:l,:s,:v),
            console.puts("log : sensor ",:s," trend = ",:v);

    // this prototype get going cyclically and compute a new reading for each of the sensors and fire-up a statement for each
    // we need to peek a second time at the 'reading' property as its value may get modified by a concurrent inference during
    // the call to #trend
    () :-   @cycle(:c,:t), hush, peek(readings,:lr), peek(trending,:lt), lst.item(:lr,:s,:v), lst.item(:lt,:s,:step?[neq(0)]),
            #trend(:v,:step,:w), #range(:s,:rng), rng.clamp(:rng,:w,:w2), peek(readings,:lr2), lst.swap(:lr2,:s,:w2,:lr3),
            poke(readings,:lr3), declare(sensors(:s,:w2));

    // when a device is halted, we just put out an hint for the user
    () :-   @device(:i,halt), hush, console.puts("log : you can stop the trend on the sensor(s)");

    // handle a reading query
    (:s?[<0|5>],:r) :- peek(readings,:l), lst.item(:l,:s,:r);

}

device { // represents a single sensor

    id      = 0,        // id of the device
    sensors = [0,1],    // list of sensors connected to that device
    state   = okay      // current state (okay, warn, halt)

} {

    // when a sensor reading is available, we check if the reading is within the accepted range. If not we adjust the state
    ()      :-  @sensors(:s,:v), hush, peek(sensors,:l), lst.item(:l,_,:s), #line(:s,:rl,:gl), #line.check(:v,:rl,:gl,:o),
                peek(state,:state), neq(:o,:state), poke(state,:o), peek(id,:i), declare(device(:i,:o));

    // handle state query
    (:i,:s) :- peek(id,:id?[eq(:i)]), peek(state,:s);

}

device { // represents a single sensor

    id      = 1,    // id of the device
    sensors = [2],  // list of sensors connected to that device
    state   = okay  // current state (okay, warn, halt)

} {

    // when a sensor reading is available, we check if the reading is within the accepted range. If not we adjust the state
    ()      :-  @sensors(:s,:v), hush, peek(sensors,:l), lst.item(:l,_,:s), #line(:s,:rl,:gl), #line.check(:v,:rl,:gl,:o),
                peek(state,:state), neq(:o,:state), poke(state,:o), peek(id,:i), declare(device(:i,:o));

    // handle state query
    (:i,:s) :- peek(id,:id?[eq(:i)]), peek(state,:s);

}

device { // represents a single sensor

    id      = 2,    // id of the device
    sensors = [3],  // list of sensors connected to that device
    state   = okay  // current state (okay, warn, halt)

} {

    // when a sensor reading is available, we check if the reading is within the accepted range. If not we adjust the state
    ()      :-  @sensors(:s,:v), hush, peek(sensors,:l), lst.item(:l,_,:s), #line(:s,:rl,:gl), #line.check(:v,:rl,:gl,:o),
                peek(state,:state), neq(:o,:state), poke(state,:o), peek(id,:i), declare(device(:i,:o));

    // handle state query
    (:i,:s) :- peek(id,:id?[eq(:i)]), peek(state,:s);

}

device { // represents a single sensor

    id      = 3,        // id of the device
    sensors = [4,5],    // list of sensors connected to that device
    state   = okay      // current state (okay, warn, halt)

} {

    // when a sensor reading is available, we check if the reading is within the accepted range. If not we adjust the state
    ()      :-  @sensors(:s,:v), hush, peek(sensors,:l), lst.item(:l,_,:s), #line(:s,:rl,:gl), #line.check(:v,:rl,:gl,:o),
                peek(state,:state), neq(:o,:state), poke(state,:o), peek(id,:i), declare(device(:i,:o));

    // handle state query
    (:i,:s) :- peek(id,:id?[eq(:i)]), peek(state,:s);

}

//----------------------------------------------------------------------------------------------------------------------------------------

[Home] [Email] [Twitter] [LinkedIn]