node-red-contrib-my-tools 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.txt +69 -0
  2. package/node-red-contrib-mysql-extended-test/mysql-extended.html +443 -0
  3. package/node-red-contrib-mysql-extended-test/mysql-extended.js +355 -0
  4. package/node-red-contrib-rfid-helper/rfid-lazy.html +153 -0
  5. package/node-red-contrib-rfid-helper/rfid-lazy.js +361 -0
  6. package/node-red-contrib-s7-plus/LICENSE +235 -0
  7. package/node-red-contrib-s7-plus/README.md +141 -0
  8. package/node-red-contrib-s7-plus/examples/read-multiple-values-flow.json +346 -0
  9. package/node-red-contrib-s7-plus/examples/read-write-test-flow.json +172 -0
  10. package/node-red-contrib-s7-plus/examples/write-single-values-flow.json +5583 -0
  11. package/node-red-contrib-s7-plus/lib/s7plus/browse/README.md +72 -0
  12. package/node-red-contrib-s7-plus/lib/s7plus/browse/areas.js +36 -0
  13. package/node-red-contrib-s7-plus/lib/s7plus/browse/datatypes.js +91 -0
  14. package/node-red-contrib-s7-plus/lib/s7plus/browse/flat-browser.js +433 -0
  15. package/node-red-contrib-s7-plus/lib/s7plus/browse/index.js +22 -0
  16. package/node-red-contrib-s7-plus/lib/s7plus/browse/lazy.js +397 -0
  17. package/node-red-contrib-s7-plus/lib/s7plus/browse/node-id.js +22 -0
  18. package/node-red-contrib-s7-plus/lib/s7plus/browse/resolve-symbolic.js +181 -0
  19. package/node-red-contrib-s7-plus/lib/s7plus/browse/vte-helpers.js +49 -0
  20. package/node-red-contrib-s7-plus/lib/s7plus/buffer-stream.js +96 -0
  21. package/node-red-contrib-s7-plus/lib/s7plus/client.js +1238 -0
  22. package/node-red-contrib-s7-plus/lib/s7plus/constants.js +155 -0
  23. package/node-red-contrib-s7-plus/lib/s7plus/crc/index.js +3 -0
  24. package/node-red-contrib-s7-plus/lib/s7plus/crc/s7-crc32.js +70 -0
  25. package/node-red-contrib-s7-plus/lib/s7plus/crc/symbol-crc.js +271 -0
  26. package/node-red-contrib-s7-plus/lib/s7plus/debug.js +88 -0
  27. package/node-red-contrib-s7-plus/lib/s7plus/explore-result.js +41 -0
  28. package/node-red-contrib-s7-plus/lib/s7plus/item-address.js +55 -0
  29. package/node-red-contrib-s7-plus/lib/s7plus/pdu-explore.js +77 -0
  30. package/node-red-contrib-s7-plus/lib/s7plus/pdu-messages.js +359 -0
  31. package/node-red-contrib-s7-plus/lib/s7plus/pobject.js +197 -0
  32. package/node-red-contrib-s7-plus/lib/s7plus/pvalue-codec.js +435 -0
  33. package/node-red-contrib-s7-plus/lib/s7plus/pvalue.js +653 -0
  34. package/node-red-contrib-s7-plus/lib/s7plus/pvar-lists.js +336 -0
  35. package/node-red-contrib-s7-plus/lib/s7plus/read-result.js +91 -0
  36. package/node-red-contrib-s7-plus/lib/s7plus/s7p.js +266 -0
  37. package/node-red-contrib-s7-plus/lib/s7plus/tag-routing.js +30 -0
  38. package/node-red-contrib-s7-plus/lib/s7plus/transport/cotp-stream.js +335 -0
  39. package/node-red-contrib-s7-plus/lib/s7plus/transport/s7-transport.js +335 -0
  40. package/node-red-contrib-s7-plus/nodes/icons/s7complus.png +0 -0
  41. package/node-red-contrib-s7-plus/nodes/icons/s7complus.svg +17 -0
  42. package/node-red-contrib-s7-plus/nodes/s7complus-endpoint.js +825 -0
  43. package/node-red-contrib-s7-plus/nodes/s7complus-explore.js +91 -0
  44. package/node-red-contrib-s7-plus/nodes/s7complus-in.js +255 -0
  45. package/node-red-contrib-s7-plus/nodes/s7complus-out.js +182 -0
  46. package/node-red-contrib-s7-plus/nodes/s7complus.html +1541 -0
  47. package/node-red-contrib-s7-plus/nodes/s7complus.js +10 -0
  48. package/node-red-contrib-s7-plus/package.json +50 -0
  49. package/node-red-contrib-s7-plus/plc/s7-1500/DB_Arrays.db +17 -0
  50. package/node-red-contrib-s7-plus/plc/s7-1500/DB_Binary.db +16 -0
  51. package/node-red-contrib-s7-plus/plc/s7-1500/DB_BitStrings.db +36 -0
  52. package/node-red-contrib-s7-plus/plc/s7-1500/DB_CharacterStrings.db +42 -0
  53. package/node-red-contrib-s7-plus/plc/s7-1500/DB_DateAndTime.db +70 -0
  54. package/node-red-contrib-s7-plus/plc/s7-1500/DB_FloatingPoint.db +42 -0
  55. package/node-red-contrib-s7-plus/plc/s7-1500/DB_Integers.db +72 -0
  56. package/node-red-contrib-s7-plus/plc/s7-1500/DB_Timers.db +40 -0
  57. package/node-red-contrib-s7-plus/scripts/example-flow-shared.js +39 -0
  58. package/node-red-contrib-s7-plus/scripts/generate-read-multiple-flow.js +331 -0
  59. package/node-red-contrib-s7-plus/scripts/generate-rw-test-flow.js +690 -0
  60. package/node-red-contrib-s7-plus/scripts/generate-write-flow.js +476 -0
  61. package/node-red-contrib-s7-plus/scripts/layout-write-flow.js +156 -0
  62. package/node-red-contrib-s7-plus/scripts/run-tests.js +16 -0
  63. package/node-red-flowgobal-change/variable-change.html +425 -0
  64. package/node-red-flowgobal-change/variable-change.js +92 -0
  65. package/node-red-ui_media_viewer/ui_media_viewer.html +87 -0
  66. package/node-red-ui_media_viewer/ui_media_viewer.js +136 -0
  67. package/package.json +12 -0
