@nordicsemiconductor/pc-nrfconnect-shared 120.0.0 → 122.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 (123) hide show
  1. package/Changelog.md +48 -0
  2. package/coverage/cobertura-coverage.xml +1358 -1215
  3. package/nrfutil/device/__mocks__/device.ts +2 -0
  4. package/nrfutil/device/batch.ts +18 -2
  5. package/nrfutil/device/common.ts +17 -38
  6. package/nrfutil/device/device.ts +2 -0
  7. package/nrfutil/device/deviceInfo.ts +70 -0
  8. package/nrfutil/device/erase.ts +2 -2
  9. package/nrfutil/device/firmwareRead.ts +2 -2
  10. package/nrfutil/device/getCoreInfo.ts +2 -2
  11. package/nrfutil/device/getFwInfo.ts +2 -2
  12. package/nrfutil/device/getProtectionStatus.ts +2 -2
  13. package/nrfutil/device/list.ts +4 -30
  14. package/nrfutil/device/program.ts +4 -4
  15. package/nrfutil/device/recover.ts +2 -2
  16. package/nrfutil/device/reset.ts +2 -2
  17. package/nrfutil/device/setMcuState.ts +2 -5
  18. package/nrfutil/device/setProtectionStatus.ts +2 -2
  19. package/nrfutil/index.ts +1 -1
  20. package/nrfutil/moduleVersion.ts +20 -0
  21. package/nrfutil/sandboxTypes.ts +14 -0
  22. package/package.json +1 -1
  23. package/src/About/DeviceCard.tsx +6 -5
  24. package/src/About/SupportCard.tsx +2 -2
  25. package/src/App/shared.scss +0 -1
  26. package/src/Device/DeviceSelector/BasicDeviceInfo.tsx +12 -6
  27. package/src/Device/DeviceSelector/DeviceList/AnimatedList.tsx +1 -1
  28. package/src/Device/DeviceSelector/DeviceList/Device.tsx +7 -5
  29. package/src/Device/DeviceSelector/DeviceList/DeviceList.tsx +23 -5
  30. package/src/Device/DeviceSelector/DeviceList/EditDeviceButtons.tsx +7 -6
  31. package/src/Device/DeviceSelector/DeviceList/MoreDeviceInfo.tsx +26 -30
  32. package/src/Device/DeviceSelector/DeviceList/edit-device-buttons.scss +1 -2
  33. package/src/Device/DeviceSelector/DeviceSelector.test.tsx +26 -46
  34. package/src/Device/DeviceSelector/DeviceSelector.tsx +40 -15
  35. package/src/Device/DeviceSelector/Favorite.tsx +10 -10
  36. package/src/Device/DeviceSelector/basic-device-info.scss +0 -12
  37. package/src/Device/deviceInfo/deviceInfo.ts +2 -2
  38. package/src/Device/deviceLister.ts +59 -50
  39. package/src/Device/deviceSetup.ts +28 -10
  40. package/src/Device/deviceSlice.ts +148 -84
  41. package/src/Device/jprogOperations.ts +56 -27
  42. package/src/Device/sdfuOperations.ts +25 -31
  43. package/src/Dropdown/Dropdown.tsx +2 -2
  44. package/src/InlineInput/InlineInput.tsx +25 -3
  45. package/src/InlineInput/NumberInlineInput.tsx +24 -8
  46. package/src/InlineInput/NumberInputWithDropdown.tsx +131 -0
  47. package/src/index.ts +3 -0
  48. package/src/logging/sendInitialLogMessages.ts +13 -9
  49. package/src/utils/logLibVersions.ts +1 -1
  50. package/src/utils/systemReport.ts +3 -3
  51. package/typings/generated/nrfutil/device/__mocks__/device.d.ts +1 -0
  52. package/typings/generated/nrfutil/device/__mocks__/device.d.ts.map +1 -1
  53. package/typings/generated/nrfutil/device/batch.d.ts +3 -2
  54. package/typings/generated/nrfutil/device/batch.d.ts.map +1 -1
  55. package/typings/generated/nrfutil/device/common.d.ts +6 -33
  56. package/typings/generated/nrfutil/device/common.d.ts.map +1 -1
  57. package/typings/generated/nrfutil/device/device.d.ts +13 -12
  58. package/typings/generated/nrfutil/device/device.d.ts.map +1 -1
  59. package/typings/generated/nrfutil/device/deviceInfo.d.ts +40 -0
  60. package/typings/generated/nrfutil/device/deviceInfo.d.ts.map +1 -0
  61. package/typings/generated/nrfutil/device/erase.d.ts +2 -2
  62. package/typings/generated/nrfutil/device/erase.d.ts.map +1 -1
  63. package/typings/generated/nrfutil/device/firmwareRead.d.ts +2 -2
  64. package/typings/generated/nrfutil/device/firmwareRead.d.ts.map +1 -1
  65. package/typings/generated/nrfutil/device/getCoreInfo.d.ts +2 -2
  66. package/typings/generated/nrfutil/device/getCoreInfo.d.ts.map +1 -1
  67. package/typings/generated/nrfutil/device/getFwInfo.d.ts +2 -2
  68. package/typings/generated/nrfutil/device/getFwInfo.d.ts.map +1 -1
  69. package/typings/generated/nrfutil/device/getProtectionStatus.d.ts +2 -2
  70. package/typings/generated/nrfutil/device/getProtectionStatus.d.ts.map +1 -1
  71. package/typings/generated/nrfutil/device/list.d.ts +3 -3
  72. package/typings/generated/nrfutil/device/list.d.ts.map +1 -1
  73. package/typings/generated/nrfutil/device/program.d.ts +2 -2
  74. package/typings/generated/nrfutil/device/program.d.ts.map +1 -1
  75. package/typings/generated/nrfutil/device/recover.d.ts +2 -2
  76. package/typings/generated/nrfutil/device/recover.d.ts.map +1 -1
  77. package/typings/generated/nrfutil/device/reset.d.ts +2 -2
  78. package/typings/generated/nrfutil/device/reset.d.ts.map +1 -1
  79. package/typings/generated/nrfutil/device/setMcuState.d.ts +2 -2
  80. package/typings/generated/nrfutil/device/setMcuState.d.ts.map +1 -1
  81. package/typings/generated/nrfutil/device/setProtectionStatus.d.ts +2 -2
  82. package/typings/generated/nrfutil/device/setProtectionStatus.d.ts.map +1 -1
  83. package/typings/generated/nrfutil/index.d.ts +2 -1
  84. package/typings/generated/nrfutil/index.d.ts.map +1 -1
  85. package/typings/generated/nrfutil/moduleVersion.d.ts +4 -0
  86. package/typings/generated/nrfutil/moduleVersion.d.ts.map +1 -1
  87. package/typings/generated/nrfutil/sandboxTypes.d.ts +9 -0
  88. package/typings/generated/nrfutil/sandboxTypes.d.ts.map +1 -1
  89. package/typings/generated/src/About/DeviceCard.d.ts.map +1 -1
  90. package/typings/generated/src/Device/DeviceSelector/BasicDeviceInfo.d.ts.map +1 -1
  91. package/typings/generated/src/Device/DeviceSelector/DeviceList/Device.d.ts +0 -1
  92. package/typings/generated/src/Device/DeviceSelector/DeviceList/Device.d.ts.map +1 -1
  93. package/typings/generated/src/Device/DeviceSelector/DeviceList/DeviceList.d.ts.map +1 -1
  94. package/typings/generated/src/Device/DeviceSelector/DeviceList/EditDeviceButtons.d.ts.map +1 -1
  95. package/typings/generated/src/Device/DeviceSelector/DeviceList/MoreDeviceInfo.d.ts +0 -1
  96. package/typings/generated/src/Device/DeviceSelector/DeviceList/MoreDeviceInfo.d.ts.map +1 -1
  97. package/typings/generated/src/Device/DeviceSelector/DeviceSelector.d.ts.map +1 -1
  98. package/typings/generated/src/Device/DeviceSelector/Favorite.d.ts +0 -1
  99. package/typings/generated/src/Device/DeviceSelector/Favorite.d.ts.map +1 -1
  100. package/typings/generated/src/Device/deviceLister.d.ts +1 -15
  101. package/typings/generated/src/Device/deviceLister.d.ts.map +1 -1
  102. package/typings/generated/src/Device/deviceSetup.d.ts +6 -5
  103. package/typings/generated/src/Device/deviceSetup.d.ts.map +1 -1
  104. package/typings/generated/src/Device/deviceSlice.d.ts +12 -15
  105. package/typings/generated/src/Device/deviceSlice.d.ts.map +1 -1
  106. package/typings/generated/src/Device/jprogOperations.d.ts.map +1 -1
  107. package/typings/generated/src/Device/sdfuOperations.d.ts.map +1 -1
  108. package/typings/generated/src/Dropdown/Dropdown.d.ts +2 -2
  109. package/typings/generated/src/Dropdown/Dropdown.d.ts.map +1 -1
  110. package/typings/generated/src/InlineInput/InlineInput.d.ts +3 -0
  111. package/typings/generated/src/InlineInput/InlineInput.d.ts.map +1 -1
  112. package/typings/generated/src/InlineInput/NumberInlineInput.d.ts +8 -4
  113. package/typings/generated/src/InlineInput/NumberInlineInput.d.ts.map +1 -1
  114. package/typings/generated/src/InlineInput/NumberInputWithDropdown.d.ts +19 -0
  115. package/typings/generated/src/InlineInput/NumberInputWithDropdown.d.ts.map +1 -0
  116. package/typings/generated/src/index.d.ts +3 -1
  117. package/typings/generated/src/index.d.ts.map +1 -1
  118. package/typings/generated/src/logging/sendInitialLogMessages.d.ts.map +1 -1
  119. package/typings/generated/src/utils/systemReport.d.ts +1 -1
  120. package/typings/generated/src/utils/systemReport.d.ts.map +1 -1
  121. package/src/Device/DeviceSelector/DeviceList/device.scss +0 -28
  122. package/src/Device/DeviceSelector/DeviceList/more-device-info.scss +0 -33
  123. package/src/Device/DeviceSelector/favorite.scss +0 -17
