diff --git a/.vscode/settings.json b/.vscode/settings.json index fd3fa8b..26ef083 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,6 +19,7 @@ "proto", "rtsp", "tongwei", + "TVTSDK", "valuestrings", "wireshark", "YLOG", diff --git a/source/device.ts b/source/device.ts index 5295757..2d53e98 100644 --- a/source/device.ts +++ b/source/device.ts @@ -3,6 +3,7 @@ import { randomUUID } from 'node:crypto' import { existsSync, mkdirSync } from 'node:fs' import { platform } from 'node:os' import { dirname } from 'node:path' +import pSeries from 'p-series' import { auth, measure } from './decorators/index.ts' import { parseBuildDate } from './helpers/date.ts' import { validateIp, validatePort } from './helpers/validators.ts' @@ -36,71 +37,64 @@ export class Device { readonly #sdkVersion: string readonly #sdkBuild: string + private constructor(ip: string, port: number, settings: Settings | undefined, sdkVersion: string, sdkBuild: string) { + this.ip = ip + this.port = port + this.uuid = settings?.uuid ?? randomUUID() + + if (settings) { + this.#connectionTimeoutMs = settings.connectionTimeoutMs ?? this.#connectionTimeoutMs + this.#maxRetries = settings.maxRetries ?? this.#maxRetries + this.#reconnectIntervalMs = settings.reconnectIntervalMs ?? this.#reconnectIntervalMs + this.#isReconnectEnabled = settings.isReconnectEnabled ?? this.#isReconnectEnabled + } + + this.#sdkVersion = sdkVersion + this.#sdkBuild = sdkBuild + + log(`Device ${this.uuid} created with IP: ${this.ip}:${this.port}`) + } + /** - * Creates a new device. + * Creates and initializes a new Device instance. * * @param ip - The IP address of the device. * @param port - The port of the device. - * @param settings - The settings for the device. + * @param settings - Optional settings for the device. + * @returns A promise that resolves to an initialized Device instance * @throws {Error} If not running on Linux or initialization fails */ - constructor(ip: string, port = 9008, settings?: Settings) { + public static async create(ip: string, port = 9008, settings?: Settings): Promise { if (platform() !== 'linux') { throw new Error('This SDK is only supported on Linux platforms') } - this.ip = validateIp(ip) - this.port = validatePort(port) - this.uuid = settings?.uuid ?? randomUUID() - - if (settings) { - this.#connectionTimeoutMs = settings.connectionTimeoutMs ?? this.#connectionTimeoutMs - this.#maxRetries = settings.maxRetries ?? this.#maxRetries - this.#reconnectIntervalMs = settings.reconnectIntervalMs ?? this.#reconnectIntervalMs - this.#isReconnectEnabled = settings.isReconnectEnabled ?? this.#isReconnectEnabled - } + const validatedIp = validateIp(ip) + const validatedPort = validatePort(port) - log(`Initializing device ${this.uuid} with IP: ${this.ip}:${this.port}`) + log(`Initializing device with IP: ${validatedIp}:${validatedPort}`) // Initialize the SDK - if ( - !sdk.init() || - !sdk.setConnectTimeout(this.#connectionTimeoutMs, this.#maxRetries) || - !sdk.setReconnectInterval(this.#reconnectIntervalMs, this.#isReconnectEnabled) - ) { - const error = this.getLastError() - log(`Failed to initialize device ${this.uuid}: ${error}`) + const [initResult, timeoutResult, reconnectResult] = await pSeries([ + () => sdk.init(), + () => sdk.setConnectTimeout(settings?.connectionTimeoutMs ?? 5000, settings?.maxRetries ?? 3), + () => sdk.setReconnectInterval(settings?.reconnectIntervalMs ?? 30000, settings?.isReconnectEnabled ?? true) + ]) + + if (!initResult || !timeoutResult || !reconnectResult) { + const errorCode = await sdk.getLastError() + const error = NET_SDK_ERROR[errorCode] ?? 'Unknown error' + log(`Failed to initialize device: ${error}`) throw new Error(error) } // Get SDK version information - const sdkVersion = sdk.getSDKVersion() - const buildVersion = sdk.getSDKBuildVersion() - this.#sdkVersion = `0x${sdkVersion.toString(16)} (${sdkVersion})` - this.#sdkBuild = `${parseBuildDate(buildVersion.toString())} (${buildVersion})` + const [sdkVersion, buildVersion] = await Promise.all([sdk.getSDKVersion(), sdk.getSDKBuildVersion()]) - log(`${this.uuid} device initialized successfully!`) - } - - /** - * Logout and dispose of the SDK resources. - * - * @returns A boolean indicating whether the disposal was successful. - */ - dispose(): boolean { - log(`Disposing device ${this.uuid}...`) + const formattedSdkVersion = `0x${sdkVersion.toString(16)} (${sdkVersion})` + const formattedSdkBuild = `${parseBuildDate(buildVersion.toString())} (${buildVersion})` - try { - if (this.userId) { - this.logout() - } - const result = sdk.cleanup() - log(`Device ${this.uuid} disposed successfully`) - return result - } catch (error) { - log(`Failed to dispose device ${this.uuid}: ${error}`) - return false - } + return new Device(validatedIp, validatedPort, settings, formattedSdkVersion, formattedSdkBuild) } /** @@ -132,11 +126,13 @@ export class Device { } /** - * Getter for device information. + * Gets the device information. + * + * @returns A promise that resolves to the device information */ @auth - get info(): DeviceInfo { - sdk.getDeviceInfo(this.userId, this.#deviceInfo) + async getInfo(): Promise { + await sdk.getDeviceInfo(this.userId, this.#deviceInfo) return this.#deviceInfo } @@ -145,17 +141,17 @@ export class Device { * * @param user - The username. * @param pass - The password. - * @returns A boolean indicating whether the login was successful. + * @returns A promise that resolves to a boolean indicating whether the login was successful. * @throws An error if the login fails. */ @measure - login(user: string, pass: string): boolean { + async login(user: string, pass: string): Promise { log(`Logging in to device ${this.uuid} with user: ${user}`) try { - this.userId = sdk.login(this.ip, this.port, user, pass, this.#deviceInfo) + this.userId = await sdk.login(this.ip, this.port, user, pass, this.#deviceInfo) if (this.userId === -1) { - throw new Error(this.getLastError()) + throw new Error(await this.getLastError()) } log(`Successfully logged in to device ${this.uuid}`) return Boolean(this.userId) @@ -168,13 +164,13 @@ export class Device { /** * Logs out of the device. * - * @returns A boolean indicating whether the logout was successful. + * @returns A promise that resolves to a boolean indicating whether the logout was successful. */ @auth - logout(): boolean { + async logout(): Promise { log(`Logging out from device ${this.uuid}`) try { - const result = sdk.logout(this.userId) + const result = await sdk.logout(this.userId) if (result) { log(`Successfully logged out from device ${this.uuid}`) } else { @@ -191,17 +187,23 @@ export class Device { * Triggers an alarm on the device. * * @param value - A boolean indicating what state to set the alarm to. - * @returns A boolean indicating whether the alarm was triggered successfully. + * @returns A promise that resolves to a boolean indicating whether the alarm was triggered successfully. */ @auth - triggerAlarm(value: boolean): boolean { + async triggerAlarm(value: boolean): Promise { log(`Triggering alarm on device ${this.uuid} with value: ${value}`) try { // @TODO: get alarm channels from device info const alarmChannels = [0] const alarmValues = [value ? 1 : 0] - const result = sdk.triggerAlarm(this.userId, alarmChannels, alarmValues, alarmChannels.length, this.#isAlarmOpen) + const result = await sdk.triggerAlarm( + this.userId, + alarmChannels, + alarmValues, + alarmChannels.length, + this.#isAlarmOpen + ) if (result) { log(`Successfully triggered alarm on device ${this.uuid}`) @@ -221,10 +223,10 @@ export class Device { * * @param channel - The channel number to save a snapshot of. * @param filePath - The path where the snapshot will be saved. - * @returns - Returns true if the snapshot was successfully saved, false otherwise. + * @returns A promise that resolves to a boolean indicating if the snapshot was successfully saved. */ @auth - saveSnapshot(channel: number, filePath: string): boolean { + async saveSnapshot(channel: number, filePath: string): Promise { log(`Saving snapshot from device ${this.uuid} channel ${channel} to ${filePath}`) try { @@ -235,7 +237,7 @@ export class Device { mkdirSync(dirPath, { recursive: true }) } - const result = sdk.captureJPEGFile_V2(this.userId, channel, filePath) + const result = await sdk.captureJPEGFile_V2(this.userId, channel, filePath) if (result) { log(`Successfully saved snapshot from device ${this.uuid}`) @@ -253,9 +255,31 @@ export class Device { /** * Gets the last error that occurred. * - * @returns A string describing the last error. + * @returns A promise that resolves to a string describing the last error. */ - getLastError(): string { - return NET_SDK_ERROR[sdk.getLastError()] ?? 'Unknown error' + async getLastError(): Promise { + const errorCode = await sdk.getLastError() + return NET_SDK_ERROR[errorCode] ?? 'Unknown error' + } + + /** + * Logout and dispose of the SDK resources. + * + * @returns A promise that resolves to a boolean indicating whether the disposal was successful. + */ + async dispose(): Promise { + log(`Disposing device ${this.uuid}...`) + + try { + if (this.userId) { + await this.logout() + } + const result = await sdk.cleanup() + log(`Device ${this.uuid} disposed successfully`) + return result + } catch (error) { + log(`Failed to dispose device ${this.uuid}: ${error}`) + return false + } } } diff --git a/source/lib/sdk.ts b/source/lib/sdk.ts index 6cdbbfd..4a23559 100644 --- a/source/lib/sdk.ts +++ b/source/lib/sdk.ts @@ -1,37 +1,42 @@ -import koffi from 'koffi' +import type { IKoffiLib } from 'koffi' import { platform } from 'node:os' import { resolve } from 'node:path' import { LPNET_SDK_DEVICEINFO, NET_SDK_DEVICE_DISCOVERY_INFO, NET_SDK_IPC_DEVICE_INFO } from './struct/index.ts' import type { DeviceInfo, LOG_LEVEL } from './types.ts' -type SDK = { +interface TVTSDK { // ✅︎ DWORD NET_SDK_GetSDKVersion(); - getSDKVersion: () => number + getSDKVersion: () => Promise // ✅︎ DWORD NET_SDK_GetSDKBuildVersion(); - getSDKBuildVersion: () => number + getSDKBuildVersion: () => Promise // int NET_SDK_DiscoverDevice(NET_SDK_DEVICE_DISCOVERY_INFO *pDeviceInfo, int bufNum, int waitSeconds = 3); - discoverDevice: (deviceInfo: DeviceInfo, bufNum: number, waitSeconds: number) => number + discoverDevice: (deviceInfo: DeviceInfo, bufNum: number, waitSeconds: number) => Promise // spell-checker: disable-next-line // ✅︎ BOOL NET_SDK_GetDeviceInfo(LONG lUserID, LPNET_SDK_DEVICEINFO pdecviceInfo); - getDeviceInfo: (userId: number, deviceInfo: DeviceInfo) => boolean + getDeviceInfo: (userId: number, deviceInfo: DeviceInfo) => Promise // BOOL NET_SDK_GetDeviceIPCInfo(LONG lUserID, NET_SDK_IPC_DEVICE_INFO *pDeviceIPCInfo, LONG lBuffSize, LONG *pIPCCount); - getDeviceIPCInfo: (userId: number, deviceIPCInfo: DeviceInfo, buffSize: number, ipcCount: number[]) => boolean + getDeviceIPCInfo: ( + userId: number, + deviceIPCInfo: DeviceInfo, + buffSize: number, + ipcCount: number[] + ) => Promise // ✅︎ BOOL NET_SDK_Init(); - init: () => boolean + init: () => Promise // ✅︎ BOOL NET_SDK_Cleanup(); - cleanup: () => boolean + cleanup: () => Promise // ✅︎ BOOL NET_SDK_SetConnectTime(DWORD dwWaitTime = 5000, DWORD dwTryTimes = 3); - setConnectTimeout: (waitTime: number, retryTimes: number) => boolean + setConnectTimeout: (waitTime: number, retryTimes: number) => Promise // ✅︎ BOOL NET_SDK_SetReconnect(DWORD dwInterval = 5000, BOOL bEnableRecon = TRUE); - setReconnectInterval: (interval: number, enableRecon: boolean) => boolean + setReconnectInterval: (interval: number, enableRecon: boolean) => Promise // ✅︎ LONG NET_SDK_Login(char *sDVRIP, WORD wDVRPort, char *sUserName, char *sPassword, LPNET_SDK_DEVICEINFO lpDeviceInfo); - login: (ip: string, port: number, username: string, password: string, deviceInfo: DeviceInfo) => number + login: (ip: string, port: number, username: string, password: string, deviceInfo: DeviceInfo) => Promise // ✅︎ BOOL NET_SDK_Logout(LONG lUserID) - logout: (userId: number) => boolean + logout: (userId: number) => Promise // LONG NET_SDK_SetupAlarmChan(LONG lUserID); - setupAlarmChanel: (userId: number) => number + setupAlarmChannel: (userId: number) => Promise // BOOL NET_SDK_CloseAlarmChan(LONG lAlarmHandle); - closeAlarmChanel: (alarmHandle: number) => boolean + closeAlarmChannel: (alarmHandle: number) => Promise // ✅︎ BOOL NET_SDK_SetDeviceManualAlarm(LONG lUserID, LONG *pAramChannel, LONG *pValue, LONG lAramChannelCount, BOOL bAlarmOpen); triggerAlarm: ( userId: number, @@ -39,13 +44,13 @@ type SDK = { value: number[], channelCount: number, alarmOpen: boolean - ) => boolean + ) => Promise // BOOL NET_SDK_GetConfigFile(LONG lUserID, char *sFileName); - getConfigFile: (userId: number, fileName: string) => boolean + getConfigFile: (userId: number, fileName: string) => Promise // BOOL NET_SDK_SetConfigFile(LONG lUserID, char *sFileName); - setConfigFile: (userId: number, fileName: string) => boolean + setConfigFile: (userId: number, fileName: string) => Promise // ✅︎ DWORD NET_SDK_GetLastError() - getLastError: () => number + getLastError: () => Promise /** * Probably windows only. Based on de compiled code from the SDK: * ... @@ -58,251 +63,354 @@ type SDK = { * ... */ // BOOL NET_SDK_SetLogToFile(BOOL bLogEnable = FALSE, char *strLogDir = NULL, BOOL bAutoDel = TRUE, int logLevel = YLOG_DEBUG); - setLogToFile: (logEnable: boolean, logDir: string, autoDel: boolean, logLevel: LOG_LEVEL) => true + setLogToFile: (logEnable: boolean, logDir: string, autoDel: boolean, logLevel: LOG_LEVEL) => Promise // BOOL NET_SDK_SaveLiveData(POINTERHANDLE lLiveHandle, char *sFileName); - startSavingLiveStream: (liveHandle: number, fileName: string) => boolean + startSavingLiveStream: (liveHandle: number, fileName: string) => Promise // BOOL NET_SDK_StopSaveLiveData(POINTERHANDLE lLiveHandle); - stopSavingLiveStream: (liveHandle: number) => boolean + stopSavingLiveStream: (liveHandle: number) => Promise // ✅︎ BOOL NET_SDK_CaptureJPEGFile_V2(LONG lUserID, LONG lChannel, char *sPicFileName); - captureJPEGFile_V2: (userId: number, channel: number, fileName: string) => boolean + captureJPEGFile_V2: (userId: number, channel: number, fileName: string) => Promise } -const isLinux = platform() === 'linux' +export class SDK implements TVTSDK { + static #instance: SDK + #_koffi: typeof import('koffi') | null = null + #_lib: IKoffiLib | null = null -if (!isLinux) { - throw new Error('This SDK is only supported on Linux platforms') -} + private constructor() { + if (!this.isLinux) { + throw new Error('This SDK is only supported on Linux platforms') + } + } -/** - * Creates a lazy-loaded SDK instance for interfacing with TVT devices. - * The SDK is only loaded when first accessed and subsequent calls use the cached instance. - * This SDK is specifically designed for Linux systems and provides methods for device control and monitoring. - * - * @returns An object containing all available SDK methods - * @throws {Error} If the library cannot be loaded or if running on non-Linux systems - */ -const createSDK = (): SDK => { - let lib: ReturnType | null = null + public static getInstance(): SDK { + if (!SDK.#instance) { + SDK.#instance = new SDK() + } + return SDK.#instance + } + + private get isLinux(): boolean { + return platform() === 'linux' + } + + private get koffi() { + return (async () => { + try { + if (!this.#_koffi) { + this.#_koffi = (await import('koffi')).default + } + return this.#_koffi + } catch (e) { + console.error('Failed to load koffi:', e) + throw new Error('Failed to initialize koffi') + } + })() + } + + private get lib() { + return (async () => { + try { + if (!this.#_lib) { + const koffi = await this.koffi + const path = resolve(import.meta.dirname, '../../', 'bin/linux/libdvrnetsdk.so') + this.#_lib = koffi.load(path) + } + return this.#_lib + } catch (e) { + console.error('Failed to load library:', e) + throw new Error('Failed to initialize TVT SDK library') + } + })() + } /** - * Gets or initializes the library instance. + * Gets the SDK version number. * - * @returns The loaded shared library instance + * @returns The SDK version in hex format */ - const getLib = () => { - if (!lib) { - const path = resolve(import.meta.dirname, '../../', 'bin/linux/libdvrnetsdk.so') - lib = koffi.load(path) - } - return lib + public async getSDKVersion(): Promise { + return (await this.lib).func('NET_SDK_GetSDKVersion', 'uint32_t', [])() } - return { - /** - * Gets the SDK version number. - * - * @returns {number} The SDK version in hex format - */ - getSDKVersion: getLib().func('NET_SDK_GetSDKVersion', 'uint32_t', []), - - /** - * Gets the SDK build version (typically represents build date). - * - * @returns {number} The SDK build version - */ - getSDKBuildVersion: getLib().func('NET_SDK_GetSDKBuildVersion', 'uint32_t', []), + /** + * Gets the SDK build version (typically represents build date). + * + * @returns The SDK build version + */ + public async getSDKBuildVersion(): Promise { + return (await this.lib).func('NET_SDK_GetSDKBuildVersion', 'uint32_t', [])() + } - /** - * Discovers TVT devices on the network. - * - * @param {DeviceInfo} deviceInfo - Buffer to store discovered device information - * @param {number} bufNum - Size of the buffer - * @param {number} waitSeconds - Time to wait for device responses - * @returns {number} Number of devices discovered - */ - discoverDevice: getLib().func('NET_SDK_DiscoverDevice', 'int', [ + /** + * Discovers TVT devices on the network. + * + * @param deviceInfo - Buffer to store discovered device information + * @param bufNum - Size of the buffer + * @param waitSeconds - Time to wait for device responses + * @returns Number of devices discovered + */ + public async discoverDevice(deviceInfo: DeviceInfo, bufNum: number, waitSeconds: number): Promise { + const koffi = await this.koffi + const lib = await this.lib + return lib.func('NET_SDK_DiscoverDevice', 'int', [ koffi.out(koffi.pointer(NET_SDK_DEVICE_DISCOVERY_INFO)), 'int', 'int' - ]), + ])(deviceInfo, bufNum, waitSeconds) + } - /** - * Gets detailed information about a connected device. - * - * @param {number} userId - User ID from successful login - * @param {DeviceInfo} deviceInfo - Buffer to store device information - * @returns {boolean} Success status - */ - getDeviceInfo: getLib().func('NET_SDK_GetDeviceInfo', 'bool', [ - 'long', - koffi.out(koffi.pointer(LPNET_SDK_DEVICEINFO)) - ]), + /** + * Gets detailed information about a connected device. + * + * @param userId - User ID from successful login + * @param deviceInfo - Buffer to store device information + * @returns Success status + */ + public async getDeviceInfo(userId: number, deviceInfo: DeviceInfo): Promise { + const koffi = await this.koffi + const lib = await this.lib + return lib.func('NET_SDK_GetDeviceInfo', 'bool', ['long', koffi.out(koffi.pointer(LPNET_SDK_DEVICEINFO))])( + userId, + deviceInfo + ) + } - /** - * Gets information about IPC devices connected to an NVR/DVR. - * - * @param {number} userId - User ID from successful login - * @param {DeviceInfo} deviceIPCInfo - Buffer to store IPC information - * @param {number} buffSize - Size of the buffer - * @param {number[]} ipcCount - Array to store the count of IPCs - * @returns {boolean} Success status - */ - getDeviceIPCInfo: getLib().func('NET_SDK_GetDeviceIPCInfo', 'bool', [ + /** + * Gets information about IPC devices connected to an NVR/DVR. + * + * @param userId - User ID from successful login + * @param deviceIPCInfo - Buffer to store IPC information + * @param buffSize - Size of the buffer + * @param ipcCount - Array to store the count of IPCs + * @returns Success status + */ + public async getDeviceIPCInfo( + userId: number, + deviceIPCInfo: DeviceInfo, + buffSize: number, + ipcCount: number[] + ): Promise { + const koffi = await this.koffi + const lib = await this.lib + return lib.func('NET_SDK_GetDeviceIPCInfo', 'bool', [ 'long', koffi.out(koffi.pointer(NET_SDK_IPC_DEVICE_INFO)), 'long', 'long *' - ]), + ])(userId, deviceIPCInfo, buffSize, ipcCount) + } - /** - * Initializes the SDK. Must be called before using any other functions. - * - * @returns {boolean} Success status - */ - init: getLib().func('NET_SDK_Init', 'bool', []), + /** + * Initializes the SDK. Must be called before using any other functions. + * + * @returns Success status + */ + public async init(): Promise { + return (await this.lib).func('NET_SDK_Init', 'bool', [])() + } - /** - * Cleans up and releases SDK resources. - * - * @returns {boolean} Success status - */ - cleanup: getLib().func('NET_SDK_Cleanup', 'bool', []), + /** + * Cleans up and releases SDK resources. + * + * @returns Success status + */ + public async cleanup(): Promise { + return (await this.lib).func('NET_SDK_Cleanup', 'bool', [])() + } - /** - * Sets connection timeout parameters. - * - * @param {number} waitTime - Wait time in milliseconds - * @param {number} retryTimes - Number of retry attempts - * @returns {boolean} Success status - */ - setConnectTimeout: getLib().func('NET_SDK_SetConnectTime', 'bool', ['uint32_t', 'uint32_t']), + /** + * Sets connection timeout parameters. + * + * @param waitTime - Wait time in milliseconds + * @param retryTimes - Number of retry attempts + * @returns Success status + */ + public async setConnectTimeout(waitTime: number, retryTimes: number): Promise { + return (await this.lib).func('NET_SDK_SetConnectTime', 'bool', ['uint32_t', 'uint32_t'])(waitTime, retryTimes) + } - /** - * Sets reconnection parameters. - * - * @param {number} interval - Reconnection interval in milliseconds - * @param {boolean} enableRecon - Enable/disable reconnection - * @returns {boolean} Success status - */ - setReconnectInterval: getLib().func('NET_SDK_SetReconnect', 'bool', ['uint32_t', 'bool']), + /** + * Sets reconnection parameters. + * + * @param interval - Reconnection interval in milliseconds + * @param enableRecon - Enable/disable reconnection + * @returns Success status + */ + public async setReconnectInterval(interval: number, enableRecon: boolean): Promise { + return (await this.lib).func('NET_SDK_SetReconnect', 'bool', ['uint32_t', 'bool'])(interval, enableRecon) + } - /** - * Logs into a device. - * - * @param {string} ip - Device IP address - * @param {number} port - Device port - * @param {string} username - Login username - * @param {string} password - Login password - * @param {DeviceInfo} deviceInfo - Buffer to store device information - * @returns {number} User ID if successful, -1 if failed - */ - login: getLib().func('NET_SDK_Login', 'long', [ + /** + * Logs into a device. + * + * @param ip - Device IP address + * @param port - Device port + * @param username - Login username + * @param password - Login password + * @param deviceInfo - Buffer to store device information + * @returns User ID if successful, -1 if failed + */ + public async login( + ip: string, + port: number, + username: string, + password: string, + deviceInfo: DeviceInfo + ): Promise { + const koffi = await this.koffi + const lib = await this.lib + return lib.func('NET_SDK_Login', 'long', [ 'string', 'uint16_t', 'string', 'string', koffi.out(koffi.pointer(LPNET_SDK_DEVICEINFO)) - ]), + ])(ip, port, username, password, deviceInfo) + } - /** - * Logs out from a device. - * - * @param {number} userId - User ID from successful login - * @returns {boolean} Success status - */ - logout: getLib().func('NET_SDK_Logout', 'bool', ['long']), + /** + * Logs out from a device. + * + * @param userId - User ID from successful login + * @returns Success status + */ + public async logout(userId: number): Promise { + return (await this.lib).func('NET_SDK_Logout', 'bool', ['long'])(userId) + } - /** - * Sets up an alarm channel. - * - * @param {number} userId - User ID from successful login - * @returns {number} Alarm handle if successful - */ - setupAlarmChanel: getLib().func('NET_SDK_SetupAlarmChan', 'long', ['long']), + /** + * Sets up an alarm channel. + * + * @param userId - User ID from successful login + * @returns Alarm handle if successful + */ + public async setupAlarmChannel(userId: number): Promise { + return (await this.lib).func('NET_SDK_SetupAlarmChan', 'long', ['long'])(userId) + } - /** - * Closes an alarm channel. - * - * @param {number} alarmHandle - Handle from setupAlarmChanel - * @returns {boolean} Success status - */ - closeAlarmChanel: getLib().func('NET_SDK_CloseAlarmChan', 'bool', ['long']), + /** + * Closes an alarm channel. + * + * @param alarmHandle - Handle from setupAlarmChannel + * @returns Success status + */ + public async closeAlarmChannel(alarmHandle: number): Promise { + return (await this.lib).func('NET_SDK_CloseAlarmChan', 'bool', ['long'])(alarmHandle) + } - /** - * Triggers manual alarms on specified channels. - * - * @param {number} userId - User ID from successful login - * @param {number[]} channel - Array of channel numbers - * @param {number[]} value - Array of alarm values - * @param {number} channelCount - Number of channels - * @param {boolean} alarmOpen - Alarm open/close state - * @returns {boolean} Success status - */ - triggerAlarm: getLib().func('NET_SDK_SetDeviceManualAlarm', 'bool', ['long', 'long *', 'long *', 'long', 'bool']), + /** + * Triggers manual alarms on specified channels. + * + * @param userId - User ID from successful login + * @param channel - Array of channel numbers + * @param value - Array of alarm values + * @param channelCount - Number of channels + * @param alarmOpen - Alarm open/close state + * @returns Success status + */ + public async triggerAlarm( + userId: number, + channel: number[], + value: number[], + channelCount: number, + alarmOpen: boolean + ): Promise { + return (await this.lib).func('NET_SDK_SetDeviceManualAlarm', 'bool', ['long', 'long *', 'long *', 'long', 'bool'])( + userId, + channel, + value, + channelCount, + alarmOpen + ) + } - /** - * Gets device configuration file. - * - * @param {number} userId - User ID from successful login - * @param {string} fileName - Path to save configuration file - * @returns {boolean} Success status - */ - getConfigFile: getLib().func('NET_SDK_GetConfigFile', 'bool', ['long', 'string']), + /** + * Gets device configuration file. + * + * @param userId - User ID from successful login + * @param fileName - Path to save configuration file + * @returns Success status + */ + public async getConfigFile(userId: number, fileName: string): Promise { + return (await this.lib).func('NET_SDK_GetConfigFile', 'bool', ['long', 'string'])(userId, fileName) + } - /** - * Sets device configuration from file. - * - * @param {number} userId - User ID from successful login - * @param {string} fileName - Path to configuration file - * @returns {boolean} Success status - */ - setConfigFile: getLib().func('NET_SDK_SetConfigFile', 'bool', ['long', 'string']), + /** + * Sets device configuration from file. + * + * @param userId - User ID from successful login + * @param fileName - Path to configuration file + * @returns Success status + */ + public async setConfigFile(userId: number, fileName: string): Promise { + return (await this.lib).func('NET_SDK_SetConfigFile', 'bool', ['long', 'string'])(userId, fileName) + } - /** - * Gets the last error code from the SDK. - * - * @returns {number} Error code - */ - getLastError: getLib().func('NET_SDK_GetLastError', 'uint32_t', []), + /** + * Gets the last error code from the SDK. + * + * @returns Error code + */ + public async getLastError(): Promise { + return (await this.lib).func('NET_SDK_GetLastError', 'uint32_t', [])() + } - /** - * Configures SDK logging to file. - * - * @param {boolean} logEnable - Enable/disable logging - * @param {string} logDir - Directory for log files - * @param {boolean} autoDel - Enable auto-deletion of old logs - * @param {number} logLevel - Logging level - * @returns {boolean} Success status - */ - setLogToFile: getLib().func('NET_SDK_SetLogToFile', 'bool', ['bool', 'string', 'bool', 'int']), + /** + * Configures SDK logging to file. + * + * @param logEnable - Enable/disable logging + * @param logDir - Directory for log files + * @param autoDel - Enable auto-deletion of old logs + * @param logLevel - Logging level + * @returns Success status + */ + public async setLogToFile(logEnable: boolean, logDir: string, autoDel: boolean, logLevel: LOG_LEVEL): Promise { + return (await this.lib).func('NET_SDK_SetLogToFile', 'bool', ['bool', 'string', 'bool', 'int'])( + logEnable, + logDir, + autoDel, + logLevel + ) + } - /** - * Starts saving live stream to file. - * - * @param {number} liveHandle - Live stream handle - * @param {string} fileName - Path to save stream file - * @returns {boolean} Success status - */ - startSavingLiveStream: getLib().func('NET_SDK_SaveLiveData', 'bool', ['long', 'string']), + /** + * Starts saving live stream to file. + * + * @param liveHandle - Live stream handle + * @param fileName - Path to save stream file + * @returns Success status + */ + public async startSavingLiveStream(liveHandle: number, fileName: string): Promise { + return (await this.lib).func('NET_SDK_SaveLiveData', 'bool', ['long', 'string'])(liveHandle, fileName) + } - /** - * Stops saving live stream to file. - * - * @param {number} liveHandle - Live stream handle - * @returns {boolean} Success status - */ - stopSavingLiveStream: getLib().func('NET_SDK_StopSaveLiveData', 'bool', ['long']), + /** + * Stops saving live stream to file. + * + * @param liveHandle - Live stream handle + * @returns Success status + */ + public async stopSavingLiveStream(liveHandle: number): Promise { + return (await this.lib).func('NET_SDK_StopSaveLiveData', 'bool', ['long'])(liveHandle) + } - /** - * Captures a JPEG snapshot from a channel. - * - * @param {number} userId - User ID from successful login - * @param {number} channel - Video channel number - * @param {string} fileName - Path to save JPEG file - * @returns {boolean} Success status - */ - captureJPEGFile_V2: getLib().func('NET_SDK_CaptureJPEGFile_V2', 'bool', ['long', 'long', 'string']) + /** + * Captures a JPEG snapshot from a channel. + * + * @param userId - User ID from successful login + * @param channel - Video channel number + * @param fileName - Path to save JPEG file + * @returns Success status + */ + public async captureJPEGFile_V2(userId: number, channel: number, fileName: string): Promise { + return (await this.lib).func('NET_SDK_CaptureJPEGFile_V2', 'bool', ['long', 'long', 'string'])( + userId, + channel, + fileName + ) } } -export const sdk = createSDK() +// Export a singleton instance +export const sdk = SDK.getInstance()