1
0

control-page-state.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. const {
  2. controlButtonRegisters,
  3. motorParameterInputRegisters,
  4. readonlyParamRegisters,
  5. speedCommandRegister,
  6. statusRegisters
  7. } = require('./registers')
  8. const {
  9. parseHexInteger
  10. } = require('./base-utils')
  11. const {
  12. getSharedInputDefault,
  13. mergeInputValues,
  14. setSharedInputValues,
  15. updateDriverParams,
  16. toFiniteNumber
  17. } = require('./calculation-context')
  18. const {
  19. calculateParameterInputWriteValue,
  20. calculateSpeedCommandWriteValue,
  21. SCALE_MAX,
  22. formatFixedValue
  23. } = require('./conversions')
  24. const {
  25. updateStatusRegisterWords
  26. } = require('./status-format')
  27. const {
  28. floatToWords,
  29. getRegisterWordCache,
  30. toRegisterWord,
  31. toAddressKey,
  32. wordsToFloat
  33. } = require('./register-value-utils')
  34. const {
  35. appendInputUnit
  36. } = require('./input-value-utils')
  37. const AUTO_READ_MIN_INTERVAL = 100
  38. const AUTO_READ_MAX_INTERVAL = 3000
  39. const DEFAULT_AUTO_READ_INTERVAL = 100
  40. const MOTOR_PARAM_START_ADDRESS = 0x60
  41. const MOTOR_PARAM_WORD_COUNT = 8
  42. const DRIVER_PARAM_START_ADDRESS = 0xA0
  43. const STATUS_START_ADDRESS = 0xC0
  44. const USER_STATUS_START_ADDRESS = 0xD3
  45. const MAX_USER_STATUS_COUNT = statusRegisters.filter((item) => item.name.indexOf('用户状态字') === 0).length
  46. const DEFAULT_USER_STATUS_COUNT = 0
  47. function getRegisterSpanWordCount(registers, startAddress) {
  48. const endAddress = registers.reduce((maxAddress, item) => {
  49. const address = parseHexInteger(item.address)
  50. if (!Number.isFinite(address)) return maxAddress
  51. return Math.max(maxAddress, address + (item.registerCount || 1))
  52. }, startAddress)
  53. return endAddress - startAddress
  54. }
  55. const DRIVER_PARAM_WORD_COUNT = getRegisterSpanWordCount(readonlyParamRegisters, DRIVER_PARAM_START_ADDRESS)
  56. const BASE_STATUS_WORD_COUNT = USER_STATUS_START_ADDRESS - STATUS_START_ADDRESS
  57. const DRIVER_SUMMARY_REGISTER_NAMES = [
  58. '芯片型号',
  59. '全区 Flash 校验码',
  60. '型号'
  61. ]
  62. function isDriverSummaryRegister(item) {
  63. return DRIVER_SUMMARY_REGISTER_NAMES.includes(item.name)
  64. }
  65. function getDriverReadonlyParamRegisters(registers = readonlyParamRegisters) {
  66. return registers.filter((item) => !isDriverSummaryRegister(item))
  67. }
  68. function getInputValues(registers) {
  69. return registers.reduce((result, item) => {
  70. result[item.name] = toFiniteNumber(item.inputValue, getSharedInputDefault(item.name))
  71. return result
  72. }, {})
  73. }
  74. function updateMotorWriteValues(registers) {
  75. const inputValues = getInputValues(registers)
  76. return registers.map((item) => ({
  77. ...item,
  78. writeValue: calculateParameterInputWriteValue(item, item.inputValue, inputValues)
  79. }))
  80. }
  81. function formatReadInputValue(item, value) {
  82. if (!Number.isFinite(value)) return ''
  83. if (item.name === 'LD' || item.name === 'LQ') return formatFixedValue(value, 6)
  84. if (item.name === 'RS') return formatFixedValue(value, 4)
  85. if (item.type === 'float') return formatFixedValue(value, 2)
  86. return String(Math.round(value))
  87. }
  88. function formatHexWord(value) {
  89. return `0x${(Number(value) & 0xFFFF).toString(16).toUpperCase().padStart(4, '0')}`
  90. }
  91. function wordsToAscii(words, startIndex, byteLength) {
  92. const chars = []
  93. const wordCount = Math.ceil(byteLength / 2)
  94. for (let index = 0; index < wordCount; index += 1) {
  95. const word = Number(words[startIndex + index])
  96. if (!Number.isInteger(word)) break
  97. const bytes = [(word >> 8) & 0xFF, word & 0xFF]
  98. for (const byte of bytes) {
  99. if (chars.length >= byteLength || byte === 0) {
  100. return chars.join('').trim() || '--'
  101. }
  102. if (byte >= 0x20 && byte <= 0x7E) {
  103. chars.push(String.fromCharCode(byte))
  104. }
  105. }
  106. }
  107. return chars.join('').trim() || '--'
  108. }
  109. function clampNumber(value, minValue, maxValue, fallback) {
  110. const numberValue = toFiniteNumber(value, NaN)
  111. if (!Number.isFinite(numberValue)) return fallback
  112. return Math.min(Math.max(Math.round(numberValue), minValue), maxValue)
  113. }
  114. function getUserStatusCount(value) {
  115. return clampNumber(value, 0, MAX_USER_STATUS_COUNT, DEFAULT_USER_STATUS_COUNT)
  116. }
  117. function getStatusWordCount(userStatusCount = DEFAULT_USER_STATUS_COUNT) {
  118. return BASE_STATUS_WORD_COUNT + getUserStatusCount(userStatusCount)
  119. }
  120. function cloneRegister(item) {
  121. return {
  122. ...item
  123. }
  124. }
  125. function createInitialState() {
  126. return {
  127. autoReadInterval: DEFAULT_AUTO_READ_INTERVAL,
  128. autoReadStatus: false,
  129. connectedDevice: null,
  130. controlActionButtons: controlButtonRegisters.filter((item) => item.momentary).map(cloneRegister),
  131. controlButtons: controlButtonRegisters.filter((item) => !item.momentary).map(cloneRegister),
  132. errorText: '',
  133. isAwaitingResponse: false,
  134. isReadingDriver: false,
  135. isReadingMotor: false,
  136. isSending: false,
  137. isWritingMotor: false,
  138. chipModel: '--',
  139. flashChecksum: '--',
  140. motorModel: '--',
  141. motorParameterInputRegisters,
  142. readonlyParamRegisters: getDriverReadonlyParamRegisters(),
  143. speedCommand: speedCommandRegister,
  144. systemTip: '',
  145. userStatusCount: DEFAULT_USER_STATUS_COUNT
  146. }
  147. }
  148. function applyTransportState(data, transportState) {
  149. const nextState = {
  150. connectedDevice: transportState.connectedDevice,
  151. errorText: transportState.errorText,
  152. isAwaitingResponse: transportState.isAwaitingResponse,
  153. isSending: transportState.isSending,
  154. systemTip: transportState.systemTip
  155. }
  156. if (!transportState.connectedDevice && data.autoReadStatus) {
  157. nextState.autoReadStatus = false
  158. }
  159. if (!transportState.connectedDevice) {
  160. nextState.isReadingDriver = false
  161. nextState.isReadingMotor = false
  162. nextState.isWritingMotor = false
  163. }
  164. return nextState
  165. }
  166. function applyMotorParameterInput(data, index, value) {
  167. const changedRegisters = data.motorParameterInputRegisters.map((item, currentIndex) => {
  168. if (currentIndex !== index) return item
  169. return {
  170. ...item,
  171. isDirty: true,
  172. inputValue: value
  173. }
  174. })
  175. const nextRegisters = updateMotorWriteValues(changedRegisters)
  176. const inputValues = mergeInputValues(nextRegisters)
  177. setSharedInputValues(nextRegisters)
  178. return {
  179. motorParameterInputRegisters: nextRegisters,
  180. speedCommand: {
  181. ...data.speedCommand,
  182. isDirty: true,
  183. writeValue: calculateSpeedCommandWriteValue(data.speedCommand.inputValue, inputValues)
  184. }
  185. }
  186. }
  187. function applySpeedCommandInput(data, inputValue) {
  188. const inputValues = mergeInputValues(data.motorParameterInputRegisters)
  189. return {
  190. speedCommand: {
  191. ...data.speedCommand,
  192. isDirty: true,
  193. inputValue,
  194. writeValue: calculateSpeedCommandWriteValue(inputValue, inputValues)
  195. }
  196. }
  197. }
  198. function applySpeedCommandReadValue(data, rawValue) {
  199. const wordValue = Number(rawValue)
  200. if (!Number.isInteger(wordValue)) return {}
  201. const inputValues = mergeInputValues(data.motorParameterInputRegisters)
  202. const speedBase = toFiniteNumber(inputValues['速度基准'])
  203. const inputValue = speedBase > 0
  204. ? appendInputUnit(data.speedCommand, formatFixedValue(wordValue / SCALE_MAX * speedBase, 2))
  205. : data.speedCommand.inputValue
  206. return {
  207. speedCommand: {
  208. ...data.speedCommand,
  209. isDirty: false,
  210. inputValue,
  211. writeValue: String(wordValue & 0xFFFF)
  212. }
  213. }
  214. }
  215. function getControlButtonWriteValue(button) {
  216. if (!button) return 0
  217. return button.writeValue
  218. }
  219. function getNextControlButton(button) {
  220. if (button.momentary) return button
  221. return {
  222. ...button,
  223. name: button.nextName,
  224. nextName: button.name,
  225. nextWriteValue: button.writeValue,
  226. writeValue: button.nextWriteValue
  227. }
  228. }
  229. function applyControlSuccess(data, button) {
  230. if (!button) return {}
  231. if (button.momentary) {
  232. return {
  233. systemTip: `${button.name}已下发`
  234. }
  235. }
  236. return {
  237. controlButtons: data.controlButtons.map((item) => (
  238. item.key === button.key ? getNextControlButton(item) : item
  239. )),
  240. systemTip: `${button.name}已下发`
  241. }
  242. }
  243. function getControlButtonFromRead(button, value) {
  244. if (!button || button.momentary) return button
  245. const readValue = Number(value)
  246. if (!Number.isFinite(readValue)) return button
  247. if (Number(button.writeValue) === readValue) return getNextControlButton(button)
  248. if (Number(button.nextWriteValue) === readValue) return button
  249. return button
  250. }
  251. function applyControlReadValues(data, coilValues = {}) {
  252. return {
  253. controlButtons: data.controlButtons.map((item) => {
  254. const value = coilValues[toAddressKey(item.address)]
  255. return value === undefined ? item : getControlButtonFromRead(item, value)
  256. })
  257. }
  258. }
  259. function buildMotorMainWriteValues(data) {
  260. const registerMap = data.motorParameterInputRegisters.reduce((result, item) => {
  261. result[item.name] = item
  262. return result
  263. }, {})
  264. const ldWords = floatToWords(registerMap.LD && registerMap.LD.inputValue)
  265. const lqWords = floatToWords(registerMap.LQ && registerMap.LQ.inputValue)
  266. const rsWords = floatToWords(registerMap.RS && registerMap.RS.inputValue)
  267. const polePairsWord = toRegisterWord(registerMap['极对数'] && registerMap['极对数'].inputValue)
  268. const speedBaseWord = toRegisterWord(registerMap['速度基准'] && registerMap['速度基准'].inputValue)
  269. if (!ldWords || !lqWords || !rsWords || !Number.isInteger(polePairsWord) || !Number.isInteger(speedBaseWord)) {
  270. return {
  271. errorText: '请检查 LD、LQ、RS、极对数和速度基准的输入值',
  272. values: null
  273. }
  274. }
  275. return {
  276. errorText: '',
  277. values: ldWords.concat(lqWords, rsWords, [polePairsWord, speedBaseWord])
  278. }
  279. }
  280. function applyMotorParameterReadValues(data, registerWordCache) {
  281. const nextRegisters = data.motorParameterInputRegisters.map((item) => {
  282. let readValue = null
  283. if (item.name === 'LD' && registerWordCache[0x60] !== undefined && registerWordCache[0x61] !== undefined) {
  284. readValue = wordsToFloat(registerWordCache[0x60], registerWordCache[0x61])
  285. } else if (item.name === 'LQ' && registerWordCache[0x62] !== undefined && registerWordCache[0x63] !== undefined) {
  286. readValue = wordsToFloat(registerWordCache[0x62], registerWordCache[0x63])
  287. } else if (item.name === 'RS' && registerWordCache[0x64] !== undefined && registerWordCache[0x65] !== undefined) {
  288. readValue = wordsToFloat(registerWordCache[0x64], registerWordCache[0x65])
  289. } else if (item.name === '极对数' && registerWordCache[0x66] !== undefined) {
  290. readValue = registerWordCache[0x66]
  291. } else if (item.name === '速度基准' && registerWordCache[0x67] !== undefined) {
  292. readValue = registerWordCache[0x67]
  293. }
  294. if (readValue === null) return item
  295. return {
  296. ...item,
  297. isDirty: false,
  298. inputValue: appendInputUnit(item, formatReadInputValue(item, readValue))
  299. }
  300. })
  301. const nextWriteRegisters = updateMotorWriteValues(nextRegisters)
  302. const inputValues = mergeInputValues(nextWriteRegisters)
  303. setSharedInputValues(nextWriteRegisters)
  304. return {
  305. motorParameterInputRegisters: nextWriteRegisters,
  306. speedCommand: {
  307. ...data.speedCommand,
  308. writeValue: calculateSpeedCommandWriteValue(data.speedCommand.inputValue, inputValues)
  309. }
  310. }
  311. }
  312. function clearMotorParameterDirty(data) {
  313. return {
  314. motorParameterInputRegisters: data.motorParameterInputRegisters.map((item) => ({
  315. ...item,
  316. isDirty: false
  317. }))
  318. }
  319. }
  320. function clearSpeedCommandDirty(data) {
  321. return {
  322. speedCommand: {
  323. ...data.speedCommand,
  324. isDirty: false
  325. }
  326. }
  327. }
  328. function applyMotorParameterBlur(data, index, value) {
  329. const item = data.motorParameterInputRegisters[index]
  330. if (!item) return {}
  331. return applyMotorParameterInput(data, index, appendInputUnit(item, value === undefined ? item.inputValue : value))
  332. }
  333. function applySpeedCommandBlur(data, value) {
  334. return applySpeedCommandInput(
  335. data,
  336. appendInputUnit(data.speedCommand, value === undefined ? data.speedCommand.inputValue : value)
  337. )
  338. }
  339. function applyDriverParameterReadValues(data, words) {
  340. if (!Array.isArray(words) || words.length < DRIVER_PARAM_WORD_COUNT) return {}
  341. const carrierFrequencyKHz = (words[0] >> 8) & 0xFF
  342. const baseVoltage = (words[0] & 0xFF) / 10
  343. const opAmpGain = words[1] & 0xFFFF
  344. const samplingResistorMohm = words[2] & 0xFFFF
  345. const busVoltageDividerRatio = wordsToFloat(words[4], words[5])
  346. const analogInputDividerRatio = wordsToFloat(words[6], words[7])
  347. const displayValues = {
  348. 芯片型号: wordsToAscii(words, 8, 8),
  349. 型号: wordsToAscii(words, 12, 16),
  350. 载波频率: String(carrierFrequencyKHz),
  351. 基准电压: formatFixedValue(baseVoltage, 2),
  352. 运放倍数: String(opAmpGain),
  353. 采样电阻: String(samplingResistorMohm),
  354. '全区 Flash 校验码': formatHexWord(words[3]),
  355. 母线电压分压比: formatFixedValue(busVoltageDividerRatio, 2),
  356. 模拟输入电压分压比: formatFixedValue(analogInputDividerRatio, 2)
  357. }
  358. updateDriverParams({
  359. analogInputDividerRatio,
  360. baseVoltage,
  361. busVoltageDividerRatio,
  362. carrierFrequencyKHz,
  363. opAmpGain,
  364. samplingResistorMohm
  365. })
  366. return {
  367. chipModel: displayValues['芯片型号'],
  368. flashChecksum: displayValues['全区 Flash 校验码'],
  369. motorModel: displayValues['型号'],
  370. readonlyParamRegisters: getDriverReadonlyParamRegisters(data.readonlyParamRegisters).map((item) => ({
  371. ...item,
  372. displayValue: displayValues[item.name] || item.displayValue || '--'
  373. }))
  374. }
  375. }
  376. function applyStatusReadValues(words, startAddress = STATUS_START_ADDRESS) {
  377. if (!Array.isArray(words) || !words.length) return {}
  378. updateStatusRegisterWords(statusRegisters, startAddress, words)
  379. return {}
  380. }
  381. module.exports = {
  382. AUTO_READ_MAX_INTERVAL,
  383. AUTO_READ_MIN_INTERVAL,
  384. DRIVER_PARAM_START_ADDRESS,
  385. DRIVER_PARAM_WORD_COUNT,
  386. MAX_USER_STATUS_COUNT,
  387. MOTOR_PARAM_START_ADDRESS,
  388. MOTOR_PARAM_WORD_COUNT,
  389. STATUS_START_ADDRESS,
  390. applyControlReadValues,
  391. applyControlSuccess,
  392. applyDriverParameterReadValues,
  393. applyMotorParameterBlur,
  394. applyMotorParameterInput,
  395. applyMotorParameterReadValues,
  396. clearMotorParameterDirty,
  397. clearSpeedCommandDirty,
  398. applySpeedCommandBlur,
  399. applySpeedCommandInput,
  400. applySpeedCommandReadValue,
  401. applyStatusReadValues,
  402. applyTransportState,
  403. buildMotorMainWriteValues,
  404. clampNumber,
  405. createInitialState,
  406. getStatusWordCount,
  407. getControlButtonWriteValue,
  408. getRegisterWordCache,
  409. getUserStatusCount,
  410. setSharedInputValues
  411. }