Event driven Temporla prolog - is a SWI-Prolog extension, created for writing scripts, which control device (phone, computer...) behaviour in dependence of state of surrounding world. The extension introduces temporal logic predicates, temporal terms (terms, which remember, what value did they have before) and tools for setting callbacks on temporal terms change.
New constructions
Logic
ET-Prolog utilizes inverted finite linear temporal logic. It is based on time scale which goes from current time moment to the past. Scale consists of ordered in time epochs. Finiteness of the scale means that information about old epochs is deleted so as not to waste system resources.
Temporal terms
Temporal terms are defined on time scale. Their value describes environment state, so it can change from epoch to epoch (but it is guaranteed, that value of temporal term would not change during epoch). ET-Prolog tries to keep track of environment and change terms value accordingly (so they can change without direct order from program).
ET-Prolog remembers values, which temporal terms receive, so you always can access them by means of
temporal operators
Guards and scripts
You can attach arbitrary number of pairs (guards list|script) to temporal term. On every temporal term calculation (which occure not more than once per epoch) guards get current and previous values and make a decision, if script must be executed in current epoch. If the answer is positive, script is added to queue and executed. Guards are designed for increasing efficiency by cutting knowingly useless queries. If guard list contains more than one guard, script would be executed only if all guards return true.
Hints
As said before, temporal terms can't change value during epoch, so messages about need to change value are stored in a form of hints. Hints can originate from prolog scripts or from threads, which monitor file changes, DBus signals, timers and so on. When all scheduled scripts are executed new epoch begins with working out queued hints, what results in temporal terms value changes, guards invocation and scripts are added to execution queue. Or, if there is no hints available ET-Prolog falls asleep and waits for them without wasting system resources.
Syntax
Temporal terms
Currently following predicates are implemented:
- fileContents(CONTENT, FILENAME) - unifies first argument with FILENAME contents. ET-Prolog would install watch and if it would get urgent read signal, file would be read again from the beginning. This is useful for reading files from /sys. Standard ET-Prolog library for n900 has following terms based on fileContents:
- headphonesState(STATE) - unifies first argument with headphones (and av cable) socket.
- slideState(STATE) - unifies first argument with keyboard status (open/closed)
- shutterState(STATE) - unifies first argument with status of camera shutter
- pollFile(STATE, FILENAME) - unifies first argument with FILENAME contents. File is reread on every first invocation per epoch. Standard ET-Prolog library for n900 has following terms based on pollFile:
- proximitySensor(STATE) - unifies proximity sensor state with first argument.
- intFile(LIGHT, FILENAME, TIME, MEASURES) - pollFile modification, which reads integer number from file. Number is read MEASURES times during TIME (in 1/1000 seconds), and LIGHT is unified with average value. Standard ET-Prolog library for n900 has following terms based on intFile:
- lightSensor(LIGHT, TIME, MEASURES) - unifies first argument with value from ambient light sensor.
- temporalTerm(VALUE, NAME) - temporal term, which can be controlled from prolog or through DBus.
Using temporalTerm
ET-Prolog can't calculate value for terms of this type. It just remembers values and returns them when requested. So you have to work a bit differently with this terms. First of all you need to specify initial value with predicate
newTemporalTerm(NAME, INITIAL_VALUE)
where first argument specifies name of term (you can get it back by this name) and INITIAL_VALUE is initial value. INITIAL_VALUE can be arbitrary prolog construction, which does not include variables.
Get this value back you can with temporalTerm(VALUE, NAME). You can change it by sending hint with
After processing this hint predicate would change value.
Important feature of this type of temporal terms is ability to work with DBus. You can register dbus RPC handler and all incoming RPC's would be converted to prolog lists and passed to term in a form of hint. This can be done with folowing predicate
addDBusHandler(SERVICE, OBJECT_PATH, INTERFACE, METHOD, IS_EVENT, TEMPORAL_TERM)
SERVICE, OBJECT_PATH, INTERFACE and METHOD describe what DBus RPC you want to receive. If IS_EVENT is false, than every RPC call would be converted to one hint and term would remember latest received values. If IS_EVENT is true, then one call would be converted to two hints: one with recieved values and one with false. In result, in all epochs in which no calls were received, term would be false. TEMPORAL_TERM is a temporal term, which should receive these hints.
You can handle DBus signals in a similar way:
addDBusListener(TYPE, OBJECT_PATH, INTERFACE, METHOD, IS_EVENT, USE_SYSTEM_BUS, TEMPORAL_TERM)
Where:
- TYPE - type of event to listen to. Can be signal or method_call
- OBJECT_PATH, INTERFACE, METHOD - describe what DBus signals you want to receive. Use empty string '' as a wildcard
- IS_EVENT have the same meaning as for addDBusHandler
- USE_SYSTEM_BUS - if true system bus would be used, otherwise session bus would be used
Received signal would be converted to prolog list. You can work with it with usual list operators.
Adding guards
Following guards are defined:
- alwaysGuard - always returns true
- changedGuard - returns true if value has been changed
- decreasedGuard - returns true if value decreased
-
- equalGuard(VALUE) - returns true if term value is equal to VALUE
-
- greaterGuard(VALUE) - returns true if term value is more than VALUE
-
- increasedGuard - returns true if value increased
-
- intervalGuard(A, B) - returns true if value is between A and B
-
- lessGuard(value) - returns true if term value is less than VALUE
-
- nEqualGuard(value) - returns true if term value is not equal to VALUE
-
- sameGuard - returns true if term value didn't change
-
guards are bind to terms with
addGuard(TEMPORAL_TERM, [[GUARD, ARGS...], [GUARD, ARGS...]...], SCRIPT, TRIGGER_ON_CHANGE)
- TEMPORAL_TERM - temporal term to which you bind guards
- [[GUARD, ARGS...], [GUARD, ARGS...]...] - guards list. ARGS - argument list, passed to temporal term. Currently used only for GpsTerm
- SCRIPT - path to script, that should be executed
- TRIGGER_ON_CHANGE if true, then script is executed on every guards conjunction change, if false, script is executed if guards conjunction is true
For example
addGuard(temporalTerm(_, 'manual_timer'), alwaysGuard, 'logic/manual.pl', false)
Alternatively you can use
addChangeGuard(TEMPORAL_TERM, [[GUARD, ARGS...], [GUARD, ARGS...]...], SCRIPT, TRIGGER_ON_CHANGE)
which works like addGuard, but script is triggered on guards conjunction change.
Working with timers
If you need to check term value on regular basis you can send it hints with predefined intervals. Use following predicates
addTimerHint(TERM, INTERVAL)
for adding timer for TERM and
delTimerHint(TERM, INTERVAL)
for removal.
Sending DBus signals
As far as main interprocess communication tool in maemo is DBus, ET-Prolog allows to send DBus signals. It can e done by means of
dbusSend(SERVICE, OBJECT_PATH, INTERFACE, METHOD, SIGNAL)
where first 4 arguments describe signal destination (for additional information see DBus documentation), and SIGNAL - is data that should be attached to signal. SIGNAL must be a list, ET-Prolog performs automatic conversion from prolog types to DBus types. The only problem is that SWI-Prolog doesn't have syntax for defining strings, so if you want to send a sting you have to do like this:
string_to_atom(PROFILE, 'silent'),
dbusSend('com.nokia.profiled', '/com/nokia/profiled', 'com.nokia.profiled', 'set_profile', [PROFILE])
Temporal operators
Temporal operator are used for access to temporal term values history. Following operators are defined:
- previous - calculates value, that was an epoch ago. You can apply this operator arbitrary number of times in recursion.
- always - returns true if argument is true in all epochs (not including old epochs deleted that free resources).
- sometimes - returns true if argument was true at least in one previous epoch.
Due to limitations of C++ SWI-Prolog interface and simple lack of time, multivalue predicates are not supported inside temporal terms (at least now). If you want to calculate several predicates you have to enclose them in additional parenthesis so as not to break syntax:
previous((
temporalTerm(OLDVALUE, 'manual'),
write('Previous manual term value:'),
write(OLDVALUE),
nl))
Cleaning memory
If you don't need temporal term any more you can delete it with
deleteTerm(TEMPORAL_TERM)
Beside this, old values are cleared automatically. By default values older than half an hour are deleted, if there are never values. This setting can be adjusted with -t command line argument.
Working with maemo libraries
alarmd - events based on alarms
Работа с таким событием состоит из двух этапов: создания события и его использования. Создается оно предикатом
newAlarmTerm(NAME, IS_EVENT, SCHEDULE)
Где NAME задает имя события, IS_EVENT определяет является ли это событие точкой во времени (значение истина), либо периодом (значение ложь). SCHEDULE описывает расписание, по которому событие наступает. Для случая IS_EVENT=true расписание имеет вид
причем вместо каждого значения можно указать *, что будет означать "любое значение", либо перечислить допустимые значения через запятую. Причем в качестве дня можно указывать как даты, так и дни недели. Т.е. "(5,wed.4,8,12.* 15:00)" означает "по средам и 5го числа 4го, 8го и 12го месяцев любого года в 15 часов". Вы можете указать несколько таких условий через точку с запятой, однако все они должны иметь одинаковый год.
Для случая IS_EVENT=false расписание имеет вид
(DD.MM.YYYY HH:MM;...)-(DD.MM.YYYY HH:MM;...)
Где в первых скобках указаны моменты времени, когда интервал начинается, а во вторых, когда он заканчивается.
Для получения значения терма используйте
liblocation - GPS
Following two predicates for working with GPS are defined:
gpsConfig(METHOD, INTERVAL)
where METHOD - method used for determining location. Can be CWP, ACWP, GNSS, AGNSS. And INTERVAL sates, how often location would be reported. See documentation for additional information
Second term allows you to get information about current location:
Where FIELD is a name of LocationGPSDeviceFix field which you want to get. Allowed values are: mode, time, time_set, ept, latitude, longitude, latlong_set, altitude, altitude_set, epv, track, track_set, epd, speed, speed_set, eps, climb, climb_set, epc. VALUE would be unified with the field.
When you add guards to GpsTerm you need to specify which field every guard should use. For example following code adds guard which is true, when you are near Moscow State University:
addGuard(gpsFix(_, 'mode'), [[equalGuard(true),'latlong_set'],[greaterGuard(55.71129),'latitude'],[greaterGuard(37.49922),'longitude'],[lessGuard(55.69145),'latitude'],[lessGuard(37.56634),'longitude']], 'logic/nearUniversity.pl', false)
Conclusion
This documentation can be incomplete. We could forget to describe some predicates or forget to write about some useful tips. But this is first beta release of ET-Prolog. If something is not clear, you can try look in the sources.