Skip to content

Commit b51959b

Browse files
committed
Pull review #5 issues fixed
1 parent 423fedd commit b51959b

4 files changed

Lines changed: 291 additions & 204 deletions

File tree

client.js

Lines changed: 127 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* client.js */
1+
// client.js
22
const WebSocket = require('ws');
33
const root = require('./generated/containerPb.js');
44
const 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,62 @@ 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
194204
*
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).
205+
* 0 = None
206+
* 1 = NewestFirst
207+
* 2 = TimeRangeBeginExclusive
208+
* 4 = TimeRangeEndExclusive
209+
* 8 = UseLogStampForTimeRange
210+
*
211+
* In addition, the user can simply supply the following properties in the query object:
197212
*
198-
* For additional information:
199-
* https://cdpstudio.com/manual/cdp/cdplogger/eventlogreader.html#Flags-enum
200-
* https://cdpstudio.com/manual/cdp/cdplogger/eventlogreader.html#cdp-event-code-flags
213+
* - **senderConditions**: An array of sender strings (exact matches by default).
214+
* - **dataConditions**: An object where each key is a data field name and the value can be:
215+
* - A string (defaults to an exact match),
216+
* - An array of strings,
217+
* - An object (or array of objects) with properties:
218+
* - `value`: the string value to match,
219+
* - `matchType`: either `"exact"` (default) or `"wildcard"`.
201220
*
202-
* We also support mapping the `evt.code` field to a human-readable string
203-
* with `getEventCodeDescription()`.
221+
* The helper method `_buildEventQuery(query)` converts this simple plain object into a proper
222+
* `DBMessaging.Protobuf.EventQuery` message.
204223
*
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'
224+
* enum EventQuery::MatchType:
225+
* - Exact (0): The string must match exactly.
226+
* - Wildcard (1): The string may contain wildcards.
214227
*
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-
* }
228+
* Example usage:
229229
*
230+
* // Filter for events with sender exactly "CDPLoggerDemoApp.InvalidLicense":
231+
* { senderConditions: ["CDPLoggerDemoApp.InvalidLicense"] }
232+
*
233+
* // Filter for events where the "Text" data field equals "Invalid or missing feature license detected.":
234+
* { dataConditions: { "Text": "Invalid or missing feature license detected." } }
235+
*
236+
*
237+
* @param {Object} query - A simple plain object representing the EventQuery.
230238
* @returns {Promise<Array>} Resolves with an array of events (each event includes a 'codeDescription').
231239
*/
232240
requestEvents(query) {
233241
this._timeRequest();
234242
const requestId = this._getRequestId();
243+
// Convert the simple query into a proper EventQuery message.
244+
const eventQuery = this._buildEventQuery(query);
245+
246+
247+
235248
if (!this.isOpen) {
236-
this.queuedRequests[requestId] = { type: "events", query: query };
249+
this.queuedRequests[requestId] = { type: "events", query: eventQuery };
237250
} else {
238-
this._sendEventsRequest(requestId, query);
251+
this._sendEventsRequest(requestId, eventQuery);
239252
}
240253
return new Promise((resolve, reject) => {
241254
this.storedPromises[requestId] = { resolve, reject };
242255
});
243256
}
244257

258+
// --- Internal methods ---
259+
245260
_sendEventsRequest(requestId, query) {
246261
const container = Container.create();
247262
container.messageType = Container.Type.eEventsRequest;
@@ -250,6 +265,74 @@ class Client {
250265
this.ws.send(buffer);
251266
}
252267

268+
/**
269+
* Helper method to build a proper EventQuery message from a simple plain object.
270+
*
271+
*
272+
*
273+
*
274+
* @param {Object} query - The simple plain object query.
275+
* @returns {DBMessaging.Protobuf.EventQuery} - The built EventQuery message.
276+
*/
277+
_buildEventQuery(query) {
278+
const root = require('./generated/containerPb.js');
279+
const { EventQuery } = root.DBMessaging.Protobuf;
280+
const { MatchType } = EventQuery;
281+
282+
// Create base query with primitive fields
283+
const baseQuery = {
284+
timeRangeBegin: query.timeRangeBegin || 0,
285+
timeRangeEnd: query.timeRangeEnd || Math.floor(Date.now() / 1000),
286+
codeMask: query.codeMask !== undefined ? query.codeMask : 0xFFFFFFFF,
287+
limit: query.limit || 50,
288+
offset: query.offset || 0,
289+
flags: query.flags || 0
290+
};
291+
292+
// Build sender conditions if present
293+
if (query.senders && query.senders.length > 0) {
294+
baseQuery.senderConditions = {
295+
conditions: query.senders.map(sender => ({
296+
value: sender,
297+
type: MatchType.Exact
298+
}))
299+
};
300+
}
301+
302+
// Build data conditions if present
303+
if (query.dataConditions) {
304+
const dataConds = {};
305+
for (const key in query.dataConditions) {
306+
const val = query.dataConditions[key];
307+
const conditions = [];
308+
309+
if (Array.isArray(val)) {
310+
for (const item of val) {
311+
conditions.push({
312+
value: String(item),
313+
type: MatchType.Exact
314+
});
315+
}
316+
} else {
317+
conditions.push({
318+
value: String(val),
319+
type: MatchType.Exact
320+
});
321+
}
322+
323+
// Store without any table qualification
324+
dataConds[key] = { conditions };
325+
}
326+
baseQuery.dataConditions = dataConds;
327+
}
328+
329+
return EventQuery.create(baseQuery);
330+
}
331+
332+
333+
334+
335+
253336
/**
254337
* Converts a numeric CDP event code into a descriptive string.
255338
* Multiple flags can be set simultaneously, so we combine them.
@@ -262,8 +345,8 @@ class Client {
262345
* 0x100 = SourceObjectUnavailable
263346
* 0x40000000 = NodeBoot
264347
*
265-
* @param {number} code - The event code from an eEventsResponse
266-
* @returns {string} - A human-readable combination of flags
348+
* @param {number} code - The event code from an eEventsResponse.
349+
* @returns {string} - A human-readable combination of flags.
267350
*/
268351
getEventCodeDescription(code) {
269352
const flags = [];
@@ -396,13 +479,12 @@ class Client {
396479
}
397480

398481
case Container.Type.eEventsResponse: {
399-
// Optionally enrich each event with a human-readable code description:
482+
// Enrich events with a human-readable code description.
400483
if (data.eventsResponse.events && data.eventsResponse.events.length > 0) {
401484
data.eventsResponse.events.forEach(evt => {
402485
evt.codeDescription = this.getEventCodeDescription(evt.code);
403486
});
404487
}
405-
406488
if (this.storedPromises[data.eventsResponse.requestId]) {
407489
const { resolve } = this.storedPromises[data.eventsResponse.requestId];
408490
delete this.storedPromises[data.eventsResponse.requestId];
@@ -524,9 +606,7 @@ class Client {
524606
}
525607
const requestId = reqId;
526608
this.lastTimeRequest = Date.now() / 1000;
527-
// Always send the time request.
528609
this._sendTimeRequest(requestId);
529-
// Create the promise and store the callbacks.
530610
const promise = new Promise((resolve, reject) => {
531611
this.storedPromises[requestId] = { resolve, reject };
532612
});

0 commit comments

Comments
 (0)