| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- const {
- formatMagnitudeNumber,
- getOption,
- normalizeIndex,
- parseLooseNumber
- } = require('./calculator-helpers')
- const SQRT3 = Math.sqrt(3)
- const DEG_PER_RAD = 180 / Math.PI
- const RAD_PER_DEG = Math.PI / 180
- const CONNECTION_OPTIONS = [
- { key: 'star', label: '星形' },
- { key: 'delta', label: '三角形' }
- ]
- const ROW_OPTIONS = [
- { key: 'lineVoltage', label: '线电压 UL', unit: 'V', placeholder: '380', editable: true, field: 'threePhaseLineVoltage' },
- { key: 'lineCurrent', label: '线电流 IL', unit: 'A', placeholder: '10', editable: true, field: 'threePhaseLineCurrent' },
- { key: 'phaseVoltage', label: '相电压 UP', unit: 'V', placeholder: '220', editable: true, field: 'threePhasePhaseVoltage' },
- { key: 'phaseCurrent', label: '相电流 IP', unit: 'A', placeholder: '10', editable: true, field: 'threePhasePhaseCurrent' },
- { key: 'apparentPower', label: '视在功率 S', unit: 'VA', placeholder: '6580', editable: true, field: 'threePhaseApparentPower' },
- { key: 'activePower', label: '实际功率 P', unit: 'W', placeholder: '5000', editable: true, field: 'threePhaseActivePower' },
- { key: 'reactivePower', label: '无功功率 Q', unit: 'var', placeholder: '3000', editable: true, field: 'threePhaseReactivePower' },
- { key: 'powerFactor', label: '功率因素 PF', unit: '', placeholder: '0.85', editable: true, field: 'threePhasePowerFactor' },
- { key: 'phaseAngle', label: '相位角 φ', unit: '°', placeholder: '31.8', editable: true, field: 'threePhasePhaseAngle' }
- ]
- const ELECTRICAL_INPUT_KEYS = [
- 'threePhaseLineVoltage',
- 'threePhaseLineCurrent',
- 'threePhasePhaseVoltage',
- 'threePhasePhaseCurrent',
- 'threePhaseApparentPower'
- ]
- const POWER_INPUT_KEYS = [
- 'threePhaseActivePower',
- 'threePhaseReactivePower',
- 'threePhasePowerFactor',
- 'threePhasePhaseAngle'
- ]
- const INPUT_KEYS = ELECTRICAL_INPUT_KEYS.concat(POWER_INPUT_KEYS)
- const POWER_DRIVER_KEYS = [
- 'threePhaseActivePower',
- 'threePhaseReactivePower',
- 'threePhasePowerFactor',
- 'threePhasePhaseAngle'
- ]
- function formatNumber(value) {
- return formatMagnitudeNumber(value, { fallbackText: '--' })
- }
- function getSignFrom(values) {
- if (Number.isFinite(values.reactivePower) && values.reactivePower !== 0) {
- return values.reactivePower < 0 ? -1 : 1
- }
- if (Number.isFinite(values.phaseAngle) && values.phaseAngle !== 0) {
- return values.phaseAngle < 0 ? -1 : 1
- }
- return 1
- }
- function getPreferredPowerKey(source, values) {
- const preferred = source.threePhasePowerDriver
- if (POWER_DRIVER_KEYS.includes(preferred)) {
- const valueName = {
- threePhaseActivePower: 'activePower',
- threePhaseReactivePower: 'reactivePower',
- threePhasePowerFactor: 'powerFactor',
- threePhasePhaseAngle: 'phaseAngle'
- }[preferred]
- if (Number.isFinite(values[valueName])) return preferred
- }
- return POWER_DRIVER_KEYS.find((key) => {
- const valueName = {
- threePhaseActivePower: 'activePower',
- threePhaseReactivePower: 'reactivePower',
- threePhasePowerFactor: 'powerFactor',
- threePhasePhaseAngle: 'phaseAngle'
- }[key]
- return Number.isFinite(values[valueName])
- }) || ''
- }
- function deriveFromKnownS(values, preferredKey) {
- const result = {
- activePower: values.activePower,
- phaseAngle: values.phaseAngle,
- powerFactor: values.powerFactor,
- reactivePower: values.reactivePower
- }
- const apparentPower = values.apparentPower
- const qSign = getSignFrom(values)
- const driver = preferredKey || getPreferredPowerKey({}, values)
- if (!Number.isFinite(apparentPower)) return result
- if (driver === 'threePhaseActivePower' && Number.isFinite(values.activePower)) {
- if (values.activePower > apparentPower) return { ...result, errorText: '实际功率不能大于视在功率' }
- result.powerFactor = apparentPower === 0 ? 0 : values.activePower / apparentPower
- result.reactivePower = qSign * Math.sqrt(Math.max(0, apparentPower * apparentPower - values.activePower * values.activePower))
- result.phaseAngle = Math.atan2(result.reactivePower, values.activePower) * DEG_PER_RAD
- return result
- }
- if (driver === 'threePhaseReactivePower' && Number.isFinite(values.reactivePower)) {
- if (Math.abs(values.reactivePower) > apparentPower) return { ...result, errorText: '无功功率绝对值不能大于视在功率' }
- result.activePower = Math.sqrt(Math.max(0, apparentPower * apparentPower - values.reactivePower * values.reactivePower))
- result.powerFactor = apparentPower === 0 ? 0 : result.activePower / apparentPower
- result.phaseAngle = Math.atan2(values.reactivePower, result.activePower) * DEG_PER_RAD
- return result
- }
- if (driver === 'threePhasePowerFactor' && Number.isFinite(values.powerFactor)) {
- result.activePower = apparentPower * values.powerFactor
- result.reactivePower = qSign * apparentPower * Math.sqrt(Math.max(0, 1 - values.powerFactor * values.powerFactor))
- result.phaseAngle = Math.atan2(result.reactivePower, result.activePower) * DEG_PER_RAD
- return result
- }
- if (driver === 'threePhasePhaseAngle' && Number.isFinite(values.phaseAngle)) {
- const angleRad = values.phaseAngle * RAD_PER_DEG
- result.powerFactor = Math.cos(angleRad)
- result.activePower = apparentPower * result.powerFactor
- result.reactivePower = apparentPower * Math.sin(angleRad)
- return result
- }
- return result
- }
- function derivePowerTriangle(values, preferredKey) {
- if (Number.isFinite(values.apparentPower)) {
- return deriveFromKnownS(values, preferredKey)
- }
- const result = {
- activePower: values.activePower,
- apparentPower: null,
- phaseAngle: values.phaseAngle,
- powerFactor: values.powerFactor,
- reactivePower: values.reactivePower
- }
- const p = values.activePower
- const q = values.reactivePower
- const pf = values.powerFactor
- const angle = values.phaseAngle
- if (Number.isFinite(p) && Number.isFinite(q)) {
- result.apparentPower = Math.sqrt(p * p + q * q)
- result.powerFactor = result.apparentPower === 0 ? 0 : p / result.apparentPower
- result.phaseAngle = Math.atan2(q, p) * DEG_PER_RAD
- return result
- }
- if (Number.isFinite(p) && Number.isFinite(pf)) {
- if (pf <= 0 && p > 0) return { ...result, errorText: '功率因素为 0 时实际功率只能为 0' }
- result.apparentPower = pf === 0 ? 0 : p / pf
- result.reactivePower = getSignFrom(values) * Math.sqrt(Math.max(0, result.apparentPower * result.apparentPower - p * p))
- result.phaseAngle = Math.atan2(result.reactivePower, p) * DEG_PER_RAD
- return result
- }
- if (Number.isFinite(p) && Number.isFinite(angle)) {
- const angleRad = angle * RAD_PER_DEG
- const cosValue = Math.cos(angleRad)
- if (cosValue <= 0 && p > 0) return { ...result, errorText: '相位角需小于 90° 才能由实际功率反推' }
- result.powerFactor = cosValue
- result.apparentPower = cosValue === 0 ? 0 : p / cosValue
- result.reactivePower = p * Math.tan(angleRad)
- return result
- }
- if (Number.isFinite(q) && Number.isFinite(pf)) {
- const reactiveRatio = Math.sqrt(Math.max(0, 1 - pf * pf))
- if (reactiveRatio === 0 && q !== 0) return { ...result, errorText: '功率因素为 1 时无功功率应为 0' }
- result.apparentPower = reactiveRatio === 0 ? 0 : Math.abs(q) / reactiveRatio
- result.activePower = result.apparentPower * pf
- result.phaseAngle = Math.atan2(q, result.activePower) * DEG_PER_RAD
- return result
- }
- if (Number.isFinite(q) && Number.isFinite(angle)) {
- const tanValue = Math.tan(angle * RAD_PER_DEG)
- if (Math.abs(tanValue) < 1e-12 && q !== 0) return { ...result, errorText: '相位角为 0° 时无功功率应为 0' }
- result.activePower = tanValue === 0 ? 0 : q / tanValue
- if (result.activePower < 0) return { ...result, errorText: '无功功率与相位角方向不一致' }
- result.apparentPower = Math.sqrt(result.activePower * result.activePower + q * q)
- result.powerFactor = result.apparentPower === 0 ? 0 : result.activePower / result.apparentPower
- return result
- }
- return result
- }
- function validate(values) {
- if ([values.lineVoltage, values.lineCurrent, values.phaseVoltage, values.phaseCurrent, values.apparentPower, values.activePower, values.reactivePower, values.powerFactor, values.phaseAngle]
- .some((value) => Number.isNaN(value))) {
- return '输入值格式无效'
- }
- if (Number.isFinite(values.lineVoltage) && values.lineVoltage <= 0) return '线电压需大于 0'
- if (Number.isFinite(values.lineCurrent) && values.lineCurrent <= 0) return '线电流需大于 0'
- if (Number.isFinite(values.phaseVoltage) && values.phaseVoltage <= 0) return '相电压需大于 0'
- if (Number.isFinite(values.phaseCurrent) && values.phaseCurrent <= 0) return '相电流需大于 0'
- if (Number.isFinite(values.apparentPower) && values.apparentPower < 0) return '视在功率不能为负数'
- if (Number.isFinite(values.activePower) && values.activePower < 0) return '实际功率不能为负数'
- if (Number.isFinite(values.powerFactor) && (values.powerFactor < 0 || values.powerFactor > 1)) return '功率因素范围为 0-1'
- if (Number.isFinite(values.phaseAngle) && Math.abs(values.phaseAngle) > 90) return '相位角范围为 -90° 到 90°'
- return ''
- }
- function assignVoltageFromLine(result, connectionKey, lineVoltage) {
- result.lineVoltage = lineVoltage
- result.phaseVoltage = connectionKey === 'star' ? lineVoltage / SQRT3 : lineVoltage
- }
- function assignVoltageFromPhase(result, connectionKey, phaseVoltage) {
- result.phaseVoltage = phaseVoltage
- result.lineVoltage = connectionKey === 'star' ? phaseVoltage * SQRT3 : phaseVoltage
- }
- function assignCurrentFromLine(result, connectionKey, lineCurrent) {
- result.lineCurrent = lineCurrent
- result.phaseCurrent = connectionKey === 'star' ? lineCurrent : lineCurrent / SQRT3
- }
- function assignCurrentFromPhase(result, connectionKey, phaseCurrent) {
- result.phaseCurrent = phaseCurrent
- result.lineCurrent = connectionKey === 'star' ? phaseCurrent : phaseCurrent * SQRT3
- }
- function resolveElectricalValues(connectionKey, values, preferredKey = '') {
- const result = {
- apparentPower: Number.isFinite(values.apparentPower) ? values.apparentPower : null,
- lineCurrent: null,
- lineVoltage: null,
- phaseCurrent: null,
- phaseVoltage: null
- }
- if (preferredKey === 'threePhasePhaseVoltage' && Number.isFinite(values.phaseVoltage)) {
- assignVoltageFromPhase(result, connectionKey, values.phaseVoltage)
- } else if (preferredKey === 'threePhaseLineVoltage' && Number.isFinite(values.lineVoltage)) {
- assignVoltageFromLine(result, connectionKey, values.lineVoltage)
- } else if (Number.isFinite(values.lineVoltage)) {
- assignVoltageFromLine(result, connectionKey, values.lineVoltage)
- } else if (Number.isFinite(values.phaseVoltage)) {
- assignVoltageFromPhase(result, connectionKey, values.phaseVoltage)
- }
- if (preferredKey === 'threePhasePhaseCurrent' && Number.isFinite(values.phaseCurrent)) {
- assignCurrentFromPhase(result, connectionKey, values.phaseCurrent)
- } else if (preferredKey === 'threePhaseLineCurrent' && Number.isFinite(values.lineCurrent)) {
- assignCurrentFromLine(result, connectionKey, values.lineCurrent)
- } else if (Number.isFinite(values.lineCurrent)) {
- assignCurrentFromLine(result, connectionKey, values.lineCurrent)
- } else if (Number.isFinite(values.phaseCurrent)) {
- assignCurrentFromPhase(result, connectionKey, values.phaseCurrent)
- }
- if (!Number.isFinite(result.apparentPower) && Number.isFinite(result.lineVoltage) && Number.isFinite(result.lineCurrent)) {
- result.apparentPower = SQRT3 * result.lineVoltage * result.lineCurrent
- }
- if (!Number.isFinite(result.lineCurrent) && Number.isFinite(result.apparentPower) && Number.isFinite(result.lineVoltage) && result.lineVoltage > 0) {
- assignCurrentFromLine(result, connectionKey, result.apparentPower / (SQRT3 * result.lineVoltage))
- }
- if (!Number.isFinite(result.lineVoltage) && Number.isFinite(result.apparentPower) && Number.isFinite(result.lineCurrent) && result.lineCurrent > 0) {
- assignVoltageFromLine(result, connectionKey, result.apparentPower / (SQRT3 * result.lineCurrent))
- }
- return result
- }
- function formatEditableValue(value) {
- return Number.isFinite(value) ? formatNumber(value) : ''
- }
- function buildRows(connectionKey, values, powerResult, preferredElectricalKey = '') {
- const electricalValues = resolveElectricalValues(connectionKey, {
- ...values,
- apparentPower: Number.isFinite(values.apparentPower) ? values.apparentPower : powerResult.apparentPower
- }, preferredElectricalKey)
- const displayValues = {
- activePower: Number.isFinite(powerResult.activePower) ? powerResult.activePower : values.activePower,
- apparentPower: electricalValues.apparentPower,
- lineCurrent: electricalValues.lineCurrent,
- lineVoltage: electricalValues.lineVoltage,
- phaseAngle: Number.isFinite(powerResult.phaseAngle) ? powerResult.phaseAngle : values.phaseAngle,
- phaseCurrent: electricalValues.phaseCurrent,
- phaseVoltage: electricalValues.phaseVoltage,
- powerFactor: Number.isFinite(powerResult.powerFactor) ? powerResult.powerFactor : values.powerFactor,
- reactivePower: Number.isFinite(powerResult.reactivePower) ? powerResult.reactivePower : values.reactivePower
- }
- const displayText = {
- activePower: formatEditableValue(displayValues.activePower),
- apparentPower: formatEditableValue(displayValues.apparentPower),
- lineCurrent: formatEditableValue(displayValues.lineCurrent),
- lineVoltage: formatEditableValue(displayValues.lineVoltage),
- phaseAngle: formatEditableValue(displayValues.phaseAngle),
- phaseCurrent: formatEditableValue(displayValues.phaseCurrent),
- phaseVoltage: formatEditableValue(displayValues.phaseVoltage),
- powerFactor: formatEditableValue(displayValues.powerFactor),
- reactivePower: formatEditableValue(displayValues.reactivePower)
- }
- return ROW_OPTIONS.map((row) => ({
- ...row,
- value: displayText[row.key] || (row.editable ? '' : '--')
- }))
- }
- function buildState(source = {}) {
- const connectionIndex = normalizeIndex(source.threePhaseConnectionIndex, CONNECTION_OPTIONS, 0)
- const connection = getOption(CONNECTION_OPTIONS, connectionIndex)
- const rawValues = INPUT_KEYS.reduce((result, key) => {
- result[key] = String(source[key] === undefined || source[key] === null ? '' : source[key])
- return result
- }, {})
- const values = {
- activePower: parseLooseNumber(rawValues.threePhaseActivePower),
- apparentPower: parseLooseNumber(rawValues.threePhaseApparentPower),
- lineCurrent: parseLooseNumber(rawValues.threePhaseLineCurrent),
- lineVoltage: parseLooseNumber(rawValues.threePhaseLineVoltage),
- phaseAngle: parseLooseNumber(rawValues.threePhasePhaseAngle),
- phaseCurrent: parseLooseNumber(rawValues.threePhasePhaseCurrent),
- phaseVoltage: parseLooseNumber(rawValues.threePhasePhaseVoltage),
- powerFactor: parseLooseNumber(rawValues.threePhasePowerFactor),
- reactivePower: parseLooseNumber(rawValues.threePhaseReactivePower)
- }
- const validationError = validate(values)
- const preferredElectricalKey = ELECTRICAL_INPUT_KEYS.includes(source.threePhaseElectricalDriver)
- ? source.threePhaseElectricalDriver
- : ''
- const electricalValues = validationError
- ? {}
- : resolveElectricalValues(connection.key, values, preferredElectricalKey)
- const preferredPowerKey = getPreferredPowerKey(source, values)
- const powerResult = validationError
- ? {}
- : derivePowerTriangle({
- ...values,
- ...electricalValues
- }, preferredPowerKey)
- const errorText = validationError || powerResult.errorText || ''
- return {
- ...rawValues,
- threePhaseConnectionIndex: connectionIndex,
- threePhaseConnectionKey: connection.key,
- threePhaseConnectionOptions: CONNECTION_OPTIONS,
- threePhaseElectricalDriver: preferredElectricalKey,
- threePhaseErrorText: errorText,
- threePhasePowerDriver: preferredPowerKey,
- threePhaseRows: buildRows(connection.key, values, powerResult || {}, preferredElectricalKey)
- }
- }
- function createInitialState() {
- return buildState({})
- }
- function updateState(state, changedData = {}) {
- return buildState({
- ...state,
- ...changedData
- })
- }
- function clearInputs(state = {}) {
- return updateState(state, INPUT_KEYS.reduce((result, key) => {
- result[key] = ''
- return result
- }, {
- threePhaseElectricalDriver: '',
- threePhasePowerDriver: ''
- }))
- }
- module.exports = {
- CONNECTION_OPTIONS,
- ELECTRICAL_INPUT_KEYS,
- POWER_DRIVER_KEYS,
- clearInputs,
- createInitialState,
- updateState
- }
|