diff --git a/environment.yml b/environment.yml index 54e67abafe..5af210f181 100644 --- a/environment.yml +++ b/environment.yml @@ -59,6 +59,7 @@ dependencies: - qhull - qt-main>=5.15.8, <5.16 - qwt <6.3.0 + - spiceql - sqlite >=3.46.0,<3.47 - suitesparse <7.7.0 - superlu diff --git a/isis/CMakeLists.txt b/isis/CMakeLists.txt index ec6fb3bf54..4b821564c0 100644 --- a/isis/CMakeLists.txt +++ b/isis/CMakeLists.txt @@ -278,6 +278,7 @@ find_package(PCL REQUIRED) find_package(Protobuf REQUIRED) find_package(Qwt 6 REQUIRED) find_package(SuperLU 4.3 REQUIRED) +find_package(SpiceQL REQUIRED) find_package(TIFF 4.0.0 REQUIRED) find_package(TNT 126 REQUIRED) find_package(XercesC 3.1.2 REQUIRED) diff --git a/isis/IsisPreferences b/isis/IsisPreferences index f53f768c6c..c903d5fb95 100644 --- a/isis/IsisPreferences +++ b/isis/IsisPreferences @@ -184,6 +184,17 @@ Group = Plugins "$HOME/.Isis/csm3.0.3/") EndGroup +######################################################## +# +# Set whether spice calls use the SpiceQL web API or +# local kernels. +# +######################################################## + +Group = WebSpice + UseWebSpice = "true" +EndGroup + ######################################################## # Customize the location of mission specific data # files (calibration and spice kernels). Usually this diff --git a/isis/TestPreferences b/isis/TestPreferences index fee162b7c2..436dabd443 100644 --- a/isis/TestPreferences +++ b/isis/TestPreferences @@ -183,6 +183,18 @@ Group = Plugins "$HOME/.Isis/csm3.0.3/") EndGroup +######################################################## +# +# Set whether spice calls use the SpiceQL web API or +# local kernels. +# +######################################################## + +Group = WebSpice + UseWebSpice = "false" +EndGroup + + ######################################################## # Customize the location of mission specific data # files (calibration and spice kernels). Usually this diff --git a/isis/cmake/FindSpiceQL.cmake b/isis/cmake/FindSpiceQL.cmake new file mode 100644 index 0000000000..20abbd03fc --- /dev/null +++ b/isis/cmake/FindSpiceQL.cmake @@ -0,0 +1,23 @@ +# CMake module for find_package(SpiceQL) +# Finds include directory and all applicable libraries +# +# Sets the following: +# SpiceQL_INCLUDE_DIR +# SpiceQL_LIBRARY + +find_path(SPICEQL_INCLUDE_DIR + NAME spiceql.h + PATH_SUFFIXES "SpiceQL" +) + +find_library(SPICEQL_LIBRARY + NAMES SpiceQL +) + +get_filename_component(SUPERLU_ROOT_INCLUDE_DIR "${SUPERLU_INCLUDE_DIR}" DIRECTORY) + + +message(STATUS "SPICEQL INCLUDE DIR: " ${SPICEQL_INCLUDE_DIR} ) +message(STATUS "SPICEQL LIB: " ${SPICEQL_LIBRARY} ) + +get_filename_component(SPICEQL_ROOT_INCLUDE_DIR "${SPICEQL_INCLUDE_DIR}" DIRECTORY) diff --git a/isis/src/apollo/apps/apollopaninit/main.cpp b/isis/src/apollo/apps/apollopaninit/main.cpp index d22d990508..eb450765b1 100644 --- a/isis/src/apollo/apps/apollopaninit/main.cpp +++ b/isis/src/apollo/apps/apollopaninit/main.cpp @@ -38,11 +38,13 @@ find files of those names at the top level of this repository. **/ #include "PvlKeyword.h" #include "PvlObject.h" #include "PvlTranslationTable.h" +#include "RestfulSpice.h" #include "Spice.h" #include "SpicePosition.h" #include "SpiceRotation.h" #include "Table.h" #include "UserInterface.h" +#include "spiceql.h" #define FIDL 26.72093 //spacing between fiducial marks in mm @@ -228,25 +230,29 @@ void IsisMain() { panCube.putGroup(kernels_pvlG); - //Load all the kernels + // Load kernels Load_Kernel(kernels_pvlG["TargetPosition"]); Load_Kernel(kernels_pvlG["TargetAttitudeShape"]); Load_Kernel(kernels_pvlG["LeapSecond"]); + //////////////////////////////////////////attach a target rotation table - char frameName[32]; - SpiceInt frameCode; - SpiceBoolean found; - //get the framecode from the body code (301=MOON) - cidfrm_c(301, sizeof(frameName), &frameCode, frameName, &found); - if(!found) { - QString naifTarget = QString("IAU_MOOM"); - namfrm_c(naifTarget.toLatin1().data(), &frameCode); + std::string frameName; + SpiceInt frameCode = 0; + try{ + json output = Isis::RestfulSpice::getTargetFrameInfo(301, RestfulSpice::spiceql_mission_map[(panCube.camera()->instrumentId()).toStdString()]); + frameCode = output["frameCode"].get(); + frameName = output["frameName"].get(); + }catch(std::invalid_argument){ + std::string naifTarget = "IAU_MOON"; + frameCode = Isis::RestfulSpice::translateNameToCode(naifTarget, mission.toLower().toStdString()); if(frameCode == 0) { - QString msg = "Can not find NAIF code for [" + naifTarget + "]"; + QString msg = "Can not find NAIF code for [" + QString::fromStdString(naifTarget) + "]"; throw IException(IException::Io, msg, _FILEINFO_); } } + + spRot = new SpiceRotation(frameCode); //create a table from starttime to endtime (streched by 3%) with NODES entries spRot->LoadCache(time0-0.015*(time1-time0), time1+0.015*(time1-time0), NODES); @@ -267,7 +273,7 @@ void IsisMain() { /////////////Finding the principal scan line position and orientation //get the radii of the MOON SpiceInt tempRadii = 0; - bodvcd_c(301,"RADII",3,&tempRadii,R_MOON); //units are km + //bodvcd_c(301,"RADII",3,&tempRadii,R_MOON); //units are km double omega,phi,kappa; std::vector posSel; //Seleno centric position @@ -808,7 +814,7 @@ void Load_Kernel(Isis::PvlKeyword &key) { throw IException(IException::Io, msg, _FILEINFO_); } QString fileName(file.expanded()); - furnsh_c(fileName.toLatin1().data()); + SpiceQL::load(fileName.toLatin1().data()); } NaifStatus::CheckErrors(); diff --git a/isis/src/base/apps/appjit/LineScanCameraRotation.cpp b/isis/src/base/apps/appjit/LineScanCameraRotation.cpp index 245429100a..57b87b997a 100644 --- a/isis/src/base/apps/appjit/LineScanCameraRotation.cpp +++ b/isis/src/base/apps/appjit/LineScanCameraRotation.cpp @@ -17,6 +17,7 @@ #include "IString.h" #include "iTime.h" #include "IException.h" +#include "RestfulSpice.h" #include "Table.h" #include "NaifStatus.h" @@ -54,8 +55,6 @@ namespace Isis { p_ckKeyword = kernels["InstrumentPointing"]; p_cacheTime = timeCache; -// std::cout<instrumentRotation(); std::vector rotationCache; + + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(p_cacheTime, "MRO", "mars", "IAU_MARS", "NONE", "mro", "reconstructed", "reconstructed"); + + double state[6]; for(std::vector::iterator i = p_cacheTime.begin(); i < p_cacheTime.end(); i++) { + // Clear state array before copying new values + std::fill_n(state, 6, 0); + double et = *i; + // Get the index from the iterator + size_t idx = i - p_cacheTime.begin(); + prot->SetEphemerisTime(et); crot->SetEphemerisTime(et); // The following code will be put into method LoadIBcache() - spkezr_c("MRO", et, "IAU_MARS", "NONE", "MARS", state, <); - NaifStatus::CheckErrors(); + + std::copy(sunLt[idx].begin(), sunLt[idx].begin()+6, state); + // Compute the direction of the radial axis (3) of the line scan camera vscl_c(1. / vnorm_c(state), state, R); // vscl and vnorm only operate on first 3 members of state diff --git a/isis/src/base/apps/shadow/shadow.cpp b/isis/src/base/apps/shadow/shadow.cpp index 989d0c2131..a1b06f0984 100644 --- a/isis/src/base/apps/shadow/shadow.cpp +++ b/isis/src/base/apps/shadow/shadow.cpp @@ -8,8 +8,10 @@ #include "KernelDb.h" #include "NaifStatus.h" #include "ProcessByBrick.h" +#include "RestfulSpice.h" #include "ShadowFunctor.h" #include "SpicePosition.h" +#include "spiceql.h" namespace Isis { QStringList kernels(QString kernelType, @@ -46,11 +48,12 @@ namespace Isis { allKernelFiles.append(kernels("PCK", &KernelDb::targetAttitudeShape, *demCube->label(), ui)); allKernelFiles.append(kernels("SPK", &KernelDb::targetPosition, *demCube->label(), ui)); + NaifStatus::CheckErrors(); foreach (QString kernelFile, allKernelFiles) { kernelsUsed += kernelFile; - furnsh_c(FileName(kernelFile).expanded().toLatin1().data()); + SpiceQL::load(FileName(kernelFile).expanded().toLatin1().data()); } // Find the NAIF target code for the DEM's target @@ -63,28 +66,43 @@ namespace Isis { // Get actual sun position, relative to target QString bodyFixedFrame = QString("IAU_%1").arg(name.toUpper()); - spkpos_c("SUN", time.Et(), bodyFixedFrame.toLatin1().data(), "NONE", - name.toUpper().toLatin1().data(), sunPosition, &lightTime); + std::vector etStart = {time.Et()}; + std::string observer = name.toUpper().toLatin1().data(); + std::string bff = bodyFixedFrame.toLatin1().data(); + std::vector> sunLt; + // If kernels are specified + bool userKernels = false; + + if (ui.WasEntered("PCK") || ui.WasEntered("SPK")){ + userKernels = true; + } + + if (userKernels){ + sunLt = SpiceQL::getTargetStates(etStart, "sun", observer, bff, "NONE", "base", "reconstructed", "reconstructed", true); + }else{ + sunLt = Isis::RestfulSpice::getTargetStates(etStart, "sun", observer, bff, "NONE", RestfulSpice::spiceql_mission_map[observer], "reconstructed", "reconstructed"); + } NaifStatus::CheckErrors(); - // Adjusted for light time - spkpos_c("SUN", time.Et() - lightTime, bodyFixedFrame.toLatin1().data(), "NONE", - name.toUpper().toLatin1().data(), sunPosition, &lightTime); + // Adjust for light time + lightTime = sunLt[0][6]; + etStart = {time.Et() - lightTime}; + if (userKernels){ + sunLt = SpiceQL::getTargetStates(etStart, "sun", observer, bff, "NONE", "base", "reconstructed", "reconstructed", true); + }else{ + sunLt = Isis::RestfulSpice::getTargetStates(etStart, "sun", observer, bff, "NONE", RestfulSpice::spiceql_mission_map[observer], "reconstructed", "reconstructed"); + } + + std::copy(sunLt[0].begin(), sunLt[0].begin()+3, sunPosition); NaifStatus::CheckErrors(); - - + // Convert sun position units: KM -> M sunPosition[0] *= 1000; sunPosition[1] *= 1000; sunPosition[2] *= 1000; - foreach (QString kernelFile, allKernelFiles) { - unload_c(FileName(kernelFile).expanded().toLatin1().data()); - } - - NaifStatus::CheckErrors(); functor.setSunPosition(sunPosition); } diff --git a/isis/src/base/objs/Kernels/Kernels.cpp b/isis/src/base/objs/Kernels/Kernels.cpp index 95030a443a..d8b6107aa9 100644 --- a/isis/src/base/objs/Kernels/Kernels.cpp +++ b/isis/src/base/objs/Kernels/Kernels.cpp @@ -23,6 +23,7 @@ find files of those names at the top level of this repository. **/ #include "NaifStatus.h" #include "PvlKeyword.h" #include "Pvl.h" +#include "spiceql.h" using namespace std; @@ -776,7 +777,7 @@ namespace Isis { if (!kfile.loaded) { NaifStatus::CheckErrors(); try { - furnsh_c(kfile.fullpath.toLatin1().data()); + SpiceQL::load(kfile.fullpath.toLatin1().data()); NaifStatus::CheckErrors(); kfile.loaded = true; kfile.managed = true; diff --git a/isis/src/base/objs/RestfulSpice/Makefile b/isis/src/base/objs/RestfulSpice/Makefile new file mode 100644 index 0000000000..7578f0b21d --- /dev/null +++ b/isis/src/base/objs/RestfulSpice/Makefile @@ -0,0 +1,7 @@ +ifeq ($(ISISROOT), $(BLANK)) +.SILENT: +error: + echo "Please set ISISROOT"; +else + include $(ISISROOT)/make/isismake.apps +endif \ No newline at end of file diff --git a/isis/src/base/objs/RestfulSpice/RestfulSpice.cpp b/isis/src/base/objs/RestfulSpice/RestfulSpice.cpp new file mode 100644 index 0000000000..05f81a3f82 --- /dev/null +++ b/isis/src/base/objs/RestfulSpice/RestfulSpice.cpp @@ -0,0 +1,404 @@ + +#include +#include +#include "RestfulSpice.h" +#include "restincurl.h" +#include + +using json=nlohmann::json; + +namespace Isis::RestfulSpice{ + std::map spiceql_mission_map = { + {"AMICA", "amica"}, + {"CHANDRAYAAN-1_M3", "m3"}, + {"CHANDRAYAAN-1_MRFFR", "mrffr"}, + {"CASSINI_ISS_NAC", "cassini"}, + {"CASSINI_ISS_WAC", "cassini"}, + {"DAWN_FC2_FILTER_1", "fc2"}, + {"DAWN_FC2_FILTER_2", "fc2"}, + {"DAWN_FC2_FILTER_3", "fc2"}, + {"DAWN_FC2_FILTER_4", "fc2"}, + {"DAWN_FC2_FILTER_5", "fc2"}, + {"DAWN_FC2_FILTER_6", "fc2"}, + {"DAWN_FC2_FILTER_7", "fc2"}, + {"DAWN_FC2_FILTER_8", "fc2"}, + {"GLL_SSI_PLATFORM", "galileo"}, + {"HAYABUSA_AMICA", "amica"}, + {"HAYABUSA_NIRS", "nirs"}, + {"HAYABUSA2_ONC-W2", ""}, + {"JUNO_JUNOCAM", "juno"}, + {"JUPITER", "voyager1"}, + {"LRO_LROCNACL", "lroc"}, + {"LRO_LROCNACR", "lroc"}, + {"LRO_LROCWAC_UV", "lroc"}, + {"LRO_LROCWAC_VIS", "lroc"}, + {"LRO_MINIRF", ""}, + {"M10_VIDICON_A", "m10_vidicon_a"}, + {"M10_VIDICON_B", "m10_vidicon_b"}, + {"MARS", "mro"}, + {"MSGR_MDIS_WAC", "mdis"}, + {"MSGR_MDIS_NAC", "mdis"}, + {"MEX_HRSC_SRC", "src"}, + {"MEX_HRSC_IR", "hrsc"}, + {"MGS_MOC_NA", "mgs"}, + {"MGS_MOC_WA_RED", "mgs"}, + {"MGS_MOC_WA_BLUE", "mgs"}, + {"MOON", "apollo15"}, + {"MRO_MARCI_VIS", "marci"}, + {"MRO_MARCI_UV", "marci"}, + {"MRO_CTX", "ctx"}, + {"MRO_HIRISE", "hirise"}, + {"MRO_CRISM_VNIR", "crism"}, + {"NEAR EARTH ASTEROID RENDEZVOUS", ""}, + {"MSL_MASTCAM_RIGHT", ""}, + {"MSL_MASTCAM_LEFT", ""}, + {"NH_LORRI", "lorri"}, + {"NH_RALPH_LEISA", "leisa"}, + {"NH_MVIC", "mvic_tdi"}, + {"ISIS_NH_RALPH_MVIC_METHANE", "mvic_framing"}, + {"THEMIS_IR", "odyssey"}, + {"THEMIS_VIS", "odyssey"}, + {"ORX_OCAMS_MAPCAM", ""}, + {"ORX_OCAMS_POLYCAM", ""}, + {"ORX_OCAMS_SAMCAM", ""}, + {"LISM_MI-VIS1", "kaguya"}, + {"LISM_MI-VIS2", "kaguya"}, + {"LISM_MI-VIS3", "kaguya"}, + {"LISM_MI-VIS4", "kaguya"}, + {"LISM_MI-VIS5", "kaguya"}, + {"LISM_MI-NIR1", "kaguya"}, + {"LISM_MI-NIR2", "kaguya"}, + {"LISM_MI-NIR3", "kaguya"}, + {"LISM_MI-NIR4", "kaguya"}, + {"LISM_TC1_WDF", "kaguya"}, + {"LISM_TC1_WTF", "kaguya"}, + {"LISM_TC1_SDF", "kaguya"}, + {"LISM_TC1_STF", "kaguya"}, + {"LISM_TC1_WDN", "kaguya"}, + {"LISM_TC1_WTN", "kaguya"}, + {"LISM_TC1_SDN", "kaguya"}, + {"LISM_TC1_STN", "kaguya"}, + {"LISM_TC1_WDH", "kaguya"}, + {"LISM_TC1_WTH", "kaguya"}, + {"LISM_TC1_SDH", "kaguya"}, + {"LISM_TC1_STH", "kaguya"}, + {"LISM_TC1_SSH", "kaguya"}, + {"LO1_HIGH_RESOLUTION_CAMERA", ""}, + {"LO2_HIGH_RESOLUTION_CAMERA", ""}, + {"LO3_HIGH_RESOLUTION_CAMERA", ""}, + {"LO4_HIGH_RESOLUTION_CAMERA", ""}, + {"LO5_HIGH_RESOLUTION_CAMERA", ""}, + {"NEPTUNE", "voyager1"}, + {"SATURN", "voyager1"}, + {"TGO_CASSIS", "cassis"}, + {"VIKING ORBITER 1", "viking1"}, + {"VIKING ORBITER 2", "viking2"}, + {"VG1_ISSNA", ""}, + {"VG1_ISSWA", ""}, + {"VG2_ISSNA", ""}, + {"VG2_ISSWA", ""}, + {"ULTRAVIOLET/VISIBLE CAMERA", "uvvis"}, + {"Near Infrared Camera", "nir"}, + {"High Resolution Camera", "clementine1"}, + {"Long Wave Infrared Camera", "clementine1"}, + {"Visual and Infrared Spectrometer", "vir"} + }; + + + std::vector> getTargetStates(std::vector ets, std::string target, std::string observer, std::string frame, std::string abcorr, std::string mission, std::string ckQuality, std::string spkQuality){ + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + // @TODO validity checks + json args = json::object({ + {"target", target}, + {"observer", observer}, + {"frame", frame}, + {"abcorr", abcorr}, + {"ets", ets}, + {"mission", mission}, + {"ckQuality", ckQuality}, + {"spkQuality", spkQuality} + }); + // @TODO check that json exists / contains what we're looking for + json out = spiceAPIQuery("getTargetStates", args, "POST"); + return out["body"]["return"].get>>(); + }else{ + return SpiceQL::getTargetStates(ets, target, observer, frame, abcorr, mission, ckQuality, spkQuality, true); + } + } + + std::vector> getTargetOrientations(std::vector ets, int toFrame, int refFrame, std::string mission, std::string ckQuality) { + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"ets", ets}, + {"toFrame", toFrame}, + {"refFrame", refFrame}, + {"mission", mission}, + {"ckQuality", ckQuality} + }); + json out = spiceAPIQuery("getTargetOrientations", args); + return out["body"]["return"].get>>(); + }else{ + return SpiceQL::getTargetOrientations(ets, toFrame, refFrame, mission, ckQuality, true); + } + } + + double strSclkToEt(int frameCode, std::string sclk, std::string mission) { + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"frameCode", frameCode}, + {"sclk", sclk}, + {"mission", mission} + }); + json out = spiceAPIQuery("strSclkToEt", args); + return out["body"]["return"].get(); + }else{ + return SpiceQL::strSclkToEt(frameCode, sclk, mission, true); + } + } + + double doubleSclkToEt(int frameCode, double sclk, std::string mission){ + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"frameCode", frameCode}, + {"sclk", sclk}, + {"mission", mission} + }); + json out = spiceAPIQuery("doubleSclkToEt", args); + return out["body"]["return"].get(); + }else{ + return SpiceQL::doubleSclkToEt(frameCode, sclk, mission, true); + } + } + + double utcToEt(std::string utc){ + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"utc", utc} + }); + json out = spiceAPIQuery("utcToEt", args); + return out["body"]["return"].get(); + + }else{ + return SpiceQL::utcToEt(utc, true); + } + } + + + std::string etToUtc(double et, std::string format, double precision){ + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + // TODO Add etToUtc to web api + if (false){ + json args = json::object({ + {"et", et}, + {"format", format}, + {"precision", precision} + }); + json out = spiceAPIQuery("etToUtc", args); + return out["body"]["return"].get(); + }else{ + return SpiceQL::etToUtc(et, format, precision, true); + } + } + + std::string doubleEtToSclk(int frameCode, double et, std::string mission) { + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"frameCode", frameCode}, + {"et", et}, + {"mission", mission} + }); + json out = spiceAPIQuery("doubleEtToSclk", args); + return out["body"]["return"].get(); + }else{ + return SpiceQL::doubleEtToSclk(frameCode, et, mission, true); + } + + } + + int translateNameToCode(std::string frame, std::string mission){ + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"frame", frame}, + {"mission", mission} + }); + json out = spiceAPIQuery("translateNameToCode", args); + return out["body"]["return"].get(); + }else{ + return SpiceQL::translateNameToCode(frame, mission, true); + } + } + + std::string translateCodeToName(int code, std::string mission){ + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"code", code}, + {"mission", mission} + }); + json out = spiceAPIQuery("translateCodeToame", args); + return out["body"]["return"].get(); + }else{ + return SpiceQL::translateCodeToName(code, mission, true); + } + } + + std::vector getFrameInfo(int frame, std::string mission) { + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"frame", frame}, + {"mission", mission} + }); + json out = spiceAPIQuery("getFrameInfo", args); + return out["body"]["return"].get>(); + + }else{ + return SpiceQL::getFrameInfo(frame, mission, true); + } + } + + json getTargetFrameInfo(int targetId, std::string mission) { + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"targetId", targetId}, + {"mission", mission} + }); + json out = spiceAPIQuery("getTargetFrameInfo", args); + return out["body"]["return"]; + }else{ + return SpiceQL::getTargetFrameInfo(targetId, mission, true); + } + } + + json findMissionKeywords(std::string key, std::string mission){ + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"key", key}, + {"mission", mission} + }); + json out = spiceAPIQuery("findMissionKeywords", args); + return out["body"]["return"]; + }else{ + return SpiceQL::findMissionKeywords(key, mission, true); + } + } + + json findTargetKeywords(std::string key, std::string mission){ + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"key", key}, + {"mission", mission} + }); + json out = spiceAPIQuery("findTargetKeywords", args); + return out["body"]["return"]; + }else{ + return SpiceQL::findTargetKeywords(key, mission, true); + } + } + + std::vector> frameTrace(double et, int initialFrame, std::string mission, std::string ckQuality) { + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"et", et}, + {"initialFrame", initialFrame}, + {"mission", mission}, + {"ckQuality", ckQuality} + }); + json out = spiceAPIQuery("frameTrace", args); + return out["body"]["return"].get>>(); + }else{ + return SpiceQL::frameTrace(et, initialFrame, mission, ckQuality, true); + } + } + + std::vector extractExactCkTimes(double observStart, double observEnd, int targetFrame, std::string mission, std::string ckQuality) { + bool useWeb = QString(Preference::Preferences().findGroup("WebSpice")["UseWebSpice"]).toUpper() == "TRUE"; + if (useWeb){ + json args = json::object({ + {"observStart", observStart}, + {"observEnd", observEnd}, + {"targetFrame",targetFrame}, + {"mission", mission}, + {"ckQuality", ckQuality} + }); + json out = spiceAPIQuery("extractExactCkTimes", args); + return out["body"]["return"].get>(); + }else{ + return SpiceQL::extractExactCkTimes(observStart, observEnd, targetFrame, mission, ckQuality, true); + } + } + + json spiceAPIQuery(std::string functionName, json args, std::string method){ + restincurl::Client client; + // std::string queryString = "https://spiceql-slot1.prod-asc.chs.usgs.gov/" + functionName; + std::string queryString = "127.0.0.1:8080/" + functionName; + + json j; + + if (method == "GET"){ + std::cout << "[RestfulSpice] spiceAPIQuery GET" << std::endl; + queryString += "?"; + for (auto x : args.items()) { + queryString+= x.key(); + queryString+= "="; + queryString+= x.value().dump(); + queryString+= "&"; + } + std::string encodedString = url_encode(queryString); + std::cout << "[RestfulSpice] spiceAPIQuery encodedString = " << encodedString << std::endl; + client.Build()->Get(encodedString).Option(CURLOPT_FOLLOWLOCATION, 1L).AcceptJson().WithCompletion([&](const restincurl::Result& result) { + std::cout << "[RestfulSpice] spiceAPIQuery GET result = " << result.body << std::endl; + j = json::parse(result.body); + }).ExecuteSynchronous(); + }else{ + client.Build()->Post(queryString).Option(CURLOPT_FOLLOWLOCATION, 1L).AcceptJson().WithJson(args.dump()).WithCompletion([&](const restincurl::Result& result) { + std::cout << "[RestfulSpice] spiceAPIQuery POST result = " << result.body << std::endl; + j = json::parse(result.body); + }).ExecuteSynchronous(); + } + client.CloseWhenFinished(); + client.WaitForFinish(); + + // @TODO throw exception if no json or invalid json is returned + return j; + } + + std::string url_encode(const std::string &value) { + std::ostringstream escaped; + escaped.fill('0'); + escaped << std::hex; + + for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) { + std::string::value_type c = (*i); + + // Keep alphanumeric and other accepted characters intact + if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~' || c == '&' || c == '/' || c == '?' || c == '=' || c == ':') { + escaped << c; + continue; + } + + if (c == '"'){ + continue; + } + + // Any other characters are percent-encoded + escaped << std::uppercase; + escaped << '%' << std::setw(2) << int((unsigned char) c); + escaped << std::nouppercase; + } + + return escaped.str(); + } + +} diff --git a/isis/src/base/objs/RestfulSpice/RestfulSpice.h b/isis/src/base/objs/RestfulSpice/RestfulSpice.h new file mode 100644 index 0000000000..7b0f28d3b1 --- /dev/null +++ b/isis/src/base/objs/RestfulSpice/RestfulSpice.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +using json=nlohmann::json; + +namespace Isis::RestfulSpice { + extern std::map spiceql_mission_map; + + std::vector> getTargetStates(std::vector ets, std::string target, std::string observer, std::string frame, std::string abscorr, std::string mission, std::string ckQuality, std::string spkQuality); + std::vector> getTargetOrientations(std::vector ets, int toFrame, int refFrame, std::string mission, std::string ckQuality); + double strSclkToEt(int frameCode, std::string sclk, std::string mission); + double doubleSclkToEt(int frameCode, double sclk, std::string mission); + double utcToEt(std::string utc); + std::string etToUtc(double et, std::string format, double precision); + std::string doubleEtToSclk(int frameCode, double et, std::string mission); + int translateNameToCode(std::string frame, std::string mission); + std::string translateCodeToName(int code, std::string mission); + std::vector getFrameInfo(int frame, std::string mission) ; + json getTargetFrameInfo(int targetId, std::string mission); + json findMissionKeywords(std::string key, std::string mission); + json findTargetKeywords(std::string key, std::string mission); + std::vector> frameTrace(double et, int initialFrame, std::string mission, std::string ckQuality); + std::vector extractExactCkTimes(double observStart, double observEnd, int targetFrame, std::string mission, std::string ckQuality); + + json spiceAPIQuery(std::string functionName, json args, std::string method="GET"); + std::string url_encode(const std::string &value); +} \ No newline at end of file diff --git a/isis/src/base/objs/RestfulSpice/restincurl.h b/isis/src/base/objs/RestfulSpice/restincurl.h new file mode 100644 index 0000000000..7c7ebb38c3 --- /dev/null +++ b/isis/src/base/objs/RestfulSpice/restincurl.h @@ -0,0 +1,1784 @@ +#pragma once + +/* + MIT License + + Copyright (c) 2018 Jarle Aase + + 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. + + On Github: https://github.com/jgaa/RESTinCurl +*/ + +/*! \file */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef RESTINCURL_WITH_OPENSSL_THREADS +# include +#endif + +/*! \def RESTINCURL_MAX_CONNECTIONS + * \brief Max concurrent connections + * + * This option restrains the maximum number of concurrent + * connections that will be used at any time. It sets + * libcurl's `CURLMOPT_MAXCONNECTS` option, and also + * puts any new requests in a waiting queue. As soon as + * some active request finish, the oldest waiting request + * will be served (FIFO queue). + * + * The default value is 32 + */ +#ifndef RESTINCURL_MAX_CONNECTIONS +# define RESTINCURL_MAX_CONNECTIONS 32L +#endif + +/*! \def RESTINCURL_ENABLE_ASYNC + * \brief Enables or disables asynchronous mode. + * + * In asynchronous mode, many requests can be + * served simultaneously using a worker-thread. + * In synchronous mode, you use the current + * thread to serve only one request at the time. + * + * Default is 1 (asynchronous mode enabled). + */ + +#ifndef RESTINCURL_ENABLE_ASYNC +# define RESTINCURL_ENABLE_ASYNC 1 +#endif + +/*! \def RESTINCURL_IDLE_TIMEOUT_SEC + * \brief How long to wait for the next request before the idle worker-thread is stopped. + * + * This will delete curl's connection-cache and cause a new thread to be created + * and new connections to be made if there are new requests at at later time. + * + * Note that this option is only relevant in asynchronous mode. + * + * Default is 60 seconds. + */ +#ifndef RESTINCURL_IDLE_TIMEOUT_SEC +# define RESTINCURL_IDLE_TIMEOUT_SEC 60 +#endif + +/*! \def RESTINCURL_LOG_VERBOSE_ENABLE + * \brief Enable very verbose logging + * + * This option enabled very verbose logging, suitable to + * pinpoint problems during development / porting of the library itself. + * + * Default is 0 (disabled). + */ +#ifndef RESTINCURL_LOG_VERBOSE_ENABLE +# define RESTINCURL_LOG_VERBOSE_ENABLE 0 +#endif + +#if defined(_LOGFAULT_H) && !defined (RESTINCURL_LOG) && !defined (RESTINCURL_LOG_TRACE) +# define RESTINCURL_LOG(msg) LFLOG_DEBUG << "restincurl: " << msg +# if RESTINCURL_LOG_VERBOSE_ENABLE +# define RESTINCURL_LOG_TRACE(msg) LFLOG_IFALL_TRACE("restincurl: " << msg) +# endif // RESTINCURL_LOG_VERBOSE_ENABLE +#endif + +#if defined(RESTINCURL_USE_SYSLOG) || defined(RESTINCURL_USE_ANDROID_NDK_LOG) +# ifdef RESTINCURL_USE_SYSLOG +# include +# endif +# ifdef RESTINCURL_USE_ANDROID_NDK_LOG +# include +# endif +# include +# define RESTINCURL_LOG(msg) ::restincurl::Log(restincurl::LogLevel::DEBUG).Line() << msg +#endif + +/*! \def RESTINCURL_ENABLE_DEFAULT_LOGGER + * \brief Enables a simple built-in logger + * + * RESTinCurl has a very simple built in logger. + * + * It writes to either std::clog, the Unix syslog or Androids log facility. + * + * - Define RESTINCURL_USE_SYSLOG to use syslog + * - Define RESTINCURL_USE_ANDROID_NDK_LOG to use the Android NDK logger. + * + * By default, it will write to std::clog. + * + * Default value is 0 (disabled) + */ +#ifndef RESTINCURL_ENABLE_DEFAULT_LOGGER +# define RESTINCURL_ENABLE_DEFAULT_LOGGER 0 +#endif + +/*! \def RESTINCURL_LOG + * \brief Macro to log debug messages + * + * If you want to log messages from the library to your own log facility, you + * may define this macro to do so. + * + * Note that the logging statements in the library expect the log `msg` to be + * std::ostream compliant. The macro must be able to deal with statement such as: + * - RESTINCURL_LOG("test"); + * - RESTINCURL_LOG("test " << 1 << " and " << 2); + * + * If you manually define this macro, you should not define `RESTINCURL_ENABLE_DEFAULT_LOGGER`. + */ +#ifndef RESTINCURL_LOG +# if RESTINCURL_ENABLE_DEFAULT_LOGGER +# define RESTINCURL_LOG(msg) std::clog << msg << std::endl +# else +# define RESTINCURL_LOG(msg) +# endif +#endif + +/*! \def RESTINCURL_LOG_TRACE + * \brief Macro to log debug messages + * + * If you want to log trace-messages from the library to your own log facility, you + * may define this macro to do so. + * + * Note that the logging statements in the library expect the log `msg` to be + * std::ostream compliant. The macro must be able to deal with statement such as: + * - RESTINCURL_LOG_TRACE("test"); + * - RESTINCURL_LOG_TRACE("test " << 1 << " and " << 2); + * + * If you manually define this macro, you should not define `RESTINCURL_ENABLE_DEFAULT_LOGGER`. + * + * This macro is only called if `RESTINCURL_LOG_VERBOSE_ENABLE` is defined and not 0. + */ +#ifndef RESTINCURL_LOG_TRACE +# if RESTINCURL_LOG_VERBOSE_ENABLE +# define RESTINCURL_LOG_TRACE(msg) RESTINCURL_LOG(msg) +# else +# define RESTINCURL_LOG_TRACE(msg) +# endif +#endif + +namespace restincurl { + +#if defined(RESTINCURL_USE_SYSLOG) || defined(RESTINCURL_USE_ANDROID_NDK_LOG) + enum class LogLevel { DEBUG }; + + class Log { + public: + Log(const LogLevel level) : level_{level} {} + ~Log() { +# ifdef RESTINCURL_USE_SYSLOG + static const std::array syslog_priority = { LOG_DEBUG }; + static std::once_flag syslog_opened; + std::call_once(syslog_opened, [] { + openlog(nullptr, 0, LOG_USER); + }); +# endif +# ifdef RESTINCURL_USE_ANDROID_NDK_LOG + static const std::array android_priority = { ANDROID_LOG_DEBUG }; +# endif + const auto msg = out_.str(); + +# ifdef RESTINCURL_USE_SYSLOG + syslog(syslog_priority.at(static_cast(level_)), "%s", msg.c_str()); +# endif +# ifdef RESTINCURL_USE_ANDROID_NDK_LOG + __android_log_write(android_priority.at(static_cast(level_)), + "restincurl", msg.c_str()); +# endif + } + + std::ostringstream& Line() { return out_; } + +private: + const LogLevel level_; + std::ostringstream out_; + }; +#endif + + + + using lock_t = std::lock_guard; + + /*! The Result from a request\ + */ + struct Result { + Result() = default; + Result(const CURLcode& code) { + curl_code = code; + msg = curl_easy_strerror(code); + } + + /*! Check if the reqtest appears to be successful */ + bool isOk() const noexcept { + if (curl_code == CURLE_OK) { + if ((http_response_code >= 200) && (http_response_code < 300)) { + return true; + } + } + + return false; + } + + /*! The CURLcode returned by libcurl for this request. + * + * CURLE_OK (or 0) indicates success. + */ + CURLcode curl_code = {}; + + /*! The HTTP result code for the request */ + long http_response_code = {}; + + /*! If the request was unsuccessful (curl_code != 0), the error string reported by libcurl. */ + std::string msg; + + /*! The body of the request returned by the server. + * + * Note that if you specified your own body handler or body variable, for the request, `body` will be empty. + */ + std::string body; + }; + + enum class RequestType { GET, PUT, POST, HEAD, DELETE, PATCH, OPTIONS, POST_MIME, INVALID }; + + /*! Completion debug_callback + * + * This callback is called when a request completes, or fails. + * + * \param result The result of the request. + */ + using completion_fn_t = std::function; + + /*! Base class for RESTinCurl exceptions */ + class Exception : public std::runtime_error { + public: + Exception(const std::string& msg) : runtime_error(msg) {} + }; + + /*! Exception thrown when some system function, like `pipe()` failed. */ + class SystemException : public Exception { + public: + SystemException(const std::string& msg, const int e) : Exception(msg + " " + strerror(e)), err_{e} {} + + int getErrorCode() const noexcept { return err_; } + + private: + const int err_; + }; + + /*! Exception thrown when a curl library method failed. */ + class CurlException : public Exception { + public: + CurlException(const std::string msg, const CURLcode err) + : Exception(msg + '(' + std::to_string(err) + "): " + curl_easy_strerror(err)) + , err_{err} + {} + + CurlException(const std::string msg, const CURLMcode err) + : Exception(msg + '(' + std::to_string(err) + "): " + curl_multi_strerror(err)) + , err_{err} + {} + + int getErrorCode() const noexcept { return err_; } + + private: + const int err_; + }; + + class EasyHandle { + public: + using ptr_t = std::unique_ptr; + using handle_t = decltype(curl_easy_init()); + + EasyHandle() { + RESTINCURL_LOG("EasyHandle created: " << handle_); + } + + ~EasyHandle() { + Close(); + } + + void Close() { + if (handle_) { + RESTINCURL_LOG("Cleaning easy-handle " << handle_); + curl_easy_cleanup(handle_); + handle_ = nullptr; + } + } + + operator handle_t () const noexcept { return handle_; } + + private: + handle_t handle_ = curl_easy_init(); + }; + + /*! Curl option wrapper class + * + * This is just a thin C++ wrapper over Curl's `curl_easy_setopt()` method. + */ + class Options { + public: + Options(EasyHandle& eh) : eh_{eh} {} + + /*! Set an option + * + * \param opt CURLoption enum to change + * \param value Value to set + */ + template + Options& Set(const CURLoption& opt, const T& value) { + const auto ret = curl_easy_setopt(eh_, opt, value); + if (ret) { + throw CurlException( + std::string("Setting option ") + std::to_string(opt), ret); + } + return *this; + } + + /*! Set an option + * + * \param opt CURLoption enum to change + * \param value String value to set + */ + Options& Set(const CURLoption& opt, const std::string& value) { + return Set(opt, value.c_str()); + } + + private: + EasyHandle& eh_; + }; + + /*! Abstract base class for Data Handlers + * + * Data handlers are used to handle libcurl's callback requirements + * for input and output data. + */ + struct DataHandlerBase { + virtual ~DataHandlerBase() = default; + }; + + /*! Template implementation for input data to curl during a request. + * + * This handler deals with the data received from the HTTP server + * during a request. This implementation will typically use + * T=std::string and just store the received data in a string. For + * json/XML payloads that's probably all you need. But if you receive + * binary data, you may want to use a container like std::vector or std::deque in stead. + */ + template + struct InDataHandler : public DataHandlerBase{ + InDataHandler(T& data) : data_{data} { + RESTINCURL_LOG_TRACE("InDataHandler address: " << this); + } + + static size_t write_callback(char *ptr, size_t size, size_t nitems, void *userdata) { + assert(userdata); + auto self = reinterpret_cast(userdata); + const auto bytes = size * nitems; + if (bytes > 0) { + std::copy(ptr, ptr + bytes, std::back_inserter(self->data_)); + } + return bytes; + } + + T& data_; + }; + + /*! Template implementation for output data to curl during a request. + * + * This handler deals with the data sent to the HTTP server + * during a request (POST, PATCH etc). This implementation will typically use + * T=std::string and just store the data in a string. For + * json/XML payloads that's probably all you need. But if you send + * binary data, you may want to use a container like std::vector or std::deque in stead. + */ + template + struct OutDataHandler : public DataHandlerBase { + OutDataHandler() = default; + OutDataHandler(const T& v) : data_{v} {} + OutDataHandler(T&& v) : data_{std::move(v)} {} + + static size_t read_callback(char *bufptr, size_t size, size_t nitems, void *userdata) { + assert(userdata); + OutDataHandler *self = reinterpret_cast(userdata); + const auto bytes = size * nitems; + auto out_bytes = std::min(bytes, (self->data_.size() - self->sendt_bytes_)); + std::copy(self->data_.cbegin() + self->sendt_bytes_, + self->data_.cbegin() + (self->sendt_bytes_ + out_bytes), + bufptr); + self->sendt_bytes_ += out_bytes; + + RESTINCURL_LOG_TRACE("Sent " << out_bytes << " of total " << self->data_.size() << " bytes."); + return out_bytes; + } + + T data_; + size_t sendt_bytes_ = 0; + }; + + class Request { + public: + using ptr_t = std::unique_ptr; + + Request() + : eh_{std::make_unique()} + { + } + + Request(EasyHandle::ptr_t&& eh) + : eh_{std::move(eh)} + { + } + + ~Request() { + if (headers_) { + curl_slist_free_all(headers_); + } + } + + void Prepare(const RequestType rq, completion_fn_t completion) { + request_type_ = rq; + SetRequestType(); + completion_ = std::move(completion); + } + + void OpenSourceFile(const std::string& path) { + assert(!fp_); + auto fp = fopen(path.c_str(), "rb"); + if (!fp) { + const auto e = errno; + throw SystemException{std::string{"Unable to open file "} + path, e}; + } + + fp_= std::shared_ptr(fp, std::fclose); + } + + FILE *GetSourceFp() { + assert(fp_); + return fp_.get(); + } + + // Synchronous execution. + void Execute() { + const auto result = curl_easy_perform(*eh_); + CallCompletion(result); + } + + void Complete(CURLcode cc, const CURLMSG& /*msg*/) { + CallCompletion(cc); + } + + EasyHandle& GetEasyHandle() noexcept { assert(eh_); return *eh_; } + RequestType GetRequestType() noexcept { return request_type_; } + + void SetDefaultInHandler(std::unique_ptr ptr) { + default_in_handler_ = std::move(ptr); + } + + void SetDefaultOutHandler(std::unique_ptr ptr) { + default_out_handler_ = std::move(ptr); + } + + using headers_t = curl_slist *; + headers_t& GetHeaders() { + return headers_; + } + + std::string& getDefaultInBuffer() { + return default_data_buffer_; + } + + void InitMime() { + if (!mime_) { + mime_ = curl_mime_init(*eh_); + } + } + + void AddFileAsMimeData(const std::string& path, + const std::string& name, + const std::string& remoteName, + const std::string& mimeType) { + + InitMime(); + assert(mime_); + auto * part = curl_mime_addpart(mime_); + curl_mime_filedata(part, path.c_str()); + curl_mime_name(part, name.empty() ? "file" :name.c_str()); + + if (!remoteName.empty()) { + curl_mime_filename(part, remoteName.c_str()); + } + + if (!mimeType.empty()) { + curl_mime_type(part, mimeType.c_str()); + } + } + + private: + void CallCompletion(CURLcode cc) { + Result result(cc); + + curl_easy_getinfo (*eh_, CURLINFO_RESPONSE_CODE, + &result.http_response_code); + RESTINCURL_LOG("Complete: http code: " << result.http_response_code); + if (completion_) { + if (!default_data_buffer_.empty()) { + result.body = std::move(default_data_buffer_); + } + completion_(result); + } + } + + void SetRequestType() { + switch(request_type_) { + case RequestType::GET: + curl_easy_setopt(*eh_, CURLOPT_HTTPGET, 1L); + break; + case RequestType::PUT: + headers_ = curl_slist_append(headers_, "Transfer-Encoding: chunked"); + curl_easy_setopt(*eh_, CURLOPT_UPLOAD, 1L); + break; + case RequestType::POST: + headers_ = curl_slist_append(headers_, "Transfer-Encoding: chunked"); + curl_easy_setopt(*eh_, CURLOPT_UPLOAD, 0L); + curl_easy_setopt(*eh_, CURLOPT_POST, 1L); + break; + case RequestType::HEAD: + curl_easy_setopt(*eh_, CURLOPT_NOBODY, 1L); + break; + case RequestType::OPTIONS: + curl_easy_setopt(*eh_, CURLOPT_CUSTOMREQUEST, "OPTIONS"); + break; + case RequestType::PATCH: + headers_ = curl_slist_append(headers_, "Transfer-Encoding: chunked"); + curl_easy_setopt(*eh_, CURLOPT_CUSTOMREQUEST, "PATCH"); + break; + case RequestType::DELETE: + curl_easy_setopt(*eh_, CURLOPT_CUSTOMREQUEST, "DELETE"); + break; + case RequestType::POST_MIME: + InitMime(); + curl_easy_setopt(*eh_, CURLOPT_MIMEPOST, mime_); + break; + default: + throw Exception("Unsupported request type" + std::to_string(static_cast(request_type_))); + } + } + + EasyHandle::ptr_t eh_; + RequestType request_type_ = RequestType::INVALID; + completion_fn_t completion_; + std::unique_ptr default_out_handler_; + std::unique_ptr default_in_handler_; + headers_t headers_ = nullptr; + std::string default_data_buffer_; + std::shared_ptr fp_; + curl_mime *mime_ = {}; + }; + +#if RESTINCURL_ENABLE_ASYNC + + class Signaler { + enum FdUsage { FD_READ = 0, FD_WRITE = 1}; + + public: + using pipefd_t = std::array; + + Signaler() { + auto status = pipe(pipefd_.data()); + if (status) { + throw SystemException("pipe", status); + } + for(auto fd : pipefd_) { + int flags = 0; + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) + flags = 0; + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + } + } + + ~Signaler() { + for(auto fd : pipefd_) { + close(fd); + } + } + + void Signal() { + char byte = {}; + RESTINCURL_LOG_TRACE("Signal: Signaling!"); + if (write(pipefd_[FD_WRITE], &byte, 1) != 1) { + throw SystemException("write pipe", errno); + } + } + + int GetReadFd() { return pipefd_[FD_READ]; } + + bool WasSignalled() { + bool rval = false; + char byte = {}; + while(read(pipefd_[FD_READ], &byte, 1) > 0) { + RESTINCURL_LOG_TRACE("Signal: Was signalled"); + rval = true; + } + + return rval; + } + + private: + pipefd_t pipefd_; + }; + + /*! Thread support for the TLS layer used by libcurl. + * + * Some TLS libraries require that you supply callback functions + * to deal with thread synchronization. + * + * See https://curl.haxx.se/libcurl/c/threadsafe.html + * + * You can deal with this yourself, or in the case + * that RESTinCurl support your TLS library, you can use + * this class. + * + * Currently supported libraries: + * + * - Opeenssl (only required for OpenSSL <= 1.0.2) + * define `RESTINCURL_WITH_OPENSSL_THREADS` + * + * The class is written so that you can use it in your code, and + * it will only do something when the appropriate define is set. + * + * This class is only available when `RESTINCURL_ENABLE_ASYNC` is nonzero. + */ + class TlsLocker + { + public: + +#ifdef RESTINCURL_WITH_OPENSSL_THREADS + static void opensslLockCb(int mode, int type, char *, int) { + if(mode & CRYPTO_LOCK) { + lock(); + } + else { + unlock(); + } + } + + static unsigned long getThreadId(void) { + return reinterpret_cast(::this_thread::get_id()); + } + + /*! Enable the built-in support. */ + static void tlsLockInit() { + CRYPTO_set_id_callback((unsigned long (*)())getThreadId); + CRYPTO_set_locking_callback((void (*)())opensslLockCb); + } + + /*! Free up resources used when finished using TLS. */ + static void tlsLockKill() { + CRYPTO_set_locking_callback(NULL); + } + +#else + /*! Enable the built-in support. */ + static void tlsLOckInit() { + ; // Do nothing + } + + /*! Free up resources used when finished using TLS. */ + static void tlsLockKill() { + ; // Do nothing + } +#endif + + + + + private: + static void lock() { + mutex_.lock(); + } + + static void unlock() { + mutex_.unlock(); + } + + static std::mutex mutex_; + }; + + class Worker { + class WorkerThread { + public: + WorkerThread(std::function && fn) + : thread_{std::move(fn)} {} + + ~WorkerThread() { + if (thread_.get_id() == std::this_thread::get_id()) { + // Allow the thread to finish exiting the lambda + thread_.detach(); + } + } + + void Join() const { + std::call_once(joined_, [this] { + const_cast(this)->thread_.join(); + }); + } + + bool Joinable() const { + return thread_.joinable(); + } + + operator std::thread& () { return thread_; } + + private: + std::thread thread_; + mutable std::once_flag joined_; + }; + + public: + Worker() = default; + + ~Worker() { + if (thread_ && thread_->Joinable()) { + Close(); + Join(); + } + } + + void PrepareThread() { + // Must only be called when the mutex_ is acquired! + assert(!mutex_.try_lock()); + if (abort_ || done_) { + return; + } + if (!thread_) { + thread_ = std::make_shared([&] { + try { + RESTINCURL_LOG("Starting thread " << std::this_thread::get_id()); + Init(); + Run(); + Clean(); + } catch (const std::exception& ex) { + RESTINCURL_LOG("Worker: " << ex.what()); + } + RESTINCURL_LOG("Exiting thread " << std::this_thread::get_id()); + + lock_t lock(mutex_); + thread_.reset(); + }); + } + assert(!abort_); + assert(!done_); + } + + static std::unique_ptr Create() { + return std::make_unique(); + } + + void Enqueue(Request::ptr_t req) { + RESTINCURL_LOG_TRACE("Queuing request "); + lock_t lock(mutex_); + PrepareThread(); + queue_.push_back(std::move(req)); + Signal(); + } + + void Join() const { + decltype(thread_) thd; + + { + lock_t lock(mutex_); + if (thread_ && thread_->Joinable()) { + thd = thread_; + } + } + + if (thd) { + thd->Join(); + } + } + + // Let the current transfers complete, then quit + void CloseWhenFinished() { + { + lock_t lock(mutex_); + close_pending_ = true; + } + Signal(); + } + + // Shut down now. Abort all transfers + void Close() { + { + lock_t lock(mutex_); + abort_ = true; + } + Signal(); + } + + // Check if the run loop has finished. + bool IsDone() const { + lock_t lock(mutex_); + return done_; + } + + bool HaveThread() const noexcept { + lock_t lock(mutex_); + if (thread_) { + return true; + } + return false; + } + + size_t GetNumActiveRequests() const { + lock_t lock(mutex_); + return ongoing_.size(); + } + + private: + void Signal() { + signal_.Signal(); + } + + void Dequeue() { + decltype(queue_) tmp; + + { + lock_t lock(mutex_); + if ((queue_.size() + ongoing_.size()) <= RESTINCURL_MAX_CONNECTIONS) { + tmp = std::move(queue_); + pending_entries_in_queue_ = false; + } else { + auto remains = std::min(RESTINCURL_MAX_CONNECTIONS - ongoing_.size(), queue_.size()); + if (remains) { + auto it = queue_.begin(); + RESTINCURL_LOG_TRACE("Adding only " << remains << " of " << queue_.size() + << " requests from queue: << RESTINCURL_MAX_CONNECTIONS=" << RESTINCURL_MAX_CONNECTIONS); + while(remains--) { + assert(it != queue_.end()); + tmp.push_back(std::move(*it)); + ++it; + } + queue_.erase(queue_.begin(), it); + } else { + assert(ongoing_.size() == RESTINCURL_MAX_CONNECTIONS); + RESTINCURL_LOG_TRACE("Adding no entries from queue: RESTINCURL_MAX_CONNECTIONS=" + << RESTINCURL_MAX_CONNECTIONS); + } + pending_entries_in_queue_ = true; + } + } + + for(auto& req: tmp) { + assert(req); + const auto& eh = req->GetEasyHandle(); + RESTINCURL_LOG_TRACE("Adding request: " << eh); + ongoing_[eh] = std::move(req); + const auto mc = curl_multi_add_handle(handle_, eh); + if (mc != CURLM_OK) { + throw CurlException("curl_multi_add_handle", mc); + } + } + } + + void Init() { + if ((handle_ = curl_multi_init()) == nullptr) { + throw std::runtime_error("curl_multi_init() failed"); + } + + curl_multi_setopt(handle_, CURLMOPT_MAXCONNECTS, RESTINCURL_MAX_CONNECTIONS); + } + + void Clean() { + if (handle_) { + RESTINCURL_LOG_TRACE("Calling curl_multi_cleanup: " << handle_); + curl_multi_cleanup(handle_); + handle_ = nullptr; + } + } + + bool EvaluateState(const bool transfersRunning, const bool doDequeue) const noexcept { + lock_t lock(mutex_); + + RESTINCURL_LOG_TRACE("Run loop: transfers_running=" << transfersRunning + << ", do_dequeue=" << doDequeue + << ", close_pending_=" << close_pending_); + + return !abort_ && (transfersRunning || !close_pending_); + } + + auto GetNextTimeout() const noexcept { + return std::chrono::steady_clock::now() + + std::chrono::seconds(RESTINCURL_IDLE_TIMEOUT_SEC); + } + + void Run() { + int transfers_running = -1; + fd_set fdread = {}; + fd_set fdwrite = {}; + fd_set fdexcep = {}; + bool do_dequeue = true; + auto timeout = GetNextTimeout(); + + while (EvaluateState(transfers_running, do_dequeue)) { + + if (do_dequeue) { + Dequeue(); + do_dequeue = false; + } + + /* timeout or readable/writable sockets */ + const bool initial_ideling = transfers_running == -1; + curl_multi_perform(handle_, &transfers_running); + if ((transfers_running == 0) && initial_ideling) { + transfers_running = -1; // Let's ignore close_pending_ until we have seen a request + } + + // Shut down the thread if we have been idling too long + if (transfers_running <= 0) { + if (timeout < std::chrono::steady_clock::now()) { + RESTINCURL_LOG("Idle timeout. Will shut down the worker-thread."); + break; + } + } else { + timeout = GetNextTimeout(); + } + + int numLeft = {}; + while (auto m = curl_multi_info_read(handle_, &numLeft)) { + assert(m); + auto it = ongoing_.find(m->easy_handle); + if (it != ongoing_.end()) { + RESTINCURL_LOG("Finishing request with easy-handle: " + << (EasyHandle::handle_t)it->second->GetEasyHandle() + << "; with result: " << m->data.result << " expl: '" << curl_easy_strerror(m->data.result) + << "'; with msg: " << m->msg); + + try { + it->second->Complete(m->data.result, m->msg); + } catch(const std::exception& ex) { + RESTINCURL_LOG("Complete threw: " << ex.what()); + } + if (m->msg == CURLMSG_DONE) { + curl_multi_remove_handle(handle_, m->easy_handle); + } + it->second->GetEasyHandle().Close(); + ongoing_.erase(it); + } else { + RESTINCURL_LOG("Failed to find easy_handle in ongoing!"); + assert(false); + } + } + + { + lock_t lock(mutex_); + // Avoid using select() as a timer when we need to exit anyway + if (abort_ || (!transfers_running && close_pending_)) { + break; + } + } + + auto next_timeout = std::chrono::duration_cast( + timeout - std::chrono::steady_clock::now()); + long sleep_duration = std::max(1, next_timeout.count()); + /* extract sleep_duration value */ + + + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + int maxfd = -1; + if (transfers_running > 0) { + curl_multi_timeout(handle_, &sleep_duration); + if (sleep_duration < 0) { + sleep_duration = 1000; + } + + /* get file descriptors from the transfers */ + const auto mc = curl_multi_fdset(handle_, &fdread, &fdwrite, &fdexcep, &maxfd); + RESTINCURL_LOG_TRACE("maxfd: " << maxfd); + if (mc != CURLM_OK) { + throw CurlException("curl_multi_fdset", mc); + } + + if (maxfd == -1) { + // Curl want's us to revisit soon + sleep_duration = 50; + } + } // active transfers + + struct timeval tv = {}; + tv.tv_sec = sleep_duration / 1000; + tv.tv_usec = (sleep_duration % 1000) * 1000; + + const auto signalfd = signal_.GetReadFd(); + + RESTINCURL_LOG_TRACE("Calling select() with timeout of " + << sleep_duration + << " ms. Next timeout in " << next_timeout.count() << " ms. " + << transfers_running << " active transfers."); + + FD_SET(signalfd, &fdread); + maxfd = std::max(signalfd, maxfd) + 1; + + const auto rval = select(maxfd, &fdread, &fdwrite, &fdexcep, &tv); + RESTINCURL_LOG_TRACE("select(" << maxfd << ") returned: " << rval); + + if (rval > 0) { + if (FD_ISSET(signalfd, &fdread)) { + RESTINCURL_LOG_TRACE("FD_ISSET was true: "); + do_dequeue = signal_.WasSignalled(); + } + + } + if (pending_entries_in_queue_) { + do_dequeue = true; + } + } // loop + + + lock_t lock(mutex_); + if (close_pending_ || abort_) { + done_ = true; + } + } + + bool close_pending_ {false}; + bool abort_ {false}; + bool done_ {false}; + bool pending_entries_in_queue_ = false; + decltype(curl_multi_init()) handle_ = {}; + mutable std::mutex mutex_; + std::shared_ptr thread_; + std::deque queue_; + std::map ongoing_; + Signaler signal_; + }; +#endif // RESTINCURL_ENABLE_ASYNC + + + /*! Convenient interface to build requests. + * + * Even if this is a light-weight wrapper around libcurl, we have a + * simple and modern way to define our requests that contains + * convenience-methods for the most common use-cases. + */ + class RequestBuilder { + // noop handler for incoming data + static size_t write_callback(char *ptr, size_t size, size_t nitems, void *userdata) { + const auto bytes = size * nitems; + return bytes; + } + + static int debug_callback(CURL *handle, curl_infotype type, + char *data, size_t size, + void *userp) { + + std::string msg; + switch(type) { + case CURLINFO_TEXT: + msg = "==> Info: "; + break; + case CURLINFO_HEADER_OUT: + msg = "=> Send header: "; + break; + case CURLINFO_DATA_OUT: + msg = "=> Send data: "; + break; + case CURLINFO_SSL_DATA_OUT: + msg = "=> Send SSL data: "; + break; + case CURLINFO_HEADER_IN: + msg = "<= Recv header: "; + break; + case CURLINFO_DATA_IN: + msg = "<= Recv data: "; + break; + case CURLINFO_SSL_DATA_IN: + msg = "<= Recv SSL data: "; + break; + case CURLINFO_END: // Don't seems to be used + msg = "<= End: "; + break; + } + + std::copy(data, data + size, std::back_inserter(msg)); + RESTINCURL_LOG(handle << " " << msg); + return 0; + } + + public: + using ptr_t = std::unique_ptr; + RequestBuilder( +#if RESTINCURL_ENABLE_ASYNC + Worker& worker +#endif + ) + : request_{std::make_unique()} + , options_{std::make_unique(request_->GetEasyHandle())} +#if RESTINCURL_ENABLE_ASYNC + , worker_(worker) +#endif + {} + + ~RequestBuilder() { + } + + protected: + RequestBuilder& Prepare(RequestType rt, const std::string& url) { + assert(request_type_ == RequestType::INVALID); + assert(!is_built_); + request_type_ = rt; + url_ = url; + return *this; + } + + public: + bool CanSendFile() const noexcept { + return request_type_ == RequestType::POST + || request_type_ == RequestType::PUT; + } + + + /*! + * \name Type of request + * + * Use one of these functions to declare what HTTP request you want to use. + * + * \param url Url to call. Must be a complete URL, starting with + * "http://" or "https://" + * + * Note that you must use only only one of these methods in one request. + */ + //@{ + + /*! Use a HTTP GET request */ + RequestBuilder& Get(const std::string& url) { + return Prepare(RequestType::GET, url); + } + + /*! Use a HTTP HEAD request */ + RequestBuilder& Head(const std::string& url) { + return Prepare(RequestType::HEAD, url); + } + + /*! Use a HTTP POST request */ + RequestBuilder& Post(const std::string& url) { + return Prepare(RequestType::POST, url); + } + + /*! Use a HTTP POST request */ + RequestBuilder& PostMime(const std::string& url) { + return Prepare(RequestType::POST_MIME, url); + } + + /*! Use a HTTP PUT request */ + RequestBuilder& Put(const std::string& url) { + return Prepare(RequestType::PUT, url); + } + + /*! Use a HTTP PATCH request */ + RequestBuilder& Patch(const std::string& url) { + return Prepare(RequestType::PATCH, url); + } + + /*! Use a HTTP DELETE request */ + RequestBuilder& Delete(const std::string& url) { + return Prepare(RequestType::DELETE, url); + } + + /*! Use a HTTP OPTIONS request */ + RequestBuilder& Options(const std::string& url) { + return Prepare(RequestType::OPTIONS, url); + } + //@} + + /*! Specify a HTTP header for the request. + * + * \param value The value of the header-line, properly formatted according to the relevant HTTP specifications. + */ + RequestBuilder& Header(const char *value) { + assert(value); + assert(!is_built_); + request_->GetHeaders() = curl_slist_append(request_->GetHeaders(), value); + return *this; + } + + /*! Specify a HTTP header for the request. + * + * \param name Name of the header + * \param value The value of the header + * + * This is a convenience method that will build the appropriate header for you. + */ + RequestBuilder& Header(const std::string& name, + const std::string& value) { + const auto v = name + ": " + value; + return Header(v.c_str()); + } + + /*! Sets the content-type to "Application/json; charset=utf-8" */ + RequestBuilder& WithJson() { + return Header("Content-type: Application/json; charset=utf-8"); + } + + /*! Sets the content-type to "Application/json; charset=utf-8" + * + * \param body Json payload to send with the request. + */ + RequestBuilder& WithJson(std::string body) { + WithJson(); + return SendData(std::move(body)); + } + + /*! Sets the accept header to "Application/json" */ + RequestBuilder& AcceptJson() { + return Header("Accept: Application/json"); + } + + /*! Sets a Curl options. + * + * \param opt CURLoption enum specifying the option + * \param value Value to set. + * + * It is critical that the type of the value is of the same type that + * libcurl is expecting for the option. RESTinCurl makes no attempt + * to validate or cast the values. + * + * Please refer to the libcurl documentation for curl_easy_setopt() + */ + template + RequestBuilder& Option(const CURLoption& opt, const T& value) { + assert(!is_built_); + options_->Set(opt, value); + return *this; + } + + /*! Enables or disables trace logging for requests. + * + * The trace logging will show detailed information about what + * libcurl does and data sent and received during a request. + * + * Basically it sets `CURLOPT_DEBUGFUNCTION` and `CURLOPT_VERBOSE`. + */ + RequestBuilder& Trace(bool enable = true) { + if (enable) { + Option(CURLOPT_DEBUGFUNCTION, debug_callback); + Option(CURLOPT_VERBOSE, 1L); + } else { + Option(CURLOPT_VERBOSE, 0L); + } + + return *this; + } + + /*! Set request timeout + * + * \param timeout Timeout in milliseconds. Set to -1 to use the default. + */ + RequestBuilder& RequestTimeout(const long timeout) { + request_timeout_ = timeout; + return *this; + } + + /*! Set the connect timeout for a request + * + * \param timeout Timeout in milliseconds. Set to -1 to use the default. + */ + RequestBuilder& ConnectTimeout(const long timeout) { + connect_timeout_ = timeout; + return *this; + } + + /*! Send a file + * + * \param path Full path to the file to send. + * + * \throws SystemException if the file cannot be opened. + * \throws Exception if the method is called for a non-send operation + */ + RequestBuilder& SendFile(const std::string& path) { + assert(!is_built_); + assert(CanSendFile()); + if (!CanSendFile()) { + throw Exception{"Invalid curl operation for a file upload"}; + } + + assert(request_); + request_->OpenSourceFile(path); + struct stat st = {}; + if(fstat(fileno(request_->GetSourceFp()), &st) != 0) { + const auto e = errno; + throw SystemException{std::string{"Unable to stat file "} + path, e}; + } + + // set where to read from (on Windows you need to use READFUNCTION too) + options_->Set(CURLOPT_READDATA, request_->GetSourceFp()); + options_->Set(CURLOPT_INFILESIZE_LARGE, static_cast(st.st_size)); + have_data_out_ = true; + return *this; + } + + /*! Send a file as a multipart/form mime segment + * + * \param path Full path to the file tro send + * \param name Otional name to use for the file in the mime segment + * \param remoteName Optional name to label the file as + * for the remote end + * \param mimeType Optional mime-type for the file + * + * \throws Exception if the method is called for a + * non-mime-post operation + */ + RequestBuilder& SendFileAsMimeData(const std::string& path, + const std::string& name = {}, + const std::string& remoteName = {}, + const std::string& mimeType = {}) { + assert(request_); + assert(request_type_ == RequestType::POST_MIME); + if (request_type_ != RequestType::POST_MIME) { + throw Exception{"Must use PostMime operation to add mime attachments"}; + } + request_->AddFileAsMimeData(path, name, remoteName, mimeType); + return *this; + } + + /*! Send a file + * + * \param path Full path to the file to send. + * + * \throws SystemException if the file cannot be opened. + * \throws Exception if the method is called for a non-send operation + */ + RequestBuilder& SendFileAsForm(const std::string& path) { + assert(!is_built_); + assert(CanSendFile()); + if (!CanSendFile()) { + throw Exception{"Invalid curl operation for a file upload"}; + } + + assert(request_); + request_->OpenSourceFile(path); + struct stat st = {}; + if(fstat(fileno(request_->GetSourceFp()), &st) != 0) { + const auto e = errno; + throw SystemException{std::string{"Unable to stat file "} + path, e}; + } + + // set where to read from (on Windows you need to use READFUNCTION too) + options_->Set(CURLOPT_READDATA, request_->GetSourceFp()); + options_->Set(CURLOPT_INFILESIZE_LARGE, static_cast(st.st_size)); + have_data_out_ = true; + return *this; + } + + /*! Specify Data Handler for outbound data + * + * You can use this method when you need to use a Data Handler, rather than a simple string, + * to provide the data for a POST, PUT etc. request. + * + * \param dh Data Handler instance. + * + * Note that the Data Handler is passed by reference. It is your responsibility that the + * instance is present at least until the request has finished (your code owns the Data Handler instance). + */ + template + RequestBuilder& SendData(OutDataHandler& dh) { + assert(!is_built_); + options_->Set(CURLOPT_READFUNCTION, dh.read_callback); + options_->Set(CURLOPT_READDATA, &dh); + have_data_out_ = true; + return *this; + } + + /*! Convenience method to specify a object that contains the data to send during a request. + * + * \param data Data to send. Typically this will be a std::string, std::vector or a similar + * object. + * + * RESTinCurl takes ownership of this data (by moving it). + */ + template + RequestBuilder& SendData(T data) { + assert(!is_built_); + auto handler = std::make_unique>(std::move(data)); + auto& handler_ref = *handler; + request_->SetDefaultOutHandler(std::move(handler)); + return SendData(handler_ref); + } + + /*! Specify Data Handler for inbound data + * + * You can use this method when you need to use a Data Handler, rather than a simple string, + * to receive data during the request. + * + * \param dh Data Handler instance. + * + * Note that the Data Handler is passed by reference. It is your responsibility that the + * instance is present at least until the request has finished (your code owns the Data Handler instance). + */ + template + RequestBuilder& StoreData(InDataHandler& dh) { + assert(!is_built_); + options_->Set(CURLOPT_WRITEFUNCTION, dh.write_callback); + options_->Set(CURLOPT_WRITEDATA, &dh); + have_data_in_ = true; + return *this; + } + + /*! Convenience method to specify a object that receives incoming data during a request. + * + * \param data Buffer to hold incoming data. Typically this will be a std::string, + * std::vector or a similar object. + * + * Note that data is passed by reference. It is your responsibility that the + * instance is present at least until the request has finished (your code owns the object). + */ + template + RequestBuilder& StoreData(T& data) { + assert(!is_built_); + auto handler = std::make_unique>(data); + auto& handler_ref = *handler; + request_->SetDefaultInHandler(std::move(handler)); + return StoreData(handler_ref); + } + + /*! Do not process incoming data + * + * The response body will be read from the network, but + * not buffered and not available for later + * inspection. + */ + RequestBuilder& IgnoreIncomingData() { + options_->Set(CURLOPT_WRITEFUNCTION, write_callback); + have_data_in_ = true; + return *this; + } + + /*! Specify a callback that will be called when the request is complete (or failed). + * + * \param fn Callback to be called + * + * For asynchronous requests, the callback will be called from the worker-thread shared + * by all requests and timers for the client instance. It is imperative that you return + * immediately, and don't keep the thread busy more than strictly required. If you need + * do do some computing or IO in response to the information you receive, you should + * do that in another thread. + */ + RequestBuilder& WithCompletion(completion_fn_t fn) { + assert(!is_built_); + completion_ = std::move(fn); + return *this; + } + + /*! HTTP Basic Authentication + * + * Authenticate the request with HTTP Basic Authentication. + * + * \param name Name to authenticate with + * \param passwd Password to authenticate with + * + * Note that if name or password is empty, authentication is + * ignored. This makes it simple to add optional authentication + * to your project, by simply assigning values to the strings + * you pass here, or not. + */ + RequestBuilder& BasicAuthentication(const std::string& name, + const std::string& passwd) { + assert(!is_built_); + + if (!name.empty() && !passwd.empty()) { + options_->Set(CURLOPT_USERNAME, name.c_str()); + options_->Set(CURLOPT_PASSWORD, passwd.c_str()); + } + + return *this; + } + + /*! Set a Curl compatible read handler. + * + * \param handler Curl C API read handler + * + * You probably don't need to call this directly. + */ + RequestBuilder& SetReadHandler(size_t (*handler)(char *, size_t , size_t , void *), void *userdata) { + options_->Set(CURLOPT_READFUNCTION, handler); + options_->Set(CURLOPT_READDATA, userdata); + have_data_out_ = true; + return *this; + } + + /*! Set a Curl compatible write handler. + * + * \param handler Curl C API write handler + * + * You probably don't need to call this directly. + */ + RequestBuilder& SetWriteHandler(size_t (*handler)(char *, size_t , size_t , void *), void *userdata) { + options_->Set(CURLOPT_WRITEFUNCTION,handler); + options_->Set(CURLOPT_WRITEDATA, userdata); + have_data_in_ = true; + return *this; + } + + void Build() { + if (!is_built_) { + // Set up Data Handlers + if (!have_data_in_) { + // Use a default std::string. We expect json anyway... + this->StoreData(request_->getDefaultInBuffer()); + } + + if (have_data_out_) { + options_->Set(CURLOPT_UPLOAD, 1L); + } + + if (request_timeout_ >= 0) { + options_->Set(CURLOPT_TIMEOUT_MS, request_timeout_); + } + + if (connect_timeout_ >= 0) { + options_->Set(CURLOPT_CONNECTTIMEOUT_MS, connect_timeout_); + } + + // Set headers + if (request_->GetHeaders()) { + options_->Set(CURLOPT_HTTPHEADER, request_->GetHeaders()); + } + + // TODO: Prepare the final url (we want nice, correctly encoded request arguments) + options_->Set(CURLOPT_URL, url_); + RESTINCURL_LOG("Preparing connect to: " << url_); + + // Prepare request + request_->Prepare(request_type_, std::move(completion_)); + is_built_ = true; + } + } + + /*! Execute the request synchronously + * + * This will execute the request and call the callback (if you declared one) in the + * current thread before the method returns. + * + * \throws restincurl::Exception derived exceptions on error + * + * This method is available even when `RESTINCURL_ENABLE_ASYNC` is enabled ( != 0). + */ + void ExecuteSynchronous() { + Build(); + request_->Execute(); + } + +#if RESTINCURL_ENABLE_ASYNC + + /*! Execute the request asynchronously + * + * This will queue the request for processing. If the number of active requests are + * less than `RESTINCURL_MAX_CONNECTIONS`, the request will start executing almost immediately. + * + * The method returns immediately. + * + * \throws restincurl::Exception derived exceptions on error + * + * This method is only available when `RESTINCURL_ENABLE_ASYNC` is nonzero. + */ + void Execute() { + Build(); + worker_.Enqueue(std::move(request_)); + } +#endif + + private: + std::unique_ptr request_; + std::unique_ptr options_; + std::string url_; + RequestType request_type_ = RequestType::INVALID; + bool have_data_in_ = false; + bool have_data_out_ = false; + bool is_built_ = false; + completion_fn_t completion_; + long request_timeout_ = 10000L; // 10 seconds + long connect_timeout_ = 3000L; // 1 second +#if RESTINCURL_ENABLE_ASYNC + Worker& worker_; +#endif + }; + + /*! The high level abstraction of the Curl library. + * + * An instance of a Client will, if asynchronous mode is enabled, create a + * worker-thread when the first request is made. This single worker-thread + * is then shared between all requests made for this client. When no requests + * are active, the thread will wait for a while (see RESTINCURL_IDLE_TIMEOUT_SEC), + * keeping libcurl's connection-cache warm, to serve new requests as efficiently as + * possible. When the idle time period expires, the thread will terminate and + * close the connection-cache associated with the client. + * If a new request is made later on, a new worker-thread will be created. + */ + class Client { + + public: + /*! Constructor + * + * \param init Set to true if you need to initialize libcurl. + * + * Libcurl require us to call an initialization method once, before using the library. + * If you use libcurl exclusively from RESTinCurl, `init` needs to be `true` (default). If + * you use RESTinCurl in a project that already initialize libcurl, you should pass `false` + * to the constructor. + * + * RESTinCurl will only call libcurl's initialization once, no matter how many times you + * call the constructor. It's therefore safe to always use the default value when you want RESTinCurl + * to deal with libcurl's initialization. + */ + Client(const bool init = true) { + if (init) { + static std::once_flag flag; + std::call_once(flag, [] { + RESTINCURL_LOG("One time initialization of curl."); + curl_global_init(CURL_GLOBAL_DEFAULT); + }); + } + } + + /*! Destructor + * + * The destructor will try to clean up resources (when in asynchronous mode) + * ant may wait for a little while for IO operations to stop and the worker-thread to + * finish. + * + * This is to prevent your application for crashing if you exit the main thread while + * the worker-thread is still working and accessing memory own by the Client instance. + */ + virtual ~Client() { +#if RESTINCURL_ENABLE_ASYNC + if (worker_) { + try { + worker_->Close(); + } catch (const std::exception& ex) { + RESTINCURL_LOG("~Client(): " << ex.what()); + } + } +#endif + } + + /*! Build a request + * + * Requests are "built" using a series of statements + * to fully express what you want to do and how you want RESTinCurl to do it. + * + * Example + * \code + restincurl::Client client; + client.Build()->Get("https://api.example.com") + .AcceptJson() + .Header("X-Client", "restincurl") + .WithCompletion([&](const Result& result) { + // Do something + }) + .Execute(); + \endcode + */ + std::unique_ptr Build() { + return std::make_unique( +#if RESTINCURL_ENABLE_ASYNC + *worker_ +#endif + ); + } + +#if RESTINCURL_ENABLE_ASYNC + /*! Shut down the event-loop and clean up internal resources when all active and queued requests are done. + * + * This method is only available when `RESTINCURL_ENABLE_ASYNC` is nonzero. + */ + void CloseWhenFinished() { + worker_->CloseWhenFinished(); + } + + /*! Close the client. + * + * This method aborts any and all requests. + * + * The worked-thread will exit shortly after this method is + * called, if it was running. + * + * This method is only available when `RESTINCURL_ENABLE_ASYNC` is nonzero. + */ + void Close() { + worker_->Close(); + } + + /*! Wait for the worker-thread to finish. + * + * You should call Close() first. + * + * This method is only available when `RESTINCURL_ENABLE_ASYNC` is nonzero. + */ + void WaitForFinish() { + worker_->Join(); + } + + /*! Check if the client instance has a worker-thread. + * + * This method is only available when `RESTINCURL_ENABLE_ASYNC` is nonzero. + */ + bool HaveWorker() const { + return worker_->HaveThread(); + } + + /*! Get the number of active / ongoing requests. + * + * This method is only available when `RESTINCURL_ENABLE_ASYNC` is nonzero. + */ + size_t GetNumActiveRequests() { + return worker_->GetNumActiveRequests(); + } +#endif + + private: +#if RESTINCURL_ENABLE_ASYNC + std::unique_ptr worker_ = std::make_unique(); +#endif + }; + + +} // namespace diff --git a/isis/src/base/objs/Spice/Spice.cpp b/isis/src/base/objs/Spice/Spice.cpp index 1e357c77d7..151c560210 100644 --- a/isis/src/base/objs/Spice/Spice.cpp +++ b/isis/src/base/objs/Spice/Spice.cpp @@ -35,6 +35,7 @@ using json = nlohmann::json; #include "SpacecraftPosition.h" #include "Target.h" #include "Blob.h" +#include "spiceql.h" using namespace std; @@ -498,7 +499,7 @@ namespace Isis { throw IException(IException::Io, msg, _FILEINFO_); } QString fileName = file.expanded(); - furnsh_c(fileName.toLatin1().data()); + SpiceQL::load(fileName.toLatin1().data()); m_kernels->push_back(key[i]); } diff --git a/isis/src/base/objs/SpicePosition/unitTest.cpp b/isis/src/base/objs/SpicePosition/unitTest.cpp index e9b3964fff..c16af94f3e 100644 --- a/isis/src/base/objs/SpicePosition/unitTest.cpp +++ b/isis/src/base/objs/SpicePosition/unitTest.cpp @@ -15,6 +15,7 @@ find files of those names at the top level of this repository. **/ #include "Preference.h" #include "SpicePosition.h" #include "Table.h" +#include "spiceql.h" using json = nlohmann::json; using namespace Isis; @@ -33,9 +34,9 @@ int main(int argc, char *argv[]) { QString moc(dir + "moc.bsp"); QString de(dir + "de405.bsp"); QString pck(dir + "pck00006.tpc"); - furnsh_c(moc.toLatin1().data()); - furnsh_c(de.toLatin1().data()); - furnsh_c(pck.toLatin1().data()); + SpiceQL::load(moc.toLatin1().data()); + SpiceQL::load(de.toLatin1().data()); + SpiceQL::load(pck.toLatin1().data()); double startTime = -69382819.0; double endTime = -69382512.0; diff --git a/isis/src/base/objs/Target/Target.cpp b/isis/src/base/objs/Target/Target.cpp index 32689d99d1..d41f665fa6 100644 --- a/isis/src/base/objs/Target/Target.cpp +++ b/isis/src/base/objs/Target/Target.cpp @@ -17,7 +17,7 @@ find files of those names at the top level of this repository. **/ #include "ShapeModelFactory.h" #include "SpecialPixel.h" #include "Spice.h" - +#include "spiceql.h" using namespace std; @@ -485,7 +485,7 @@ namespace Isis { QString kernName = kern.expanded(); if(!pckLoaded) { - furnsh_c(kernName.toLatin1().data()); + SpiceQL::load(kernName.toLatin1().data()); pckLoaded = true; } diff --git a/isis/src/base/objs/iTime/iTime.cpp b/isis/src/base/objs/iTime/iTime.cpp index 5a30402785..7524b66cb7 100644 --- a/isis/src/base/objs/iTime/iTime.cpp +++ b/isis/src/base/objs/iTime/iTime.cpp @@ -17,6 +17,7 @@ find files of those names at the top level of this repository. **/ #include "iTime.h" #include "SpecialPixel.h" #include "NaifStatus.h" +#include "spiceql.h" using namespace std; namespace Isis { @@ -488,7 +489,7 @@ namespace Isis { } NaifStatus::CheckErrors(); - furnsh_c(leapSecondName.toLatin1().data()); + SpiceQL::load(leapSecondName.toLatin1().data()); NaifStatus::CheckErrors(); p_lpInitialized = true; diff --git a/isis/src/chandrayaan1/apps/chan1m32isis/chan1m32isis.cpp b/isis/src/chandrayaan1/apps/chan1m32isis/chan1m32isis.cpp index 371fab97fa..76c3b77edf 100644 --- a/isis/src/chandrayaan1/apps/chan1m32isis/chan1m32isis.cpp +++ b/isis/src/chandrayaan1/apps/chan1m32isis/chan1m32isis.cpp @@ -30,6 +30,7 @@ find files of those names at the top level of this repository. **/ #include "ProcessImportPds.h" #include "Progress.h" #include "Pvl.h" +#include "RestfulSpice.h" #include "Table.h" #include "UserInterface.h" @@ -347,16 +348,6 @@ namespace Isis { // jigsaw, so use the clock counts to update these keywords. NaifStatus::CheckErrors(); - QString lsk = "$base/kernels/lsk/naif????.tls"; - FileName lskName(lsk); - lskName = lskName.highestVersion(); - furnsh_c(lskName.expanded().toLatin1().data()); - - QString sclk = "$chandrayaan1/kernels/sclk/aig_ch1_sclk_complete_biased_m1p???.tsc"; - FileName sclkName(sclk); - sclkName = sclkName.highestVersion(); - furnsh_c(sclkName.expanded().toLatin1().data()); - SpiceInt sclkCode = -86; // Remmoved when we found out the lable counts are not as correct as we need. We use the time @@ -393,15 +384,13 @@ namespace Isis { } inst.findKeyword("StartTime").setValue(firstEt.UTC()); - SpiceChar startClockString[100]; - sce2s_c (sclkCode, firstEt.Et(), 100, startClockString); - QString startClock(startClockString); + std::string startClockString = Isis::RestfulSpice::doubleEtToSclk(sclkCode, firstEt.Et(), "chandrayaan1"); + QString startClock = QString::fromStdString(startClockString); inst.findKeyword("SpacecraftClockStartCount").setValue(startClock); inst.findKeyword("StopTime").setValue(lastEt.UTC()); - SpiceChar stopClockString[100]; - sce2s_c (sclkCode, lastEt.Et(), 100, stopClockString); - QString stopClock(stopClockString); + std::string stopClockString= Isis::RestfulSpice::doubleEtToSclk(sclkCode, lastEt.Et(), "chandrayaan1"); + QString stopClock = QString::fromStdString(stopClockString); inst.findKeyword("SpacecraftClockStopCount").setValue(stopClock); } diff --git a/isis/src/control/apps/sumspice/SumFinder.cpp b/isis/src/control/apps/sumspice/SumFinder.cpp index a550e71e52..c9808946da 100644 --- a/isis/src/control/apps/sumspice/SumFinder.cpp +++ b/isis/src/control/apps/sumspice/SumFinder.cpp @@ -28,6 +28,7 @@ find files of those names at the top level of this repository. **/ #include "Kernels.h" #include "NaifStatus.h" #include "Progress.h" +#include "RestfulSpice.h" #include "History.h" #include "Application.h" @@ -511,13 +512,12 @@ namespace Isis { // Compute start SCLK if present on labels if ( origStartClock.size() > 0 ) { NaifStatus::CheckErrors(); - char newSCLK[256]; - sce2s_c(camera->naifSclkCode(), newStartClock.Et(), - sizeof(newSCLK), newSCLK); + std::string newSCLK = Isis::RestfulSpice::doubleEtToSclk(camera->naifSclkCode(), newStartClock.Et(), RestfulSpice::spiceql_mission_map[(camera->instrumentId()).toStdString()]); + NaifStatus::CheckErrors(); sumtStartClock.addValue(origStartClock[0], origStartClock.unit()); - origStartClock.setValue(QString(newSCLK), origStartClock.unit()); + origStartClock.setValue(QString::fromStdString(newSCLK), origStartClock.unit()); setKeyword(origStartClock, instGrp); setKeyword(sumtStartClock, sumtGrp); @@ -527,13 +527,11 @@ namespace Isis { // Compute end SCLK if present on labels if ( origStopClock.size() > 0 ) { NaifStatus::CheckErrors(); - char newSCLK[256]; - sce2s_c(camera->naifSclkCode(), newStopClock.Et(), - sizeof(newSCLK), newSCLK); + std::string newSCLK = Isis::RestfulSpice::doubleEtToSclk(camera->naifSclkCode(), newStopClock.Et(), RestfulSpice::spiceql_mission_map[(camera->instrumentId()).toStdString()]); NaifStatus::CheckErrors(); sumtStopClock.addValue(origStopClock[0], origStopClock.unit()); - origStopClock.setValue(QString(newSCLK), origStopClock.unit()); + origStopClock.setValue(QString::fromStdString(newSCLK), origStopClock.unit()); setKeyword(origStopClock, instGrp); setKeyword(sumtStopClock, sumtGrp); diff --git a/isis/src/galileo/apps/gllssical/gllssical.cpp b/isis/src/galileo/apps/gllssical/gllssical.cpp index b806cfd0dd..b2201da61a 100644 --- a/isis/src/galileo/apps/gllssical/gllssical.cpp +++ b/isis/src/galileo/apps/gllssical/gllssical.cpp @@ -10,6 +10,7 @@ find files of those names at the top level of this repository. **/ #include "Buffer.h" #include "Camera.h" #include "iTime.h" +#include "RestfulSpice.h" #include "SpecialPixel.h" #include "Spice.h" #include "TextFile.h" @@ -482,12 +483,9 @@ namespace Isis { Isis::FileName sclk(label->findGroup("Kernels",Pvl::Traverse)["SpacecraftClock"][0]); QString sclkName(sclk.expanded()); - NaifStatus::CheckErrors(); - furnsh_c(sclkName.toLatin1().data()); NaifStatus::CheckErrors(); - double obsStartTime; - scs2e_c(-77, startTime.toLatin1().data(), &obsStartTime); + double obsStartTime = Isis::RestfulSpice::strSclkToEt(-77, startTime.toStdString(), "galileo"); spicegll.setTime(obsStartTime); double sunv[3]; spicegll.sunPosition(sunv); diff --git a/isis/src/hayabusa/apps/amicacal/AmicaCalUtils.h b/isis/src/hayabusa/apps/amicacal/AmicaCalUtils.h index 325f5675fb..4f221015a3 100644 --- a/isis/src/hayabusa/apps/amicacal/AmicaCalUtils.h +++ b/isis/src/hayabusa/apps/amicacal/AmicaCalUtils.h @@ -24,6 +24,7 @@ find files of those names at the top level of this repository. **/ #include "IString.h" #include "Pvl.h" #include "PvlGroup.h" +#include "RestfulSpice.h" // OpenCV libraries #include @@ -41,58 +42,9 @@ using namespace std; namespace Isis { -/** - * @brief Load required NAIF kernels required for timing needs. - * - * This method maintains the loading of kernels for HAYABUSA timing and - * planetary body ephemerides to support time and relative positions of planet - * bodies. - */ - -static void loadNaifTiming() { - static bool naifLoaded = false; - if (!naifLoaded) { - -// Load the NAIF kernels to determine timing data - Isis::FileName leapseconds("$base/kernels/lsk/naif????.tls"); - leapseconds = leapseconds.highestVersion(); - Isis::FileName sclk("$hayabusa/kernels/sclk/hayabusa.tsc"); - Isis::FileName pck1("$hayabusa/kernels/tspk/de403s.bsp"); - Isis::FileName pck2("$hayabusa/kernels/tspk/sb_25143_140.bsp"); - Isis::FileName pck3("$hayabusa/kernels/spk/hay_jaxa_050916_051119_v1n.bsp"); - Isis::FileName pck4("$hayabusa/kernels/spk/hay_osbj_050911_051118_v1n.bsp"); - -// Load the kernels - QString leapsecondsName(leapseconds.expanded()); - QString sclkName(sclk.expanded()); - - QString pckName1(pck1.expanded()); - QString pckName2(pck2.expanded()); - QString pckName3(pck3.expanded()); - QString pckName4(pck4.expanded()); - - furnsh_c(leapsecondsName.toLatin1().data()); - furnsh_c(sclkName.toLatin1().data()); - - furnsh_c(pckName1.toLatin1().data()); - furnsh_c(pckName2.toLatin1().data()); - furnsh_c(pckName3.toLatin1().data()); - furnsh_c(pckName4.toLatin1().data()); - - -// Ensure it is loaded only once - naifLoaded = true; - } - return; -} - - /** * @brief Computes the distance from the Sun to the observed body. * - * This method requires the appropriate NAIK kernels to be loaded that - * provides instrument time support, leap seconds and planet body ephemeris. - * * @return @b double Distance in AU between Sun and observed body. */ static bool sunDistanceAU(Cube *iCube, @@ -107,28 +59,26 @@ static bool sunDistanceAU(Cube *iCube, sunDist = cam->sunToBodyDist() / KM_PER_AU; } catch(IException &e) { - try { - // Ensure NAIF kernels are loaded - loadNaifTiming(); - sunDist = 1.0; - - NaifStatus::CheckErrors(); - - // Determine if the target is a valid NAIF target - SpiceInt tcode; - SpiceBoolean found; - bodn2c_c(target.toLatin1().data(), &tcode, &found); + sunDist = 1.0; - if (!found) return false; + // Determine if the target is a valid NAIF target + try{ + Isis::RestfulSpice::translateNameToCode(target.toLatin1().data(), "amica"); + }catch(invalid_argument){ + return false; + } - // Convert starttime to et - double obsStartTime; - scs2e_c(-130, scStartTime.toLatin1().data(), &obsStartTime); + // Convert starttime to et + try{ + double obsStartTime = Isis::RestfulSpice::strSclkToEt(-130, scStartTime.toLatin1().data(), "amica"); // Get the vector from target to sun and determine its length double sunv[3]; - double lt; - spkpos_c(target.toLatin1().data(), obsStartTime, "J2000", "LT+S", "sun", sunv, <); + + std::vector etStart = {obsStartTime}; + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(etStart, target.toLatin1().data(), "sun", "J2000", "LT+S", "amica", "reconstructed", "reconstructed"); + std::copy(sunLt[0].begin(), sunLt[0].begin()+3, sunv); + NaifStatus::CheckErrors(); double sunkm = vnorm_c(sunv); @@ -140,9 +90,10 @@ static bool sunDistanceAU(Cube *iCube, QString message = "Failed to calculate the sun-target distance."; throw IException(e, IException::User, message, _FILEINFO_); } - } + } return true; + } diff --git a/isis/src/hayabusa/apps/amicacal/main.cpp b/isis/src/hayabusa/apps/amicacal/main.cpp index 0b235a623f..dd809b5095 100644 --- a/isis/src/hayabusa/apps/amicacal/main.cpp +++ b/isis/src/hayabusa/apps/amicacal/main.cpp @@ -671,8 +671,7 @@ QString loadCalibrationVariables(const QString &config, Cube *iCube) { } catch(IException &e) { try{ - loadNaifTiming(); // Ensure the proper kernels are loaded - scs2e_c(g_hayabusaNaifCode, g_startTime.toLatin1().data(), &obsStartTime); + obsStartTime = Isis::RestfulSpice::strSclkToEt(g_hayabusaNaifCode, g_startTime.toLatin1().data(), "amica"); } catch (IException &e) { QString message = "IOF option does not work with non-spiceinited cubes."; @@ -732,7 +731,6 @@ QString loadCalibrationVariables(const QString &config, Cube *iCube) { * @param out Radometrically corrected image */ void calibrate(vector& in, vector& out) { - Buffer& imageIn = *in[0]; Buffer& flatField = *in[1]; Buffer& imageOut = *out[0]; diff --git a/isis/src/lro/apps/lronaccal/lronaccal.cpp b/isis/src/lro/apps/lronaccal/lronaccal.cpp index 6f9bf917eb..b65b2ed1d3 100644 --- a/isis/src/lro/apps/lronaccal/lronaccal.cpp +++ b/isis/src/lro/apps/lronaccal/lronaccal.cpp @@ -16,6 +16,7 @@ find files of those names at the top level of this repository. **/ #include "Brick.h" #include "Table.h" #include "PvlGroup.h" +#include "RestfulSpice.h" #include "Statistics.h" #include "UserInterface.h" #include "lronaccal.h" @@ -318,28 +319,11 @@ namespace Isis { catch(IException &e) { // Failed to instantiate a camera, try furnishing kernels directly try { - - double etStart = startTime.Et(); - // Get the distance between the Moon and the Sun at the given time in - // Astronomical Units (AU) - QString bspKernel1 = p.MissionData("lro", "/kernels/tspk/moon_pa_de421_1900-2050.bpc", false); - QString bspKernel2 = p.MissionData("lro", "/kernels/tspk/de421.bsp", false); - furnsh_c(bspKernel1.toLatin1().data()); - furnsh_c(bspKernel2.toLatin1().data()); - QString pckKernel1 = p.MissionData("base", "/kernels/pck/pck?????.tpc", true); - QString pckKernel2 = p.MissionData("lro", "/kernels/pck/moon_080317.tf", false); - QString pckKernel3 = p.MissionData("lro", "/kernels/pck/moon_assoc_me.tf", false); - furnsh_c(pckKernel1.toLatin1().data()); - furnsh_c(pckKernel2.toLatin1().data()); - furnsh_c(pckKernel3.toLatin1().data()); - double sunpos[6], lt; - spkezr_c("sun", etStart, "MOON_ME", "LT+S", "MOON", sunpos, <); + std::vector etStart = {startTime.Et()}; + double sunpos[6]; + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(etStart, "sun", "MOON", "MOON_ME", "LT+S", "lroc", "reconstructed", "reconstructed"); + std::copy(sunLt[0].begin(), sunLt[0].begin()+6, sunpos); g_solarDistance = vnorm_c(sunpos) / KM_PER_AU; - unload_c(bspKernel1.toLatin1().data()); - unload_c(bspKernel2.toLatin1().data()); - unload_c(pckKernel1.toLatin1().data()); - unload_c(pckKernel2.toLatin1().data()); - unload_c(pckKernel3.toLatin1().data()); } catch(IException &e) { QString msg = "Unable to find the necessary SPICE kernels for converting to IOF"; @@ -492,10 +476,8 @@ namespace Isis { } TextFile file(filename.expanded()); QString lineString; - unsigned int line = 0; while(file.GetLine(lineString)) { data.push_back(toDouble(lineString.split(QRegExp("[ ,;]")).first())); - line++; } fileString = filename.original(); } diff --git a/isis/src/lro/apps/lrowaccal/lrowaccal.cpp b/isis/src/lro/apps/lrowaccal/lrowaccal.cpp index 5439cde1f9..bc0abcee4e 100644 --- a/isis/src/lro/apps/lrowaccal/lrowaccal.cpp +++ b/isis/src/lro/apps/lrowaccal/lrowaccal.cpp @@ -14,6 +14,7 @@ #include "Preference.h" #include "ProcessByBrick.h" #include "PvlGroup.h" +#include "RestfulSpice.h" #include "SpecialPixel.h" #include "Statistics.h" @@ -669,31 +670,13 @@ namespace Isis { double etStart = startTime.Et(); // Get the distance between the Moon and the Sun at the given time in // Astronomical Units (AU) - QString bspKernel1 = p.MissionData("lro", "/kernels/tspk/moon_pa_de421_1900-2050.bpc", false); - QString bspKernel2 = p.MissionData("lro", "/kernels/tspk/de421.bsp", false); - NaifStatus::CheckErrors(); - furnsh_c(bspKernel1.toLatin1().data()); - NaifStatus::CheckErrors(); - furnsh_c(bspKernel2.toLatin1().data()); - NaifStatus::CheckErrors(); - QString pckKernel1 = p.MissionData("base", "/kernels/pck/pck?????.tpc", true); - QString pckKernel2 = p.MissionData("lro", "/kernels/pck/moon_080317.tf", false); - QString pckKernel3 = p.MissionData("lro", "/kernels/pck/moon_assoc_me.tf", false); - NaifStatus::CheckErrors(); - furnsh_c(pckKernel1.toLatin1().data()); - NaifStatus::CheckErrors(); - furnsh_c(pckKernel2.toLatin1().data()); - NaifStatus::CheckErrors(); - furnsh_c(pckKernel3.toLatin1().data()); - NaifStatus::CheckErrors(); - double sunpos[6], lt; - spkezr_c("sun", etStart, "MOON_ME", "LT+S", "MOON", sunpos, <); + double sunpos[6]; + + std::vector etStartVec = {etStart}; + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(etStartVec, "sun", "moon", "MOON_ME", "LT+S", "lroc", "reconstructed", "reconstructed"); + std::copy(sunLt[0].begin(), sunLt[0].begin()+6, sunpos); + g_solarDistance = vnorm_c(sunpos) / KM_PER_AU; - unload_c(bspKernel1.toLatin1().data()); - unload_c(bspKernel2.toLatin1().data()); - unload_c(pckKernel1.toLatin1().data()); - unload_c(pckKernel2.toLatin1().data()); - unload_c(pckKernel3.toLatin1().data()); } catch (IException &e) { QString msg = "Can not find necessary SPICE kernels for converting to IOF"; diff --git a/isis/src/mer/apps/mical/main.cpp b/isis/src/mer/apps/mical/main.cpp index 1f9a8de608..b90e1530b7 100644 --- a/isis/src/mer/apps/mical/main.cpp +++ b/isis/src/mer/apps/mical/main.cpp @@ -9,14 +9,15 @@ find files of those names at the top level of this repository. **/ #define GUIHELPERS #include "Isis.h" +#include "Brick.h" +#include "Histogram.h" +#include "IException.h" +#include "iTime.h" +#include "MiCalibration.h" #include "ProcessByLine.h" #include "Pvl.h" +#include "RestfulSpice.h" #include "UserInterface.h" -#include "IException.h" -#include "MiCalibration.h" -#include "iTime.h" -#include "Brick.h" -#include "Histogram.h" #include #include @@ -92,23 +93,17 @@ void IsisMain() { } iTime startTime = gbl::mi->StartTime(); - double ETstartTime = startTime.Et(); //Get the distance between Mars and the Sun at the given time in // Astronomical Units (AU) - QString bspKernel = p.MissionData("base", "/kernels/spk/de???.bsp", true); - furnsh_c(bspKernel.toLatin1().data()); - QString satKernel = p.MissionData("base", "/kernels/spk/mar???.bsp", true); - furnsh_c(satKernel.toLatin1().data()); - QString pckKernel = p.MissionData("base", "/kernels/pck/pck?????.tpc", true); - furnsh_c(pckKernel.toLatin1().data()); - double sunpos[6], lt; - spkezr_c("sun", ETstartTime, "iau_mars", "LT+S", "mars", sunpos, <); + + double sunpos[6]; + std::vector etStart = {startTime.Et()}; + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(etStart, "mars", "sun", "iau_mars", "LT+S", "mer1", "reconstructed", "reconstructed"); + std::copy(sunLt[0].begin(), sunLt[0].begin()+6, sunpos); + double dist = vnorm_c(sunpos); double kmperAU = 1.4959787066E8; gbl::sunAU = dist / kmperAU; - unload_c(bspKernel.toLatin1().data()); - unload_c(satKernel.toLatin1().data()); - unload_c(pckKernel.toLatin1().data()); //See what calibtation values the user wants to apply diff --git a/isis/src/messenger/apps/mdiscal/MdisCalUtils.h b/isis/src/messenger/apps/mdiscal/MdisCalUtils.h index 1bdd05cb62..a8c65c15af 100644 --- a/isis/src/messenger/apps/mdiscal/MdisCalUtils.h +++ b/isis/src/messenger/apps/mdiscal/MdisCalUtils.h @@ -19,6 +19,7 @@ find files of those names at the top level of this repository. **/ #include "IString.h" #include "iTime.h" #include "NaifStatus.h" +#include "RestfulSpice.h" #include "Spice.h" /** @@ -51,41 +52,6 @@ namespace Isis { return (QString('"' + value + '"')); } - /** - * @brief Load required NAIF kernels required for timing needs - * - * This method maintains the loading of kernels for MESSENGER timing and - * planetary body ephemerides to support time and relative positions of planet - * bodies. - */ - static void loadNaifTiming() { - static bool naifLoaded = false; - if (!naifLoaded) { - // Load the NAIF kernels to determine timing data - Isis::FileName leapseconds("$base/kernels/lsk/naif????.tls"); - leapseconds = leapseconds.highestVersion(); - - Isis::FileName sclk("$messenger/kernels/sclk/messenger_????.tsc"); - sclk = sclk.highestVersion(); - - Isis::FileName pck("$base/kernels/spk/de???.bsp"); - pck = pck.highestVersion(); - - // Load the kernels - QString leapsecondsName(leapseconds.expanded()); - QString sclkName(sclk.expanded()); - QString pckName(pck.expanded()); - furnsh_c(leapsecondsName.toLatin1().data()); - furnsh_c(sclkName.toLatin1().data()); - furnsh_c(pckName.toLatin1().data()); - - // Ensure it is loaded only once - naifLoaded = true; - } - return; - } - - /** * @brief Computes the distance from the Sun to the observed body * @@ -108,25 +74,25 @@ namespace Isis { try { // Ensure NAIF kernels are loaded NaifStatus::CheckErrors(); - loadNaifTiming(); sunDist = 1.0; // Determine if the target is a valid NAIF target - SpiceInt tcode; - SpiceBoolean found; - bodn2c_c(target.toLatin1().data(), &tcode, &found); - if (!found) return (false); + try{ + Isis::RestfulSpice::translateNameToCode(target.toLatin1().data(), "mdis"); + }catch(std::invalid_argument){ + return false; + } + // Convert starttime to et - double obsStartTime; - scs2e_c(-236, scStartTime.toLatin1().data(), &obsStartTime); - NaifStatus::CheckErrors(); + double obsStartTime = Isis::RestfulSpice::strSclkToEt(-236, scStartTime.toLatin1().data(), "mdis"); // Get the vector from target to sun and determine its length double sunv[3]; - double lt; - spkpos_c(target.toLatin1().data(), obsStartTime, "J2000", "LT+S", "sun", - sunv, <); + std::vector etStart = {obsStartTime}; + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(etStart, target.toLatin1().data(), "sun", "J2000", "LT+S", "mdis", "reconstructed", "reconstructed"); + std::copy(sunLt[0].begin(), sunLt[0].begin()+3, sunv); + double sunkm = vnorm_c(sunv); // Return in AU units @@ -403,12 +369,9 @@ namespace Isis { try { // Ensure NAIF kernels are loaded for NAIF time computations NaifStatus::CheckErrors(); - loadNaifTiming(); // Convert s/c clock start time to et - scs2e_c(-236, scStartTime.toLatin1().data(), &obsStartTime); - NaifStatus::CheckErrors(); - + obsStartTime = Isis::RestfulSpice::strSclkToEt(-236, scStartTime.toLatin1().data(), "mdis"); } catch (IException &e) { QString message = "Could not convert spacecraft clock start count to ET."; diff --git a/isis/src/messenger/apps/mdisedrinfo/SpiceManager.cpp b/isis/src/messenger/apps/mdisedrinfo/SpiceManager.cpp index b2e4446326..2f18927af0 100644 --- a/isis/src/messenger/apps/mdisedrinfo/SpiceManager.cpp +++ b/isis/src/messenger/apps/mdisedrinfo/SpiceManager.cpp @@ -21,6 +21,7 @@ find files of those names at the top level of this repository. **/ #include "Pvl.h" #include "SpiceManager.h" #include "NaifStatus.h" +#include "spiceql.h" using namespace std; @@ -216,7 +217,7 @@ namespace Isis { throw IException(IException::Io, msg, _FILEINFO_); } QString fileName(file.expanded()); - if(_furnish) furnsh_c(fileName.toLatin1().data()); + if(_furnish) SpiceQL::load(fileName.toLatin1().data()); addKernelName((QString)key[i]); } NaifStatus::CheckErrors(); diff --git a/isis/src/mgs/apps/moccal/moccal.cpp b/isis/src/mgs/apps/moccal/moccal.cpp index 49838cfe3b..3cc651ef43 100644 --- a/isis/src/mgs/apps/moccal/moccal.cpp +++ b/isis/src/mgs/apps/moccal/moccal.cpp @@ -6,15 +6,16 @@ find files of those names at the top level of this repository. **/ /* SPDX-License-Identifier: CC0-1.0 */ -#include "ProcessByLine.h" -#include "SpecialPixel.h" -#include "MocLabels.h" -#include "iTime.h" +#include "Camera.h" #include "IException.h" -#include "TextFile.h" +#include "iTime.h" #include "LineManager.h" +#include "MocLabels.h" #include "NaifStatus.h" -#include "Camera.h" +#include "ProcessByLine.h" +#include "RestfulSpice.h" +#include "SpecialPixel.h" +#include "TextFile.h" #include "moccal.h" @@ -140,24 +141,16 @@ namespace Isis { // Astronomical Units (AU) NaifStatus::CheckErrors(); - QString bspKernel = p.MissionData("base", "/kernels/spk/de???.bsp", true); - furnsh_c(bspKernel.toLatin1().data()); - QString satKernel = p.MissionData("base", "/kernels/spk/mar???.bsp", true); - furnsh_c(satKernel.toLatin1().data()); - QString pckKernel = p.MissionData("base", "/kernels/pck/pck?????.tpc", true); - furnsh_c(pckKernel.toLatin1().data()); - NaifStatus::CheckErrors(); - double sunpos[6], lt; - spkezr_c("sun", etStart, "iau_mars", "LT+S", "mars", sunpos, <); + double sunpos[6]; + std::vector etStartVec = {etStart}; + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(etStartVec, "mars", "sun", "iau_mars", "LT+S", "mgs", "reconstructed", "reconstructed"); + std::copy(sunLt[0].begin(), sunLt[0].begin()+6, sunpos); + double dist = vnorm_c(sunpos); sunAU = dist / kmPerAU; NaifStatus::CheckErrors(); - unload_c(bspKernel.toLatin1().data()); - unload_c(satKernel.toLatin1().data()); - unload_c(pckKernel.toLatin1().data()); - NaifStatus::CheckErrors(); } // See if the user wants counts/ms or i/f but if w0 is 0 then diff --git a/isis/src/mgs/objs/MocLabels/MocLabels.cpp b/isis/src/mgs/objs/MocLabels/MocLabels.cpp index e07862ddea..93962b0b45 100644 --- a/isis/src/mgs/objs/MocLabels/MocLabels.cpp +++ b/isis/src/mgs/objs/MocLabels/MocLabels.cpp @@ -20,6 +20,7 @@ find files of those names at the top level of this repository. **/ #include "IString.h" #include "iTime.h" #include "mocxtrack.h" +#include "RestfulSpice.h" #include "TextFile.h" #include "AlphaCube.h" @@ -225,19 +226,9 @@ namespace Isis { // Initialize the maps from sample coordinate to detector coordinates InitDetectorMaps(); - // Temporarily load some naif kernels - QString lsk = p_lsk.expanded(); - QString sclk = p_sclk.expanded(); - furnsh_c(lsk.toLatin1().data()); - furnsh_c(sclk.toLatin1().data()); - - // Compute the starting ephemeris time - scs2e_c(-94, p_clockCount.toLatin1().data(), &p_etStart); + p_etStart = Isis::RestfulSpice::strSclkToEt(-94, p_clockCount.toLatin1().data(), "mgs"); p_etEnd = EphemerisTime((double)p_nl); - // Unload the naif kernels - unload_c(lsk.toLatin1().data()); - unload_c(sclk.toLatin1().data()); } /** @@ -427,12 +418,6 @@ namespace Isis { if(!firstTime) return; firstTime = false; - // Load naif kernels - QString lskKern = p_lsk.expanded(); - QString sclkKern = p_sclk.expanded(); - furnsh_c(lskKern.toLatin1().data()); - furnsh_c(sclkKern.toLatin1().data()); - //Set up file for reading FileName wagoFile("$mgs/calibration/MGSC_????_wago.tab"); wagoFile = wagoFile.highestVersion(); @@ -473,8 +458,7 @@ namespace Isis { sclk = currentSclk; sclk.Remove("\""); sclk.Trim(" "); - double et; - scs2e_c(-94, currentSclk.c_str(), &et); + double et = Isis::RestfulSpice::strSclkToEt(-94, currentSclk, "mgs"); //Compare time against given parameters, if it fits, process if(et < p_etEnd && et > p_etStart) { @@ -500,6 +484,7 @@ namespace Isis { } sclk = currentSclk; sclk.Trim(" "); + et = Isis::RestfulSpice::strSclkToEt(-94, currentSclk, "mgs"); scs2e_c(-94, currentSclk.c_str(), &et); bottom = linenum; @@ -522,7 +507,7 @@ namespace Isis { } sclk = currentSclk; sclk.Trim(" "); - scs2e_c(-94, currentSclk.c_str(), &et); + et = Isis::RestfulSpice::strSclkToEt(-94, currentSclk, "mgs"); top = linenum; } //Now, go from the upper limit to the lower limit, and grab all lines @@ -550,7 +535,7 @@ namespace Isis { sclk.Remove("\""); sclk.Trim(" "); - scs2e_c(-94, sclk.c_str(), &et); + et = Isis::RestfulSpice::strSclkToEt(-94, currentSclk, "mgs"); // Get the gain mode id gainId = line.Token(",").ToQt().remove("\"").trimmed(); @@ -566,8 +551,6 @@ namespace Isis { p = p_gainMapWA.find(gainId); if(p == p_gainMapWA.end()) { // Unload the naif kernels - unload_c(lskKern.toLatin1().data()); - unload_c(sclkKern.toLatin1().data()); QString msg = "Invalid GainModeId [" + gainId + "] in wago table"; throw IException(IException::Unknown, msg, _FILEINFO_); @@ -601,8 +584,5 @@ namespace Isis { sort(p_wagos.begin(), p_wagos.end()); boost::ignore_unused((unique(p_wagos.begin(), p_wagos.end()))); - // Unload the naif kernels - unload_c(lskKern.toLatin1().data()); - unload_c(sclkKern.toLatin1().data()); } } diff --git a/isis/src/mro/apps/ctxcal/ctxcal.cpp b/isis/src/mro/apps/ctxcal/ctxcal.cpp index 6969f691cc..838248b7dc 100644 --- a/isis/src/mro/apps/ctxcal/ctxcal.cpp +++ b/isis/src/mro/apps/ctxcal/ctxcal.cpp @@ -13,6 +13,7 @@ find files of those names at the top level of this repository. **/ #include "TextFile.h" #include "LineManager.h" #include "Brick.h" +#include "RestfulSpice.h" #include "Table.h" #include "UserInterface.h" #include "Camera.h" @@ -166,28 +167,16 @@ namespace Isis { catch(IException &e) { // Get the distance between Mars and the Sun at the given time in // Astronomical Units (AU) - QString bspKernel = p.MissionData("base", "/kernels/spk/de???.bsp", true); NaifStatus::CheckErrors(); - furnsh_c(bspKernel.toLatin1().data()); - NaifStatus::CheckErrors(); - QString satKernel = p.MissionData("base", "/kernels/spk/mar???.bsp", true); - furnsh_c(satKernel.toLatin1().data()); - NaifStatus::CheckErrors(); - QString pckKernel = p.MissionData("base", "/kernels/pck/pck?????.tpc", true); - furnsh_c(pckKernel.toLatin1().data()); - NaifStatus::CheckErrors(); - double sunpos[6], lt; + double sunpos[6]; - spkezr_c("sun", etStart, "iau_mars", "LT+S", "mars", sunpos, <); - NaifStatus::CheckErrors(); + std::vector etStartVec = {etStart}; + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(etStartVec, "mars", "sun", "iau_mars", "LT+S", "mro", "reconstructed", "reconstructed"); + std::copy(sunLt[0].begin(), sunLt[0].begin()+6, sunpos); dist1 = vnorm_c(sunpos); NaifStatus::CheckErrors(); - unload_c(bspKernel.toLatin1().data()); - unload_c(satKernel.toLatin1().data()); - unload_c(pckKernel.toLatin1().data()); - NaifStatus::CheckErrors(); } double dist = 2.07E8; diff --git a/isis/src/mro/apps/hicrop/hicrop.cpp b/isis/src/mro/apps/hicrop/hicrop.cpp index 6241d4b8f1..8fef2e6676 100644 --- a/isis/src/mro/apps/hicrop/hicrop.cpp +++ b/isis/src/mro/apps/hicrop/hicrop.cpp @@ -22,6 +22,8 @@ find files of those names at the top level of this repository. **/ #include "LineManager.h" #include "NaifStatus.h" #include "ProcessByLine.h" +#include "RestfulSpice.h" +#include "spiceql.h" #include "Table.h" #include "TextFile.h" @@ -42,12 +44,9 @@ namespace Isis { double unbinnedRate, double binMode); iTime labelClockCountTime(iTime actualCalculatedTime, double tdiMode, double unbinnedRate, double binMode); - pair ckBeginEndTimes(IString ckFileName); // methods for converting between lines/times/clock counts iTime line2time(double lineNumber, double lineRate, double originalStartEt); double et2line(double et, double lineRate, double originalStartEt); - QString time2clock(iTime time); - iTime clock2time(QString spacecraftClockCount); // method to validate calculated or user-entered cropped line and time values void validateCropLines(); void validateCropTimes(double cropStart, double cropStop, @@ -74,45 +73,29 @@ namespace Isis { } void hicrop(Cube *cube, UserInterface &ui, Pvl *log) { - // Isis::Preference::Preferences(true); // delete ??? - cout << setprecision(25);// ??? - g_cube = cube; QString inputFileName = g_cube->fileName(); // get user inputs for input cube and open try { - // read kernel files and furnish these kernels for naif routines used in // originalStartTime() and time2clock() IString ckFileName = ui.GetFileName("CK"); - IString lskFileName = ""; + SpiceQL::load(FileName(QString::fromStdString(ckFileName)).expanded().toLatin1().data()); + if (ui.WasEntered("LSK")) { - lskFileName = ui.GetFileName("LSK"); - } - else { - FileName lskFile("$base/kernels/lsk/naif????.tls"); - lskFileName = lskFile.highestVersion().expanded(); + IString lskFileName = ui.GetFileName("LSK"); + SpiceQL::load(FileName(QString::fromStdString(lskFileName)).expanded().toLatin1().data()); + }else{ } - IString sclkFileName = ""; if (ui.WasEntered("SCLK")) { - sclkFileName = ui.GetFileName("SCLK"); - } - else { - FileName sclkFile("$mro/kernels/sclk/MRO_SCLKSCET.?????.65536.tsc"); - sclkFileName = sclkFile.highestVersion().expanded(); + IString sclkFileName = ui.GetFileName("SCLK"); + SpiceQL::load(FileName(QString::fromStdString(sclkFileName)).expanded().toLatin1().data()); + }else{ + // kPool.loadClockKernels(); } - // furnish these kernels - NaifStatus::CheckErrors(); - furnsh_c(ckFileName.c_str()); - NaifStatus::CheckErrors(); - furnsh_c(sclkFileName.c_str()); - NaifStatus::CheckErrors(); - furnsh_c(lskFileName.c_str()); - NaifStatus::CheckErrors(); - // get values from the labels needed to compute the line rate and the // actual start time of the input cube Pvl &inLabels = *g_cube->label(); @@ -135,12 +118,13 @@ namespace Isis { // get the actual original start time by making adjustments to the // spacecraft clock start count in the labels - iTime timeFromLabelClockCount = clock2time(labelStartClockCount); + iTime timeFromLabelClockCount = Isis::RestfulSpice::strSclkToEt(-74999, labelStartClockCount.toLatin1().data(), "hirise"); iTime originalStart = actualTime(timeFromLabelClockCount, tdiMode, unbinnedRate, binMode); double originalStartEt = originalStart.Et(); - pair ckCoverage = ckBeginEndTimes(ckFileName); + + pair ckCoverage = SpiceQL::getTimeIntervals(ckFileName)[0]; // find the values of the first and last lines to be kept from user inputs if (ui.GetString("SOURCE") == "LINEVALUES") { g_cropStartLine = ui.GetInteger("LINE"); @@ -263,55 +247,21 @@ namespace Isis { ckCoverage.first, ckCoverage.second); // HiRise spacecraft clock format is P/SSSSSSSSSS:FFFFF - IString actualCropStartClockCount = time2clock(cropStartTime);//??? - IString actualCropStopClockCount = time2clock(cropStopTime); //??? - - //??? - // UTC - // cout << "labelStartClock2time = " << timeFromLabelClockCount.UTC() << endl; - // cout << "adjustedStartClock2time = " << originalStart.UTC() << endl; - // cout << "cropped starttime = " << cropStartTime.UTC() << endl; - // cout << "cropped stoptime = " << cropStopTime.UTC() << endl; - // cout << "time at 80000.5 = " << line2time(80000.5, lineRate, originalStartEt).UTC() << endl << endl; - // - // // ET - // cout << "labelStartClockEt = " << timeFromLabelClockCount.Et() << endl;// should this be - // cout << "adjustedStartClockEt = " << originalStartEt << endl;// should this be - // cout << "cropped starttime = " << cropStartTime.Et() << endl;//??? 264289109.970381856 - // cout << "cropped stoptime = " << cropStopTime.Et() << endl;//??? 264289117.285806835 - // cout << "time at 80000.5 = " << line2time(80000.5, lineRate, originalStartEt).Et() << endl << endl; + IString actualCropStartClockCount = Isis::RestfulSpice::doubleEtToSclk(-74999, cropStartTime.Et(), "hirise"); + IString actualCropStopClockCount = Isis::RestfulSpice::doubleEtToSclk(-74999, cropStopTime.Et(), "hirise"); // readjust the time to get the appropriate label value for the // spacecraft clock start count for the labels of the cropped cube iTime adjustedCropStartTime = labelClockCountTime(cropStartTime, tdiMode, unbinnedRate, binMode); - QString adjustedCropStartClockCount = time2clock(adjustedCropStartTime); + QString adjustedCropStartClockCount = QString::fromStdString(Isis::RestfulSpice::doubleEtToSclk(-74999, adjustedCropStartTime.Et(), "hirise")); iTime adjustedCropStopTime = labelClockCountTime(cropStopTime, tdiMode, unbinnedRate, binMode); - QString adjustedCropStopClockCount = time2clock(adjustedCropStopTime); + QString adjustedCropStopClockCount = QString::fromStdString(Isis::RestfulSpice::doubleEtToSclk(-74999, adjustedCropStopTime.Et(), "hirise")); - //??? string stopClockCount = inputInst["SpacecraftClockStopCount"]; - //??? iTime labelStopTime = clock2time(stopClockCount); - //??? iTime origStop = actualTime(labelStopTime, tdiMode, - //??? unbinnedRate, binMode); - //??? double endline = et2line(origStop.Et(), lineRate, originalStartEt); - //??? cout << std::setprecision(20); - //??? cout << "Label Stop Count = " << stopClockCount << endl; - //??? cout << "Stop Count Time = " << labelStopTime.Et() << endl; - //??? cout << "Actual Stop Count = " << origStop.Et() << endl; - //??? cout << "Actual End Line = " << endline << endl; - //??? - //??? cout << endl << "New End Line = " << g_cropEndLine << endl; - //??? cout << "New End Line = " << et2line(cropStopTime.Et(), lineRate, originalStartEt) << endl; - //??? cout << "New Stop Time = " << cropStopTime.Et() << endl; - //??? cout << "Actual Stop Count = " << actualCropStopClockCount << endl; - //??? cout << "Label Stop Time = " << adjustedCropStopTime.Et() << endl; - //??? cout << "Label Stop Count = " << adjustedCropStopClockCount << endl; - - // Allocate the output file and make sure things get propogated nicely ProcessByLine p; p.SetInputCube(g_cube); @@ -375,12 +325,6 @@ namespace Isis { log->addLogGroup(results); } - // Unfurnishes kernel files to prevent file table overflow - NaifStatus::CheckErrors(); - unload_c(ckFileName.c_str()); - unload_c(sclkFileName.c_str()); - unload_c(lskFileName.c_str()); - NaifStatus::CheckErrors(); } catch (IException &e) { IString msg = "Unable to crop the given cube [" + inputFileName @@ -398,7 +342,6 @@ namespace Isis { void crop(Buffer &out) { // Read the input line int iline = g_cropStartLine + (out.Line() - 1); - int band = 1; g_in->SetLine(iline, 1); g_cube->read(*g_in); @@ -406,8 +349,6 @@ namespace Isis { for (int i = 0; i < out.size(); i++) { out[i] = (*g_in)[i]; } - - if (out.Line() == g_cropLineCount) band++; } /** @@ -472,67 +413,6 @@ namespace Isis { return labelStartTime; } - /** - * Returns the first and last times that are covered by the given CK file. The - * SCLK and LSK files must be furnished before this method is called. - * - * @param ckFileName String containing the name of the ck file provided by the - * user. - * @return A pair of doubles, the first is the earliest time covered by the CK - * file and the second is the latest time covered by the CK file. - */ - pair ckBeginEndTimes(IString ckFileName) { - //create a spice cell capable of containing all the objects in the kernel. - NaifStatus::CheckErrors(); - SPICEINT_CELL(currCell, 1000); - NaifStatus::CheckErrors(); - //this resizing is done because otherwise a spice cell will append new data - //to the last "currCell" - ssize_c(0, &currCell); - NaifStatus::CheckErrors(); - ssize_c(1000, &currCell); - NaifStatus::CheckErrors(); - ckobj_c(ckFileName.c_str(), &currCell); - NaifStatus::CheckErrors(); - int numberOfBodies = card_c(&currCell); - if (numberOfBodies != 1) { - IString msg = "Unable to find start and stop times using the given CK " - "file [" + ckFileName + "]. This application only works with" - "single body CK files."; - throw IException(IException::Unknown, msg, _FILEINFO_); - } - - //get the NAIF body code - int body = SPICE_CELL_ELEM_I(&currCell, numberOfBodies-1); - NaifStatus::CheckErrors(); - // 200,000 is the max coverage window size for a CK kernel - SPICEDOUBLE_CELL(cover, 200000); - NaifStatus::CheckErrors(); - ssize_c(0, &cover); - NaifStatus::CheckErrors(); - ssize_c(200000, &cover); - NaifStatus::CheckErrors(); - ckcov_c(ckFileName.c_str(), body, SPICEFALSE, "SEGMENT", 0.0, "TDB", &cover); - NaifStatus::CheckErrors(); - //Get the number of intervals in the object. - int numberOfIntervals = card_c(&cover) / 2; - NaifStatus::CheckErrors(); - if (numberOfIntervals != 1) { - IString msg = "Unable to find start and stop times using the given CK " - "file [" + ckFileName + "]. This application only works with " - "single interval CK files."; - throw IException(IException::Unknown, msg, _FILEINFO_); - } - //Convert the coverage interval start and stop times to TDB - //Get the endpoints of the interval. - double begin, end; - wnfetd_c(&cover, numberOfIntervals-1, &begin, &end); - NaifStatus::CheckErrors(); - QVariant startTime = begin;//??? why use variants? why not just use begin and end ??? - QVariant stopTime = end; //??? why use variants? why not just use begin and end ??? - pair< double, double > coverage(startTime.toDouble(), stopTime.toDouble()); - return coverage; - } /** * Returns the corresponding time for the given line number. @@ -580,49 +460,6 @@ namespace Isis { return (et - originalStartEt) / lineRate + 0.5; } - /** - * Returns the corresponding clock count, in string format, for the given time. - * - * HiRise is high precision, so the spacecraft clock format is - * P/SSSSSSSSSS:FFFFF and the clock id = -74999. (See any mro sclk file - * for documentation on these values.) - * - * @param time The time of the image to be converted. - * - * @return A string containing the spacecraft clock count corresponding - * to the given time. - */ - QString time2clock(iTime time) { - // char - char stringOutput[19]; - double et = time.Et(); - NaifStatus::CheckErrors(); - sce2s_c(-74999, et, 19, stringOutput); - NaifStatus::CheckErrors(); - return stringOutput; - } - - /** - * Returns the corresponding time for the given spacecraft clock count. - * - * @param spacecraftClockCount The clock count of the image to be converted. - * - * @return The image time corresponding to the given spacecraft clock count. - * - * @see Spice::getClockTime(clockCountString, sclkCode) - */ - iTime clock2time(QString spacecraftClockCount) { - // Convert the spacecraft clock count to ephemeris time - SpiceDouble timeOutput; - // The -74999 is the code to select the transformation from - // high-precision MRO SCLK to ET - NaifStatus::CheckErrors(); - scs2e_c(-74999, spacecraftClockCount.toLatin1().data(), &timeOutput); - NaifStatus::CheckErrors(); - QVariant clockTime = timeOutput; - iTime time = clockTime.toDouble(); - return time; - } /** diff --git a/isis/src/mro/apps/hijitreg/HiJitCube.cpp b/isis/src/mro/apps/hijitreg/HiJitCube.cpp index 731ca10a4e..455a6f954e 100644 --- a/isis/src/mro/apps/hijitreg/HiJitCube.cpp +++ b/isis/src/mro/apps/hijitreg/HiJitCube.cpp @@ -21,6 +21,7 @@ find files of those names at the top level of this repository. **/ #include "Pvl.h" #include "PvlGroup.h" #include "NaifStatus.h" +#include "RestfulSpice.h" using namespace UA::HiRISE; using std::endl; @@ -137,30 +138,6 @@ namespace Isis { } - void HiJitCube::loadNaifTiming() { - if(!naifLoaded) { -// Load the NAIF kernels to determine timing data - Isis::FileName leapseconds("$base/kernels/lsk/naif????.tls"); - leapseconds = leapseconds.highestVersion(); - - Isis::FileName sclk("$mro/kernels/sclk/MRO_SCLKSCET.?????.65536.tsc"); - sclk = sclk.highestVersion(); - -// Load the kernels - QString lsk = leapseconds.expanded(); - QString sClock = sclk.expanded(); - NaifStatus::CheckErrors(); - furnsh_c(lsk.toLatin1().data()); - NaifStatus::CheckErrors(); - furnsh_c(sClock.toLatin1().data()); - NaifStatus::CheckErrors(); - -// Ensure it is loaded only once - naifLoaded = true; - } - return; - } - void HiJitCube::computeStartTime() { // Compute the unbinned and binned linerates in seconds @@ -180,11 +157,8 @@ namespace Isis { jdata.obsStartTime = cam->time().Et(); } catch (IException &e) { try { - loadNaifTiming(); QString scStartTimeString = jdata.scStartTime; - NaifStatus::CheckErrors(); - scs2e_c(-74999, scStartTimeString.toLatin1().data(), &jdata.obsStartTime); - NaifStatus::CheckErrors(); + jdata.obsStartTime = Isis::RestfulSpice::strSclkToEt(-74999, scStartTimeString.toLatin1().data(), "hirise"); } catch (IException &e) { QString message = "Start time of the image can not be determined."; throw IException(e, IException::User, message, _FILEINFO_); diff --git a/isis/src/mro/objs/HiCal/HiCalConf.cpp b/isis/src/mro/objs/HiCal/HiCalConf.cpp index bdb1b926fe..8eeea7157b 100644 --- a/isis/src/mro/objs/HiCal/HiCalConf.cpp +++ b/isis/src/mro/objs/HiCal/HiCalConf.cpp @@ -24,6 +24,7 @@ find files of those names at the top level of this repository. **/ #include "IString.h" #include "IException.h" #include "Pvl.h" +#include "RestfulSpice.h" #include "SpecialPixel.h" #include "NaifStatus.h" @@ -307,24 +308,23 @@ bool HiCalConf::_naifLoaded = false; } catch (IException &e) { try { - loadNaifTiming(); - QString scStartTime = getKey("SpacecraftClockStartCount", "Instrument"); - double obsStartTime; NaifStatus::CheckErrors(); - scs2e_c (-74999,scStartTime.toLatin1().data(),&obsStartTime); + double obsStartTime = Isis::RestfulSpice::strSclkToEt(-74999, scStartTime.toLatin1().data(), "hirise"); QString targetName = getKey("TargetName", "Instrument"); if (targetName.toLower() == "sky" || targetName.toLower() == "cal" || targetName.toLower() == "phobos" || targetName.toLower() == "deimos") { - targetName = "Mars"; + targetName = "mars"; } double sunv[3]; - double lt; - (void) spkpos_c(targetName.toLatin1().data(), obsStartTime, "J2000", "LT+S", "sun", - sunv, <); + + std::vector etStart = {obsStartTime}; + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(etStart, targetName.toLower().toLatin1().data(), "sun", "J2000", "LT+S", "hirise", "reconstructed", "reconstructed"); + std::copy(sunLt[0].begin(), sunLt[0].begin()+3, sunv); + sunkm = vnorm_c(sunv); NaifStatus::CheckErrors(); @@ -403,45 +403,6 @@ bool HiCalConf::_naifLoaded = false; return (slist); } -/** - * @brief Load required NAIF kernels required for timing needs - * - * This method maintains the loading of kernels for HiRISE timing and planetary - * body ephemerides to support time and relative positions of planet bodies. - */ -void HiCalConf::loadNaifTiming( ) { - NaifStatus::CheckErrors(); - if (!_naifLoaded) { -// Load the NAIF kernels to determine timing data - Isis::FileName leapseconds("$base/kernels/lsk/naif????.tls"); - leapseconds = leapseconds.highestVersion(); - - Isis::FileName sclk("$mro/kernels/sclk/MRO_SCLKSCET.?????.65536.tsc"); - sclk = sclk.highestVersion(); - - Isis::FileName pck("$base/kernels/spk/de???.bsp"); - pck = pck.highestVersion(); - - Isis::FileName sat("$base/kernels/spk/mar???.bsp"); - sat = sat.highestVersion(); - -// Load the kernels - QString lsk = leapseconds.expanded(); - QString sClock = sclk.expanded(); - QString pConstants = pck.expanded(); - QString satConstants = sat.expanded(); - furnsh_c(lsk.toLatin1().data()); - furnsh_c(sClock.toLatin1().data()); - furnsh_c(pConstants.toLatin1().data()); - furnsh_c(satConstants.toLatin1().data()); - NaifStatus::CheckErrors(); - -// Ensure it is loaded only once - _naifLoaded = true; - } - return; -} - /** * @brief Intialization of object variables */ diff --git a/isis/src/newhorizons/apps/mvic2isis/mvic2isis.cpp b/isis/src/newhorizons/apps/mvic2isis/mvic2isis.cpp index bb1d8739de..914b1ad70a 100644 --- a/isis/src/newhorizons/apps/mvic2isis/mvic2isis.cpp +++ b/isis/src/newhorizons/apps/mvic2isis/mvic2isis.cpp @@ -25,6 +25,7 @@ find files of those names at the top level of this repository. **/ #include "Pvl.h" #include "PvlGroup.h" #include "PvlKeyword.h" +#include "RestfulSpice.h" #include "UserInterface.h" using namespace std; @@ -236,17 +237,6 @@ namespace Isis { // Create StartTime (UTC) from the SpacecraftClockStartCount. Need to load the leapsecond // and spacecraft clock kernels to calculate time. NaifStatus::CheckErrors(); - // Leapsecond kernel - QString lsk = "$ISISDATA/base/kernels/lsk/naif????.tls"; - FileName lskName(lsk); - lskName = lskName.highestVersion(); - furnsh_c(lskName.expanded().toLatin1().data()); - - // Spacecraft clock kernel - QString sclk = "$ISISDATA/newhorizons/kernels/sclk/new_horizons_???.tsc"; - FileName sclkName(sclk); - sclkName = sclkName.highestVersion(); - furnsh_c(sclkName.expanded().toLatin1().data()); SpiceInt sclkCode; if (fitslabel.hasKeyword("SPCSCID", Pvl::Traverse)) { @@ -258,11 +248,9 @@ namespace Isis { } QString scTime = inst["SpacecraftClockStartCount"]; - double et; - scs2e_c(sclkCode, scTime.toLatin1().data(), &et); - SpiceChar utc[30]; - et2utc_c(et, "ISOC", 3, 30, utc); - inst.addKeyword(PvlKeyword("StartTime", QString(utc))); + double et = Isis::RestfulSpice::strSclkToEt(sclkCode, scTime.toLatin1().data(), "mvic"); + std::string utc = Isis::RestfulSpice::etToUtc(et, "ISOC", 3); + inst.addKeyword(PvlKeyword("StartTime", QString::fromStdString(utc))); // Create a Band Bin group FileName bandTransFile(transDir + "NewHorizonsMvicBandBin_fit.trn"); diff --git a/isis/src/rosetta/apps/rosvirtis2isis/main.cpp b/isis/src/rosetta/apps/rosvirtis2isis/main.cpp index 34e866112a..c75933d7f0 100644 --- a/isis/src/rosetta/apps/rosvirtis2isis/main.cpp +++ b/isis/src/rosetta/apps/rosvirtis2isis/main.cpp @@ -21,13 +21,14 @@ find files of those names at the top level of this repository. **/ #include "FileName.h" #include "ImportPdsTable.h" +#include "iTime.h" #include "LineManager.h" +#include "PolynomialUnivariate.h" #include "ProcessImportPds.h" +#include "RestfulSpice.h" #include "Table.h" #include "UserInterface.h" -#include "PolynomialUnivariate.h" #include "VirtisHK.h" -#include "iTime.h" using namespace std; using namespace Isis; @@ -402,20 +403,10 @@ void IsisMain () // Fix the StartTime and SpacecraftStartClockCount in the ISIS label PvlGroup &inst = outLabel.findGroup("Instrument", Pvl::Traverse); - // Pass the Start/Stop SCET values to naif to get the utc time. - QString sclk = "$ISISDATA/rosetta/kernels/sclk/ROS_??????_STEP.TSC"; - QString lsk = "$ISISDATA/base/kernels/lsk/naif????.tls"; - FileName sclkName(sclk); - FileName lskName(lsk); - sclkName = sclkName.highestVersion(); - lskName = lskName.highestVersion(); + double etStart = Isis::RestfulSpice::strSclkToEt(-226, startScet.toLatin1().data(), "virtis"); + double etEnd = Isis::RestfulSpice::strSclkToEt(-226, stopScet.toLatin1().data(), "virtis"); - furnsh_c(lskName.expanded().toLatin1().data()); - furnsh_c(sclkName.expanded().toLatin1().data()); - - SpiceDouble etStart; - SpiceDouble etEnd; scs2e_c( (SpiceInt) -226, startScet.toLatin1().data(), &etStart); scs2e_c( (SpiceInt) -226, stopScet.toLatin1().data(), &etEnd); @@ -425,22 +416,17 @@ void IsisMain () QString startTime = iTime(etStart-exposureTime).UTC(); QString stopTime = iTime(etEnd-exposureTime).UTC(); - SpiceChar startSclkString[50]; - SpiceChar endSclkString[50]; - sce2s_c( (SpiceInt) -226, etStart-exposureTime, (SpiceInt) 50, startSclkString); - sce2s_c( (SpiceInt) -226, etEnd-exposureTime, (SpiceInt) 50, endSclkString); + + std::string startSclkString = Isis::RestfulSpice::doubleEtToSclk( -226, etStart-exposureTime, "virtis"); + std::string endSclkString = Isis::RestfulSpice::doubleEtToSclk( -226, etEnd-exposureTime, "virtis"); inst.findKeyword("StartTime").setValue(startTime); inst.findKeyword("StopTime").setValue(stopTime); - inst.findKeyword("SpacecraftClockStartCount").setValue(startSclkString); - inst.findKeyword("SpacecraftClockStopCount").setValue(endSclkString); + inst.findKeyword("SpacecraftClockStartCount").setValue(QString::fromStdString(startSclkString)); + inst.findKeyword("SpacecraftClockStopCount").setValue(QString::fromStdString(endSclkString)); outcube->putGroup(inst); - - // Unload the naif kernels - unload_c(lsk.toLatin1().data()); - unload_c(sclk.toLatin1().data()); } // Write the Archive and Instrument groups to the output cube label diff --git a/isis/src/system/apps/kerneldbgen/SpiceDbGen.cpp b/isis/src/system/apps/kerneldbgen/SpiceDbGen.cpp index 3a8ca2071b..d03a735e36 100644 --- a/isis/src/system/apps/kerneldbgen/SpiceDbGen.cpp +++ b/isis/src/system/apps/kerneldbgen/SpiceDbGen.cpp @@ -14,6 +14,7 @@ find files of those names at the top level of this repository. **/ #include "SpiceDbGen.h" #include "NaifStatus.h" +#include "spiceql.h" using namespace std; using namespace Isis; @@ -229,7 +230,7 @@ PvlGroup SpiceDbGen::AddSelection(FileName fileIn, double startOffset, double en //finalize the filename so that it may be used in spice routines QString tmp = fileIn.expanded(); // const char* file = fileIn.expanded().c_str(); - furnsh_c(tmp.toLatin1().data()); + SpiceQL::load(tmp.toLatin1().data()); SpiceChar fileType[32], source[2048]; SpiceInt handle; QString instrument = ""; @@ -387,17 +388,17 @@ void SpiceDbGen::FurnishDependencies(QList sclks, QList lsks // furnish the lsk files foreach (FileName lsk, lsks) { - furnsh_c(lsk.expanded().toLatin1().data()); + SpiceQL::load(lsk.expanded().toLatin1().data()); } // furnish the sclk files foreach (FileName sclk, sclks) { - furnsh_c(sclk.expanded().toLatin1().data()); + SpiceQL::load(sclk.expanded().toLatin1().data()); } // furnish the extra files foreach (FileName extra, extras) { - furnsh_c(extra.expanded().toLatin1().data()); + SpiceQL::load(extra.expanded().toLatin1().data()); } NaifStatus::CheckErrors(); diff --git a/isis/src/viking/apps/vikcal/CalParameters.cpp b/isis/src/viking/apps/vikcal/CalParameters.cpp index 00f74e7277..1e42cb3f06 100644 --- a/isis/src/viking/apps/vikcal/CalParameters.cpp +++ b/isis/src/viking/apps/vikcal/CalParameters.cpp @@ -23,6 +23,7 @@ find files of those names at the top level of this repository. **/ #include "iTime.h" #include "LeastSquares.h" #include "Pvl.h" +#include "RestfulSpice.h" #include "TextFile.h" #include "NaifStatus.h" @@ -385,15 +386,12 @@ namespace Isis { try { NaifStatus::CheckErrors(); double sunv[3]; - SpiceDouble lt, et; - FileName fname1 = (FileName)"$base/kernels/lsk/naif0007.tls"; - FileName fname2 = (FileName)"$base/kernels/spk/de405.bsp"; - QString tempfname1 = fname1.expanded(); - QString tempfname2 = fname2.expanded(); - furnsh_c(tempfname1.toLatin1().data()); - furnsh_c(tempfname2.toLatin1().data()); - utc2et_c(t.toLatin1().data(), &et); - spkezp_c(10, et, "J2000", "LT+S", 499, sunv, <); + double et = Isis::RestfulSpice::utcToEt(t.toLatin1().data()); + + std::vector etStart = {et}; + std::vector> sunLt = Isis::RestfulSpice::getTargetStates(etStart, "sun", "mars", "J2000", "LT+S", "viking2", "reconstructed", "reconstructed"); + std::copy(sunLt[0].begin(), sunLt[0].begin()+3, sunv); + return sqrt(sunv[0] * sunv[0] + sunv[1] * sunv[1] + sunv[2] * sunv[2]); NaifStatus::CheckErrors(); } diff --git a/isis/src/voyager/apps/voy2isis/main.cpp b/isis/src/voyager/apps/voy2isis/main.cpp index 8593bf86c8..bf37b263ca 100644 --- a/isis/src/voyager/apps/voy2isis/main.cpp +++ b/isis/src/voyager/apps/voy2isis/main.cpp @@ -35,6 +35,7 @@ find files of those names at the top level of this repository. **/ #include "PvlGroup.h" #include "PvlKeyword.h" #include "PvlToPvlTranslationManager.h" +#include "RestfulSpice.h" #include "UserInterface.h" using namespace std; @@ -356,34 +357,18 @@ void TranslateVoyagerLabels(Pvl &inputLab, Cube *ocube) { // We've already handled a couple of the steps mentioned above. NaifStatus::CheckErrors(); - //* 3 *// - // Leapsecond kernel - QString lsk = "$base/kernels/lsk/naif????.tls"; - FileName lskName(lsk); - lskName = lskName.highestVersion(); - furnsh_c(lskName.expanded().toLatin1().data()); - - // Spacecraft clock kernel - QString sclk = "$ISISDATA/voyager"; - sclk.append(spacecraftNumber); - sclk.append("/kernels/sclk/vg"); - sclk.append(spacecraftNumber); - sclk.append("?????.tsc"); - FileName sclkName(sclk); - sclkName = sclkName.highestVersion(); - furnsh_c(sclkName.expanded().toLatin1().data()); // The purpose of the next two steps, getting the spacecraft clock count, // are simply to get the partition, the very first number 1/... - double approxEphemeris = 0.0; - utc2et_c(inst["StartTime"][0].toLatin1().data(), &approxEphemeris); - char approxSpacecraftClock[80]; + + double approxEphemeris = Isis::RestfulSpice::utcToEt(inst["StartTime"][0].toLatin1().data()); // sce2s_c requires the spacecraft number, not the instrument number as // we've found elsewhere, either -31 or -32 in this case. int spacecraftClockNumber = -30; spacecraftClockNumber -= toInt(spacecraftNumber); - sce2s_c(spacecraftClockNumber, approxEphemeris, 80, approxSpacecraftClock); + std::string confId = "voyager" + spacecraftNumber.toStdString(); + std::string approxSpacecraftClock = Isis::RestfulSpice::doubleEtToSclk(spacecraftClockNumber, approxEphemeris, confId); /* * For our next trick, we will substitute the image number we got earlier @@ -403,21 +388,18 @@ void TranslateVoyagerLabels(Pvl &inputLab, Cube *ocube) { */ // Get first digit and the / - QString newClockCount = QString(approxSpacecraftClock).mid(0, 2); + QString newClockCount = QString::fromStdString(approxSpacecraftClock).mid(0, 2); // Get first five digits of imgNumber and append newClockCount.append(imgNumber.mid(0, 5)); - // I'll stop commenting now, you can read code as well as me newClockCount.append(":"); - // I lied ;) get the last two digits. newClockCount.append(imgNumber.mid(5, 2)); - scs2e_c(spacecraftClockNumber, newClockCount.toLatin1().data(), &approxEphemeris); + approxEphemeris = Isis::RestfulSpice::strSclkToEt(spacecraftClockNumber, newClockCount.toStdString(), confId); //* 4 *// - char utcOut[25]; - et2utc_c(approxEphemeris, "ISOC", 3, 26, utcOut); + std::string utcOut = Isis::RestfulSpice::etToUtc(approxEphemeris, "ISOC", 3); NaifStatus::CheckErrors(); - inst["StartTime"].setValue(QString(utcOut)); + inst["StartTime"].setValue(QString::fromStdString(utcOut)); // Set up the nominal reseaus group PvlGroup res("Reseaus"); diff --git a/isis/tests/FunctionalTestsCkwriter.cpp b/isis/tests/FunctionalTestsCkwriter.cpp index 248965dd6c..09522fd6eb 100644 --- a/isis/tests/FunctionalTestsCkwriter.cpp +++ b/isis/tests/FunctionalTestsCkwriter.cpp @@ -8,6 +8,7 @@ #include "Table.h" #include "TextFile.h" #include "TestUtilities.h" +#include "spiceql.h" #include "gtest/gtest.h" #include "gmock/gmock.h" @@ -229,7 +230,7 @@ TEST(Ckwriter, FunctionalTestCkwriterOffsets) { FAIL() << "Unable to write kernel file: " << e.toString().toStdString().c_str() << std::endl; } QString tmp = options.GetFileName("TO"); - furnsh_c(tmp.toLatin1().data()); + SpiceQL::load(tmp.toLatin1().data()); SpiceChar fileType[32], source[2048]; SpiceInt handle; QString instrument = ""; diff --git a/isis/tests/FunctionalTestsLronaccal.cpp b/isis/tests/FunctionalTestsLronaccal.cpp index 0d16795db6..0007a43041 100644 --- a/isis/tests/FunctionalTestsLronaccal.cpp +++ b/isis/tests/FunctionalTestsLronaccal.cpp @@ -212,7 +212,7 @@ TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacLFull) { ub RadiometricType = IOF ResponsivityValue = 15869.0 - SolarDistance = 0.98615168542222 + SolarDistance = 0.98615168541745 End_Group )"); @@ -223,10 +223,10 @@ TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacLFull) { EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, radGroup, truthRadGroup); Histogram *oCubeStats = outCube.histogram(); - EXPECT_DOUBLE_EQ(oCubeStats->Average(), 0.026724545839011172); - EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 136829.67469573719); + EXPECT_DOUBLE_EQ(oCubeStats->Average(), 0.026724545838699577); + EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 136829.67469414184); EXPECT_EQ(oCubeStats->ValidPixels(), 5120000); - EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 0.0020650268181325645); + EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 0.0020650268181003251); } TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacLSummed) { @@ -267,7 +267,7 @@ TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacLSummed) { .0006.cub RadiometricType = IOF ResponsivityValue = 15869.0 - SolarDistance = 1.0092946598536 + SolarDistance = 1.0092946598529 End_Group )"); @@ -278,10 +278,10 @@ TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacLSummed) { EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, radGroup, truthRadGroup); Histogram *oCubeStats = outCube.histogram(); - EXPECT_DOUBLE_EQ(oCubeStats->Average(), 0.0067645818969427939); - EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 51951.988968520658); + EXPECT_DOUBLE_EQ(oCubeStats->Average(), 0.0067645818969367987); + EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 51951.988968474616); EXPECT_EQ(oCubeStats->ValidPixels(), 7680000); - EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 0.0086012102391031867); + EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 0.0086012102390964074); } TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacRFull) { @@ -325,7 +325,7 @@ TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacRFull) { ub RadiometricType = IOF ResponsivityValue = 15058.0 - SolarDistance = 0.98615168542222 + SolarDistance = 0.98615168541745 End_Group )"); @@ -336,10 +336,10 @@ TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacRFull) { EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, radGroup, truthRadGroup); Histogram *oCubeStats = outCube.histogram(); - EXPECT_DOUBLE_EQ(oCubeStats->Average(), 0.025868278779590172); - EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 132445.58735150169); + EXPECT_DOUBLE_EQ(oCubeStats->Average(), 0.025868278779362618); + EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 132445.5873503366); EXPECT_EQ(oCubeStats->ValidPixels(), 5120000); - EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 0.0018962021917208359); + EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 0.0018962021917028015); } TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacRSummed) { @@ -380,7 +380,7 @@ TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacRSummed) { .0006.cub RadiometricType = IOF ResponsivityValue = 15058.0 - SolarDistance = 1.0092946598536 + SolarDistance = 1.0092946598529 End_Group )"); @@ -391,8 +391,8 @@ TEST_F(TempTestingFiles, FunctionalTestsLronaccalNacRSummed) { EXPECT_PRED_FORMAT2(AssertPvlGroupEqual, radGroup, truthRadGroup); Histogram *oCubeStats = outCube.histogram(); - EXPECT_DOUBLE_EQ(oCubeStats->Average(), 0.0067305094629900421); - EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 51690.312675763525); + EXPECT_DOUBLE_EQ(oCubeStats->Average(), 0.0067305094629838691); + EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 51690.312675716115); EXPECT_EQ(oCubeStats->ValidPixels(), 7680000); - EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 0.0086439695700371976); + EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 0.0086439695700290947); } \ No newline at end of file diff --git a/isis/tests/FunctionalTestsSpkwriter.cpp b/isis/tests/FunctionalTestsSpkwriter.cpp index e581ff21b1..64e615d2fe 100644 --- a/isis/tests/FunctionalTestsSpkwriter.cpp +++ b/isis/tests/FunctionalTestsSpkwriter.cpp @@ -10,6 +10,7 @@ #include "Table.h" #include "TextFile.h" #include "TestUtilities.h" +#include "spiceql.h" #include "gtest/gtest.h" #include "gmock/gmock.h" @@ -210,7 +211,7 @@ TEST(Spkwriter, FunctionalTestSpkwriterOffsets) { FAIL() << "Unable to write kernel file: " << e.toString().toStdString().c_str() << std::endl; } QString tmp = options.GetFileName("TO"); - furnsh_c(tmp.toLatin1().data()); + SpiceQL::load(tmp.toLatin1().data()); SpiceChar fileType[32], source[2048]; SpiceInt handle; QString instrument = ""; diff --git a/isis/tests/SensorUtilWrappersTests.cpp b/isis/tests/SensorUtilWrappersTests.cpp index 85e1a96a6c..0766af59b4 100644 --- a/isis/tests/SensorUtilWrappersTests.cpp +++ b/isis/tests/SensorUtilWrappersTests.cpp @@ -14,6 +14,7 @@ #include "IsisShape.h" #include "IsisBody.h" #include "SurfacePoint.h" +#include "spiceql.h" using namespace std; @@ -44,7 +45,7 @@ class SpiceKernels : public ::testing::Test { kernels.push_back(dir + "ROS_V29.TF"); kernels.push_back(dir + "CATT_DV_145_02_______00216.BC"); for (QString& kernel : kernels) { - furnsh_c(kernel.toLatin1().data()); + SpiceQL::load(kernel.toLatin1().data()); } } diff --git a/isis/tests/SpiceRotationTests.cpp b/isis/tests/SpiceRotationTests.cpp index 75ca7fe019..5c50ff9847 100644 --- a/isis/tests/SpiceRotationTests.cpp +++ b/isis/tests/SpiceRotationTests.cpp @@ -17,6 +17,7 @@ #include "SpiceRotation.h" #include "Table.h" #include "TestUtilities.h" +#include "spiceql.h" // Declarations for bindings for Naif Spicelib routines that do not have // a wrapper @@ -62,7 +63,7 @@ class SpiceRotationKernels : public ::testing::Test { kernels.push_back(dir + "ROS_V29.TF"); kernels.push_back(dir + "CATT_DV_145_02_______00216.BC"); for (QString& kernel : kernels) { - furnsh_c(kernel.toLatin1().data()); + SpiceQL::load(kernel.toLatin1().data()); } }