@zwave-js/nvmedit 13.3.0 → 13.4.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 (235) hide show
  1. package/build/cli.js +5 -5
  2. package/build/cli.js.map +1 -1
  3. package/build/convert.d.ts +15 -14
  4. package/build/convert.d.ts.map +1 -1
  5. package/build/convert.js +647 -389
  6. package/build/convert.js.map +1 -1
  7. package/build/index.d.ts +12 -4
  8. package/build/index.d.ts.map +1 -1
  9. package/build/index.js +20 -2
  10. package/build/index.js.map +1 -1
  11. package/build/index_safe.d.ts +7 -4
  12. package/build/index_safe.d.ts.map +1 -1
  13. package/build/index_safe.js +4 -2
  14. package/build/index_safe.js.map +1 -1
  15. package/build/lib/NVM3.d.ts +53 -0
  16. package/build/lib/NVM3.d.ts.map +1 -0
  17. package/build/lib/NVM3.js +650 -0
  18. package/build/lib/NVM3.js.map +1 -0
  19. package/build/lib/NVM500.d.ts +46 -0
  20. package/build/lib/NVM500.d.ts.map +1 -0
  21. package/build/lib/NVM500.js +413 -0
  22. package/build/lib/NVM500.js.map +1 -0
  23. package/build/lib/common/definitions.d.ts +138 -0
  24. package/build/lib/common/definitions.d.ts.map +1 -0
  25. package/build/lib/common/definitions.js +11 -0
  26. package/build/lib/common/definitions.js.map +1 -0
  27. package/build/lib/common/routeCache.d.ts +18 -0
  28. package/build/lib/common/routeCache.d.ts.map +1 -0
  29. package/build/lib/common/routeCache.js +56 -0
  30. package/build/lib/common/routeCache.js.map +1 -0
  31. package/build/lib/common/sucUpdateEntry.d.ts +10 -0
  32. package/build/lib/common/sucUpdateEntry.d.ts.map +1 -0
  33. package/build/lib/common/sucUpdateEntry.js +35 -0
  34. package/build/lib/common/sucUpdateEntry.js.map +1 -0
  35. package/build/lib/common/utils.d.ts +9 -0
  36. package/build/lib/common/utils.d.ts.map +1 -0
  37. package/build/lib/common/utils.js +52 -0
  38. package/build/lib/common/utils.js.map +1 -0
  39. package/build/lib/io/BufferedNVMReader.d.ts +21 -0
  40. package/build/lib/io/BufferedNVMReader.d.ts.map +1 -0
  41. package/build/lib/io/BufferedNVMReader.js +79 -0
  42. package/build/lib/io/BufferedNVMReader.js.map +1 -0
  43. package/build/lib/io/NVMFileIO.d.ts +24 -0
  44. package/build/lib/io/NVMFileIO.d.ts.map +1 -0
  45. package/build/lib/io/NVMFileIO.js +86 -0
  46. package/build/lib/io/NVMFileIO.js.map +1 -0
  47. package/build/lib/io/NVMMemoryIO.d.ts +20 -0
  48. package/build/lib/io/NVMMemoryIO.d.ts.map +1 -0
  49. package/build/lib/io/NVMMemoryIO.js +48 -0
  50. package/build/lib/io/NVMMemoryIO.js.map +1 -0
  51. package/build/lib/nvm3/adapter.d.ts +33 -0
  52. package/build/lib/nvm3/adapter.d.ts.map +1 -0
  53. package/build/lib/nvm3/adapter.js +903 -0
  54. package/build/lib/nvm3/adapter.js.map +1 -0
  55. package/build/lib/nvm3/consts.d.ts.map +1 -0
  56. package/build/lib/nvm3/consts.js.map +1 -0
  57. package/build/{files → lib/nvm3/files}/ApplicationCCsFile.d.ts +5 -3
  58. package/build/lib/nvm3/files/ApplicationCCsFile.d.ts.map +1 -0
  59. package/build/{files → lib/nvm3/files}/ApplicationCCsFile.js +4 -3
  60. package/build/lib/nvm3/files/ApplicationCCsFile.js.map +1 -0
  61. package/build/{files → lib/nvm3/files}/ApplicationDataFile.d.ts +4 -4
  62. package/build/lib/nvm3/files/ApplicationDataFile.d.ts.map +1 -0
  63. package/build/{files → lib/nvm3/files}/ApplicationDataFile.js +7 -6
  64. package/build/lib/nvm3/files/ApplicationDataFile.js.map +1 -0
  65. package/build/{files → lib/nvm3/files}/ApplicationNameFile.d.ts +5 -3
  66. package/build/lib/nvm3/files/ApplicationNameFile.d.ts.map +1 -0
  67. package/build/{files → lib/nvm3/files}/ApplicationNameFile.js +4 -3
  68. package/build/lib/nvm3/files/ApplicationNameFile.js.map +1 -0
  69. package/build/{files → lib/nvm3/files}/ApplicationRFConfigFile.d.ts +5 -3
  70. package/build/lib/nvm3/files/ApplicationRFConfigFile.d.ts.map +1 -0
  71. package/build/{files → lib/nvm3/files}/ApplicationRFConfigFile.js +4 -3
  72. package/build/lib/nvm3/files/ApplicationRFConfigFile.js.map +1 -0
  73. package/build/{files → lib/nvm3/files}/ApplicationTypeFile.d.ts +5 -3
  74. package/build/lib/nvm3/files/ApplicationTypeFile.d.ts.map +1 -0
  75. package/build/{files → lib/nvm3/files}/ApplicationTypeFile.js +4 -3
  76. package/build/lib/nvm3/files/ApplicationTypeFile.js.map +1 -0
  77. package/build/{files → lib/nvm3/files}/ControllerInfoFile.d.ts +5 -3
  78. package/build/lib/nvm3/files/ControllerInfoFile.d.ts.map +1 -0
  79. package/build/{files → lib/nvm3/files}/ControllerInfoFile.js +4 -3
  80. package/build/lib/nvm3/files/ControllerInfoFile.js.map +1 -0
  81. package/build/{files → lib/nvm3/files}/NVMFile.d.ts +18 -7
  82. package/build/lib/nvm3/files/NVMFile.d.ts.map +1 -0
  83. package/build/{files → lib/nvm3/files}/NVMFile.js +44 -30
  84. package/build/lib/nvm3/files/NVMFile.js.map +1 -0
  85. package/build/{files → lib/nvm3/files}/NodeInfoFiles.d.ts +10 -4
  86. package/build/lib/nvm3/files/NodeInfoFiles.d.ts.map +1 -0
  87. package/build/{files → lib/nvm3/files}/NodeInfoFiles.js +6 -3
  88. package/build/lib/nvm3/files/NodeInfoFiles.js.map +1 -0
  89. package/build/{files → lib/nvm3/files}/ProtocolNodeMaskFiles.d.ts +22 -15
  90. package/build/lib/nvm3/files/ProtocolNodeMaskFiles.d.ts.map +1 -0
  91. package/build/{files → lib/nvm3/files}/ProtocolNodeMaskFiles.js +50 -30
  92. package/build/lib/nvm3/files/ProtocolNodeMaskFiles.js.map +1 -0
  93. package/build/{files → lib/nvm3/files}/RouteCacheFiles.d.ts +8 -17
  94. package/build/lib/nvm3/files/RouteCacheFiles.d.ts.map +1 -0
  95. package/build/{files → lib/nvm3/files}/RouteCacheFiles.js +16 -64
  96. package/build/lib/nvm3/files/RouteCacheFiles.js.map +1 -0
  97. package/build/{files → lib/nvm3/files}/SUCUpdateEntriesFile.d.ts +10 -13
  98. package/build/lib/nvm3/files/SUCUpdateEntriesFile.d.ts.map +1 -0
  99. package/build/{files → lib/nvm3/files}/SUCUpdateEntriesFile.js +15 -42
  100. package/build/lib/nvm3/files/SUCUpdateEntriesFile.js.map +1 -0
  101. package/build/{files → lib/nvm3/files}/VersionFiles.d.ts +7 -5
  102. package/build/lib/nvm3/files/VersionFiles.d.ts.map +1 -0
  103. package/build/{files → lib/nvm3/files}/VersionFiles.js +10 -7
  104. package/build/lib/nvm3/files/VersionFiles.js.map +1 -0
  105. package/build/{files → lib/nvm3/files}/index.d.ts +1 -0
  106. package/build/lib/nvm3/files/index.d.ts.map +1 -0
  107. package/build/{files → lib/nvm3/files}/index.js +1 -0
  108. package/build/lib/nvm3/files/index.js.map +1 -0
  109. package/build/lib/nvm3/object.d.ts +29 -0
  110. package/build/lib/nvm3/object.d.ts.map +1 -0
  111. package/build/lib/nvm3/object.js +118 -0
  112. package/build/lib/nvm3/object.js.map +1 -0
  113. package/build/{nvm3 → lib/nvm3}/page.d.ts +1 -5
  114. package/build/lib/nvm3/page.d.ts.map +1 -0
  115. package/build/lib/nvm3/page.js +37 -0
  116. package/build/lib/nvm3/page.js.map +1 -0
  117. package/build/{nvm3 → lib/nvm3}/utils.d.ts +2 -4
  118. package/build/lib/nvm3/utils.d.ts.map +1 -0
  119. package/build/lib/nvm3/utils.js +143 -0
  120. package/build/lib/nvm3/utils.js.map +1 -0
  121. package/build/lib/nvm500/EntryParsers.d.ts.map +1 -0
  122. package/build/lib/nvm500/EntryParsers.js.map +1 -0
  123. package/build/lib/nvm500/adapter.d.ts +22 -0
  124. package/build/lib/nvm500/adapter.d.ts.map +1 -0
  125. package/build/lib/nvm500/adapter.js +371 -0
  126. package/build/lib/nvm500/adapter.js.map +1 -0
  127. package/build/lib/nvm500/impls/Bridge_6_6x.d.ts +3 -0
  128. package/build/lib/nvm500/impls/Bridge_6_6x.d.ts.map +1 -0
  129. package/build/{nvm500/parsers → lib/nvm500/impls}/Bridge_6_6x.js +1 -1
  130. package/build/lib/nvm500/impls/Bridge_6_6x.js.map +1 -0
  131. package/build/lib/nvm500/impls/Bridge_6_7x.d.ts +3 -0
  132. package/build/lib/nvm500/impls/Bridge_6_7x.d.ts.map +1 -0
  133. package/build/{nvm500/parsers → lib/nvm500/impls}/Bridge_6_7x.js +1 -1
  134. package/build/lib/nvm500/impls/Bridge_6_7x.js.map +1 -0
  135. package/build/lib/nvm500/impls/Bridge_6_8x.d.ts +3 -0
  136. package/build/lib/nvm500/impls/Bridge_6_8x.d.ts.map +1 -0
  137. package/build/{nvm500/parsers → lib/nvm500/impls}/Bridge_6_8x.js +1 -1
  138. package/build/lib/nvm500/impls/Bridge_6_8x.js.map +1 -0
  139. package/build/lib/nvm500/impls/Static_6_6x.d.ts +3 -0
  140. package/build/lib/nvm500/impls/Static_6_6x.d.ts.map +1 -0
  141. package/build/{nvm500/parsers → lib/nvm500/impls}/Static_6_6x.js +1 -1
  142. package/build/lib/nvm500/impls/Static_6_6x.js.map +1 -0
  143. package/build/lib/nvm500/impls/Static_6_7x.d.ts +3 -0
  144. package/build/lib/nvm500/impls/Static_6_7x.d.ts.map +1 -0
  145. package/build/{nvm500/parsers → lib/nvm500/impls}/Static_6_7x.js +1 -1
  146. package/build/lib/nvm500/impls/Static_6_7x.js.map +1 -0
  147. package/build/lib/nvm500/impls/Static_6_8x.d.ts +3 -0
  148. package/build/lib/nvm500/impls/Static_6_8x.d.ts.map +1 -0
  149. package/build/{nvm500/parsers → lib/nvm500/impls}/Static_6_8x.js +1 -1
  150. package/build/lib/nvm500/impls/Static_6_8x.js.map +1 -0
  151. package/build/lib/nvm500/impls/index.d.ts +2 -0
  152. package/build/lib/nvm500/impls/index.d.ts.map +1 -0
  153. package/build/lib/nvm500/impls/index.js +18 -0
  154. package/build/lib/nvm500/impls/index.js.map +1 -0
  155. package/build/{nvm500 → lib/nvm500}/shared.d.ts +19 -2
  156. package/build/lib/nvm500/shared.d.ts.map +1 -0
  157. package/build/{nvm500 → lib/nvm500}/shared.js +19 -1
  158. package/build/lib/nvm500/shared.js.map +1 -0
  159. package/build/nvm500/NVMParser.d.ts +5 -36
  160. package/build/nvm500/NVMParser.d.ts.map +1 -1
  161. package/build/nvm500/NVMParser.js +0 -524
  162. package/build/nvm500/NVMParser.js.map +1 -1
  163. package/package.json +2 -2
  164. package/build/files/ApplicationCCsFile.d.ts.map +0 -1
  165. package/build/files/ApplicationCCsFile.js.map +0 -1
  166. package/build/files/ApplicationDataFile.d.ts.map +0 -1
  167. package/build/files/ApplicationDataFile.js.map +0 -1
  168. package/build/files/ApplicationNameFile.d.ts.map +0 -1
  169. package/build/files/ApplicationNameFile.js.map +0 -1
  170. package/build/files/ApplicationRFConfigFile.d.ts.map +0 -1
  171. package/build/files/ApplicationRFConfigFile.js.map +0 -1
  172. package/build/files/ApplicationTypeFile.d.ts.map +0 -1
  173. package/build/files/ApplicationTypeFile.js.map +0 -1
  174. package/build/files/ControllerInfoFile.d.ts.map +0 -1
  175. package/build/files/ControllerInfoFile.js.map +0 -1
  176. package/build/files/NVMFile.d.ts.map +0 -1
  177. package/build/files/NVMFile.js.map +0 -1
  178. package/build/files/NodeInfoFiles.d.ts.map +0 -1
  179. package/build/files/NodeInfoFiles.js.map +0 -1
  180. package/build/files/ProtocolNodeMaskFiles.d.ts.map +0 -1
  181. package/build/files/ProtocolNodeMaskFiles.js.map +0 -1
  182. package/build/files/RouteCacheFiles.d.ts.map +0 -1
  183. package/build/files/RouteCacheFiles.js.map +0 -1
  184. package/build/files/SUCUpdateEntriesFile.d.ts.map +0 -1
  185. package/build/files/SUCUpdateEntriesFile.js.map +0 -1
  186. package/build/files/VersionFiles.d.ts.map +0 -1
  187. package/build/files/VersionFiles.js.map +0 -1
  188. package/build/files/index.d.ts.map +0 -1
  189. package/build/files/index.js.map +0 -1
  190. package/build/nvm3/consts.d.ts.map +0 -1
  191. package/build/nvm3/consts.js.map +0 -1
  192. package/build/nvm3/nvm.d.ts +0 -31
  193. package/build/nvm3/nvm.d.ts.map +0 -1
  194. package/build/nvm3/nvm.js +0 -184
  195. package/build/nvm3/nvm.js.map +0 -1
  196. package/build/nvm3/object.d.ts +0 -25
  197. package/build/nvm3/object.d.ts.map +0 -1
  198. package/build/nvm3/object.js +0 -197
  199. package/build/nvm3/object.js.map +0 -1
  200. package/build/nvm3/page.d.ts.map +0 -1
  201. package/build/nvm3/page.js +0 -129
  202. package/build/nvm3/page.js.map +0 -1
  203. package/build/nvm3/utils.d.ts.map +0 -1
  204. package/build/nvm3/utils.js +0 -103
  205. package/build/nvm3/utils.js.map +0 -1
  206. package/build/nvm500/EntryParsers.d.ts.map +0 -1
  207. package/build/nvm500/EntryParsers.js.map +0 -1
  208. package/build/nvm500/parsers/Bridge_6_6x.d.ts +0 -3
  209. package/build/nvm500/parsers/Bridge_6_6x.d.ts.map +0 -1
  210. package/build/nvm500/parsers/Bridge_6_6x.js.map +0 -1
  211. package/build/nvm500/parsers/Bridge_6_7x.d.ts +0 -3
  212. package/build/nvm500/parsers/Bridge_6_7x.d.ts.map +0 -1
  213. package/build/nvm500/parsers/Bridge_6_7x.js.map +0 -1
  214. package/build/nvm500/parsers/Bridge_6_8x.d.ts +0 -3
  215. package/build/nvm500/parsers/Bridge_6_8x.d.ts.map +0 -1
  216. package/build/nvm500/parsers/Bridge_6_8x.js.map +0 -1
  217. package/build/nvm500/parsers/Static_6_6x.d.ts +0 -3
  218. package/build/nvm500/parsers/Static_6_6x.d.ts.map +0 -1
  219. package/build/nvm500/parsers/Static_6_6x.js.map +0 -1
  220. package/build/nvm500/parsers/Static_6_7x.d.ts +0 -3
  221. package/build/nvm500/parsers/Static_6_7x.d.ts.map +0 -1
  222. package/build/nvm500/parsers/Static_6_7x.js.map +0 -1
  223. package/build/nvm500/parsers/Static_6_8x.d.ts +0 -3
  224. package/build/nvm500/parsers/Static_6_8x.d.ts.map +0 -1
  225. package/build/nvm500/parsers/Static_6_8x.js.map +0 -1
  226. package/build/nvm500/shared.d.ts.map +0 -1
  227. package/build/nvm500/shared.js.map +0 -1
  228. package/build/shared.d.ts +0 -2
  229. package/build/shared.d.ts.map +0 -1
  230. package/build/shared.js +0 -3
  231. package/build/shared.js.map +0 -1
  232. /package/build/{nvm3 → lib/nvm3}/consts.d.ts +0 -0
  233. /package/build/{nvm3 → lib/nvm3}/consts.js +0 -0
  234. /package/build/{nvm500 → lib/nvm500}/EntryParsers.d.ts +0 -0
  235. /package/build/{nvm500 → lib/nvm500}/EntryParsers.js +0 -0
