1- /* client.js */
1+ // client.js
22const WebSocket = require ( 'ws' ) ;
33const root = require ( './generated/containerPb.js' ) ;
44const Container = root . DBMessaging . Protobuf . Container ;
@@ -21,9 +21,7 @@ class Client {
2121
2222 this . reqId = - 1 ;
2323 this . autoReconnect = autoReconnect ;
24-
25- // Time synchronization is enabled by default.
26- this . enableTimeSync = true ;
24+ this . enableTimeSync = true ; // Time synchronization is enabled by default.
2725
2826 this . isOpen = false ;
2927 this . queuedRequests = { } ;
@@ -129,6 +127,9 @@ class Client {
129127
130128 // --- Public API methods ---
131129
130+ /**
131+ * Request the API version.
132+ */
132133 requestApiVersion ( ) {
133134 this . _timeRequest ( ) ;
134135 const requestId = this . _getRequestId ( ) ;
@@ -142,6 +143,9 @@ class Client {
142143 } ) ;
143144 }
144145
146+ /**
147+ * Request the list of logged nodes.
148+ */
145149 requestLoggedNodes ( ) {
146150 this . _timeRequest ( ) ;
147151 const requestId = this . _getRequestId ( ) ;
@@ -155,6 +159,9 @@ class Client {
155159 } ) ;
156160 }
157161
162+ /**
163+ * Request the log limits.
164+ */
158165 requestLogLimits ( ) {
159166 this . _timeRequest ( ) ;
160167 const requestId = this . _getRequestId ( ) ;
@@ -168,6 +175,14 @@ class Client {
168175 } ) ;
169176 }
170177
178+ /**
179+ * Request data points for given node names and time range.
180+ * @param {Array<string> } nodeNames
181+ * @param {number } startS
182+ * @param {number } endS
183+ * @param {number } noOfDataPoints
184+ * @returns {Promise<Array> }
185+ */
171186 requestDataPoints ( nodeNames , startS , endS , noOfDataPoints ) {
172187 this . _timeRequest ( ) ;
173188 const requestId = this . _getRequestId ( ) ;
@@ -186,62 +201,63 @@ class Client {
186201 * Request events based on the provided query parameters.
187202 *
188203 * The `query.flags` field uses bitmask values similar to an enum:
189- * 0 = None
190- * 1 = NewestFirst
191- * 2 = TimeRangeBeginExclusive
192- * 4 = TimeRangeEndExclusive
193- * 8 = UseLogStampForTimeRange
194- *
195- * The `query.senderConditions` field can be used to filter by event sender (Source).
196- * The `query.dataConditions` field can be used to filter by data fields (key-value patterns).
197204 *
198- * For additional information:
199- * https://cdpstudio.com/manual/cdp/cdplogger/eventlogreader.html#Flags-enum
205+ * 0 = None
206+ * 1 = NewestFirst
207+ * 2 = TimeRangeBeginExclusive
208+ * 4 = TimeRangeEndExclusive
209+ * 8 = UseLogStampForTimeRange
210+ *
211+ * For additional information:
212+ * https://cdpstudio.com/manual/cdp/cdp2sql/logmanager-eventquery.html#Flags-enum
200213 * https://cdpstudio.com/manual/cdp/cdplogger/eventlogreader.html#cdp-event-code-flags
201214 *
202- * We also support mapping the `evt.code` field to a human-readable string
203- * with `getEventCodeDescription()`.
215+ * In addition, the user can simply supply the following properties in the query object:
216+ *
217+ * - **senderConditions**: An array of sender strings (exact matches by default).
218+ * - **dataConditions**: An object where each key is a data field name and the value can be:
219+ * - A string (defaults to an exact match),
220+ * - An array of strings,
221+ * - An object (or array of objects) with properties:
222+ * - `value`: the string value to match,
223+ * - `matchType`: either `"exact"` (default) or `"wildcard"`.
224+ *
225+ * enum EventQuery::MatchType:
226+ * - Exact (0): The string must match exactly.
227+ * - Wildcard (1): The string may contain wildcards.
228+ *
229+ * Example usage:
204230 *
205- * @param {Object } query - An object matching the EventQuery schema.
206- * Example:
207- * {
208- * timeRangeBegin: 1620000000,
209- * timeRangeEnd: 1620003600,
210- * codeMask: 0xFFFFFFFF,
211- * limit: 100,
212- * offset: 0,
213- * flags: 1, // e.g. 'NewestFirst'
231+ * // Filter for events with sender exactly "CDPLoggerDemoApp.InvalidLicense":
232+ * { senderConditions: ["CDPLoggerDemoApp.InvalidLicense"] }
214233 *
215- * // Example conditions:
216- * senderConditions: {
217- * conditions: [
218- * { value: "*TemperatureSensor*", type: 1 } // 1 = Wildcard
219- * ]
220- * },
221- * dataConditions: {
222- * pressure: {
223- * conditions: [
224- * { value: "high", type: 0 } // 0 = Exact
225- * ]
226- * }
227- * }
228- * }
234+ * // Filter for events where the "Text" data field equals "Invalid or missing feature license detected.":
235+ * { dataConditions: { "Text": "Invalid or missing feature license detected." } }
229236 *
237+ *
238+ * @param {Object } query - A simple plain object representing the EventQuery.
230239 * @returns {Promise<Array> } Resolves with an array of events (each event includes a 'codeDescription').
231240 */
232241 requestEvents ( query ) {
233242 this . _timeRequest ( ) ;
234243 const requestId = this . _getRequestId ( ) ;
244+ // Convert the simple query into a proper EventQuery message.
245+ const eventQuery = this . _buildEventQuery ( query ) ;
246+
247+
248+
235249 if ( ! this . isOpen ) {
236- this . queuedRequests [ requestId ] = { type : "events" , query : query } ;
250+ this . queuedRequests [ requestId ] = { type : "events" , query : eventQuery } ;
237251 } else {
238- this . _sendEventsRequest ( requestId , query ) ;
252+ this . _sendEventsRequest ( requestId , eventQuery ) ;
239253 }
240254 return new Promise ( ( resolve , reject ) => {
241255 this . storedPromises [ requestId ] = { resolve, reject } ;
242256 } ) ;
243257 }
244258
259+ // --- Internal methods ---
260+
245261 _sendEventsRequest ( requestId , query ) {
246262 const container = Container . create ( ) ;
247263 container . messageType = Container . Type . eEventsRequest ;
@@ -250,6 +266,74 @@ class Client {
250266 this . ws . send ( buffer ) ;
251267 }
252268
269+ /**
270+ * Helper method to build a proper EventQuery message from a simple plain object.
271+ *
272+ *
273+ *
274+ *
275+ * @param {Object } query - The simple plain object query.
276+ * @returns {DBMessaging.Protobuf.EventQuery } - The built EventQuery message.
277+ */
278+ _buildEventQuery ( query ) {
279+ const root = require ( './generated/containerPb.js' ) ;
280+ const { EventQuery } = root . DBMessaging . Protobuf ;
281+ const { MatchType } = EventQuery ;
282+
283+ // Create base query with primitive fields
284+ const baseQuery = {
285+ timeRangeBegin : query . timeRangeBegin || 0 ,
286+ timeRangeEnd : query . timeRangeEnd || Math . floor ( Date . now ( ) / 1000 ) ,
287+ codeMask : query . codeMask !== undefined ? query . codeMask : 0xFFFFFFFF ,
288+ limit : query . limit || 50 ,
289+ offset : query . offset || 0 ,
290+ flags : query . flags || 0
291+ } ;
292+
293+ // Build sender conditions if present
294+ if ( query . senders && query . senders . length > 0 ) {
295+ baseQuery . senderConditions = {
296+ conditions : query . senders . map ( sender => ( {
297+ value : sender ,
298+ type : MatchType . Exact
299+ } ) )
300+ } ;
301+ }
302+
303+ // Build data conditions if present
304+ if ( query . dataConditions ) {
305+ const dataConds = { } ;
306+ for ( const key in query . dataConditions ) {
307+ const val = query . dataConditions [ key ] ;
308+ const conditions = [ ] ;
309+
310+ if ( Array . isArray ( val ) ) {
311+ for ( const item of val ) {
312+ conditions . push ( {
313+ value : String ( item ) ,
314+ type : MatchType . Exact
315+ } ) ;
316+ }
317+ } else {
318+ conditions . push ( {
319+ value : String ( val ) ,
320+ type : MatchType . Exact
321+ } ) ;
322+ }
323+
324+ // Store without any table qualification
325+ dataConds [ key ] = { conditions } ;
326+ }
327+ baseQuery . dataConditions = dataConds ;
328+ }
329+
330+ return EventQuery . create ( baseQuery ) ;
331+ }
332+
333+
334+
335+
336+
253337 /**
254338 * Converts a numeric CDP event code into a descriptive string.
255339 * Multiple flags can be set simultaneously, so we combine them.
@@ -262,8 +346,8 @@ class Client {
262346 * 0x100 = SourceObjectUnavailable
263347 * 0x40000000 = NodeBoot
264348 *
265- * @param {number } code - The event code from an eEventsResponse
266- * @returns {string } - A human-readable combination of flags
349+ * @param {number } code - The event code from an eEventsResponse.
350+ * @returns {string } - A human-readable combination of flags.
267351 */
268352 getEventCodeDescription ( code ) {
269353 const flags = [ ] ;
@@ -280,6 +364,51 @@ class Client {
280364 return flags . join ( " + " ) ;
281365 }
282366
367+ /**
368+ * Returns a human‐readable string for a given event code.
369+ *
370+ * @param {number } code - The numeric event code.
371+ * @returns {string } - The corresponding event code string.
372+ */
373+ getEventCodeString ( code ) {
374+ // Return empty string if eventCode is zero
375+ if ( code === 0 ) return "" ;
376+
377+ // Define the flag values (adjust these constants if needed)
378+ const EventCodeFlags = {
379+ AlarmSet : 0x1 ,
380+ AlarmClr : 0x2 ,
381+ AlarmAck : 0x4 ,
382+ AlarmReprise : 0x40
383+ } ;
384+
385+ // Check for specific combinations first
386+ if ( code === EventCodeFlags . AlarmSet ) return "AlarmSet" ;
387+ if ( code === EventCodeFlags . AlarmClr ) return "AlarmClear" ;
388+ if ( code === EventCodeFlags . AlarmAck ) return "Ack" ;
389+ if ( code === EventCodeFlags . AlarmReprise ) return "Reprise" ;
390+ if ( code === ( EventCodeFlags . AlarmReprise | EventCodeFlags . AlarmSet ) )
391+ return "RepriseAlarmSet" ;
392+ if ( code === ( EventCodeFlags . AlarmReprise | EventCodeFlags . AlarmClr ) )
393+ return "RepriseAlarmClear" ;
394+ if ( code === ( EventCodeFlags . AlarmReprise | EventCodeFlags . AlarmAck ) )
395+ return "RepriseAck" ;
396+
397+ // Otherwise, combine the flag strings based on which bits are set.
398+ let s = "" ;
399+ if ( code & EventCodeFlags . AlarmReprise )
400+ s += ( s ? "+" : "" ) + "Reprise" ;
401+ if ( code & EventCodeFlags . AlarmSet )
402+ s += ( s ? "+" : "" ) + "AlarmSet" ;
403+ if ( code & EventCodeFlags . AlarmClr )
404+ s += ( s ? "+" : "" ) + "AlarmClear" ;
405+ if ( code & EventCodeFlags . AlarmAck )
406+ s += ( s ? "+" : "" ) + "Ack" ;
407+
408+ return s ;
409+ }
410+
411+
283412 _handleMessage ( ws , message ) {
284413 const data = Container . decode ( new Uint8Array ( message ) ) ;
285414 this . _parseMessage ( data ) ;
@@ -396,13 +525,12 @@ class Client {
396525 }
397526
398527 case Container . Type . eEventsResponse : {
399- // Optionally enrich each event with a human-readable code description:
528+ // Enrich events with a human-readable code description.
400529 if ( data . eventsResponse . events && data . eventsResponse . events . length > 0 ) {
401530 data . eventsResponse . events . forEach ( evt => {
402531 evt . codeDescription = this . getEventCodeDescription ( evt . code ) ;
403532 } ) ;
404533 }
405-
406534 if ( this . storedPromises [ data . eventsResponse . requestId ] ) {
407535 const { resolve } = this . storedPromises [ data . eventsResponse . requestId ] ;
408536 delete this . storedPromises [ data . eventsResponse . requestId ] ;
@@ -524,9 +652,7 @@ class Client {
524652 }
525653 const requestId = reqId ;
526654 this . lastTimeRequest = Date . now ( ) / 1000 ;
527- // Always send the time request.
528655 this . _sendTimeRequest ( requestId ) ;
529- // Create the promise and store the callbacks.
530656 const promise = new Promise ( ( resolve , reject ) => {
531657 this . storedPromises [ requestId ] = { resolve, reject } ;
532658 } ) ;
0 commit comments