ios: added recovery feature

This commit is contained in:
matlabbe
2022-01-02 17:08:44 -05:00
parent fcde52c6bf
commit 3147b4ab56
7 changed files with 205 additions and 52 deletions

View File

@@ -64,6 +64,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/core/Memory.h> #include <rtabmap/core/Memory.h>
#include <rtabmap/core/GainCompensator.h> #include <rtabmap/core/GainCompensator.h>
#include <rtabmap/core/DBDriver.h> #include <rtabmap/core/DBDriver.h>
#include <rtabmap/core/Recovery.h>
#include <pcl/common/common.h> #include <pcl/common/common.h>
#include <pcl/filters/extract_indices.h> #include <pcl/filters/extract_indices.h>
#include <pcl/io/ply_io.h> #include <pcl/io/ply_io.h>
@@ -305,9 +306,9 @@ void RTABMapApp::setScreenRotation(int displayRotation, int cameraRotation)
} }
} }
int RTABMapApp::openDatabase(const std::string & databasePath, bool databaseInMemory, bool optimize, const std::string & databaseSource) int RTABMapApp::openDatabase(const std::string & databasePath, bool databaseInMemory, bool optimize, bool clearDatabase)
{ {
LOGW("Opening database %s (inMemory=%d, optimize=%d)", databasePath.c_str(), databaseInMemory?1:0, optimize?1:0); LOGW("Opening database %s (inMemory=%d, optimize=%d, clearDatabase=%d)", databasePath.c_str(), databaseInMemory?1:0, optimize?1:0, clearDatabase?1:0);
this->unregisterFromEventsManager(); // to ignore published init events when closing rtabmap this->unregisterFromEventsManager(); // to ignore published init events when closing rtabmap
status_.first = rtabmap::RtabmapEventInit::kInitializing; status_.first = rtabmap::RtabmapEventInit::kInitializing;
rtabmapMutex_.lock(); rtabmapMutex_.lock();
@@ -360,11 +361,11 @@ int RTABMapApp::openDatabase(const std::string & databasePath, bool databaseInMe
std::vector<std::vector<Eigen::Vector2f> > texCoords; std::vector<std::vector<Eigen::Vector2f> > texCoords;
#endif #endif
cv::Mat textures; cv::Mat textures;
if(!databaseSource.empty()) if(!databasePath.empty() && UFile::exists(databasePath) && !clearDatabase)
{ {
UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized cloud/mesh...")); UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading optimized cloud/mesh..."));
rtabmap::DBDriver * driver = rtabmap::DBDriver::create(); rtabmap::DBDriver * driver = rtabmap::DBDriver::create();
if(driver->openConnection(databaseSource)) if(driver->openConnection(databasePath))
{ {
cloudMat = driver->loadOptimizedMesh(&polygons, &texCoords, &textures); cloudMat = driver->loadOptimizedMesh(&polygons, &texCoords, &textures);
if(!cloudMat.empty()) if(!cloudMat.empty())
@@ -416,12 +417,10 @@ int RTABMapApp::openDatabase(const std::string & databasePath, bool databaseInMe
} }
UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading database...")); UEventsManager::post(new rtabmap::RtabmapEventInit(rtabmap::RtabmapEventInit::kInfo, "Loading database..."));
if(clearDatabase)
{
LOGI("Erasing database \"%s\"...", databasePath.c_str()); LOGI("Erasing database \"%s\"...", databasePath.c_str());
UFile::erase(databasePath); UFile::erase(databasePath);
if(!databaseSource.empty())
{
LOGI("Copying database source \"%s\" to \"%s\"...", databaseSource.c_str(), databasePath.c_str());
UFile::copy(databaseSource, databasePath);
} }
//Rtabmap //Rtabmap
@@ -2557,6 +2556,26 @@ void RTABMapApp::save(const std::string & databasePath)
} }
} }
bool RTABMapApp::recover(const std::string & from, const std::string & to)
{
std::string errorMsg;
if(!databaseRecovery(from, false, &errorMsg, &progressionStatus_))
{
LOGE("Recovery Error: %s", errorMsg.c_str());
return false;
}
else
{
LOGI("Renaming %s to %s", from.c_str(), to.c_str());
if(UFile::rename(from, to) != 0)
{
LOGE("Failed renaming %s to %s", from.c_str(), to.c_str());
return false;
}
return true;
}
}
void RTABMapApp::cancelProcessing() void RTABMapApp::cancelProcessing()
{ {
UWARN("Processing canceled!"); UWARN("Processing canceled!");

View File

@@ -75,7 +75,7 @@ class RTABMapApp : public UEventsHandler {
void setScreenRotation(int displayRotation, int cameraRotation); void setScreenRotation(int displayRotation, int cameraRotation);
int openDatabase(const std::string & databasePath, bool databaseInMemory, bool optimize, const std::string & databaseSource=std::string()); int openDatabase(const std::string & databasePath, bool databaseInMemory, bool optimize, bool clearDatabase);
bool isBuiltWith(int cameraDriver) const; bool isBuiltWith(int cameraDriver) const;
#ifdef __ANDROID__ #ifdef __ANDROID__
@@ -153,6 +153,7 @@ class RTABMapApp : public UEventsHandler {
void addEnvSensor(int type, float value); void addEnvSensor(int type, float value);
void save(const std::string & databasePath); void save(const std::string & databasePath);
bool recover(const std::string & from, const std::string & to);
void cancelProcessing(); void cancelProcessing();
bool exportMesh( bool exportMesh(
float cloudVoxelSize, float cloudVoxelSize,

View File

@@ -981,7 +981,7 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10; CURRENT_PROJECT_VERSION = 11;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 3RRB6NV8U9; DEVELOPMENT_TEAM = 3RRB6NV8U9;
EXCLUDED_ARCHS = ""; EXCLUDED_ARCHS = "";
@@ -1006,7 +1006,7 @@
"$(PROJECT_DIR)/RTABMapApp/Libraries/lib", "$(PROJECT_DIR)/RTABMapApp/Libraries/lib",
"$(PROJECT_DIR)/RTABMapApp/Libraries/share/OpenCV/3rdparty/lib", "$(PROJECT_DIR)/RTABMapApp/Libraries/share/OpenCV/3rdparty/lib",
); );
MARKETING_VERSION = 0.20.16; MARKETING_VERSION = "0.20.16-2";
OTHER_CFLAGS = ""; OTHER_CFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.introlab.rtabmap; PRODUCT_BUNDLE_IDENTIFIER = com.introlab.rtabmap;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1038,7 +1038,7 @@
CLANG_USE_OPTIMIZATION_PROFILE = NO; CLANG_USE_OPTIMIZATION_PROFILE = NO;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 10; CURRENT_PROJECT_VERSION = 11;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 3RRB6NV8U9; DEVELOPMENT_TEAM = 3RRB6NV8U9;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@@ -1063,7 +1063,7 @@
"$(PROJECT_DIR)/RTABMapApp/Libraries/lib", "$(PROJECT_DIR)/RTABMapApp/Libraries/lib",
"$(PROJECT_DIR)/RTABMapApp/Libraries/share/OpenCV/3rdparty/lib", "$(PROJECT_DIR)/RTABMapApp/Libraries/share/OpenCV/3rdparty/lib",
); );
MARKETING_VERSION = 0.20.16; MARKETING_VERSION = "0.20.16-2";
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = ""; OTHER_CFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.introlab.rtabmap; PRODUCT_BUNDLE_IDENTIFIER = com.introlab.rtabmap;

View File

@@ -69,24 +69,11 @@ void setScreenRotationNative(const void *object, int displayRotation)
} }
} }
int openDatabaseNative(const void *object, const char * databasePath, bool databaseInMemory, bool optimize) int openDatabaseNative(const void *object, const char * databasePath, bool databaseInMemory, bool optimize, bool clearDatabase)
{ {
if(object) if(object)
{ {
return native(object)->openDatabase(databasePath, databaseInMemory, optimize); return native(object)->openDatabase(databasePath, databaseInMemory, optimize, clearDatabase);
}
else
{
UERROR("object is null!");
return -1;
}
}
int openDatabase2Native(const void *object, const char * databaseSource, const char * databasePath, bool databaseInMemory, bool optimize)
{
if(object)
{
return native(object)->openDatabase(databasePath, databaseInMemory, optimize, databaseSource);
} }
else else
{ {
@@ -107,6 +94,31 @@ void saveNative(const void *object, const char * databasePath)
} }
} }
bool recoverNative(const void *object, const char * from, const char * to)
{
if(object)
{
return native(object)->recover(from, to);
}
else
{
UERROR("object is null!");
}
return false;
}
void cancelProcessingNative(const void *object)
{
if(object)
{
native(object)->cancelProcessing();
}
else
{
UERROR("object is null!");
}
}
int postProcessingNative(const void *object, int approach) int postProcessingNative(const void *object, int approach)
{ {
if(object) if(object)

View File

@@ -32,9 +32,10 @@ void setupCallbacksNative(const void *object, void * classPtr,
float, float, float, float, float, float)); float, float, float, float, float, float));
void destroyNativeApplication(const void *object); void destroyNativeApplication(const void *object);
void setScreenRotationNative(const void *object, int displayRotation); void setScreenRotationNative(const void *object, int displayRotation);
int openDatabaseNative(const void *object, const char * databasePath, bool databaseInMemory, bool optimize); int openDatabaseNative(const void *object, const char * databasePath, bool databaseInMemory, bool optimize, bool clearDatabase);
int openDatabase2Native(const void *object, const char * databaseSource, const char * databasePath, bool databaseInMemory, bool optimize);
void saveNative(const void *object, const char * databasePath); void saveNative(const void *object, const char * databasePath);
bool recoverNative(const void *object, const char * from, const char * to);
void cancelProcessingNative(const void * object);
int postProcessingNative(const void *object, int approach); int postProcessingNative(const void *object, int approach);
bool exportMeshNative( bool exportMeshNative(
const void *object, const void *object,

View File

@@ -113,17 +113,9 @@ class RTABMap {
setupGraphicNative(native_rtabmap, Int32(size.width), Int32(size.height)); setupGraphicNative(native_rtabmap, Int32(size.width), Int32(size.height));
} }
func openDatabase(databasePath:String, databaseInMemory:Bool, optimize:Bool) -> Int { func openDatabase(databasePath:String, databaseInMemory:Bool, optimize:Bool, clearDatabase: Bool) -> Int {
databasePath.utf8CString.withUnsafeBufferPointer { buffer -> Int in databasePath.utf8CString.withUnsafeBufferPointer { buffer -> Int in
return Int(openDatabaseNative(native_rtabmap, buffer.baseAddress, databaseInMemory, optimize)) return Int(openDatabaseNative(native_rtabmap, buffer.baseAddress, databaseInMemory, optimize, clearDatabase))
}
}
func openDatabase(databaseSource:String, databasePath:String, databaseInMemory:Bool, optimize:Bool) -> Int {
databasePath.utf8CString.withUnsafeBufferPointer { buffer -> Int in
databaseSource.utf8CString.withUnsafeBufferPointer { bufferSource -> Int in
return Int(openDatabase2Native(native_rtabmap, bufferSource.baseAddress, buffer.baseAddress, databaseInMemory, optimize))
}
} }
} }
@@ -133,6 +125,18 @@ class RTABMap {
} }
} }
func recover(from: String, to: String) -> Bool {
from.utf8CString.withUnsafeBufferPointer { bufferFrom -> Bool in
to.utf8CString.withUnsafeBufferPointer { bufferTo -> Bool in
return recoverNative(native_rtabmap, bufferFrom.baseAddress, bufferTo.baseAddress)
}
}
}
func cancelProcessing() {
cancelProcessingNative(native_rtabmap);
}
func postProcessing(approach: Int) -> Int { func postProcessing(approach: Int) -> Int {
return Int(postProcessingNative(native_rtabmap, Int32(approach))) return Int(postProcessingNative(native_rtabmap, Int32(approach)))
} }

