00001
00002
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
00050
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
00058
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
00068
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
00096
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
00125
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
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
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
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
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
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
00223 return lama_object.id
00224
00225 if result is not None and not is_new_vertex:
00226
00227 object_id = self._update_lama_object(lama_object)
00228 else:
00229
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
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
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