motion-master-client 0.0.422 → 0.0.424
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.
- package/package.json +1 -1
- package/src/api.js +87 -43
- package/src/api.js.map +1 -1
- package/src/lib/motion-master-req-res-client.d.ts +117 -50
- package/src/lib/motion-master-req-res-client.js +305 -222
- package/src/lib/motion-master-req-res-client.js.map +1 -1
- package/src/lib/smm.d.ts +23 -6
- package/src/lib/smm.js +32 -10
- package/src/lib/smm.js.map +1 -1
- package/src/lib/types.d.ts +40 -0
|
@@ -310,47 +310,61 @@ class MotionMasterReqResClient {
|
|
|
310
310
|
*
|
|
311
311
|
* If the requested file content is empty, Motion Master will return a not found error. This is subject to change now that Motion Master reads the file error codes.
|
|
312
312
|
*
|
|
313
|
-
* SOMANET_CiA_402.xml
|
|
313
|
+
* The ESI file ("SOMANET_CiA_402.xml" or "SOMANET_CiA_402.xml.zip") is never read from the device, see {@link getEsiFromIndexedDB}.
|
|
314
314
|
*/
|
|
315
315
|
getDeviceFile(props, requestTimeout, messageId) {
|
|
316
316
|
return this.resolveDeviceAddress(props).pipe((0, operators_1.mergeMap)((deviceAddress) => {
|
|
317
|
-
if (
|
|
318
|
-
|
|
319
|
-
typeof window !== 'undefined' &&
|
|
320
|
-
// @ts-ignore
|
|
321
|
-
window.indexedDB) {
|
|
322
|
-
return this.getParameterValue(deviceAddress, 0x100a, 0x00).pipe((0, operators_1.mergeMap)((firmwareVersion) => {
|
|
323
|
-
return (0, rxjs_1.from)(this.mmcidb.open()).pipe((0, operators_1.mergeMap)(() => this.mmcidb.getEsi(firmwareVersion)), (0, operators_1.mergeMap)((data) => {
|
|
324
|
-
if (data) {
|
|
325
|
-
const status = {
|
|
326
|
-
request: 'succeeded',
|
|
327
|
-
name: props.name,
|
|
328
|
-
deviceAddress,
|
|
329
|
-
content: data,
|
|
330
|
-
success: {
|
|
331
|
-
code: types_1.MotionMasterMessage.Status.DeviceFile.Success.Code.UNSPECIFIED,
|
|
332
|
-
message: 'ESI file successfully retrieved from IndexedDB.',
|
|
333
|
-
},
|
|
334
|
-
messageId,
|
|
335
|
-
};
|
|
336
|
-
return (0, rxjs_1.of)(status);
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
return this.getDeviceFileUsingResolvedDeviceAddress(deviceAddress, props, requestTimeout, messageId).pipe((0, rxjs_1.tap)((status) => {
|
|
340
|
-
if (status.content) {
|
|
341
|
-
this.mmcidb.putEsi(firmwareVersion, new Uint8Array(status.content));
|
|
342
|
-
}
|
|
343
|
-
}));
|
|
344
|
-
}
|
|
345
|
-
}));
|
|
346
|
-
}), (0, operators_1.catchError)((err) => {
|
|
347
|
-
console.error('Failed to retrieve and cache ESI file from IndexedDB:', err);
|
|
348
|
-
return this.getDeviceFileUsingResolvedDeviceAddress(deviceAddress, props, requestTimeout, messageId);
|
|
349
|
-
}));
|
|
317
|
+
if (props.name === firmware_esi_1.esiFilename || props.name === firmware_esi_1.esiZipFilename) {
|
|
318
|
+
return this.getEsiFromIndexedDB(deviceAddress, props.name, messageId);
|
|
350
319
|
}
|
|
351
|
-
|
|
352
|
-
|
|
320
|
+
return this.getDeviceFileUsingResolvedDeviceAddress(deviceAddress, props, requestTimeout, messageId);
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Returns the ESI file cached in IndexedDB under the firmware version the device reports in 0x100A:00.
|
|
325
|
+
*
|
|
326
|
+
* The ESI file is never read from the device. Firmware installation no longer writes it to the device (see
|
|
327
|
+
* {@link installFirmwareAndCacheEsi}), and Motion Master does not remove a skipped file, so the file on the device may
|
|
328
|
+
* belong to an older firmware. Neither the file nor its ESI revision tells which firmware it belongs to.
|
|
329
|
+
* Caching it under the installed version would hide the correct ESI file for good.
|
|
330
|
+
*
|
|
331
|
+
* The ESI file gets into IndexedDB only from a firmware package: at firmware installation, see
|
|
332
|
+
* {@link cacheEsiFromFirmwarePackage}, or when a user loads it from a package in OBLAC Drives.
|
|
333
|
+
*
|
|
334
|
+
* Returns a failed status with the NOT_FOUND error code when the ESI file is not cached for the installed firmware,
|
|
335
|
+
* or when there is no IndexedDB, as outside the browser. Returns a failed status with the FAILED error code when the
|
|
336
|
+
* firmware version or IndexedDB can't be read.
|
|
337
|
+
*/
|
|
338
|
+
getEsiFromIndexedDB(deviceAddress, name, messageId) {
|
|
339
|
+
const failed = (code, message) => ({
|
|
340
|
+
request: 'failed',
|
|
341
|
+
name,
|
|
342
|
+
deviceAddress,
|
|
343
|
+
error: { code, message },
|
|
344
|
+
messageId,
|
|
345
|
+
});
|
|
346
|
+
if (!globalThis.indexedDB) {
|
|
347
|
+
return (0, rxjs_1.of)(failed(types_1.MotionMasterMessage.Status.DeviceFile.Error.Code.NOT_FOUND, 'The ESI file is not read from the device, and there is no IndexedDB to read it from.'));
|
|
348
|
+
}
|
|
349
|
+
return this.getParameterValue(deviceAddress, 0x100a, 0x00).pipe((0, operators_1.mergeMap)((firmwareVersion) => (0, rxjs_1.from)(this.mmcidb.open().then(() => this.mmcidb.getEsi(firmwareVersion))).pipe((0, operators_1.map)((data) => {
|
|
350
|
+
if (data) {
|
|
351
|
+
return {
|
|
352
|
+
request: 'succeeded',
|
|
353
|
+
name,
|
|
354
|
+
deviceAddress,
|
|
355
|
+
content: data,
|
|
356
|
+
success: {
|
|
357
|
+
code: types_1.MotionMasterMessage.Status.DeviceFile.Success.Code.UNSPECIFIED,
|
|
358
|
+
message: 'ESI file successfully retrieved from IndexedDB.',
|
|
359
|
+
},
|
|
360
|
+
messageId,
|
|
361
|
+
};
|
|
353
362
|
}
|
|
363
|
+
return failed(types_1.MotionMasterMessage.Status.DeviceFile.Error.Code.NOT_FOUND, `The ESI file for firmware ${firmwareVersion} is not cached. The ESI file is not read from the device.`);
|
|
364
|
+
}))), (0, operators_1.catchError)((err) => {
|
|
365
|
+
console.error('Failed to retrieve the ESI file from IndexedDB:', err);
|
|
366
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
367
|
+
return (0, rxjs_1.of)(failed(types_1.MotionMasterMessage.Status.DeviceFile.Error.Code.FAILED, `Failed to retrieve the ESI file from IndexedDB: ${reason}`));
|
|
354
368
|
}));
|
|
355
369
|
}
|
|
356
370
|
getDeviceFileUsingResolvedDeviceAddress(deviceAddress, props, requestTimeout, messageId) {
|
|
@@ -507,8 +521,7 @@ class MotionMasterReqResClient {
|
|
|
507
521
|
* Extracts the ESI file (SOMANET_CiA_402.xml) from a firmware package and stores its unzipped content in
|
|
508
522
|
* IndexedDB, keyed by the given firmware version.
|
|
509
523
|
*
|
|
510
|
-
*
|
|
511
|
-
* cached here, OBLAC can resolve it from IndexedDB without reading it back from the drive. This is what
|
|
524
|
+
* IndexedDB is the only source of the ESI file: {@link getDeviceFile} never reads it from the drive. This is what
|
|
512
525
|
* allows the ESI file (SOMANET_CiA_402.xml.zip) to be skipped during firmware installation, which avoids
|
|
513
526
|
* slow and unreliable FoE transfers and works around devices (e.g. NetX) that reject writing that file.
|
|
514
527
|
*
|
|
@@ -542,17 +555,18 @@ class MotionMasterReqResClient {
|
|
|
542
555
|
* user-overridable: when `props.skipFiles` is provided it is passed through unchanged, so a caller can choose to
|
|
543
556
|
* still write the ESI by omitting it from that list.
|
|
544
557
|
* - Extracts the ESI from the package and stores it in IndexedDB keyed by firmware version (see
|
|
545
|
-
* {@link cacheEsiFromFirmwarePackage}) *before* the installation starts, so
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
558
|
+
* {@link cacheEsiFromFirmwarePackage}) *before* the installation starts, so {@link getDeviceFile} can return it
|
|
559
|
+
* as soon as the installation ends. IndexedDB is the only source of the ESI file: {@link getDeviceFile} never
|
|
560
|
+
* reads it from the drive, even when the ESI is also written there. A caching failure is logged but does not
|
|
561
|
+
* prevent installation; the ESI file is then not found until a user loads it from the package (Load ESI in
|
|
562
|
+
* OBLAC Drives).
|
|
549
563
|
*
|
|
550
564
|
* @param firmwareVersion - The firmware version to key the cached ESI by; this must match the value reported by
|
|
551
565
|
* the device parameter 0x100a:00 after installation, which {@link getDeviceFile} uses.
|
|
552
566
|
* For OBLAC firmware packages this is the version encoded in the package filename, see
|
|
553
567
|
* {@link extractFirmwareVersionFromFirmwarePackageFilename}. When it cannot be determined
|
|
554
568
|
* (e.g. an arbitrary package with an unrecognized filename), pass an empty value to skip
|
|
555
|
-
* caching; the
|
|
569
|
+
* caching; the ESI file is then not found unless it was cached before.
|
|
556
570
|
*/
|
|
557
571
|
installFirmwareAndCacheEsi(firmwareVersion, props, requestTimeout, messageId) {
|
|
558
572
|
var _a;
|
|
@@ -3351,21 +3365,27 @@ class MotionMasterReqResClient {
|
|
|
3351
3365
|
* attempt will fail unless the user logs out beforehand.
|
|
3352
3366
|
*
|
|
3353
3367
|
* @param deviceRef - The reference to the device to which the login command will be sent.
|
|
3354
|
-
* @param username - The
|
|
3355
|
-
*
|
|
3368
|
+
* @param username - The name of the user who logs in, 4 to 24 printable ASCII characters. It tells who configured
|
|
3369
|
+
* the SMM, so pass the real user's name.
|
|
3370
|
+
* @param password - The password used for logging in to the SMM, 1 to 24 printable ASCII characters.
|
|
3356
3371
|
* @param commandTimeout - The timeout in milliseconds for the command to complete.
|
|
3357
3372
|
* @param responsePollingInterval - The interval in milliseconds between polling for the response.
|
|
3358
3373
|
*
|
|
3359
|
-
* @throws {Error} If the
|
|
3374
|
+
* @throws {Error} If the username or the password is not valid, in which case nothing is sent, or if the login
|
|
3375
|
+
* attempt fails.
|
|
3360
3376
|
*
|
|
3361
3377
|
* @remarks
|
|
3362
3378
|
* If the device is already logged in, the next login attempt will fail unless the user logs out first.
|
|
3363
3379
|
*/
|
|
3364
3380
|
loginToSmmForParameterDownload(deviceRef, username, password, commandTimeout = 5000, responsePollingInterval = 500) {
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3381
|
+
return (0, rxjs_1.defer)(() => {
|
|
3382
|
+
(0, smm_1.assertValidSmmCredential)(username, 'Username', smm_1.SMM_USERNAME_MIN_LENGTH);
|
|
3383
|
+
(0, smm_1.assertValidSmmCredential)(password, 'Password');
|
|
3384
|
+
const command = (0, os_command_1.createSmmAcyclicHandlerOsCommand)(os_command_1.SmmAcyclicHandlerSubcommandId.PARAMETER_DOWNLOAD_LOGIN);
|
|
3385
|
+
const content = (0, smm_1.packSmmCredentials)(username, password);
|
|
3386
|
+
// @ts-ignore
|
|
3387
|
+
return this.runOsCommandAndWriteFsBuffer(deviceRef, command, content, commandTimeout, responsePollingInterval);
|
|
3388
|
+
}).pipe((0, operators_1.last)(), (0, operators_1.map)((response) => {
|
|
3369
3389
|
if (response.request !== 'succeeded') {
|
|
3370
3390
|
throw new Error(`Login to SMM for parameter download failed: ${(0, os_command_1.describeOsCommandFailure)(response)}.`);
|
|
3371
3391
|
}
|
|
@@ -3379,21 +3399,27 @@ class MotionMasterReqResClient {
|
|
|
3379
3399
|
* attempt will fail unless the user logs out beforehand.
|
|
3380
3400
|
*
|
|
3381
3401
|
* @param deviceRef - The reference to the device to which the login command will be sent.
|
|
3382
|
-
* @param username - The
|
|
3383
|
-
*
|
|
3402
|
+
* @param username - The name of the user who logs in, 4 to 24 printable ASCII characters. It tells who configured
|
|
3403
|
+
* the SMM, so pass the real user's name.
|
|
3404
|
+
* @param password - The password used for logging in to the SMM, 1 to 24 printable ASCII characters.
|
|
3384
3405
|
* @param commandTimeout - The timeout in milliseconds for the command to complete.
|
|
3385
3406
|
* @param responsePollingInterval - The interval in milliseconds between polling for the response.
|
|
3386
3407
|
*
|
|
3387
|
-
* @throws {Error} If the
|
|
3408
|
+
* @throws {Error} If the username or the password is not valid, in which case nothing is sent, or if the login
|
|
3409
|
+
* attempt fails.
|
|
3388
3410
|
*
|
|
3389
3411
|
* @remarks
|
|
3390
3412
|
* If the device is already logged in, the next login attempt will fail unless the user logs out first.
|
|
3391
3413
|
*/
|
|
3392
3414
|
loginToSmmForSoftwareUpdate(deviceRef, username, password, commandTimeout = 10000, responsePollingInterval = 500) {
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3415
|
+
return (0, rxjs_1.defer)(() => {
|
|
3416
|
+
(0, smm_1.assertValidSmmCredential)(username, 'Username', smm_1.SMM_USERNAME_MIN_LENGTH);
|
|
3417
|
+
(0, smm_1.assertValidSmmCredential)(password, 'Password');
|
|
3418
|
+
const command = (0, os_command_1.createSmmAcyclicHandlerOsCommand)(os_command_1.SmmAcyclicHandlerSubcommandId.SOFTWARE_UPDATE_LOGIN);
|
|
3419
|
+
const content = (0, smm_1.packSmmCredentials)(username, password);
|
|
3420
|
+
// @ts-ignore
|
|
3421
|
+
return this.runOsCommandAndWriteFsBuffer(deviceRef, command, content, commandTimeout, responsePollingInterval);
|
|
3422
|
+
}).pipe((0, operators_1.last)(), (0, operators_1.map)((response) => {
|
|
3397
3423
|
if (response.request !== 'succeeded') {
|
|
3398
3424
|
throw new Error(`Login to SMM for software update failed: ${(0, os_command_1.describeOsCommandFailure)(response)}.`);
|
|
3399
3425
|
}
|
|
@@ -3441,38 +3467,46 @@ class MotionMasterReqResClient {
|
|
|
3441
3467
|
* Changes the SMM password for the specified device.
|
|
3442
3468
|
*
|
|
3443
3469
|
* The sequence is: write `<newPassword><oldPassword>` to the fs-buffer and run CHANGE_PASSWORD. If, and only if, the
|
|
3444
|
-
* SMM accepts the change, log
|
|
3445
|
-
*
|
|
3446
|
-
*
|
|
3470
|
+
* SMM accepts the change, log in for parameter download with the new password until the SMM accepts the login. The
|
|
3471
|
+
* SMM is not restarted, because it does not need a restart to use a new password.
|
|
3472
|
+
*
|
|
3473
|
+
* The SMM acknowledges CHANGE_PASSWORD before it stores the new password. It writes the password to its EEPROM in the
|
|
3474
|
+
* background, refuses every login while the write runs, and closes the session itself when the write is done. A
|
|
3475
|
+
* restart during the write leaves a stored password that the SMM discards at boot, so neither the new nor the old
|
|
3476
|
+
* password logs in and only the master password gets in again. A logout does not wait for the write either. The
|
|
3477
|
+
* first login that the SMM accepts with the new password is therefore the proof that the write is done and that the
|
|
3478
|
+
* SMM stored the intended password.
|
|
3447
3479
|
*
|
|
3448
|
-
*
|
|
3449
|
-
*
|
|
3450
|
-
* After an accepted change the session is therefore closed; log in again with the new password to continue.
|
|
3480
|
+
* After the call, a session for parameter download is open, logged in with the new password, so a caller can go on
|
|
3481
|
+
* with SMM commands without a new login. Log out with {@link logoutFromSmm} when done.
|
|
3451
3482
|
*
|
|
3452
3483
|
* @param deviceRef - A reference to the target device.
|
|
3484
|
+
* @param username - The name of the logged-in user, 4 to 24 printable ASCII characters, for the login with the new
|
|
3485
|
+
* password. Pass the same name the user logged in with: it tells who configured the SMM.
|
|
3453
3486
|
* @param oldPassword - The current SMM password.
|
|
3454
3487
|
* @param newPassword - The new password to set for the SMM, 4 to 24 printable ASCII characters.
|
|
3455
3488
|
* @param commandTimeout - The timeout in milliseconds for the CHANGE_PASSWORD command. On firmware below 5.2.0 it also
|
|
3456
|
-
* covers the FoE write of the fs-buffer, because the buffer is written before the command is issued.
|
|
3457
|
-
*
|
|
3458
|
-
*
|
|
3459
|
-
* retry after a timeout, the SMM may still be running the change.
|
|
3489
|
+
* covers the FoE write of the fs-buffer, because the buffer is written before the command is issued. Defaults to
|
|
3490
|
+
* 12000: the drive gives up on the SMM after 10 s, so do not set it lower. If it runs out, the result of the change
|
|
3491
|
+
* is unknown. Do not retry after a timeout, because the SMM may still be running the change.
|
|
3460
3492
|
* @param responsePollingInterval - The interval in milliseconds between polling for the response.
|
|
3461
|
-
* @param
|
|
3462
|
-
*
|
|
3463
|
-
*
|
|
3464
|
-
*
|
|
3465
|
-
*
|
|
3466
|
-
*
|
|
3467
|
-
*
|
|
3468
|
-
*
|
|
3469
|
-
* @throws {Error} If a password is not 1 (old) or 4 (new) to 24 printable ASCII characters, if the SMM rejects the
|
|
3470
|
-
* change (the SMM is then not restarted and the session stays open), if the logout after an accepted change fails
|
|
3471
|
-
* (the SMM is then not restarted), or if the restart fails.
|
|
3493
|
+
* @param verifyTimeout - How long to keep trying the login with the new password, in milliseconds, counted from the
|
|
3494
|
+
* accepted change. Defaults to 10000.
|
|
3495
|
+
* @param verifyInterval - The pause in milliseconds between two login attempts with the new password. Defaults to
|
|
3496
|
+
* 500.
|
|
3497
|
+
*
|
|
3498
|
+
* @throws {Error} If the username is not 4, or a password not 1 (old) or 4 (new), to 24 printable ASCII characters
|
|
3499
|
+
* (nothing is sent then), if the SMM rejects the change (the session then stays open), or if the SMM does not
|
|
3500
|
+
* accept a login with the new password within `verifyTimeout`.
|
|
3472
3501
|
*
|
|
3473
3502
|
* @remarks
|
|
3474
|
-
* Completion means the SMM accepted
|
|
3475
|
-
*
|
|
3503
|
+
* Completion means the SMM accepted a login with the new password, so the new password is stored.
|
|
3504
|
+
*
|
|
3505
|
+
* When the SMM does not accept the new password within `verifyTimeout`, the stored password is unknown. Do not run
|
|
3506
|
+
* the change again. Try to log in with the new password, then with the old one.
|
|
3507
|
+
*
|
|
3508
|
+
* Do not restart the SMM, and do not power the device off, until the call completes. Either one during the write
|
|
3509
|
+
* leaves a stored password that the SMM discards.
|
|
3476
3510
|
*
|
|
3477
3511
|
* Rules a caller has to follow, because the SMM and the drive do not enforce them for you:
|
|
3478
3512
|
*
|
|
@@ -3484,28 +3518,32 @@ class MotionMasterReqResClient {
|
|
|
3484
3518
|
* - When the SMM rejects the change, the response does not say why: a wrong old password, a missing or expired
|
|
3485
3519
|
* session and an SMM that is still booting all produce the same "the SMM rejected the request (status 2)".
|
|
3486
3520
|
*/
|
|
3487
|
-
changeSmmPassword(deviceRef, oldPassword, newPassword, commandTimeout = 12000, responsePollingInterval = 500,
|
|
3521
|
+
changeSmmPassword(deviceRef, username, oldPassword, newPassword, commandTimeout = 12000, responsePollingInterval = 500, verifyTimeout = 10000, verifyInterval = 500) {
|
|
3488
3522
|
return (0, rxjs_1.defer)(() => {
|
|
3489
|
-
(0, smm_1.
|
|
3490
|
-
(0, smm_1.
|
|
3523
|
+
(0, smm_1.assertValidSmmCredential)(username, 'Username', smm_1.SMM_USERNAME_MIN_LENGTH);
|
|
3524
|
+
(0, smm_1.assertValidSmmCredential)(oldPassword, 'Old password');
|
|
3525
|
+
(0, smm_1.assertValidSmmCredential)(newPassword, 'New password', smm_1.SMM_PASSWORD_MIN_LENGTH);
|
|
3491
3526
|
const command = (0, os_command_1.createSmmAcyclicHandlerOsCommand)(os_command_1.SmmAcyclicHandlerSubcommandId.CHANGE_PASSWORD);
|
|
3492
3527
|
const content = (0, smm_1.packSmmChangePassword)(newPassword, oldPassword);
|
|
3493
3528
|
// @ts-ignore
|
|
3494
3529
|
return this.runOsCommandAndWriteFsBuffer(deviceRef, command, content, commandTimeout, responsePollingInterval);
|
|
3495
3530
|
}).pipe((0, operators_1.last)(), (0, operators_1.concatMap)((response) => {
|
|
3496
3531
|
if (response.request !== 'succeeded') {
|
|
3497
|
-
throw new Error(`Changing SMM password failed: ${(0, os_command_1.describeOsCommandFailure)(response)}. The password was not changed
|
|
3532
|
+
throw new Error(`Changing SMM password failed: ${(0, os_command_1.describeOsCommandFailure)(response)}. The password was not changed.`);
|
|
3498
3533
|
}
|
|
3499
|
-
//
|
|
3500
|
-
//
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3534
|
+
// The SMM acknowledges the change before it writes the password to its EEPROM, and refuses logins until the
|
|
3535
|
+
// write is done. A restart during the write makes the SMM discard the stored password (issue #185), so the
|
|
3536
|
+
// first accepted login with the new password is what ends the write, not a logout or a delay. That login's
|
|
3537
|
+
// session stays open for the caller.
|
|
3538
|
+
const deadline = Date.now() + verifyTimeout;
|
|
3539
|
+
return (0, rxjs_1.defer)(() => this.loginToSmmForParameterDownload(deviceRef, username, newPassword, undefined, responsePollingInterval)).pipe((0, operators_1.retry)({
|
|
3540
|
+
delay: (err) => Date.now() + verifyInterval < deadline
|
|
3541
|
+
? (0, rxjs_1.timer)(verifyInterval)
|
|
3542
|
+
: (0, rxjs_1.throwError)(() => {
|
|
3543
|
+
var _a;
|
|
3544
|
+
return new Error(`The SMM accepted the password change, but did not accept a login with the new password within ${verifyTimeout} ms, so the stored password is unknown. Do not change the password again; try to log in with the new password, then with the old one. ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
|
3545
|
+
}),
|
|
3546
|
+
}), (0, operators_1.last)(null, undefined));
|
|
3509
3547
|
}), (0, operators_1.last)(null, undefined), (0, operators_1.map)(() => undefined));
|
|
3510
3548
|
}
|
|
3511
3549
|
/**
|
|
@@ -3610,45 +3648,94 @@ class MotionMasterReqResClient {
|
|
|
3610
3648
|
* The commands execute only when subscribed to, ensuring a strict sequence.
|
|
3611
3649
|
*/
|
|
3612
3650
|
updateSmmSoftware(deviceRef, username, password, buffer, crc, chunkSize = 1000, commandTimeout = 30000, responsePollingInterval = 1000, fsBufferReadWriteTimeout = 120000) {
|
|
3651
|
+
return this.updateSmmSoftwareWithProgress(deviceRef, username, password, buffer, crc, chunkSize, commandTimeout, responsePollingInterval, fsBufferReadWriteTimeout).pipe((0, operators_1.last)(), (0, operators_1.map)(() => undefined));
|
|
3652
|
+
}
|
|
3653
|
+
/**
|
|
3654
|
+
* Performs a complete SMM software update like {@link updateSmmSoftware}, reporting progress as it goes.
|
|
3655
|
+
*
|
|
3656
|
+
* A {@link SmmSoftwareUpdateProgress} value is emitted when each stage starts, once per transmitted chunk,
|
|
3657
|
+
* and a final `done` value after the SMM restart has been triggered. Use it to show the user what the
|
|
3658
|
+
* update is doing; transmitting a large software file can take many minutes.
|
|
3659
|
+
*
|
|
3660
|
+
* Parameters are the same as for {@link updateSmmSoftware}.
|
|
3661
|
+
*
|
|
3662
|
+
* @throws {Error} If `buffer` is empty or `chunkSize` is not a positive integer. Nothing is sent to the device then.
|
|
3663
|
+
* @throws {Error} If any step in the software update process fails.
|
|
3664
|
+
*/
|
|
3665
|
+
updateSmmSoftwareWithProgress(deviceRef, username, password, buffer, crc, chunkSize = 1000, commandTimeout = 30000, responsePollingInterval = 1000, fsBufferReadWriteTimeout = 120000) {
|
|
3613
3666
|
const fileSize = buffer.byteLength;
|
|
3614
|
-
//
|
|
3667
|
+
// A NaN chunk size (e.g. from a disabled form control) would otherwise skip every chunk
|
|
3668
|
+
// and finalize an update with no software transmitted.
|
|
3669
|
+
if (!Number.isInteger(chunkSize) || chunkSize <= 0) {
|
|
3670
|
+
return (0, rxjs_1.throwError)(() => new Error(`Invalid SMM software update chunk size: ${chunkSize}. It must be a positive integer.`));
|
|
3671
|
+
}
|
|
3672
|
+
if (fileSize === 0) {
|
|
3673
|
+
return (0, rxjs_1.throwError)(() => new Error('The SMM software file is empty.'));
|
|
3674
|
+
}
|
|
3675
|
+
const chunks = Math.ceil(fileSize / chunkSize); // Total number of chunks to send.
|
|
3676
|
+
// Reports the stage, then runs its work. Deferred, so each step starts only after the previous one completes.
|
|
3677
|
+
const step = (stage, work, chunk = 0) => (0, rxjs_1.concat)((0, rxjs_1.of)({ stage, chunk, chunks }), (0, rxjs_1.defer)(work).pipe((0, operators_1.ignoreElements)()));
|
|
3615
3678
|
const observables = [
|
|
3616
3679
|
// Step 1: Ensure any existing session is logged out before starting the update.
|
|
3617
|
-
(
|
|
3680
|
+
step('logout', () => this.logoutFromSmm(deviceRef)),
|
|
3618
3681
|
// Step 2: Log in to the device specifically for software update permissions.
|
|
3619
|
-
(
|
|
3682
|
+
step('login', () => this.loginToSmmForSoftwareUpdate(deviceRef, username, password)),
|
|
3620
3683
|
// Step 3: Inform the device about the incoming file size for proper setup.
|
|
3621
|
-
(
|
|
3684
|
+
step('configure', () => this.configureSmmSoftwareUpdate(deviceRef, fileSize)),
|
|
3622
3685
|
];
|
|
3623
|
-
const n = Math.ceil(fileSize / chunkSize); // Total number of chunks to send.
|
|
3624
3686
|
// Step 4: Transmit the update file chunk-by-chunk.
|
|
3625
3687
|
const transmitSoftwareCommand = (0, os_command_1.createSmmAcyclicHandlerOsCommand)(os_command_1.SmmAcyclicHandlerSubcommandId.TRANSMIT_SOFTWARE);
|
|
3626
|
-
for (let i = 0; i <
|
|
3688
|
+
for (let i = 0; i < chunks; i++) {
|
|
3627
3689
|
const start = i * chunkSize;
|
|
3628
|
-
const
|
|
3629
|
-
|
|
3630
|
-
observables.push((0, rxjs_1.defer)(() => this.runOsCommandAndWriteFsBuffer(deviceRef, transmitSoftwareCommand, fsBufferContent, commandTimeout, responsePollingInterval, false, fsBufferReadWriteTimeout).pipe((0, operators_1.last)(), // Only consider the final response of the command.
|
|
3690
|
+
const fsBufferContent = buffer.slice(start, start + chunkSize);
|
|
3691
|
+
observables.push(step('transmit', () => this.runOsCommandAndWriteFsBuffer(deviceRef, transmitSoftwareCommand, fsBufferContent, commandTimeout, responsePollingInterval, false, fsBufferReadWriteTimeout).pipe((0, operators_1.last)(), // Only consider the final response of the command.
|
|
3631
3692
|
(0, operators_1.map)((response) => {
|
|
3632
3693
|
if (response.request !== 'succeeded') {
|
|
3633
|
-
throw new Error(`Transmitting SMM software chunk ${i + 1}/${
|
|
3694
|
+
throw new Error(`Transmitting SMM software chunk ${i + 1}/${chunks} failed: ${(0, os_command_1.describeOsCommandFailure)(response)}.`);
|
|
3634
3695
|
}
|
|
3635
|
-
})
|
|
3636
|
-
)));
|
|
3696
|
+
})), i + 1));
|
|
3637
3697
|
}
|
|
3638
|
-
// Step 5:
|
|
3698
|
+
// Step 5: Send the CRC to validate data integrity and finalize the update.
|
|
3639
3699
|
const packedCrc = (0, smm_1.packSmmSoftwareUpdateCrc)(crc);
|
|
3640
3700
|
const finalizeSoftwareUpdateCommand = (0, os_command_1.createSmmAcyclicHandlerOsCommand)(os_command_1.SmmAcyclicHandlerSubcommandId.FINALIZE_SOFTWARE_UPDATE);
|
|
3641
|
-
|
|
3642
|
-
observables.push((0, rxjs_1.defer)(() =>
|
|
3701
|
+
observables.push(step('finalize', () =>
|
|
3643
3702
|
// @ts-ignore
|
|
3644
3703
|
this.runOsCommandAndWriteFsBuffer(deviceRef, finalizeSoftwareUpdateCommand, packedCrc).pipe((0, operators_1.last)(), (0, operators_1.map)((response) => {
|
|
3645
3704
|
if (response.request !== 'succeeded') {
|
|
3646
|
-
throw new Error(`Finalizing SMM software update failed: ${(0, os_command_1.describeOsCommandFailure)(response)}.`);
|
|
3705
|
+
throw new Error(`Finalizing SMM software update failed: ${(0, os_command_1.describeOsCommandFailure)(response)}. Check that the CRC matches the software file.`);
|
|
3647
3706
|
}
|
|
3648
|
-
}))),
|
|
3649
|
-
// Step
|
|
3650
|
-
(
|
|
3651
|
-
return (0, rxjs_1.concat)(...observables)
|
|
3707
|
+
})), chunks),
|
|
3708
|
+
// Step 6: Trigger a restart so the new software takes effect.
|
|
3709
|
+
step('restart', () => this.triggerSmmRestart(deviceRef), chunks), (0, rxjs_1.of)({ stage: 'done', chunk: chunks, chunks }));
|
|
3710
|
+
return (0, rxjs_1.concat)(...observables);
|
|
3711
|
+
}
|
|
3712
|
+
/**
|
|
3713
|
+
* Waits until the SMM reports the expected software version.
|
|
3714
|
+
*
|
|
3715
|
+
* After an SMM software update the SMM restarts and boots the new software, which can take around 30 s.
|
|
3716
|
+
* Reads may fail or return the previous version during that time, so the version is polled until it
|
|
3717
|
+
* matches or the timeout expires.
|
|
3718
|
+
*
|
|
3719
|
+
* @param deviceRef - Reference to the target device.
|
|
3720
|
+
* @param expectedVersion - The expected SMM software version, bitwise encoded (e.g. 25344 for v99.0).
|
|
3721
|
+
* @param timeout - (Optional) How long to wait, in ms. Defaults to 90,000 ms.
|
|
3722
|
+
* @param pollingInterval - (Optional) Delay between reads, in ms. Defaults to 2000 ms.
|
|
3723
|
+
*
|
|
3724
|
+
* @throws {Error} If the SMM does not report the expected version within `timeout`.
|
|
3725
|
+
*/
|
|
3726
|
+
waitForSmmFirmwareVersion(deviceRef, expectedVersion, timeout = 90000, pollingInterval = 2000) {
|
|
3727
|
+
let lastVersion;
|
|
3728
|
+
let lastError;
|
|
3729
|
+
const read$ = (0, rxjs_1.defer)(() => this.readSmmFirmwareVersionAsNumber(deviceRef)).pipe((0, rxjs_1.tap)((version) => (lastVersion = version)), (0, operators_1.catchError)((err) => {
|
|
3730
|
+
lastError = err;
|
|
3731
|
+
return rxjs_1.EMPTY;
|
|
3732
|
+
}));
|
|
3733
|
+
return read$.pipe((0, operators_1.repeat)({ delay: () => (lastVersion === expectedVersion ? rxjs_1.EMPTY : (0, rxjs_1.timer)(pollingInterval)) }), (0, operators_1.filter)((version) => version === expectedVersion), (0, operators_1.take)(1), (0, operators_1.map)(() => undefined), (0, operators_1.takeUntil)((0, rxjs_1.timer)(timeout).pipe((0, operators_1.mergeMap)(() => {
|
|
3734
|
+
const reported = lastVersion !== undefined
|
|
3735
|
+
? `it reports v${(0, smm_1.bitwiseDecodeNumberVersion)(lastVersion)}`
|
|
3736
|
+
: `reading the version failed: ${lastError instanceof Error ? lastError.message : String(lastError)}`;
|
|
3737
|
+
return (0, rxjs_1.throwError)(() => new Error(`SMM did not report software v${(0, smm_1.bitwiseDecodeNumberVersion)(expectedVersion)} within ${timeout / 1000} s after the update; ${reported}.`));
|
|
3738
|
+
}))));
|
|
3652
3739
|
}
|
|
3653
3740
|
/**
|
|
3654
3741
|
* Resolves whether an automated SMM software update is allowed for the specified device.
|
|
@@ -3660,7 +3747,8 @@ class MotionMasterReqResClient {
|
|
|
3660
3747
|
*
|
|
3661
3748
|
* The automation is only permitted when **all** of the following conditions are met:
|
|
3662
3749
|
* - The device is a recognized Somanet product (`isValidProduct`).
|
|
3663
|
-
* - The device belongs to the Circulo product family (`isCirculo`).
|
|
3750
|
+
* - The device belongs to the Circulo product family (`isCirculo`). ACTILINK-JD / JD Duo devices are judged
|
|
3751
|
+
* by the underlying Circulo from their hardware description, see {@link resolveEncoderProductId}.
|
|
3664
3752
|
* - The binary is encrypted (`isBinaryEncrypted`).
|
|
3665
3753
|
* - The binary targets one of the supported SMM versions — v1.4 (260) or v2.56 (568) (`isAllowedBinaryVersion`).
|
|
3666
3754
|
* - If targeting v2.56, the device SoC firmware version is ≥ 5.5.0 (`canInstallV256`).
|
|
@@ -3675,7 +3763,7 @@ class MotionMasterReqResClient {
|
|
|
3675
3763
|
*
|
|
3676
3764
|
* @returns A promise that resolves to an {@link SmmAutomatedUpdate} object containing:
|
|
3677
3765
|
* - `isValidProduct` — Whether the device maps to a known Somanet product.
|
|
3678
|
-
* - `isCirculo` — Whether the device
|
|
3766
|
+
* - `isCirculo` — Whether the device is a Circulo or a Circulo-based ACTILINK-JD / JD Duo.
|
|
3679
3767
|
* - `isCirculo7` — Whether the device is a Circulo 7.
|
|
3680
3768
|
* - `isCirculo9` — Whether the device is a Circulo 9.
|
|
3681
3769
|
* - `isDeviceLw1` — Whether the device SMM firmware is below v1.3 (version < 259).
|
|
@@ -3689,10 +3777,11 @@ class MotionMasterReqResClient {
|
|
|
3689
3777
|
* - `binaryVersion` — The target SMM version encoded in the binary header.
|
|
3690
3778
|
*/
|
|
3691
3779
|
resolveSmmAutomatedUpdateConditions(deviceRef, buffer) {
|
|
3692
|
-
var _a, _b;
|
|
3780
|
+
var _a, _b, _c;
|
|
3693
3781
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
3694
3782
|
const device = yield (0, rxjs_1.lastValueFrom)(this.resolveDevice(deviceRef));
|
|
3695
|
-
|
|
3783
|
+
// ACTILINK-JD / JD Duo wrap a Circulo; judge the product by the underlying Circulo device.
|
|
3784
|
+
const product = (0, product_1.findSomanetProductById)((0, product_1.resolveEncoderProductId)((_a = device === null || device === void 0 ? void 0 : device.hardwareDescription) !== null && _a !== void 0 ? _a : device === null || device === void 0 ? void 0 : device.serialNumber));
|
|
3696
3785
|
const isValidProduct = !!product;
|
|
3697
3786
|
// v1.4 (no safe torque), v2.56 (safe torque)
|
|
3698
3787
|
const allowedTargetSmmVersions = [260, 568];
|
|
@@ -3722,7 +3811,7 @@ class MotionMasterReqResClient {
|
|
|
3722
3811
|
const binaryHeader = (0, smm_1.parseSmmBinaryHeaders)(buffer);
|
|
3723
3812
|
const binaryInformation = (0, smm_1.getInformationFromSmmFirmwareVersion)(binaryHeader.swVersion);
|
|
3724
3813
|
// Allow installation of smm v2.56 only if device SoC firmware version is >=5.5.0
|
|
3725
|
-
const canInstallV256 = binaryHeader.swVersion === 568 ? semver.gte((
|
|
3814
|
+
const canInstallV256 = binaryHeader.swVersion === 568 ? semver.gte((_b = semver.coerce(deviceFirmwareVersion)) !== null && _b !== void 0 ? _b : '0.0.0', '5.5.0') : true;
|
|
3726
3815
|
const isCirculo = product.family === 'Circulo';
|
|
3727
3816
|
const isDeviceLw1 = deviceSmmVersion < 259; // <v1.3
|
|
3728
3817
|
const isV990 = deviceSmmVersion === 25344;
|
|
@@ -3732,7 +3821,7 @@ class MotionMasterReqResClient {
|
|
|
3732
3821
|
const isAllowedBinaryVersion = allowedTargetSmmVersions.includes(binaryInformation.version);
|
|
3733
3822
|
const isCirculo7 = isCirculo && product.group === 'Circulo 7';
|
|
3734
3823
|
const isCirculo9 = isCirculo && product.group === 'Circulo 9';
|
|
3735
|
-
const deviceSupportsStreaming = semver.gte((
|
|
3824
|
+
const deviceSupportsStreaming = semver.gte((_c = semver.coerce(deviceFirmwareVersion)) !== null && _c !== void 0 ? _c : '0.0.0', '5.2.0');
|
|
3736
3825
|
const isAutomationAllowed = isCirculo &&
|
|
3737
3826
|
isBinaryEncrypted &&
|
|
3738
3827
|
isAllowedBinaryVersion &&
|
|
@@ -3760,7 +3849,8 @@ class MotionMasterReqResClient {
|
|
|
3760
3849
|
});
|
|
3761
3850
|
}
|
|
3762
3851
|
/**
|
|
3763
|
-
* Performs an automated, multi-step SMM software update to an encrypted firmware version on a Circulo device
|
|
3852
|
+
* Performs an automated, multi-step SMM software update to an encrypted firmware version on a Circulo device,
|
|
3853
|
+
* or a Circulo-based ACTILINK-JD / JD Duo (judged by the underlying Circulo).
|
|
3764
3854
|
*
|
|
3765
3855
|
* Before installing the target binary, this method evaluates the device state by calling
|
|
3766
3856
|
* {@link resolveSmmAutomatedUpdateConditions}. Based on the result, it determines which
|
|
@@ -3774,20 +3864,29 @@ class MotionMasterReqResClient {
|
|
|
3774
3864
|
* - **v99.0 + Circulo 9**: v77.1 → target
|
|
3775
3865
|
* - **v77.0 or v77.1**: target only
|
|
3776
3866
|
*
|
|
3777
|
-
*
|
|
3867
|
+
* After each installation the SMM restarts. The next step starts only once the SMM reports the
|
|
3868
|
+
* version that was just installed (see {@link waitForSmmFirmwareVersion}), so a failed or
|
|
3869
|
+
* unfinished installation stops the sequence with an error instead of continuing blindly.
|
|
3870
|
+
*
|
|
3871
|
+
* Transfer settings that are not given are resolved from the device firmware with
|
|
3872
|
+
* {@link resolveSmmSoftwareUpdateTimings}. On firmware without fs-buffer streaming (< v5.2.0),
|
|
3873
|
+
* `chunkSize` is always 1000 and `commandTimeout` is at least 420,000 ms.
|
|
3778
3874
|
*
|
|
3779
3875
|
* @param deviceRef - Reference to the target device.
|
|
3780
3876
|
* @param username - Username used for SMM authentication.
|
|
3781
3877
|
* @param password - Password used for SMM authentication.
|
|
3782
3878
|
* @param buffer - The target encrypted firmware binary as a `Uint8Array`.
|
|
3783
3879
|
* @param crc - CRC32 checksum of the target firmware binary for integrity verification.
|
|
3784
|
-
* @param chunkSize - (Optional) Size of each data chunk transmitted per step, in bytes.
|
|
3785
|
-
*
|
|
3786
|
-
*
|
|
3880
|
+
* @param chunkSize - (Optional) Size of each data chunk transmitted per step, in bytes.
|
|
3881
|
+
* Defaults to 1,000,000 with streaming (one command per file) and 1000 without.
|
|
3882
|
+
* @param commandTimeout - (Optional) Timeout for each OS command in ms.
|
|
3883
|
+
* Defaults to 180,000 ms with streaming and 420,000 ms without.
|
|
3787
3884
|
* @param responsePollingInterval - (Optional) Polling interval for command responses in ms. Defaults to 1000 ms.
|
|
3788
|
-
* @param fsBufferReadWriteTimeout - (Optional) Timeout for fs-buffer read/write operations in ms.
|
|
3885
|
+
* @param fsBufferReadWriteTimeout - (Optional) Timeout for fs-buffer read/write operations in ms.
|
|
3886
|
+
* Defaults to 150,000 ms with streaming and 5000 ms without.
|
|
3789
3887
|
*
|
|
3790
|
-
* @returns An `Observable
|
|
3888
|
+
* @returns An `Observable` of {@link SmmAutomatedUpdateProgress} that reports each step and stage,
|
|
3889
|
+
* and completes when the full installation sequence finishes successfully.
|
|
3791
3890
|
*
|
|
3792
3891
|
* @throws {Error} With message `'Unknown product. Cannot perform SMM software update.'` if the device
|
|
3793
3892
|
* serial number does not map to a known Somanet product.
|
|
@@ -3795,104 +3894,88 @@ class MotionMasterReqResClient {
|
|
|
3795
3894
|
* e.g. the device is not a Circulo, the binary is not encrypted, the target version is
|
|
3796
3895
|
* unsupported, the SoC firmware is below v5.5.0 (for SMM v2.56), or the device is
|
|
3797
3896
|
* already on SMM v1.3+.
|
|
3798
|
-
* @throws {Error} If any
|
|
3897
|
+
* @throws {Error} If any installation step fails, or the SMM does not report the installed version afterwards.
|
|
3799
3898
|
*/
|
|
3800
|
-
updateSmmSoftwareToEncrypted(deviceRef, username, password, buffer, crc, chunkSize
|
|
3801
|
-
return
|
|
3802
|
-
(
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
* subscriber only when we tell it to do so via `subscriber.<method>`. Error handling from the outside
|
|
3810
|
-
* will not pick-up the errors from here as this is "fire-and-forget" type of function.
|
|
3811
|
-
*
|
|
3812
|
-
* @example
|
|
3813
|
-
*
|
|
3814
|
-
* ```typescript
|
|
3815
|
-
* asyncHandler(async (req, res, next) => {
|
|
3816
|
-
* somePromiseThatRejects().then(...) // ← no await, no .catch()
|
|
3817
|
-
* // or a setTimeout, event emitter, etc.
|
|
3818
|
-
*
|
|
3819
|
-
* // handler finishes immediately
|
|
3820
|
-
})
|
|
3821
|
-
*/
|
|
3822
|
-
try {
|
|
3823
|
-
const automatedInstall = yield this.resolveSmmAutomatedUpdateConditions(deviceRef, buffer);
|
|
3824
|
-
if (!automatedInstall.isValidProduct) {
|
|
3825
|
-
subscriber.error(new Error('Unknown product. Cannot perform SMM software update.'));
|
|
3826
|
-
return;
|
|
3827
|
-
}
|
|
3828
|
-
let observables = [];
|
|
3829
|
-
if (automatedInstall.isAutomationAllowed) {
|
|
3830
|
-
const v990crc = 0xd1a3bb72;
|
|
3831
|
-
const v990Buffer = yield Promise.resolve().then(() => tslib_1.__importStar(require('./smm/40701_DEBUG_Update_V99.0'))).then((m) => Uint8Array.from(atob(m.smmBinaryV990), (c, _) => { var _a; return (_a = c.codePointAt(0)) !== null && _a !== void 0 ? _a : 0; }));
|
|
3832
|
-
const v770crc = 0xe290a20a;
|
|
3833
|
-
const v770Buffer = yield Promise.resolve().then(() => tslib_1.__importStar(require('./smm/40701_DEBUG_Update_V77.0'))).then((m) => Uint8Array.from(atob(m.smmBinaryV770), (c, _) => { var _a; return (_a = c.codePointAt(0)) !== null && _a !== void 0 ? _a : 0; }));
|
|
3834
|
-
const v771crc = 0xa4a11125;
|
|
3835
|
-
const v771Buffer = yield Promise.resolve().then(() => tslib_1.__importStar(require('./smm/40701_DEBUG_Update_V77.1'))).then((m) => Uint8Array.from(atob(m.smmBinaryV771), (c, _) => { var _a; return (_a = c.codePointAt(0)) !== null && _a !== void 0 ? _a : 0; }));
|
|
3836
|
-
const v990$ = (0, rxjs_1.defer)(() => this.updateSmmSoftware(deviceRef, username, password, new Uint8Array(v990Buffer), v990crc, chunkSize, commandTimeout, responsePollingInterval, fsBufferReadWriteTimeout));
|
|
3837
|
-
const v770$ = (0, rxjs_1.defer)(() => this.updateSmmSoftware(deviceRef, username, password, new Uint8Array(v770Buffer), v770crc, chunkSize, commandTimeout, responsePollingInterval, fsBufferReadWriteTimeout));
|
|
3838
|
-
const v771$ = (0, rxjs_1.defer)(() => this.updateSmmSoftware(deviceRef, username, password, new Uint8Array(v771Buffer), v771crc, chunkSize, commandTimeout, responsePollingInterval, fsBufferReadWriteTimeout));
|
|
3839
|
-
const userVersion$ = (0, rxjs_1.defer)(() => this.updateSmmSoftware(deviceRef, username, password, buffer, crc, chunkSize, commandTimeout, responsePollingInterval, fsBufferReadWriteTimeout));
|
|
3840
|
-
if (automatedInstall.isDeviceLw1) {
|
|
3841
|
-
if (automatedInstall.isCirculo7) {
|
|
3842
|
-
observables = [v990$, v770$, userVersion$];
|
|
3843
|
-
}
|
|
3844
|
-
else if (automatedInstall.isCirculo9) {
|
|
3845
|
-
observables = [v990$, v771$, userVersion$];
|
|
3846
|
-
}
|
|
3847
|
-
}
|
|
3848
|
-
else if (automatedInstall.isV990) {
|
|
3849
|
-
if (automatedInstall.isCirculo7) {
|
|
3850
|
-
observables = [v770$, userVersion$];
|
|
3851
|
-
}
|
|
3852
|
-
else if (automatedInstall.isCirculo9) {
|
|
3853
|
-
observables = [v771$, userVersion$];
|
|
3854
|
-
}
|
|
3855
|
-
}
|
|
3856
|
-
else if (automatedInstall.isV770 || automatedInstall.isV771) {
|
|
3857
|
-
observables = [userVersion$];
|
|
3858
|
-
}
|
|
3859
|
-
(0, rxjs_1.concat)(...observables).subscribe({
|
|
3860
|
-
next: () => {
|
|
3861
|
-
subscriber.next();
|
|
3862
|
-
},
|
|
3863
|
-
error: (err) => {
|
|
3864
|
-
subscriber.error(err);
|
|
3865
|
-
},
|
|
3866
|
-
complete: () => {
|
|
3867
|
-
subscriber.complete();
|
|
3868
|
-
},
|
|
3869
|
-
});
|
|
3870
|
-
}
|
|
3871
|
-
else {
|
|
3872
|
-
const reasons = [];
|
|
3873
|
-
if (!automatedInstall.isCirculo) {
|
|
3874
|
-
reasons.push('The device is not part of the Circulo family.');
|
|
3875
|
-
}
|
|
3876
|
-
if (!automatedInstall.isDeviceLw1) {
|
|
3877
|
-
reasons.push('The device SMM software version is not lower than 1.3.');
|
|
3878
|
-
}
|
|
3879
|
-
if (!automatedInstall.isBinaryEncrypted) {
|
|
3880
|
-
reasons.push('The SMM binary is not encrypted.');
|
|
3881
|
-
}
|
|
3882
|
-
if (!automatedInstall.isAllowedBinaryVersion) {
|
|
3883
|
-
reasons.push('The target SMM software version is not supported for encrypted update.');
|
|
3884
|
-
}
|
|
3885
|
-
if (!automatedInstall.canInstallV256) {
|
|
3886
|
-
reasons.push('The device firmware must be minimum v5.5.0 for installing SMM v2.56.');
|
|
3887
|
-
}
|
|
3888
|
-
subscriber.error(new Error(`The following conditions are not met for SMM software automated update: ${reasons.join(' ')}`));
|
|
3889
|
-
}
|
|
3899
|
+
updateSmmSoftwareToEncrypted(deviceRef, username, password, buffer, crc, chunkSize, commandTimeout, responsePollingInterval = 1000, fsBufferReadWriteTimeout) {
|
|
3900
|
+
return (0, rxjs_1.defer)(() => (0, rxjs_1.from)(this.resolveSmmAutomatedUpdateConditions(deviceRef, buffer))).pipe((0, operators_1.mergeMap)((conditions) => {
|
|
3901
|
+
if (!conditions.isValidProduct) {
|
|
3902
|
+
throw new Error('Unknown product. Cannot perform SMM software update.');
|
|
3903
|
+
}
|
|
3904
|
+
if (!conditions.isAutomationAllowed) {
|
|
3905
|
+
const reasons = [];
|
|
3906
|
+
if (!conditions.isCirculo) {
|
|
3907
|
+
reasons.push('The device is not a Circulo or a Circulo-based ACTILINK-JD.');
|
|
3890
3908
|
}
|
|
3891
|
-
|
|
3892
|
-
|
|
3909
|
+
if (!conditions.isDeviceLw1 && !conditions.isV990 && !conditions.isV770 && !conditions.isV771) {
|
|
3910
|
+
reasons.push('The device SMM software version is not lower than 1.3.');
|
|
3893
3911
|
}
|
|
3894
|
-
|
|
3895
|
-
|
|
3912
|
+
if (!conditions.isBinaryEncrypted) {
|
|
3913
|
+
reasons.push('The SMM binary is not encrypted.');
|
|
3914
|
+
}
|
|
3915
|
+
if (!conditions.isAllowedBinaryVersion) {
|
|
3916
|
+
reasons.push('The target SMM software version is not supported for encrypted update.');
|
|
3917
|
+
}
|
|
3918
|
+
if (!conditions.canInstallV256) {
|
|
3919
|
+
reasons.push('The device firmware must be minimum v5.5.0 for installing SMM v2.56.');
|
|
3920
|
+
}
|
|
3921
|
+
throw new Error(`The following conditions are not met for SMM software automated update: ${reasons.join(' ')}`);
|
|
3922
|
+
}
|
|
3923
|
+
const timings = (0, smm_1.resolveSmmSoftwareUpdateTimings)(conditions.deviceSupportsStreaming);
|
|
3924
|
+
const transfer = conditions.deviceSupportsStreaming
|
|
3925
|
+
? {
|
|
3926
|
+
chunkSize: chunkSize !== null && chunkSize !== void 0 ? chunkSize : timings.chunkSize,
|
|
3927
|
+
commandTimeout: commandTimeout !== null && commandTimeout !== void 0 ? commandTimeout : timings.commandTimeout,
|
|
3928
|
+
fsBufferReadWriteTimeout: fsBufferReadWriteTimeout !== null && fsBufferReadWriteTimeout !== void 0 ? fsBufferReadWriteTimeout : timings.fsBufferReadWriteTimeout,
|
|
3929
|
+
}
|
|
3930
|
+
: {
|
|
3931
|
+
// Without streaming the fs-buffer is written before each command, which only works with 1000-byte chunks.
|
|
3932
|
+
chunkSize: timings.chunkSize,
|
|
3933
|
+
commandTimeout: Math.max(commandTimeout !== null && commandTimeout !== void 0 ? commandTimeout : 0, timings.commandTimeout),
|
|
3934
|
+
fsBufferReadWriteTimeout: fsBufferReadWriteTimeout !== null && fsBufferReadWriteTimeout !== void 0 ? fsBufferReadWriteTimeout : timings.fsBufferReadWriteTimeout,
|
|
3935
|
+
};
|
|
3936
|
+
const decode = (base64) => Uint8Array.from(atob(base64), (c) => { var _a; return (_a = c.codePointAt(0)) !== null && _a !== void 0 ? _a : 0; });
|
|
3937
|
+
const v990 = {
|
|
3938
|
+
version: 25344,
|
|
3939
|
+
load: () => Promise.resolve().then(() => tslib_1.__importStar(require('./smm/40701_DEBUG_Update_V99.0'))).then((m) => ({
|
|
3940
|
+
buffer: decode(m.smmBinaryV990),
|
|
3941
|
+
crc: 0xd1a3bb72,
|
|
3942
|
+
})),
|
|
3943
|
+
};
|
|
3944
|
+
const v770 = {
|
|
3945
|
+
version: 19712,
|
|
3946
|
+
load: () => Promise.resolve().then(() => tslib_1.__importStar(require('./smm/40701_DEBUG_Update_V77.0'))).then((m) => ({
|
|
3947
|
+
buffer: decode(m.smmBinaryV770),
|
|
3948
|
+
crc: 0xe290a20a,
|
|
3949
|
+
})),
|
|
3950
|
+
};
|
|
3951
|
+
const v771 = {
|
|
3952
|
+
version: 19713,
|
|
3953
|
+
load: () => Promise.resolve().then(() => tslib_1.__importStar(require('./smm/40701_DEBUG_Update_V77.1'))).then((m) => ({
|
|
3954
|
+
buffer: decode(m.smmBinaryV771),
|
|
3955
|
+
crc: 0xa4a11125,
|
|
3956
|
+
})),
|
|
3957
|
+
};
|
|
3958
|
+
const target = { version: conditions.binaryVersion, load: () => Promise.resolve({ buffer, crc }) };
|
|
3959
|
+
const intermediate = conditions.isCirculo7 ? v770 : conditions.isCirculo9 ? v771 : undefined;
|
|
3960
|
+
let plan = [];
|
|
3961
|
+
if (conditions.isV770 || conditions.isV771) {
|
|
3962
|
+
plan = [target];
|
|
3963
|
+
}
|
|
3964
|
+
else if (intermediate && conditions.isV990) {
|
|
3965
|
+
plan = [intermediate, target];
|
|
3966
|
+
}
|
|
3967
|
+
else if (intermediate && conditions.isDeviceLw1) {
|
|
3968
|
+
plan = [v990, intermediate, target];
|
|
3969
|
+
}
|
|
3970
|
+
if (plan.length === 0) {
|
|
3971
|
+
throw new Error('Cannot determine the SMM update path: the device is neither a Circulo 7 nor a Circulo 9.');
|
|
3972
|
+
}
|
|
3973
|
+
const steps = plan.length;
|
|
3974
|
+
return (0, rxjs_1.concat)(...plan.map(({ version, load }, i) => {
|
|
3975
|
+
const progress = { step: i + 1, steps, version };
|
|
3976
|
+
return (0, rxjs_1.concat)((0, rxjs_1.defer)(() => (0, rxjs_1.from)(load())).pipe((0, operators_1.mergeMap)((software) => this.updateSmmSoftwareWithProgress(deviceRef, username, password, software.buffer, software.crc, transfer.chunkSize, transfer.commandTimeout, responsePollingInterval, transfer.fsBufferReadWriteTimeout)), (0, operators_1.map)((install) => (Object.assign(Object.assign({}, progress), { stage: 'install', install })))), (0, rxjs_1.of)(Object.assign(Object.assign({}, progress), { stage: 'verify' })), (0, rxjs_1.defer)(() => this.waitForSmmFirmwareVersion(deviceRef, version)).pipe((0, operators_1.ignoreElements)()), (0, rxjs_1.of)(Object.assign(Object.assign({}, progress), { stage: 'installed' })));
|
|
3977
|
+
}));
|
|
3978
|
+
}));
|
|
3896
3979
|
}
|
|
3897
3980
|
/**
|
|
3898
3981
|
* Transmits SMM parameters to the specified device.
|