@@ -4,11 +4,7 @@
4
4
  * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
5
  */
6
6
 
7
- import {
8
- DeviceTraits,
9
- NrfutilDevice,
10
- NrfutilDeviceWithSerialnumber,
11
- } from '../../nrfutil/device/common';
7
+ import { DeviceTraits, NrfutilDevice } from '../../nrfutil/device/common';
12
8
  import NrfutilDeviceLib from '../../nrfutil/device/device';
13
9
  import logger from '../logging';
14
10
  import type { AppThunk, RootState } from '../store';
@@ -23,7 +19,14 @@ import {
23
19
  WaitForDevice,
24
20
  } from './deviceAutoSelectSlice';
25
21
  import { closeDeviceSetupDialog } from './deviceSetupSlice';
26
- import { addDevice, Device, removeDevice } from './deviceSlice';
22
+ import {
23
+ addDevice,
24
+ Device,
25
+ getDevice,
26
+ removeDevice,
27
+ selectDevice,
28
+ setSelectedDeviceInfo,
29
+ } from './deviceSlice';
27
30
  import { isDeviceInDFUBootloader } from './sdfuOperations';
28
31
 
29
32
  let stopNrfutilDevice: (callback?: () => void) => void;
@@ -52,6 +55,11 @@ const shouldAutoReselect = (
52
55
  // device is still connected
53
56
  if (disconnectionTime === undefined) return false;
54
57
 
58
+ // device does not have sn
59
+ if (!addedDevice.serialNumber) {
60
+ return false;
61
+ }
62
+
55
63
  // The device that was selected when disconnection occurred is not yet connected
56
64
  if (addedDevice.serialNumber !== autoReselectDevice.serialNumber) {
57
65
  return false;
@@ -100,28 +108,6 @@ const initAutoReconnectTimeout =
100
108
  );
101
109
  };
