Skip to content

Commit a61fd57

Browse files
update reelay to new version, update readme
1 parent 0dab356 commit a61fd57

File tree

8 files changed

+116
-71
lines changed

8 files changed

+116
-71
lines changed

README.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,16 @@ $ sudo apt-get install swi-prolog
3131
```
3232

3333
## Reelay (https://github.com/doganulus/reelay)
34-
In order to use TL oracle we need to install Reelay. If the user is not interested in using the RML Oracle, this step can be skipped.
34+
In order to use TL oracle we need to install Reelay. If the user is not interested in using the TL Oracle, this step can be skipped.
3535
To install Reelay, follow the instructions at: https://github.com/doganulus/reelay/blob/master/docs/install.md
3636

37+
Note: Reelay requires Python 3.
38+
39+
Using pip:
40+
```bash
41+
$ python -m pip install reelay
42+
```
43+
3744
## Java (https://openjdk.java.net/install/):
3845
The following instructions are for installing OpenJDK-11.
3946
```bash
@@ -250,9 +257,14 @@ More information about RML can be found at: https://rmlatdibris.github.io/
250257
Alternatively, we can analyse the log file using the TL oracle.
251258
```bash
252259
$ cd ~/ROSMonitoring/oracle/TLOracle/
253-
$ ./oracle.py --offline --trace ../../log.txt
260+
$ ./oracle.py --offline --property property --trace ../../log.txt --discrete
254261
```
255-
The MTL property defined now into 'property.py' is only an example (in the chatter example there is nothing interesting enough to be checked).
262+
The TL property defined now into 'property.py' is only an example (in the chatter example there is nothing interesting enough to be checked). The property says that 'chatter' is always contained inside the message.
263+
Note:
264+
265+
--discrete means that we are assuming the events homogeneously distributed (the time between two events is fixed)
266+
267+
--dense means that the events can be observed at a different rate (the time between two events is not always the same)
256268

257269
### Adding a monitor in the middle.
258270

@@ -290,7 +302,7 @@ monitors: # here we list the monitors we are going to generate
290302
291303
This configuration file is very similar to the previous one. But this time we are asking for the generation of an online monitor. In order to do so, we need to inform the generator where the Oracle is listening and on which port. In this way, the generated monitor will be capable of communicating with it using WebSockets.
292304
Another addition to this configuration file is the 'publishers' field inside the chatter topic.
293-
Since we are doing online RV, the monitor is checking the events at runtime. Now, if we wanted just to log each event, we could maintain the action set to 'log'. The behaviour in this way would be exactly the same as for the offline monitor, with the only difference that each time an event is observed, the monitor propagates this event to the oracle and waits for the current verdict against a chosen property. Consequently, rather than the offline case, in the online scenario, the monitor will also log the satisfaction/violation of the property (but nothing more). This can be useful if we are debugging a system, but in a real scenario we could need to enforce the correcteness of the events. For instance, filtering the events which are considered wrong by the Oracle. For doing this, we can change the action from 'log' to 'filter'.
305+
Since we are doing online RV, the monitor is checking the events at runtime. Now, if we wanted just to log each event, we could maintain the action set to 'log'. The behaviour in this way would be exactly the same as for the offline monitor, with the only difference that each time an event is observed, the monitor propagates this event to the oracle and waits for the current verdict against a chosen property. Consequently, rather than the offline case, in the online scenario, the monitor will also log the satisfaction/violation of the property (but nothing more). This can be useful if we are debugging a system, but in a real scenario we could need to enforce the correctness of the events. For instance, filtering the events which are considered wrong by the Oracle. For doing this, we can change the action from 'log' to 'filter'.
294306
295307
Once the action 'filter' is selected, the monitor will filter the wrong messages. But, to be able to do so, it must be in the middle of the communication. Until now the monitor was only another node in the system and was just subscribing the topics. This is not enough if we want to filter the wrong messages. In order to solve this problem, ROSMonitoring instrument the nodes changing the names and creating gaps in the communications. Thanks to this communication gaps, the monitor can become a bridge for the topics of our interest, and filter the messages in case they are wrong.
296308
@@ -331,11 +343,20 @@ The monitor will now check the events at runtime. But, since the property is alw
331343
Alternatively, we can use the online oracle with TL properties.
332344
```bash
333345
$ cd ~/ROSMonitoring/oracle/TLOracle/
334-
$ ./oracle.py --online --port 8080
346+
$ ./oracle.py --online --property property --port 8080 --discrete
335347

336348
```
337349
In this way, the Python oracle will start listening on the 8080 port. Each message observed on this connection will be passed to Reelay and checked against the property defined in property.py.
338350