@@ -0,0 +1,650 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NVM3 = void 0;
4
+ const core_1 = require("@zwave-js/core");
5
+ const shared_1 = require("@zwave-js/shared");
6
+ const definitions_1 = require("./common/definitions");
7
+ const utils_1 = require("./common/utils");
8
+ const consts_1 = require("./nvm3/consts");
9
+ const files_1 = require("./nvm3/files");
10
+ const object_1 = require("./nvm3/object");
11
+ const page_1 = require("./nvm3/page");
12
+ const utils_2 = require("./nvm3/utils");
13
+ class NVM3 {
14
+ constructor(io) {
15
+ this._io = io;
16
+ }
17
+ _io;
18
+ _access = definitions_1.NVMAccess.None;
19
+ _info;
20
+ get info() {
21
+ return this._info;
22
+ }
23
+ async ensureReadable() {
24
+ if (this._access === definitions_1.NVMAccess.Read
25
+ || this._access === definitions_1.NVMAccess.ReadWrite) {
26
+ return;
27
+ }
28
+ if (this._access === definitions_1.NVMAccess.Write) {
29
+ await this._io.close();
30
+ }
31
+ this._access = await this._io.open(definitions_1.NVMAccess.Read);
32
+ }
33
+ async ensureWritable() {
34
+ if (this._access === definitions_1.NVMAccess.Write
35
+ || this._access === definitions_1.NVMAccess.ReadWrite) {
36
+ return;
37
+ }
38
+ if (this._access === definitions_1.NVMAccess.Read) {
39
+ await this._io.close();
40
+ }
41
+ this._access = await this._io.open(definitions_1.NVMAccess.Write);
42
+ }
43
+ async init() {
44
+ await this.ensureReadable();
45
+ let pageOffset = 0;
46
+ // Determine NVM size, scan pages
47
+ const pages = [];
48
+ let isSharedFileSystem = false;
49
+ while (pageOffset < this._io.size) {
50
+ // console.debug(
51
+ // `NVM3 init() - reading page header at offset ${
52
+ // num2hex(pageOffset)
53
+ // }`,
54
+ // );
55
+ const header = await readPageHeader(this._io, pageOffset);
56
+ pages.push({
57
+ ...header,
58
+ objects: [],
59
+ });
60
+ pageOffset += header.pageSize;
61
+ }
62
+ // Scan each page for objects
63
+ for (const page of pages) {
64
+ // Scan objects in this page
65
+ let objectOffset = page.offset + consts_1.NVM3_PAGE_HEADER_SIZE;
66
+ const nextPageOffset = page.offset + page.pageSize;
67
+ while (objectOffset < nextPageOffset) {
68
+ // console.debug(
69
+ // `NVM3 init() - reading object header. page offset ${
70
+ // num2hex(page.offset)
71
+ // }, object offset ${num2hex(objectOffset)}`,
72
+ // );
73
+ const objectHeader = await readObjectHeader(this._io, objectOffset);
74
+ if (objectHeader) {
75
+ page.objects.push(objectHeader);
76
+ objectOffset += objectHeader.alignedSize;
77
+ // Detect the 800 series shared protocol & application NVM file system
78
+ // by looking for the 800 series application version file
79
+ if (objectHeader.key === files_1.ApplicationVersionFile800ID) {
80
+ isSharedFileSystem = true;
81
+ }
82
+ }
83
+ else {
84
+ // Reached the end of the data in this page
85
+ break;
86
+ }
87
+ }
88
+ }
89
+ // By convention, we only use the applicationPages in that case
90
+ let applicationPages;
91
+ let protocolPages;
92
+ if (isSharedFileSystem) {
93
+ applicationPages = pages;
94
+ protocolPages = [];
95
+ }
96
+ else {
97
+ applicationPages = pages.filter((p) => p.offset < consts_1.ZWAVE_APPLICATION_NVM_SIZE);
98
+ protocolPages = pages.filter((p) => p.offset >= consts_1.ZWAVE_APPLICATION_NVM_SIZE);
99
+ }
100
+ // NVM3 layouts pages in a ring buffer. Pages are written from front to back, then occupied pages
101
+ // are erased and overwritten. Pages at the start of the memory section may have an erase count that's 1 higher
102
+ // than the pages at the end.
103
+ const pageInfoToSectionInfo = (pages) => {
104
+ // Find the current page, which is either:
105
+ // - The last page with the high erase count that contains an object
106
+ const maxEraseCount = Math.max(...pages.map((p) => p.eraseCount));
107
+ let currentPageIndex = pages.findLastIndex((p) => p.eraseCount === maxEraseCount && p.objects.length > 0);
108
+ // - or if there is none, the last page with the lower erase count that contains an object
109
+ if (currentPageIndex === -1) {
110
+ currentPageIndex = pages.findLastIndex((p) => p.objects.length > 0);
111
+ }
112
+ // - Or if no objects exist at all, the beginning of the section
113
+ if (currentPageIndex === -1)
114
+ currentPageIndex = 0;
115
+ // Find the next free byte of the current page
116
+ const currentPage = pages[currentPageIndex];
117
+ let offset = consts_1.NVM3_PAGE_HEADER_SIZE;
118
+ for (const object of currentPage.objects) {
119
+ offset += object.alignedSize;
120
+ }
121
+ const objectLocations = new Map();
122
+ for (let i = 0; i < pages.length; i++) {
123
+ const page = pages[i];
124
+ for (const object of page.objects) {
125
+ const location = objectLocations.get(object.key);
126
+ if (location == undefined) {
127
+ // Object seen for the first time, remember the page it is in
128
+ objectLocations.set(object.key, i);
129
+ }
130
+ else if ((object.fragmentType === consts_1.FragmentType.None
131
+ || object.fragmentType === consts_1.FragmentType.First)
132
+ && (page.eraseCount >= pages[location].eraseCount)) {
133
+ // Object was seen before. Only remember it if it is the only
134
+ // or first fragment and the object appears in a later location
135
+ // of the ring buffer
136
+ objectLocations.set(object.key, i);
137
+ }
138
+ }
139
+ }
140
+ return {
141
+ pages,
142
+ offsetInPage: offset,
143
+ currentPage: currentPageIndex,
144
+ objectLocations,
145
+ };
146
+ };
147
+ if (isSharedFileSystem) {
148
+ this._info = {
149
+ isSharedFileSystem: true,
150
+ sections: {
151
+ all: pageInfoToSectionInfo(applicationPages),
152
+ },
153
+ };
154
+ }
155
+ else {
156
+ this._info = {
157
+ isSharedFileSystem: false,
158
+ sections: {
159
+ application: pageInfoToSectionInfo(applicationPages),
160
+ protocol: pageInfoToSectionInfo(protocolPages),
161
+ },
162
+ };
163
+ }
164
+ return this._info;
165
+ }
166
+ getNVMSectionForFile(fileId) {
167
+ // Determine which ring buffer to read in
168
+ return this._info.isSharedFileSystem
169
+ ? this._info.sections.all
170
+ : this._info.sections[(0, files_1.getNVMSectionByFileID)(fileId)];
171
+ }
172
+ async has(fileId) {
173
+ this._info ??= await this.init();
174
+ // Determine which ring buffer to read in
175
+ const section = this.getNVMSectionForFile(fileId);
176
+ return section.objectLocations.has(fileId);
177
+ }
178
+ readObjectData(object) {
179
+ return (0, utils_1.nvmReadBuffer)(this._io, object.offset + object.headerSize, object.fragmentSize);
180
+ }
181
+ async get(fileId) {
182
+ this._info ??= await this.init();
183
+ // Determine which ring buffer to read in
184
+ const section = this.getNVMSectionForFile(fileId);
185
+ const pages = section.pages;
186
+ // TODO: There should be no need for scanning, since we know the object locations after init().
187
+ // Start scanning backwards through the pages ring buffer, starting with the current page
188
+ let parts;
189
+ let complete = false;
190
+ let objType;
191
+ const resetFragments = () => {
192
+ // if (parts?.length) {
193
+ // console.debug("Resetting fragmented object");
194
+ // }
195
+ parts = undefined;
196
+ complete = false;
197
+ };
198
+ pages: for (let offset = 0; offset < pages.length; offset++) {
199
+ const index = (section.currentPage - offset + pages.length)
200
+ % pages.length;
201
+ const page = pages[index];
202
+ // console.debug(
203
+ // `NVM3.get(${fileId}): scanning page ${index} at offset ${
204
+ // num2hex(page.offset)
205
+ // }`,
206
+ // );
207
+ // Scan objects in this page, read backwards.
208
+ // The last non-deleted object wins
209
+ objects: for (let j = page.objects.length - 1; j >= 0; j--) {
210
+ const object = page.objects[j];
211
+ const readObject = () => this.readObjectData(object);
212
+ if (object.key !== fileId) {
213
+ // Reset any fragmented objects when encountering a different key
214
+ resetFragments();
215
+ continue objects;
216
+ }
217
+ if (object.type === consts_1.ObjectType.Deleted) {
218
+ // Last action for this object was a deletion. There is no data.
219
+ return;
220
+ }
221
+ else if (object.fragmentType === consts_1.FragmentType.None) {
222
+ // console.debug(
223
+ // `NVM3.get(${fileId}): found complete object - header offset ${
224
+ // num2hex(object.offset)
225
+ // }, content offset ${
226
+ // num2hex(object.offset + object.headerSize)
227
+ // }, length ${object.fragmentSize}`,
228
+ // );
229
+ // This is a complete object
230
+ parts = [await readObject()];
231
+ objType = object.type;
232
+ complete = true;
233
+ break pages;
234
+ }
235
+ else if (object.fragmentType === consts_1.FragmentType.Last) {
236
+ // console.debug(
237
+ // `NVM3.get(${fileId}): found LAST fragment - header offset ${
238
+ // num2hex(object.offset)
239
+ // }, content offset ${
240
+ // num2hex(object.offset + object.headerSize)
241
+ // }, length ${object.fragmentSize}`,
242
+ // );
243
+ parts = [await readObject()];
244
+ objType = object.type;
245
+ complete = false;
246
+ }
247
+ else if (object.fragmentType === consts_1.FragmentType.Next) {
248
+ if (parts?.length && objType === object.type) {
249
+ // console.debug(
250
+ // `NVM3.get(${fileId}): found NEXT fragment - header offset ${
251
+ // num2hex(object.offset)
252
+ // }, content offset ${
253
+ // num2hex(object.offset + object.headerSize)
254
+ // }, length ${object.fragmentSize}`,
255
+ // );
256
+ parts.unshift(await readObject());
257
+ }
258
+ else {
259
+ // This shouldn't be here
260
+ resetFragments();
261
+ }
262
+ }
263
+ else if (object.fragmentType === consts_1.FragmentType.First) {
264
+ if (parts?.length && objType === object.type) {
265
+ // console.debug(
266
+ // `NVM3.get(${fileId}): found FIRST fragment - header offset ${
267
+ // num2hex(object.offset)
268
+ // }, content offset ${
269
+ // num2hex(object.offset + object.headerSize)
270
+ // }, length ${object.fragmentSize}`,
271
+ // );
272
+ parts.unshift(await readObject());
273
+ complete = true;
274
+ break pages;
275
+ }
276
+ else {
277
+ // This shouldn't be here
278
+ resetFragments();
279
+ }
280
+ }
281
+ }
282
+ }
283
+ if (!parts?.length || !complete || objType == undefined)
284
+ return;
285
+ return Buffer.concat(parts);
286
+ }
287
+ async writeObjects(objects) {
288
+ const section = this.getNVMSectionForFile(objects[0].key);
289
+ let page = section.pages[section.currentPage];
290
+ let remainingSpace = page.pageSize
291
+ - consts_1.NVM3_PAGE_HEADER_SIZE
292
+ - section.offsetInPage;
293
+ // TODO: See if we can avoid double writes on a page change
294
+ /** Moves to the next page and erases it if necessary */
295
+ const nextPage = async () => {
296
+ section.currentPage = (section.currentPage + 1)
297
+ % section.pages.length;
298
+ page = section.pages[section.currentPage];
299
+ // Find headers of objects that need to be preserved
300
+ const toPreserve = [...section.objectLocations].filter(([, pageIndex]) => pageIndex === section.currentPage)
301
+ .map(([fileID]) => page.objects.findLast((h) => h.key === fileID))
302
+ .filter((h) => h != undefined)
303
+ .filter((h) => h.type !== consts_1.ObjectType.Deleted);
304
+ // And add the objects to the TODO list
305
+ for (const header of toPreserve) {
306
+ const data = await this.get(header.key);
307
+ console.error(`Need to preserve object ${(0, shared_1.num2hex)(header.key)}
308
+ page index: ${section.currentPage}
309
+ object type: ${(0, shared_1.getEnumMemberName)(consts_1.ObjectType, header.type)}
310
+ data: ${data != undefined ? `${data.length} bytes` : "(no data)"}`);
311
+ objects.push({
312
+ key: header.key,
313
+ type: header.type,
314
+ fragmentType: consts_1.FragmentType.None,
315
+ data,
316
+ });
317
+ }
318
+ if (page.objects.length > 0) {
319
+ // The page needs to be erased
320
+ page.eraseCount++;
321
+ page.objects = [];
322
+ const pageHeaderBuffer = (0, page_1.serializePageHeader)(page);
323
+ const pageBuffer = Buffer.alloc(page.pageSize, 0xff);
324
+ pageHeaderBuffer.copy(pageBuffer, 0);
325
+ await (0, utils_1.nvmWriteBuffer)(this._io, page.offset, pageBuffer);
326
+ }
327
+ section.offsetInPage = consts_1.NVM3_PAGE_HEADER_SIZE;
328
+ remainingSpace = page.pageSize - consts_1.NVM3_PAGE_HEADER_SIZE;
329
+ };
330
+ // Go through the list of objects and write all of them to the NVM
331
+ for (const object of objects) {
332
+ const isLargeObject = object.type === consts_1.ObjectType.DataLarge
333
+ || object.type === consts_1.ObjectType.CounterLarge;
334
+ let fragments;
335
+ if (isLargeObject) {
336
+ // Large objects may be fragmented
337
+ // We need to start a new page, if the remaining space is not enough for
338
+ // the object header plus additional data
339
+ if (remainingSpace <= consts_1.NVM3_OBJ_HEADER_SIZE_LARGE) {
340
+ await nextPage();
341
+ }
342
+ fragments = (0, object_1.fragmentLargeObject)(object, remainingSpace, page.pageSize - consts_1.NVM3_PAGE_HEADER_SIZE);
343
+ }
344
+ else {
345
+ // Small objects cannot be fragmented. If they don't fit,
346
+ // they need to go on the next page.
347
+ const requiredSpace = (0, object_1.getRequiredSpace)(object);
348
+ if (requiredSpace > remainingSpace) {
349
+ await nextPage();
350
+ }
351
+ fragments = [object];
352
+ }
353
+ // Write each fragment to the NVM. If there are multiple fragments,
354
+ // each one but the first needs to be written at the beginning of a new page
355
+ for (let i = 0; i < fragments.length; i++) {
356
+ if (i > 0)
357
+ await nextPage();
358
+ const fragment = fragments[i];
359
+ const objBuffer = (0, object_1.serializeObject)(fragment);
360
+ const objOffset = page.offset + section.offsetInPage;
361
+ await this._io.write(objOffset, objBuffer);
362
+ const requiredSpace = (0, object_1.getRequiredSpace)(fragment);
363
+ section.offsetInPage += requiredSpace;
364
+ remainingSpace -= requiredSpace;
365
+ // Remember which objects exist in this page
366
+ page.objects.push((0, object_1.getObjectHeader)(object, objOffset));
367
+ // And remember where this object lives
368
+ if (object.type === consts_1.ObjectType.Deleted) {
369
+ section.objectLocations.delete(object.key);
370
+ }
371
+ else if (fragment.fragmentType === consts_1.FragmentType.None
372
+ || fragment.fragmentType === consts_1.FragmentType.First) {
373
+ section.objectLocations.set(fragment.key, section.currentPage);
374
+ }
375
+ }
376
+ }
377
+ }
378
+ async set(property, value) {
379
+ if (!this._info)
380
+ await this.init();
381
+ await this.ensureWritable();
382
+ await this.writeObjects([{
383
+ key: property,
384
+ type: value.length <= consts_1.NVM3_MAX_OBJ_SIZE_SMALL
385
+ ? consts_1.ObjectType.DataSmall
386
+ : consts_1.ObjectType.DataLarge,
387
+ // writeObject deals with fragmentation
388
+ fragmentType: consts_1.FragmentType.None,
389
+ data: value,
390
+ }]);
391
+ }
392
+ /** Writes multiple values to the NVM at once. `null` / `undefined` cause the value to be deleted */
393
+ async setMany(values) {
394
+ if (!this._info)
395
+ await this.init();
396
+ await this.ensureWritable();
397
+ // Group objects by their NVM section
398
+ const objectsBySection = new Map();
399
+ for (const [key, value] of values) {
400
+ const sectionOffset = this.getNVMSectionForFile(key).pages[0].offset;
401
+ if (!objectsBySection.has(sectionOffset)) {
402
+ objectsBySection.set(sectionOffset, []);
403
+ }
404
+ objectsBySection.get(sectionOffset).push([key, value]);
405
+ }
406
+ // And call writeObjects for each group
407
+ for (const objectGroups of objectsBySection.values()) {
408
+ await this.writeObjects(objectGroups.map(([key, value]) => (value
409
+ ? {
410
+ key,
411
+ type: value.length <= consts_1.NVM3_MAX_OBJ_SIZE_SMALL
412
+ ? consts_1.ObjectType.DataSmall
413
+ : consts_1.ObjectType.DataLarge,
414
+ // writeObject deals with fragmentation
415
+ fragmentType: consts_1.FragmentType.None,
416
+ data: value,
417
+ }
418
+ : {
419
+ key,
420
+ type: consts_1.ObjectType.Deleted,
421
+ fragmentType: consts_1.FragmentType.None,
422
+ })));
423
+ }
424
+ }
425
+ async delete(property) {
426
+ if (!this._info)
427
+ await this.init();
428
+ await this.ensureWritable();
429
+ await this.writeObjects([{
430
+ key: property,
431
+ type: consts_1.ObjectType.Deleted,
432
+ fragmentType: consts_1.FragmentType.None,
433
+ }]);
434
+ }
435
+ async erase(options) {
436
+ const { deviceFamily = 2047, writeSize = consts_1.PageWriteSize.WRITE_SIZE_16, memoryMapped = true, sharedFileSystem = false, } = options ?? {};
437
+ const maxPageSize = sharedFileSystem
438
+ ? consts_1.FLASH_MAX_PAGE_SIZE_800
439
+ : consts_1.FLASH_MAX_PAGE_SIZE_700;
440
+ const pageSize = Math.min(options?.pageSize ?? maxPageSize, maxPageSize);
441
+ // Make sure we won't be writing incomplete pages
442
+ if (this._io.size % pageSize !== 0) {
443
+ throw new core_1.ZWaveError(`Invalid page size. NVM size ${this._io.size} must be a multiple of the page size ${pageSize}.`, core_1.ZWaveErrorCodes.Argument_Invalid);
444
+ }
445
+ else if (!sharedFileSystem && consts_1.ZWAVE_APPLICATION_NVM_SIZE % pageSize !== 0) {
446
+ throw new core_1.ZWaveError(`Invalid page size. The application NVM size ${consts_1.ZWAVE_APPLICATION_NVM_SIZE} must be a multiple of the page size ${pageSize}.`, core_1.ZWaveErrorCodes.Argument_Invalid);
447
+ }
448
+ else if (!sharedFileSystem
449
+ && (this._io.size - consts_1.ZWAVE_APPLICATION_NVM_SIZE) % pageSize !== 0) {
450
+ throw new core_1.ZWaveError(`Invalid page size. The protocol NVM size ${this._io.size
451
+ - consts_1.ZWAVE_APPLICATION_NVM_SIZE} must be a multiple of the page size ${pageSize}.`, core_1.ZWaveErrorCodes.Argument_Invalid);
452
+ }
453
+ await this.ensureWritable();
454
+ // Create empty pages, write them to the NVM
455
+ const applicationPages = [];
456
+ const protocolPages = [];
457
+ const numPages = this._io.size / pageSize;
458
+ for (let i = 0; i < numPages; i++) {
459
+ const offset = i * pageSize;
460
+ const pageBuffer = Buffer.alloc(pageSize, 0xff);
461
+ const pageHeader = {
462
+ offset,
463
+ version: 0x01,
464
+ eraseCount: 0,
465
+ encrypted: false,
466
+ deviceFamily,
467
+ memoryMapped,
468
+ pageSize,
469
+ status: consts_1.PageStatus.OK,
470
+ writeSize,
471
+ };
472
+ (0, page_1.serializePageHeader)(pageHeader).copy(pageBuffer, 0);
473
+ await (0, utils_1.nvmWriteBuffer)(this._io, offset, pageBuffer);
474
+ if (sharedFileSystem || offset < consts_1.ZWAVE_APPLICATION_NVM_SIZE) {
475
+ applicationPages.push({ ...pageHeader, objects: [] });
476
+ }
477
+ else {
478
+ protocolPages.push({ ...pageHeader, objects: [] });
479
+ }
480
+ }
481
+ // Remember the pages we just created for further use
482
+ this._info = sharedFileSystem
483
+ ? {
484
+ isSharedFileSystem: true,
485
+ sections: {
486
+ all: {
487
+ currentPage: 0,
488
+ objectLocations: new Map(),
489
+ offsetInPage: consts_1.NVM3_PAGE_HEADER_SIZE,
490
+ pages: applicationPages,
491
+ },
492
+ },
493
+ }
494
+ : {
495
+ isSharedFileSystem: false,
496
+ sections: {
497
+ application: {
498
+ currentPage: 0,
499
+ objectLocations: new Map(),
500
+ offsetInPage: consts_1.NVM3_PAGE_HEADER_SIZE,
501
+ pages: applicationPages,
502
+ },
503
+ protocol: {
504
+ currentPage: 0,
505
+ objectLocations: new Map(),
506
+ offsetInPage: consts_1.NVM3_PAGE_HEADER_SIZE,
507
+ pages: protocolPages,
508
+ },
509
+ },
510
+ };
511
+ }
512
+ }
513
+ exports.NVM3 = NVM3;
514
+ async function readPageHeader(io, offset) {
515
+ if (offset > io.size - consts_1.NVM3_PAGE_HEADER_SIZE) {
516
+ throw new core_1.ZWaveError("Incomplete page in buffer!", core_1.ZWaveErrorCodes.NVM_InvalidFormat);
517
+ }
518
+ const buffer = (await io.read(offset, consts_1.NVM3_PAGE_HEADER_SIZE)).buffer;
519
+ const { version, eraseCount } = tryGetVersionAndEraseCount(buffer);
520
+ // Page status
521
+ const status = buffer.readUInt32LE(12);
522
+ const devInfo = buffer.readUInt16LE(16);
523
+ const deviceFamily = devInfo & 0x7ff;
524
+ const writeSize = (devInfo >> 11) & 0b1;
525
+ const memoryMapped = !!((devInfo >> 12) & 0b1);
526
+ let pageSize = (0, page_1.pageSizeFromBits)((devInfo >> 13) & 0b111);
527
+ if (pageSize > 0xffff) {
528
+ // Some controllers have no valid info in the page size bits, resulting
529
+ // in an impossibly large page size. To try and figure out the actual page
530
+ // size without knowing the hardware, we scan the buffer for the next valid
531
+ // page start.
532
+ for (let exponent = 0; exponent < 0b111; exponent++) {
533
+ const testPageSize = (0, page_1.pageSizeFromBits)(exponent);
534
+ const nextOffset = offset + testPageSize;
535
+ if (
536
+ // exactly end of NVM OR
537
+ io.size === nextOffset
538
+ // next page
539
+ || await isValidPageHeaderAtOffset(io, nextOffset)) {
540
+ pageSize = testPageSize;
541
+ break;
542
+ }
543
+ }
544
+ }
545
+ if (pageSize > 0xffff) {
546
+ throw new core_1.ZWaveError("Could not determine page size!", core_1.ZWaveErrorCodes.NVM_InvalidFormat);
547
+ }
548
+ if (io.size < offset + pageSize) {
549
+ throw new core_1.ZWaveError(`NVM contains incomplete page at offset ${(0, shared_1.num2hex)(offset)}!`, core_1.ZWaveErrorCodes.NVM_InvalidFormat);
550
+ }
551
+ const formatInfo = buffer.readUInt16LE(18);
552
+ const encrypted = !(formatInfo & 0b1);
553
+ return {
554
+ offset,
555
+ version,
556
+ eraseCount,
557
+ status,
558
+ encrypted,
559
+ pageSize,
560
+ writeSize,
561
+ memoryMapped,
562
+ deviceFamily,
563
+ };
564
+ }
565
+ function tryGetVersionAndEraseCount(header) {
566
+ const version = header.readUInt16LE(0);
567
+ const magic = header.readUInt16LE(2);
568
+ if (magic !== consts_1.NVM3_PAGE_MAGIC) {
569
+ throw new core_1.ZWaveError("Not a valid NVM3 page!", core_1.ZWaveErrorCodes.NVM_InvalidFormat);
570
+ }
571
+ if (version !== 0x01) {
572
+ throw new core_1.ZWaveError(`Unsupported NVM3 page version: ${version}`, core_1.ZWaveErrorCodes.NVM_NotSupported);
573
+ }
574
+ // The erase counter is saved twice, once normally, once inverted
575
+ let eraseCount = header.readUInt32LE(4);
576
+ const eraseCountCode = eraseCount >>> consts_1.NVM3_PAGE_COUNTER_SIZE;
577
+ eraseCount &= consts_1.NVM3_PAGE_COUNTER_MASK;
578
+ (0, utils_2.validateBergerCode)(eraseCount, eraseCountCode, consts_1.NVM3_PAGE_COUNTER_SIZE);
579
+ let eraseCountInv = header.readUInt32LE(8);
580
+ const eraseCountInvCode = eraseCountInv >>> consts_1.NVM3_PAGE_COUNTER_SIZE;
581
+ eraseCountInv &= consts_1.NVM3_PAGE_COUNTER_MASK;
582
+ (0, utils_2.validateBergerCode)(eraseCountInv, eraseCountInvCode, consts_1.NVM3_PAGE_COUNTER_SIZE);
583
+ if (eraseCount !== (~eraseCountInv & consts_1.NVM3_PAGE_COUNTER_MASK)) {
584
+ throw new core_1.ZWaveError("Invalid erase count!", core_1.ZWaveErrorCodes.NVM_InvalidFormat);
585
+ }
586
+ return { version, eraseCount };
587
+ }
588
+ async function isValidPageHeaderAtOffset(io, offset) {
589
+ if (offset > io.size - consts_1.NVM3_PAGE_HEADER_SIZE) {
590
+ return false;
591
+ }
592
+ const { buffer } = await io.read(offset, consts_1.NVM3_PAGE_HEADER_SIZE);
593
+ try {
594
+ tryGetVersionAndEraseCount(buffer);
595
+ return true;
596
+ }
597
+ catch {
598
+ return false;
599
+ }
600
+ }
601
+ async function readObjectHeader(io, offset) {
602
+ let headerSize = 4;
603
+ const hdr1 = await (0, utils_1.nvmReadUInt32LE)(io, offset);
604
+ // Skip over blank page areas
605
+ if (hdr1 === 0xffffffff)
606
+ return;
607
+ const key = (hdr1 >> consts_1.NVM3_OBJ_KEY_SHIFT) & consts_1.NVM3_OBJ_KEY_MASK;
608
+ let objType = hdr1 & consts_1.NVM3_OBJ_TYPE_MASK;
609
+ let fragmentSize = 0;
610
+ let hdr2;
611
+ const isLarge = objType === consts_1.ObjectType.DataLarge
612
+ || objType === consts_1.ObjectType.CounterLarge;
613
+ if (isLarge) {
614
+ hdr2 = await (0, utils_1.nvmReadUInt32LE)(io, offset + 4);
615
+ headerSize += 4;
616
+ fragmentSize = hdr2 & consts_1.NVM3_OBJ_LARGE_LEN_MASK;
617
+ }
618
+ else if (objType > consts_1.ObjectType.DataSmall) {
619
+ // In small objects with data, the length and object type are stored in the same value
620
+ fragmentSize = objType - consts_1.ObjectType.DataSmall;
621
+ objType = consts_1.ObjectType.DataSmall;
622
+ }
623
+ else if (objType === consts_1.ObjectType.CounterSmall) {
624
+ fragmentSize = consts_1.NVM3_COUNTER_SIZE;
625
+ }
626
+ const fragmentType = isLarge
627
+ ? (hdr1 >>> consts_1.NVM3_OBJ_FRAGTYPE_SHIFT) & consts_1.NVM3_OBJ_FRAGTYPE_MASK
628
+ : consts_1.FragmentType.None;
629
+ if (isLarge) {
630
+ (0, utils_2.validateBergerCodeMulti)([hdr1, hdr2], 32 + consts_1.NVM3_CODE_LARGE_SHIFT);
631
+ }
632
+ else {
633
+ (0, utils_2.validateBergerCodeMulti)([hdr1], consts_1.NVM3_CODE_SMALL_SHIFT);
634
+ }
635
+ if (io.size < offset + headerSize + fragmentSize) {
636
+ throw new core_1.ZWaveError(`NVM contains incomplete object at offset ${(0, shared_1.num2hex)(offset)}!`, core_1.ZWaveErrorCodes.NVM_InvalidFormat);
637
+ }
638
+ const alignedFragmentSize = (0, object_1.getAlignedSize)(fragmentSize);
639
+ const alignedSize = headerSize + alignedFragmentSize;
640
+ return {
641
+ key,
642
+ offset,
643
+ type: objType,
644
+ fragmentType,
645
+ headerSize,
646
+ fragmentSize,
647
+ alignedSize,
648
+ };
649
+ }
650
+ //# sourceMappingURL=NVM3.js.map