core_interface.py
Go to the documentation of this file.
00001 # Class for management of the map core functionalities.
00002 # This includes management of vertices, edges, and descriptor links.
00003 
00004 from sqlalchemy import ForeignKey
00005 from sqlalchemy import Table
00006 from sqlalchemy import Column
00007 from sqlalchemy import types
00008 
00009 import rospy
00010 
00011 from lama_msgs.msg import DescriptorLink
00012 from lama_msgs.msg import InterfaceInfo
00013 from lama_msgs.msg import LamaObject
00014 
00015 from abstract_db_interface import AbstractDBInterface
00016 
00017 _default_core_table_name = 'lama_object'
00018 _default_descriptor_table_name = 'lama_descriptor_link'
00019 _expected_lama_object_md5sum = 'e2747a1741c10b06140b9673d9018102'
00020 
00021 
00022 class CoreDBInterface(AbstractDBInterface):
00023     def __init__(self, interface_name=None, descriptor_table_name=None,
00024                  start=False):
00025         """Build the map interface for LamaObject
00026 
00027         Parameters
00028         ----------
00029         - interface_name: string, name of the table containing LamaObject
00030             messages (vertices and edges). Defaults to 'lama_objects'.
00031         - descriptor_table_name: string, name of the table for DescriptorLink
00032             messages. Defaults to 'lama_descriptor_links'.
00033         """
00034         self._check_md5sum()
00035         self.direct_attributes = ['id', 'id_in_world', 'name', 'emitter_id',
00036                                   'emitter_name', 'type']
00037         if not interface_name:
00038             interface_name = _default_core_table_name
00039         if not descriptor_table_name:
00040             descriptor_table_name = _default_descriptor_table_name
00041         self.descriptor_table_name = descriptor_table_name
00042 
00043         get_srv_type = 'lama_msgs/GetLamaObject'
00044         set_srv_type = 'lama_msgs/SetLamaObject'
00045         super(CoreDBInterface, self).__init__(interface_name,
00046                                               get_srv_type, set_srv_type,
00047                                               start=start)
00048 
00049         # Add the "unvisited" vertex. Edge for which the outoing vertex is not
00050         # visited yet have reference[1] == unvisited_vertex.id.
00051         unvisited_vertex = LamaObject()
00052         unvisited_vertex.id = -1
00053         unvisited_vertex.name = 'unvisited'
00054         unvisited_vertex.type = LamaObject.VERTEX
00055         self.set_lama_object(unvisited_vertex)
00056 
00057         # Add the "unknown" edge. This is used to indicate that the robot
00058         # position is unknown because we didn't recognized any vertex yet.
00059         unknown_edge = LamaObject()
00060         unknown_edge.id = -2
00061         unknown_edge.name = 'unknown'
00062         unknown_edge.type = LamaObject.EDGE
00063         unknown_edge.references[0] = unvisited_vertex.id
00064         unknown_edge.references[1] = unvisited_vertex.id
00065         self.set_lama_object(unknown_edge)
00066 
00067         # Add the "undefined" vertex. This is to ensure that automatically
00068         # generated ids (primary keys) are greater than 0.
00069         undefined_vertex = LamaObject()
00070         undefined_vertex.id = 0
00071         undefined_vertex.name = 'undefined'
00072         undefined_vertex.type = LamaObject.VERTEX
00073         self.set_lama_object(undefined_vertex)
00074 
00075     @property
00076     def interface_type(self):
00077         return 'core'
00078 
00079     def _check_md5sum(self):
00080         """Check that current implementation is compatible with LamaObject"""
00081         lama_object = LamaObject()
00082         if lama_object._md5sum != _expected_lama_object_md5sum:
00083             raise rospy.ROSException('CoreDBInterface incompatible ' +
00084                                      'with current LamaObject implementation')
00085 
00086     def _generate_schema(self):
00087         """Create the SQL tables"""
00088         self._add_interface_description()
00089         self._generate_core_table()
00090         self._generate_descriptor_table()
00091         self.metadata.create_all()
00092 
00093     def _generate_core_table(self):
00094         """Create the SQL tables for LamaObject messages"""
00095         # The table format is hard-coded but the compatibility was checked in
00096         # __init__.
00097         table = Table(self.interface_name,
00098                       self.metadata,
00099                       Column('id', types.Integer, primary_key=True),
00100                       extend_existing=True)
00101         table.append_column(Column('id_in_world',
00102                                    types.Integer))
00103         table.append_column(Column('name', types.String))
00104         table.append_column(Column('emitter_id', types.Integer))
00105         table.append_column(Column('emitter_name', types.String))
00106         table.append_column(Column('type', types.Integer))
00107         table.append_column(Column('v0', types.Integer))
00108         table.append_column(Column('v1', types.Integer))
00109         self.core_table = table
00110 
00111     def _generate_descriptor_table(self):
00112         """Create the SQL tables for descriptor_links"""
00113         table = Table(self.descriptor_table_name,
00114                       self.metadata,
00115                       Column('id', types.Integer, primary_key=True),
00116                       extend_existing=True)
00117         table.append_column(Column('object_id',
00118                                    types.Integer,
00119                                    ForeignKey(self.interface_name + '.id')))
00120         table.append_column(Column('descriptor_id', types.Integer))
00121         table.append_column(Column('interface_name', types.String))
00122         table.append_column(Column('timestamp_secs', types.Integer))
00123         table.append_column(Column('timestamp_nsecs', types.Integer))
00124         # TODO: add a uniqueness constraint on (object_id, descriptor_id,
00125         # interface_name)
00126         self.descriptor_table = table
00127 
00128     def _lama_object_from_query_result(self, result):
00129         lama_object = LamaObject()
00130         for attr in self.direct_attributes:
00131             setattr(lama_object, attr, result[attr])
00132         lama_object.references[0] = result['v0']
00133         lama_object.references[1] = result['v1']
00134         return lama_object
00135 
00136     def getter_callback(self, msg):
00137         """Get a LamaObject from the database
00138 
00139         Get a LamaObject from the database, from its id.
00140         Return an instance of GetLamaObject.srv response.
00141 
00142         Parameters
00143         ----------
00144         - msg: an instance of GetLamaObject.srv request.
00145         """
00146         id_ = msg.id
00147         lama_object = self.get_lama_object(id_)
00148         # Create an instance of getter response.
00149         response = self.getter_service_class._response_class()
00150         response.object = lama_object
00151         return response
00152 
00153     def setter_callback(self, msg):
00154         """Add a LamaObject message to the database
00155 
00156         Return an instance of SetLamaObject.srv response.
00157 
00158         Parameters
00159         ----------
00160         - msg: an instance of SetLamaObject.srv request.
00161         """
00162         # Create an instance of setter response.
00163         response = self.setter_service_class._response_class()
00164         response.id = self.set_lama_object(msg.object)
00165         return response
00166 
00167     def get_lama_object(self, id_):
00168         """Get a vertex or an edge from its unique id_
00169 
00170         Return an instance of LamaObject.
00171 
00172         Parameters
00173         ----------
00174         - id_: int, lama object id (id in the database).
00175         """
00176         # Make the transaction for the core table.
00177         query = self.core_table.select(
00178             whereclause=(self.core_table.c.id == id_))
00179         connection = self.engine.connect()
00180         with connection.begin():
00181             result = connection.execute(query).fetchone()
00182         connection.close()
00183         if not result:
00184             err = 'No element with id {} in database table {}'.format(
00185                 id_, self.core_table.name)
00186             raise rospy.ServiceException(err)
00187 
00188         return self._lama_object_from_query_result(result)
00189 
00190     def set_lama_object(self, lama_object):
00191         """Add/modify a lama object to the database
00192 
00193         Return the lama object's id.
00194 
00195         Parameter
00196         ---------
00197         - lama_object: an instance of LamaObject.
00198         """
00199         if len(lama_object.references) != 2:
00200                 raise rospy.ServiceException(
00201                     'malformed references, length = {}'.format(
00202                         len(lama_object.references)))
00203         if ((lama_object.id == 0) and
00204             (lama_object.type == LamaObject.EDGE) and
00205             (0 in lama_object.references)):
00206                 # 0 is undefined and not allowed.
00207                 raise rospy.ServiceException('edge references cannot be 0')
00208 
00209         is_undefined = (lama_object.name == 'undefined')
00210         is_special_vertex = lama_object.id < 0 or is_undefined
00211         is_new_vertex = lama_object.id == 0 and not is_undefined
00212 
00213         # Check for id existence.
00214         query = self.core_table.select(
00215             whereclause=(self.core_table.c.id == lama_object.id))
00216         connection = self.engine.connect()
00217         with connection.begin():
00218             result = connection.execute(query).fetchone()
00219         connection.close()
00220 
00221         if result is not None and is_special_vertex:
00222             # Exit if lama_object is an already-existing special object.
00223             return lama_object.id
00224 
00225         if result is not None and not is_new_vertex:
00226             # Update existing Lama Object.
00227             object_id = self._update_lama_object(lama_object)
00228         else:
00229             # Insert new Lama Object.
00230             object_id = self._insert_lama_object(lama_object)
00231 
00232         return object_id
00233 
00234     def _insert_lama_object(self, lama_object):
00235         """Insert a new Lama Object into the core table without any checks
00236 
00237         Do not invoke directly, use self.set_lama_object.
00238         """
00239         insert_args = {
00240             'id_in_world': lama_object.id_in_world,
00241             'name': lama_object.name,
00242             'emitter_id': lama_object.emitter_id,
00243             'emitter_name': lama_object.emitter_name,
00244             'type': lama_object.type,
00245             'v0': lama_object.references[0],
00246             'v1': lama_object.references[1],
00247         }
00248         if lama_object.id:
00249             insert_args['id'] = lama_object.id
00250         connection = self.engine.connect()
00251         with connection.begin():
00252             result = connection.execute(self.core_table.insert(),
00253                                         insert_args)
00254         connection.close()
00255         object_id = result.inserted_primary_key[0]
00256         self._set_timestamp(rospy.Time.now())
00257         return object_id
00258 
00259     def _update_lama_object(self, lama_object):
00260         """Update a Lama Object without any checks
00261 
00262         Do not invoke directly, use self.set_lama_object.
00263         """
00264         # Update the core table, if necessary.
00265         update_args = {}
00266         for attr in self.direct_attributes:
00267             v = getattr(lama_object, attr)
00268             if v:
00269                 update_args[attr] = v
00270         if lama_object.references[0]:
00271             update_args['v0'] = lama_object.references[0]
00272         if lama_object.references[1]:
00273             update_args['v1'] = lama_object.references[1]
00274         if update_args:
00275             update = self.core_table.update(
00276                 whereclause=(self.core_table.c.id == lama_object.id))
00277             connection = self.engine.connect()
00278             with connection.begin():
00279                 connection.execute(update, update_args)
00280             connection.close()
00281 
00282         self._set_timestamp(rospy.Time.now())
00283         return lama_object.id
00284 
00285     def del_lama_object(self, id_):
00286         """Remove a LamaObject from the database"""
00287         delete = self.core_table.delete(
00288             whereclause=(self.core_table.c.id == id_))
00289         connection = self.engine.connect()
00290         with connection.begin():
00291             connection.execute(delete)
00292         connection.close()
00293 
00294     def get_descriptor_links(self, id_, interface_name=None):
00295         """Retrieve the list of DescriptorLink associated with a Lama object
00296 
00297         Return a list of DescriptorLink. If interface_name is given, return
00298         all DescriptorLink corresponding to this interface_name, otherwise, and
00299         if interface_name is '' or '*', return all DescriptorLink.
00300 
00301         Parameters
00302         ----------
00303         - id_: int, lama object id in the database.
00304         - interface_name: string, default to None.
00305             If None, '', or '*', all DescriptorLink are returned.
00306             Otherwise, only DescriptorLink from this interface are returned.
00307         """
00308         desc_links = []
00309         # Make the transaction from the descriptor table.
00310         table = self.descriptor_table
00311         query = table.select()
00312         query = query.where(table.c.object_id == id_)
00313         if interface_name and interface_name != '*':
00314             query = query.where(table.c.interface_name == interface_name)
00315         connection = self.engine.connect()
00316         with connection.begin():
00317             results = connection.execute(query).fetchall()
00318         connection.close()
00319         if not results:
00320             return []
00321         for result in results:
00322             desc_link = DescriptorLink()
00323             desc_link.object_id = id_
00324             desc_link.descriptor_id = result['descriptor_id']
00325             desc_link.interface_name = result['interface_name']
00326             desc_links.append(desc_link)
00327         return desc_links
00328 
00329     def get_interface_info(self, interface_name):
00330         table = self.interface_table
00331         query = table.select()
00332         query = query.where(table.c.interface_name == interface_name)
00333         connection = self.engine.connect()
00334         with connection.begin():
00335             result = connection.execute(query).fetchone()
00336         connection.close()
00337         if not result:
00338             return
00339         interface_info = InterfaceInfo()
00340         interface_info.interface_name = interface_name
00341         interface_info.interface_type = str(result['interface_type'])
00342         interface_info.message_type = str(result['message_type'])
00343         interface_info.get_service_type = str(result['get_service_type'])
00344         interface_info.set_service_type = str(result['set_service_type'])
00345         interface_info.timestamp.secs = int(result['timestamp_secs'])
00346         interface_info.timestamp.nsecs = int(result['timestamp_nsecs'])
00347         interface_info.get_service_name = self.default_getter_service_name(
00348             interface_name)
00349         interface_info.set_service_name = self.default_setter_service_name(
00350             interface_name)
00351         return interface_info


interfaces
Author(s): Gaël Ecorchard , Karel Košnar
autogenerated on Sat Jun 8 2019 19:03:14