diff --git a/ntcore/src/main/native/cpp/ntcore_c.cpp b/ntcore/src/main/native/cpp/ntcore_c.cpp index 8837fbfff9..d6844d8e5b 100644 --- a/ntcore/src/main/native/cpp/ntcore_c.cpp +++ b/ntcore/src/main/native/cpp/ntcore_c.cpp @@ -588,6 +588,27 @@ void NT_SetNow(int64_t timestamp) { nt::SetNow(timestamp); } +NT_DataLogger NT_StartEntryDataLog(NT_Inst inst, struct WPI_DataLog* log, + const char* prefix, const char* logPrefix) { + return nt::StartEntryDataLog(inst, *reinterpret_cast(log), + prefix, logPrefix); +} + +void NT_StopEntryDataLog(NT_DataLogger logger) { + nt::StopEntryDataLog(logger); +} + +NT_ConnectionDataLogger NT_StartConnectionDataLog(NT_Inst inst, + struct WPI_DataLog* log, + const char* name) { + return nt::StartConnectionDataLog( + inst, *reinterpret_cast(log), name); +} + +void NT_StopConnectionDataLog(NT_ConnectionDataLogger logger) { + nt::StopConnectionDataLog(logger); +} + NT_Listener NT_AddLogger(NT_Inst inst, unsigned int min_level, unsigned int max_level, void* data, NT_ListenerCallback func) { diff --git a/ntcore/src/main/native/include/ntcore_c.h b/ntcore/src/main/native/include/ntcore_c.h index b0fb058660..56426fe818 100644 --- a/ntcore/src/main/native/include/ntcore_c.h +++ b/ntcore/src/main/native/include/ntcore_c.h @@ -16,6 +16,8 @@ extern "C" { #endif +struct WPI_DataLog; + /** * @defgroup ntcore_c_api ntcore C API * @@ -1347,6 +1349,54 @@ void NT_SetNow(int64_t timestamp); /** @} */ +/** + * @defgroup ntcore_data_logger_cfunc Data Logger Functions + * @{ + */ + +/** + * Starts logging entry changes to a DataLog. + * + * @param inst instance handle + * @param log data log object; lifetime must extend until StopEntryDataLog is + * called or the instance is destroyed + * @param prefix only store entries with names that start with this prefix; + * the prefix is not included in the data log entry name + * @param logPrefix prefix to add to data log entry names + * @return Data logger handle + */ +NT_DataLogger NT_StartEntryDataLog(NT_Inst inst, struct WPI_DataLog* log, + const char* prefix, const char* logPrefix); + +/** + * Stops logging entry changes to a DataLog. + * + * @param logger data logger handle + */ +void NT_StopEntryDataLog(NT_DataLogger logger); + +/** + * Starts logging connection changes to a DataLog. + * + * @param inst instance handle + * @param log data log object; lifetime must extend until StopConnectionDataLog + * is called or the instance is destroyed + * @param name data log entry name + * @return Data logger handle + */ +NT_ConnectionDataLogger NT_StartConnectionDataLog(NT_Inst inst, + struct WPI_DataLog* log, + const char* name); + +/** + * Stops logging connection changes to a DataLog. + * + * @param logger data logger handle + */ +void NT_StopConnectionDataLog(NT_ConnectionDataLogger logger); + +/** @} */ + /** * @defgroup ntcore_logger_cfunc Logger Functions * @{ diff --git a/ntcoreffi/src/main/native/symbols.txt b/ntcoreffi/src/main/native/symbols.txt index d097c8a1e6..1bfc067351 100644 --- a/ntcoreffi/src/main/native/symbols.txt +++ b/ntcoreffi/src/main/native/symbols.txt @@ -199,11 +199,15 @@ NT_SetTopicProperty NT_SetTopicRetained NT_StartClient3 NT_StartClient4 +NT_StartConnectionDataLog NT_StartDSClient +NT_StartEntryDataLog NT_StartLocal NT_StartServer NT_StopClient +NT_StopConnectionDataLog NT_StopDSClient +NT_StopEntryDataLog NT_StopLocal NT_StopServer NT_Subscribe @@ -213,6 +217,28 @@ NT_WaitForListenerQueue WPI_CreateEvent WPI_CreateSemaphore WPI_CreateSignalObject +WPI_DataLog_AppendBoolean +WPI_DataLog_AppendBooleanArray +WPI_DataLog_AppendBooleanArrayByte +WPI_DataLog_AppendDouble +WPI_DataLog_AppendDoubleArray +WPI_DataLog_AppendFloat +WPI_DataLog_AppendFloatArray +WPI_DataLog_AppendInteger +WPI_DataLog_AppendIntegerArray +WPI_DataLog_AppendRaw +WPI_DataLog_AppendString +WPI_DataLog_AppendStringArray +WPI_DataLog_Create +WPI_DataLog_Create_Func +WPI_DataLog_Finish +WPI_DataLog_Flush +WPI_DataLog_Pause +WPI_DataLog_Release +WPI_DataLog_Resume +WPI_DataLog_SetFilename +WPI_DataLog_SetMetadata +WPI_DataLog_Start WPI_DestroyEvent WPI_DestroySemaphore WPI_DestroySignalObject diff --git a/wpiutil/src/main/native/cpp/DataLog.cpp b/wpiutil/src/main/native/cpp/DataLog.cpp index 70096288da..acb0fd9b9c 100644 --- a/wpiutil/src/main/native/cpp/DataLog.cpp +++ b/wpiutil/src/main/native/cpp/DataLog.cpp @@ -815,7 +815,160 @@ void DataLog::AppendStringArray(int entry, } uint8_t* buf = StartRecord(entry, timestamp, size, 4); wpi::support::endian::write32le(buf, arr.size()); - for (auto sv : arr) { + for (auto&& sv : arr) { AppendStringImpl(sv); } } + +void DataLog::AppendStringArray(int entry, + std::span arr, + int64_t timestamp) { + if (entry <= 0) { + return; + } + // storage: 4-byte array length, each string prefixed by 4-byte length + // calculate total size + size_t size = 4; + for (auto&& str : arr) { + size += 4 + str.len; + } + std::scoped_lock lock{m_mutex}; + if (m_paused) { + return; + } + uint8_t* buf = StartRecord(entry, timestamp, size, 4); + wpi::support::endian::write32le(buf, arr.size()); + for (auto&& sv : arr) { + AppendStringImpl(sv.str); + } +} + +extern "C" { + +struct WPI_DataLog* WPI_DataLog_Create(const char* dir, const char* filename, + double period, const char* extraHeader) { + return reinterpret_cast( + new DataLog{dir, filename, period, extraHeader}); +} + +struct WPI_DataLog* WPI_DataLog_Create_Func( + void (*write)(void* ptr, const uint8_t* data, size_t len), void* ptr, + double period, const char* extraHeader) { + return reinterpret_cast( + new DataLog{[=](auto data) { write(ptr, data.data(), data.size()); }, + period, extraHeader}); +} + +void WPI_DataLog_Release(struct WPI_DataLog* datalog) { + delete reinterpret_cast(datalog); +} + +void WPI_DataLog_SetFilename(struct WPI_DataLog* datalog, + const char* filename) { + reinterpret_cast(datalog)->SetFilename(filename); +} + +void WPI_DataLog_Flush(struct WPI_DataLog* datalog) { + reinterpret_cast(datalog)->Flush(); +} + +void WPI_DataLog_Pause(struct WPI_DataLog* datalog) { + reinterpret_cast(datalog)->Pause(); +} + +void WPI_DataLog_Resume(struct WPI_DataLog* datalog) { + reinterpret_cast(datalog)->Resume(); +} + +int WPI_DataLog_Start(struct WPI_DataLog* datalog, const char* name, + const char* type, const char* metadata, + int64_t timestamp) { + return reinterpret_cast(datalog)->Start(name, type, metadata, + timestamp); +} + +void WPI_DataLog_Finish(struct WPI_DataLog* datalog, int entry, + int64_t timestamp) { + reinterpret_cast(datalog)->Finish(entry, timestamp); +} + +void WPI_DataLog_SetMetadata(struct WPI_DataLog* datalog, int entry, + const char* metadata, int64_t timestamp) { + reinterpret_cast(datalog)->SetMetadata(entry, metadata, timestamp); +} + +void WPI_DataLog_AppendRaw(struct WPI_DataLog* datalog, int entry, + const uint8_t* data, size_t len, int64_t timestamp) { + reinterpret_cast(datalog)->AppendRaw(entry, {data, len}, timestamp); +} + +void WPI_DataLog_AppendBoolean(struct WPI_DataLog* datalog, int entry, + int value, int64_t timestamp) { + reinterpret_cast(datalog)->AppendBoolean(entry, value, timestamp); +} + +void WPI_DataLog_AppendInteger(struct WPI_DataLog* datalog, int entry, + int64_t value, int64_t timestamp) { + reinterpret_cast(datalog)->AppendInteger(entry, value, timestamp); +} + +void WPI_DataLog_AppendFloat(struct WPI_DataLog* datalog, int entry, + float value, int64_t timestamp) { + reinterpret_cast(datalog)->AppendFloat(entry, value, timestamp); +} + +void WPI_DataLog_AppendDouble(struct WPI_DataLog* datalog, int entry, + double value, int64_t timestamp) { + reinterpret_cast(datalog)->AppendDouble(entry, value, timestamp); +} + +void WPI_DataLog_AppendString(struct WPI_DataLog* datalog, int entry, + const char* value, size_t len, + int64_t timestamp) { + reinterpret_cast(datalog)->AppendString(entry, {value, len}, + timestamp); +} + +void WPI_DataLog_AppendBooleanArray(struct WPI_DataLog* datalog, int entry, + const int* arr, size_t len, + int64_t timestamp) { + reinterpret_cast(datalog)->AppendBooleanArray(entry, {arr, len}, + timestamp); +} + +void WPI_DataLog_AppendBooleanArrayByte(struct WPI_DataLog* datalog, int entry, + const uint8_t* arr, size_t len, + int64_t timestamp) { + reinterpret_cast(datalog)->AppendBooleanArray(entry, {arr, len}, + timestamp); +} + +void WPI_DataLog_AppendIntegerArray(struct WPI_DataLog* datalog, int entry, + const int64_t* arr, size_t len, + int64_t timestamp) { + reinterpret_cast(datalog)->AppendIntegerArray(entry, {arr, len}, + timestamp); +} + +void WPI_DataLog_AppendFloatArray(struct WPI_DataLog* datalog, int entry, + const float* arr, size_t len, + int64_t timestamp) { + reinterpret_cast(datalog)->AppendFloatArray(entry, {arr, len}, + timestamp); +} + +void WPI_DataLog_AppendDoubleArray(struct WPI_DataLog* datalog, int entry, + const double* arr, size_t len, + int64_t timestamp) { + reinterpret_cast(datalog)->AppendDoubleArray(entry, {arr, len}, + timestamp); +} + +void WPI_DataLog_AppendStringArray(struct WPI_DataLog* datalog, int entry, + const WPI_DataLog_String* arr, size_t len, + int64_t timestamp) { + reinterpret_cast(datalog)->AppendStringArray(entry, {arr, len}, + timestamp); +} + +} // extern "C" diff --git a/wpiutil/src/main/native/include/wpi/DataLog.h b/wpiutil/src/main/native/include/wpi/DataLog.h index 5dfddfd025..5a3a76feb8 100644 --- a/wpiutil/src/main/native/include/wpi/DataLog.h +++ b/wpiutil/src/main/native/include/wpi/DataLog.h @@ -6,6 +6,7 @@ #include +#ifdef __cplusplus #include #include #include @@ -19,6 +20,20 @@ #include "wpi/StringMap.h" #include "wpi/condition_variable.h" #include "wpi/mutex.h" +#endif // __cplusplus + +/** + * A datalog string (for use with string array). + */ +struct WPI_DataLog_String { + /** Contents. */ + const char* str; + + /** Length. */ + size_t len; +}; + +#ifdef __cplusplus namespace wpi { class Logger; @@ -333,6 +348,16 @@ class DataLog final { void AppendStringArray(int entry, std::span arr, int64_t timestamp); + /** + * Appends a string array record to the log. + * + * @param entry Entry index, as returned by Start() + * @param arr String array to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ + void AppendStringArray(int entry, std::span arr, + int64_t timestamp); + private: void WriterThreadMain(std::string_view dir); void WriterThreadMain( @@ -797,3 +822,268 @@ class StringArrayLogEntry : public DataLogEntry { }; } // namespace wpi::log + +extern "C" { +#endif // __cplusplus + +/** C-compatible data log (opaque struct). */ +struct WPI_DataLog; + +/** + * Construct a new Data Log. The log will be initially created with a + * temporary filename. + * + * @param dir directory to store the log + * @param filename filename to use; if none provided, a random filename is + * generated of the form "wpilog_{}.wpilog" + * @param period time between automatic flushes to disk, in seconds; + * this is a time/storage tradeoff + * @param extraHeader extra header data + */ +struct WPI_DataLog* WPI_DataLog_Create(const char* dir, const char* filename, + double period, const char* extraHeader); + +/** + * Construct a new Data Log that passes its output to the provided function + * rather than a file. The write function will be called on a separate + * background thread and may block. The write function is called with an + * empty data array (data=NULL, len=0) when the thread is terminating. + * + * @param write write function + * @param ptr pointer to pass to write function ptr parameter + * @param period time between automatic calls to write, in seconds; + * this is a time/storage tradeoff + * @param extraHeader extra header data + */ +struct WPI_DataLog* WPI_DataLog_Create_Func( + void (*write)(void* ptr, const uint8_t* data, size_t len), void* ptr, + double period, const char* extraHeader); + +/** + * Releases a data log object. Closes the file and returns resources to the + * system. + * + * @param datalog data log + */ +void WPI_DataLog_Release(struct WPI_DataLog* datalog); + +/** + * Change log filename. + * + * @param datalog data log + * @param filename filename + */ +void WPI_DataLog_SetFilename(struct WPI_DataLog* datalog, const char* filename); + +/** + * Explicitly flushes the log data to disk. + * + * @param datalog data log + */ +void WPI_DataLog_Flush(struct WPI_DataLog* datalog); + +/** + * Pauses appending of data records to the log. While paused, no data records + * are saved (e.g. AppendX is a no-op). Has no effect on entry starts / + * finishes / metadata changes. + * + * @param datalog data log + */ +void WPI_DataLog_Pause(struct WPI_DataLog* datalog); + +/** + * Resumes appending of data records to the log. + * + * @param datalog data log + */ +void WPI_DataLog_Resume(struct WPI_DataLog* datalog); + +/** + * Start an entry. Duplicate names are allowed (with the same type), and + * result in the same index being returned (Start/Finish are reference + * counted). A duplicate name with a different type will result in an error + * message being printed to the console and 0 being returned (which will be + * ignored by the Append functions). + * + * @param datalog data log + * @param name Name + * @param type Data type + * @param metadata Initial metadata (e.g. data properties) + * @param timestamp Time stamp (may be 0 to indicate now) + * + * @return Entry index + */ +int WPI_DataLog_Start(struct WPI_DataLog* datalog, const char* name, + const char* type, const char* metadata, + int64_t timestamp); + +/** + * Finish an entry. + * + * @param datalog data log + * @param entry Entry index + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_Finish(struct WPI_DataLog* datalog, int entry, + int64_t timestamp); + +/** + * Updates the metadata for an entry. + * + * @param datalog data log + * @param entry Entry index + * @param metadata New metadata for the entry + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_SetMetadata(struct WPI_DataLog* datalog, int entry, + const char* metadata, int64_t timestamp); + +/** + * Appends a raw record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param data Byte array to record + * @param len Length of byte array + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendRaw(struct WPI_DataLog* datalog, int entry, + const uint8_t* data, size_t len, int64_t timestamp); + +/** + * Appends a boolean record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param value Boolean value to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendBoolean(struct WPI_DataLog* datalog, int entry, + int value, int64_t timestamp); + +/** + * Appends an integer record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param value Integer value to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendInteger(struct WPI_DataLog* datalog, int entry, + int64_t value, int64_t timestamp); + +/** + * Appends a float record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param value Float value to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendFloat(struct WPI_DataLog* datalog, int entry, + float value, int64_t timestamp); + +/** + * Appends a double record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param value Double value to record + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendDouble(struct WPI_DataLog* datalog, int entry, + double value, int64_t timestamp); + +/** + * Appends a string record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param value String value to record + * @param len Length of string + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendString(struct WPI_DataLog* datalog, int entry, + const char* value, size_t len, int64_t timestamp); + +/** + * Appends a boolean array record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param arr Boolean array to record + * @param len Number of elements in array + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendBooleanArray(struct WPI_DataLog* datalog, int entry, + const int* arr, size_t len, + int64_t timestamp); + +/** + * Appends a boolean array record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param arr Boolean array to record + * @param len Number of elements in array + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendBooleanArrayByte(struct WPI_DataLog* datalog, int entry, + const uint8_t* arr, size_t len, + int64_t timestamp); + +/** + * Appends an integer array record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param arr Integer array to record + * @param len Number of elements in array + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendIntegerArray(struct WPI_DataLog* datalog, int entry, + const int64_t* arr, size_t len, + int64_t timestamp); + +/** + * Appends a float array record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param arr Float array to record + * @param len Number of elements in array + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendFloatArray(struct WPI_DataLog* datalog, int entry, + const float* arr, size_t len, + int64_t timestamp); + +/** + * Appends a double array record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param arr Double array to record + * @param len Number of elements in array + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendDoubleArray(struct WPI_DataLog* datalog, int entry, + const double* arr, size_t len, + int64_t timestamp); + +/** + * Appends a string array record to the log. + * + * @param datalog data log + * @param entry Entry index, as returned by WPI_DataLog_Start() + * @param arr String array to record + * @param len Number of elements in array + * @param timestamp Time stamp (may be 0 to indicate now) + */ +void WPI_DataLog_AppendStringArray(struct WPI_DataLog* datalog, int entry, + const WPI_DataLog_String* arr, size_t len, + int64_t timestamp); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus