DatabaseTopicReader.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package ArmarX
19 * @author Philipp Schmidt( ufedv at student dot kit dot edu)
20 * @date 2016
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24 #include "DatabaseTopicReader.h"
25 
27 
28 namespace armarx
29 {
30 
31  DatabaseTopicReader::DatabaseTopicReader(std::filesystem::path path) :
32  filepath(path), db(nullptr), stmt(nullptr), database_open(true), end_of_database(false)
33  {
34  //Open database
35  int error_code = sqlite3_open(filepath.c_str(), &db);
36  if (error_code != SQLITE_OK)
37  {
38  ARMARX_ERROR_S << "Error opening database: " << sqlite3_errmsg(db);
39  ARMARX_ERROR_S << "Database Path: " << filepath.c_str();
40  sqlite3_close(db);
41  database_open = false;
42  return;
43  }
44 
45  //Prepare SQL statement
46  std::string sql_select = "SELECT * FROM TopicData WHERE ID >= ?";
47  error_code = sqlite3_prepare_v2(db, sql_select.c_str(), -1, &stmt, nullptr);
48  if (error_code != SQLITE_OK)
49  {
50  ARMARX_ERROR_S << "Can not prepare sql statement: " << sqlite3_errmsg(db);
51  ARMARX_ERROR_S << "SQL Query: " << sql_select;
52  sqlite3_finalize(stmt);
53  sqlite3_close(db);
54  database_open = false;
55  return;
56  }
57 
58  //Seek to start position
59  if (!seekTo(1))
60  {
61  ARMARX_ERROR_S << "Can not seek to start position, or maybe database is empty.";
62  sqlite3_finalize(stmt);
63  sqlite3_close(db);
64  database_open = false;
65  return;
66  }
67 
68  //Check if layout is correct (at least correct amount of columns)
69  if (sqlite3_column_count(stmt) != 5)
70  {
71  ARMARX_ERROR_S << "Database table layout is wrong. Closing database.";
72  sqlite3_finalize(stmt);
73  sqlite3_close(db);
74  database_open = false;
75  return;
76  }
77  }
78 
80  {
81  //NULL pointer is a harmless op
82  sqlite3_finalize(stmt);
83  sqlite3_close(db);
84  }
85 
86  std::filesystem::path
88  {
89  return filepath;
90  }
91 
92  bool
94  {
95  //Make sure this is threadsafe
96  std::unique_lock lock(mutex);
97 
98  //Check if database is open
99  if (!database_open)
100  {
101  ARMARX_ERROR_S << "Database not open or database table layout is wrong";
102  return false;
103  }
104 
105  //Check if end of select statement is reached
106  if (end_of_database)
107  {
108  //ARMARX_ERROR << "Can not read, end of replay data reached";
109  return false;
110  }
111 
112 
113  //Return current row
114  data.timestamp = IceUtil::Time::microSeconds(sqlite3_column_int64(stmt, 1));
115  data.topicName = std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2)));
116  data.operationName =
117  std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3)));
118 
119  //Retreive blob data and store in vector
120  const void* blob_data = sqlite3_column_blob(stmt, 4);
121  int size = sqlite3_column_bytes(stmt, 4);
122  //vector range constructor should copy the data according to docu
123  data.inParams = std::vector<Ice::Byte>((Ice::Byte*)blob_data, (Ice::Byte*)blob_data + size);
124 
125  //Move to next row for next read access
126  int error_code = sqlite3_step(stmt);
127  //We might get another row or the end of the list
128  if (error_code != SQLITE_ROW)
129  {
130  //Maybe end of select statement?
131  if (error_code == SQLITE_DONE)
132  {
133  ARMARX_INFO << "End of replay reached";
134  end_of_database = true;
135  }
136  else
137  {
138  ARMARX_ERROR_S << "Retrieving next row of data failed, closing database: "
139  << sqlite3_errmsg(db);
140  sqlite3_finalize(stmt);
141  sqlite3_close(db);
142  database_open = false;
143  return false;
144  }
145  }
146 
147  return true;
148  }
149 
150  bool
152  {
153  sqlite3_stmt* stmt_get_id;
154  std::string sql_get_id = "SELECT ID FROM TopicData WHERE TIME >= ? LIMIT 1";
155 
156  int error_code = sqlite3_prepare_v2(db, sql_get_id.c_str(), -1, &stmt_get_id, nullptr);
157  if (error_code != SQLITE_OK)
158  {
159  ARMARX_ERROR << "Could not seek to timestamp: " << timestamp.toMicroSeconds();
160  ARMARX_ERROR << "Can not prepare sql statement: " << sqlite3_errmsg(db);
161  ARMARX_ERROR << "SQL Query: " << sql_get_id;
162  sqlite3_finalize(stmt_get_id);
163  return false;
164  }
165 
166  error_code = sqlite3_bind_int64(stmt_get_id, 1, timestamp.toMicroSeconds());
167  if (error_code != SQLITE_OK)
168  {
169  ARMARX_ERROR << "Could not seek to timestamp: " << timestamp.toMicroSeconds();
170  ARMARX_ERROR << "Can not bind parameter to sql query: " << sqlite3_errmsg(db);
171  ARMARX_ERROR << "SQL Query: " << sql_get_id;
172  sqlite3_finalize(stmt_get_id);
173  return false;
174  }
175 
176  error_code = sqlite3_step(stmt_get_id);
177  //Expecting some results, otherwise something went wrong
178  if (error_code != SQLITE_ROW)
179  {
180  ARMARX_ERROR << "Could not seek to timestamp: " << timestamp.toMicroSeconds();
181  ARMARX_ERROR << "Query did not succeed or we got no result: " << sqlite3_errmsg(db);
182  ARMARX_ERROR << "SQL Query: " << sql_get_id;
183  sqlite3_finalize(stmt_get_id);
184  return false;
185  }
186 
187  bool returnValue = seekTo(sqlite3_column_int(stmt_get_id, 0));
188  sqlite3_finalize(stmt_get_id);
189  return returnValue;
190  }
191 
194  {
195  // Get timestamp of last entry
196  sqlite3_stmt* stmt_replay_length;
197  std::string sql_replay_length = "SELECT max(TIME) FROM TopicData";
198  int error_code =
199  sqlite3_prepare_v2(db, sql_replay_length.c_str(), -1, &stmt_replay_length, nullptr);
200  if (error_code != SQLITE_OK)
201  {
202  ARMARX_ERROR_S << "Can not prepare sql statement: " << sqlite3_errmsg(db);
203  ARMARX_ERROR_S << "SQL Query: " << sql_replay_length;
204  sqlite3_finalize(stmt_replay_length);
205  return IceUtil::Time::microSeconds(0);
206  }
207  error_code = sqlite3_step(stmt_replay_length);
208  if (error_code != SQLITE_ROW)
209  {
210  ARMARX_ERROR_S << "Can not execute sql statement, or no result: " << sqlite3_errmsg(db);
211  ARMARX_ERROR_S << "SQL Query: " << sql_replay_length;
212  sqlite3_finalize(stmt_replay_length);
213  return IceUtil::Time::microSeconds(0);
214  }
215 
216  long returnValue = sqlite3_column_int64(stmt_replay_length, 0);
217  sqlite3_finalize(stmt_replay_length);
218  return IceUtil::Time::microSeconds(returnValue);
219  }
220 
221  std::vector<std::string>
223  {
224  std::vector<std::string> topicList;
225 
226  //Check if database is open
227  if (!database_open)
228  {
229  ARMARX_ERROR << "Can not read list of replay topics if database is not open";
230  return topicList;
231  }
232 
233  //Retrieve all topics of database
234  sqlite3_stmt* stmt_topic_list;
235  std::string sql_topic_list = "SELECT TOPIC FROM Topics";
236  int error_code =
237  sqlite3_prepare_v2(db, sql_topic_list.c_str(), -1, &stmt_topic_list, nullptr);
238  if (error_code != SQLITE_OK)
239  {
240  ARMARX_ERROR_S << "Can not prepare sql statement: " << sqlite3_errmsg(db);
241  ARMARX_ERROR_S << "SQL Query: " << sql_topic_list;
242  sqlite3_finalize(stmt_topic_list);
243  return topicList;
244  }
245  error_code = sqlite3_step(stmt_topic_list);
246  if (error_code != SQLITE_ROW)
247  {
248  ARMARX_ERROR_S << "Can not execute sql statement, or no result: " << sqlite3_errmsg(db);
249  ARMARX_ERROR_S << "SQL Query: " << sql_topic_list;
250  sqlite3_finalize(stmt_topic_list);
251  return topicList;
252  }
253 
254  //Process until end of list is reached
255  while (error_code == SQLITE_ROW)
256  {
257  topicList.push_back(std::string(
258  reinterpret_cast<const char*>(sqlite3_column_text(stmt_topic_list, 0))));
259  error_code = sqlite3_step(stmt_topic_list);
260  }
261 
262  //Just for good practice
263  //State after reading all columns should be SQLITE_DONE
264  if (error_code != SQLITE_DONE)
265  {
266  ARMARX_ERROR_S << "Error reading topic list: " << sqlite3_errmsg(db);
267  ARMARX_ERROR_S << "SQL Query: " << sql_topic_list;
268  sqlite3_finalize(stmt_topic_list);
269  return topicList;
270  }
271 
272  sqlite3_finalize(stmt_topic_list);
273  return topicList;
274  }
275 
276  bool
278  {
279  //Make sure this is threadsafe
280  std::unique_lock lock(mutex);
281 
282  //Check if database is open
283  if (!database_open)
284  {
285  ARMARX_ERROR << "Can not seek to position if database is not open";
286  return false;
287  }
288 
289  //Reset statement
290  sqlite3_reset(stmt);
291  sqlite3_clear_bindings(stmt);
292 
293  //Reset end of database
294  end_of_database = false;
295 
296  //Bind ID
297  int error_code = sqlite3_bind_int(stmt, 1, ID);
298  if (error_code != SQLITE_OK)
299  {
300  ARMARX_ERROR_S << "Could not bind data while trying to seek: " << sqlite3_errmsg(db);
301  sqlite3_finalize(stmt);
302  sqlite3_close(db);
303  database_open = false;
304  return false;
305  }
306 
307  error_code = sqlite3_step(stmt);
308  //Expecting some results, otherwise we are at the end of database or something went wrong
309  if (error_code != SQLITE_ROW)
310  {
311  ARMARX_ERROR_S << "Query did not succeed or we got no result while trying to seek: "
312  << sqlite3_errmsg(db);
313  sqlite3_finalize(stmt);
314  sqlite3_close(db);
315  database_open = false;
316  return false;
317  }
318 
319  return true;
320  }
321 
322 } // namespace armarx
armarx::DatabaseTopicReader::DatabaseTopicReader
DatabaseTopicReader(std::filesystem::path path)
Definition: DatabaseTopicReader.cpp:31
DatabaseTopicReader.h
armarx::DatabaseTopicReader::getReplayTopics
std::vector< std::string > getReplayTopics() override
Definition: DatabaseTopicReader.cpp:222
armarx::TopicUtil::TopicData
Definition: TopicUtil.h:34
ARMARX_ERROR_S
#define ARMARX_ERROR_S
Definition: Logging.h:216
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
armarx::DatabaseTopicReader::~DatabaseTopicReader
~DatabaseTopicReader()
Definition: DatabaseTopicReader.cpp:79
armarx::DatabaseTopicReader::read
bool read(TopicUtil::TopicData &data) override
Definition: DatabaseTopicReader.cpp:93
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:196
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
armarx::DatabaseTopicReader::seekTo
bool seekTo(IceUtil::Time timestamp) override
Definition: DatabaseTopicReader.cpp:151
armarx::armem::server::ltm::mongodb::constantes::ID
const std::string ID
Definition: operations.h:15
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
armarx::DatabaseTopicReader::getReplayLength
IceUtil::Time getReplayLength() override
Definition: DatabaseTopicReader.cpp:193
armarx::DatabaseTopicReader::getFilepath
std::filesystem::path getFilepath() const
Definition: DatabaseTopicReader.cpp:87
Logging.h
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27