View File

@@ -1428,9 +1428,114 @@ class ViewController: GLKViewController, ARSessionDelegate, RTABMapObserver, UIP
mMapNodes = 0; mMapNodes = 0;
self.openedDatabasePath = nil self.openedDatabasePath = nil
let tmpDatabase = self.getTmpDirectory().appendingPathComponent(self.RTABMAP_TMP_DB) let tmpDatabase = self.getTmpDirectory().appendingPathComponent(self.RTABMAP_TMP_DB)
let inMemory = UserDefaults.standard.bool(forKey: "DatabaseInMemory") let inMemory = UserDefaults.standard.bool(forKey: "DatabaseInMemory")
self.rtabmap!.openDatabase(databasePath: tmpDatabase.path, databaseInMemory: inMemory, optimize: false) if(!(self.mState == State.STATE_CAMERA || self.mState == State.STATE_MAPPING) &&
FileManager.default.fileExists(atPath: tmpDatabase.path) &&
tmpDatabase.fileSize > 1024*1024) // > 1MB
{
dismiss(animated: true, completion: {
let msg = "The previous session (\(tmpDatabase.fileSizeString)) was not correctly saved, do you want to recover it?"
let alert = UIAlertController(title: "Recovery", message: msg, preferredStyle: .alert)
let alertActionNo = UIAlertAction(title: "Ignore", style: .destructive) {
(UIAlertAction) -> Void in
do {
try FileManager.default.removeItem(at: tmpDatabase)
}
catch {
print("Could not clear tmp database: \(error)")
}
self.newScan()
}
alert.addAction(alertActionNo)
let alertActionCancel = UIAlertAction(title: "Cancel", style: .cancel) {
(UIAlertAction) -> Void in
// do nothing
}
alert.addAction(alertActionCancel)
let alertActionYes = UIAlertAction(title: "Yes", style: .default) {
(UIAlertAction2) -> Void in
let fileName = Date().getFormattedDate(format: "yyMMdd-HHmmss") + ".db"
let outputDbPath = self.getDocumentDirectory().appendingPathComponent(fileName).path
var indicator: UIActivityIndicatorView?
let alertView = UIAlertController(title: "Recovering", message: "Please wait while recovering data...", preferredStyle: .alert)
let alertViewActionCancel = UIAlertAction(title: "Cancel", style: .cancel) {
(UIAlertAction) -> Void in
self.dismiss(animated: true, completion: {
self.progressView = nil
indicator = UIActivityIndicatorView(style: .large)
indicator?.frame = CGRect(x: 0.0, y: 0.0, width: 60.0, height: 60.0)
indicator?.center = self.view.center
self.view.addSubview(indicator!)
indicator?.bringSubviewToFront(self.view)
indicator?.startAnimating()
self.rtabmap!.cancelProcessing();
})
}
alertView.addAction(alertViewActionCancel)
let previousState = self.mState
self.updateState(state: .STATE_PROCESSING);
self.present(alertView, animated: true, completion: {
// Add your progressbar after alert is shown (and measured)
let margin:CGFloat = 8.0
let rect = CGRect(x: margin, y: 84.0, width: alertView.view.frame.width - margin * 2.0 , height: 2.0)
self.progressView = UIProgressView(frame: rect)
self.progressView!.progress = 0
self.progressView!.tintColor = self.view.tintColor
alertView.view.addSubview(self.progressView!)
var success : Bool = false
DispatchQueue.background(background: {
success = self.rtabmap!.recover(from: tmpDatabase.path, to: outputDbPath)
}, completion:{
if(indicator != nil)
{
indicator!.stopAnimating()
indicator!.removeFromSuperview()
}
if self.progressView != nil
{
self.dismiss(animated: self.openedDatabasePath == nil, completion: {
if(success)
{
let alertSaved = UIAlertController(title: "Database saved!", message: String(format: "Database \"%@\" successfully recovered!", fileName), preferredStyle: .alert)
let yes = UIAlertAction(title: "OK", style: .default) {
(UIAlertAction) -> Void in
self.openDatabase(fileUrl: URL(fileURLWithPath: outputDbPath))
}
alertSaved.addAction(yes)
self.present(alertSaved, animated: true, completion: nil)
}
else
{
self.updateState(state: previousState);
self.showToast(message: "Recovery failed!", seconds: 4)
}
})
}
else
{
self.showToast(message: "Recovery canceled", seconds: 2)
self.updateState(state: previousState);
}
})
})
}
alert.addAction(alertActionYes)
self.present(alert, animated: true, completion: nil)
})
}
else
{
self.rtabmap!.openDatabase(databasePath: tmpDatabase.path, databaseInMemory: inMemory, optimize: false, clearDatabase: true)
if(!(self.mState == State.STATE_CAMERA || self.mState == State.STATE_MAPPING)) if(!(self.mState == State.STATE_CAMERA || self.mState == State.STATE_MAPPING))
{ {
@@ -1438,6 +1543,7 @@ class ViewController: GLKViewController, ARSessionDelegate, RTABMapObserver, UIP
self.startCamera(); self.startCamera();
} }
} }
}
func save() func save()
{ {
@@ -1519,12 +1625,19 @@ class ViewController: GLKViewController, ARSessionDelegate, RTABMapObserver, UIP
self.openedDatabasePath = URL(fileURLWithPath: filePath) self.openedDatabasePath = URL(fileURLWithPath: filePath)
let alert = UIAlertController(title: "Database saved!", message: String(format: "Database \"%@\" successfully saved on the SD-CARD!", fileName), preferredStyle: .alert) let alert = UIAlertController(title: "Database saved!", message: String(format: "Database \"%@\" successfully saved!", fileName), preferredStyle: .alert)
let yes = UIAlertAction(title: "OK", style: .default) { let yes = UIAlertAction(title: "OK", style: .default) {
(UIAlertAction) -> Void in (UIAlertAction) -> Void in
} }
alert.addAction(yes) alert.addAction(yes)
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
do {
let tmpDatabase = self.getTmpDirectory().appendingPathComponent(self.RTABMAP_TMP_DB)
try FileManager.default.removeItem(at: tmpDatabase)
}
catch {
print("Could not clear tmp database: \(error)")
}
self.updateDatabases() self.updateDatabases()
self.updateState(state: previousState) self.updateState(state: previousState)
}) })
@@ -1560,6 +1673,8 @@ class ViewController: GLKViewController, ARSessionDelegate, RTABMapObserver, UIP
indicator?.bringSubviewToFront(self.view) indicator?.bringSubviewToFront(self.view)
indicator?.startAnimating() indicator?.startAnimating()
self.rtabmap!.cancelProcessing()
}) })
})) }))
@@ -1664,6 +1779,7 @@ class ViewController: GLKViewController, ARSessionDelegate, RTABMapObserver, UIP
alertView.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in alertView.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in
self.dismiss(animated: true) self.dismiss(animated: true)
self.progressView = nil self.progressView = nil
self.rtabmap!.cancelProcessing()
})) }))
let previousState = mState let previousState = mState
@@ -1800,7 +1916,6 @@ class ViewController: GLKViewController, ARSessionDelegate, RTABMapObserver, UIP
stopMapping(ignoreSaving: true) stopMapping(ignoreSaving: true)
} }
let tmpDatabase = self.getTmpDirectory().appendingPathComponent(self.RTABMAP_TMP_DB)
openedDatabasePath = fileUrl; openedDatabasePath = fileUrl;
let fileName: String = self.openedDatabasePath!.lastPathComponent let fileName: String = self.openedDatabasePath!.lastPathComponent
@@ -1812,7 +1927,7 @@ class ViewController: GLKViewController, ARSessionDelegate, RTABMapObserver, UIP
updateState(state: .STATE_PROCESSING); updateState(state: .STATE_PROCESSING);
var status = 0 var status = 0
DispatchQueue.background(background: { DispatchQueue.background(background: {
status = self.rtabmap!.openDatabase(databaseSource: self.openedDatabasePath!.path, databasePath: tmpDatabase.path, databaseInMemory: true, optimize: false) status = self.rtabmap!.openDatabase(databasePath: self.openedDatabasePath!.path, databaseInMemory: true, optimize: false, clearDatabase: false)
}, completion:{ }, completion:{
// main thread // main thread
if(status == -1) { if(status == -1) {
@@ -1974,6 +2089,7 @@ class ViewController: GLKViewController, ARSessionDelegate, RTABMapObserver, UIP
alertView.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in alertView.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in
self.dismiss(animated: true) self.dismiss(animated: true)
self.progressView = nil self.progressView = nil
self.rtabmap!.cancelProcessing()
})) }))
let previousState = mState; let previousState = mState;