351+
## Additional information published by the monitor (online)
352+
353+
The monitor reports constantly information about its analysis.
354+
355+
- When a violation of the formal property is observed, the monitor publishes a message on the topic '/{monitor_id}/monitor_error' of type MonitorError (monitor/msg/MonitorError.msg). Where {monitor_id} is the monitor id added in the configuration file (see generation section above). For instance, if monitor id is 'monitor_1', then the error messages will be reported on the topic '/monitor_1/monitor_error'.
356+
- On the topic '/{monitor_id}/monitor_verdict' it is possible to keep track of the current monitor's verdict. The message is of type String, and can be: 'true', 'false', 'currently_true', 'currently_false', 'unknown'. Depending on the chosen oracle, all or a subset of these verdicts can be reported by the monitor.
357+
358+
Note: To enable the publishing of error messages, the user has to set the warning field to 1 or 2 (0 no warnings will be published). If the warning level is set to 1, then an error will be reported when the monitor's verdict is 'currently_false' or 'false'. If the warning level is set to 2, then an error will be reported only when the monitor's verdict is 'false'. Thanks to the warning level we can customise how strict we want to be on the monitor's verdict. There might be scenarios where we might be more interested in checking a property against the system (in this case warning level 2, since we only care about the system satisfying/violating the property); while there might be other scenarios where we care about the current satisfaction/violation of a property in the current system state (warning level 2), even though in the future the verdict might change. In practice, warning level has to be set to 2 when we only care about final verdicts, so we want the monitor to report an error only when it has proven the property has been violated by the system and will always be violated; warning level to 1, when we are not interested only in the final verdict, and we want to give importance to the current observed trace, and its satisfaction/violation of the property under analysis. In this case, the current state of the system might be satisfying/violating the property, but the monitor cannot conclude it will always be satisfied/violated.
359+
339360
# License:
340361
ROSMonitoring project is released under MIT license
341362

