Add debug and log functions to ANSLIB
This commit is contained in:
1
.claude/scheduled_tasks.lock
Normal file
1
.claude/scheduled_tasks.lock
Normal file
@@ -0,0 +1 @@
|
||||
{"sessionId":"b4ba6a5c-5267-4098-8e7c-152012d3df22","pid":5176,"acquiredAt":1776991716766}
|
||||
@@ -110,7 +110,13 @@
|
||||
"Bash(tasklist //FI \"IMAGENAME eq ANSLPR-UnitTest.exe\")",
|
||||
"Bash(taskkill //PID 41856 //F)",
|
||||
"PowerShell(Stop-Process -Id 41856 -Force)",
|
||||
"PowerShell(Start-Process -FilePath \"taskkill.exe\" -ArgumentList \"/PID\",\"41856\",\"/F\" -Verb RunAs -Wait)"
|
||||
"PowerShell(Start-Process -FilePath \"taskkill.exe\" -ArgumentList \"/PID\",\"41856\",\"/F\" -Verb RunAs -Wait)",
|
||||
"Bash(npm root *)",
|
||||
"Bash(npm list *)",
|
||||
"Bash(npm install *)",
|
||||
"Bash(NODE_PATH=\"C:/home/alex/.npm-global/node_modules\" node build_anslib_logging_guide.js)",
|
||||
"Bash(python \"C:/Users/nghia/AppData/Roaming/Claude/local-agent-mode-sessions/skills-plugin/d8e35aa4-a14e-4e20-b921-ba1b9a3cce86/cdda7cc8-a1c7-42ff-98b4-473ec3e8b9fb/skills/docx/scripts/office/validate.py\" \"C:/Projects/CLionProjects/ANSCORE/docs/ANSLIB_Logging_Guide.docx\")",
|
||||
"Bash(python \"C:/Users/nghia/AppData/Roaming/Claude/local-agent-mode-sessions/skills-plugin/d8e35aa4-a14e-4e20-b921-ba1b9a3cce86/cdda7cc8-a1c7-42ff-98b4-473ec3e8b9fb/skills/docx/scripts/office/soffice.py\" --headless --convert-to pdf --outdir \"C:/Projects/CLionProjects/ANSCORE/docs\" \"C:/Projects/CLionProjects/ANSCORE/docs/ANSLIB_Logging_Guide.docx\")"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,61 @@
|
||||
#include "ANSLIB.h"
|
||||
#include "ANSLibsLoader.h"
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#define LOAD_FUNC(name) \
|
||||
name##Func = (name##FuncT)GetProcAddress(dllHandle, #name); \
|
||||
if (!name##Func) { success = false; }
|
||||
namespace ANSCENTER
|
||||
{
|
||||
ANSLIB::ANSLIB() {
|
||||
namespace {
|
||||
// Function-pointer typedefs for the ANSLicensingSystem C-facade exports.
|
||||
// Resolved exactly once during PreloadSharedDllsOnce(); after that the
|
||||
// pointers are write-once globals — concurrent reads are race-free.
|
||||
using ANSLogger_LogFnT = void(*)(const char*, const char*, const char*, int);
|
||||
using ANSCENTER_IsDbgEnFnT = int (*)(void);
|
||||
ANSLogger_LogFnT g_ANSLogger_LogInfo = nullptr;
|
||||
ANSLogger_LogFnT g_ANSLogger_LogError = nullptr;
|
||||
ANSLogger_LogFnT g_ANSLogger_LogFatal = nullptr;
|
||||
ANSCENTER_IsDbgEnFnT g_ANSCENTER_IsDebugViewEnabled = nullptr;
|
||||
|
||||
// Pre-load all ANSCORE DLLs from the Shared folder using full paths.
|
||||
// This ensures the correct versions are loaded regardless of PATH order
|
||||
// (e.g., DLHUB_Runtime_Engine may contain older copies on some machines).
|
||||
// Once loaded, Windows reuses them for all subsequent implicit dependencies.
|
||||
const char* sharedDir = "C:\\ProgramData\\ANSCENTER\\Shared\\";
|
||||
const char* preloadDlls[] = {
|
||||
"ANSLicensingSystem.dll",
|
||||
"anslicensing.dll",
|
||||
"ANSMOT.dll",
|
||||
"ANSODEngine.dll",
|
||||
nullptr
|
||||
};
|
||||
for (int i = 0; preloadDlls[i] != nullptr; i++) {
|
||||
std::string fullPath = std::string(sharedDir) + preloadDlls[i];
|
||||
LoadLibraryA(fullPath.c_str());
|
||||
}
|
||||
// Run exactly once per process so LoadLibrary refcounts don't grow per
|
||||
// ANSLIB instance. The DLLs are intentionally pinned for process lifetime.
|
||||
void PreloadSharedDllsOnce() {
|
||||
static std::once_flag s_preloadFlag;
|
||||
std::call_once(s_preloadFlag, []() {
|
||||
const char* sharedDir = "C:\\ProgramData\\ANSCENTER\\Shared\\";
|
||||
const char* preloadDlls[] = {
|
||||
"ANSLicensingSystem.dll",
|
||||
"anslicensing.dll",
|
||||
"ANSMOT.dll",
|
||||
"ANSODEngine.dll",
|
||||
nullptr
|
||||
};
|
||||
for (int i = 0; preloadDlls[i] != nullptr; i++) {
|
||||
std::string fullPath = std::string(sharedDir) + preloadDlls[i];
|
||||
LoadLibraryA(fullPath.c_str());
|
||||
}
|
||||
// Ensure all shared DLLs (OpenCV, OpenVINO, TRT, ORT) are pre-loaded
|
||||
ANSCENTER::ANSLibsLoader::Initialize();
|
||||
|
||||
// Ensure all shared DLLs (OpenCV, OpenVINO, TRT, ORT) are pre-loaded
|
||||
ANSCENTER::ANSLibsLoader::Initialize();
|
||||
// Resolve logging C-facade symbols from the (now-pinned) licensing DLL.
|
||||
// Use GetModuleHandleA — the DLL is already in this process's module
|
||||
// list courtesy of the LoadLibraryA above, so refcount is unchanged.
|
||||
if (HMODULE lic = GetModuleHandleA("ANSLicensingSystem.dll")) {
|
||||
g_ANSLogger_LogInfo = (ANSLogger_LogFnT) GetProcAddress(lic, "ANSLogger_LogInfo");
|
||||
g_ANSLogger_LogError = (ANSLogger_LogFnT) GetProcAddress(lic, "ANSLogger_LogError");
|
||||
g_ANSLogger_LogFatal = (ANSLogger_LogFnT) GetProcAddress(lic, "ANSLogger_LogFatal");
|
||||
g_ANSCENTER_IsDebugViewEnabled = (ANSCENTER_IsDbgEnFnT)GetProcAddress(lic, "ANSCENTER_IsDebugViewEnabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ANSLIB::ANSLIB() {
|
||||
PreloadSharedDllsOnce();
|
||||
|
||||
const char* dllPath = "C:\\ProgramData\\ANSCENTER\\Shared\\ANSODEngine.dll";
|
||||
dllHandle = LoadLibraryA(dllPath);
|
||||
@@ -99,6 +129,10 @@ namespace ANSCENTER
|
||||
return -1;
|
||||
}
|
||||
if (CreateANSODHandle_CSFunc) {
|
||||
if (ANSHandle && ReleaseANSODHandleFunc) {
|
||||
ReleaseANSODHandleFunc(&ANSHandle);
|
||||
ANSHandle = nullptr;
|
||||
}
|
||||
const char* result = CreateANSODHandle_CSFunc(&ANSHandle, licenseKey, modelFilePath, modelFileZipPassword,
|
||||
modelThreshold, modelConfThreshold, modelNMSThreshold,
|
||||
1, modelType, detectionType, loadEngineOnCreation);
|
||||
@@ -118,9 +152,8 @@ namespace ANSCENTER
|
||||
return -1;
|
||||
}
|
||||
if (RunInference_CPPFunc) {
|
||||
auto imagePtr = std::make_unique<cv::Mat>(cvImage);
|
||||
try {
|
||||
cv::Mat* rawPtr = imagePtr.get();
|
||||
cv::Mat* rawPtr = &cvImage;
|
||||
int result = RunInference_CPPFunc(&ANSHandle, &rawPtr, cameraId, detectionResult);
|
||||
return result;
|
||||
}
|
||||
@@ -135,9 +168,8 @@ namespace ANSCENTER
|
||||
return -1;
|
||||
}
|
||||
if (RunInferenceComplete_CPPFunc) {
|
||||
auto imagePtr = std::make_unique<cv::Mat>(cvImage);
|
||||
try {
|
||||
cv::Mat* rawPtr = imagePtr.get();
|
||||
cv::Mat* rawPtr = &cvImage;
|
||||
int result = RunInferenceComplete_CPPFunc(&ANSHandle, &rawPtr, cameraId, activeROIMode,detectionResult);
|
||||
return result;
|
||||
}
|
||||
@@ -193,6 +225,10 @@ namespace ANSCENTER
|
||||
int autoDetectEngine, int modelType, int detectionType, int loadEngineOnCreation, const char* modelFolder,std::string& labelMap)
|
||||
{
|
||||
if (!loaded || !LoadModelFromFolderFunc) return -1;
|
||||
if (ANSHandle && ReleaseANSODHandleFunc) {
|
||||
ReleaseANSODHandleFunc(&ANSHandle);
|
||||
ANSHandle = nullptr;
|
||||
}
|
||||
return LoadModelFromFolderFunc(&ANSHandle, licenseKey, modelName, className,
|
||||
detectionScoreThreshold, modelConfThreshold, modelMNSThreshold,
|
||||
autoDetectEngine, modelType, detectionType, loadEngineOnCreation,modelFolder, labelMap);
|
||||
@@ -241,4 +277,27 @@ namespace ANSCENTER
|
||||
if (!loaded || !SetTrackerParametersFunc) return -1;
|
||||
return SetTrackerParametersFunc(&ANSHandle, trackerParams);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Static logging API. Each call ensures the DLL/symbols are resolved (cheap
|
||||
// after the first call — std::call_once degenerates to an atomic load) then
|
||||
// forwards to the C facade. Null-pointer guard makes missing symbols a
|
||||
// silent no-op rather than a crash.
|
||||
// ------------------------------------------------------------------------
|
||||
int ANSLIB::IsDebugViewEnabled() {
|
||||
PreloadSharedDllsOnce();
|
||||
return g_ANSCENTER_IsDebugViewEnabled ? g_ANSCENTER_IsDebugViewEnabled() : 0;
|
||||
}
|
||||
void ANSLIB::LogInfo(const char* src, const char* msg, const char* file, int line) {
|
||||
PreloadSharedDllsOnce();
|
||||
if (g_ANSLogger_LogInfo) g_ANSLogger_LogInfo(src, msg, file, line);
|
||||
}
|
||||
void ANSLIB::LogError(const char* src, const char* msg, const char* file, int line) {
|
||||
PreloadSharedDllsOnce();
|
||||
if (g_ANSLogger_LogError) g_ANSLogger_LogError(src, msg, file, line);
|
||||
}
|
||||
void ANSLIB::LogFatal(const char* src, const char* msg, const char* file, int line) {
|
||||
PreloadSharedDllsOnce();
|
||||
if (g_ANSLogger_LogFatal) g_ANSLogger_LogFatal(src, msg, file, line);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <windows.h>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <opencv2/core/cuda.hpp>
|
||||
#include <cstdio>
|
||||
enum DetectionType {
|
||||
CLASSIFICATION = 0,
|
||||
DETECTION = 1,
|
||||
@@ -263,6 +264,10 @@ namespace ANSCENTER {
|
||||
public:
|
||||
ANSLIB();
|
||||
~ANSLIB() noexcept;
|
||||
ANSLIB(const ANSLIB&) = delete;
|
||||
ANSLIB& operator=(const ANSLIB&) = delete;
|
||||
ANSLIB(ANSLIB&&) = delete;
|
||||
ANSLIB& operator=(ANSLIB&&) = delete;
|
||||
[[nodiscard]] static ANSLIB* Create();
|
||||
static void Destroy(ANSLIB* instance);
|
||||
[[nodiscard]] int Initialize(const char* licenseKey,
|
||||
@@ -286,6 +291,20 @@ namespace ANSCENTER {
|
||||
[[nodiscard]] int SetPrompt(const char* text);
|
||||
[[nodiscard]] int SetTracker(int trackerType, int enableTracker);
|
||||
[[nodiscard]] int SetTrackerParameters(const char* trackerParams);
|
||||
|
||||
// Logging — process-wide, thread-safe, no instance required.
|
||||
// Routes to SPDLogger inside ANSLicensingSystem.dll via dynamically-resolved
|
||||
// C exports. Silently no-ops if the DLL or symbols are unavailable.
|
||||
static void LogInfo (const char* src, const char* msg, const char* file = "", int line = 0);
|
||||
static void LogError(const char* src, const char* msg, const char* file = "", int line = 0);
|
||||
static void LogFatal(const char* src, const char* msg, const char* file = "", int line = 0);
|
||||
|
||||
// DebugView gate. Wraps ANSCENTER_IsDebugViewEnabled() — non-zero when
|
||||
// logging should be emitted (env var ANSCENTER_DBGVIEW or sentinel file
|
||||
// C:\ProgramData\ANSCENTER\ansvisdebugview.txt). Cheap on the hot path.
|
||||
// Used by the ANSLIB_DBG macro below; can also be called directly to
|
||||
// gate expensive log-prep work.
|
||||
static int IsDebugViewEnabled();
|
||||
private:
|
||||
HMODULE dllHandle = nullptr;
|
||||
bool loaded = false;
|
||||
@@ -436,4 +455,31 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// ANSLIB_DBG: DebugView logging macro mirroring ANS_DBG, but the gate function
|
||||
// is dynamic-loaded inside ANSLIB.dll — consumers don't need to link or even
|
||||
// know about ANSLicensingSystem.dll. Output goes to BOTH OutputDebugStringA
|
||||
// (DebugView) and stderr, identical format to ANS_DBG: "[tag] formatted\n".
|
||||
//
|
||||
// Usage: ANSLIB_DBG("MyModule", "value=%d ptr=%p", val, ptr);
|
||||
//
|
||||
// Hot-path cost when gate is OFF: one indirect call (atomic load + branch
|
||||
// inside ANSLicensingSystem). When gate is ON: snprintf to a 1024-byte stack
|
||||
// buffer + OutputDebugStringA + fputs. No heap, no per-call allocation.
|
||||
#ifndef ANSCORE_DEBUGVIEW
|
||||
#define ANSCORE_DEBUGVIEW 1 // 1 = runtime-gated (default), 0 = hard-off (stripped)
|
||||
#endif
|
||||
#if ANSCORE_DEBUGVIEW
|
||||
#define ANSLIB_DBG(tag, fmt, ...) do { \
|
||||
if (::ANSCENTER::ANSLIB::IsDebugViewEnabled()) { \
|
||||
char _ansl_dbg_buf[1024]; \
|
||||
snprintf(_ansl_dbg_buf, sizeof(_ansl_dbg_buf), "[" tag "] " fmt "\n", ##__VA_ARGS__); \
|
||||
OutputDebugStringA(_ansl_dbg_buf); \
|
||||
fputs(_ansl_dbg_buf, stderr); \
|
||||
fflush(stderr); \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
#define ANSLIB_DBG(tag, fmt, ...) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -327,6 +327,35 @@ namespace ANSCENTER
|
||||
std::cerr << "SPDLogger::LogFatal - Exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// C facade — exported with stable C linkage so ANSLIB (and any other consumer)
|
||||
// can resolve via GetProcAddress without coupling to the C++ class ABI.
|
||||
// Each call delegates to the process-wide SPDLogger singleton; the magic-static
|
||||
// inside GetInstance handles thread-safe one-time initialisation.
|
||||
extern "C" {
|
||||
ANSLICENSE_API void ANSLogger_LogInfo(const char* src, const char* msg, const char* file, int line) {
|
||||
try {
|
||||
ANSCENTER::SPDLogger::GetInstance("ANSLIB").LogInfo(
|
||||
src ? src : "", msg ? msg : "", file ? file : "", line);
|
||||
} catch (...) {}
|
||||
}
|
||||
ANSLICENSE_API void ANSLogger_LogError(const char* src, const char* msg, const char* file, int line) {
|
||||
try {
|
||||
ANSCENTER::SPDLogger::GetInstance("ANSLIB").LogError(
|
||||
src ? src : "", msg ? msg : "", file ? file : "", line);
|
||||
} catch (...) {}
|
||||
}
|
||||
ANSLICENSE_API void ANSLogger_LogFatal(const char* src, const char* msg, const char* file, int line) {
|
||||
try {
|
||||
ANSCENTER::SPDLogger::GetInstance("ANSLIB").LogFatal(
|
||||
src ? src : "", msg ? msg : "", file ? file : "", line);
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ANSCENTER
|
||||
{
|
||||
|
||||
EngineType ANSCENTER::ANSLicenseHelper::CheckHardwareInformation()
|
||||
{
|
||||
|
||||
@@ -32,6 +32,15 @@
|
||||
// filesystem check runs at most once per ~2 s.
|
||||
extern "C" ANSLICENSE_API int ANSCENTER_IsDebugViewEnabled(void);
|
||||
|
||||
// C-linkage facade over SPDLogger so consumers can dynamic-load these via
|
||||
// GetProcAddress without depending on the C++ class ABI. Each call routes to
|
||||
// the process-wide SPDLogger singleton (thread-safe magic-static init).
|
||||
extern "C" {
|
||||
ANSLICENSE_API void ANSLogger_LogInfo (const char* src, const char* msg, const char* file, int line);
|
||||
ANSLICENSE_API void ANSLogger_LogError(const char* src, const char* msg, const char* file, int line);
|
||||
ANSLICENSE_API void ANSLogger_LogFatal(const char* src, const char* msg, const char* file, int line);
|
||||
}
|
||||
|
||||
// ANS_DBG: Debug logging macro for DebugView (OutputDebugStringA on Windows).
|
||||
// Usage: ANS_DBG("MyModule", "value=%d ptr=%p", val, ptr);
|
||||
// Output: [MyModule] value=42 ptr=0x1234
|
||||
|
||||
BIN
docs/ANSLIB_Logging_Guide.docx
Normal file
BIN
docs/ANSLIB_Logging_Guide.docx
Normal file
Binary file not shown.
412
docs/build_anslib_logging_guide.js
Normal file
412
docs/build_anslib_logging_guide.js
Normal file
@@ -0,0 +1,412 @@
|
||||
// Build ANSLIB Logging Guide.docx — examples document for developers.
|
||||
// Run: node build_anslib_logging_guide.js
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {
|
||||
Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
|
||||
AlignmentType, LevelFormat, HeadingLevel, BorderStyle, WidthType,
|
||||
ShadingType, PageOrientation
|
||||
} = require('docx');
|
||||
|
||||
// -------- helpers ----------------------------------------------------------
|
||||
const FONT_BODY = "Calibri";
|
||||
const FONT_CODE = "Consolas";
|
||||
|
||||
function p(text, opts = {}) {
|
||||
return new Paragraph({
|
||||
spacing: { after: 120 },
|
||||
...opts,
|
||||
children: [new TextRun({ text, font: FONT_BODY, size: 22, ...(opts.run || {}) })],
|
||||
});
|
||||
}
|
||||
|
||||
function h1(text) {
|
||||
return new Paragraph({
|
||||
heading: HeadingLevel.HEADING_1,
|
||||
spacing: { before: 360, after: 200 },
|
||||
children: [new TextRun({ text, font: FONT_BODY, size: 36, bold: true, color: "1F3864" })],
|
||||
});
|
||||
}
|
||||
|
||||
function h2(text) {
|
||||
return new Paragraph({
|
||||
heading: HeadingLevel.HEADING_2,
|
||||
spacing: { before: 280, after: 160 },
|
||||
children: [new TextRun({ text, font: FONT_BODY, size: 28, bold: true, color: "2E74B5" })],
|
||||
});
|
||||
}
|
||||
|
||||
function h3(text) {
|
||||
return new Paragraph({
|
||||
heading: HeadingLevel.HEADING_3,
|
||||
spacing: { before: 200, after: 100 },
|
||||
children: [new TextRun({ text, font: FONT_BODY, size: 24, bold: true, color: "404040" })],
|
||||
});
|
||||
}
|
||||
|
||||
// Single-line inline-code style (uses gray-ish background via shading isn't applied to runs;
|
||||
// we just use a monospace font + light shading via a wrapping single-cell table when block).
|
||||
function codeLine(text) {
|
||||
return new TextRun({ text, font: FONT_CODE, size: 20 });
|
||||
}
|
||||
|
||||
// Multi-line code block — render as a one-cell table with light-gray fill,
|
||||
// each source line becomes a paragraph inside the cell.
|
||||
function codeBlock(source) {
|
||||
const lines = source.replace(/\r\n/g, "\n").split("\n");
|
||||
const paras = lines.map(line =>
|
||||
new Paragraph({
|
||||
spacing: { after: 0, line: 260 },
|
||||
children: [new TextRun({ text: line || " ", font: FONT_CODE, size: 20 })],
|
||||
})
|
||||
);
|
||||
return new Table({
|
||||
width: { size: 9360, type: WidthType.DXA },
|
||||
columnWidths: [9360],
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
width: { size: 9360, type: WidthType.DXA },
|
||||
shading: { fill: "F4F4F4", type: ShadingType.CLEAR },
|
||||
margins: { top: 120, bottom: 120, left: 180, right: 180 },
|
||||
borders: {
|
||||
top: { style: BorderStyle.SINGLE, size: 4, color: "DDDDDD" },
|
||||
bottom: { style: BorderStyle.SINGLE, size: 4, color: "DDDDDD" },
|
||||
left: { style: BorderStyle.SINGLE, size: 4, color: "DDDDDD" },
|
||||
right: { style: BorderStyle.SINGLE, size: 4, color: "DDDDDD" },
|
||||
},
|
||||
children: paras,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function bullet(text, runs = null) {
|
||||
return new Paragraph({
|
||||
numbering: { reference: "bullets", level: 0 },
|
||||
spacing: { after: 80 },
|
||||
children: runs || [new TextRun({ text, font: FONT_BODY, size: 22 })],
|
||||
});
|
||||
}
|
||||
|
||||
function spacer() {
|
||||
return new Paragraph({ spacing: { after: 120 }, children: [new TextRun(" ")] });
|
||||
}
|
||||
|
||||
// ---- table builder for the cheat sheets -----------------------------------
|
||||
const CELL_BORDER = { style: BorderStyle.SINGLE, size: 4, color: "BFBFBF" };
|
||||
const CELL_BORDERS = { top: CELL_BORDER, bottom: CELL_BORDER, left: CELL_BORDER, right: CELL_BORDER };
|
||||
|
||||
function headerCell(text, width) {
|
||||
return new TableCell({
|
||||
width: { size: width, type: WidthType.DXA },
|
||||
borders: CELL_BORDERS,
|
||||
shading: { fill: "1F3864", type: ShadingType.CLEAR },
|
||||
margins: { top: 100, bottom: 100, left: 140, right: 140 },
|
||||
children: [new Paragraph({
|
||||
children: [new TextRun({ text, font: FONT_BODY, size: 22, bold: true, color: "FFFFFF" })],
|
||||
})],
|
||||
});
|
||||
}
|
||||
|
||||
function bodyCell(text, width, opts = {}) {
|
||||
const runs = Array.isArray(text)
|
||||
? text
|
||||
: [new TextRun({ text, font: opts.code ? FONT_CODE : FONT_BODY, size: 20 })];
|
||||
return new TableCell({
|
||||
width: { size: width, type: WidthType.DXA },
|
||||
borders: CELL_BORDERS,
|
||||
shading: opts.shade ? { fill: opts.shade, type: ShadingType.CLEAR } : undefined,
|
||||
margins: { top: 80, bottom: 80, left: 140, right: 140 },
|
||||
children: [new Paragraph({ children: runs })],
|
||||
});
|
||||
}
|
||||
|
||||
function sheetTable(columnWidths, headers, rows) {
|
||||
const total = columnWidths.reduce((a, b) => a + b, 0);
|
||||
return new Table({
|
||||
width: { size: total, type: WidthType.DXA },
|
||||
columnWidths,
|
||||
rows: [
|
||||
new TableRow({
|
||||
tableHeader: true,
|
||||
children: headers.map((h, i) => headerCell(h, columnWidths[i])),
|
||||
}),
|
||||
...rows.map(row =>
|
||||
new TableRow({
|
||||
children: row.map((c, i) => {
|
||||
if (c && typeof c === 'object' && c.runs) {
|
||||
return bodyCell(c.runs, columnWidths[i], c.opts || {});
|
||||
}
|
||||
return bodyCell(c, columnWidths[i]);
|
||||
}),
|
||||
})
|
||||
),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Document content
|
||||
// =========================================================================
|
||||
const children = [];
|
||||
|
||||
// ---------- Title ----------
|
||||
children.push(new Paragraph({
|
||||
spacing: { after: 80 },
|
||||
alignment: AlignmentType.LEFT,
|
||||
children: [new TextRun({ text: "ANSLIB Logging API", font: FONT_BODY, size: 48, bold: true, color: "1F3864" })],
|
||||
}));
|
||||
children.push(new Paragraph({
|
||||
spacing: { after: 360 },
|
||||
children: [new TextRun({ text: "Developer guide — usage examples for ANSLIB_DBG, LogInfo, LogError, LogFatal", font: FONT_BODY, size: 24, italics: true, color: "595959" })],
|
||||
}));
|
||||
|
||||
// ---------- Overview ----------
|
||||
children.push(h1("Overview"));
|
||||
children.push(p("ANSLIB exposes a small logging API that consumers can use without linking against ANSLicensingSystem.dll or any spdlog headers. All symbols are dynamically loaded inside ANSLIB; users only need to include ANSLIB.h and link ANSLIB.lib."));
|
||||
children.push(p("Two flavours are provided:"));
|
||||
children.push(bullet("ANSLIB_DBG — a printf-style macro for trace breadcrumbs that can be turned on or off remotely (DebugView-style logging)."));
|
||||
children.push(bullet("ANSCENTER::ANSLIB::LogInfo / LogError / LogFatal — leveled logging that writes through SPDLogger to the rolling log file."));
|
||||
|
||||
// ---------- Setup ----------
|
||||
children.push(h1("Setup"));
|
||||
children.push(p("Same prerequisites for every example below. Add this to any consumer translation unit:"));
|
||||
children.push(codeBlock(
|
||||
`#include "ANSLIB.h" // brings everything: static methods + ANSLIB_DBG macro
|
||||
|
||||
// Optional alias to shorten call sites:
|
||||
using LOG = ANSCENTER::ANSLIB;`));
|
||||
children.push(p("No other includes, no extra .lib to link beyond ANSLIB.lib."));
|
||||
|
||||
// ---------- Example 1 ----------
|
||||
children.push(h1("Example 1 — Function entry/exit (ANSLIB_DBG, the workhorse)"));
|
||||
children.push(codeBlock(
|
||||
`int LoadConfig(const std::string& path) {
|
||||
ANSLIB_DBG("Config", "LoadConfig entry path=%s", path.c_str());
|
||||
|
||||
if (path.empty()) {
|
||||
ANSLIB_DBG("Config", "empty path, bailing");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rc = ParseFile(path);
|
||||
ANSLIB_DBG("Config", "ParseFile returned rc=%d", rc);
|
||||
return rc;
|
||||
}`));
|
||||
children.push(p("DebugView output (only when the gate is on):"));
|
||||
children.push(codeBlock(
|
||||
`[Config] LoadConfig entry path=C:\\models\\car.cfg
|
||||
[Config] ParseFile returned rc=0`));
|
||||
children.push(p("This is the drop-in replacement for ANS_DBG. Use it for breadcrumb logging that you want to be able to flip on/off remotely."));
|
||||
|
||||
// ---------- Example 2 ----------
|
||||
children.push(h1("Example 2 — Error path (LogError, persisted to spdlog file)"));
|
||||
children.push(p("When something goes wrong and you want it in the rolling log file (not just DebugView), use LogError:"));
|
||||
children.push(codeBlock(
|
||||
`int InitEngine(const std::string& modelPath) {
|
||||
auto* engine = ANSCENTER::ANSLIB::Create();
|
||||
if (!engine) {
|
||||
LOG::LogError("InitEngine", "ANSLIB::Create returned null", __FILE__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string labels;
|
||||
int rc = engine->Initialize("license-key", modelPath.c_str(), "",
|
||||
0.5f, 0.5f, 0.4f, 8, 5, 1, labels);
|
||||
if (rc < 0) {
|
||||
char detail[256];
|
||||
snprintf(detail, sizeof(detail),
|
||||
"Initialize failed rc=%d model=%s", rc, modelPath.c_str());
|
||||
LOG::LogError("InitEngine", detail, __FILE__, __LINE__);
|
||||
ANSCENTER::ANSLIB::Destroy(engine);
|
||||
return rc;
|
||||
}
|
||||
|
||||
LOG::LogInfo("InitEngine", "engine ready", __FILE__, __LINE__);
|
||||
return 0;
|
||||
}`));
|
||||
children.push(p("Rolling log file output (ANSLIB.log or whichever name spdlog has):"));
|
||||
children.push(codeBlock(
|
||||
`[2026-04-26 14:32:11.483] [error] [foo.cpp] [42] InitEngine: Initialize failed rc=-1 model=C:\\models\\car.zip
|
||||
[2026-04-26 14:32:11.484] [info] [foo.cpp] [50] InitEngine: engine ready`));
|
||||
|
||||
// ---------- Example 3 ----------
|
||||
children.push(h1("Example 3 — Fatal / unrecoverable (LogFatal)"));
|
||||
children.push(p("Use when the process can't continue — e.g. license check failed, GPU lost, mandatory model missing:"));
|
||||
children.push(codeBlock(
|
||||
`void OnGpuDeviceLost() {
|
||||
LOG::LogFatal("Engine",
|
||||
"CUDA device lost, cannot continue inference",
|
||||
__FILE__, __LINE__);
|
||||
std::abort(); // or whatever your shutdown path is
|
||||
}`));
|
||||
|
||||
// ---------- Example 4 ----------
|
||||
children.push(h1("Example 4 — Mixing both (recommended pattern)"));
|
||||
children.push(p("Use ANSLIB_DBG for the chatty trace breadcrumbs and LogInfo / LogError / LogFatal for things you want preserved in the file:"));
|
||||
children.push(codeBlock(
|
||||
`int RunBatch(const std::vector<cv::Mat>& frames) {
|
||||
ANSLIB_DBG("Batch", "RunBatch entry frames=%zu", frames.size());
|
||||
|
||||
if (frames.empty()) {
|
||||
LOG::LogError("Batch", "called with empty frame list", __FILE__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int processed = 0;
|
||||
for (const auto& f : frames) {
|
||||
ANSLIB_DBG("Batch", "processing frame %d (%dx%d)", processed, f.cols, f.rows);
|
||||
// ...inference...
|
||||
++processed;
|
||||
}
|
||||
|
||||
char summary[128];
|
||||
snprintf(summary, sizeof(summary), "RunBatch OK processed=%d", processed);
|
||||
LOG::LogInfo("Batch", summary, __FILE__, __LINE__);
|
||||
|
||||
ANSLIB_DBG("Batch", "RunBatch exit");
|
||||
return processed;
|
||||
}`));
|
||||
children.push(p("DebugView shows the full trace; the log file keeps just the entry/exit summary. This keeps the file small but lets you toggle full tracing remotely when something goes wrong."));
|
||||
|
||||
// ---------- Example 5 ----------
|
||||
children.push(h1("Example 5 — Gating expensive log preparation"));
|
||||
children.push(p("ANSLIB_DBG already gates the snprintf for you. But if preparing the message itself is expensive (serializing a struct, dumping a Mat, walking a tree), check the gate first manually:"));
|
||||
children.push(codeBlock(
|
||||
`void DumpDetections(const std::vector<ANSCENTER::Object>& dets) {
|
||||
if (!ANSCENTER::ANSLIB::IsDebugViewEnabled()) return; // skip the whole prep
|
||||
|
||||
std::string dump;
|
||||
for (const auto& d : dets) {
|
||||
dump += d.className + "(" + std::to_string(d.confidence) + ") ";
|
||||
}
|
||||
ANSLIB_DBG("Detections", "%zu objects: %s", dets.size(), dump.c_str());
|
||||
}`));
|
||||
children.push(p("This skips the entire string-building loop when the gate is off."));
|
||||
|
||||
// ---------- Example 6 ----------
|
||||
children.push(h1("Example 6 — Logging from a class method"));
|
||||
children.push(p("The __FILE__ / __LINE__ defaults make instance methods cleaner if you don't need them:"));
|
||||
children.push(codeBlock(
|
||||
`class MyDetector {
|
||||
public:
|
||||
void Process(const cv::Mat& img) {
|
||||
ANSLIB_DBG("MyDetector", "Process %dx%d", img.cols, img.rows);
|
||||
|
||||
if (img.empty()) {
|
||||
// No file/line — defaults to "" / 0
|
||||
LOG::LogError("MyDetector", "empty image input");
|
||||
return;
|
||||
}
|
||||
// ...
|
||||
}
|
||||
};`));
|
||||
|
||||
// ---------- Example 7 ----------
|
||||
children.push(h1("Example 7 — Macro for \"log everywhere with caller info\" (optional)"));
|
||||
children.push(p("If you want every LogInfo call to auto-include __FILE__ / __LINE__, define a one-liner in your own code:"));
|
||||
children.push(codeBlock(
|
||||
`#define MYAPP_INFO(tag, msg) ::ANSCENTER::ANSLIB::LogInfo (tag, msg, __FILE__, __LINE__)
|
||||
#define MYAPP_ERROR(tag, msg) ::ANSCENTER::ANSLIB::LogError(tag, msg, __FILE__, __LINE__)
|
||||
|
||||
// Usage:
|
||||
MYAPP_INFO ("Boot", "starting up");
|
||||
MYAPP_ERROR("Net", "connection refused");`));
|
||||
children.push(p("This keeps call sites short while preserving file/line tracking in the rolling log."));
|
||||
|
||||
// ---------- Cheat sheet 1 ----------
|
||||
children.push(h1("Cheat sheet — pick the right call"));
|
||||
children.push(sheetTable(
|
||||
[3000, 3360, 3000],
|
||||
["Want to log…", "Use", "Goes to"],
|
||||
[
|
||||
["Trace / breadcrumb that's toggled per-incident",
|
||||
{ runs: [codeLine('ANSLIB_DBG("Tag", "fmt %d", x)')] },
|
||||
"DebugView + stderr (when gate is on)"],
|
||||
["Notable event (started, finished, config loaded)",
|
||||
{ runs: [codeLine('LOG::LogInfo("Tag", "msg", __FILE__, __LINE__)')] },
|
||||
"spdlog file + console"],
|
||||
["Recoverable error (will retry, will skip, etc.)",
|
||||
{ runs: [codeLine('LOG::LogError("Tag", "msg", __FILE__, __LINE__)')] },
|
||||
"spdlog file + console"],
|
||||
["Unrecoverable (about to crash / abort)",
|
||||
{ runs: [codeLine('LOG::LogFatal("Tag", "msg", __FILE__, __LINE__)')] },
|
||||
"spdlog file + console (critical level)"],
|
||||
]
|
||||
));
|
||||
children.push(spacer());
|
||||
|
||||
// ---------- Cheat sheet 2 ----------
|
||||
children.push(h1("Toggling the DebugView gate at runtime"));
|
||||
children.push(p("LogInfo / LogError / LogFatal are not gated — they always go to the spdlog file. Only ANSLIB_DBG is gated by the mechanism below."));
|
||||
children.push(spacer());
|
||||
children.push(sheetTable(
|
||||
[4680, 4680],
|
||||
["Action", "Effect"],
|
||||
[
|
||||
[{ runs: [new TextRun({ text: "Drop file ", font: FONT_BODY, size: 20 }),
|
||||
codeLine("C:\\ProgramData\\ANSCENTER\\ansvisdebugview.txt")] },
|
||||
{ runs: [new TextRun({ text: "Turns ", font: FONT_BODY, size: 20 }),
|
||||
codeLine("ANSLIB_DBG"), new TextRun({ text: " (and ", font: FONT_BODY, size: 20 }),
|
||||
codeLine("ANS_DBG"), new TextRun({ text: ") ON within ~2s", font: FONT_BODY, size: 20 })] }],
|
||||
["Delete that file", "Turns them OFF within ~2s"],
|
||||
[{ runs: [codeLine("set ANSCENTER_DBGVIEW=1"), new TextRun({ text: " (or ", font: FONT_BODY, size: 20 }),
|
||||
codeLine("=0"), new TextRun({ text: ")", font: FONT_BODY, size: 20 })] },
|
||||
"Forces ON or OFF — overrides the file"],
|
||||
]
|
||||
));
|
||||
|
||||
// ---------- Operational notes ----------
|
||||
children.push(h1("Operational notes"));
|
||||
children.push(bullet("All four entry points (ANSLIB_DBG, LogInfo, LogError, LogFatal) are thread-safe and require no instance — they're static methods that lazy-resolve their underlying DLL symbols on first call."));
|
||||
children.push(bullet("If ANSLicensingSystem.dll is missing on the target machine, every logging call silently no-ops (no crash, no exception). The application keeps running."));
|
||||
children.push(bullet("ANSLIB_DBG output format is identical to the original ANS_DBG: \"[tag] formatted\\n\" emitted to both OutputDebugStringA and stderr."));
|
||||
children.push(bullet("Don't log from static destructors. DLL unload order during process shutdown is undefined on Windows — calling into the licensing DLL after it has been unmapped will crash."));
|
||||
children.push(bullet("To strip ANSLIB_DBG entirely at compile time (for performance-critical builds), define -DANSCORE_DEBUGVIEW=0 when compiling the consumer translation unit. The macro becomes ((void)0)."));
|
||||
|
||||
// =========================================================================
|
||||
// Document assembly
|
||||
// =========================================================================
|
||||
const doc = new Document({
|
||||
styles: {
|
||||
default: { document: { run: { font: FONT_BODY, size: 22 } } },
|
||||
paragraphStyles: [
|
||||
{ id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true,
|
||||
run: { size: 36, bold: true, font: FONT_BODY, color: "1F3864" },
|
||||
paragraph: { spacing: { before: 360, after: 200 }, outlineLevel: 0 } },
|
||||
{ id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true,
|
||||
run: { size: 28, bold: true, font: FONT_BODY, color: "2E74B5" },
|
||||
paragraph: { spacing: { before: 280, after: 160 }, outlineLevel: 1 } },
|
||||
{ id: "Heading3", name: "Heading 3", basedOn: "Normal", next: "Normal", quickFormat: true,
|
||||
run: { size: 24, bold: true, font: FONT_BODY, color: "404040" },
|
||||
paragraph: { spacing: { before: 200, after: 100 }, outlineLevel: 2 } },
|
||||
],
|
||||
},
|
||||
numbering: {
|
||||
config: [
|
||||
{ reference: "bullets",
|
||||
levels: [{ level: 0, format: LevelFormat.BULLET, text: "•", alignment: AlignmentType.LEFT,
|
||||
style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] },
|
||||
],
|
||||
},
|
||||
sections: [{
|
||||
properties: {
|
||||
page: {
|
||||
size: { width: 12240, height: 15840 },
|
||||
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
|
||||
},
|
||||
},
|
||||
children,
|
||||
}],
|
||||
});
|
||||
|
||||
const outPath = path.join(__dirname, "ANSLIB_Logging_Guide.docx");
|
||||
Packer.toBuffer(doc).then(buf => {
|
||||
fs.writeFileSync(outPath, buf);
|
||||
console.log("Wrote", outPath, "(" + buf.length + " bytes)");
|
||||
});
|
||||
BIN
docs/~$SLIB_Logging_Guide.docx
Normal file
BIN
docs/~$SLIB_Logging_Guide.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user