@@ -0,0 +1,435 @@
1
+ 'use strict';
2
+
3
+ const pvalue = require('./pvalue');
4
+ const { softdatatypeName } = require('./browse/datatypes');
5
+
6
+ // ── S7 String (softdatatype 19) ──────────────────────────────────────
7
+ // Wire format: USInt array [maxLen, currentLen, ...bytes]
8
+ // Byte 0 = max capacity
9
+ // Byte 1 = actual character count
10
+ // Bytes 2..n = ISO-8859-1 / CP1252 encoded characters
11
+ function decodeS7String(arr) {
12
+ if (!Array.isArray(arr) || arr.length < 2) return '';
13
+ const len = arr[1];
14
+ const bytes = arr.slice(2, 2 + len);
15
+ return Buffer.from(bytes).toString('latin1');
16
+ }
17
+
18
+ // ── S7 WString (softdatatype 62) ─────────────────────────────────────
19
+ // Wire format: UInt16 array [maxLen, currentLen, ...UCS-2 codepoints]
20
+ // Word 0 = max capacity (chars)
21
+ // Word 1 = actual character count
22
+ // Words 2..n = UCS-2 code points
23
+ function decodeS7WString(arr) {
24
+ if (!Array.isArray(arr) || arr.length < 2) return '';
25
+ const len = arr[1];
26
+ const codes = arr.slice(2, 2 + len);
27
+ return String.fromCharCode(...codes);
28
+ }
29
+
30
+ // ── S5Time (BCD) → milliseconds ──────────────────────────────────────
31
+ // Wire: UInt16 (ValueWord). Bits 13-12 = time base, 11-0 = BCD value
32
+ function decodeS5Time(raw) {
33
+ const n = typeof raw === 'number' ? raw : Number(raw);
34
+ const base = (n >> 12) & 0x03;
35
+ const bcd = n & 0x0fff;
36
+ const hundreds = (bcd >> 8) & 0x0f;
37
+ const tens = (bcd >> 4) & 0x0f;
38
+ const ones = bcd & 0x0f;
39
+ const val = hundreds * 100 + tens * 10 + ones;
40
+ const multipliers = [10, 100, 1000, 10000];
41
+ return val * multipliers[base];
42
+ }
43
+
44
+ function encodeS5Time(ms) {
45
+ const abs = Math.max(0, Math.min(ms, 9990000));
46
+ let base, divisor;
47
+ if (abs <= 9990) { base = 0; divisor = 10; }
48
+ else if (abs <= 99900) { base = 1; divisor = 100; }
49
+ else if (abs <= 999000) { base = 2; divisor = 1000; }
50
+ else { base = 3; divisor = 10000; }
51
+ const val = Math.round(abs / divisor);
52
+ const hundreds = Math.floor(val / 100) % 10;
53
+ const tens = Math.floor(val / 10) % 10;
54
+ const ones = val % 10;
55
+ return (base << 12) | (hundreds << 8) | (tens << 4) | ones;
56
+ }
57
+
58
+ // ── DateAndTime (BCD, 8 bytes) → Date ────────────────────────────────
59
+ // Wire: USInt array [YY, MM, DD, HH, MM, SS, ms_hi, ms_lo_dow]
60
+ function decodeDateAndTime(arr) {
61
+ if (!Array.isArray(arr) || arr.length < 8) return new Date(0);
62
+ const bcd = b => ((b >> 4) & 0x0f) * 10 + (b & 0x0f);
63
+ let year = bcd(arr[0]);
64
+ year += year >= 90 ? 1900 : 2000;
65
+ const month = bcd(arr[1]);
66
+ const day = bcd(arr[2]);
67
+ const hour = bcd(arr[3]);
68
+ const minute = bcd(arr[4]);
69
+ const second = bcd(arr[5]);
70
+ const msHi = bcd(arr[6]);
71
+ const msLo = (arr[7] >> 4) & 0x0f;
72
+ const ms = msHi * 10 + msLo;
73
+ return new Date(Date.UTC(year, month - 1, day, hour, minute, second, ms));
74
+ }
75
+
76
+ function encodeDateAndTimeBcd(d) {
77
+ const toBcd = n => ((Math.floor(n / 10) % 10) << 4) | (n % 10);
78
+ const year = d.getUTCFullYear();
79
+ const ms = d.getUTCMilliseconds();
80
+ const dow = d.getUTCDay() === 0 ? 7 : d.getUTCDay();
81
+ const arr = [
82
+ toBcd(year % 100),
83
+ toBcd(d.getUTCMonth() + 1),
84
+ toBcd(d.getUTCDate()),
85
+ toBcd(d.getUTCHours()),
86
+ toBcd(d.getUTCMinutes()),
87
+ toBcd(d.getUTCSeconds()),
88
+ toBcd(Math.floor(ms / 10)),
89
+ ((ms % 10) << 4) | (dow & 0x0f),
90
+ ];
91
+ return new pvalue.ValueUSIntArray(arr);
92
+ }
93
+
94
+ // ── LDT / Ldt → Date ──────────────────────────────────────────────
95
+ // Wire: Timestamp (UInt64), nanoseconds since 1970-01-01
96
+ function decodeLdt(bigVal) {
97
+ const ns = typeof bigVal === 'bigint' ? bigVal : BigInt(bigVal);
98
+ const ms = Number(ns / 1000000n);
99
+ return new Date(ms);
100
+ }
101
+
102
+ // ── DTL (softdatatype 67) ↔ Date ─────────────────────────────────────
103
+ // DTL is transmitted as a packed Struct (id 0x02000043 = TI_LIB.SimpleType.67)
104
+ // carrying a 12-byte big-endian array:
105
+ // [0..1] YEAR (UInt) [2] MONTH [3] DAY [4] WEEKDAY
106
+ // [5] HOUR [6] MINUTE [7] SECOND [8..11] NANOSECOND (UDInt)
107
+ // Valid range: 1970-01-01 00:00:00 .. 2262-04-11 23:47:16.
108
+ const DTL_STRUCT_ID = 0x02000043;
109
+ // Type-version timestamp the PLC expects when writing a packed DTL. Used
110
+ // only as a fallback when the live value has not been captured from the
111
+ // PLC yet (see endpoint DTL timestamp cache).
112
+ const DTL_DEFAULT_INTERFACE_TIMESTAMP = 0x10ff4ad6dfd5774cn;
113
+ const DTL_MIN_MS = Date.UTC(1970, 0, 1, 0, 0, 0);
114
+ const DTL_MAX_MS = Date.UTC(2262, 3, 11, 23, 47, 16, 999);
115
+
116
+ // Extract the raw 12-byte payload from a decoded DTL value. The packed
117
+ // struct decodes to { <structId>: Buffer }; accept a Buffer/array directly
118
+ // for robustness.
119
+ function dtlBytes(j) {
120
+ if (Buffer.isBuffer(j)) return j;
121
+ if (Array.isArray(j)) return Buffer.from(j);
122
+ if (j && typeof j === 'object') {
123
+ const buf = Object.values(j).find(v => Buffer.isBuffer(v));
124
+ if (buf) return buf;
125
+ const arr = Object.values(j).find(v => Array.isArray(v));
126
+ if (arr) return Buffer.from(arr);
127
+ }
128
+ return null;
129
+ }
130
+
131
+ function decodeDtl(j) {
132
+ const b = dtlBytes(j);
133
+ if (!b || b.length < 12) return new Date(0);
134
+ const year = (b[0] << 8) | b[1];
135
+ const month = b[2];
136
+ const day = b[3];
137
+ // b[4] = WEEKDAY (derived from the date; ignored here)
138
+ const hour = b[5];
139
+ const minute = b[6];
140
+ const second = b[7];
141
+ const nanosecond = ((b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11]) >>> 0;
142
+ const ms = Math.round(nanosecond / 1e6);
143
+ return new Date(Date.UTC(year, month - 1, day, hour, minute, second, ms));
144
+ }
145
+
146
+ function encodeDtlBytes(d) {
147
+ const year = d.getUTCFullYear();
148
+ const month = d.getUTCMonth() + 1;
149
+ const day = d.getUTCDate();
150
+ const hour = d.getUTCHours();
151
+ const minute = d.getUTCMinutes();
152
+ const second = d.getUTCSeconds();
153
+ const nanosecond = (d.getUTCMilliseconds() * 1e6) >>> 0;
154
+ const b = Buffer.alloc(12);
155
+ b[0] = (year >> 8) & 0xff;
156
+ b[1] = year & 0xff;
157
+ b[2] = month;
158
+ b[3] = day;
159
+ b[4] = 0; // WEEKDAY: the PLC recomputes it on write
160
+ b[5] = hour;
161
+ b[6] = minute;
162
+ b[7] = second;
163
+ b[8] = (nanosecond >>> 24) & 0xff;
164
+ b[9] = (nanosecond >>> 16) & 0xff;
165
+ b[10] = (nanosecond >>> 8) & 0xff;
166
+ b[11] = nanosecond & 0xff;
167
+ return b;
168
+ }
169
+
170
+ // ── Date (softdatatype 9) → Date ─────────────────────────────────────
171
+ // Wire: UInt16, days since 1990-01-01
172
+ const DATE_EPOCH = Date.UTC(1990, 0, 1);
173
+ function decodeS7Date(daysSinceEpoch) {
174
+ const n = typeof daysSinceEpoch === 'number' ? daysSinceEpoch : Number(daysSinceEpoch);
175
+ // Return local midnight of the calendar day so encode/decode are symmetric
176
+ // and read->write round-trips stay stable in any timezone.
177
+ const utc = new Date(DATE_EPOCH + n * 86400000);
178
+ return new Date(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate());
179
+ }
180
+
181
+ // ═════════════════════════════════════════════════════════════════════
182
+ // decodeReadValue – PValue → JS value, per README type contract
183
+ // ═════════════════════════════════════════════════════════════════════
184
+
185
+ function decodeReadValue(pval, softdatatype) {
186
+ if (!pval || typeof pval.toJs !== 'function') return null;
187
+ const j = pval.toJs();
188
+
189
+ // Accept both the canonical type name ('String') and the raw numeric
190
+ // softdatatype id (19). The latter may leak in from the CRC warmup
191
+ // cache; normalizing here keeps the switch a single source of truth.
192
+ if (typeof softdatatype === 'number') softdatatype = softdatatypeName(softdatatype);
193
+
194
+ switch (softdatatype) {
195
+ // ── Char / WChar → string ────────────────────────────────
196
+ case 'Char':
197
+ return String.fromCharCode(typeof j === 'number' ? j : Number(j));
198
+ case 'WChar':
199
+ return String.fromCharCode(typeof j === 'number' ? j : Number(j));
200
+
201
+ // ── String → decode header+bytes to string ───────────────
202
+ case 'String':
203
+ if (Array.isArray(j)) return decodeS7String(j);
204
+ if (typeof j === 'string') return j;
205
+ if (Buffer.isBuffer(j)) return j.toString('latin1');
206
+ return String(j);
207
+
208
+ // ── WString → decode header+UCS2 to string ──────────────
209
+ case 'WString':
210
+ if (Array.isArray(j)) return decodeS7WString(j);
211
+ if (typeof j === 'string') return j;
212
+ return String(j);
213
+
214
+ // ── 64-bit integers → BigInt ─────────────────────────────
215
+ case 'LInt':
216
+ case 'ULInt':
217
+ return typeof j === 'bigint' ? j : BigInt(j);
218
+
219
+ // ── LTime → BigInt (nanoseconds) ─────────────────────────
220
+ case 'LTime':
221
+ return typeof j === 'bigint' ? j : BigInt(j);
222
+
223
+ // ── LTOD → BigInt (nanoseconds since midnight) ───────────
224
+ case 'LTod':
225
+ return typeof j === 'bigint' ? j : BigInt(j);
226
+
227
+ // ── Date → Date object ───────────────────────────────────
228
+ case 'Date':
229
+ return decodeS7Date(j);
230
+
231
+ // ── LDT / Ldt → Date object ─────────────────────────────
232
+ case 'Ldt':
233
+ return decodeLdt(j);
234
+
235
+ // ── DTL → Date object (packed struct, 12-byte payload) ───
236
+ case 'Dtl':
237
+ return decodeDtl(j);
238
+
239
+ // ── DateAndTime → Date object ────────────────────────────
240
+ case 'DateAndTime':
241
+ if (Array.isArray(j)) return decodeDateAndTime(j);
242
+ if (typeof j === 'bigint') return decodeLdt(j);
243
+ return new Date(typeof j === 'number' ? j : 0);
244
+
245
+ // ── S5Time → number (milliseconds) ───────────────────────
246
+ case 'S5Time':
247
+ return decodeS5Time(typeof j === 'number' ? j : Number(j));
248
+
249
+ // ── Time → number (milliseconds, already DInt) ───────────
250
+ case 'Time':
251
+ return typeof j === 'number' ? j : Number(j);
252
+
253
+ // ── TOD → number (ms since midnight, already UDInt) ──────
254
+ case 'TimeOfDay':
255
+ return typeof j === 'number' ? j : Number(j);
256
+
257
+ // ── LWord → BigInt (64-bit, cannot fit a JS number) ──────
258
+ case 'LWord':
259
+ return typeof j === 'bigint' ? j : BigInt(j);
260
+
261
+ // ── default: best-effort ─────────────────────────────────
262
+ default:
263
+ if (typeof j === 'bigint') return Number(j);
264
+ if (Buffer.isBuffer(j)) return j;
265
+ return j;
266
+ }
267
+ }
268
+
269
+ // ═════════════════════════════════════════════════════════════════════
270
+ // encodeWriteValue – JS value → PValue, per softdatatype
271
+ // ═════════════════════════════════════════════════════════════════════
272
+
273
+ // Coerce a write value to a 64-bit BigInt without silent precision loss.
274
+ // A JS number only holds 53 significant bits, so any 64-bit literal beyond
275
+ // Number.MAX_SAFE_INTEGER is already corrupted by the time it arrives here
276
+ // (e.g. a Node-RED "num" inject of 0xFFFFFFFFFFFFFFFF). We therefore accept
277
+ // BigInt and strings (hex "0x.." or decimal, losslessly parsed) and reject
278
+ // unsafe numbers loudly instead of writing a wrong value.
279
+ function toBigInt64(val, typeName) {
280
+ if (typeof val === 'bigint') return val;
281
+ if (typeof val === 'string') {
282
+ const s = val.trim();
283
+ try {
284
+ return BigInt(s);
285
+ } catch (e) {
286
+ throw new Error(`Cannot encode ${typeName} value "${val}": not a valid integer`);
287
+ }
288
+ }
289
+ if (typeof val === 'number') {
290
+ if (!Number.isInteger(val) || !Number.isSafeInteger(val)) {
291
+ throw new Error(
292
+ `Cannot encode ${typeName} value ${val}: 64-bit values beyond ` +
293
+ `Number.MAX_SAFE_INTEGER (2^53) lose precision as a JS number — ` +
294
+ `pass a BigInt or a string instead`
295
+ );
296
+ }
297
+ return BigInt(val);
298
+ }
299
+ return BigInt(val);
300
+ }
301
+
302
+ function encodeWriteValue(val, softdatatype, opts = {}) {
303
+ if (typeof softdatatype === 'number') softdatatype = softdatatypeName(softdatatype);
304
+
305
+ switch (softdatatype) {
306
+ case 'Bool':
307
+ case 'BBool':
308
+ return new pvalue.ValueBool(!!val);
309
+
310
+ case 'Byte':
311
+ return new pvalue.ValueByte(Number(val));
312
+ case 'Word':
313
+ return new pvalue.ValueWord(Number(val));
314
+ case 'DWord':
315
+ return new pvalue.ValueDWord(Number(val));
316
+ case 'LWord':
317
+ return new pvalue.ValueLWord(toBigInt64(val, 'LWord'));
318
+
319
+ case 'SInt':
320
+ return new pvalue.ValueSInt(Number(val));
321
+ case 'Int':
322
+ return new pvalue.ValueInt(Number(val));
323
+ case 'DInt':
324
+ return new pvalue.ValueDInt(Number(val));
325
+ case 'USInt':
326
+ return new pvalue.ValueUSInt(Number(val));
327
+ case 'UInt':
328
+ return new pvalue.ValueUInt(Number(val));
329
+ case 'UDInt':
330
+ return new pvalue.ValueUDInt(Number(val));
331
+ case 'LInt':
332
+ return new pvalue.ValueLInt(toBigInt64(val, 'LInt'));
333
+ case 'ULInt':
334
+ return new pvalue.ValueULInt(toBigInt64(val, 'ULInt'));
335
+
336
+ case 'Real':
337
+ return new pvalue.ValueReal(Number(val));
338
+ case 'LReal':
339
+ return new pvalue.ValueLReal(Number(val));
340
+
341
+ case 'Char':
342
+ return new pvalue.ValueUSInt(typeof val === 'string' ? val.charCodeAt(0) : Number(val));
343
+ case 'WChar':
344
+ return new pvalue.ValueUInt(typeof val === 'string' ? val.charCodeAt(0) : Number(val));
345
+ case 'String': {
346
+ const s = String(val);
347
+ const bytes = Buffer.from(s, 'latin1');
348
+ const maxLen = 254;
349
+ const actualLen = Math.min(bytes.length, maxLen);
350
+ const arr = new Array(maxLen + 2).fill(0);
351
+ arr[0] = maxLen;
352
+ arr[1] = actualLen;
353
+ for (let i = 0; i < actualLen; i++) arr[i + 2] = bytes[i];
354
+ return new pvalue.ValueUSIntArray(arr);
355
+ }
356
+ case 'WString': {
357
+ const s = String(val);
358
+ const maxLen = 254;
359
+ const actualLen = Math.min(s.length, maxLen);
360
+ const arr = new Array(maxLen + 2).fill(0);
361
+ arr[0] = maxLen;
362
+ arr[1] = actualLen;
363
+ for (let i = 0; i < actualLen; i++) arr[i + 2] = s.charCodeAt(i);
364
+ return new pvalue.ValueUIntArray(arr);
365
+ }
366
+
367
+ case 'Time':
368
+ return new pvalue.ValueDInt(Number(val));
369
+ case 'S5Time': {
370
+ const bcd = encodeS5Time(Number(val));
371
+ return new pvalue.ValueWord(bcd);
372
+ }
373
+ case 'LTime':
374
+ return new pvalue.ValueTimespan(toBigInt64(val, 'LTime'));
375
+
376
+ case 'TimeOfDay':
377
+ return new pvalue.ValueUDInt(Number(val));
378
+ case 'LTod':
379
+ return new pvalue.ValueULInt(toBigInt64(val, 'LTod'));
380
+
381
+ case 'Date': {
382
+ const d = val instanceof Date ? val : new Date(val);
383
+ // Treat the JS Date as a local calendar date: derive the day count
384
+ // from its local Y/M/D so a local-midnight Date does not truncate to
385
+ // the previous day in timezones east of UTC.
386
+ const localMidnightUtc = Date.UTC(d.getFullYear(), d.getMonth(), d.getDate());
387
+ const days = Math.floor((localMidnightUtc - DATE_EPOCH) / 86400000);
388
+ return new pvalue.ValueUInt(Math.max(0, days));
389
+ }
390
+ case 'Ldt': {
391
+ const d = val instanceof Date ? val : (typeof val === 'bigint' ? val : new Date(val));
392
+ const ns = typeof d === 'bigint'
393
+ ? toBigInt64(d, 'Ldt')
394
+ : BigInt(d.getTime()) * 1000000n;
395
+ // LDT is an unsigned nanosecond count since 1970-01-01. A value
396
+ // before the epoch (e.g. a Date parsed in a positive UTC offset)
397
+ // would otherwise wrap to ~2554 via two's complement.
398
+ if (ns < 0n || ns > 0xffffffffffffffffn) {
399
+ throw new Error(
400
+ `Cannot encode Ldt value ${ns} ns: out of LDT range ` +
401
+ `(1970-01-01 00:00:00.000000000 .. 2554-07-21 23:34:33.709551615)`
402
+ );
403
+ }
404
+ return new pvalue.ValueTimestamp(ns);
405
+ }
406
+ case 'DateAndTime': {
407
+ const d = val instanceof Date ? val : new Date(val);
408
+ return encodeDateAndTimeBcd(d);
409
+ }
410
+ case 'Dtl': {
411
+ const d = val instanceof Date ? val : new Date(val);
412
+ const ms = d.getTime();
413
+ if (Number.isNaN(ms) || ms < DTL_MIN_MS || ms > DTL_MAX_MS) {
414
+ throw new Error(
415
+ `Cannot encode Dtl value ${val}: out of DTL range ` +
416
+ `(1970-01-01 00:00:00 .. 2262-04-11 23:47:16)`
417
+ );
418
+ }
419
+ const struct = new pvalue.ValueStruct(DTL_STRUCT_ID);
420
+ struct.packedInterfaceTimestamp = opts.dtlInterfaceTimestamp != null
421
+ ? (typeof opts.dtlInterfaceTimestamp === 'bigint'
422
+ ? opts.dtlInterfaceTimestamp
423
+ : BigInt(opts.dtlInterfaceTimestamp))
424
+ : DTL_DEFAULT_INTERFACE_TIMESTAMP;
425
+ struct.packedTransportFlags = 2;
426
+ struct.addStructElement(DTL_STRUCT_ID, new pvalue.ValueBlob(0, encodeDtlBytes(d)));
427
+ return struct;
428
+ }
429
+
430
+ default:
431
+ return pvalue.valueFromJs(val, softdatatype ? softdatatype.toLowerCase() : undefined);
432
+ }
433
+ }
434
+
435
+ module.exports = { encodeWriteValue, decodeReadValue, decodeS5Time, encodeS5Time };