1
0

persistence.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. const {
  2. formatExportStamp,
  3. isCancelError,
  4. loadSelectedFile,
  5. saveTextFileToChat
  6. } = require('../../repositories/file.js')
  7. const {
  8. getWxApi
  9. } = require('../../utils/platform-utils.js')
  10. const {
  11. cloneImportedGroup,
  12. getRegisterJsonValue
  13. } = require('../../domain/parameter-groups/model.js')
  14. const STORAGE_KEY = 'parameter-groups-json'
  15. const STORAGE_MIGRATION_KEY = 'parameter-groups-json-protocol-migration'
  16. const JSON_DOCUMENT_TYPE = 'parameter-groups'
  17. const JSON_SCHEMA_VERSION = 2
  18. const PROTOCOL_MODE = {
  19. MODBUS_RTU: 'modbus-rtu',
  20. STORAGE_ACCESS: 'storage-access'
  21. }
  22. const GROUP_SOURCE_FIELDS = [
  23. 'addressUnit',
  24. 'sourceAddress',
  25. 'sourceAddressText',
  26. 'sourceByteLength',
  27. 'sourceMemoryArea',
  28. 'sourceMemoryClass',
  29. 'sourceSegment',
  30. 'sourceSegmentModule',
  31. 'sourceSymbolName'
  32. ]
  33. const REGISTER_SOURCE_FIELDS = [
  34. 'conversionFormula',
  35. 'sourceAddress',
  36. 'sourceAddressText',
  37. 'sourceByteLength',
  38. 'sourceBitOffset',
  39. 'sourceBitWidth',
  40. 'sourceMemoryArea',
  41. 'sourceMemoryClass',
  42. 'sourceSymbolName',
  43. 'sourceSymbolType'
  44. ]
  45. const REGISTER_STRUCT_FIELDS = [
  46. 'bitOffset',
  47. 'bitWidth',
  48. 'byteStart',
  49. 'isPlaceholderByteField',
  50. 'isBitField',
  51. 'structByteLength'
  52. ]
  53. function pickFields(source, fields) {
  54. return fields.reduce((result, field) => {
  55. if (source && source[field] !== undefined && source[field] !== null && source[field] !== '') {
  56. result[field] = source[field]
  57. }
  58. return result
  59. }, {})
  60. }
  61. function toPersistedGroups(groups = []) {
  62. return groups.map((group) => ({
  63. layout: group.layout,
  64. name: group.name,
  65. registerType: group.registerType,
  66. startAddress: group.startAddress,
  67. quantity: group.quantity,
  68. registers: group.registers.map((register) => ({
  69. dataType: register.dataType,
  70. defaultValue: register.defaultValue,
  71. conversionFormula: register.conversionFormula,
  72. isStructField: register.isStructField,
  73. name: register.name,
  74. maxValue: register.maxValue,
  75. minValue: register.minValue,
  76. textByteLength: register.textByteLength,
  77. remark: register.remark,
  78. unit: register.unit,
  79. value: getRegisterJsonValue(register),
  80. ...pickFields(register, REGISTER_STRUCT_FIELDS),
  81. ...pickFields(register, REGISTER_SOURCE_FIELDS)
  82. })),
  83. ...pickFields(group, GROUP_SOURCE_FIELDS)
  84. }))
  85. }
  86. function toJsonData(groups = [], options = {}) {
  87. const jsonData = {
  88. groups: toPersistedGroups(groups),
  89. type: JSON_DOCUMENT_TYPE,
  90. version: JSON_SCHEMA_VERSION
  91. }
  92. if (options.includeExportedAt) {
  93. jsonData.exportedAt = new Date().toISOString()
  94. }
  95. return jsonData
  96. }
  97. function toJsonText(groups = [], options = {}) {
  98. return JSON.stringify(toJsonData(groups, options), null, 2)
  99. }
  100. function normalizeProtocolMode(protocolMode) {
  101. return protocolMode === PROTOCOL_MODE.MODBUS_RTU
  102. ? PROTOCOL_MODE.MODBUS_RTU
  103. : PROTOCOL_MODE.STORAGE_ACCESS
  104. }
  105. function getProtocolStorageKey(protocolMode) {
  106. return `${STORAGE_KEY}:${normalizeProtocolMode(protocolMode)}`
  107. }
  108. function parseJsonGroups(jsonText) {
  109. const parsed = typeof jsonText === 'string' ? JSON.parse(jsonText) : jsonText
  110. const groups = Array.isArray(parsed)
  111. ? parsed
  112. : (Array.isArray(parsed && parsed.groups) ? parsed.groups : parsed && parsed.parameterGroups)
  113. if (parsed && parsed.type && parsed.type !== JSON_DOCUMENT_TYPE) {
  114. throw new Error('JSON 文件不是参数组配置')
  115. }
  116. if (parsed && parsed.version && parsed.version !== JSON_SCHEMA_VERSION) {
  117. throw new Error('JSON 版本不兼容')
  118. }
  119. if (!Array.isArray(groups)) {
  120. throw new Error('JSON 中没有找到寄存器组数组')
  121. }
  122. return groups
  123. }
  124. function readStoredGroups(protocolMode = PROTOCOL_MODE.STORAGE_ACCESS) {
  125. const wxApi = getWxApi()
  126. if (typeof wxApi.getStorageSync !== 'function') return []
  127. try {
  128. const storageKey = getProtocolStorageKey(protocolMode)
  129. const jsonText = wxApi.getStorageSync(storageKey)
  130. if (jsonText) return parseJsonGroups(jsonText).map(cloneImportedGroup)
  131. } catch (error) {
  132. return []
  133. }
  134. return []
  135. }
  136. function persistGroups(groups = [], protocolMode = PROTOCOL_MODE.STORAGE_ACCESS) {
  137. const wxApi = getWxApi()
  138. if (typeof wxApi.setStorageSync !== 'function') return
  139. try {
  140. wxApi.setStorageSync(getProtocolStorageKey(protocolMode), toJsonText(groups))
  141. } catch (error) {}
  142. }
  143. function legacyGroupsNeedMigration() {
  144. const wxApi = getWxApi()
  145. if (typeof wxApi.getStorageSync !== 'function') return false
  146. try {
  147. return !!wxApi.getStorageSync(STORAGE_KEY) && !wxApi.getStorageSync(STORAGE_MIGRATION_KEY)
  148. } catch (error) {
  149. return false
  150. }
  151. }
  152. function migrateLegacyGroupsToProtocol(protocolMode = PROTOCOL_MODE.STORAGE_ACCESS) {
  153. const wxApi = getWxApi()
  154. if (typeof wxApi.getStorageSync !== 'function' || typeof wxApi.setStorageSync !== 'function') return false
  155. try {
  156. if (!legacyGroupsNeedMigration()) return false
  157. const normalizedProtocolMode = normalizeProtocolMode(protocolMode)
  158. const legacyJsonText = wxApi.getStorageSync(STORAGE_KEY)
  159. const targetStorageKey = getProtocolStorageKey(normalizedProtocolMode)
  160. const targetJsonText = wxApi.getStorageSync(targetStorageKey)
  161. if (legacyJsonText && !targetJsonText) {
  162. persistGroups(parseJsonGroups(legacyJsonText).map(cloneImportedGroup), normalizedProtocolMode)
  163. }
  164. wxApi.setStorageSync(STORAGE_MIGRATION_KEY, normalizedProtocolMode)
  165. return true
  166. } catch (error) {
  167. return false
  168. }
  169. }
  170. function getShareFileName() {
  171. return `parameter-groups-${formatExportStamp()}.json`
  172. }
  173. async function loadGroupsFromMessageFile() {
  174. const file = await loadSelectedFile('message', {
  175. encoding: 'utf8',
  176. extensionMessage: '请选择 .json 寄存器配置文件',
  177. extensions: ['json'],
  178. fallbackName: 'parameter-groups.json'
  179. })
  180. return parseJsonGroups(file.text).map(cloneImportedGroup)
  181. }
  182. async function saveGroupsToChat(groups = []) {
  183. if (!groups.length) {
  184. throw new Error('没有可保存的寄存器组')
  185. }
  186. const jsonText = toJsonText(groups, {
  187. includeExportedAt: true
  188. })
  189. await saveTextFileToChat(getShareFileName(), jsonText)
  190. return groups.length
  191. }
  192. module.exports = {
  193. JSON_DOCUMENT_TYPE,
  194. JSON_SCHEMA_VERSION,
  195. STORAGE_KEY,
  196. isCancelError,
  197. legacyGroupsNeedMigration,
  198. loadGroupsFromMessageFile,
  199. migrateLegacyGroupsToProtocol,
  200. parseJsonGroups,
  201. persistGroups,
  202. readStoredGroups,
  203. saveGroupsToChat,
  204. toJsonData,
  205. toJsonText
  206. }