diff --git a/.gitignore b/.gitignore
index 0802dbea..243e1890 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,7 @@ tmp
# VS Code Specific
.devcontainer
.vscode
-.coverage
\ No newline at end of file
+.coverage
+
+# Jetbrains Specific
+.idea
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3f0d8161..8eaf28b0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,12 +56,12 @@ else()
add_subdirectory(tests)
add_subdirectory(src/vcl)
add_subdirectory(client/cpp)
- add_subdirectory(ext/custom_vcl)
add_subdirectory(distributed)
link_directories(/usr/local/lib /usr/lib/x86_64-linux-gnu/)
include_directories(/usr/include/jsoncpp utils/include/ src/pmgd/include src/pmgd/util include/ src/vcl /usr/include ${CMAKE_CURRENT_BINARY_DIR}/utils/src/protobuf)
add_library(dms SHARED
+ src/BackendNeo4j.cc
src/BoundingBoxCommand.cc
src/BlobCommand.cc
src/CommunicationManager.cc
@@ -69,11 +69,15 @@ else()
src/DescriptorsManager.cc
src/ExceptionsCommand.cc
src/ImageCommand.cc
+ src/Neo4jBaseCommands.cc
+ src/Neo4JHandlerCommands.cc
+ src/OpsIOCoordinator.cc
src/PMGDIterators.cc
src/PMGDQuery.cc
src/PMGDQueryHandler.cc
src/QueryHandlerExample.cc
src/QueryHandlerBase.cc
+ src/QueryHandlerNeo4j.cc
src/QueryHandlerPMGD.cc
src/QueryMessage.cc
src/RSCommand.cc
@@ -85,7 +89,7 @@ else()
src/ImageLoop.cc
src/VideoLoop.cc
)
- target_link_libraries(dms vcl pmgd pmgd-util protobuf tbb tiledb vdms-utils pthread -lcurl -lzmq ${AWSSDK_LINK_LIBRARIES})
+ target_link_libraries(dms vcl pmgd pmgd-util protobuf tbb tiledb vdms-utils pthread -lcurl -lzmq ${AWSSDK_LINK_LIBRARIES} neo4j-client)
add_executable(vdms src/vdms.cc)
target_link_libraries(vdms dms vdms_protobuf vcl tiledb faiss flinng jsoncpp ${OpenCV_LIBS} ${AWSSDK_LINK_LIBRARIES})
endif ()
diff --git a/INSTALL.md b/INSTALL.md
index e383cb55..821cb1b8 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -10,10 +10,10 @@ Here we will install the Debian/Ubuntu packages.
sudo apt-get update -y --fix-missing
sudo apt-get upgrade -y
sudo apt-get install -y --no-install-suggests --no-install-recommends \
- apt-transport-https autoconf automake bison build-essential bzip2 ca-certificates \
+ apt-transport-https automake bison build-essential bzip2 ca-certificates \
curl ed flex g++-9 gcc-9 git gnupg-agent javacc libarchive-tools libatlas-base-dev \
libavcodec-dev libavformat-dev libboost-all-dev libbz2-dev libc-ares-dev libcurl4-openssl-dev \
- libdc1394-22-dev libgflags-dev libgoogle-glog-dev libgtk-3-dev libgtk2.0-dev \
+ libncurses5-dev libdc1394-22-dev libgflags-dev libgoogle-glog-dev libgtk-3-dev libgtk2.0-dev \
libhdf5-dev libjpeg-dev libjsoncpp-dev libleveldb-dev liblmdb-dev \
liblz4-dev libopenblas-dev libopenmpi-dev libpng-dev librdkafka-dev libsnappy-dev libssl-dev \
libswscale-dev libtbb-dev libtbb2 libtiff-dev libtiff5-dev libtool libzmq3-dev linux-libc-dev mpich \
@@ -80,6 +80,20 @@ cd $VDMS_DEP_DIR/CMake
make ${BUILD_THREADS}
sudo make install
```
+***NOTE:*** If multiple versions of Python 3 are present on your system, verify you are using Python3.9 or higher. You can specify the specific verison in above command and also set the following with your specific version: `alias python3=/usr/bin/python3.x`.
+
+
+#### **Autoconf v2.71**
+```bash
+AUTOCONF_VERSION="2.71"
+curl -L -o $VDMS_DEP_DIR/autoconf-${AUTOCONF_VERSION}.tar.xz https://ftp.gnu.org/gnu/autoconf/autoconf-${AUTOCONF_VERSION}.tar.xz
+cd $VDMS_DEP_DIR
+tar -xf autoconf-${AUTOCONF_VERSION}.tar.xz
+cd autoconf-${AUTOCONF_VERSION}
+./configure
+make ${BUILD_THREADS}
+sudo make install
+```
#### **Protobuf v24.2 (4.24.2)**
@@ -90,23 +104,28 @@ git clone -b v${PROTOBUF_VERSION} --recurse-submodules https://github.com/protoc
cd $VDMS_DEP_DIR/protobuf/third_party/googletest
mkdir build && cd build
-cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local \
+cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/local \
-DBUILD_GMOCK=ON -DCMAKE_CXX_STANDARD=17 ..
make ${BUILD_THREADS}
sudo make install
-sudo ldconfig
cd $VDMS_DEP_DIR/protobuf/third_party/abseil-cpp
mkdir build && cd build
-cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_PREFIX_PATH=/usr/local/ -DCMAKE_INSTALL_PREFIX=/usr/local/ \
- -DABSL_BUILD_TESTING=ON -DABSL_ENABLE_INSTALL=ON -DABSL_USE_EXTERNAL_GOOGLETEST=ON \
+cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=ON \
+ -DCMAKE_INSTALL_PREFIX=/usr/local -DABSL_BUILD_TESTING=ON \
+ -DABSL_USE_EXTERNAL_GOOGLETEST=ON \
-DABSL_FIND_GOOGLETEST=ON -DCMAKE_CXX_STANDARD=17 ..
make ${BUILD_THREADS}
sudo make install
+sudo ldconfig /usr/local/lib
cd $VDMS_DEP_DIR/protobuf
-cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_CXX_STANDARD=17 \
- -Dprotobuf_ABSL_PROVIDER=package -DCMAKE_PREFIX_PATH=/usr/local .
+cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=/usr/local \
+ -DCMAKE_CXX_STANDARD=17 -Dprotobuf_BUILD_SHARED_LIBS=ON \
+ -Dprotobuf_ABSL_PROVIDER=package \
+ -Dprotobuf_BUILD_TESTS=ON \
+ -Dabsl_DIR=/usr/local/lib/cmake/absl .
make ${BUILD_THREADS}
sudo make install
@@ -114,14 +133,15 @@ python3 -m pip install --no-cache-dir "protobuf==4.${PROTOBUF_VERSION}"
```
-#### **Faiss v1.7.3**
+#### **Faiss v1.7.4**
Install the Faiss library for similarity search.
```bash
-FAISS_VERSION="v1.7.3"
+FAISS_VERSION="v1.7.4"
git clone --branch ${FAISS_VERSION} https://github.com/facebookresearch/faiss.git $VDMS_DEP_DIR/faiss
cd $VDMS_DEP_DIR/faiss
mkdir build && cd build
-cmake -DFAISS_ENABLE_GPU=OFF -DPython_EXECUTABLE=/usr/bin/python3 ..
+cmake -DFAISS_ENABLE_GPU=OFF -DPython_EXECUTABLE=/usr/bin/python3 \
+ -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release ..
make ${BUILD_THREADS}
sudo make install
```
@@ -192,6 +212,40 @@ cmake -D BUILD_PERF_TESTS=OFF -D BUILD_TESTS=OFF -D CMAKE_BUILD_TYPE=RELEASE -D
make ${BUILD_THREADS}
sudo make install
```
+
+
+#### **Neo4j Client**
+Below are instructions for installing ***libneo4j-omni*** which requires Peg, libcypher-parser and libedit as dependencies.
+```bash
+PEG_VERSION="0.1.19"
+curl -L -o $VDMS_DEP_DIR/peg-${PEG_VERSION}.tar.gz https://github.com/gpakosz/peg/releases/download/${PEG_VERSION}/peg-${PEG_VERSION}.tar.gz
+cd $VDMS_DEP_DIR/
+tar -xf peg-${PEG_VERSION}.tar.gz
+cd peg-${PEG_VERSION}
+make ${BUILD_THREADS}
+sudo make install
+
+git clone https://github.com/cleishm/libcypher-parser.git $VDMS_DEP_DIR/libcypher
+cd $VDMS_DEP_DIR/libcypher
+./autogen.sh
+./configure
+sudo make install
+
+LIBEDIT_VERSION="20230828-3.1"
+curl -L -o $VDMS_DEP_DIR/libedit-${LIBEDIT_VERSION}.tar.gz https://thrysoee.dk/editline/libedit-${LIBEDIT_VERSION}.tar.gz
+cd $VDMS_DEP_DIR/
+tar -xzf libedit-${LIBEDIT_VERSION}.tar.gz
+cd libedit-${LIBEDIT_VERSION}
+./configure
+make ${BUILD_THREADS}
+sudo make install
+
+git clone https://github.com/majensen/libneo4j-omni.git $VDMS_DEP_DIR/libomni
+cd $VDMS_DEP_DIR/libomni
+./autogen.sh
+./configure --disable-werror --prefix=/usr
+sudo make install -w --debug
+```
## Install VDMS
@@ -216,3 +270,10 @@ cmake -DCMAKE_CXX_FLAGS='-DPM' ..
make ${BUILD_THREADS}
```
+***NOTE:*** If error similar to `cannot open shared object file: No such file or directory` obtained during loading shared libraries, such as `libpmgd.so` or `libvcl.so`, add the correct directories to `LD_LIBRARY_PATH`. This may occur for non-root users. To find the correct directory, run `find` command for missing object file. An example solution for missing `libpmgd.so` and `libvcl.so` is:
+```bash
+find / -name "libpmgd*so*" # /build/src/pmgd/src
+find / -name "libvcl*so*" # /build/src/vcl
+export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/build/src/pmgd/src:/build/src/vcl
+```
+
diff --git a/client/cpp/CMakeLists.txt b/client/cpp/CMakeLists.txt
index 3cc3a302..83c7b900 100644
--- a/client/cpp/CMakeLists.txt
+++ b/client/cpp/CMakeLists.txt
@@ -1,13 +1,11 @@
cmake_minimum_required (VERSION 3.10)
project(vdms_client)
find_package( OpenCV REQUIRED )
+find_package(Protobuf CONFIG REQUIRED)
find_package( Threads REQUIRED )
include_directories(../../utils/include/ )
-add_library( vdms-client SHARED
- VDMSClient.cc
- CSVParserUtil.cpp
-)
+add_library(vdms-client SHARED VDMSClient.cc CSVParserUtil.cpp)
target_link_libraries(vdms-client protobuf vdms_protobuf vdms-utils pthread ${CMAKE_THREAD_LIBS_INIT} )
diff --git a/client/cpp/CSVParser.h b/client/cpp/CSVParser.h
index 401ae8f1..cb6c85f1 100644
--- a/client/cpp/CSVParser.h
+++ b/client/cpp/CSVParser.h
@@ -14,41 +14,52 @@ class CSVParser {
int port)
: m_filename(filename), m_num_threads(num_threads), vdms_server(server),
vdms_port(port) {}
+ ~CSVParser() {}
+
std::vector
parse_csv_lines(const std::string &filename, int start_line, int end_line,
- std::mutex &mutex, std::vector &all_results,
+ std::vector &local_results,
const size_t thread_id) {
- rapidcsv::Document csv(filename);
- size_t rowCount = csv.GetRowCount();
+ rapidcsv::Document csv(filename);
std::vector columnNames = csv.GetColumnNames();
VDMS::CSVParserUtil csv_util(vdms_server, vdms_port, columnNames,
static_cast(thread_id));
- for (int i = start_line; i < end_line; ++i) {
+ for (int i = start_line; i <= end_line - 1; ++i) {
std::vector row = csv.GetRow(i);
VDMS::Response result = csv_util.parse_row(row);
+ if (local_results.empty()) {
+ // If local_results is empty, resize it to have at least one element
+ local_results.resize(1);
+ } else if (i - start_line >= static_cast(local_results.size())) {
+ // If the index is beyond the current size, resize to accommodate the
+ // index
+ local_results.resize(i - start_line + 1);
+ }
- std::lock_guard lock(mutex);
- all_results.push_back(result);
+ // Replace the value at the specified index in local_results
+ local_results[i - start_line] = result;
}
- return all_results;
+ return local_results;
}
- std::vector parse() {
- auto start = std::chrono::high_resolution_clock::now();
+ std::vector parse() {
std::ifstream file(m_filename);
- if (!file) {
- std::cerr << "Error opening file\n";
+ if (!file.is_open()) {
+ std::cerr << "Error opening file: " << m_filename << std::endl;
}
-
int num_lines = std::count(std::istreambuf_iterator(file),
std::istreambuf_iterator(), '\n');
+ file.close();
+
std::size_t lines_per_thread = num_lines / m_num_threads;
std::mutex mutex;
std::vector threads;
- std::vector all_results;
+ std::vector> all_local_results(m_num_threads);
+ std::vector all_results; // Local vector for each thread
+ all_results.reserve(num_lines);
for (size_t i = 0; i < m_num_threads; i++) {
size_t start_line = i * lines_per_thread;
@@ -57,16 +68,24 @@ class CSVParser {
threads.emplace_back(&CSVParser::parse_csv_lines, this,
std::ref(m_filename), start_line, end_line,
- std::ref(mutex), std::ref(all_results), i);
+ std::ref(all_local_results[i]), i);
}
for (auto &thread : threads) {
thread.join();
}
+ size_t allResultsSizeBefore = all_results.size();
+ for (const auto &local_results : all_local_results) {
+
+ // Extend the size of all_results to accommodate local_results
+ all_results.resize(all_results.size() + local_results.size());
+
+ // Copy elements from local_results to the appropriate positions in
+ // all_results
+ std::copy(local_results.begin(), local_results.end(),
+ all_results.begin() + allResultsSizeBefore);
+ }
- auto finish = std::chrono::high_resolution_clock::now();
- std::chrono::duration elapsed = finish - start;
- // std::cout << "Elapsed time: " << elapsed.count() << " s\n";
return all_results;
}
diff --git a/client/cpp/CSVParserUtil.cpp b/client/cpp/CSVParserUtil.cpp
index d5b10dbe..2f9de106 100644
--- a/client/cpp/CSVParserUtil.cpp
+++ b/client/cpp/CSVParserUtil.cpp
@@ -11,7 +11,7 @@
#include
#include
#include
-static std::mutex barrier;
+
std::mutex mtx;
using namespace std;
using namespace std::chrono;
@@ -46,7 +46,6 @@ void VDMS::CSVParserUtil::initCommandsMap() {
{"RectangleUpdate", RectangleUpdate}};
}
string VDMS::CSVParserUtil::function_accessing_columnNames(int i) {
- std::lock_guard lock(mtx);
return _columnNames[i];
}
@@ -146,7 +145,6 @@ VDMS::CSVParserUtil::commandType
VDMS::CSVParserUtil::get_query_type(const string &str) {
CSVParserUtil::commandType querytype = commandType::UNKNOWN;
- std::lock_guard lock(CSVParserUtil::querytype_mutex);
std::map::iterator iter;
iter = commands.find(str);
if (iter != commands.end()) {
@@ -173,8 +171,6 @@ VDMS::CSVParserUtil::get_query_type(const string &str) {
querytype = commandType::AddBoundingBox;
break;
}
- // std::cout << " I executed queryType "<< querytype << std::endl;
- // return querytype;
}
return querytype;
@@ -200,9 +196,7 @@ void VDMS::CSVParserUtil::parseProperty(const string &columnNames,
const string &row,
const string &queryType,
Json::Value &aquery) {
- std::lock_guard lock(CSVParserUtil::aquery_mutex);
string propname = columnNames.substr(5, string::npos);
- // std::cout << "Inside parseProp " << propname < lock(CSVParserUtil::cons_mutex);
vector consvals = spiltrow(row);
string consname = consvals[0];
if (consname.substr(0, 5) == "date:") {
@@ -284,7 +277,6 @@ void VDMS::CSVParserUtil::parseOperations(string columnNames, string row,
string queryType,
Json::Value &aquery) {
- std::lock_guard lock(CSVParserUtil::ops_mutex);
string type = columnNames.substr(4, string::npos);
Json::Value opsjson;
vector opsKeys;
@@ -424,13 +416,6 @@ VDMS::CSVParserUtil::DATATYPE VDMS::CSVParserUtil ::isValidDataType(string data,
int type) {
CSVParserUtil::DATATYPE actualtype = getDataType(data, "");
return actualtype;
-
- // if(type==2 && (actualt=3 || acype=tualtype==4))//2 is for num
- // return actualtype;
- // else if(type==1 && (actualtype==1 || actualtype==2))//1 is for bool
- // return actualtype;
- // else
- // return -1;
}
bool VDMS::CSVParserUtil::isValidOpsType(string &type) {
@@ -542,6 +527,5 @@ VDMS::Response
VDMS::CSVParserUtil::send_to_vdms(const Json::Value &query,
const std::vector blobs) {
Json::StyledWriter _fastwriter;
-
return vdms_client->query(_fastwriter.write(query), blobs);
}
diff --git a/client/cpp/CSVParserUtil.h b/client/cpp/CSVParserUtil.h
index 9d223fe7..1aad7534 100644
--- a/client/cpp/CSVParserUtil.h
+++ b/client/cpp/CSVParserUtil.h
@@ -97,10 +97,6 @@ class CSVParserUtil {
std::string vdms_server;
int vdms_port;
std::vector _columnNames;
- std::mutex querytype_mutex;
- std::mutex aquery_mutex;
- std::mutex cons_mutex;
- std::mutex ops_mutex;
int id;
std::unique_ptr vdms_client;
};
diff --git a/client/python/setup.py b/client/python/setup.py
index a6c72e0f..1e8b3a14 100644
--- a/client/python/setup.py
+++ b/client/python/setup.py
@@ -5,7 +5,7 @@
setuptools.setup(
name="vdms",
- version="0.0.20",
+ version="0.0.21",
author="Chaunté W. Lacewell",
author_email="chaunte.w.lacewell@intel.com",
description="VDMS Client Module",
diff --git a/client/python/vdms/vdms.py b/client/python/vdms/vdms.py
index 9044858d..59c72595 100644
--- a/client/python/vdms/vdms.py
+++ b/client/python/vdms/vdms.py
@@ -26,48 +26,80 @@
#
#! /usr/bin/python
-import struct
-from threading import Thread
-import sys
import os
-import socket
-import urllib
-import time
+import ssl
+import sys
import json
+import time
+import urllib
+import socket
+import struct
+from threading import Thread
# VDMS Protobuf import (autogenerated)
from . import queryMessage_pb2
class vdms(object):
- def __init__(self):
+ def __init__(
+ self,
+ use_tls: bool = False,
+ ca_cert_file: str = "",
+ client_cert_file: str = "",
+ client_key_file: str = "",
+ ):
+ self.conn = None
+ self.sock = None
+ self.connected = False
+ self.use_tls = use_tls
+ self.ca_file = ca_cert_file
+ self.cert_file = client_cert_file
+ self.key_file = client_key_file
self.dataNotUsed = []
self.init_connection()
self.last_response = ""
def __del__(self):
- self.conn.close()
+ self.sock.close()
self.connected = False
def init_connection(self):
- if hasattr(self, "conn") and self.conn is not None:
- self.conn.close()
+ if hasattr(self, "conn") and self.sock is not None:
+ self.sock.close()
- self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.conn.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
# TCP_QUICKACK only supported in Linux 2.4.4+.
# We use startswith for checking the platform following Python's
# documentation:
# https://docs.python.org/dev/library/sys.html#sys.platform
if sys.platform.startswith("linux"):
- self.conn.setsockopt(socket.SOL_TCP, socket.TCP_QUICKACK, 1)
+ self.sock.setsockopt(socket.SOL_TCP, socket.TCP_QUICKACK, 1)
+
self.connected = False
def connect(self, host="localhost", port=55555):
if self.connected is False:
self.init_connection()
+
+ if self.use_tls:
+ context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
+ if self.ca_file != "":
+ context.load_verify_locations(cafile=self.ca_file)
+ if self.cert_file != "" and self.key_file != "":
+ context.load_cert_chain(
+ certfile=self.cert_file, keyfile=self.key_file
+ )
+ self.sock.settimeout(
+ 5
+ ) # 5 seconds timeout as the server will hang if a tls client attempts to connect to a non-tls enabled server
+ self.conn = context.wrap_socket(self.sock, server_hostname=host)
+ else:
+ self.conn = self.sock
+
self.conn.connect((host, port))
+
self.connected = True
return True
else:
@@ -86,9 +118,11 @@ def disconnect(self):
def is_connected(self):
return self.connected
- # Recieves a json struct as a string
- def query(self, query, blob_array=[]):
+ # Receives a json struct as a string
+ def query(self, query, blob_array=None):
# Check the query type
+ if blob_array is None:
+ blob_array = []
if not isinstance(query, str): # assumes json
query_str = json.dumps(query)
else:
diff --git a/config-vdms.json b/config-vdms.json
index 40b29d7c..c8318dbf 100755
--- a/config-vdms.json
+++ b/config-vdms.json
@@ -1,5 +1,8 @@
{
"port": 55555,
+// "cert_file": "cert.pem",
+// "key_file": "key.pem",
+// "ca_file": "ca.pem",
"autoreplicate_interval":-1, // it should be > 0
"unit":"s",
"max_simultaneous_clients": 100,
diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile
index 242937ec..16d17746 100644
--- a/docker/base/Dockerfile
+++ b/docker/base/Dockerfile
@@ -8,12 +8,27 @@ ARG BUILD_THREADS="-j16"
FROM debian:${BASE_VERSION} as base
# Dockerfile limitations force a repetition of global args
ARG BUILD_THREADS
+ARG AWS_ACCESS_KEY_ID=""
+ARG AWS_SECRET_ACCESS_KEY=""
+ARG NEO4J_USER=""
+ARG NEO4J_PASS=""
+ARG NEO4J_ENDPOINT=""
+ARG AWS_API_PORT=9000
+ARG AWS_CONSOLE_PORT=9001
ENV DEBIAN_FRONTEND=noninteractive
ENV DEBCONF_NOWARNINGS="yes"
ENV PROTOBUF_VERSION="24.2"
ENV NUMPY_MIN_VERSION="1.26.0"
+ENV AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}"
+ENV AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}"
+ENV NEO4J_USER="${NEO4J_USER}"
+ENV NEO4J_PASS="${NEO4J_PASS}"
+ENV NEO4J_ENDPOINT="${NEO4J_ENDPOINT}"
+ENV AWS_API_PORT="${AWS_API_PORT}"
+ENV AWS_CONSOLE_PORT="${AWS_CONSOLE_PORT}"
+
############################################################
# BUILD DEPENDENCIES
FROM base as build
@@ -21,11 +36,11 @@ FROM base as build
# Install Packages
# hadolint ignore=DL3008
RUN apt-get update -y && apt-get upgrade -y && \
- apt-get install -y --no-install-suggests --no-install-recommends --fix-missing \
- apt-transport-https autoconf automake bison build-essential bzip2 ca-certificates \
+ apt-get install -o 'Acquire::Retries=3' -y --no-install-suggests --no-install-recommends --fix-broken --fix-missing \
+ apt-transport-https automake bison build-essential bzip2 ca-certificates \
curl ed flex g++-9 gcc-9 git gnupg-agent javacc libarchive-tools libatlas-base-dev \
libavcodec-dev libavformat-dev libboost-all-dev libbz2-dev libc-ares-dev libcurl4-openssl-dev \
- libdc1394-22-dev libgflags-dev libgoogle-glog-dev libgtk-3-dev libgtk2.0-dev \
+ libncurses5-dev libdc1394-22-dev libgflags-dev libgoogle-glog-dev libgtk-3-dev libgtk2.0-dev \
libhdf5-dev libjpeg-dev libjpeg62-turbo-dev libjsoncpp-dev libleveldb-dev liblmdb-dev \
liblz4-dev libopenblas-dev libopenmpi-dev libpng-dev librdkafka-dev libsnappy-dev libssl-dev \
libswscale-dev libtbb-dev libtbb2 libtiff-dev libtiff5-dev libtool libzmq3-dev linux-libc-dev mpich \
@@ -40,10 +55,13 @@ RUN apt-get update -y && apt-get upgrade -y && \
WORKDIR /dependencies
ENV CMAKE_VERSION="v3.27.2" \
VALIJSON_VERSION="v0.6" \
- FAISS_VERSION="v1.7.3" \
+ FAISS_VERSION="v1.7.4" \
OPENCV_VERSION="4.5.5" \
TILEDB_VERSION="2.14.1" \
- AWS_SDK_VERSION="1.11.0"
+ AWS_SDK_VERSION="1.11.0" \
+ AUTOCONF_VERSION="2.71" \
+ PEG_VERSION="0.1.19" \
+ LIBEDIT_VERSION="20230828-3.1"
# hadolint ignore=DL3003
RUN python3 -m pip install --no-cache-dir "numpy>=${NUMPY_MIN_VERSION}" && \
@@ -56,26 +74,40 @@ RUN git clone --branch ${CMAKE_VERSION} https://github.com/Kitware/CMake.git /de
cd /dependencies/CMake && ./bootstrap && make ${BUILD_THREADS} && \
make install DESTDIR=/opt/dist && make install
+# AUTOCONF VERSION FOR NEO4J
+# hadolint ignore=DL3003,SC2086
+RUN curl -O https://ftp.gnu.org/gnu/autoconf/autoconf-${AUTOCONF_VERSION}.tar.xz && \
+ tar -xf autoconf-${AUTOCONF_VERSION}.tar.xz && cd autoconf-${AUTOCONF_VERSION} && \
+ ./configure && make ${BUILD_THREADS} && make install DESTDIR=/opt/dist && make install
+
# PROTOBUF & ITS DEPENDENCIES
# hadolint ignore=DL3003,SC2086
RUN git clone -b "v${PROTOBUF_VERSION}" --recurse-submodules https://github.com/protocolbuffers/protobuf.git /dependencies/protobuf && \
cd /dependencies/protobuf/third_party/googletest && mkdir build && cd build/ && \
- cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_GMOCK=ON -DCMAKE_CXX_STANDARD=17 .. && \
- make ${BUILD_THREADS} && make install DESTDIR=/opt/dist && make install && ldconfig && \
- cd /dependencies/protobuf/third_party/abseil-cpp && mkdir build && cd build && \
- cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_PREFIX_PATH=/usr/local/ -DCMAKE_INSTALL_PREFIX=/usr/local/ -DABSL_BUILD_TESTING=ON \
- -DABSL_ENABLE_INSTALL=ON -DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON -DCMAKE_CXX_STANDARD=17 .. && \
- make ${BUILD_THREADS} && make install DESTDIR=/opt/dist && make install && \
+ cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/opt/dist/usr/local \
+ -DBUILD_GMOCK=ON -DCMAKE_CXX_STANDARD=17 .. && \
+ make ${BUILD_THREADS} && make install && \
+ cd /dependencies/protobuf/third_party/abseil-cpp && mkdir build && cd build && \
+ cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=ON \
+ -DCMAKE_INSTALL_PREFIX=/opt/dist/usr/local -DABSL_BUILD_TESTING=ON \
+ -DABSL_USE_EXTERNAL_GOOGLETEST=ON \
+ -DABSL_FIND_GOOGLETEST=ON -DCMAKE_CXX_STANDARD=17 .. && \
+ make ${BUILD_THREADS} && make install && ldconfig /opt/dist/usr/local/lib && \
cd /dependencies/protobuf && \
- cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_CXX_STANDARD=17 \
- -Dprotobuf_ABSL_PROVIDER=package -DCMAKE_PREFIX_PATH=/usr/local . && \
- make ${BUILD_THREADS} && make install DESTDIR=/opt/dist && make install
+ cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=/opt/dist/usr/local \
+ -DCMAKE_CXX_STANDARD=17 -Dprotobuf_BUILD_SHARED_LIBS=ON \
+ -Dprotobuf_ABSL_PROVIDER=package \
+ -Dprotobuf_BUILD_TESTS=ON \
+ -Dabsl_DIR=/opt/dist/usr/local/lib/cmake/absl . && \
+ make ${BUILD_THREADS} && make install
# DESCRIPTOR LIBRARIES
# hadolint ignore=DL3003,SC2086
RUN git clone --branch ${FAISS_VERSION} https://github.com/facebookresearch/faiss.git /dependencies/faiss && \
cd /dependencies/faiss && mkdir build && cd build && \
- cmake -DFAISS_ENABLE_GPU=OFF -DPython_EXECUTABLE=/usr/bin/python3 .. && \
+ cmake -DFAISS_ENABLE_GPU=OFF -DPython_EXECUTABLE=/usr/bin/python3 \
+ -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release .. && \
make ${BUILD_THREADS} && make install DESTDIR=/opt/dist && make install && \
git clone https://github.com/tonyzhang617/FLINNG.git /dependencies/FLINNG && \
cd /dependencies/FLINNG && mkdir build && cd build && cmake .. && \
@@ -100,18 +132,36 @@ RUN git clone --branch ${OPENCV_VERSION} https://github.com/opencv/opencv.git /d
cd /dependencies/opencv && mkdir build && cd build && cmake -D BUILD_PERF_TESTS=OFF -D BUILD_TESTS=OFF .. && \
make ${BUILD_THREADS} && make install DESTDIR=/opt/dist && make install
+# LIB-OMNI FOR NEO4J QUERY HANDLER
+# hadolint ignore=DL3003,SC2086
+RUN curl -L -o /dependencies/peg-${PEG_VERSION}.tar.gz \
+ https://github.com/gpakosz/peg/releases/download/${PEG_VERSION}/peg-${PEG_VERSION}.tar.gz && \
+ cd /dependencies/ && tar -xf peg-${PEG_VERSION}.tar.gz && cd peg-${PEG_VERSION} && \
+ make ${BUILD_THREADS} && make install DESTDIR=/opt/dist && make install && \
+ git clone https://github.com/cleishm/libcypher-parser.git /dependencies/libcypher && \
+ cd /dependencies/libcypher && ./autogen.sh && ./configure && \
+ make install DESTDIR=/opt/dist && make install && \
+ curl -O https://thrysoee.dk/editline/libedit-${LIBEDIT_VERSION}.tar.gz && \
+ tar -xzf libedit-${LIBEDIT_VERSION}.tar.gz && cd libedit-${LIBEDIT_VERSION} && \
+ ./configure && make ${BUILD_THREADS} && make install DESTDIR=/opt/dist && make install && \
+ git clone https://github.com/majensen/libneo4j-omni.git /dependencies/libomni && \
+ cd /dependencies/libomni && ./autogen.sh && \
+ ./configure --disable-werror --prefix=/opt/dist/usr && \
+ make clean check && make install -w --debug
+
# CLEANUP
RUN rm -rf /dependencies /usr/local/share/doc /usr/local/share/man && \
mkdir -p /opt/dist/usr/include/x86_64-linux-gnu && \
cp -rp /usr/include/x86_64-linux-gnu /opt/dist/usr/include/x86_64-linux-gnu
+
############################################################
# FINAL IMAGE
FROM base
# hadolint ignore=DL3008
RUN apt-get update -y && apt-get upgrade -y && \
- apt-get install -y --no-install-suggests --no-install-recommends --fix-missing \
+ apt-get install -o 'Acquire::Retries=3' -y --no-install-suggests --no-install-recommends --fix-broken --fix-missing \
build-essential bzip2 curl g++-9 gcc-9 git javacc libarchive-tools libavcodec-dev libavformat-dev libcurl4-openssl-dev \
libdc1394-22-dev libgoogle-glog-dev libgtk-3-dev libgtk2.0-dev libhdf5-dev libjpeg-dev libjpeg62-turbo-dev libjsoncpp-dev libopenblas-dev \
libpng-dev librdkafka-dev libssl-dev libswscale-dev libtbb-dev libtbb2 libtiff-dev libtiff5-dev libzmq3-dev openjdk-11-jdk-headless procps python3-dev python3-pip && \
@@ -130,8 +180,9 @@ WORKDIR /vdms
RUN git clone -b develop --recurse-submodules https://github.com/IntelLabs/vdms.git /vdms && \
mkdir -p /vdms/build && cd /vdms/build && \
cmake .. && make ${BUILD_THREADS} && \
- cp /vdms/config-vdms.json /vdms/build/ && \
echo '#!/bin/bash' > /start.sh && echo 'cd /vdms/build' >> /start.sh && \
+ cp /vdms/docker/override_default_config.py /vdms/ && \
+ echo 'python3 /vdms/override_default_config.py -i /vdms/config-vdms.json -o /vdms/build/config-vdms.json' >> /start.sh && \
echo './vdms' >> /start.sh && chmod 755 /start.sh
ENV PYTHONPATH=/vdms/client/python:${PYTHONPATH}
diff --git a/docker/override_default_config.py b/docker/override_default_config.py
new file mode 100644
index 00000000..75b41c78
--- /dev/null
+++ b/docker/override_default_config.py
@@ -0,0 +1,112 @@
+#!/usr/bin/python3
+#
+# The MIT License
+#
+# @copyright Copyright (c) 2024 Intel Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify,
+# merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+import os
+import json
+import argparse
+from pathlib import Path
+
+
+class JSONCustomDecoder(json.JSONDecoder):
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+
+ def decode(self, str_to_decode: str):
+ comments_replaced = [
+ line if not line.lstrip().startswith("//") else ""
+ for line in str_to_decode.split("\n")
+ ]
+ comments_replaced = []
+ for line in str_to_decode.split("\n"):
+ if "//" not in line:
+ new_value = line
+ elif not line.lstrip().startswith("//") and "//" in line:
+ comment_idx = line.find("//")
+ new_value = line[:comment_idx]
+ else:
+ new_value = ""
+ comments_replaced.append(new_value)
+ str_to_decode = "\n".join(comments_replaced)
+ return super().decode(str_to_decode)
+
+
+def get_arguments():
+ """
+ PURPOSE: This function gets the arguments needed
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-i",
+ type=Path,
+ dest="config_path",
+ default="/vdms/config-vdms.json",
+ help="Path of config-vdms.json to update",
+ )
+ parser.add_argument(
+ "-o",
+ type=Path,
+ dest="out_path",
+ default="/vdms/build/config-vdms.json",
+ help="Path to write updated config-vdms.json",
+ )
+
+ params = parser.parse_args()
+ params.config_path = params.config_path.absolute()
+
+ return params
+
+
+def main(args):
+ with open(str(args.config_path), "r") as infile:
+ config = json.load(infile, cls=JSONCustomDecoder)
+
+ updated_params = []
+ for env_var, env_value in os.environ.items():
+ if env_var.startswith("OVERRIDE_"):
+ updated_config_key = env_var.replace("OVERRIDE_", "").lower()
+
+ try:
+ updated_config_value = int(env_value)
+ except:
+ updated_config_value = env_value
+
+ config[updated_config_key] = updated_config_value
+ updated_params.append(updated_config_key)
+
+ if len(updated_params) > 0:
+ print("Updating config parameters")
+ for key in updated_params:
+ new_value = config[key]
+ print(f"\t{key}: {new_value}")
+
+ with open(str(args.out_path), "w") as outfile:
+ json.dump(config, outfile, indent=4)
+
+
+if __name__ == "__main__":
+ args = get_arguments()
+ main(args)
diff --git a/ext/custom_vcl/CMakeLists.txt b/ext/custom_vcl/CMakeLists.txt
deleted file mode 100644
index ea543e85..00000000
--- a/ext/custom_vcl/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-cmake_minimum_required (VERSION 3.17)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
-set(CMAKE_CXX_STANDARD 17)
-project(custom_vcl_application)
-find_package( OpenCV REQUIRED )
-find_package(AWSSDK REQUIRED COMPONENTS s3)
-
-link_directories(/usr/local/lib /usr/lib/x86_64-linux-gnu/)
-include_directories(. ../../src ../../include/ ../../src/vcl /usr/include/jsoncpp ${OpenCV_INCLUDE_DIRS})
-add_executable(custom_vcl custom_vcl_process.cc )
-target_link_libraries(custom_vcl vcl tbb tiledb jsoncpp ${OpenCV_LIBS} ${AWSSDK_LINK_LIBRARIES})
\ No newline at end of file
diff --git a/ext/custom_vcl/LICENSE b/ext/custom_vcl/LICENSE
deleted file mode 100644
index ded58703..00000000
--- a/ext/custom_vcl/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2021 omp87
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/ext/custom_vcl/custom_vcl_process.cc b/ext/custom_vcl/custom_vcl_process.cc
deleted file mode 100644
index fffdc91b..00000000
--- a/ext/custom_vcl/custom_vcl_process.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-#include "vcl/CustomVCL.h"
-#include
-
-int main(int argc, char *argv[]) {
-
- // create IPC structures for communicating between processes
- key_t key_ctl_host_remote;
- key_ctl_host_remote = ftok("../../vdms", 60);
- int msgid_ctl_host_remote = msgget(key_ctl_host_remote, 0666 | IPC_CREAT);
- data_message message_ctl_host_remote;
- // need size of data message excluding message_type field for msgsnd and
- // msgrcv
- size_t data_message_size = sizeof(message_ctl_host_remote.data_rows) +
- sizeof(message_ctl_host_remote.data_cols) +
- sizeof(message_ctl_host_remote.data_type) +
- sizeof(message_ctl_host_remote.data_image_size) +
- sizeof(message_ctl_host_remote.data_json_size);
-
- key_t key_data_host_remote;
- key_data_host_remote = ftok("../../vdms", 61);
- int shmid_data_host_remote =
- shmget(key_data_host_remote, SHARED_IMAGE_BUFFER_SIZE, 0666 | IPC_CREAT);
- uint8_t *image_buffer =
- (uint8_t *)shmat(shmid_data_host_remote, (void *)0, 0);
-
- key_t key_ctl_remote_host;
- key_ctl_remote_host = ftok("../../vdms", 62);
- int msgid_ctl_remote_host = msgget(key_ctl_remote_host, 0666 | IPC_CREAT);
- ;
- data_message message_ctl_remote_host;
-
- heartbeat_message message_hb_host_remote;
- heartbeat_message message_hb_remote_host;
- size_t heartbeat_message_size = sizeof(message_hb_host_remote.status);
-
- while (true) {
- // Handle handshake to indicate remote process is alive
- int in_alive_msg_status = msgrcv(
- msgid_ctl_host_remote, &message_hb_host_remote, heartbeat_message_size,
- (long)vcl_message_type::VCL_MESSAGE_HEARTBEAT, 0);
-
- message_hb_remote_host.message_type =
- (long)vcl_message_type::VCL_MESSAGE_HEARTBEAT;
- message_hb_remote_host.status = 0;
- int msg_send_result = msgsnd(msgid_ctl_remote_host, &message_hb_remote_host,
- heartbeat_message_size, 0);
-
- int msg_status =
- msgrcv(msgid_ctl_host_remote, &message_ctl_host_remote,
- data_message_size, (long)vcl_message_type::VCL_MESSAGE_DATA, 0);
- if (msg_status > 0) {
- // Read image from shared memory
- cv::Mat *in_image = new cv::Mat(message_ctl_host_remote.data_rows,
- message_ctl_host_remote.data_cols,
- message_ctl_host_remote.data_type);
- memcpy((uint8_t *)&(in_image->data[0]), image_buffer,
- message_ctl_host_remote.data_image_size);
-
- // Read Json operands from shared memory and store into Json::Value
- char *json_string_char = new char[message_ctl_host_remote.data_json_size];
- memcpy(&(json_string_char[0]),
- &(image_buffer[message_ctl_host_remote.data_image_size]),
- message_ctl_host_remote.data_json_size);
- std::string *json_string = new std::string(json_string_char);
- Json::Value vcl_op;
- Json::Reader vcl_reader;
- bool parse_flag = vcl_reader.parse(json_string->c_str(), vcl_op);
- if (parse_flag) {
- // Manipulate Image
- if (vcl_op.get("custom_function_type", 0).asString() ==
- "hsv_threshold") {
- cv::cvtColor(*in_image, *in_image, cv::COLOR_RGB2HSV);
- cv::inRange(*in_image,
- cv::Scalar(vcl_op.get("h0", -1).asInt(),
- vcl_op.get("s0", -1).asInt(),
- vcl_op.get("v0", -1).asInt()),
- cv::Scalar(vcl_op.get("h1", -1).asInt(),
- vcl_op.get("s1", -1).asInt(),
- vcl_op.get("v1", -1).asInt()),
- *in_image);
-
- size_t in_image_size = in_image->total() * in_image->elemSize();
- memcpy(&(image_buffer[0]), &(in_image->data[0]), in_image_size);
-
- // Send Response back to host
- message_ctl_remote_host.message_type =
- (long)vcl_message_type::VCL_MESSAGE_DATA;
- message_ctl_remote_host.data_rows = in_image->rows;
- message_ctl_remote_host.data_cols = in_image->cols;
- message_ctl_remote_host.data_type = in_image->type();
- message_ctl_remote_host.data_image_size = in_image_size;
- message_ctl_remote_host.data_json_size = 0;
-
- } else {
- // Send Response back to host in event of error
- message_ctl_remote_host.message_type =
- (long)vcl_message_type::VCL_MESSAGE_DATA;
- message_ctl_remote_host.data_rows = 0;
- message_ctl_remote_host.data_cols = 0;
- message_ctl_remote_host.data_type = 0;
- message_ctl_remote_host.data_image_size = 0;
- message_ctl_remote_host.data_json_size = 0;
- }
- }
-
- int msg_send_result =
- msgsnd(msgid_ctl_remote_host, &message_ctl_remote_host,
- data_message_size, 0);
- if (msg_send_result < 0) {
- }
-
- // Free allocated memory
- delete in_image;
- delete[] json_string_char;
- delete json_string;
- }
- }
-
- // Free shared IPC components
- msgctl(msgid_ctl_host_remote, IPC_RMID, NULL);
- shmdt(image_buffer);
- shmctl(shmid_data_host_remote, IPC_RMID, NULL);
- return 0;
-}
\ No newline at end of file
diff --git a/ext/custom_vcl/sample_query/images/intel_logo.png b/ext/custom_vcl/sample_query/images/intel_logo.png
deleted file mode 100644
index 211d5b7a..00000000
Binary files a/ext/custom_vcl/sample_query/images/intel_logo.png and /dev/null differ
diff --git a/ext/custom_vcl/sample_query/sample_query.py b/ext/custom_vcl/sample_query/sample_query.py
deleted file mode 100644
index 4a2962bb..00000000
--- a/ext/custom_vcl/sample_query/sample_query.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import vdms
-
-db = vdms.vdms()
-db.connect("localhost", 55555)
-image_file = open("images/intel_logo.png", "rb")
-image_blob = image_file.read()
-addImage = {}
-addImage["format"] = "png"
-props = {}
-props["name"] = "intel_logo.png"
-addImage["properties"] = props
-operations = []
-operation = {}
-operation["type"] = "custom"
-operation["custom_function_type"] = "hsv_threshold"
-operation["h0"] = 10
-operation["s0"] = 250
-operation["v0"] = 175
-operation["h1"] = 20
-operation["s1"] = 255
-operation["v1"] = 185
-
-
-operations.append(operation)
-addImage["operations"] = operations
-query = {}
-query["AddImage"] = addImage
-print(query)
-res, blobs = db.query([query], [image_blob])
-print(res)
diff --git a/include/vcl/Image.h b/include/vcl/Image.h
index 5c52d34d..82f5c438 100644
--- a/include/vcl/Image.h
+++ b/include/vcl/Image.h
@@ -63,14 +63,18 @@ typedef cv::Rect Rectangle;
class Image {
public:
- enum class Format { NONE_IMAGE = 0, JPG = 1, PNG = 2, TDB = 3, BIN = 4 };
-
// enum class Storage { LOCAL = 0, AWS = 1 };
/* *********************** */
/* CONSTRUCTORS */
/* *********************** */
+ /**
+ * Default constructor, creates an empty Image object.
+ * Used when reading from the file system
+ */
+ Image();
+
/**
* Creates an Image object from the image id (where the
* image data can be found in the system).
@@ -81,6 +85,15 @@ class Image {
*/
Image(const std::string &image_id, std::string bucket_name = "");
+ /**
+ * Creates an Image object from the image id (where the
+ * image data can be found in the system).
+ *
+ * @param image_id The full path to the image
+ * @param no_blob If no blob is to be stored
+ */
+ Image(const std::string &image_id, bool no_blob);
+
/**
* Creates an Image object from the OpenCV Mat
*
@@ -101,6 +114,17 @@ class Image {
Image(void *buffer, long size, char raw_binary_file = 0,
int flags = cv::IMREAD_ANYCOLOR);
+ /**
+ * Creates an Image object from the image id (where the
+ * image data can be found in the system).
+ *
+ * @param fullpath The full path to the image
+ * @param blob A buffer that contains the image data
+ * @param input_forman is the format of the input image
+ */
+ Image(const std::string &fullpath, const std::string &blob,
+ VCL::Format input_format);
+
/**
* Creates a TDB Image object from a buffer of raw pixel data
*
@@ -163,7 +187,7 @@ class Image {
*
* @return The Format of the Image object
*/
- VCL::Image::Format get_image_format() const;
+ VCL::Format get_image_format() const;
/**
* Gets the size of the image in pixels (height * width * channels)
@@ -216,12 +240,12 @@ class Image {
void get_raw_data(void *buffer, long buffer_size, bool performOp = true);
/**
- * Gets encoded image data in a buffer
- *
- * @param format The Format the Image should be encoded as
- * @param params Optional parameters
- * @return A vector containing the encoded image
- * @see OpenCV documentation for imencode for more details
+ * Gets encoded image data in a buffer
+ *
+ * @param format The Format the Image should be encoded as
+ * @param params Optional parameters
+ * @return A vector containing the encoded image
+ * @see OpenCV documentation for imencode for more details
/**
* Gets encoded image data in a buffer
@@ -232,7 +256,7 @@ class Image {
* @see OpenCV documentation for imencode for more details
*/
std::vector
- get_encoded_image(VCL::Image::Format format,
+ get_encoded_image(VCL::Format format,
const std::vector ¶ms = std::vector());
/**
@@ -245,7 +269,7 @@ class Image {
* @see OpenCV documentation for imencode for more details
*/
std::vector
- get_encoded_image_async(VCL::Image::Format format,
+ get_encoded_image_async(VCL::Format format,
const std::vector ¶ms = std::vector());
/**
@@ -275,6 +299,13 @@ class Image {
*/
std::string get_query_error_response();
+ /**
+ * Checks if a blob is stored for the image or not
+ *
+ * @return True if blob is stored
+ */
+ bool is_blob_not_stored() const;
+
/* *********************** */
/* SET FUNCTIONS */
/* *********************** */
@@ -323,7 +354,15 @@ class Image {
/* *********************** */
/* IMAGE INTERACTIONS */
/* *********************** */
+ /**
+ * Writes the Image to the system at the given location from blob data
+ *
+ * @param _fullpath Full path to where the image should be written
+ * @param blob buffuer that conatinas the actual image
+ *
+ */
+ void save_image(const std::string &_fullpath, const std::string &blob);
/**
* Writes the Image to the system at the given location and in
* the given format
@@ -334,7 +373,8 @@ class Image {
* image metadata. Defaults to true (assuming no other metadata
* storage)
*/
- void store(const std::string &image_id, VCL::Image::Format image_format,
+
+ void store(const std::string &image_id, VCL::Format image_format,
bool store_metadata = true);
/**
@@ -423,7 +463,7 @@ class Image {
* Deletes the Image as well as removes file from system if
* it exists
*/
- void delete_image();
+ bool delete_image();
/* *********************** */
/* COPY FUNCTIONS */
/* *********************** */
@@ -449,15 +489,7 @@ class Image {
*/
template void copy_to_buffer(T *buffer);
- std::string format_to_string(Image::Format format);
-
private:
- /**
- * Default constructor, creates an empty Image object.
- * Used when reading from the file system
- */
- Image();
-
// Forward declaration of Operation class, to be used of _operations
// list
class Operation;
@@ -495,6 +527,10 @@ class Image {
// Full path to image
std::string _image_id;
+ // No blob stored. The file path is stored instead
+ // and is accessed locally or over the network.
+ bool _no_blob = false;
+
// Query Error response
std::string _query_error_response = "";
@@ -512,18 +548,18 @@ class Image {
* Performs the set of operations that have been requested
* on the Image
*/
+
void perform_operations();
/**
* Creates full path to Image with appropriate extension based
- * on the Image::Format
+ * on the VCL::Format
*
* @param filename The path to the Image object
- * @param format The Image::Format of the Image object
+ * @param format The VCL::Format of the Image object
* @return Full path to the object including extension
*/
- std::string create_fullpath(const std::string &filename,
- Image::Format format);
+ std::string create_fullpath(const std::string &filename, VCL::Format format);
/* *********************** */
/* OPERATION */
@@ -562,7 +598,7 @@ class Image {
* @param format The format for the operation
* @see Image.h for more details on Format
*/
- Operation(Format format) : _format(format){};
+ Operation(VCL::Format format) : _format(format){};
public:
/**
@@ -595,7 +631,7 @@ class Image {
* @param format The format to read the image from
* @see Image.h for more details on ::Format
*/
- Read(const std::string &filename, Format format);
+ Read(const std::string &filename, VCL::Format format);
/**
* Reads an image from the file system (based on the format
@@ -665,7 +701,7 @@ class Image {
* @param format The current format of the image data
* @see Image.h for more details on ::Format and Rectangle
*/
- Resize(const Rectangle &rect, Format format)
+ Resize(const Rectangle &rect, VCL::Format format)
: Operation(format), _rect(rect){};
/**
@@ -698,7 +734,7 @@ class Image {
* @param format The current format of the image data
* @see Image.h for more details on ::Format and Rectangle
*/
- Crop(const Rectangle &rect, Format format)
+ Crop(const Rectangle &rect, VCL::Format format)
: Operation(format), _rect(rect){};
/**
@@ -731,7 +767,7 @@ class Image {
* @param format The current format of the image data
* @see Image.h for more details on ::Format
*/
- Threshold(const int value, Format format)
+ Threshold(const int value, VCL::Format format)
: Operation(format), _threshold(value){};
/**
@@ -762,7 +798,7 @@ class Image {
* @param format The current format of the image data
* @see Image.h for more details on ::Format
*/
- Flip(const int code, Format format) : Operation(format), _code(code){};
+ Flip(const int code, VCL::Format format) : Operation(format), _code(code){};
/**
* Performs the flip operation
@@ -792,7 +828,7 @@ class Image {
* @param format The current format of the image data
* @see Image.h for more details on Format
*/
- Rotate(float angle, bool keep_size, Format format)
+ Rotate(float angle, bool keep_size, VCL::Format format)
: Operation(format), _angle(angle), _keep_size(keep_size){};
/**
@@ -824,7 +860,8 @@ class Image {
* @param options
* @see Image.h for more details on Format
*/
- SyncRemoteOperation(std::string url, Json::Value options, Format format)
+ SyncRemoteOperation(std::string url, Json::Value options,
+ VCL::Format format)
: Operation(format), _url(url), _options(options){};
/**
@@ -858,7 +895,7 @@ class Image {
* @param options
* @see Image.h for more details on Format
*/
- RemoteOperation(std::string url, Json::Value options, Format format)
+ RemoteOperation(std::string url, Json::Value options, VCL::Format format)
: Operation(format), _url(url), _options(options){};
/**
@@ -888,7 +925,7 @@ class Image {
* @param options
* @see Image.h for more details on Format
*/
- UserOperation(Json::Value options, Format format)
+ UserOperation(Json::Value options, VCL::Format format)
: Operation(format), _options(options){};
/**
@@ -939,8 +976,8 @@ class Image {
* Sets the format of the Image object
*
* @param extension A string containing the file system
- * extension corresponding to the desired Image::Format
- * @see Image.h for more details on Image::Format
+ * extension corresponding to the desired VCL::Format
+ * @see Image.h for more details on VCL::Format
*/
void set_format(const std::string &extension);
};
diff --git a/include/vcl/RemoteConnection.h b/include/vcl/RemoteConnection.h
index 1b5f5f5f..6071513a 100644
--- a/include/vcl/RemoteConnection.h
+++ b/include/vcl/RemoteConnection.h
@@ -52,13 +52,13 @@ class RemoteConnection {
RemoteConnection();
~RemoteConnection();
- void Write(const std::string &path, std::vector data);
- void Write(const std::string &filename);
+ bool Write(const std::string &path, std::vector data);
+ bool Write(const std::string &filename);
std::vector Read(const std::string &path);
- void RetrieveFile(const std::string &filename);
+ bool RetrieveFile(const std::string &filename);
std::vector ListFilesInFolder(const std::string &folder_name);
- void Read_Video(const std::string &path);
- void Remove_Object(const std::string &path);
+ bool Read_Video(const std::string &path);
+ bool Remove_Object(const std::string &path);
void start();
void end();
bool connected() { return _remote_connected; };
@@ -73,13 +73,15 @@ class RemoteConnection {
void ConfigureAws();
void ShutdownAws();
- void write_s3(const std::string &path, std::vector data);
- void write_s3(const std::string &filename);
+ bool write_s3(const std::string &path, std::vector data);
+ bool write_s3(const std::string &filename);
std::vector read_s3(const std::string &path);
- void retrieve_file(const std::string &filename);
+ bool retrieve_file(const std::string &filename);
std::vector get_file_list(const std::string &path);
- void read_s3_video(const std::string &file_path);
- void remove_s3_object(const std::string &file_path);
+ bool read_s3_video(const std::string &file_path);
+ bool remove_s3_object(const std::string &file_path);
+ void printErrorMessage(const std::string &functionName,
+ const std::string &errorMessage = "");
// void LogEntry(std::string functionName);
};
} // namespace VCL
diff --git a/include/vcl/Video.h b/include/vcl/Video.h
index 0c34424a..1bb5a753 100644
--- a/include/vcl/Video.h
+++ b/include/vcl/Video.h
@@ -96,7 +96,7 @@ class Video {
*
* @param video_id A string indicating where the Video is on disk
*/
- Video(const std::string &video_id);
+ Video(const std::string &video_id, bool no_blob = false);
/**
* Creates an Video object from an existing Video object
@@ -245,6 +245,13 @@ class Video {
*/
std::string get_operated_video_id();
+ /**
+ * Checks if a blob is stored for the video or not
+ *
+ * @return True if blob is stored
+ */
+ bool is_blob_not_stored() const;
+
/* *********************** */
/* SET FUNCTIONS */
/* *********************** */
@@ -426,6 +433,10 @@ class Video {
// Full path to the temporary video file on which operations are performed.
std::string _operated_video_id;
+ // No blob stored. The file path is stored instead
+ // and is accessed locally or over the network.
+ bool _no_blob = false;
+
// Query Error response
std::string _query_error_response = "";
diff --git a/include/vcl/utils.h b/include/vcl/utils.h
index 10fc8ff2..33774503 100644
--- a/include/vcl/utils.h
+++ b/include/vcl/utils.h
@@ -42,7 +42,6 @@ typedef std::vector cv_buffer;
/**
* Determines what kind of compression to use
*/
-
enum class CompressionType {
NOCOMPRESSION = 0,
GZIP = 1,
@@ -56,6 +55,13 @@ enum class CompressionType {
BZSTD = 9,
RLE = 10
};
+/* *********************** */
+/* ENUMS */
+/* *********************** */
+/**
+ * Determines what kind of format to use
+ */
+enum class Format { NONE_IMAGE = 0, JPG = 1, PNG = 2, TDB = 3, BIN = 4 };
static const struct init_rand_t {
init_rand_t() { srand(time(NULL)); }
@@ -66,7 +72,14 @@ uint64_t rdrand();
bool supports_rdrand();
uint64_t get_uint64();
+/* a util function to covert the enum format value to string*/
+std::string format_to_string(VCL::Format format);
+/**
+ * Save the image directly as a blob file without the need to re-encoding it
+ * with cv::imwrite
+ */
+void save_image(const std::string &_fullpath, const std::string &blob);
/**
* Gets the extension of a filename
*
@@ -86,4 +99,9 @@ bool exists(const std::string &name);
std::string create_unique(const std::string &path,
const std::string &extension);
+/**
+ * Determines what is the format of the input blob image using the signature
+ * value of PNG and JPG format
+ */
+Format read_image_format(void *buffer, long size);
}; // namespace VCL
diff --git a/remote_function/requirements.txt b/remote_function/requirements.txt
index 03c7d0e0..60864807 100644
--- a/remote_function/requirements.txt
+++ b/remote_function/requirements.txt
@@ -1,5 +1,5 @@
opencv-python==4.5.5.64
-flask==2.3.3
-numpy==1.26.0
+flask==3.0.2
+numpy==1.26.4
sk-video==1.1.10
imutils==0.5.4
\ No newline at end of file
diff --git a/src/BackendNeo4j.cc b/src/BackendNeo4j.cc
new file mode 100644
index 00000000..a981e968
--- /dev/null
+++ b/src/BackendNeo4j.cc
@@ -0,0 +1,228 @@
+#include "BackendNeo4j.h"
+#include
+
+int val_check(neo4j_value_t val) {
+
+ if (neo4j_instanceof(val, NEO4J_BOOL)) {
+ return NEO4J_BOOL;
+ } else if (neo4j_instanceof(val, NEO4J_STRING)) {
+ return NEO4J_STRING;
+ } else if (neo4j_instanceof(val, NEO4J_NULL)) {
+ return NEO4J_NULL;
+ } else if (neo4j_instanceof(val, NEO4J_FLOAT)) {
+ return NEO4J_FLOAT;
+ } else if (neo4j_instanceof(val, NEO4J_BYTES)) {
+ return NEO4J_BYTES;
+ } else if (neo4j_instanceof(val, NEO4J_INT)) {
+ return NEO4J_INT;
+ }
+
+ return -1;
+}
+/*Hat Tip
+ * https://stackoverflow.com/questions/4800605/iterating-through-objects-in-jsoncpp*/
+void print_val(neo4j_value_t val, int val_type) {
+ unsigned int nr_bytes;
+ char strbuf[8192];
+ char *strptr;
+
+ if (val_type == NEO4J_BOOL) {
+ printf("%d\n", neo4j_bool_value(val));
+ } else if (val_type == NEO4J_STRING) {
+ strptr = neo4j_string_value(val, strbuf, 8192);
+ printf("%s\n", strptr);
+ } else if (val_type == NEO4J_NULL) {
+ printf("NULL\n");
+ } else if (val_type == NEO4J_FLOAT) {
+ printf("%f\n", neo4j_float_value(val));
+ } else if (val_type == NEO4J_BYTES) {
+ printf("\n");
+ } else if (val_type == NEO4J_INT) {
+ printf("%lld\n", neo4j_int_value(val));
+ }
+}
+
+// constructor
+BackendNeo4j::BackendNeo4j(unsigned int nr_conns, char *tgt_url, char *user,
+ char *pass, uint_fast32_t flags) {
+
+ const char *conn_error;
+
+ // Client initialization
+ neo4j_client_init();
+
+ // initializing configuration structure: will be re-used
+ config = neo4j_new_config();
+ neo4j_config_set_username(config, user);
+ neo4j_config_set_password(config, pass);
+ neo4j_config_set_supported_versions(config, "4,5");
+
+ // initialize connection pool
+ for (int i = 0; i < nr_conns; i++) {
+ neo4j_connection_t *connection = neo4j_connect(tgt_url, config, flags);
+
+ if (connection == NULL) {
+ printf("Warning: Connection failed to instantiate!\n");
+ printf("Errno: %d\n", errno);
+ conn_error = neo4j_strerror(errno, NULL, 0);
+ printf("%s\n", conn_error);
+ exit(1);
+ }
+
+ conn_pool.push(connection);
+ }
+}
+
+// For Putting/retrieving connections
+neo4j_connection_t *BackendNeo4j::get_conn() {
+
+ neo4j_connection_t *conn;
+ // TODO need to understand functionality when all conns are taken
+ conn_pool.pop(conn);
+ return conn;
+}
+void BackendNeo4j::put_conn(neo4j_connection_t *connection) {
+ conn_pool.push(connection);
+}
+
+int BackendNeo4j::nr_avail_conn() { return conn_pool.size(); }
+
+// Transaction Helpers
+neo4j_transaction_t *BackendNeo4j::open_tx(neo4j_connection_t *connection,
+ int timeout_ms, char *mode) {
+ neo4j_transaction_t *my_tx;
+ my_tx = neo4j_begin_tx(connection, timeout_ms, mode, tgt_db.c_str());
+ return my_tx;
+}
+neo4j_result_stream_t *BackendNeo4j::run_in_tx(char *cypher_string,
+ neo4j_transaction_t *tx) {
+
+ neo4j_result_stream_t *results =
+ neo4j_run_in_tx(tx, cypher_string, neo4j_null);
+
+ return results;
+}
+int BackendNeo4j::commit_tx(neo4j_transaction_t *tx) {
+ int rc = 0;
+ rc = neo4j_commit(tx);
+ neo4j_free_tx(tx);
+
+ return rc;
+}
+
+int BackendNeo4j::rollback_tx(neo4j_transaction *tx) {
+ int rc = 0;
+ rc = neo4j_rollback(tx);
+
+ return rc;
+}
+
+// Result helpers
+Json::Value BackendNeo4j::results_to_json(neo4j_result_stream_t *results) {
+
+ neo4j_result_t *res;
+ neo4j_value_t res_val;
+ int val_type;
+ char *fname_ptr;
+ unsigned int nr_fields;
+ char *fields[32];
+ int val_types[32];
+ Json::Value agg_response;
+ Json::Value row_resp;
+ char *kv_val_ptr;
+ int rowctr;
+ char strbuf[4096];
+ char *strptr;
+ char *retstr;
+ Json::StyledWriter styled;
+
+ nr_fields = neo4j_nfields(results);
+
+ for (unsigned int i = 0; i < nr_fields; i++) {
+ fname_ptr = (char *)neo4j_fieldname(results, i);
+ fields[i] = fname_ptr;
+ }
+
+ rowctr = 0;
+ while (true) {
+
+ res = neo4j_fetch_next(results);
+ if (res == NULL) {
+ break;
+ }
+
+ for (unsigned int i = 0; i < nr_fields; i++) {
+ res_val = neo4j_result_field(res, i);
+ val_type = val_check(res_val);
+ fname_ptr = fields[i];
+ std::string cpp_fname = fname_ptr;
+
+ if (val_type == NEO4J_BOOL) {
+ row_resp[rowctr][cpp_fname] = neo4j_bool_value(res_val);
+ } else if (val_type == NEO4J_STRING) {
+ kv_val_ptr = neo4j_string_value(res_val, strbuf, 8192);
+ std::string cpp_val = kv_val_ptr;
+ row_resp[rowctr][cpp_fname] = cpp_val;
+ } else if (val_type == NEO4J_NULL) {
+ row_resp[rowctr][cpp_fname] = Json::nullValue;
+ } else if (val_type == NEO4J_FLOAT) {
+ row_resp[rowctr][cpp_fname] = neo4j_float_value(res_val);
+ } else if (val_type == NEO4J_BYTES) {
+ printf("\n");
+ } else if (val_type == NEO4J_INT) {
+ row_resp[rowctr][cpp_fname] =
+ (Json::Value::Int64)neo4j_int_value(res_val);
+ }
+ }
+ rowctr++;
+ }
+
+ // add rows to primary results structure
+ agg_response["metadata_res"] = row_resp;
+
+ return agg_response;
+}
+void BackendNeo4j::print_results_stream(neo4j_result_stream_t *results) {
+
+ neo4j_result_t *res;
+ neo4j_value_t res_val;
+ int val_type;
+ char *fname_ptr;
+ unsigned int nr_fields;
+ char *fields[32];
+
+ nr_fields = neo4j_nfields(results);
+
+ for (unsigned int i = 0; i < nr_fields; i++) {
+ fname_ptr = (char *)neo4j_fieldname(results, i);
+ printf("Field: %s\n", neo4j_fieldname(results, i));
+ fields[i] = fname_ptr;
+ }
+
+ while (true) {
+ printf("---Result Row---\n");
+ res = neo4j_fetch_next(results);
+
+ if (res == NULL) {
+ break;
+ }
+
+ for (unsigned int i = 0; i < nr_fields; i++) {
+ printf("Fieldname: %s\n", fields[i]);
+ res_val = neo4j_result_field(res, i);
+ val_type = val_check(res_val);
+ print_val(res_val, val_type);
+ }
+ }
+}
+
+BackendNeo4j::~BackendNeo4j() {
+
+ neo4j_connection_t *conn;
+ // TODO need to understand functionality when all conns are taken
+ while (conn_pool.size() > 0) {
+ conn_pool.pop(conn);
+ neo4j_close(conn);
+ }
+ neo4j_client_cleanup();
+}
\ No newline at end of file
diff --git a/src/BackendNeo4j.h b/src/BackendNeo4j.h
new file mode 100644
index 00000000..d8b321b6
--- /dev/null
+++ b/src/BackendNeo4j.h
@@ -0,0 +1,47 @@
+//
+// Created by ifadams on 9/28/2023.
+//
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+void print_val(neo4j_value_t val, int val_type);
+int val_check(neo4j_value_t val);
+
+class BackendNeo4j {
+
+ tbb::concurrent_bounded_queue conn_pool;
+ neo4j_config_t *config;
+ std::string tgt_db;
+
+public:
+ // constructor
+ BackendNeo4j(unsigned int nr_conns, char *tgt_url, char *user, char *pass,
+ uint_fast32_t flags);
+
+ // For Putting/retrieving connections
+ neo4j_connection_t *get_conn();
+ void put_conn(neo4j_connection_t *connection);
+ int nr_avail_conn();
+
+ // Transaction Helpers
+ neo4j_transaction_t *open_tx(neo4j_connection_t *connection, int timeout_ms,
+ char *mode);
+ neo4j_result_stream_t *run_in_tx(char *cypher_string,
+ neo4j_transaction_t *tx);
+ int commit_tx(neo4j_transaction_t *tx);
+ int rollback_tx(neo4j_transaction_t *tx);
+
+ // Result helpers
+ Json::Value results_to_json(neo4j_result_stream_t *results);
+ void print_results_stream(neo4j_result_stream_t *results);
+
+ // cleanup/desctructors
+ ~BackendNeo4j();
+};
diff --git a/src/BlobCommand.cc b/src/BlobCommand.cc
index eae93672..15d7209a 100644
--- a/src/BlobCommand.cc
+++ b/src/BlobCommand.cc
@@ -59,7 +59,7 @@ int AddBlob::construct_protobuf(PMGDQuery &query, const Json::Value &jsoncmd,
VCL::Image img((void *)blob.data(), blob.size(), binary_img_flag);
std::string blob_root = _storage_bin;
- VCL::Image::Format blob_format = VCL::Image::Format::BIN;
+ VCL::Format blob_format = VCL::Format::BIN;
std::string file_name = VCL::create_unique(blob_root, format);
// std::cout << "Blob was added in " <<_storage_bin << "\t"<< file_name <<
// std::endl;
@@ -171,7 +171,7 @@ Json::Value FindBlob::construct_responses(Json::Value &responses,
// We will return the image in the format the user
// request, or on its format in disk, except for the case
// of .tdb, where we will encode as png.
- VCL::Image::Format format = VCL::Image::Format::BIN;
+ VCL::Format format = VCL::Format::BIN;
std::vector blob_buffer;
blob_buffer = blob_im.get_encoded_image(format);
diff --git a/src/BoundingBoxCommand.cc b/src/BoundingBoxCommand.cc
index 9aaee45c..5aed6a99 100644
--- a/src/BoundingBoxCommand.cc
+++ b/src/BoundingBoxCommand.cc
@@ -289,19 +289,18 @@ Json::Value FindBoundingBox::construct_responses(
get_value(coords, "x"), get_value(coords, "y"),
get_value(coords, "w"), get_value(coords, "h")));
- VCL::Image::Format format =
- img.get_image_format() != VCL::Image::Format::TDB
- ? img.get_image_format()
- : VCL::Image::Format::PNG;
+ VCL::Format format = img.get_image_format() != VCL::Format::TDB
+ ? img.get_image_format()
+ : VCL::Format::PNG;
if (cmd.isMember("format")) {
std::string requested_format =
get_value(cmd, "format");
if (requested_format == "png") {
- format = VCL::Image::Format::PNG;
+ format = VCL::Format::PNG;
} else if (requested_format == "jpg") {
- format = VCL::Image::Format::JPG;
+ format = VCL::Format::JPG;
}
}
diff --git a/src/CommunicationManager.cc b/src/CommunicationManager.cc
index 865b775d..5e368ad3 100644
--- a/src/CommunicationManager.cc
+++ b/src/CommunicationManager.cc
@@ -31,6 +31,7 @@
#include "CommunicationManager.h"
#include "QueryHandlerExample.h"
+#include "QueryHandlerNeo4j.h"
#include "QueryHandlerPMGD.h"
#include "VDMSConfig.h"
@@ -75,6 +76,9 @@ void CommunicationManager::process_queue() {
} else if (_q_handler == "example") {
QueryHandlerExample qh;
qh.process_connection(c);
+ } else if (_q_handler == "neo4j") {
+ QueryHandlerNeo4j qh;
+ qh.process_connection(c);
}
printf("Connection received...\n");
diff --git a/src/DescriptorsCommand.cc b/src/DescriptorsCommand.cc
index a61ce4e8..033b740e 100644
--- a/src/DescriptorsCommand.cc
+++ b/src/DescriptorsCommand.cc
@@ -378,7 +378,9 @@ void AddDescriptor::retrieve_aws_descriptorSet(const std::string &set_path) {
for (auto file : files) {
// if file isn't already on disk, retrieve it from AWS
if (!fs::exists(file)) {
- connection->RetrieveFile(file);
+ if (!connection->RetrieveFile(file)) {
+ throw VCLException(ObjectNotFound, "File was not found");
+ }
}
}
}
diff --git a/src/ImageCommand.cc b/src/ImageCommand.cc
index cfbdb8b5..bc4b978d 100644
--- a/src/ImageCommand.cc
+++ b/src/ImageCommand.cc
@@ -97,25 +97,25 @@ int ImageCommand::enqueue_operations(VCL::Image &img, const Json::Value &ops,
return 0;
}
-VCL::Image::Format ImageCommand::get_requested_format(const Json::Value &cmd) {
- VCL::Image::Format format;
+VCL::Format ImageCommand::get_requested_format(const Json::Value &cmd) {
+ VCL::Format format;
std::string requested_format = get_value(cmd, "format", "");
if (requested_format == "png") {
- return VCL::Image::Format::PNG;
+ return VCL::Format::PNG;
}
if (requested_format == "jpg") {
- return VCL::Image::Format::JPG;
+ return VCL::Format::JPG;
}
if (requested_format == "tdb") {
- return VCL::Image::Format::TDB;
+ return VCL::Format::TDB;
}
if (requested_format == "bin") {
- return VCL::Image::Format::BIN;
+ return VCL::Format::BIN;
}
- return VCL::Image::Format::NONE_IMAGE;
+ return VCL::Format::NONE_IMAGE;
}
//========= AddImage definitions =========
@@ -125,16 +125,20 @@ AddImage::AddImage() : ImageCommand("AddImage") {
_storage_png = VDMSConfig::instance()->get_path_png();
_storage_jpg = VDMSConfig::instance()->get_path_jpg();
_storage_bin = VDMSConfig::instance()->get_path_bin();
- //_use_aws_storage = VDMSConfig::instance()->get_aws_flag();
+ _use_aws_storage = VDMSConfig::instance()->get_aws_flag();
}
int AddImage::construct_protobuf(PMGDQuery &query, const Json::Value &jsoncmd,
const std::string &blob, int grp_id,
Json::Value &error) {
+
const Json::Value &cmd = jsoncmd[_cmd_name];
int operation_flags = 0;
-
+ bool sameFormat = false;
int node_ref = get_value(cmd, "_ref", query.get_available_reference());
+ const std::string from_file_path =
+ get_value(cmd, "from_file_path", "");
+ const bool is_local_file = get_value(cmd, "is_local_file", false);
std::string format = get_value(cmd, "format", "");
char binary_img_flag = 0;
@@ -142,59 +146,107 @@ int AddImage::construct_protobuf(PMGDQuery &query, const Json::Value &jsoncmd,
binary_img_flag = 1;
}
- VCL::Image img((void *)blob.data(), blob.size(), binary_img_flag);
- if (_use_aws_storage) {
- VCL::RemoteConnection *connection = new VCL::RemoteConnection();
- std::string bucket = VDMSConfig::instance()->get_bucket_name();
- connection->_bucket_name = bucket;
- img.set_connection(connection);
- }
+ std::string img_root = _storage_tdb;
+ std::string file_name;
+ VCL::Format input_format =
+ VCL::read_image_format((void *)blob.data(), blob.size());
+ std::string image_fomrat = VCL::format_to_string(input_format);
+ if ((image_fomrat == format) && (!cmd.isMember("operations"))) {
+ if (image_fomrat == "png")
+ img_root = _storage_png;
+ else if (image_fomrat == "jpg")
+ img_root = _storage_jpg;
+ file_name = VCL::create_unique(img_root, format);
+ Json::Value props = get_value(cmd, "properties");
+ props[VDMS_IM_PATH_PROP] = file_name;
- if (cmd.isMember("operations")) {
- operation_flags = enqueue_operations(img, cmd["operations"], true);
- }
+ query.AddNode(node_ref, VDMS_IM_TAG, props, Json::Value());
+ VCL::Image img;
- std::string img_root = _storage_tdb;
- VCL::Image::Format vcl_format = img.get_image_format();
+ if (from_file_path.empty()) {
+ img = VCL::Image(file_name, blob, input_format);
+ } else {
+ if (is_local_file) {
+ img = VCL::Image(from_file_path, false);
+ } else {
+ img = VCL::Image(from_file_path, true);
+ }
+ }
+
+ if (_use_aws_storage) {
+ VCL::RemoteConnection *connection = new VCL::RemoteConnection();
+ std::string bucket = VDMSConfig::instance()->get_bucket_name();
+ connection->_bucket_name = bucket;
+ img.set_connection(connection);
+ }
+ img.save_image(file_name, blob);
+
+ } else { // used when input format is not the same as the output format
+ VCL::Image img;
- if (operation_flags != 0) {
- error["info"] = "custom function process not found";
- error["status"] = RSCommand::Error;
- return -1;
- } else if (cmd.isMember("format")) {
+ if (from_file_path.empty()) {
+ img = VCL::Image((void *)blob.data(), blob.size(), binary_img_flag);
+ } else {
+ if (is_local_file) {
+ img = VCL::Image(from_file_path, false);
+ } else {
+ img = VCL::Image(from_file_path, true);
+ }
+ }
+
+ if (_use_aws_storage) {
+ VCL::RemoteConnection *connection = new VCL::RemoteConnection();
+ std::string bucket = VDMSConfig::instance()->get_bucket_name();
+ connection->_bucket_name = bucket;
+ img.set_connection(connection);
+ }
+
+ if (cmd.isMember("operations")) {
+ operation_flags = enqueue_operations(img, cmd["operations"], true);
+ }
+
+ if (operation_flags != 0) {
+ error["info"] = "custom function process not found";
+ error["status"] = RSCommand::Error;
+ return -1;
+ }
if (format == "png") {
- vcl_format = VCL::Image::Format::PNG;
+ input_format = VCL::Format::PNG;
img_root = _storage_png;
} else if (format == "tdb") {
- vcl_format = VCL::Image::Format::TDB;
+ input_format = VCL::Format::TDB;
img_root = _storage_tdb;
} else if (format == "jpg") {
- vcl_format = VCL::Image::Format::JPG;
+ input_format = VCL::Format::JPG;
img_root = _storage_jpg;
} else if (format == "bin") {
- vcl_format = VCL::Image::Format::BIN;
+ input_format = VCL::Format::BIN;
img_root = _storage_bin;
} else {
error["info"] = format + ": format not implemented";
error["status"] = RSCommand::Error;
return -1;
}
- }
- std::string file_name = VCL::create_unique(img_root, format);
+ file_name = VCL::create_unique(img_root, format);
- // Modifiyng the existing properties that the user gives
- // is a good option to make the AddNode more simple.
- // This is not ideal since we are manupulating with user's
- // input, but for now it is an acceptable solution.
- Json::Value props = get_value(cmd, "properties");
- props[VDMS_IM_PATH_PROP] = file_name;
+ // Modifiyng the existing properties that the user gives
+ // is a good option to make the AddNode more simple.
+ // This is not ideal since we are manupulating with user's
+ // input, but for now it is an acceptable solution.
+ Json::Value props = get_value(cmd, "properties");
+ props[VDMS_IM_PATH_PROP] = file_name;
- // Add Image node
- query.AddNode(node_ref, VDMS_IM_TAG, props, Json::Value());
+ if (img.is_blob_not_stored()) {
+ props[VDMS_IM_PATH_PROP] = from_file_path;
+ file_name = from_file_path;
+ }
+ // Add Image node
+ query.AddNode(node_ref, VDMS_IM_TAG, props, Json::Value());
- img.store(file_name, vcl_format);
+ img.store(file_name, input_format);
+ }
// In case we need to cleanup the query
error["image_added"] = file_name;
@@ -206,6 +258,14 @@ int AddImage::construct_protobuf(PMGDQuery &query, const Json::Value &jsoncmd,
return 0;
}
+bool AddImage::need_blob(const Json::Value &cmd) {
+ if (cmd.isMember(_cmd_name)) {
+ const Json::Value &add_image_cmd = cmd[_cmd_name];
+ return !(add_image_cmd.isMember("from_file_path"));
+ }
+ throw VCLException(UndefinedException, "Query Error");
+}
+
//========= UpdateImage definitions =========
UpdateImage::UpdateImage() : ImageCommand("UpdateImage") {}
@@ -227,7 +287,7 @@ int UpdateImage::construct_protobuf(PMGDQuery &query,
//========= FindImage definitions =========
FindImage::FindImage() : ImageCommand("FindImage") {
- //_use_aws_storage = VDMSConfig::instance()->get_aws_flag();
+ _use_aws_storage = VDMSConfig::instance()->get_aws_flag();
}
int FindImage::construct_protobuf(PMGDQuery &query, const Json::Value &jsoncmd,
@@ -257,10 +317,9 @@ Json::Value FindImage::construct_responses(Json::Value &responses,
int operation_flags = 0;
bool has_operations = false;
std::string no_op_def_image;
-
Json::Value ret;
- std::map formats;
+ std::map formats;
auto error = [&](Json::Value &res) {
ret[_cmd_name] = res;
@@ -286,7 +345,6 @@ Json::Value FindImage::construct_responses(Json::Value &responses,
// Uses PMGD info error.
return error(findImage);
}
-
Json::Value results = get_value(cmd, "results");
bool flag_empty = false;
@@ -316,7 +374,6 @@ Json::Value FindImage::construct_responses(Json::Value &responses,
try {
VCL::Image img(im_path);
-
if (_use_aws_storage) {
VCL::RemoteConnection *connection = new VCL::RemoteConnection();
std::string bucket = VDMSConfig::instance()->get_bucket_name();
@@ -332,10 +389,9 @@ Json::Value FindImage::construct_responses(Json::Value &responses,
// We will return the image in the format the user
// request, or on its format in disk, except for the case
// of .tdb, where we will encode as png.
- VCL::Image::Format format =
- img.get_image_format() != VCL::Image::Format::TDB
- ? img.get_image_format()
- : VCL::Image::Format::PNG;
+ VCL::Format format = img.get_image_format() != VCL::Format::TDB
+ ? img.get_image_format()
+ : VCL::Format::PNG;
if (operation_flags != 0) {
Json::Value return_error;
@@ -345,8 +401,7 @@ Json::Value FindImage::construct_responses(Json::Value &responses,
}
if (cmd.isMember("format")) {
format = get_requested_format(cmd);
- if (format == VCL::Image::Format::NONE_IMAGE ||
- format == VCL::Image::Format::TDB) {
+ if (format == VCL::Format::NONE_IMAGE || format == VCL::Format::TDB) {
Json::Value return_error;
return_error["status"] = RSCommand::Error;
return_error["info"] = "Invalid Requested Format for FindImage";
@@ -355,15 +410,14 @@ Json::Value FindImage::construct_responses(Json::Value &responses,
}
if (has_operations) {
- formats.insert(std::pair(
- img.get_image_id(), format));
+ formats.insert(
+ std::pair(img.get_image_id(), format));
eventloop.enqueue(&img);
} else {
std::vector img_enc;
img_enc = img.get_encoded_image(format);
no_op_def_image = img.get_image_id();
if (!img_enc.empty()) {
-
std::string *img_str = query_res.add_blobs();
img_str->resize(img_enc.size());
std::memcpy((void *)img_str->data(), (void *)img_enc.data(),
diff --git a/src/ImageCommand.h b/src/ImageCommand.h
index 7041911c..abfd55c8 100644
--- a/src/ImageCommand.h
+++ b/src/ImageCommand.h
@@ -62,7 +62,7 @@ class ImageCommand : public RSCommand {
// Checks if 'format' parameter is specified, and if so, returns the
// corresponding VCL::Image::Format type.
- VCL::Image::Format get_requested_format(const Json::Value &cmd);
+ VCL::Format get_requested_format(const Json::Value &cmd);
};
class AddImage : public ImageCommand {
@@ -79,7 +79,7 @@ class AddImage : public ImageCommand {
const std::string &blob, int grp_id,
Json::Value &error);
- bool need_blob(const Json::Value &cmd) { return true; }
+ bool need_blob(const Json::Value &cmd);
};
class UpdateImage : public ImageCommand {
diff --git a/src/ImageLoop.cc b/src/ImageLoop.cc
index 04472d4b..e4dcef59 100644
--- a/src/ImageLoop.cc
+++ b/src/ImageLoop.cc
@@ -32,6 +32,8 @@
#include "ImageLoop.h"
#include
+#include "VDMSConfig.h"
+
ImageLoop::~ImageLoop() noexcept {
VCL::Image img(imageMap.begin()->first);
m_running = false;
@@ -173,8 +175,8 @@ CURL *ImageLoop::get_easy_handle(VCL::Image *img, std::string &readBuffer) {
auto time_now = std::chrono::system_clock::now();
std::chrono::duration utc_time = time_now.time_since_epoch();
- VCL::Image::Format img_format = img->get_image_format();
- std::string format = img->format_to_string(img_format);
+ VCL::Format img_format = img->get_image_format();
+ std::string format = VCL::format_to_string(img_format);
if (format == "" && options.isMember("format")) {
format = options["format"].toStyledString().data();
@@ -185,8 +187,9 @@ CURL *ImageLoop::get_easy_handle(VCL::Image *img, std::string &readBuffer) {
format = "jpg";
}
- std::string filePath =
- "/tmp/tempfile" + std::to_string(utc_time.count()) + "." + format;
+ std::string filePath = VDMS::VDMSConfig::instance()->get_path_tmp() +
+ "/tempfile" + std::to_string(utc_time.count()) +
+ "." + format;
cv::imwrite(filePath, img->get_cvmat(false, false));
_tempfiles.push_back(filePath);
diff --git a/src/Neo4JCommands.h b/src/Neo4JCommands.h
new file mode 100644
index 00000000..dc6b45ce
--- /dev/null
+++ b/src/Neo4JCommands.h
@@ -0,0 +1,104 @@
+/**
+ * @file Neo4jCommands.h
+ *
+ * @section LICENSE
+ *
+ * The MIT License
+ *
+ * @copyright Copyright (c) 2024 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#pragma once
+
+#include "queryMessage.pb.h"
+#include "vcl/Image.h"
+#include "vcl/VCL.h"
+#include
+
+namespace VDMS {
+
+class Neo4jCommand {
+protected:
+ const std::string _cmd_name;
+ std::map _valid_params_map;
+
+ template
+ T get_value(const Json::Value &json, const std::string &key, T def = T());
+ void add_link(std::string &tx, const Json::Value &link, int node_ref,
+ const std::string tag);
+ virtual Json::Value check_responses(Json::Value &responses);
+ bool _use_aws_storage;
+
+public:
+ enum ErrorCode {
+ Success = 0,
+ Error = -1,
+ Empty = 1,
+ Exists = 2,
+ NotUnique = 3
+ };
+
+ Neo4jCommand(const std::string &cmd_name);
+ virtual bool need_blob(const Json::Value &cmd) { return false; }
+ virtual int data_processing(std::string &tx, const Json::Value &root,
+ const std::string &blob, int grp_id,
+ Json::Value &error) = 0;
+ virtual Json::Value construct_responses(Json::Value &json_responses,
+ const Json::Value &json,
+ protobufs::queryMessage &response,
+ const std::string &blob);
+};
+
+// Cypher Based Commands
+class Neo4jNeoAdd : public Neo4jCommand {
+ std::string _storage_tdb;
+ std::string _storage_png;
+ std::string _storage_jpg;
+ std::string _storage_bin;
+
+public:
+ Neo4jNeoAdd();
+ bool need_blob(const Json::Value &cmd);
+ int data_processing(std::string &tx, const Json::Value &root,
+ const std::string &blob, int grp_id, Json::Value &error);
+ Json::Value construct_responses(Json::Value &neo4j_responses,
+ const Json::Value &orig_query,
+ protobufs::queryMessage &query_res,
+ const std::string &blob);
+};
+
+class Neo4jNeoFind : public Neo4jCommand {
+
+public:
+ Neo4jNeoFind();
+ bool need_blob(const Json::Value &cmd) { return false; }
+ int data_processing(std::string &tx, const Json::Value &root,
+ const std::string &blob, int grp_id, Json::Value &error);
+ Json::Value construct_responses(Json::Value &json_responses,
+ const Json::Value &json,
+ protobufs::queryMessage &response,
+ const std::string &blob);
+};
+
+} // namespace VDMS
diff --git a/src/Neo4JHandlerCommands.cc b/src/Neo4JHandlerCommands.cc
new file mode 100644
index 00000000..c7bef459
--- /dev/null
+++ b/src/Neo4JHandlerCommands.cc
@@ -0,0 +1,233 @@
+/**
+ * @file Neo4jHandlerCommands.cc
+ *
+ * @section LICENSE
+ *
+ * The MIT License
+ *
+ * @copyright Copyright (c) 2024 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "ExceptionsCommand.h"
+#include "ImageLoop.h"
+#include "Neo4JCommands.h"
+#include "VDMSConfig.h"
+#include "defines.h"
+#include "vcl/VCL.h"
+#include
+
+#include "OpsIOCoordinator.h"
+#include
+#include
+
+#include
+
+using namespace VDMS;
+
+// hat-tip
+// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c
+std::string gen_random(const int len) {
+ static const char alphanum[] = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+ std::string tmp_s;
+ tmp_s.reserve(len);
+
+ for (int i = 0; i < len; ++i) {
+ tmp_s += alphanum[rand() % (sizeof(alphanum) - 1)];
+ }
+
+ return tmp_s;
+}
+
+// Constructor + superclass call
+Neo4jNeoAdd::Neo4jNeoAdd() : Neo4jCommand("NeoAdd") {
+ _storage_tdb = VDMSConfig::instance()->get_path_tdb();
+ _storage_png = VDMSConfig::instance()->get_path_png();
+ _storage_jpg = VDMSConfig::instance()->get_path_jpg();
+ _storage_bin = VDMSConfig::instance()->get_path_bin();
+}
+
+bool Neo4jNeoAdd::need_blob(const Json::Value &cmd) {
+
+ Json::Value first_level = cmd["NeoAdd"];
+ std::string tgt_data_type =
+ first_level.get("target_data_type", "tgt_type_not_specified").asString();
+ if (tgt_data_type == "md_only") {
+ return false;
+ } else if (tgt_data_type == "tgt_type_not_specified") {
+ return false;
+ } else {
+ return true;
+ }
+};
+
+int Neo4jNeoAdd::data_processing(std::string &cypher,
+ const Json::Value &orig_query,
+ const std::string &blob, int grp_id,
+ Json::Value &error) {
+
+ std::chrono::steady_clock::time_point ops_start, ops_end;
+ VCL::RemoteConnection *connection;
+ std::vector enc_img;
+
+ const Json::Value &cmd = orig_query[_cmd_name];
+ int operation_flags = 0;
+
+ std::string format = get_value(cmd, "target_format", "");
+ std::string tgt_data_type =
+ get_value(cmd, "target_data_type", "");
+
+ char binary_img_flag = 0;
+
+ // if we're only doing metadata, we can ignore the ingest processing and just
+ // return
+ if (tgt_data_type == "md_only") {
+ return 0;
+ }
+
+ std::vector raw_data(blob.begin(), blob.end());
+ connection = get_existing_connection();
+
+ try {
+ enc_img = do_single_img_ops(orig_query, raw_data, _cmd_name);
+ } catch (VCL::Exception &e) {
+ print_exception(e, stdout);
+ exit(1); // brutal exit, future iterations should throw exception for
+ // handling and graceful rollback
+ }
+
+ std::string img_obj_id;
+ img_obj_id = gen_random(32);
+
+ s3_upload(img_obj_id, enc_img, connection);
+
+ // In case we need to cleanup the query
+ error["image_added"] = img_obj_id;
+ error["data_type"] = tgt_data_type;
+
+ // Later this will require validation/checks but for experimental should be
+ // okay
+ cypher = cypher + " SET VDMSNODE:" + error["data_type"].asString();
+ cypher = cypher + " SET VDMSNODE.img_loc = \"" +
+ error["image_added"].asString() + "\"";
+
+ return 0;
+}
+
+Json::Value Neo4jNeoAdd::construct_responses(Json::Value &neo4j_responses,
+ const Json::Value &orig_query,
+ protobufs::queryMessage &query_res,
+ const std::string &blob) {
+
+ Json::Value ret;
+ ret[_cmd_name] = "Filler";
+ return ret;
+}
+
+// Neo find
+Neo4jNeoFind::Neo4jNeoFind() : Neo4jCommand("NeoFind") {}
+
+int Neo4jNeoFind::data_processing(std::string &tx, const Json::Value &jsoncmd,
+ const std::string &blob, int grp_id,
+ Json::Value &error) {
+ const Json::Value &cmd = jsoncmd[_cmd_name];
+
+ Json::Value results = get_value(cmd, "results");
+
+ // Unless otherwise specified, we return the blob.
+ if (get_value(results, "blob", true)) {
+ results["list"].append(VDMS_IM_PATH_PROP);
+ }
+
+ return 0;
+}
+
+Json::Value Neo4jNeoFind::construct_responses(
+ Json::Value &neo4j_responses, const Json::Value &orig_query,
+ protobufs::queryMessage &query_res, const std::string &blob) {
+
+ std::chrono::steady_clock::time_point min_conn_start, min_conn_end;
+ std::chrono::steady_clock::time_point min_conn_run_start, min_conn_run_end;
+ std::chrono::steady_clock::time_point ops_start, ops_end;
+
+ Json::FastWriter fastWriter;
+ const Json::Value &cmd = orig_query[_cmd_name];
+ int operation_flags = 0;
+ bool has_operations = false;
+ std::string no_op_def_image;
+ Json::Value ret;
+ std::map formats;
+
+ auto error = [&](Json::Value &res) {
+ ret[_cmd_name] = res;
+ return ret;
+ };
+
+ auto empty = [&](Json::Value &res) {
+ ret[_cmd_name] = res;
+ return ret;
+ };
+
+ std::vector img_paths;
+ for (int i = 0; i < neo4j_responses["metadata_res"].size(); i++) {
+ Json::Value res_row = neo4j_responses["metadata_res"][i];
+ std::string img_loc = res_row["VDMSNODE.img_loc"].asString();
+
+ img_paths.push_back(img_loc);
+ }
+
+ Json::Value results = get_value(cmd, "results");
+
+ // Check if blob (image) must be returned
+ if (get_value(results, "blob", true)) {
+
+ for (int img_idx = 0; img_idx < img_paths.size(); img_idx++) {
+ std::vector raw_data;
+ std::string im_path = img_paths[img_idx];
+
+ try {
+ // NOTE CURRENTLY FIXED TO USE ONLY S3
+ raw_data = s3_retrieval(im_path, global_s3_connection);
+
+ std::vector img_enc;
+ img_enc = do_single_img_ops(orig_query, raw_data, _cmd_name);
+
+ std::string *img_str = query_res.add_blobs();
+ img_str->resize(img_enc.size());
+ std::memcpy((void *)img_str->data(), (void *)img_enc.data(),
+ img_enc.size());
+
+ } catch (VCL::Exception e) {
+ print_exception(e);
+ Json::Value return_error;
+ return_error["status"] = Neo4jCommand::Error;
+ return_error["info"] = "VCL Exception";
+ return error(return_error);
+ }
+ }
+ }
+
+ return ret;
+}
\ No newline at end of file
diff --git a/src/Neo4jBaseCommands.cc b/src/Neo4jBaseCommands.cc
new file mode 100644
index 00000000..6b37191d
--- /dev/null
+++ b/src/Neo4jBaseCommands.cc
@@ -0,0 +1,81 @@
+
+#include "Neo4JCommands.h"
+#include "VDMSConfig.h"
+
+using namespace VDMS;
+
+Neo4jCommand::Neo4jCommand(const std::string &cmd_name) : _cmd_name(cmd_name) {
+ _use_aws_storage = VDMSConfig::instance()->get_aws_flag();
+}
+
+template <>
+int Neo4jCommand::get_value(const Json::Value &json, const std::string &key,
+ int def) {
+ if (json.isMember(key))
+ return json[key].asInt();
+
+ return def;
+}
+
+template <>
+double Neo4jCommand::get_value(const Json::Value &json, const std::string &key,
+ double def) {
+ if (json.isMember(key))
+ return json[key].asDouble();
+
+ return def;
+}
+
+template <>
+bool Neo4jCommand::get_value(const Json::Value &json, const std::string &key,
+ bool def) {
+ if (json.isMember(key))
+ return json[key].asBool();
+
+ return def;
+}
+
+template <>
+std::string Neo4jCommand::get_value(const Json::Value &json,
+ const std::string &key, std::string def) {
+ if (json.isMember(key))
+ return json[key].asString();
+
+ return def;
+}
+
+template <>
+Json::Value Neo4jCommand::get_value(const Json::Value &json,
+ const std::string &key, Json::Value def) {
+ return json[key];
+}
+
+Json::Value Neo4jCommand::construct_responses(
+ Json::Value &response, const Json::Value &json,
+ protobufs::queryMessage &query_res, const std::string &blob) {
+
+ Json::Value ret;
+ ret[_cmd_name] = check_responses(response);
+ return ret;
+}
+
+Json::Value Neo4jCommand::check_responses(Json::Value &responses) {
+ bool flag_error = false;
+ Json::Value ret;
+ if (responses.size() == 0) {
+ printf("NO responses found! Setting Errror!\n");
+ ret["status"] = Neo4jCommand::Error;
+ ret["info"] = "No responses!";
+ printf("Error Response!\n");
+ return ret;
+ }
+ printf("No Error found!\n");
+
+ ret = responses[0];
+
+ if (!flag_error) {
+ ret["status"] = Neo4jCommand::Success;
+ }
+
+ return ret;
+}
\ No newline at end of file
diff --git a/src/Neo4jQueryHelpers.h b/src/Neo4jQueryHelpers.h
new file mode 100644
index 00000000..b23167ec
--- /dev/null
+++ b/src/Neo4jQueryHelpers.h
@@ -0,0 +1,62 @@
+/**
+ * @file Neo4jQueryHelpers.h
+ *
+ * @section LICENSE
+ *
+ * The MIT License
+ *
+ * @copyright Copyright (c) 2024 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+#pragma once
+#include
+
+/*Hat Tip
+ * https://stackoverflow.com/questions/4800605/iterating-through-objects-in-jsoncpp*/
+void PrintJSONValue(const Json::Value &val) {
+ if (val.isString()) {
+ printf("string(%s)", val.asString().c_str());
+ } else if (val.isBool()) {
+ printf("bool(%d)", val.asBool());
+ } else if (val.isInt()) {
+ printf("int(%d)", val.asInt());
+ } else if (val.isUInt()) {
+ printf("uint(%u)", val.asUInt());
+ } else if (val.isDouble()) {
+ printf("double(%f)", val.asDouble());
+ } else {
+ printf("unknown type=[%d]", val.type());
+ }
+}
+
+void json_printer(const Json::Value &root, unsigned short depth) {
+
+ printf("\n---------JSON Dumper-----------\n");
+ for (Json::Value::const_iterator itr = root.begin(); itr != root.end();
+ itr++) {
+ printf(" subvalue(");
+ PrintJSONValue(itr.key());
+ printf(") -");
+ json_printer(*itr, depth);
+ }
+}
diff --git a/src/OpsIOCoordinator.cc b/src/OpsIOCoordinator.cc
new file mode 100644
index 00000000..c680dbbf
--- /dev/null
+++ b/src/OpsIOCoordinator.cc
@@ -0,0 +1,247 @@
+/**
+ * @file OpsIoCoordinator.cc
+ *
+ * @section LICENSE
+ *
+ * The MIT License
+ *
+ * @copyright Copyright (c) 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "OpsIOCoordinator.h"
+#include "ExceptionsCommand.h"
+#include "VDMSConfig.h"
+#include "vcl/Image.h"
+#include "vcl/VCL.h"
+
+#include
+
+using namespace VDMS;
+
+template
+T get_json_val(const Json::Value &json, const std::string &key, T def = T());
+
+template <>
+int get_json_val(const Json::Value &json, const std::string &key, int def) {
+ if (json.isMember(key))
+ return json[key].asInt();
+
+ return def;
+}
+
+template <>
+double get_json_val(const Json::Value &json, const std::string &key,
+ double def) {
+ if (json.isMember(key))
+ return json[key].asDouble();
+
+ return def;
+}
+
+template <>
+bool get_json_val(const Json::Value &json, const std::string &key, bool def) {
+ if (json.isMember(key))
+ return json[key].asBool();
+
+ return def;
+}
+
+template <>
+std::string get_json_val(const Json::Value &json, const std::string &key,
+ std::string def) {
+ if (json.isMember(key))
+ return json[key].asString();
+
+ return def;
+}
+
+template <>
+Json::Value get_json_val(const Json::Value &json, const std::string &key,
+ Json::Value def) {
+ return json[key];
+}
+
+int img_enqueue_operations(VCL::Image &img, const Json::Value &ops) {
+
+ std::chrono::steady_clock::time_point total_start, total_end;
+ double total_runtime;
+
+ total_start = std::chrono::steady_clock::now();
+ // Correct operation type and parameters are guaranteed at this point
+ for (auto &op : ops) {
+ const std::string &type = get_json_val(op, "type");
+ if (type == "threshold") {
+ img.threshold(get_json_val(op, "value"));
+ } else if (type == "resize") {
+ img.resize(get_json_val(op, "height"),
+ get_json_val(op, "width"));
+ } else if (type == "crop") {
+ img.crop(VCL::Rectangle(
+ get_json_val(op, "x"), get_json_val(op, "y"),
+ get_json_val(op, "width"), get_json_val(op, "height")));
+ } else if (type == "flip") {
+ img.flip(get_json_val(op, "code"));
+ } else if (type == "rotate") {
+ img.rotate(get_json_val(op, "angle"),
+ get_json_val(op, "resize"));
+ } else {
+ throw ExceptionCommand(ImageError, "Operation is not defined");
+ return -1;
+ }
+ }
+ total_end = std::chrono::steady_clock::now();
+ total_runtime = std::chrono::duration_cast(
+ total_end - total_start)
+ .count();
+
+ return 0;
+}
+
+std::vector
+do_single_img_ops(const Json::Value &orig_query,
+ std::vector &raw_data, std::string cmd_name) {
+
+ std::chrono::steady_clock::time_point total_start, total_end;
+ total_start = std::chrono::steady_clock::now();
+ double total_runtime;
+ Json::Value cmd;
+ if (orig_query.isMember(cmd_name)) {
+ cmd = orig_query[cmd_name];
+ } else {
+ // TODO this is clunky and not optimal,but for experimental feature is okay
+ // IMO
+ printf("CMD Not Found: %s, returning empty image vector!\n",
+ cmd_name.c_str());
+ return std::vector();
+ }
+
+ int operation_flags = 0;
+ char binary_img_flag = 0;
+
+ std::string format = get_json_val(cmd, "target_format", "");
+ if (format == "bin" || format == "") {
+ binary_img_flag = 1;
+ }
+
+ VCL::Image img(std::data(raw_data), raw_data.size(), binary_img_flag);
+ VCL::Format vcl_format = img.get_image_format();
+
+ if (cmd.isMember("operations")) {
+ operation_flags = img_enqueue_operations(img, cmd["operations"]);
+ }
+
+ if (cmd.isMember("target_format")) {
+ if (format == "png") {
+ vcl_format = VCL::Format::PNG;
+ } else if (format == "jpg") {
+ vcl_format = VCL::Format::JPG;
+ } else if (format == "bin") {
+ vcl_format = VCL::Format::BIN;
+ } else {
+ printf("Warning! %s not supported!\n", format.c_str());
+ } // FUTURE, add TDB support
+ }
+
+ long imgsize = 0;
+ std::vector img_enc;
+
+ // getting the image size performs operation as a side effect
+ imgsize = img.get_raw_data_size();
+ img_enc = img.get_encoded_image(vcl_format);
+ total_end = std::chrono::steady_clock::now();
+ total_runtime = std::chrono::duration_cast(
+ total_end - total_start)
+ .count();
+
+ return img_enc;
+}
+
+std::vector s3_retrieval(std::string obj_name,
+ VCL::RemoteConnection *connection) {
+
+ if (!connection->connected()) {
+ printf("Warning, attempting to use uninitialized S3 connection, returning "
+ "empty object\n");
+ return std::vector();
+ }
+
+ std::chrono::steady_clock::time_point total_start, total_end;
+ total_start = std::chrono::steady_clock::now();
+ double total_runtime;
+
+ std::vector raw_data;
+
+ raw_data = connection->Read(obj_name);
+ total_end = std::chrono::steady_clock::now();
+ total_runtime = std::chrono::duration_cast(
+ total_end - total_start)
+ .count();
+
+ return raw_data;
+}
+
+int s3_upload(std::string obj_name, std::vector upload_data,
+ VCL::RemoteConnection *connection) {
+
+ if (!connection->connected()) {
+ printf("Warning, attempting to use uninitialized S3 connection, no uploads "
+ "will occur\n");
+ return -1;
+ }
+
+ std::chrono::steady_clock::time_point total_start, total_end;
+ total_start = std::chrono::steady_clock::now();
+ double total_runtime;
+
+ connection->Write(obj_name, upload_data);
+ total_end = std::chrono::steady_clock::now();
+ total_runtime = std::chrono::duration_cast(
+ total_end - total_start)
+ .count();
+
+ return 0;
+}
+
+VCL::RemoteConnection *instantiate_connection() {
+ printf("Instantiating global S3 Connection...\n");
+ std::chrono::steady_clock::time_point total_start, total_end;
+ total_start = std::chrono::steady_clock::now();
+ double total_runtime;
+
+ VCL::RemoteConnection *connection;
+ connection = new VCL::RemoteConnection();
+ std::string bucket = VDMSConfig::instance()->get_bucket_name();
+ connection->_bucket_name = bucket;
+ connection->start();
+
+ total_end = std::chrono::steady_clock::now();
+ total_runtime = std::chrono::duration_cast(
+ total_end - total_start)
+ .count();
+
+ printf("Global S3 Connection Started!\n");
+ return connection;
+}
+
+VCL::RemoteConnection *get_existing_connection() {
+ return global_s3_connection;
+}
\ No newline at end of file
diff --git a/src/OpsIOCoordinator.h b/src/OpsIOCoordinator.h
new file mode 100644
index 00000000..2b0dc400
--- /dev/null
+++ b/src/OpsIOCoordinator.h
@@ -0,0 +1,42 @@
+/**
+ * @file OpsIoCoordinator.h
+ *
+ * @section LICENSE
+ *
+ * The MIT License
+ *
+ * @copyright Copyright (c) 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+#pragma once
+#include "vcl/VCL.h"
+
+extern VCL::RemoteConnection *global_s3_connection;
+
+std::vector
+do_single_img_ops(const Json::Value &orig_query,
+ std::vector &raw_data, std::string cmd_name);
+std::vector s3_retrieval(std::string obj_name,
+ VCL::RemoteConnection *connection);
+int s3_upload(std::string obj_name, std::vector upload_data,
+ VCL::RemoteConnection *connection);
+VCL::RemoteConnection *instantiate_connection();
+VCL::RemoteConnection *get_existing_connection();
diff --git a/src/PMGDQueryHandler.cc b/src/PMGDQueryHandler.cc
index 888dddde..fcf4fd89 100644
--- a/src/PMGDQueryHandler.cc
+++ b/src/PMGDQueryHandler.cc
@@ -1039,7 +1039,11 @@ void delete_by_value(std::list *queue, void *p_delete_node) {
void cleanup_pmgd_files(std::vector *p_cleanup_list) {
std::vector::iterator it = p_cleanup_list->begin();
while (it != p_cleanup_list->end()) {
- remove((*it).c_str());
+ std::string filename = (*it).c_str();
+ if (filename.find(VDMSConfig::instance()->get_path_videos()) !=
+ std::string::npos) {
+ remove((*it).c_str());
+ }
it++;
}
}
diff --git a/src/QueryHandlerBase.cc b/src/QueryHandlerBase.cc
index 3e9b31e5..494cceb7 100644
--- a/src/QueryHandlerBase.cc
+++ b/src/QueryHandlerBase.cc
@@ -25,14 +25,21 @@ QueryHandlerBase::QueryHandlerBase()
// For now, we do it for videos/images as a starting point.
void QueryHandlerBase::cleanup_query(const std::vector &images,
const std::vector &videos) {
- for (auto &img_path : images) {
- VCL::Image img(img_path);
- img.delete_image();
- }
+ try {
+ for (auto &img_path : images) {
+ VCL::Image img(img_path);
+ bool result = img.delete_image();
+ if (!result) {
+ throw VCLException(UndefinedException,
+ "delete_image() failed: " + img_path);
+ }
+ }
- for (auto &vid_path : videos) {
- VCL::Video vid(vid_path);
- vid.delete_video();
+ for (auto &vid_path : videos) {
+ VCL::Video vid(vid_path);
+ vid.delete_video();
+ }
+ } catch (VCL::Exception &e) {
}
}
diff --git a/src/QueryHandlerNeo4j.cc b/src/QueryHandlerNeo4j.cc
new file mode 100644
index 00000000..8f4c91cf
--- /dev/null
+++ b/src/QueryHandlerNeo4j.cc
@@ -0,0 +1,312 @@
+/**
+ * @file QueryHandlerNeo4j.cc
+ *
+ * @section LICENSE
+ *
+ * The MIT License
+ *
+ * @copyright Copyright (c) 2024 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+#include "QueryHandlerNeo4j.h"
+#include "../include/vcl/RemoteConnection.h"
+#include "APISchema.h"
+#include "BackendNeo4j.h"
+#include "Neo4JCommands.h"
+#include "Neo4jQueryHelpers.h"
+#include "OpsIOCoordinator.h"
+#include "VDMSConfig.h"
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+using namespace VDMS;
+
+std::unordered_map QueryHandlerNeo4j::_rs_cmds;
+BackendNeo4j *QueryHandlerNeo4j::neoconn_pool;
+// VCL::RemoteConnection *global_s3_connection;
+
+void QueryHandlerNeo4j::init() {
+
+ _rs_cmds["NeoAdd"] = new Neo4jNeoAdd();
+ _rs_cmds["NeoFind"] = new Neo4jNeoFind();
+ // seed random time
+ srand((unsigned)time(NULL));
+
+ char *tgtdb = getenv("NEO4J_ENDPOINT");
+ char *user = getenv("NEO4J_USER");
+ char *pass = getenv("NEO4J_PASS");
+
+ uint_fast32_t flags = NEO4J_INSECURE;
+ int nr_conns = 16;
+
+ neoconn_pool = new BackendNeo4j(nr_conns, (char *)tgtdb, user, pass, flags);
+
+ // Load the string containing the schema (api_schema/APISchema.h)
+ Json::Reader reader;
+ Json::Value api_schema;
+ bool parseSuccess = reader.parse(schema_json.c_str(), api_schema);
+ if (!parseSuccess) {
+ std::cerr << "Failed to parse API reference schema." << std::endl;
+ std::cerr << "PANIC! Aborting." << std::endl;
+ exit(0);
+ }
+
+ // Parse the json schema into an internal schema format
+ valijson::SchemaParser parser;
+ valijson::adapters::JsonCppAdapter schemaDocumentAdapter(api_schema);
+ try {
+ parser.populateSchema(schemaDocumentAdapter, *_schema);
+ } catch (std::exception &e) {
+ std::cerr << "Failed to load schema: " << e.what() << std::endl;
+ std::cerr << "PANIC! Aborting." << std::endl;
+ exit(0);
+ }
+}
+
+QueryHandlerNeo4j::QueryHandlerNeo4j() {}
+
+bool QueryHandlerNeo4j::syntax_checker(const Json::Value &root,
+ Json::Value &error) {
+ valijson::ValidationResults results;
+ valijson::adapters::JsonCppAdapter user_query(root);
+ std::cerr << root.toStyledString() << std::endl; // TEMPORARY
+ if (!_validator.validate(*_schema, user_query, &results)) {
+ std::cerr << "API validation failed for:" << std::endl;
+ std::cerr << root.toStyledString() << std::endl;
+
+ // Will attempt to find the simple error
+ // To avoid valijson dump
+ for (int j = 0; j < root.size(); j++) {
+ const Json::Value &query = root[j];
+ if (query.getMemberNames().size() != 1) {
+ error["info"] = "Error: Only one command per element allowed";
+ return false;
+ }
+
+ const std::string cmd_str = query.getMemberNames()[0];
+ auto it = _rs_cmds.find(cmd_str);
+ if (it == _rs_cmds.end()) {
+ error["info"] = cmd_str + ": Command not found!";
+ return false;
+ }
+ }
+
+ valijson::ValidationResults::Error va_error;
+ unsigned int errorNum = 1;
+ std::stringstream str_error;
+ while (results.popError(va_error)) {
+ std::string context;
+ std::vector::iterator itr = va_error.context.begin();
+ for (; itr != va_error.context.end(); itr++) {
+ context += *itr;
+ }
+
+ str_error << "Error #" << errorNum << std::endl
+ << " context: " << context << std::endl
+ << " desc: " << va_error.description << std::endl;
+ ++errorNum;
+ }
+ std::cerr << str_error.str();
+ error["info"] = str_error.str();
+ return false;
+ }
+
+ for (auto &cmdTop : root) {
+ const std::string cmd_str = cmdTop.getMemberNames()[0];
+ auto &cmd = cmdTop[cmd_str];
+ if (cmd.isMember("constraints")) {
+ for (auto &member : cmd["constraints"].getMemberNames()) {
+ if (!cmd["constraints"][member].isArray()) {
+ error["info"] =
+ "Constraint for property '" + member + "' must be an array";
+ return false;
+ }
+ auto size = cmd["constraints"][member].size();
+ if (size != 2 && size != 4) {
+ error["info"] = "Constraint for property '" + member +
+ "' must be an array of size 2 or 4";
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+void QueryHandlerNeo4j::process_query(protobufs::queryMessage &proto_query,
+ protobufs::queryMessage &proto_res) {
+
+ std::chrono::steady_clock::time_point dbconn_start, dbconn_end;
+ std::chrono::steady_clock::time_point pre_proc_start, pre_proc_end;
+ std::chrono::steady_clock::time_point resp_start, resp_end;
+ std::chrono::steady_clock::time_point total_start, total_end;
+ std::chrono::steady_clock::time_point db_trans_time_start, db_trans_time_end;
+ std::chrono::steady_clock::time_point db_cmt_time_start, db_cmt_time_end;
+ double total_runtime, db_conn_time, pre_proc_time, cons_resp_time,
+ db_trans_time, db_cmt_time;
+
+ neo4j_transaction *tx;
+ neo4j_connection_t *conn;
+ neo4j_result_stream_t *res_stream;
+
+ total_start = std::chrono::steady_clock::now();
+ dbconn_start = std::chrono::steady_clock::now();
+ conn = neoconn_pool->get_conn();
+ ///// connection retrieved
+ dbconn_end = std::chrono::steady_clock::now();
+
+ int rc;
+
+ Json::FastWriter fastWriter;
+ Json::Value hello_res;
+ Json::Value json_responses;
+ Json::Value cmd_result;
+
+ Json::Value root;
+ int blob_count = 0;
+
+ rc = parse_commands(proto_query, root);
+
+ // begin neo4j transaction
+ tx = neoconn_pool->open_tx(conn, 10000, "w");
+ for (int j = 0; j < root.size(); j++) {
+ Json::Value neo4j_resp;
+ std::string cypher;
+
+ const Json::Value &query = root[j];
+ std::string cmd = query.getMemberNames()[0];
+
+ Neo4jCommand *rscmd = _rs_cmds[cmd];
+
+ cypher = query[cmd]["cypher"].asString();
+
+ const std::string &blob =
+ rscmd->need_blob(query) ? proto_query.blobs(blob_count++) : "";
+
+ pre_proc_start = std::chrono::steady_clock::now();
+ rscmd->data_processing(cypher, query, blob, 0, cmd_result);
+ pre_proc_end = std::chrono::steady_clock::now();
+
+ db_trans_time_start = std::chrono::steady_clock::now();
+ res_stream = neoconn_pool->run_in_tx((char *)cypher.c_str(), tx);
+ db_trans_time_end = std::chrono::steady_clock::now();
+
+ neo4j_resp = neoconn_pool->results_to_json(res_stream);
+
+ resp_start = std::chrono::steady_clock::now();
+ rscmd->construct_responses(neo4j_resp, query, proto_res, blob);
+ resp_end = std::chrono::steady_clock::now();
+
+ if (neo4j_resp.isMember("metadata_res")) {
+
+ hello_res["metadata_res"] = neo4j_resp["metadata_res"];
+ }
+
+ json_responses.append(hello_res);
+
+ proto_res.set_json(fastWriter.write(json_responses));
+ }
+ // commit neo4j transaction
+
+ db_cmt_time_start = std::chrono::steady_clock::now();
+ neoconn_pool->commit_tx(tx);
+ db_cmt_time_end = std::chrono::steady_clock::now();
+ neoconn_pool->put_conn(conn);
+ total_end = std::chrono::steady_clock::now();
+
+ db_conn_time = std::chrono::duration_cast(
+ dbconn_end - dbconn_start)
+ .count();
+ pre_proc_time = std::chrono::duration_cast(
+ pre_proc_end - pre_proc_start)
+ .count();
+ cons_resp_time = std::chrono::duration_cast(
+ resp_end - resp_start)
+ .count();
+ total_runtime = std::chrono::duration_cast(
+ total_end - total_start)
+ .count();
+ db_trans_time = std::chrono::duration_cast(
+ db_trans_time_end - db_trans_time_start)
+ .count();
+ db_cmt_time = std::chrono::duration_cast(
+ db_cmt_time_end - db_cmt_time_start)
+ .count();
+}
+
+int QueryHandlerNeo4j::parse_commands(
+ const protobufs::queryMessage &proto_query, Json::Value &root) {
+ Json::Reader reader;
+ const std::string commands = proto_query.json();
+
+ try {
+ bool parseSuccess = reader.parse(commands.c_str(), root);
+
+ if (!parseSuccess) {
+ root["info"] = "Error parsing the query, ill formed JSON";
+ root["status"] = Neo4jCommand::Error;
+ return -1;
+ }
+
+ Json::Value error;
+ if (!syntax_checker(root, error)) {
+ root = error;
+ root["status"] = Neo4jCommand::Error;
+ return -1;
+ }
+
+ unsigned blob_counter = 0;
+ for (int j = 0; j < root.size(); j++) {
+ const Json::Value &query = root[j];
+ assert(query.getMemberNames().size() == 1);
+ std::string cmd = query.getMemberNames()[0];
+
+ if (_rs_cmds[cmd]->need_blob(query)) {
+ blob_counter++;
+ }
+ }
+
+ if ((blob_counter != 0) && (blob_counter != proto_query.blobs().size())) {
+ root = error;
+ root["info"] = std::string(
+ "Expected blobs: " + std::to_string(blob_counter) +
+ ". Received blobs: " + std::to_string(proto_query.blobs().size()));
+ root["status"] = Neo4jCommand::Error;
+ std::cerr << "Number of Blobs Mismatch!" << std::endl;
+ return -1;
+ }
+
+ } catch (Json::Exception const &) {
+ root["info"] = "Json Exception at Parsing";
+ root["status"] = Neo4jCommand::Error;
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/QueryHandlerNeo4j.h b/src/QueryHandlerNeo4j.h
new file mode 100644
index 00000000..4c1f79e1
--- /dev/null
+++ b/src/QueryHandlerNeo4j.h
@@ -0,0 +1,57 @@
+/**
+ * @file QueryHandlerNeo4j.h
+ *
+ * @section LICENSE
+ *
+ * The MIT License
+ *
+ * @copyright Copyright (c) 2024 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+#pragma once
+
+#include "BackendNeo4j.h"
+#include "Neo4JCommands.h"
+#include "QueryHandlerBase.h"
+#include "queryMessage.pb.h"
+
+namespace VDMS {
+
+class QueryHandlerNeo4j : public QueryHandlerBase {
+
+protected:
+ static BackendNeo4j *neoconn_pool;
+ static std::unordered_map _rs_cmds;
+ friend class QueryHandlerTester;
+ bool syntax_checker(const Json::Value &root, Json::Value &error);
+ int parse_commands(const protobufs::queryMessage &proto_query,
+ Json::Value &root);
+
+public:
+ static void init();
+ QueryHandlerNeo4j();
+ void process_query(protobufs::queryMessage &proto_query,
+ protobufs::queryMessage &response);
+};
+
+} // namespace VDMS
diff --git a/src/Server.cc b/src/Server.cc
index 0b08ab33..9e6d23cb 100644
--- a/src/Server.cc
+++ b/src/Server.cc
@@ -42,17 +42,20 @@
#include "comm/Connection.h"
#include "DescriptorsManager.h"
+#include "OpsIOCoordinator.h"
#include "QueryHandlerExample.h"
+#include "QueryHandlerNeo4j.h"
#include "QueryHandlerPMGD.h"
#include "VDMSConfig.h"
#include "pmgdMessages.pb.h" // Protobuff implementation
using namespace VDMS;
-
+VCL::RemoteConnection *global_s3_connection = NULL;
bool Server::shutdown = false;
-Server::Server(std::string config_file) {
+Server::Server(std::string config_file, std::string cert_file,
+ std::string key_file, std::string ca_file) {
VDMSConfig::init(config_file);
@@ -60,6 +63,23 @@ Server::Server(std::string config_file) {
// debugging
cfg = VDMSConfig::instance();
+ // If cert_file and key_file were passed then override the config file values
+ // for them
+ if (!cert_file.empty() && !key_file.empty()) {
+ _cert_file = cert_file;
+ _key_file = key_file;
+ } else {
+ _cert_file = cfg->get_string_value(PARAM_CERT_FILE, "");
+ _key_file = cfg->get_string_value(PARAM_KEY_FILE, "");
+ }
+
+ // if ca_file was passed then override the config file value for it.
+ if (!ca_file.empty()) {
+ _ca_file = ca_file;
+ } else {
+ _ca_file = cfg->get_string_value(PARAM_CA_FILE, "");
+ }
+
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
@@ -124,6 +144,12 @@ void Server::setup_query_handler() {
// initialized
} else if (qhandler_type == "example") {
QueryHandlerExample::init();
+ } else if (qhandler_type == "neo4j") {
+ printf("Setting up Neo4j handler...\n");
+ _autoreplicate_settings.server_port =
+ cfg->get_int_value("port", DEFAULT_PORT);
+ global_s3_connection = instantiate_connection();
+ QueryHandlerNeo4j::init();
} else {
printf("Unrecognized handler: \"%s\", exiting!\n", qhandler_type.c_str());
exit(1);
@@ -133,7 +159,8 @@ void Server::setup_query_handler() {
void Server::process_requests() {
comm::ConnServer *server;
try {
- server = new comm::ConnServer(_autoreplicate_settings.server_port);
+ server = new comm::ConnServer(_autoreplicate_settings.server_port,
+ _cert_file, _key_file, _ca_file);
} catch (comm::ExceptionComm e) {
print_exception(e);
delete server;
diff --git a/src/Server.h b/src/Server.h
index 1b1049a4..e3dfdfa3 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -37,6 +37,7 @@
#include "VDMSConfig.h"
#include "pmgd.h"
#include
+#include
namespace VDMS {
struct ReplicationConfig {
@@ -83,7 +84,9 @@ class Server {
CommunicationManager *_cm;
ReplicationConfig _autoreplicate_settings;
- bool _untar;
+ std::string _cert_file;
+ std::string _key_file;
+ std::string _ca_file;
// signal handling for crtl-c,
static bool shutdown;
@@ -98,7 +101,8 @@ class Server {
public:
VDMSConfig *cfg;
- Server(std::string config_file);
+ Server(std::string config_file, std::string cert_file, std::string key_file,
+ std::string ca_file);
void process_requests();
void autodelete_expired_data();
void auto_replicate_interval();
diff --git a/src/VDMSConfig.cc b/src/VDMSConfig.cc
index d61e72d3..c87ecd98 100644
--- a/src/VDMSConfig.cc
+++ b/src/VDMSConfig.cc
@@ -53,7 +53,7 @@
#define DEFAULT_PATH_BLOBS "blobs"
#define DEFAULT_PATH_VIDEOS "videos"
#define DEFAULT_PATH_DESCRIPTORS "descriptors"
-#define DEFAULT_PATH_TMP "tmp"
+#define DEFAULT_PATH_TMP "/tmp"
#define DEFAULT_STORAGE_TYPE "local"
#define DEFAULT_BUCKET_NAME "vdms_bucket"
@@ -65,9 +65,11 @@ const bool DEFAULT_USE_ENDPOINT = false;
using namespace VDMS;
-VDMSConfig *VDMSConfig::cfg;
+VDMSConfig *VDMSConfig::cfg{nullptr};
+std::mutex VDMSConfig::_mutex;
bool VDMSConfig::init(std::string config_file) {
+ std::lock_guard lock(_mutex);
if (cfg)
return false;
@@ -75,18 +77,24 @@ bool VDMSConfig::init(std::string config_file) {
return true;
}
-void VDMSConfig::destroy() {
+bool VDMSConfig::destroy() {
if (cfg) {
delete cfg;
cfg = NULL;
+ return true;
}
+
+ std::cerr << "ERROR: destroy() was ignored due config was not initialized"
+ << std::endl;
+
+ return false;
}
VDMSConfig *VDMSConfig::instance() {
if (cfg)
return cfg;
- std::cout << "ERROR: Config not init" << std::endl;
+ std::cerr << "ERROR: config is not initialized" << std::endl;
return NULL;
}
@@ -94,12 +102,23 @@ VDMSConfig::VDMSConfig(std::string config_file) {
Json::Reader reader;
std::ifstream file(config_file);
+ cfg = nullptr;
+ storage_type = StorageType::LOCAL;
+ aws_flag = false;
+ use_endpoint = false;
+ aws_log_level = Aws::Utils::Logging::LogLevel::Off;
+ endpoint_override = std::nullopt;
+ proxy_host = std::nullopt;
+ proxy_port = std::nullopt;
+ proxy_scheme = std::nullopt;
+
bool parsingSuccessful = reader.parse(file, json_config);
if (!parsingSuccessful) {
- std::cout << "Error parsing config file." << std::endl;
- std::cout << "Exiting..." << std::endl;
- exit(0);
+ std::string error_message =
+ "VDMSConfig() Error: parsing the config file: " + config_file + ".\n" +
+ reader.getFormatedErrorMessages();
+ throw std::runtime_error(error_message);
}
build_dirs();
@@ -134,7 +153,6 @@ void VDMSConfig::expand_directory_layer(
tmp_stream << std::internal << std::setfill('0')
<< std::setw(CHARS_PER_LAYER_NAME) << i;
tmp_directory_list->push_back(tmp_stream.str() + "/");
- // std::cout << (*tmp_directory_list)[i] << std::endl;
}
p_directory_list->push_back(tmp_directory_list);
} else {
@@ -147,8 +165,6 @@ void VDMSConfig::expand_directory_layer(
tmp_directory_list->push_back(
(*(*p_directory_list)[p_directory_list->size() - 1])[j] +
tmp_stream.str() + "/");
- // std::cout << (*tmp_directory_list)[tmp_directory_list->size() - 1] <<
- // std::endl;
}
}
p_directory_list->push_back(tmp_directory_list);
@@ -187,14 +203,15 @@ int VDMSConfig::create_dir(std::string path) {
}
void VDMSConfig::check_or_create(std::string path) {
- if (create_dir(path) == 0) {
- return;
- } else {
- std::cout << "Cannot open/create directories structure." << std::endl;
- std::cout << "Failed dir: " << path << std::endl;
- std::cout << "Check paths and permissions." << std::endl;
- std::cout << "Exiting..." << std::endl;
- exit(0);
+ if (create_dir(path) != 0) {
+ std::string error_message = "Cannot open/create directories structure.\n"
+ "Failed dir: " +
+ path +
+ ".\n"
+ "Check paths and permissions." +
+ ".\n"
+ "Exiting...\n";
+ throw std::runtime_error(error_message);
}
}
@@ -296,6 +313,7 @@ void VDMSConfig::build_dirs() {
// specified in the config file then it uses DEFAULT_ENDPOINT
// as default endpoint value
endpoint_override = std::optional{DEFAULT_ENDPOINT};
+ std::cerr << "Warning: Using default endpoint_override" << std::endl;
}
}
break;
diff --git a/src/VDMSConfig.h b/src/VDMSConfig.h
index c5cf822a..298e3301 100644
--- a/src/VDMSConfig.h
+++ b/src/VDMSConfig.h
@@ -58,6 +58,9 @@
#define PARAM_DB_TMP "tmp_path"
#define PARAM_STORAGE_TYPE "storage_type"
#define PARAM_BUCKET_NAME "bucket_name"
+#define PARAM_CERT_FILE "cert_file"
+#define PARAM_KEY_FILE "key_file"
+#define PARAM_CA_FILE "ca_file"
#define PARAM_NODE_EXPIRATION "expiration_time"
#define DEFAULT_NODE_EXPIRATION 0
@@ -93,11 +96,56 @@ class VDMSConfig {
public:
static bool init(std::string config_file);
- static void destroy();
+ static bool destroy();
+
+ /******************************************
+ * VDMSConfig should not be cloneable
+ *******************************************/
+ VDMSConfig(VDMSConfig &other) = delete;
+
+ /*******************************************
+ * VDMSConfig should not be assignable.
+ ********************************************/
+ void operator=(const VDMSConfig &) = delete;
+
static VDMSConfig *instance();
-private:
+ int get_int_value(std::string val, int def);
+ std::string get_string_value(std::string val, std::string def);
+ bool get_bool_value(std::string val, bool def);
+ bool exists_key(const std::string &key);
+ const std::string &get_path_root() { return path_root; }
+ const std::string &get_path_pmgd() { return path_pmgd; }
+ const std::string &get_path_jpg() { return path_jpg; }
+ const std::string &get_path_png() { return path_png; }
+ const std::string &get_path_bin() { return path_bin; }
+ const std::string &get_path_tdb() { return path_tdb; }
+ const std::string &get_path_blobs() { return path_blobs; }
+ const std::string &get_path_videos() { return path_videos; }
+ const std::string &get_path_descriptors() { return path_descriptors; }
+ const std::string &get_path_tmp() { return path_tmp; }
+ const StorageType &get_storage_type() { return storage_type; }
+ const std::string &get_bucket_name() { return aws_bucket_name; }
+ const bool &get_aws_flag() { return aws_flag; }
+
+ std::optional get_endpoint_override() {
+ return endpoint_override;
+ }
+ const std::optional &get_proxy_host() { return proxy_host; }
+ const std::optional &get_proxy_port() { return proxy_port; }
+ const std::optional &get_proxy_scheme() { return proxy_scheme; }
+ const bool &get_use_endpoint() { return use_endpoint; }
+ const Aws::Utils::Logging::LogLevel get_aws_log_level() {
+ return aws_log_level;
+ }
+
+protected:
static VDMSConfig *cfg;
+ static std::mutex _mutex;
+ VDMSConfig(std::string config_file);
+ ~VDMSConfig() {}
+
+private:
Json::Value json_config;
// Dirs
@@ -124,8 +172,6 @@ class VDMSConfig {
std::optional proxy_scheme;
Aws::Utils::Logging::LogLevel aws_log_level;
- VDMSConfig(std::string config_file);
-
void expand_directory_layer(
std::vector *> *p_directory_list,
int current_layer);
@@ -136,34 +182,17 @@ class VDMSConfig {
void check_or_create(std::string path);
int create_dir(std::string path);
-public:
- int get_int_value(std::string val, int def);
- std::string get_string_value(std::string val, std::string def);
- bool get_bool_value(std::string val, bool def);
- bool exists_key(const std::string &key);
- const std::string &get_path_root() { return path_root; }
- const std::string &get_path_pmgd() { return path_pmgd; }
- const std::string &get_path_jpg() { return path_jpg; }
- const std::string &get_path_png() { return path_png; }
- const std::string &get_path_bin() { return path_bin; }
- const std::string &get_path_tdb() { return path_tdb; }
- const std::string &get_path_blobs() { return path_blobs; }
- const std::string &get_path_videos() { return path_videos; }
- const std::string &get_path_descriptors() { return path_descriptors; }
- const std::string &get_path_tmp() { return path_tmp; }
- const StorageType &get_storage_type() { return storage_type; }
- const std::string &get_bucket_name() { return aws_bucket_name; }
- const bool &get_aws_flag() { return aws_flag; }
-
- std::optional get_endpoint_override() {
- return endpoint_override;
- }
- const std::optional &get_proxy_host() { return proxy_host; }
- const std::optional &get_proxy_port() { return proxy_port; }
- const std::optional &get_proxy_scheme() { return proxy_scheme; }
- const bool &get_use_endpoint() { return use_endpoint; }
- const Aws::Utils::Logging::LogLevel get_aws_log_level() & {
- return aws_log_level;
+ VDMSConfig *getCfg() { return cfg; }
+ VDMSConfig() {
+ cfg = nullptr;
+ storage_type = StorageType::LOCAL;
+ aws_flag = false;
+ use_endpoint = false;
+ aws_log_level = Aws::Utils::Logging::LogLevel::Off;
+ endpoint_override = std::nullopt;
+ proxy_host = std::nullopt;
+ proxy_port = std::nullopt;
+ proxy_scheme = std::nullopt;
}
};
diff --git a/src/VideoCommand.cc b/src/VideoCommand.cc
index 291c3b4f..128bd4ae 100644
--- a/src/VideoCommand.cc
+++ b/src/VideoCommand.cc
@@ -141,8 +141,9 @@ int AddVideo::construct_protobuf(PMGDQuery &query, const Json::Value &jsoncmd,
int node_ref = get_value(cmd, "_ref", query.get_available_reference());
- const std::string from_server_file =
- get_value(cmd, "from_server_file", "");
+ const std::string from_file_path =
+ get_value(cmd, "from_file_path", "");
+ const bool is_local_file = get_value(cmd, "is_local_file", false);
VCL::Video video;
if (_use_aws_storage) {
@@ -152,10 +153,15 @@ int AddVideo::construct_protobuf(PMGDQuery &query, const Json::Value &jsoncmd,
video.set_connection(connection);
}
- if (from_server_file.empty())
+ if (from_file_path.empty())
video = VCL::Video((void *)blob.data(), blob.size());
- else
- video = VCL::Video(from_server_file);
+ else {
+ if (is_local_file) {
+ video = VCL::Video(from_file_path, false);
+ } else {
+ video = VCL::Video(from_file_path, true);
+ }
+ }
// Key frame extraction works on binary stream data, without encoding. We
// check whether key-frame extraction is to be applied, and if so, we
@@ -183,6 +189,10 @@ int AddVideo::construct_protobuf(PMGDQuery &query, const Json::Value &jsoncmd,
Json::Value props = get_value(cmd, "properties");
props[VDMS_VID_PATH_PROP] = file_name;
+ if (video.is_blob_not_stored()) {
+ props[VDMS_VID_PATH_PROP] = video.get_video_id();
+ }
+
// Add Video node
query.AddNode(node_ref, VDMS_VID_TAG, props, Json::Value());
@@ -196,8 +206,12 @@ int AddVideo::construct_protobuf(PMGDQuery &query, const Json::Value &jsoncmd,
}
if (_use_aws_storage) {
- video._remote->Write(file_name);
+ bool result = video._remote->Write(file_name);
std::remove(file_name.c_str()); // remove the local copy of the file
+ if (!result) {
+ throw VCLException(ObjectNotFound,
+ "Add video: Path to the file was not found");
+ }
}
// Add key-frames (if extracted) as nodes connected to the video
@@ -233,7 +247,7 @@ Json::Value AddVideo::construct_responses(Json::Value &response,
bool AddVideo::need_blob(const Json::Value &cmd) {
const Json::Value &add_video_cmd = cmd[_cmd_name];
- return !(add_video_cmd.isMember("from_server_file"));
+ return !(add_video_cmd.isMember("from_file_path"));
}
//========= UpdateVideo definitions =========
@@ -357,9 +371,15 @@ Json::Value FindVideo::construct_responses(Json::Value &responses,
connection->_bucket_name = bucket;
VCL::Video video(video_path);
video.set_connection(connection);
- video._remote->Read_Video(
+ bool result = video._remote->Read_Video(
video_path); // this takes the file from aws and puts it back in
// the local database location
+ if (!result) {
+ Json::Value return_error;
+ return_error["status"] = RSCommand::Error;
+ return_error["info"] = "Path to the video was not found";
+ return error(return_error);
+ }
}
// Return video as is.
@@ -600,15 +620,22 @@ Json::Value FindFrames::construct_responses(Json::Value &responses,
connection->_bucket_name = bucket;
VCL::Video video(video_path);
video.set_connection(connection);
- video._remote->Read_Video(
+ bool result = video._remote->Read_Video(
video_path); // this takes the file from aws and puts it back in the
// local database location
+
+ if (!result) {
+ Json::Value return_error;
+ return_error["status"] = RSCommand::Error;
+ return_error["info"] = "Path to the video was not found";
+ return error(return_error);
+ }
}
VCL::Video video(video_path);
// By default, return frames as PNGs
- VCL::Image::Format format = VCL::Image::Format::PNG;
+ VCL::Format format = VCL::Format::PNG;
FindImage img_cmd;
@@ -616,8 +643,7 @@ Json::Value FindFrames::construct_responses(Json::Value &responses,
format = img_cmd.get_requested_format(cmd);
- if (format == VCL::Image::Format::NONE_IMAGE ||
- format == VCL::Image::Format::TDB) {
+ if (format == VCL::Format::NONE_IMAGE || format == VCL::Format::TDB) {
Json::Value return_error;
return_error["status"] = RSCommand::Error;
return_error["info"] = "Invalid Return Format for FindFrames";
diff --git a/src/VideoLoop.cc b/src/VideoLoop.cc
index 9ce18a54..e199f522 100644
--- a/src/VideoLoop.cc
+++ b/src/VideoLoop.cc
@@ -2,6 +2,8 @@
#include "vcl/Exception.h"
#include
+#include "VDMSConfig.h"
+
VideoLoop::~VideoLoop() noexcept {
VCL::Video video(videoMap.begin()->first);
m_running = false;
@@ -117,6 +119,8 @@ void VideoLoop::operationThread() noexcept {
std::pair(video.get_video_id(), video));
if (not result.second) {
result.first->second = video;
+ result.first->second.set_operated_video_id(
+ video.get_operated_video_id());
}
}
}
@@ -265,7 +269,8 @@ void VideoLoop::execute_remote_operations(std::vector &readBuffer) {
auto time_now = std::chrono::system_clock::now();
std::chrono::duration utc_time = time_now.time_since_epoch();
std::string response_filepath =
- "/tmp/rtempfile" + std::to_string(utc_time.count()) + "." + format;
+ VDMS::VDMSConfig::instance()->get_path_tmp() + "/rtempfile" +
+ std::to_string(utc_time.count()) + "." + format;
responseBuffer.push_back(response_filepath);
CURL *curl = get_easy_handle(video, responseBuffer[rindex]);
diff --git a/src/vcl/DescriptorSet.cc b/src/vcl/DescriptorSet.cc
index e4ee990e..5592f15a 100644
--- a/src/vcl/DescriptorSet.cc
+++ b/src/vcl/DescriptorSet.cc
@@ -189,7 +189,11 @@ void DescriptorSet::store() {
}
for (int i = 0; i < filenames.size(); i++) {
- _remote->Write(filenames[i]);
+ bool result = _remote->Write(filenames[i]);
+ if (!result) {
+ throw VCLException(ObjectNotFound,
+ "Descriptor: File was not added: " + filenames[i]);
+ }
// std::remove(filename.c_str());
}
}
diff --git a/src/vcl/Image.cc b/src/vcl/Image.cc
index 6a95207e..10af6a43 100644
--- a/src/vcl/Image.cc
+++ b/src/vcl/Image.cc
@@ -30,6 +30,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -38,6 +40,8 @@
#include "vcl/Exception.h"
#include "vcl/Image.h"
+#include "../VDMSConfig.h"
+
using namespace VCL;
/* *********************** */
@@ -48,14 +52,14 @@ using namespace VCL;
/* READ OPERATION */
/* *********************** */
-Image::Read::Read(const std::string &filename, Image::Format format)
+Image::Read::Read(const std::string &filename, VCL::Format format)
: Operation(format), _fullpath(filename) {}
void Image::Read::operator()(Image *img) {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
if (img->_tdb == NULL)
- throw VCLException(TileDBNotFound, "Image::Format indicates image \
+ throw VCLException(TileDBNotFound, "VCL::Format indicates image \
stored in TDB format, but no data was found");
img->_tdb->read();
@@ -63,12 +67,19 @@ void Image::Read::operator()(Image *img) {
img->_width = img->_tdb->get_image_width();
img->_channels = img->_tdb->get_image_channels();
} else if (img->_storage == VDMS::StorageType::LOCAL) {
- if (_format == Image::Format::BIN) {
+ if (_format == VCL::Format::BIN) {
+
FILE *bin_file;
bin_file = fopen(_fullpath.c_str(), "rb");
if (bin_file != NULL) {
+ // Prevent a possible memory leak
+ if (nullptr != img->_bin) {
+ free(img->_bin);
+ img->_bin = nullptr;
+ }
+
img->_bin = (char *)malloc(sizeof(char) * img->_bin_size);
- if (img->_bin != NULL)
+ if (nullptr != img->_bin)
fread(img->_bin, 1, img->_bin_size, bin_file);
fclose(bin_file);
} else {
@@ -96,13 +107,14 @@ void Image::Read::operator()(Image *img) {
/* WRITE OPERATION */
/* *********************** */
-Image::Write::Write(const std::string &filename, Image::Format format,
- Image::Format old_format, bool metadata)
+Image::Write::Write(const std::string &filename, VCL::Format format,
+ VCL::Format old_format, bool metadata)
: Operation(format), _old_format(old_format), _metadata(metadata),
_fullpath(filename) {}
+// image1.jpg
void Image::Write::operator()(Image *img) {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
if (img->_tdb == NULL) {
if (img->_storage == VDMS::StorageType::LOCAL) {
img->_tdb = new TDBImage(_fullpath);
@@ -124,7 +136,7 @@ void Image::Write::operator()(Image *img) {
} else {
img->_tdb->write(img->_cv_img, _metadata);
}
- } else if (_format == Image::Format::BIN) // TODO: Implement Remote
+ } else if (_format == VCL::Format::BIN) // TODO: Implement Remote
{
FILE *bin_file;
bin_file = fopen(_fullpath.c_str(), "wb");
@@ -136,7 +148,7 @@ void Image::Write::operator()(Image *img) {
}
} else {
cv::Mat cv_img;
- if (_old_format == Image::Format::TDB)
+ if (_old_format == VCL::Format::TDB)
cv_img = img->_tdb->get_cvmat();
else
cv_img = img->_cv_img;
@@ -146,9 +158,13 @@ void Image::Write::operator()(Image *img) {
cv::imwrite(_fullpath, cv_img);
} else {
std::vector data;
- std::string ext = "." + img->format_to_string(_format);
+ std::string ext = "." + VCL::format_to_string(_format);
cv::imencode(ext, cv_img, data);
- img->_remote->Write(_fullpath, data);
+ bool result = img->_remote->Write(_fullpath, data);
+ if (!result) {
+ throw VCLException(ObjectNotFound,
+ "Image: Path to the file was not found");
+ }
}
} else
throw VCLException(ObjectEmpty,
@@ -162,7 +178,7 @@ void Image::Write::operator()(Image *img) {
void Image::Resize::operator()(Image *img) {
try {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
img->_tdb->resize(_rect);
img->_height = img->_tdb->get_image_height();
img->_width = img->_tdb->get_image_width();
@@ -190,7 +206,7 @@ void Image::Resize::operator()(Image *img) {
void Image::Crop::operator()(Image *img) {
try {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
img->_tdb->read(_rect);
img->_height = img->_tdb->get_image_height();
img->_width = img->_tdb->get_image_width();
@@ -220,7 +236,7 @@ void Image::Crop::operator()(Image *img) {
void Image::Threshold::operator()(Image *img) {
try {
- if (_format == Image::Format::TDB)
+ if (_format == VCL::Format::TDB)
img->_tdb->threshold(_threshold);
else {
if (!img->_cv_img.empty())
@@ -243,7 +259,7 @@ void Image::Threshold::operator()(Image *img) {
void Image::Flip::operator()(Image *img) {
try {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
// Not implemented
throw VCLException(NotImplemented,
"Operation not supported for this format");
@@ -270,7 +286,7 @@ void Image::Flip::operator()(Image *img) {
void Image::Rotate::operator()(Image *img) {
try {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
// Not implemented
throw VCLException(NotImplemented,
"Operation not supported for this format");
@@ -320,7 +336,7 @@ void Image::Rotate::operator()(Image *img) {
void Image::RemoteOperation::operator()(Image *img) {
try {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
// Not implemented
throw VCLException(NotImplemented,
"Operation not supported for this format");
@@ -348,7 +364,7 @@ size_t writeCallback(char *ip, size_t size, size_t nmemb, void *op) {
void Image::SyncRemoteOperation::operator()(Image *img) {
try {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
// Not implemented
throw VCLException(NotImplemented,
"Operation not supported for this format");
@@ -370,8 +386,8 @@ void Image::SyncRemoteOperation::operator()(Image *img) {
auto time_now = std::chrono::system_clock::now();
std::chrono::duration utc_time = time_now.time_since_epoch();
- VCL::Image::Format img_format = img->get_image_format();
- std::string format = img->format_to_string(img_format);
+ VCL::Format img_format = img->get_image_format();
+ std::string format = VCL::format_to_string(img_format);
if (format == "" && _options.isMember("format")) {
format = _options["format"].toStyledString().data();
@@ -383,7 +399,8 @@ void Image::SyncRemoteOperation::operator()(Image *img) {
}
std::string filePath =
- "/tmp/tempfile" + std::to_string(utc_time.count()) + "." + format;
+ VDMS::VDMSConfig::instance()->get_path_tmp() + "/tempfile" +
+ std::to_string(utc_time.count()) + "." + format;
cv::imwrite(filePath, img->_cv_img);
std::ofstream tsfile;
@@ -493,7 +510,7 @@ void Image::SyncRemoteOperation::operator()(Image *img) {
void Image::UserOperation::operator()(Image *img) {
try {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
// Not implemented
throw VCLException(NotImplemented,
"Operation not supported for this format");
@@ -513,8 +530,8 @@ void Image::UserOperation::operator()(Image *img) {
auto time_now = std::chrono::system_clock::now();
std::chrono::duration utc_time = time_now.time_since_epoch();
- VCL::Image::Format img_format = img->get_image_format();
- std::string format = img->format_to_string(img_format);
+ VCL::Format img_format = img->get_image_format();
+ std::string format = VCL::format_to_string(img_format);
if (format == "" && _options.isMember("format")) {
format = _options["format"].toStyledString().data();
@@ -525,8 +542,9 @@ void Image::UserOperation::operator()(Image *img) {
format = "jpg";
}
- std::string filePath =
- "/tmp/tempfile" + std::to_string(utc_time.count()) + "." + format;
+ std::string filePath = VDMS::VDMSConfig::instance()->get_path_tmp() +
+ "/tempfile" + std::to_string(utc_time.count()) +
+ "." + format;
cv::imwrite(filePath, img->_cv_img);
_options["ipfile"] = filePath;
@@ -589,7 +607,7 @@ Image::Image() {
_width = 0;
_cv_type = CV_8UC3;
- _format = Image::Format::NONE_IMAGE;
+ _format = VCL::Format::NONE_IMAGE;
_compress = CompressionType::LZ4;
_tdb = nullptr;
@@ -602,7 +620,6 @@ Image::Image() {
Image::Image(const std::string &image_id, std::string bucket_name) {
_remote = nullptr;
-
if (bucket_name.length() != 0) {
VCL::RemoteConnection *connection = new VCL::RemoteConnection();
connection->_bucket_name = bucket_name;
@@ -613,17 +630,15 @@ Image::Image(const std::string &image_id, std::string bucket_name) {
_height = 0;
_width = 0;
_cv_type = CV_8UC3;
- _bin = 0;
+ _bin = nullptr;
_bin_size = 0;
std::string extension = get_extension(image_id);
set_format(extension);
-
_compress = CompressionType::LZ4;
-
_image_id = create_fullpath(image_id, _format);
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
_tdb = new TDBImage(_image_id);
_tdb->set_compression(_compress);
} else
@@ -634,6 +649,30 @@ Image::Image(const std::string &image_id, std::string bucket_name) {
_op_completed = 0;
}
+Image::Image(const std::string &image_id, bool no_blob) {
+ _remote = nullptr;
+
+ _channels = 0;
+ _height = 0;
+ _width = 0;
+ _cv_type = CV_8UC3;
+ _bin = 0;
+ _bin_size = 0;
+
+ std::string extension = get_extension(image_id);
+ set_format(extension);
+
+ _compress = CompressionType::LZ4;
+
+ _image_id = create_fullpath(image_id, _format);
+ _no_blob = no_blob;
+
+ _tdb = nullptr;
+ read(image_id);
+
+ _op_completed = 0;
+}
+
Image::Image(const cv::Mat &cv_img, bool copy) {
if (cv_img.empty()) {
throw VCLException(ObjectEmpty, "Image object is empty");
@@ -646,34 +685,56 @@ Image::Image(const cv::Mat &cv_img, bool copy) {
else
shallow_copy_cv(cv_img);
- _format = Image::Format::NONE_IMAGE;
+ _format = VCL::Format::NONE_IMAGE;
_compress = CompressionType::LZ4;
_image_id = "";
-
_tdb = nullptr;
_bin = nullptr;
_bin_size = 0;
_op_completed = 0;
}
+Image::Image(const std::string &filename, const std::string &blob,
+ VCL::Format input_format) {
+ _channels = 0;
+ _height = 0;
+ _width = 0;
+ _cv_type = CV_8UC3;
+
+ _format = input_format;
+ _compress = CompressionType::LZ4;
+
+ _tdb = nullptr;
+ _image_id = "";
+ _bin = nullptr;
+ _bin_size = 0;
+ _remote = nullptr;
+ _op_completed = 0;
+}
Image::Image(void *buffer, long size, char binary_image_flag, int flags) {
- _bin = 0;
+ _bin = nullptr;
_bin_size = 0;
_remote = nullptr;
_tdb = nullptr;
- _bin = nullptr;
+ // _bin = nullptr; Duplicated?
+ if (size < 0) {
+ std::cerr << "Error: Invalid buffer size." << std::endl;
+ return;
+ }
+
+ _format = VCL::Format::NONE_IMAGE;
+
set_data_from_encoded(buffer, size, binary_image_flag, flags);
- _format = Image::Format::NONE_IMAGE;
_compress = CompressionType::LZ4;
_image_id = "";
_op_completed = 0;
}
Image::Image(void *buffer, cv::Size dimensions, int cv_type) {
- _bin = 0;
+ _bin = nullptr;
_bin_size = 0;
_remote = nullptr;
@@ -682,7 +743,7 @@ Image::Image(void *buffer, cv::Size dimensions, int cv_type) {
_cv_type = cv_type;
_channels = (cv_type / 8) + 1;
- _format = Image::Format::TDB;
+ _format = VCL::Format::TDB;
_compress = CompressionType::LZ4;
_image_id = "";
@@ -693,7 +754,7 @@ Image::Image(void *buffer, cv::Size dimensions, int cv_type) {
}
Image::Image(const Image &img, bool copy) {
- _bin = 0;
+ _bin = nullptr;
_bin_size = 0;
_remote = nullptr;
@@ -705,6 +766,7 @@ Image::Image(const Image &img, bool copy) {
_format = img._format;
_compress = img._compress;
_image_id = img._image_id;
+ _no_blob = img._no_blob;
if (!(img._cv_img).empty()) {
if (copy) {
@@ -738,9 +800,10 @@ Image::Image(const Image &img, bool copy) {
}
Image::Image(Image &&img) noexcept {
- _bin = 0;
+ _bin = nullptr;
_bin_size = 0;
_remote = nullptr;
+ _no_blob = img._no_blob;
_format = img._format;
_compress = img._compress;
@@ -758,7 +821,7 @@ Image::Image(Image &&img) noexcept {
Image &Image::operator=(const Image &img) {
TDBImage *temp = _tdb;
- _bin = 0;
+ _bin = nullptr;
_bin_size = 0;
if (!(img._cv_img).empty())
deep_copy_cv(img._cv_img);
@@ -774,6 +837,7 @@ Image &Image::operator=(const Image &img) {
_format = img._format;
_compress = img._compress;
_image_id = img._image_id;
+ _no_blob = img._no_blob;
if (img._tdb != NULL) {
_tdb = new TDBImage(*img._tdb);
@@ -809,17 +873,43 @@ Image &Image::operator=(const Image &img) {
Image::~Image() {
_operations.clear();
_operations.shrink_to_fit();
- delete _tdb;
- if (_bin)
+ if (_tdb) {
+ delete _tdb;
+ _tdb = nullptr;
+ }
+
+ if (_bin) {
free(_bin);
+ _bin = nullptr;
+ }
}
/* *********************** */
/* GET FUNCTIONS */
/* *********************** */
+void Image::save_image(const std::string &fullpath, const std::string &blob) {
+ if (_storage == VDMS::StorageType::LOCAL) {
+ FILE *bin_file = fopen(fullpath.c_str(), "wb");
+ if (bin_file != NULL) {
+ // Write the blob data to the binary file
+ fwrite(blob.data(), sizeof(char), blob.size(), bin_file);
+ fclose(bin_file); // Close the file when done
+ } else {
+ // Handle the case where the file couldn't be opened
+ std::cerr << "Failed to open " << fullpath << " for writing."
+ << std::endl;
+ }
+ } else {
+ // store the image in AWS
+ std::vector data(blob.begin(), blob.end());
+ _remote->Write(fullpath, data);
+ }
+}
std::string Image::get_image_id() const { return _image_id; }
+bool Image::is_blob_not_stored() const { return _no_blob; }
+
cv::Size Image::get_dimensions(bool performOp) {
// TODO: iterate over operations themsevles to determine
// image size, rather than performing the operations.
@@ -828,13 +918,13 @@ cv::Size Image::get_dimensions(bool performOp) {
return cv::Size(_width, _height);
}
-Image::Format Image::get_image_format() const { return _format; }
+VCL::Format Image::get_image_format() const { return _format; }
long Image::get_raw_data_size() {
if (_height == 0) {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
if (_tdb == NULL)
- throw VCLException(TileDBNotFound, "Image::Format indicates image \
+ throw VCLException(TileDBNotFound, "VCL::Format indicates image \
stored in TDB format, but no data was found");
return _tdb->get_image_size();
} else {
@@ -852,9 +942,9 @@ int Image::get_image_type() const { return _cv_type; }
Image Image::get_area(const Rectangle &roi, bool performOp) const {
Image area(*this);
- if (area._format == Image::Format::TDB && area._operations.size() == 1) {
+ if (area._format == VCL::Format::TDB && area._operations.size() == 1) {
if (area._tdb == NULL)
- throw VCLException(TileDBNotFound, "Image::Format indicates image \
+ throw VCLException(TileDBNotFound, "VCL::Format indicates image \
stored in TDB format, but no data was found");
area._operations.pop_back();
}
@@ -876,7 +966,7 @@ cv::Mat Image::get_cvmat(bool copy, bool performOp) {
if (performOp)
perform_operations();
- cv::Mat mat = (_format == Format::TDB) ? _tdb->get_cvmat() : _cv_img;
+ cv::Mat mat = (_format == VCL::Format::TDB) ? _tdb->get_cvmat() : _cv_img;
if (copy)
return mat.clone();
@@ -890,43 +980,43 @@ void Image::get_raw_data(void *buffer, long buffer_size, bool performOp) {
switch (_cv_type % 8) {
case 0:
- if (_format != Format::TDB)
+ if (_format != VCL::Format::TDB)
copy_to_buffer(static_cast(buffer));
else
_tdb->get_buffer(static_cast(buffer), buffer_size);
break;
case 1:
- if (_format != Format::TDB)
+ if (_format != VCL::Format::TDB)
copy_to_buffer(static_cast(buffer));
else
_tdb->get_buffer(static_cast(buffer), buffer_size);
break;
case 2:
- if (_format != Format::TDB)
+ if (_format != VCL::Format::TDB)
copy_to_buffer(static_cast(buffer));
else
_tdb->get_buffer(static_cast(buffer), buffer_size);
break;
case 3:
- if (_format != Format::TDB)
+ if (_format != VCL::Format::TDB)
copy_to_buffer(static_cast(buffer));
else
_tdb->get_buffer(static_cast(buffer), buffer_size);
break;
case 4:
- if (_format != Format::TDB)
+ if (_format != VCL::Format::TDB)
copy_to_buffer(static_cast(buffer));
else
_tdb->get_buffer(static_cast(buffer), buffer_size);
break;
case 5:
- if (_format != Format::TDB)
+ if (_format != VCL::Format::TDB)
copy_to_buffer(static_cast(buffer));
else
_tdb->get_buffer(static_cast(buffer), buffer_size);
break;
case 6:
- if (_format != Format::TDB)
+ if (_format != VCL::Format::TDB)
copy_to_buffer(static_cast(buffer));
else
_tdb->get_buffer(static_cast(buffer), buffer_size);
@@ -947,12 +1037,14 @@ Json::Value Image::get_remoteOp_params() { return remoteOp_params; }
std::string Image::get_query_error_response() { return _query_error_response; }
std::vector
-Image::get_encoded_image(Image::Format format, const std::vector ¶ms) {
-
- // When data is stored in raw binary format, read data from file
- if (format == VCL::Image::Format::BIN) {
+Image::get_encoded_image(VCL::Format format, const std::vector ¶ms) {
+ if (format == VCL::Format::BIN) {
std::ifstream bin_image(_image_id, std::ios::in | std::ifstream::binary);
+ if (!bin_image) {
+ std::cerr << "Error opening image file: " << _image_id << std::endl;
+ }
long file_size = bin_image.tellg();
+
bin_image.seekg(0, std::ios::end);
file_size = bin_image.tellg() - file_size;
std::vector buffer(file_size, 0);
@@ -984,11 +1076,11 @@ Image::get_encoded_image(Image::Format format, const std::vector ¶ms) {
}
std::vector
-Image::get_encoded_image_async(Image::Format format,
+Image::get_encoded_image_async(VCL::Format format,
const std::vector ¶ms) {
// When data is stored in raw binary format, read data from file
- if (format == VCL::Image::Format::BIN) {
+ if (format == VCL::Format::BIN) {
std::ifstream bin_image(_image_id, std::ios::in | std::ifstream::binary);
long file_size = bin_image.tellg();
bin_image.seekg(0, std::ios::end);
@@ -1058,12 +1150,15 @@ void Image::set_data_from_encoded(void *buffer, long size,
// with raw binary files, we simply copy the data and do not encode
if (binary_image_flag) {
_bin_size = size;
+ if (_bin) {
+ free(_bin);
+ _bin = nullptr;
+ }
_bin = (char *)malloc(sizeof(char) * size);
memcpy(_bin, buffer, size);
} else {
cv::Mat raw_data(cv::Size(size, 1), CV_8UC1, buffer);
cv::Mat img = cv::imdecode(raw_data, flags);
-
if (img.empty()) {
throw VCLException(ObjectEmpty, "Image object is empty");
}
@@ -1082,9 +1177,9 @@ void Image::set_dimensions(cv::Size dims) {
_height = dims.height;
_width = dims.width;
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
if (_tdb == NULL)
- throw VCLException(TileDBNotFound, "Image::Format indicates image \
+ throw VCLException(TileDBNotFound, "VCL::Format indicates image \
stored in TDB format, but no data was found");
_tdb->set_image_properties(_height, _width, _channels);
}
@@ -1092,13 +1187,13 @@ void Image::set_dimensions(cv::Size dims) {
void Image::set_format(const std::string &extension) {
if (extension == "jpg")
- _format = Image::Format::JPG;
+ _format = VCL::Format::JPG;
else if (extension == "png")
- _format = Image::Format::PNG;
+ _format = VCL::Format::PNG;
else if (extension == "tdb")
- _format = Image::Format::TDB;
+ _format = VCL::Format::TDB;
else if (extension == "bin")
- _format = Image::Format::BIN;
+ _format = VCL::Format::BIN;
else
throw VCLException(UnsupportedFormat, extension + " is not a \
supported format");
@@ -1111,9 +1206,9 @@ void Image::set_image_type(int cv_type) {
}
void Image::set_minimum_dimension(int dimension) {
- if (_format == Image::Format::TDB) {
+ if (_format == VCL::Format::TDB) {
if (_tdb == NULL)
- throw VCLException(TileDBNotFound, "Image::Format indicates image \
+ throw VCLException(TileDBNotFound, "VCL::Format indicates image \
stored in TDB format, but no data was found\n");
_tdb->set_minimum(dimension);
}
@@ -1131,6 +1226,10 @@ void Image::set_query_error_response(std::string response_error) {
void Image::update_op_completed() { _op_completed++; }
void Image::set_connection(RemoteConnection *remote) {
+ if (!remote) {
+ throw VCLException(SystemNotFound, "Invalid remote connection");
+ }
+
if (!remote->connected())
remote->start();
@@ -1192,7 +1291,7 @@ void Image::read(const std::string &image_id) {
_operations.push_back(std::make_shared(_image_id, _format));
}
-void Image::store(const std::string &image_id, Image::Format image_format,
+void Image::store(const std::string &image_id, VCL::Format image_format,
bool store_metadata) {
_operations.push_back(
std::make_shared(create_fullpath(image_id, image_format),
@@ -1200,15 +1299,18 @@ void Image::store(const std::string &image_id, Image::Format image_format,
perform_operations();
}
-void Image::delete_image() {
+bool Image::delete_image() {
if (_tdb != NULL)
_tdb->delete_image();
if (exists(_image_id)) {
std::remove(_image_id.c_str());
} else if (_remote != NULL) {
- _remote->Remove_Object(_image_id);
+ bool result = _remote->Remove_Object(_image_id);
+ return result;
}
+
+ return true;
}
void Image::resize(int new_height, int new_width) {
@@ -1219,7 +1321,7 @@ void Image::resize(int new_height, int new_width) {
void Image::crop(const Rectangle &rect) {
if (_format == Format::TDB && _operations.size() == 1) {
if (_tdb == NULL)
- throw VCLException(TileDBNotFound, "Image::Format indicates image \
+ throw VCLException(TileDBNotFound, "VCL::Format indicates image \
stored in TDB format, but no data was found");
_operations.pop_back();
}
@@ -1273,9 +1375,7 @@ void Image::shallow_copy_cv(const cv::Mat &cv_img) {
_height = cv_img.rows;
_width = cv_img.cols;
-
_cv_type = cv_img.type();
-
_cv_img = cv_img; // shallow copy
}
@@ -1314,33 +1414,15 @@ template void Image::copy_to_buffer(T *buffer) {
/* *********************** */
std::string Image::create_fullpath(const std::string &filename,
- Image::Format format) {
+ VCL::Format format) {
if (filename == "")
throw VCLException(ObjectNotFound, "Location to write object is undefined");
std::string extension = get_extension(filename);
- std::string ext = format_to_string(format);
+ std::string ext = VCL::format_to_string(format);
if (ext.compare(extension) == 0 || ext == "")
return filename;
else
return filename + "." + ext;
}
-
-std::string Image::format_to_string(Image::Format format) {
- switch (format) {
- case Image::Format::NONE_IMAGE:
- return "";
- case Image::Format::JPG:
- return "jpg";
- case Image::Format::PNG:
- return "png";
- case Image::Format::TDB:
- return "tdb";
- case Image::Format::BIN:
- return "bin";
- default:
- throw VCLException(UnsupportedFormat, (int)format + " is not a \
- valid format");
- }
-}
diff --git a/src/vcl/RemoteConnection.cc b/src/vcl/RemoteConnection.cc
index c04b2340..b7655578 100644
--- a/src/vcl/RemoteConnection.cc
+++ b/src/vcl/RemoteConnection.cc
@@ -30,16 +30,18 @@
* This file declares the C++ API for RemoteConnection, which allows users to
* connect to different file systems. At the moment, S3 is enabled.
*/
+#include
-#include "../../include/vcl/RemoteConnection.h"
#include "../../include/VDMSConfigHelper.h"
+#include "../../include/vcl/Exception.h"
+#include "../../include/vcl/RemoteConnection.h"
#include "../../src/VDMSConfig.h"
using namespace VCL;
+namespace fs = std::filesystem;
// CONSTRUCTOR
RemoteConnection::RemoteConnection() {
- // LogEntry(__FUNCTION__);
_remote_connected = false;
_aws_client = nullptr;
_aws_sdk_options = nullptr;
@@ -49,304 +51,461 @@ RemoteConnection::RemoteConnection() {
RemoteConnection::~RemoteConnection() {}
void RemoteConnection::start() {
- // LogEntry(__FUNCTION__);
- ConfigureAws();
+ try {
+ ConfigureAws();
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::start", ex.what());
+ }
}
void RemoteConnection::end() {
- // LogEntry(__FUNCTION__);
- ShutdownAws();
+ try {
+ ShutdownAws();
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::end", ex.what());
+ }
}
void RemoteConnection::ConfigureAws() {
- // LogEntry(__FUNCTION__);
+ try {
+ _aws_sdk_options = new Aws::SDKOptions();
+ Aws::InitAPI(*_aws_sdk_options);
- _aws_sdk_options = new Aws::SDKOptions();
- Aws::InitAPI(*_aws_sdk_options);
+ Aws::Client::ClientConfiguration clientConfig;
- Aws::Client::ClientConfiguration clientConfig;
+ std::optional value = std::nullopt;
+ if (value = VDMS::VDMSConfig::instance()->get_proxy_host()) {
+ clientConfig.proxyHost = *value;
+ }
- std::optional value = std::nullopt;
- if (value = VDMS::VDMSConfig::instance()->get_proxy_host()) {
- clientConfig.proxyHost = *value;
- }
+ std::optional port_value = std::nullopt;
+ if (port_value = VDMS::VDMSConfig::instance()->get_proxy_port()) {
+ clientConfig.proxyPort = *port_value;
+ }
- std::optional port_value = std::nullopt;
- if (port_value = VDMS::VDMSConfig::instance()->get_proxy_port()) {
- clientConfig.proxyPort = *port_value;
- }
+ if (value = VDMS::VDMSConfig::instance()->get_proxy_scheme()) {
+ if (*value == "http") {
+ clientConfig.proxyScheme = Aws::Http::Scheme::HTTP;
+ } else if (*value == "https") {
+ clientConfig.proxyScheme = Aws::Http::Scheme::HTTPS;
+ } else {
+ std::cerr << "Error: Invalid scheme in the config file" << std::endl;
+ }
+ }
- if (value = VDMS::VDMSConfig::instance()->get_proxy_scheme()) {
- if (*value == "http") {
- clientConfig.proxyScheme = Aws::Http::Scheme::HTTP;
- } else if (*value == "https") {
- clientConfig.proxyScheme = Aws::Http::Scheme::HTTPS;
- } else {
- std::cerr << "Error: Invalid scheme in the config file" << std::endl;
+ // Use this property to set the endpoint for MinIO when the use_endpoint
+ // value in the config file is equals to true and the storage type is equals
+ // to AWS Format: "http://127.0.0.1:9000";
+ if ((VDMS::VDMSConfig::instance()->get_storage_type() ==
+ VDMS::StorageType::AWS) &&
+ (VDMS::VDMSConfig::instance()->get_use_endpoint()) &&
+ (VDMS::VDMSConfig::instance()->get_endpoint_override())) {
+ value = VDMS::VDMSConfig::instance()->get_endpoint_override();
+ clientConfig.endpointOverride = *value;
}
- }
- // Use this property to set the endpoint for MinIO when the use_endpoint value
- // in the config file is equals to true and the storage type is equals to AWS
- // Format: "http://127.0.0.1:9000";
- if ((VDMS::VDMSConfig::instance()->get_storage_type() ==
- VDMS::StorageType::AWS) &&
- (VDMS::VDMSConfig::instance()->get_use_endpoint()) &&
- (VDMS::VDMSConfig::instance()->get_endpoint_override())) {
- value = VDMS::VDMSConfig::instance()->get_endpoint_override();
- clientConfig.endpointOverride = *value;
- }
+ // Set AWS Logging level
+ if (_aws_sdk_options) {
+ _aws_sdk_options->loggingOptions.logLevel =
+ VDMS::VDMSConfig::instance()->get_aws_log_level();
+ }
- // Set AWS Logging level
- if (_aws_sdk_options) {
- _aws_sdk_options->loggingOptions.logLevel =
- VDMS::VDMSConfig::instance()->get_aws_log_level();
+ _aws_client = new Aws::S3::S3Client(clientConfig);
+ _remote_connected = true;
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::ConfigureAws", ex.what());
}
-
- _aws_client = new Aws::S3::S3Client(clientConfig);
- _remote_connected = true;
}
void RemoteConnection::ShutdownAws() {
- // LogEntry(__FUNCTION__);
- Aws::ShutdownAPI(*_aws_sdk_options);
- _remote_connected = false;
+ try {
+ // LogEntry(__FUNCTION__);
+ Aws::ShutdownAPI(*_aws_sdk_options);
+ _remote_connected = false;
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::ShutdownAws", ex.what());
+ }
}
// image file, takes path to store and vector of data
// TODO: make the raw data a more efficient format?
-void RemoteConnection::Write(const std::string &path,
+bool RemoteConnection::Write(const std::string &path,
std::vector data) {
- if (_remote_connected) {
- write_s3(path, data);
- } else {
- std::cerr << "WRITE: The RemoteConnection has not been started"
- << std::endl;
+ try {
+ if (_remote_connected) {
+ return write_s3(path, data);
+ } else {
+ std::cerr << "WRITE: The RemoteConnection has not been started"
+ << std::endl;
+ return false;
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::Write", ex.what());
}
+ return false;
}
// video file (or any file on disk specified by full path)
// opens file, reads into memory, uploads to AWS
-void RemoteConnection::Write(const std::string &filename) {
- if (_remote_connected) {
- write_s3(filename);
- } else {
- std::cerr << "WRITE: The RemoteConnection has not been started"
- << std::endl;
+bool RemoteConnection::Write(const std::string &filename) {
+ try {
+ if (_remote_connected) {
+ return write_s3(filename);
+ } else {
+ std::cerr << "WRITE: The RemoteConnection has not been started"
+ << std::endl;
+ return false;
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::Write", ex.what());
}
+ return false;
}
-void RemoteConnection::RetrieveFile(const std::string &filename) {
- if (_remote_connected) {
- retrieve_file(filename);
- } else {
- std::cerr << "WRITE: The RemoteConnection has not been started"
- << std::endl;
+bool RemoteConnection::RetrieveFile(const std::string &filename) {
+ try {
+ if (_remote_connected) {
+ return retrieve_file(filename);
+ } else {
+ std::cerr << "WRITE: The RemoteConnection has not been started"
+ << std::endl;
+ return false;
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::RetrieveFile", ex.what());
}
+ return false;
}
std::vector
RemoteConnection::ListFilesInFolder(const std::string &folder_name) {
- if (_remote_connected) {
- return get_file_list(folder_name);
- } else {
- std::cerr << "WRITE: The RemoteConnection has not been started"
- << std::endl;
- return std::vector();
+ try {
+ if (_remote_connected) {
+ return get_file_list(folder_name);
+ } else {
+ std::cerr << "WRITE: The RemoteConnection has not been started"
+ << std::endl;
+ return std::vector();
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::ListFilesInFolder", ex.what());
}
+ return std::vector();
}
std::vector RemoteConnection::Read(const std::string &path) {
- if (_remote_connected) {
- return read_s3(path);
- } else {
- std::cerr << "READ: The RemoteConnection has not been started" << std::endl;
+ try {
+ if (_remote_connected) {
+ return read_s3(path);
+ } else {
+ std::cerr << "READ: The RemoteConnection has not been started"
+ << std::endl;
+ }
+ return std::vector();
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::Read", ex.what());
}
return std::vector();
}
-void RemoteConnection::Read_Video(const std::string &path) {
- if (_remote_connected) {
- read_s3_video(path);
- } else {
- std::cerr << "READ_Video: The RemoteConnection has not been started"
- << std::endl;
+bool RemoteConnection::Read_Video(const std::string &path) {
+ try {
+ bool result = false;
+ if (_remote_connected) {
+ result = read_s3_video(path);
+ } else {
+ std::cerr << "READ_Video: The RemoteConnection has not been started"
+ << std::endl;
+ result = false;
+ }
+
+ return result;
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::Read_Video", ex.what());
}
+
+ return false;
}
-void RemoteConnection::Remove_Object(const std::string &path) {
- if (_remote_connected) {
- return remove_s3_object(path);
- } else {
- std::cerr << "REMOVE: The RemoteConnection has not been started"
- << std::endl;
+bool RemoteConnection::Remove_Object(const std::string &path) {
+ try {
+ if (_remote_connected) {
+ return remove_s3_object(path);
+ } else {
+ std::cerr << "REMOVE: The RemoteConnection has not been started"
+ << std::endl;
+ return false;
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::Remove_Object", ex.what());
}
+ return false;
}
// ########Private S3 Functions########
-void RemoteConnection::write_s3(const std::string &filename) {
- Aws::S3::Model::PutObjectRequest put_request;
- put_request.SetBucket(_bucket_name);
- put_request.SetKey(filename);
+bool RemoteConnection::write_s3(const std::string &filename) {
+ try {
+ if (fs::is_directory(filename)) {
+ std::cerr << "Upload to S3 failed: " << filename
+ << " is a directory instead of a regular file." << std::endl;
+ return false;
+ }
+ Aws::S3::Model::PutObjectRequest put_request;
+ put_request.SetBucket(_bucket_name);
+ put_request.SetKey(filename);
- std::shared_ptr inputData =
- Aws::MakeShared("SampleAllocationTag", filename.c_str(),
- std::ios_base::in | std::ios_base::binary);
+ std::shared_ptr inputData = Aws::MakeShared(
+ "SampleAllocationTag", filename.c_str(),
+ std::ios_base::in | std::ios_base::binary);
- if (!*inputData) {
- std::cerr << "Error unable to read file " << filename << std::endl;
- return;
- }
+ if (!*inputData) {
+ std::cerr << "Error unable to read file " << filename << std::endl;
+ return false;
+ }
- put_request.SetBody(inputData);
+ put_request.SetBody(inputData);
- Aws::S3::Model::PutObjectOutcome outcome =
- _aws_client->PutObject(put_request);
+ Aws::S3::Model::PutObjectOutcome outcome =
+ _aws_client->PutObject(put_request);
- if (!outcome.IsSuccess()) {
- const Aws::S3::S3Error &err = outcome.GetError();
- std::cerr << "Error: PutObject: " << err.GetExceptionName() << ": "
- << err.GetMessage() << std::endl;
- } else {
- std::cout << "Added object '" << filename << "' to bucket: " << _bucket_name
- << std::endl;
+ if (!outcome.IsSuccess()) {
+ const Aws::S3::S3Error &err = outcome.GetError();
+ std::cerr << "Error: PutObject: " << err.GetExceptionName() << ": "
+ << err.GetMessage() << ". Bucket: " << _bucket_name
+ << ", key: " << filename << std::endl;
+ return false;
+ } else {
+ std::cout << "Added object '" << filename
+ << "' to bucket: " << _bucket_name << std::endl;
+ return true;
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::write_s3", ex.what());
}
+ return false;
}
-void RemoteConnection::write_s3(const std::string &path,
+bool RemoteConnection::write_s3(const std::string &path,
std::vector data) {
- Aws::S3::Model::PutObjectRequest put_request;
- put_request.SetBucket(_bucket_name);
- put_request.SetKey(path);
-
- auto input_data = Aws::MakeShared("PutObjectInputStream");
- input_data->write(reinterpret_cast(data.data()), data.size());
-
- put_request.SetBody(input_data);
- Aws::S3::Model::PutObjectOutcome outcome =
- _aws_client->PutObject(put_request);
-
- if (!outcome.IsSuccess()) {
- const Aws::S3::S3Error &err = outcome.GetError();
- std::cerr << "Error: PutObject: " << err.GetExceptionName() << ": "
- << err.GetMessage() << std::endl;
- } else {
- std::cout << "Added object '" << path << "' to bucket: " << _bucket_name
- << std::endl;
+ try {
+ Aws::S3::Model::PutObjectRequest put_request;
+ put_request.SetBucket(_bucket_name);
+ put_request.SetKey(path);
+
+ auto input_data =
+ Aws::MakeShared("PutObjectInputStream");
+ input_data->write(reinterpret_cast(data.data()), data.size());
+
+ put_request.SetBody(input_data);
+ Aws::S3::Model::PutObjectOutcome outcome =
+ _aws_client->PutObject(put_request);
+
+ if (!outcome.IsSuccess()) {
+ const Aws::S3::S3Error &err = outcome.GetError();
+ std::cerr << "Error: PutObject: " << err.GetExceptionName() << ": "
+ << err.GetMessage() << std::endl;
+ return false;
+ } else {
+ std::cout << "Added object '" << path << "' to bucket: " << _bucket_name
+ << std::endl;
+ return true;
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::write_s3", ex.what());
}
+ return false;
}
-void RemoteConnection::read_s3_video(const std::string &file_path) {
- Aws::S3::Model::GetObjectRequest request;
- request.SetBucket(_bucket_name);
- request.SetKey(file_path);
-
- Aws::S3::Model::GetObjectOutcome outcome = _aws_client->GetObject(request);
-
- if (!outcome.IsSuccess()) {
- const Aws::S3::S3Error &err = outcome.GetError();
- std::cerr << "Error: GetObject: " << err.GetExceptionName() << ": "
- << err.GetMessage() << std::endl;
- } else {
- std::cout << "Successfully retrieved '" << file_path << "' from '"
- << _bucket_name << "'." << std::endl;
-
- auto &retrieved_file = outcome.GetResult().GetBody();
- std::ofstream output_file(file_path.c_str(),
- std::ios::out | std::ios::binary);
- output_file << retrieved_file.rdbuf();
+bool RemoteConnection::read_s3_video(const std::string &file_path) {
+ try {
+ Aws::S3::Model::GetObjectRequest request;
+ request.SetBucket(_bucket_name);
+ request.SetKey(file_path);
+
+ Aws::S3::Model::GetObjectOutcome outcome = _aws_client->GetObject(request);
+
+ if (!outcome.IsSuccess()) {
+ const Aws::S3::S3Error &err = outcome.GetError();
+ std::cerr << "Error: GetObject: " << err.GetExceptionName() << ": "
+ << err.GetMessage() << " Key: " << file_path << std::endl;
+ return false;
+ } else {
+ std::cout << "Successfully retrieved '" << file_path << "' from '"
+ << _bucket_name << "'." << std::endl;
+
+ auto &retrieved_file = outcome.GetResult().GetBody();
+ std::ofstream output_file(file_path.c_str(),
+ std::ios::out | std::ios::binary);
+ output_file << retrieved_file.rdbuf();
+ return true;
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::read_s3_video", ex.what());
}
+ return false;
}
std::vector
RemoteConnection::read_s3(const std::string &file_path) {
- Aws::S3::Model::GetObjectRequest request;
- request.SetBucket(_bucket_name);
- request.SetKey(file_path);
-
- Aws::S3::Model::GetObjectOutcome outcome = _aws_client->GetObject(request);
-
- if (!outcome.IsSuccess()) {
- const Aws::S3::S3Error &err = outcome.GetError();
- std::cerr << "Error: GetObject: " << err.GetExceptionName() << ": "
- << err.GetMessage() << std::endl;
- return std::vector();
- } else {
- std::cout << "Successfully retrieved '" << file_path << "' from '"
- << _bucket_name << "'." << std::endl;
-
- std::stringstream stream;
- stream << outcome.GetResult().GetBody().rdbuf();
- std::string str_stream = stream.str();
- std::vector data(str_stream.begin(), str_stream.end());
- return data;
+ try {
+ Aws::S3::Model::GetObjectRequest request;
+ request.SetBucket(_bucket_name);
+ request.SetKey(file_path);
+
+ Aws::S3::Model::GetObjectOutcome outcome = _aws_client->GetObject(request);
+
+ if (!outcome.IsSuccess()) {
+ const Aws::S3::S3Error &err = outcome.GetError();
+ std::cerr << "Error: GetObject: " << err.GetExceptionName() << ": "
+ << err.GetMessage() << " Key: " << file_path << std::endl;
+ return std::vector();
+ } else {
+ std::cout << "Successfully retrieved '" << file_path << "' from '"
+ << _bucket_name << "'." << std::endl;
+
+ std::stringstream stream;
+ stream << outcome.GetResult().GetBody().rdbuf();
+ std::string str_stream = stream.str();
+ std::vector data(str_stream.begin(), str_stream.end());
+ return data;
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::read_s3", ex.what());
}
+ return std::vector();
}
-void RemoteConnection::retrieve_file(const std::string &file_path) {
- Aws::S3::Model::GetObjectRequest request;
- request.SetBucket(_bucket_name);
- request.SetKey(file_path);
-
- Aws::S3::Model::GetObjectOutcome outcome = _aws_client->GetObject(request);
-
- if (!outcome.IsSuccess()) {
- const Aws::S3::S3Error &err = outcome.GetError();
- std::cerr << "Error: GetObject: " << err.GetExceptionName() << ": "
- << err.GetMessage() << std::endl;
- } else {
- std::cout << "Successfully retrieved '" << file_path << "' from '"
- << _bucket_name << "'." << std::endl;
-
- auto &retrieved_file = outcome.GetResult().GetBody();
- std::ofstream output_file(file_path.c_str(),
- std::ios::out | std::ios::binary);
- output_file << retrieved_file.rdbuf();
+bool RemoteConnection::retrieve_file(const std::string &file_path) {
+ try {
+ Aws::S3::Model::GetObjectRequest request;
+ request.SetBucket(_bucket_name);
+ request.SetKey(file_path);
+
+ Aws::S3::Model::GetObjectOutcome outcome = _aws_client->GetObject(request);
+
+ if (!outcome.IsSuccess()) {
+ const Aws::S3::S3Error &err = outcome.GetError();
+ std::cerr << "Error: GetObject: " << err.GetExceptionName() << ": "
+ << err.GetMessage() << std::endl;
+ return false;
+ } else {
+ std::cout << "Successfully retrieved '" << file_path << "' from '"
+ << _bucket_name << "'." << std::endl;
+
+ auto &retrieved_file = outcome.GetResult().GetBody();
+ std::ofstream output_file(file_path.c_str(),
+ std::ios::out | std::ios::binary);
+ output_file << retrieved_file.rdbuf();
+ return true;
+ }
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::retrieve_file", ex.what());
}
+ return false;
}
std::vector
RemoteConnection::get_file_list(const std::string &path) {
std::vector results;
+ try {
- Aws::S3::Model::ListObjectsRequest request;
- request.SetBucket(_bucket_name);
- request.SetPrefix(path);
+ Aws::S3::Model::ListObjectsRequest request;
+ request.SetBucket(_bucket_name);
+ request.SetPrefix(path);
- Aws::S3::Model::ListObjectsOutcome outcome =
- _aws_client->ListObjects(request);
+ Aws::S3::Model::ListObjectsOutcome outcome =
+ _aws_client->ListObjects(request);
- if (!outcome.IsSuccess()) {
- std::cerr << "Error: ListObjects: " << outcome.GetError().GetMessage()
- << std::endl;
- } else {
- Aws::Vector objects =
- outcome.GetResult().GetContents();
+ if (!outcome.IsSuccess()) {
+ std::string error_message =
+ "Error in get_file_list(): " + outcome.GetError().GetMessage();
+ throw VCLException(ObjectNotFound, error_message);
+ } else {
+ Aws::Vector objects =
+ outcome.GetResult().GetContents();
- for (Aws::S3::Model::Object &object : objects) {
- results.push_back(object.GetKey());
+ for (Aws::S3::Model::Object &object : objects) {
+ results.push_back(object.GetKey());
+ }
}
- }
+ return results;
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::get_file_list", ex.what());
+ }
return results;
}
-void RemoteConnection::remove_s3_object(const std::string &file_path) {
- Aws::S3::Model::DeleteObjectRequest delete_request;
+bool RemoteConnection::remove_s3_object(const std::string &file_path) {
+ try {
+ Aws::S3::Model::DeleteObjectRequest delete_request;
- delete_request.SetBucket(_bucket_name);
- delete_request.SetKey(file_path);
+ delete_request.SetBucket(_bucket_name);
+ delete_request.SetKey(file_path);
- auto delete_object_outcome = _aws_client->DeleteObject(delete_request);
+ auto delete_object_outcome = _aws_client->DeleteObject(delete_request);
- if (!delete_object_outcome.IsSuccess()) {
- const Aws::S3::S3Error &err = delete_object_outcome.GetError();
- std::cerr << "Error: DeleteObject: " << err.GetExceptionName() << ": "
- << err.GetMessage() << std::endl;
+ if (!delete_object_outcome.IsSuccess()) {
+ const Aws::S3::S3Error &err = delete_object_outcome.GetError();
+ std::cerr << "Error: DeleteObject: " << err.GetExceptionName() << ": "
+ << err.GetMessage() << std::endl;
+ return false;
+ }
+
+ return true;
+ } catch (VCL::Exception &ex) {
+ print_exception(ex);
+ } catch (std::exception &ex) {
+ printErrorMessage("RemoteConnection::remove_s3_object", ex.what());
}
+ return false;
}
-// void RemoteConnection::LogEntry(std::string functionName) {
-// // std::cout << "Entering " << functionName << "()" << std::endl;
-// }
+void RemoteConnection::printErrorMessage(const std::string &functionName,
+ const std::string &errorMessage) {
+ try {
+ std::string message = "Exception ocurred in " + functionName + "().";
+ if (errorMessage != "") {
+ message += " Error: " + errorMessage;
+ }
+ std::cout << message << std::endl;
+ } catch (std::exception &ex) {
+ std::cout << "Exception ocurred in RemoteConnection::printErrorMessage()."
+ << " Error: " << ex.what() << std::endl;
+ }
+}
\ No newline at end of file
diff --git a/src/vcl/TDBImage.cc b/src/vcl/TDBImage.cc
index 2225108e..7dfd89f5 100644
--- a/src/vcl/TDBImage.cc
+++ b/src/vcl/TDBImage.cc
@@ -169,6 +169,27 @@ void TDBImage::operator=(TDBImage &tdb) {
}
}
+bool TDBImage::operator==(const TDBImage &right) {
+ bool result = true;
+ result &= this->_img_height == right._img_height;
+ result &= this->_img_width == right._img_width;
+ result &= this->_img_channels == right._img_channels;
+ result &= this->_img_size == right._img_size;
+
+ // threshold value
+ result &= this->_threshold == right._threshold;
+
+ // raw data of the image
+ for (size_t index = 0; index < _img_size; index++) {
+ result &= (this->_raw_data[index] == right._raw_data[index]);
+ if (!result) {
+ break;
+ }
+ }
+ result &= this->_full_array == right._full_array;
+ return result;
+}
+
void TDBImage::set_image_data_equal(const TDBImage &tdb) {
_img_height = tdb._img_height;
_img_width = tdb._img_width;
@@ -277,6 +298,10 @@ void TDBImage::set_image_properties(int height, int width, int channels) {
}
void TDBImage::set_configuration(RemoteConnection *remote) {
+ if (!remote) {
+ throw VCLException(SystemNotFound, "Remote Connection is invalid");
+ }
+
if (!remote->connected())
throw VCLException(SystemNotFound, "Remote Connection not initialized");
@@ -287,8 +312,9 @@ void TDBImage::set_configuration(RemoteConnection *remote) {
/* TDBIMAGE INTERACTION */
/* *********************** */
void TDBImage::write(const std::string &image_id, bool metadata) {
- if (_raw_data == NULL)
+ if (_raw_data == NULL) {
throw VCLException(ObjectEmpty, "No data to be written");
+ }
std::string array_name = namespace_setup(image_id);
diff --git a/src/vcl/TDBImage.h b/src/vcl/TDBImage.h
index 1433b74a..997b1b69 100644
--- a/src/vcl/TDBImage.h
+++ b/src/vcl/TDBImage.h
@@ -242,6 +242,13 @@ class TDBImage : public TDBObject {
*/
void delete_image();
+ /**
+ * Overloads the "equals to/comparison" operator
+ *
+ * @param rhs The object located at the right of the operator ==
+ */
+ bool operator==(const TDBImage &rhs);
+
private:
/* *********************** */
/* GET FUNCTIONS */
diff --git a/src/vcl/TDBObject.cc b/src/vcl/TDBObject.cc
index 357840a8..d53df6f0 100644
--- a/src/vcl/TDBObject.cc
+++ b/src/vcl/TDBObject.cc
@@ -120,6 +120,46 @@ TDBObject &TDBObject::operator=(const TDBObject &tdb) {
return *this;
}
+bool TDBObject::operator==(const TDBObject &right) {
+ // Path variables
+ bool result = true;
+ result &= this->_group == right._group;
+ result &= this->_name == right._name;
+
+ result &= this->_num_dimensions == right._num_dimensions;
+ result &= this->_dimension_names == right._dimension_names;
+ result &= this->_lower_dimensions == right._lower_dimensions;
+ result &= this->_upper_dimensions == right._upper_dimensions;
+ // TileDB doesn't implement the overload of the operator ==
+ // result &= this->_full_dimensions == right._full_dimensions;
+
+ // TileDB doesn't implement the overload of the operator ==
+ // result &= this->_full_attributes == right._full_attributes;
+
+ // Attributes (number of values in a cell)
+ result &= this->_num_attributes == right._num_attributes;
+ result &= this->_attributes == right._attributes;
+
+ // Compression type
+ result &= this->_compressed == right._compressed;
+ result &= this->_min_tile_dimension == right._min_tile_dimension;
+
+ result &= this->_extent == right._extent;
+ result &= this->_tile_capacity == right._tile_capacity;
+
+ // TileDB variables
+ result &= this->_array_dimension == right._array_dimension;
+ result &= this->_tile_dimension == right._tile_dimension;
+ // TileDB::Context doesn't implement the overload of the operator ==
+ // result &= this->_ctx == right._ctx;
+
+ // TileDB::Config throws an exception inside of the overloaded
+ // function for operator ==
+ // result &= this->_config == right._config;
+
+ return result;
+}
+
void TDBObject::set_equal(const TDBObject &tdb) {
_group = tdb._group;
@@ -535,6 +575,7 @@ void TDBObject::read_metadata(const std::string &array_name,
++j;
}
} catch (tiledb::TileDBError &e) {
+ std::cerr << "TileDB error: " << e.what() << std::endl;
throw VCLException(TileDBNotFound, "No data in TileDB object yet");
}
}
diff --git a/src/vcl/TDBObject.h b/src/vcl/TDBObject.h
index 898b90c6..2b76b9fb 100644
--- a/src/vcl/TDBObject.h
+++ b/src/vcl/TDBObject.h
@@ -120,6 +120,13 @@ class TDBObject {
*/
TDBObject &operator=(const TDBObject &tdb);
+ /**
+ * Overloads the "equals to/comparison" operator
+ *
+ * @param rhs The object located at the right of the operator ==
+ */
+ bool operator==(const TDBObject &rhs);
+
/**
* TDBObject destructor
*/
diff --git a/src/vcl/Video.cc b/src/vcl/Video.cc
index eb238ef6..a1001058 100644
--- a/src/vcl/Video.cc
+++ b/src/vcl/Video.cc
@@ -45,9 +45,12 @@ Video::Video()
_video_id(""), _flag_stored(true), _codec(Video::Codec::NOCODEC),
_remote(nullptr) {}
-Video::Video(const std::string &video_id) : Video() {
+Video::Video(const std::string &video_id, bool no_blob) : Video() {
_video_id = video_id;
_remote = nullptr;
+
+ _no_blob = no_blob;
+
initialize_video_attributes(_video_id);
}
@@ -72,6 +75,8 @@ Video::Video(const Video &video) {
_video_id = video._video_id;
_remote = nullptr;
+ _no_blob = video._no_blob;
+
_size = video._size;
_fps = video._fps;
@@ -105,6 +110,8 @@ Video::~Video() {
/* GET FUNCTIONS */
/* *********************** */
+bool Video::is_blob_not_stored() const { return _no_blob; }
+
std::string Video::get_video_id() const { return _video_id; }
Video::Codec Video::get_codec() const { return _codec; }
@@ -119,21 +126,18 @@ cv::Mat Video::get_frame(unsigned frame_number, bool performOp) {
throw VCLException(OutOfBounds, "Frame requested is out of bounds");
cv::VideoCapture inputVideo(_video_id);
- int i = 0;
- // Loop until the required frame is read
- while (true) {
- cv::Mat mat_frame;
- inputVideo >> mat_frame;
- if (mat_frame.empty()) {
- break;
- }
- if (i == frame_number) {
- frame = mat_frame;
- break;
- }
- i++;
+ // Set the index of the frame to be read
+ if (!inputVideo.set(cv::CAP_PROP_POS_FRAMES, frame_number)) {
+ throw VCLException(UnsupportedOperation, "Set the frame index failed");
+ }
+
+ // Read the frame
+ if (!inputVideo.read(frame)) {
+ throw VCLException(UnsupportedOperation,
+ "Frame requested cannot be read");
}
+
inputVideo.release();
} else {
@@ -235,8 +239,9 @@ std::vector Video::get_encoded(std::string container,
auto time_now = std::chrono::system_clock::now();
std::chrono::duration utc_time = time_now.time_since_epoch();
- std::string fname =
- "tmp/tempfile" + std::to_string(utc_time.count()) + container;
+ std::string fname = VDMS::VDMSConfig::instance()->get_path_tmp() +
+ "/tempfile" + std::to_string(utc_time.count()) +
+ container;
// check sufficient memory
bool memory_avail = check_sufficient_memory(_size);
@@ -472,9 +477,12 @@ void Video::store_video_no_operation(std::string id, std::string store_id,
inputVideo.release();
outputVideo.release();
- if (std::remove(_video_id.data()) != 0) {
- throw VCLException(ObjectEmpty,
- "Error encountered while removing the file.");
+ if (_video_id.find(VDMS::VDMSConfig::instance()->get_path_tmp()) !=
+ std::string::npos) {
+ if (std::remove(_video_id.data()) != 0) {
+ throw VCLException(ObjectEmpty,
+ "Error encountered while removing the file.");
+ }
}
_video_id = store_id;
if (std::rename(fname.data(), _video_id.data()) != 0) {
@@ -564,8 +572,9 @@ void Video::perform_operations(bool is_store, std::string store_id) {
// Setup temporary files
auto time_now = std::chrono::system_clock::now();
std::chrono::duration utc_time = time_now.time_since_epoch();
- std::string fname =
- "/tmp/tempfile" + std::to_string(utc_time.count()) + "." + format;
+ std::string fname = VDMS::VDMSConfig::instance()->get_path_tmp() +
+ "/tempfile" + std::to_string(utc_time.count()) + "." +
+ format;
std::string id =
(_operated_video_id == "") ? _video_id : _operated_video_id;
@@ -576,17 +585,20 @@ void Video::perform_operations(bool is_store, std::string store_id) {
if (file) {
file.close();
} else {
- throw VCLException(OpenFailed, "video_id could not be opened");
+ throw VCLException(OpenFailed,
+ "video_id " + id + " could not be opened");
}
} catch (Exception e) {
- throw VCLException(OpenFailed, "video_id could not be opened");
+ throw VCLException(OpenFailed, "video_id " + id + " could not be opened");
}
if (_operations.size() == 0) {
- // If the call is made with not operations.
+ // If the call is made with no operations.
if (is_store) {
- // If called to store a video into the data store
- store_video_no_operation(id, store_id, fname);
+ // If called to store a video into the data store as blob
+ if (!_no_blob) {
+ store_video_no_operation(id, store_id, fname);
+ }
} else {
_operated_video_id = _video_id;
}
@@ -595,8 +607,8 @@ void Video::perform_operations(bool is_store, std::string store_id) {
while (op_count < _operations.size()) {
time_now = std::chrono::system_clock::now();
utc_time = time_now.time_since_epoch();
- fname =
- "/tmp/tempfile" + std::to_string(utc_time.count()) + "." + format;
+ fname = VDMS::VDMSConfig::instance()->get_path_tmp() + "/tempfile" +
+ std::to_string(utc_time.count()) + "." + format;
op_count = perform_single_frame_operations(id, op_count, fname);
@@ -621,13 +633,23 @@ void Video::perform_operations(bool is_store, std::string store_id) {
}
}
if (is_store) {
- if (std::remove(_video_id.data()) != 0) {
- throw VCLException(ObjectEmpty,
- "Error encountered while removing the file.");
+ if (_video_id.find(VDMS::VDMSConfig::instance()->get_path_tmp()) !=
+ std::string::npos) {
+ if (std::remove(_video_id.data()) != 0) {
+ throw VCLException(ObjectEmpty,
+ "Error encountered while removing the file.");
+ }
}
- if (std::rename(fname.data(), store_id.data()) != 0) {
- throw VCLException(ObjectEmpty,
- "Error encountered while renaming the file.");
+ if (!_no_blob) {
+ if (std::rename(fname.data(), store_id.data()) != 0) {
+ throw VCLException(ObjectEmpty,
+ "Error encountered while renaming the file.");
+ }
+ } else {
+ if (std::rename(fname.data(), _video_id.data()) != 0) {
+ throw VCLException(ObjectEmpty,
+ "Error encountered while renaming the file.");
+ }
}
} else {
_operated_video_id = fname;
@@ -702,6 +724,7 @@ void Video::swap(Video &rhs) noexcept {
using std::swap;
swap(_video_id, rhs._video_id);
+ swap(_no_blob, rhs._no_blob);
swap(_flag_stored, rhs._flag_stored);
swap(_size, rhs._size);
swap(_fps, rhs._fps);
@@ -714,6 +737,10 @@ void Video::set_query_error_response(std::string response_error) {
}
void Video::set_connection(RemoteConnection *remote) {
+ if (!remote) {
+ throw VCLException(SystemNotFound, "Invalid remote connection");
+ }
+
if (!remote->connected())
remote->start();
@@ -863,7 +890,8 @@ void Video::Interval::operator()(Video *video, cv::Mat &frame,
}
auto time_now = std::chrono::system_clock::now();
std::chrono::duration utc_time = time_now.time_since_epoch();
- std::string tmp_fname = "/tmp/tempfile_interval" +
+ std::string tmp_fname = VDMS::VDMSConfig::instance()->get_path_tmp() +
+ "/tempfile_interval" +
std::to_string(utc_time.count()) + "." + format;
cv::VideoCapture inputVideo(fname);
@@ -997,7 +1025,8 @@ void Video::SyncRemoteOperation::operator()(Video *video, cv::Mat &frame,
auto time_now = std::chrono::system_clock::now();
std::chrono::duration utc_time = time_now.time_since_epoch();
std::string response_filepath =
- "/tmp/rtempfile" + std::to_string(utc_time.count()) + "." + format;
+ VDMS::VDMSConfig::instance()->get_path_tmp() + "/rtempfile" +
+ std::to_string(utc_time.count()) + "." + format;
FILE *response_file = fopen(response_filepath.data(), "wb");
if (curl_easy_setopt(curl, CURLOPT_URL, _url.data()) != CURLE_OK) {
diff --git a/src/vcl/utils.cc b/src/vcl/utils.cc
index 4d60b8ee..529f7526 100644
--- a/src/vcl/utils.cc
+++ b/src/vcl/utils.cc
@@ -34,10 +34,51 @@
#include "../VDMSConfig.h"
#include "vcl/Exception.h"
+#include "vcl/Image.h"
#include "vcl/utils.h"
namespace VCL {
+std::string format_to_string(VCL::Format format) {
+ switch (format) {
+ case VCL::Format::NONE_IMAGE:
+ return "";
+ case VCL::Format::JPG:
+ return "jpg";
+ case VCL::Format::PNG:
+ return "png";
+ case VCL::Format::TDB:
+ return "tdb";
+ case VCL::Format::BIN:
+ return "bin";
+ default:
+ throw VCLException(UnsupportedFormat, (int)format + " is not a \
+ valid format");
+ }
+}
+
+VCL::Format read_image_format(void *buffer, long size) {
+ static const unsigned char pngSignature[] = {0x89, 0x50, 0x4E, 0x47,
+ 0x0D, 0x0A, 0x1A, 0x0A};
+ static const unsigned char jpgSignature[] = {0xFF, 0xD8};
+ if (size < 4) {
+ return VCL::Format::NONE_IMAGE; // The buffer is too small to identify the
+ // format.
+ }
+ unsigned char *data = static_cast(buffer);
+ // Check for JPEG format.
+ if (std::equal(data, data + 2, jpgSignature)) {
+ return VCL::Format::JPG; // JPEG magic number (SOI marker).
+ }
+ // else if ( (data[0] == 0x89) && (data[1]==0x50 )&& (data[2]==0x4E) && (
+ // data[3] == 0x47 ) && (data[4] == 0x0D) && (data[5] == 0x0A) && (data[6] ==
+ // 0x1A )&& (data[7]==0x0A)){
+ else if (std::equal(data, data + 8, pngSignature)) {
+ return VCL::Format::PNG;
+ } else
+ return VCL::Format::NONE_IMAGE;
+}
+
uint64_t rdrand() {
static const unsigned retry_limit = 10;
unsigned retries = retry_limit;
diff --git a/src/vdms.cc b/src/vdms.cc
index e2056eaa..ca56b03e 100644
--- a/src/vdms.cc
+++ b/src/vdms.cc
@@ -36,11 +36,26 @@
#include "Server.h"
-void printUsage() {
- std::cout << "Usage: vdms -cfg config-file.json" << std::endl;
+void ignore_sigpipe() { signal(SIGPIPE, SIG_IGN); }
- std::cout << "Usage: vdms -restore db.tar.gz" << std::endl;
- exit(0);
+void printUsage() {
+ std::cout << "Usage: vdms" << std::endl
+ << " -cfg