generator/generator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def on_message(ws, message):
194194
other_callbacks += '''
195195
else:
196196
logging(json_dict)
197-
if (json_dict['verdict'] == 'false' and actions[json_dict['topic']][1] == 2) or (json_dict['verdict'] == 'currently_false' and actions[json_dict['topic']][1] == 1):'''
197+
if (json_dict['verdict'] == 'false' and actions[json_dict['topic']][1] >= 1) or (json_dict['verdict'] == 'currently_false' and actions[json_dict['topic']][1] == 1):'''
198198
if not silent:
199199
other_callbacks += '''
200200
rospy.loginfo('The event ' + message + ' is inconsistent..')'''
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# MIT License
2+
#
3+
# Copyright (c) [2020] [Angelo Ferrando]
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
import oracle
24+
25+
26+
# property to verify
27+
PROPERTY = r'historically{x > 0, y > 0, z > 0}'
28+
29+
# predicates used in the property (initialization for time 0)
30+
predicates = dict(
31+
)
32+
# in here we can add all the predicates we are interested in.. Of course, we also need to define how to translate Json messages to predicates.
33+
34+
# function to abstract a dictionary (obtained from Json message) into a list of predicates
35+
def abstract_message(message):
36+
return message
37+
# This function has to be defined by the user depending on the property defined.
38+
# In this case we have just implemented a simple and general function which
39+
# updates the predicates if it finds the topic in the list of predicates.
40+
# Since the property is defined on predicates, we need this function to update the
41+
# predicates each time a message is observed. This abstraction of course is totally
42+
# dependent on the specific application.

oracle/TLOracle/examples/radiation_orange.py renamed to oracle/TLOracle/examples/radiation/radiation_orange.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,14 @@
2222

2323
import oracle
2424

25-
# type of the property (PLTL, PMTL, or PSTL)
26-
TYPE = oracle.TypeOfProperty.PMTL
2725

2826
# MTL property to verify
29-
PROPERTY = "(once[0:3](not radiation_level_high))"
27+
PROPERTY = "once[0:3](not {radiation_level_high})"
3028
# In this case is Past-MTL, but it can also be a Past-LTL or Past-STL
3129

3230
# predicates used in the property (initialization for time 0)
3331
predicates = dict(
3432
time = 0,
35-
radiation_level = 0,
3633
radiation_level_high = False
3734
)
3835
# in here we can add all the predicates we are interested in.. Of course, we also need to define how to translate Json messages to predicates.

oracle/TLOracle/examples/radiation_red.py renamed to oracle/TLOracle/examples/radiation/radiation_red.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,13 @@
2222

2323
import oracle
2424

25-
# type of the property (PLTL, PMTL, or PSTL)
26-
TYPE = oracle.TypeOfProperty.PMTL
27-
2825
# MTL property to verify
29-
PROPERTY = "(once[0:3](not radiation_level_high))"
26+
PROPERTY = "(once[0:3](not {radiation_level_high}))"
3027
# In this case is Past-MTL, but it can also be a Past-LTL or Past-STL
3128

3229
# predicates used in the property (initialization for time 0)
3330
predicates = dict(
3431
time = 0,
35-
radiation_level = 0,
3632
radiation_level_high = False
3733
)
3834
# in here we can add all the predicates we are interested in.. Of course, we also need to define how to translate Json messages to predicates.

oracle/TLOracle/oracle.py

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import argparse
3131
import importlib
3232
from enum import Enum
33-
import time
3433

3534
# type of properties available in reelay
3635
class TypeOfProperty(Enum):
@@ -63,26 +62,33 @@ def message_received(client, server, message):
6362

6463
# Function checking the event against the specification (it simply calls Reelay, nothing more)
6564
def check_event(event):
66-
global last_time
65+
global last_time, tl_oracle, property, last_res
6766
event_dict = json.loads(event)
67+
if not tl_oracle:
68+
if model == 'dense' and 'time' in event_dict:
69+
tl_oracle = reelay.dense_timed_monitor(pattern = property.PROPERTY)
70+
else:
71+
tl_oracle = reelay.discrete_timed_monitor(pattern = property.PROPERTY)
6872
if 'time' in event_dict:
69-
if not last_time:
73+
if last_time is None:
7074
last_time = event_dict['time']
7175
event_dict['time'] = 0
7276
else:
7377
event_dict['time'] = event_dict['time'] - last_time
74-
else:
75-
if not last_time:
76-
last_time = time.time()
77-
event_dict['time'] = 0
78-
else:
79-
event_dict['time'] = int(time.time() - last_time)
80-
return tl_oracle.update(property.abstract_message(event_dict))
78+
abs_msg = property.abstract_message(event_dict)
79+
res = tl_oracle.update(abs_msg)
80+
if model == 'discrete' and res:
81+
last_res = res['value']
82+
if model == 'dense' and res:
83+
last_res = True
84+
for d in res:
85+
if not d['value']:
86+
last_res = False
87+
break
88+
return last_res
8189

8290
def main(argv):
83-
global property
84-
global tl_oracle
85-
global last_time
91+
global property, tl_oracle, last_time, model, last_res
8692

8793
parser = argparse.ArgumentParser(
8894
description='this is an Oracle Python implementation based on Reelay for monitoring PTL, MTL and STL properties',
@@ -95,6 +101,10 @@ def main(argv):
95101
action='store_true')
96102
parser.add_argument('--offline',
97103
action='store_true')
104+
parser.add_argument('--discrete',
105+
action='store_true')
106+
parser.add_argument('--dense',
107+
action='store_true')
98108
parser.add_argument('--port',
99109
help='Port where the Websocket Oracle has to listen on',
100110
default = 8080,
@@ -106,20 +116,15 @@ def main(argv):
106116

107117
property = importlib.import_module(args.property)
108118
last_time = None
119+
tl_oracle = None
120+
last_res = True
109121

110-
if property.TYPE.value == TypeOfProperty.PLTL.value:
111-
tl_oracle = reelay.past_ltl.monitor(
112-
pattern = property.PROPERTY)
113-
elif property.TYPE.value == TypeOfProperty.PMTL.value:
114-
tl_oracle = reelay.past_mtl.monitor(
115-
pattern = property.PROPERTY,
116-
time_model = 'discrete')
117-
elif property.TYPE.value ==TypeOfProperty.PSTL.value:
118-
tl_oracle = reelay.past_stl.monitor(
119-
pattern = property.PROPERTY,
120-
time_model = 'dense')
122+
if args.discrete:
123+
model = 'discrete'
124+
elif args.dense:
125+
model = 'dense'
121126
else:
122-
print('Property type not supported by reelay')
127+
print('You have to specify if the time is discrete or dense (example, --discrete, -- dense)')
123128
return
124129

125130
if args.online:
@@ -139,7 +144,7 @@ def main(argv):
139144
if not check_event(event):
140145
print('Property violated: ' + property.PROPERTY)
141146
print('The event: \n' + event + '\n' + 'is not consistent with the specification.')
142-
return
147+
#return
143148
else:
144149
print('You have to specify if the oracle has to perform Online (--online) or Offline (--offline) verification')
145150

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,27 @@
2222

2323
import oracle
2424

25-
# type of the property (PLTL, PMTL, or PSTL)
26-
TYPE = oracle.TypeOfProperty.PMTL
27-
28-
# MTL property to verify
29-
PROPERTY = "(historically[0:5]((door_open) and not dow_suppressed) or (not chatter)) -> door_open_warning"
30-
# In this case is Past-MTL, but it can also be a Past-LTL or Past-STL
25+
# property to verify
26+
PROPERTY = "historically[0:3]{hello}"
3127

3228
# predicates used in the property (initialization for time 0)
3329
predicates = dict(
3430
time = 0,
35-
door_open = False,
36-
dow_suppressed = False,
37-
door_open_warning = False,
38-
chatter = False
31+
hello = False
3932
)
4033
# in here we can add all the predicates we are interested in.. Of course, we also need to define how to translate Json messages to predicates.
4134

4235
# function to abstract a dictionary (obtained from Json message) into a list of predicates
4336
def abstract_message(message):
44-
if message['topic'] in predicates:
45-
if isinstance(message['data'], bool):
46-
predicates[message['topic']] = message['data']
47-
else:
48-
predicates[message['topic']] = False
49-
predicates['time'] = int(message['time'])
37+
if message['topic'] == 'chatter' and message['data'] == 'hello':
38+
predicates['hello'] = True
39+
else:
40+
predicates['hello'] = False
41+
predicates['time'] = message['time']
5042
return predicates
5143
# This function has to be defined by the user depending on the property defined.
5244
# In this case we have just implemented a simple and general function which
5345
# updates the predicates if it finds the topic in the list of predicates.
5446
# Since the property is defined on predicates, we need this function to update the
5547
# predicates each time a message is observed. This abstraction of course is totally
56-
# dependent on the specific application.
48+
# dependent on the specific application.

oracle/TLOracle/property_R2_1.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,17 @@
2222

2323
import oracle
2424

25-
# type of the property (PLTL, PMTL, or PSTL)
26-
TYPE = oracle.TypeOfProperty.PLTL
27-
28-
# MTL property to verify
29-
PROPERTY = "(historically(dist_x > 0 or dist_y > 0 or dist_z > 0))"
30-
# In this case is Past-LTL
25+
# property to verify
26+
PROPERTY = r'historically{x > 0, y > 0, z > 0}'
3127

3228
# predicates used in the property (initialization for time 0)
33-
predicates = dict()
29+
predicates = dict(
30+
)
3431
# in here we can add all the predicates we are interested in.. Of course, we also need to define how to translate Json messages to predicates.
3532

3633
# function to abstract a dictionary (obtained from Json message) into a list of predicates
3734
def abstract_message(message):
38-
predicates['dist_x'] = message['data'][0]
39-
predicates['dist_y'] = message['data'][1]
40-
predicates['dist_z'] = message['data'][2]
41-
predicates['time'] = message['time']
42-
print(predicates)
43-
return predicates
35+
return message
4436
# This function has to be defined by the user depending on the property defined.
4537
# In this case we have just implemented a simple and general function which
4638
# updates the predicates if it finds the topic in the list of predicates.

0 commit comments

Comments
 (0)