102
110
 
103
- /**
104
- * Wrap the device form nrfutil-device to make the Device type consistent
105
- *
106
- * @param {NrfutilDevice} device The input device from nrfutil-device
107
- * @returns {NrfutilDevice} The updated device
108
- */
109
- export const wrapDeviceFromNrfdl = (device: NrfutilDevice): Device => ({
110
- ...device,
111
- boardVersion: device.jlink?.boardVersion ?? undefined,
112
- serialport: device.serialPorts?.[0] ?? undefined,
113
- serialNumber: device.serialNumber ?? `fallback-serialnumber-${device.id}`,
114
- });
115
-
116
- /**
117
- * Wrap the device form nrfutil-device to make the Device type consistent
118
- *
119
- * @param {NrfutilDevice[]} devices The input devices from nrfutil-device
120
- * @returns {NrfutilDevice[]} The updated devices
121
- */
122
- export const wrapDevicesFromNrfdl = (devices: NrfutilDevice[]): Device[] =>
123
- devices.map(wrapDeviceFromNrfdl);
124
-
125
111
  export const hasValidDeviceTraits = (
126
112
  deviceTraits: DeviceTraits,
127
113
  requiredTraits: DeviceTraits
@@ -143,17 +129,17 @@ const removeDeviceFromList =
143
129
  ): AppThunk =>
144
130
  (dispatch, getState) => {
145
131
  if (
146
- removedDevice.serialNumber ===
147
- getState().device.selectedSerialNumber
132
+ removedDevice.serialNumber !==
133
+ getState().device.selectedDevice?.serialNumber
148
134
  ) {
149
- onDeviceDeselected();
135
+ dispatch(removeDevice(removedDevice));
136
+ onDeviceDisconnected(removedDevice);
137
+ return;
150
138
  }
151
139
 
152
- if (
153
- removedDevice.serialNumber ===
154
- getState().device.selectedSerialNumber &&
155
- !getState().deviceAutoSelect.waitForDevice
156
- ) {
140
+ onDeviceDeselected();
141
+
142
+ if (!getState().deviceAutoSelect.waitForDevice) {
157
143
  dispatch(closeDeviceSetupDialog());
158
144
  }
159
145
 
@@ -176,22 +162,21 @@ export const startWatchingDevices =
176
162
  doSelectDevice: (device: Device, autoReselected: boolean) => void
177
163
  ): AppThunk<RootState, void> =>
178
164
  (dispatch, getState) => {
179
- const onDeviceArrived = (device: NrfutilDeviceWithSerialnumber) => {
165
+ const onDeviceArrived = async (device: NrfutilDevice) => {
180
166
  if (hasValidDeviceTraits(device.traits, deviceListing)) {
181
- device = wrapDeviceFromNrfdl(device);
182
167
  if (
183
168
  device.serialNumber &&
184
- !getState().device.devices.has(device.serialNumber)
169
+ !getState().device.devices.find(
170
+ d => d.serialNumber === device.serialNumber
171
+ )
185
172
  ) {
186
173
  onDeviceConnected(device);
187
174
  }
188
175
 
189
- const sn = getState().device.selectedSerialNumber;
190
176
  const disconnectionTime =
191
177
  getState().deviceAutoSelect.disconnectionTime;
192
178
  const autoSelectDevice = getState().deviceAutoSelect.device;
193
- const selectedDevice =
194
- sn !== null ? getState().device.devices.get(sn) : undefined;
179
+ const selectedDevice = getState().device.selectedDevice;
195
180
 
196
181
  const result = shouldAutoReselect(
197
182
  device,
@@ -202,17 +187,32 @@ export const startWatchingDevices =
202
187
  );
203
188
 
204
189
  dispatch(addDevice(device));
205
- const deviceWithPersistedData = getState().device.devices.get(
206
- device.serialNumber
190
+ const deviceWithPersistedData = getState().device.devices.find(
191
+ d => d.serialNumber === device.serialNumber
207
192
  );
208
193
 
209
194
  if (!deviceWithPersistedData) return;
210
195
 
211
- if (result) {
196
+ // We might get multiple events with the same info so no to trigger auto reconnect multiple times we
197
+ // only do it once per device id
198
+ if (
199
+ result &&
200
+ getState().deviceAutoSelect.lastArrivedDeviceId !==
201
+ deviceWithPersistedData.id
202
+ ) {
203
+ dispatch(
204
+ setLastArrivedDeviceId(deviceWithPersistedData.id)
205
+ );
206
+
207
+ const deviceInfo = await NrfutilDeviceLib.deviceInfo(
208
+ device
209
+ );
210
+
212
211
  logger.info(
213
212
  `Auto Reconnecting Device SN: ${deviceWithPersistedData.serialNumber}`
214
213
  );
215
214
  doSelectDevice(deviceWithPersistedData, true);
215
+ dispatch(setSelectedDeviceInfo(deviceInfo));
216
216
  } else if (
217
217
  deviceWithPersistedData.serialNumber ===
218
218
  getState().deviceAutoSelect.device?.serialNumber
@@ -220,6 +220,10 @@ export const startWatchingDevices =
220
220
  const waitForDevice =
221
221
  getState().deviceAutoSelect.waitForDevice;
222
222
 
223
+ const deviceInfo = await NrfutilDeviceLib.deviceInfo(
224
+ device
225
+ );
226
+
223
227
  // Device lib might fail to advertise that a device has left before it rejoins (Mainly OSx)
224
228
  // but we still need to trigger the onSuccess if a device 'reappeared' with a different 'id'
225
229
  // and there is an outstanding waitForDevice Request. In this case the disconnectionTime was
@@ -245,8 +249,7 @@ export const startWatchingDevices =
245
249
  deviceWithPersistedData
246
250
  )) ||
247
251
  (waitForDevice.when === 'applicationMode' &&
248
- deviceWithPersistedData.dfuTriggerInfo !==
249
- null) ||
252
+ deviceInfo?.dfuTriggerVersion) ||
250
253
  (selectedDevice &&
251
254
  waitForDevice.when === 'sameTraits' &&
252
255
  hasSameDeviceTraits(
@@ -269,6 +272,9 @@ export const startWatchingDevices =
269
272
  clearWaitForDeviceTimeout(waitForDevice.once)
270
273
  );
271
274
 
275
+ dispatch(selectDevice(deviceWithPersistedData));
276
+ dispatch(setSelectedDeviceInfo(deviceInfo));
277
+
272
278
  if (waitForDevice.onSuccess)
273
279
  waitForDevice.onSuccess(
274
280
  deviceWithPersistedData
@@ -288,8 +294,9 @@ export const startWatchingDevices =
288
294
  const waitForDevice =
289
295
  getState().deviceAutoSelect.waitForDevice;
290
296
  if (
297
+ device.serialNumber &&
291
298
  device.serialNumber ===
292
- getState().deviceAutoSelect.device?.serialNumber
299
+ getState().deviceAutoSelect.device?.serialNumber
293
300
  ) {
294
301
  if (waitForDevice) {
295
302
  dispatch(setArrivedButWrongWhen(undefined));
@@ -351,8 +358,10 @@ export const startWatchingDevices =
351
358
  const autoSelectSN = getAutoSelectDeviceCLISerial();
352
359
 
353
360
  if (autoSelectSN !== undefined) {
354
- const autoSelectDevice =
355
- getState().device.devices.get(autoSelectSN);
361
+ const autoSelectDevice = getDevice(
362
+ getState(),
363
+ autoSelectSN
364
+ );
356
365
 
357
366
  if (autoSelectDevice)
358
367
  doSelectDevice(autoSelectDevice, true);
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
5
5
  */
6
+ import { DeviceInfo } from '../../nrfutil/device/deviceInfo';
6
7
  import logger from '../logging';
7
8
  import describeError from '../logging/describeError';
8
9
  import { AppThunk, RootState } from '../store';
@@ -33,15 +34,24 @@ export interface JprogEntry {
33
34
  }
34
35
 
35
36
  export interface DeviceSetup {
36
- supportsProgrammingMode: (device: Device) => boolean; // Return true if this device can be programed using this interface e.g. MCU Boot or DFU
37
- getFirmwareOptions: (device: Device) => {
37
+ supportsProgrammingMode: (
38
+ device: Device,
39
+ deviceInfo?: DeviceInfo
40
+ ) => boolean; // Return true if this device can be programed using this interface e.g. MCU Boot or DFU
41
+ getFirmwareOptions: (
42
+ device: Device,
43
+ deviceInfo?: DeviceInfo
44
+ ) => {
38
45
  key: string;
39
46
  description?: string;
40
47
  programDevice: (
41
48
  onProgress: (progress: number, message?: string) => void
42
49
  ) => AppThunk<RootState, Promise<Device>>;
43
50
  }[]; // The list of all firmware that can be applied for this device with the program function for that fw item
44
- isExpectedFirmware: (device: Device) => AppThunk<
51
+ isExpectedFirmware: (
52
+ device: Device,
53
+ deviceInfo?: DeviceInfo
54
+ ) => AppThunk<
45
55
  RootState,
46
56
  Promise<{
47
57
  device: Device;
@@ -66,6 +76,7 @@ export const prepareDevice =
66
76
  deviceSetupConfig: DeviceSetupConfig,
67
77
  onSuccess: (device: Device) => void,
68
78
  onFail: (reason?: unknown) => void,
79
+ deviceInfo: DeviceInfo | undefined,
69
80
  checkCurrentFirmwareVersion = true,
70
81
  requireUserConfirmation = true
71
82
  ): AppThunk<RootState, Promise<void>> =>
@@ -74,18 +85,22 @@ export const prepareDevice =
74
85
  onSuccess(d);
75
86
  dispatch(closeDeviceSetupDialog());
76
87
  };
88
+
77
89
  const validDeviceSetups = deviceSetupConfig.deviceSetups.filter(
78
- deviceSetup => deviceSetup.supportsProgrammingMode(device)
90
+ deviceSetup =>
91
+ deviceSetup.supportsProgrammingMode(device, deviceInfo)
79
92
  );
80
93
 
81
94
  const possibleFirmware = validDeviceSetups
82
- .map(deviceSetup => deviceSetup.getFirmwareOptions(device))
95
+ .map(deviceSetup =>
96
+ deviceSetup.getFirmwareOptions(device, deviceInfo)
97
+ )
83
98
  .flat();
84
99
 
85
100
  if (possibleFirmware.length === 0) {
86
101
  logger.info(
87
102
  `Connected to device with serial number: ${device.serialNumber} ` +
88
- `and family: ${device.jlink?.deviceFamily || 'Unknown'} `
103
+ `and family: ${device.devkit?.deviceFamily || 'Unknown'} `
89
104
  );
90
105
  if (deviceSetupConfig.allowCustomDevice) {
91
106
  logger.info(
@@ -110,7 +125,7 @@ export const prepareDevice =
110
125
  try {
111
126
  // eslint-disable-next-line no-await-in-loop
112
127
  const result = await dispatch(
113
- deviceSetup.isExpectedFirmware(device)
128
+ deviceSetup.isExpectedFirmware(device, deviceInfo)
114
129
  );
115
130
  device = result.device;
116
131
  if (result.validFirmware) {
@@ -153,6 +168,7 @@ export const prepareDevice =
153
168
  };
154
169
 
155
170
  const cancelAction = async () => {
171
+ dispatch(closeDeviceSetupDialog());
156
172
  let i = 0;
157
173
  do {
158
174
  const deviceSetup = validDeviceSetups[i];
@@ -226,7 +242,8 @@ export const setupDevice =
226
242
  device: Device,
227
243
  deviceSetupConfig: DeviceSetupConfig,
228
244
  onDeviceIsReady: (device: Device) => void,
229
- doDeselectDevice: () => void
245
+ doDeselectDevice: () => void,
246
+ deviceInfo: DeviceInfo | undefined
230
247
  ): AppThunk<RootState> =>
231
248
  (dispatch, getState) =>
232
249
  dispatch(
@@ -241,7 +258,7 @@ export const setupDevice =
241
258
  // while that is still in progress select some other device
242
259
  // if both were to call onDeviceIsReady the app might have unexpected side effects
243
260
  if (
244
- getState().device.selectedSerialNumber ===
261
+ getState().device.selectedDevice?.serialNumber ===
245
262
  d.serialNumber
246
263
  ) {
247
264
  onDeviceIsReady(d);
@@ -254,6 +271,7 @@ export const setupDevice =
254
271
  );
255
272
  logger.error(describeError(error));
256
273
  doDeselectDevice();
257
- }
274
+ },
275
+ deviceInfo
258
276
  )
259
277
  );
@@ -8,7 +8,8 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
8
8
  import type { AutoDetectTypes } from '@serialport/bindings-cpp';
9
9
  import { SerialPortOpenOptions } from 'serialport';
10
10
 
11
- import { NrfutilDevice, SerialPort } from '../../nrfutil/device/common';
11
+ import { DeviceInfo } from '../../nrfutil';
12
+ import { NrfutilDevice } from '../../nrfutil/device/common';
12
13
  import type { RootState } from '../store';
13
14
  import {
14
15
  getPersistedIsFavorite,
@@ -20,65 +21,74 @@ import {
20
21
  } from '../utils/persistentStore';
21
22
 
22
23
  export interface Device extends NrfutilDevice {
23
- serialNumber: string;
24
- boardVersion?: string;
25
24
  nickname?: string;
26
- serialport?: SerialPort;
27
25
  favorite?: boolean;
28
- id: number;
29
26
  persistedSerialPortOptions?: SerialPortOpenOptions<AutoDetectTypes>;
30
27
  }
31
28
 
29
+ const findDeviceItem = (
30
+ devices: Device[],
31
+ id: number,
32
+ serialNumber?: string
33
+ ) => {
34
+ const index = devices.findIndex(
35
+ d => d.id === id || d.serialNumber === serialNumber
36
+ );
37
+
38
+ return { index, device: devices[index] };
39
+ };
40
+
32
41
  const updateDevice = (
33
42
  state: DeviceState,
34
- serialNumber: string,
35
- updateToMergeIn: Partial<Device>
43
+ updateToMergeIn: Partial<Device>,
44
+ id: number,
45
+ serialNumber?: string
36
46
  ) => {
37
- const device = state.devices.get(serialNumber);
47
+ const device = findDeviceItem(state.devices, id, serialNumber).device;
38
48
  if (device) {
39
49
  Object.assign(device, updateToMergeIn);
40
50
  }
41
51
  };
42
52
 
43
53
  export interface DeviceState {
44
- devices: Map<string, Device>;
45
- deviceInfo: Device | null;
46
- selectedSerialNumber: string | null;
54
+ devices: Device[];
47
55
  readbackProtection: 'unknown' | 'protected' | 'unprotected';
56
+ selectedDevice?: Device;
57
+ selectedDeviceInfo?: DeviceInfo;
48
58
  }
49
59
 
50
60
  const initialState: DeviceState = {
51
- devices: new Map(),
52
- selectedSerialNumber: null,
53
- deviceInfo: null,
61
+ devices: [],
54
62
  readbackProtection: 'unknown',
55
63
  };
56
64
 
57
65
  const setPersistedData = (device: Device) => {
58
- const newDevice = {
59
- ...device,
60
- favorite: getPersistedIsFavorite(device.serialNumber),
61
- nickname: getPersistedNickname(device.serialNumber),
62
- };
63
-
64
- const persistedSerialPortSettings = getPersistedSerialPortSettings(
65
- newDevice.serialNumber
66
- );
67
-
68
- if (persistedSerialPortSettings) {
69
- const path =
70
- newDevice.serialPorts?.[persistedSerialPortSettings.vComIndex]
71
- ?.comName;
72
-
73
- if (path) {
74
- newDevice.persistedSerialPortOptions = {
75
- ...persistedSerialPortSettings.serialPortOptions,
76
- path,
77
- };
66
+ if (device.serialNumber) {
67
+ const newDevice = { ...device };
68
+ newDevice.favorite = getPersistedIsFavorite(device.serialNumber);
69
+ newDevice.nickname = getPersistedNickname(device.serialNumber);
70
+
71
+ const persistedSerialPortSettings = getPersistedSerialPortSettings(
72
+ device.serialNumber
73
+ );
74
+
75
+ if (persistedSerialPortSettings) {
76
+ const path =
77
+ newDevice.serialPorts?.[persistedSerialPortSettings.vComIndex]
78
+ ?.comName;
79
+
80
+ if (path) {
81
+ newDevice.persistedSerialPortOptions = {
82
+ ...persistedSerialPortSettings.serialPortOptions,
83
+ path,
84
+ };
85
+ }
78
86
  }
87
+
88
+ return newDevice;
79
89
  }
80
90
 
81
- return newDevice;
91
+ return device;
82
92
  };
83
93
 
84
94
  const slice = createSlice({
@@ -89,104 +99,158 @@ const slice = createSlice({
89
99
  * Indicates that a device has been selected.
90
100
  */
91
101
  selectDevice: (state, action: PayloadAction<Device>) => {
92
- state.selectedSerialNumber = action.payload.serialNumber;
102
+ state.selectedDevice = action.payload;
103
+ },
104
+
105
+ setSelectedDeviceInfo: (
106
+ state,
107
+ action: PayloadAction<DeviceInfo | undefined>
108
+ ) => {
109
+ state.selectedDeviceInfo = action.payload;
93
110
  },
94
111
 
95
112
  /*
96
113
  * Indicates that the currently selected device has been deselected.
97
114
  */
98
115
  deselectDevice: state => {
99
- state.selectedSerialNumber = null;
100
- state.deviceInfo = null;
116
+ state.selectedDevice = undefined;
117
+ state.selectedDeviceInfo = undefined;
101
118
  state.readbackProtection = 'unknown';
102
119
  },
103
120
 
104
121
  addDevice: (state, action: PayloadAction<Device>) => {
105
- state.devices.set(
106
- action.payload.serialNumber,
107
- setPersistedData(action.payload)
122
+ const index = state.devices.findIndex(
123
+ item =>
124
+ item.serialNumber === action.payload.serialNumber ||
125
+ item.id === action.payload.id
108
126
  );
127
+
128
+ const device = setPersistedData(action.payload);
129
+ if (index !== -1) {
130
+ state.devices[index] = device;
131
+ } else {
132
+ state.devices.push(device);
133
+ }
109
134
  },
110
135
 
111
136
  persistSerialPortOptions: (
112
137
  state,
113
138
  action: PayloadAction<SerialPortOpenOptions<AutoDetectTypes>>
114
139
  ) => {
115
- if (state.selectedSerialNumber === null) return;
140
+ if (!state.selectedDevice) return;
116
141
 
117
- const selectedDevice = state.devices.get(
118
- state.selectedSerialNumber
119
- );
142
+ const selectedDevice = state.selectedDevice;
120
143
 
121
144
  if (selectedDevice) {
122
145
  const vComIndex = selectedDevice.serialPorts?.findIndex(
123
146
  e => e.comName === action.payload.path
124
147
  );
125
148
 
126
- if (vComIndex !== undefined && vComIndex !== -1) {
149
+ if (
150
+ selectedDevice.serialNumber &&
151
+ vComIndex !== undefined &&
152
+ vComIndex !== -1
153
+ ) {
127
154
  // eslint-disable-next-line @typescript-eslint/no-unused-vars -- used to filter out the path property
128
155
  const { path: _, ...serialPortOptions } = action.payload;
129
156
 
130
157
  persistSerialPortSettingsToStore(
131
- state.selectedSerialNumber,
158
+ selectedDevice.serialNumber,
132
159
  {
133
160
  serialPortOptions,
134
161
  vComIndex,
135
162
  }
136
163
  );
137
164
 
138
- state.devices.set(state.selectedSerialNumber, {
139
- ...selectedDevice,
140
- persistedSerialPortOptions: action.payload,
141
- });
165
+ updateDevice(
166
+ state,
167
+ {
168
+ ...selectedDevice,
169
+ persistedSerialPortOptions: action.payload,
170
+ },
171
+ selectedDevice.id,
172
+ selectedDevice.serialNumber
173
+ );
142
174
  }
143
175
  }
144
176
  },
145
177
 
146
178
  removeDevice: (state, action: PayloadAction<Device>) => {
147
- state.devices.delete(action.payload.serialNumber);
148
-
149
- if (state.selectedSerialNumber === action.payload.serialNumber) {
150
- state.selectedSerialNumber = null;
151
- state.deviceInfo = null;
179
+ const index = findDeviceItem(
180
+ state.devices,
181
+ action.payload.id,
182
+ action.payload.serialNumber
183
+ ).index;
184
+ state.devices.splice(index, 1);
185
+
186
+ if (action.payload.id === state.selectedDevice?.id) {
187
+ state.selectedDevice = undefined;
188
+ state.selectedDeviceInfo = undefined;
152
189
  }
153
190
  },
154
191
 
155
- toggleDeviceFavorited: (state, action: PayloadAction<string>) => {
156
- const newFavoriteState = !state.devices.get(action.payload)
157
- ?.favorite;
158
- persistIsFavorite(action.payload, newFavoriteState);
159
- updateDevice(state, action.payload, {
160
- favorite: newFavoriteState,
161
- });
192
+ toggleDeviceFavorited: (state, action: PayloadAction<Device>) => {
193
+ const device = findDeviceItem(
194
+ state.devices,
195
+ action.payload.id,
196
+ action.payload.serialNumber
197
+ ).device;
198
+
199
+ if (!device.serialNumber) return;
200
+
201
+ const newFavoriteState = !device.favorite;
202
+
203
+ persistIsFavorite(device.serialNumber, newFavoriteState);
204
+ updateDevice(
205
+ state,
206
+ {
207
+ favorite: newFavoriteState,
208
+ },
209
+ action.payload.id,
210
+ action.payload.serialNumber
211
+ );
162
212
  },
163
213
 
164
214
  setDeviceNickname: {
165
- prepare: (serialNumber: string, nickname: string) => ({
166
- payload: { serialNumber, nickname },
215
+ prepare: (device: Device, nickname: string) => ({
216
+ payload: { device, nickname },
167
217
  }),
168
218
  reducer: (
169
219
  state,
170
220
  action: PayloadAction<{
171
- serialNumber: string;
221
+ device: Device;
172
222
  nickname: string;
173
223
  }>
174
224
  ) => {
225
+ if (!action.payload.device.serialNumber) return;
226
+
175
227
  persistNickname(
176
- action.payload.serialNumber,
228
+ action.payload.device.serialNumber,
177
229
  action.payload.nickname
178
230
  );
179
- updateDevice(state, action.payload.serialNumber, {
180
- nickname: action.payload.nickname,
181
- });
231
+ updateDevice(
232
+ state,
233
+ {
234
+ nickname: action.payload.nickname,
235
+ },
236
+ action.payload.device.id,
237
+ action.payload.device.serialNumber
238
+ );
182
239
  },
183
240
  },
184
241
 
185
- resetDeviceNickname: (state, action: PayloadAction<string>) => {
186
- persistNickname(action.payload, '');
187
- updateDevice(state, action.payload, {
188
- nickname: '',
189
- });
242
+ resetDeviceNickname: (state, action: PayloadAction<Device>) => {
243
+ if (!action.payload.serialNumber) return;
244
+
245
+ persistNickname(action.payload.serialNumber, '');
246
+ updateDevice(
247
+ state,
248
+ {
249
+ nickname: '',
250
+ },
251
+ action.payload.id,
252
+ action.payload.serialNumber
253
+ );
190
254
  },
191
255
 
192
256
  setReadbackProtected: (
@@ -204,6 +268,7 @@ export const {
204
268
  deselectDevice,
205
269
  resetDeviceNickname,
206
270
  selectDevice,
271
+ setSelectedDeviceInfo,
207
272
  addDevice,
208
273
  removeDevice,
209
274
  setDeviceNickname,
@@ -213,22 +278,21 @@ export const {
213
278
  },
214
279
  } = slice;
215
280
 
216
- export const getDevice = (serialNumber: string) => (state: RootState) =>
217
- state.device.devices.get(serialNumber);
281
+ export const getDevice = (state: RootState, serialNumber: string) =>
282
+ state.device.devices.find(d => d.serialNumber === serialNumber);
218
283
 
219
284
  export const getDevices = (state: RootState) => state.device.devices;
220
285
 
221
286
  export const deviceIsSelected = (state: RootState) =>
222
- state.device.selectedSerialNumber != null;
287
+ !!state.device.selectedDevice;
288
+
289
+ export const selectedDevice = (state: RootState) => state.device.selectedDevice;
223
290
 
224
- export const selectedDevice = (state: RootState) =>
225
- state.device.selectedSerialNumber !== null
226
- ? state.device.devices.get(state.device.selectedSerialNumber)
227
- : undefined;
291
+ export const selectedDeviceInfo = (state: RootState) =>
292
+ state.device.selectedDevice ? state.device.selectedDeviceInfo : undefined;
228
293
 
229
- export const deviceInfo = (state: RootState) => state.device.deviceInfo;
230
294
  export const selectedSerialNumber = (state: RootState) =>
231
- state.device.selectedSerialNumber;
295
+ state.device.selectedDevice?.serialNumber;
232
296
 
233
297
  export const getReadbackProtection = (state: RootState) =>
234
298
  state.device.readbackProtection;