Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Q
qingdaoMuseumPlatform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
node_server
qingdaoMuseumPlatform
Commits
b16d534d
Commit
b16d534d
authored
Jun 29, 2026
by
PC-20251223ZVQQ\Administrator
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
空调温度上下限和生效解除逻辑优化
parent
b8c4706b
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
131 additions
and
70 deletions
+131
-70
.gitignore
.gitignore
+7
-0
dataIntegration.ts
src/biz/dataIntegration.ts
+2
-2
running.ts
src/biz/running.ts
+122
-68
No files found.
.gitignore
View file @
b16d534d
...
@@ -10,3 +10,10 @@
...
@@ -10,3 +10,10 @@
*.logs
*.logs
*.zip
*.zip
/dist
/dist
# 衍生/临时文件
/tmp
# 参考文档
/res/青岛啤酒博物馆空调智能化实施方案20260506.pptx
/res/飞奕云平台接口文档V1.3-计费.pdf
src/biz/dataIntegration.ts
View file @
b16d534d
...
@@ -592,8 +592,8 @@ async function integrateAcDeviceData() {
...
@@ -592,8 +592,8 @@ async function integrateAcDeviceData() {
fanSpeed
:
FAN_SPEED_MAP
[
item
.
fanSpeed
]
||
'自动'
,
fanSpeed
:
FAN_SPEED_MAP
[
item
.
fanSpeed
]
||
'自动'
,
setTemp
:
item
.
tempSet
||
0
,
setTemp
:
item
.
tempSet
||
0
,
roomTemp
:
item
.
roomTemp
||
0
,
roomTemp
:
item
.
roomTemp
||
0
,
maxTemp
:
item
.
tempSetHi
||
0
,
maxTemp
:
item
.
tempSetHi
||
28
,
// 飞奕无数据时默认上限28℃
minTemp
:
item
.
tempSetLo
||
0
,
minTemp
:
item
.
tempSetLo
||
16
,
// 飞奕无数据时默认下限16℃
autoControl
:
'生效'
,
autoControl
:
'生效'
,
};
};
toInsert
.
push
({
toInsert
.
push
({
...
...
src/biz/running.ts
View file @
b16d534d
...
@@ -9,6 +9,12 @@ import { getRegionList } from "./region";
...
@@ -9,6 +9,12 @@ import { getRegionList } from "./region";
import
{
startSchedule
,
stopSchedule
}
from
"../config/schedule"
;
import
{
startSchedule
,
stopSchedule
}
from
"../config/schedule"
;
/**
/**
* 联动去重缓存:记录每个区域上次下发的控制状态,避免重复调用第三方接口
* key: regionKey, value: 上次下发的控制参数
*/
const
envControlCache
:
Map
<
string
,
{
tempSet
?:
number
;
workMode
?:
number
;
fanSpeed
?:
number
;
onOff
?:
number
;
maxTemp
?:
number
;
minTemp
?:
number
;
co2Action
?:
string
}
>
=
new
Map
();
/**
* 将 Date 对象格式化为本地时间字符串 'YYYY-MM-DD HH:mm:ss'
* 将 Date 对象格式化为本地时间字符串 'YYYY-MM-DD HH:mm:ss'
* 替代 toISOString() 避免 UTC 时区偏移问题
* 替代 toISOString() 避免 UTC 时区偏移问题
*/
*/
...
@@ -19,8 +25,8 @@ function formatLocalTime(date: Date): string {
...
@@ -19,8 +25,8 @@ function formatLocalTime(date: Date): string {
/**
/**
* 智能管控-运行分析
* 智能管控-运行分析
* @param regionGroup 区域组(可选),如
“A馆”
,如果提供则返回该类型下所有区域的分析数据
* @param regionGroup 区域组(可选),如
"A馆"
,如果提供则返回该类型下所有区域的分析数据
* @param regionType 区域类型(可选),如
“1F”
,如果提供则返回该区域的分析数据
* @param regionType 区域类型(可选),如
"1F"
,如果提供则返回该区域的分析数据
* @returns 大屏所需的所有指标数据
* @returns 大屏所需的所有指标数据
*/
*/
export
async
function
getRunAnalysis
(
regionGroup
:
string
,
regionType
:
string
)
{
export
async
function
getRunAnalysis
(
regionGroup
:
string
,
regionType
:
string
)
{
...
@@ -749,8 +755,8 @@ export async function controlAcRunning(params: {}) {
...
@@ -749,8 +755,8 @@ export async function controlAcRunning(params: {}) {
let
mode
=
params
[
"mode"
];
let
mode
=
params
[
"mode"
];
let
power
=
params
[
"power"
];
let
power
=
params
[
"power"
];
let
setTemp
=
params
[
"setTemp"
];
let
setTemp
=
params
[
"setTemp"
];
let
maxTemp
=
params
[
"maxTemp"
]
;
let
maxTemp
=
params
[
"maxTemp"
]
||
28
;
// 默认锁定温度上限
let
minTemp
=
params
[
"minTemp"
]
;
let
minTemp
=
params
[
"minTemp"
]
||
16
;
// 默认锁定温度下限
let
autoControl
=
params
[
"autoControl"
];
let
autoControl
=
params
[
"autoControl"
];
let
fanSpeed
=
params
[
"fanSpeed"
];
let
fanSpeed
=
params
[
"fanSpeed"
];
...
@@ -775,7 +781,7 @@ export async function controlAcRunning(params: {}) {
...
@@ -775,7 +781,7 @@ export async function controlAcRunning(params: {}) {
"tempSetLock"
:
acDeviceKeyMap
[
autoControl
],
"tempSetLock"
:
acDeviceKeyMap
[
autoControl
],
"tempSetLo"
:
Number
(
minTemp
),
"tempSetLo"
:
Number
(
minTemp
),
"tempSetHi"
:
Number
(
maxTemp
),
"tempSetHi"
:
Number
(
maxTemp
),
"openApiAction"
:
autoControl
===
"
解除
"
?
"control"
:
"lock"
"openApiAction"
:
autoControl
===
"
生效
"
?
"control"
:
"lock"
};
};
return
await
controlIndoorUnit
(
acPrarms
);
return
await
controlIndoorUnit
(
acPrarms
);
}
}
...
@@ -827,17 +833,21 @@ function getSeason(): 'summer' | 'winter' {
...
@@ -827,17 +833,21 @@ function getSeason(): 'summer' | 'winter' {
}
}
// 根据IAQ温度计算空调设定温度
// 根据IAQ温度计算空调设定温度
function
calcAcTempSet
(
temperature
:
number
,
season
:
'summer'
|
'winter'
):
number
{
/**
* 根据文档 Slide 39 三段式温度段,计算联动控制参数
* 返回:目标温度、风速、开关状态
*/
function
calcAcTempSet
(
temperature
:
number
,
season
:
'summer'
|
'winter'
):
{
tempSet
:
number
;
fanSpeed
:
number
;
onOff
:
number
}
{
if
(
season
===
'summer'
)
{
if
(
season
===
'summer'
)
{
// 夏季制冷:温度越高,设定越低
// 夏季制冷:温度越高,设定越低
if
(
temperature
>
26
)
return
22
;
if
(
temperature
>
26
)
return
{
tempSet
:
22
,
fanSpeed
:
1
,
onOff
:
1
};
// 未达需求:高风制冷
if
(
temperature
>=
22
)
return
24
;
if
(
temperature
>=
22
)
return
{
tempSet
:
24
,
fanSpeed
:
4
,
onOff
:
Math
.
round
(
temperature
)
===
24
?
0
:
1
};
// 舒适区:四舍五入24℃待机
return
26
;
return
{
tempSet
:
26
,
fanSpeed
:
4
,
onOff
:
1
};
// 已达需求:低风维持
}
else
{
}
else
{
// 冬季制热:温度越低,设定越高
// 冬季制热:温度越低,设定越高
if
(
temperature
>
21
)
return
18
;
if
(
temperature
<
18
)
return
{
tempSet
:
21
,
fanSpeed
:
1
,
onOff
:
1
};
// 未达需求:高风制热
if
(
temperature
>=
18
)
return
20
;
if
(
temperature
<=
21
)
return
{
tempSet
:
20
,
fanSpeed
:
4
,
onOff
:
Math
.
round
(
temperature
)
===
20
?
0
:
1
};
// 舒适区:四舍五入20℃待机
return
21
;
return
{
tempSet
:
18
,
fanSpeed
:
4
,
onOff
:
1
};
// 已达需求:低风维持
}
}
}
}
...
@@ -874,37 +884,64 @@ export async function controlAcByEnvironment(deviceId: string, data: any) {
...
@@ -874,37 +884,64 @@ export async function controlAcByEnvironment(deviceId: string, data: any) {
},
[
"id"
,
"device_name"
,
"device_id"
,
"device_ad"
,
"control_params"
]);
},
[
"id"
,
"device_name"
,
"device_id"
,
"device_ad"
,
"control_params"
]);
if
(
acDevices
.
data
.
length
>
0
)
{
if
(
acDevices
.
data
.
length
>
0
)
{
const
tempSet
=
calcAcTempSet
(
temperature
,
season
);
const
{
tempSet
,
fanSpeed
,
onOff
}
=
calcAcTempSet
(
temperature
,
season
);
const
workMode
=
season
===
'summer'
?
1
:
2
;
// 1制冷 2制热
const
workMode
=
season
===
'summer'
?
1
:
2
;
// 1制冷 2制热
const
modeName
=
season
===
'summer'
?
'制冷'
:
'制热'
;
const
modeName
=
season
===
'summer'
?
'制冷'
:
'制热'
;
let
controlRes
=
await
controlAcRunningApi
(
acDevices
,
workMode
,
tempSet
);
// 取空调设备数据信息中的温度上下限
let
maxTemp
:
number
|
undefined
;
// 空调日志
let
minTemp
:
number
|
undefined
;
const
acLogs
=
acDevices
.
data
.
map
((
ac
:
any
)
=>
({
const
acDeviceIds
=
acDevices
.
data
.
map
((
d
:
any
)
=>
d
.
device_id
);
device_id
:
ac
.
device_id
,
if
(
acDeviceIds
.
length
)
{
device_type
:
'空调'
,
const
acDataRes
=
await
selectDataListByParam
(
TABLENAME
.
设备数据表
,
{
operation_content
:
{
device_id
:
{
"%in%"
:
acDeviceIds
},
option
:
'自动联动控制'
,
"%orderDesc%"
:
"device_time"
,
device_id
:
deviceId
,
"%limit%"
:
1
time
:
operationTime
,
},
[
"data"
]);
season
,
if
(
acDataRes
.
data
.
length
)
{
mode
:
modeName
,
const
acData
=
acDataRes
.
data
[
0
].
data
;
roomTemp
:
temperature
,
maxTemp
=
acData
.
maxTemp
||
28
;
setTemp
:
tempSet
,
minTemp
=
acData
.
minTemp
||
16
;
success
:
controlRes
.
success
,
}
message
:
controlRes
.
message
||
(
controlRes
.
success
?
'控制成功'
:
'控制失败'
),
}
},
status
:
controlRes
.
success
?
1
:
0
,
// 去重:若本次计算的控制参数与上次相同,跳过调用
created_at
:
now
,
const
cacheKey
=
`ac_
${
regionKey
}
`
;
updated_at
:
now
,
const
lastState
=
envControlCache
.
get
(
cacheKey
);
}));
if
(
lastState
&&
lastState
.
tempSet
===
tempSet
&&
lastState
.
workMode
===
workMode
&&
lastState
.
fanSpeed
===
fanSpeed
&&
lastState
.
onOff
===
onOff
&&
lastState
.
maxTemp
===
maxTemp
&&
lastState
.
minTemp
===
minTemp
)
{
await
addData
(
TABLENAME
.
设备日志表
,
acLogs
);
console
.
log
(
`空调联动跳过(控制参数未变):
${
modeName
}
模式, 设定
${
tempSet
}
℃, 风速
${
fanSpeed
===
1
?
'高'
:
'低'
}
风,
${
onOff
?
'开机'
:
'待机'
}
, 区域
${
regionKey
}
`
);
if
(
controlRes
.
success
)
{
console
.
log
(
`空调联动(
${
modeName
}
)成功: IAQ温度
${
temperature
}
℃ → 设定
${
tempSet
}
℃, 设备
${
deviceId
}
`
);
}
else
{
}
else
{
console
.
error
(
`空调联动(
${
modeName
}
)失败:
${
controlRes
.
message
}
`
);
let
controlRes
=
await
controlAcRunningApi
(
acDevices
,
workMode
,
tempSet
,
fanSpeed
,
onOff
,
maxTemp
,
minTemp
);
// 更新缓存
envControlCache
.
set
(
cacheKey
,
{
tempSet
,
workMode
,
fanSpeed
,
onOff
,
maxTemp
,
minTemp
});
// 空调日志
const
acLogs
=
acDevices
.
data
.
map
((
ac
:
any
)
=>
({
device_id
:
ac
.
device_id
,
device_type
:
'空调'
,
operation_content
:
{
option
:
'自动联动控制'
,
device_id
:
deviceId
,
time
:
operationTime
,
season
,
mode
:
modeName
,
roomTemp
:
temperature
,
setTemp
:
tempSet
,
success
:
controlRes
.
success
,
message
:
controlRes
.
message
||
(
controlRes
.
success
?
'控制成功'
:
'控制失败'
),
},
status
:
controlRes
.
success
?
1
:
0
,
created_at
:
now
,
updated_at
:
now
,
}));
await
addData
(
TABLENAME
.
设备日志表
,
acLogs
);
if
(
controlRes
.
success
)
{
console
.
log
(
`空调联动(
${
modeName
}
)成功: IAQ温度
${
temperature
}
℃ → 设定
${
tempSet
}
℃, 设备
${
deviceId
}
`
);
}
else
{
console
.
error
(
`空调联动(
${
modeName
}
)失败:
${
controlRes
.
message
}
`
);
}
}
}
}
}
}
}
...
@@ -919,30 +956,40 @@ export async function controlAcByEnvironment(deviceId: string, data: any) {
...
@@ -919,30 +956,40 @@ export async function controlAcByEnvironment(deviceId: string, data: any) {
},
[
"id"
,
"device_name"
,
"device_id"
,
"device_ad"
]);
},
[
"id"
,
"device_name"
,
"device_id"
,
"device_ad"
]);
if
(
fanDevices
.
data
.
length
>
0
)
{
if
(
fanDevices
.
data
.
length
>
0
)
{
let
fanControlRes
=
await
controlFanRunningApi
(
fanDevices
,
fanParams
);
// 去重:若本次新风动作与上次相同,跳过调用
const
fanCacheKey
=
`fan_
${
regionKey
}
`
;
const
fanLogs
=
fanDevices
.
data
.
map
((
fan
:
any
)
=>
({
const
lastFanState
=
envControlCache
.
get
(
fanCacheKey
);
device_id
:
fan
.
device_id
,
if
(
lastFanState
&&
lastFanState
.
co2Action
===
fanParams
.
action
)
{
device_type
:
'新风'
,
console
.
log
(
`新风联动跳过(控制参数未变): CO2
${
co2
}
ppm, 动作
${
fanParams
.
action
}
, 区域
${
regionKey
}
`
);
operation_content
:
{
option
:
'自动联动控制'
,
device_id
:
deviceId
,
time
:
operationTime
,
roomCo2
:
co2
,
action
:
fanParams
.
action
===
'high'
?
'风量调高'
:
'风量调低待机'
,
success
:
fanControlRes
.
success
,
message
:
fanControlRes
.
message
||
(
fanControlRes
.
success
?
'控制成功'
:
'控制失败'
),
},
status
:
fanControlRes
.
success
?
1
:
0
,
created_at
:
now
,
updated_at
:
now
,
}));
await
addData
(
TABLENAME
.
设备日志表
,
fanLogs
);
if
(
fanControlRes
.
success
)
{
console
.
log
(
`新风联动成功: IAQ CO2
${
co2
}
ppm →
${
fanParams
.
action
===
'high'
?
'风量调高'
:
'风量调低待机'
}
, 设备
${
deviceId
}
`
);
}
else
{
}
else
{
console
.
error
(
`新风联动失败:
${
fanControlRes
.
message
}
`
);
let
fanControlRes
=
await
controlFanRunningApi
(
fanDevices
,
fanParams
);
// 更新缓存
envControlCache
.
set
(
fanCacheKey
,
{
co2Action
:
fanParams
.
action
});
const
fanLogs
=
fanDevices
.
data
.
map
((
fan
:
any
)
=>
({
device_id
:
fan
.
device_id
,
device_type
:
'新风'
,
operation_content
:
{
option
:
'自动联动控制'
,
device_id
:
deviceId
,
time
:
operationTime
,
roomCo2
:
co2
,
action
:
fanParams
.
action
===
'high'
?
'风量调高'
:
'风量调低待机'
,
success
:
fanControlRes
.
success
,
message
:
fanControlRes
.
message
||
(
fanControlRes
.
success
?
'控制成功'
:
'控制失败'
),
},
status
:
fanControlRes
.
success
?
1
:
0
,
created_at
:
now
,
updated_at
:
now
,
}));
await
addData
(
TABLENAME
.
设备日志表
,
fanLogs
);
if
(
fanControlRes
.
success
)
{
console
.
log
(
`新风联动成功: IAQ CO2
${
co2
}
ppm →
${
fanParams
.
action
===
'high'
?
'风量调高'
:
'风量调低待机'
}
, 设备
${
deviceId
}
`
);
}
else
{
console
.
error
(
`新风联动失败:
${
fanControlRes
.
message
}
`
);
}
}
}
}
}
}
}
...
@@ -951,7 +998,7 @@ export async function controlAcByEnvironment(deviceId: string, data: any) {
...
@@ -951,7 +998,7 @@ export async function controlAcByEnvironment(deviceId: string, data: any) {
}
}
// 辅助:接入空调控制API,根据温度设定值启停空调设备
// 辅助:接入空调控制API,根据温度设定值启停空调设备
async
function
controlAcRunningApi
(
acDevices
?:
any
,
workMode
?:
number
,
tempSet
?:
number
)
{
async
function
controlAcRunningApi
(
acDevices
?:
any
,
workMode
?:
number
,
tempSet
?:
number
,
fanSpeed
?:
number
,
onOff
?:
number
,
maxTemp
?:
number
,
minTemp
?:
number
)
{
let
deviceList
:
any
=
[];
let
deviceList
:
any
=
[];
acDevices
.
data
.
forEach
((
ac
:
any
)
=>
{
acDevices
.
data
.
forEach
((
ac
:
any
)
=>
{
deviceList
.
push
(
ac
.
device_id
);
deviceList
.
push
(
ac
.
device_id
);
...
@@ -963,14 +1010,21 @@ async function controlAcRunningApi(acDevices?: any, workMode?: number, tempSet?:
...
@@ -963,14 +1010,21 @@ async function controlAcRunningApi(acDevices?: any, workMode?: number, tempSet?:
let
params
:
any
=
{
let
params
:
any
=
{
"indoorUnitAddressFull"
:
deviceList
,
"indoorUnitAddressFull"
:
deviceList
,
"workMode"
:
workMode
,
"workMode"
:
workMode
,
"onOff"
:
1
,
// 联动时自动开机
"onOff"
:
onOff
??
1
,
// 联动时按温差动态判断,默认开机兜底
"fanSpeed"
:
2
,
// 默认中风
"fanSpeed"
:
fanSpeed
??
2
,
// 联动时按温差动态计算,默认中风兜底
"openApiAction"
:
"control"
,
"openApiAction"
:
"control"
,
};
};
// 如果传入了设定温度,一并下发
// 如果传入了设定温度,一并下发
if
(
tempSet
!=
null
)
{
if
(
tempSet
!=
null
)
{
params
.
tempSet
=
tempSet
;
params
.
tempSet
=
tempSet
;
}
}
// 下发温度锁定上下限(取自设备数据信息)
if
(
maxTemp
!=
null
)
{
params
.
tempSetHi
=
maxTemp
;
}
if
(
minTemp
!=
null
)
{
params
.
tempSetLo
=
minTemp
;
}
let
res
=
await
controlIndoorUnit
(
params
);
let
res
=
await
controlIndoorUnit
(
params
);
if
(
res
.
success
)
{
if
(
res
.
success
)
{
...
@@ -1039,7 +1093,7 @@ export async function stopScheduleTask() {
...
@@ -1039,7 +1093,7 @@ export async function stopScheduleTask() {
* 1 制冷 2 制热 3 送风 4 除湿
* 1 制冷 2 制热 3 送风 4 除湿
* 0 关-off 1 开-on
* 0 关-off 1 开-on
* 1 高风-超强 2 中风-强 4 低风-弱
* 1 高风-超强 2 中风-强 4 低风-弱
* 0 不锁定-
解除 1 锁定-生效
* 0 不锁定-
生效 1 锁定-解除
*/
*/
let
acDeviceKeyMap
:
{
[
key
:
string
]:
number
}
=
{
let
acDeviceKeyMap
:
{
[
key
:
string
]:
number
}
=
{
"制冷"
:
1
,
"制冷"
:
1
,
...
@@ -1051,8 +1105,8 @@ let acDeviceKeyMap: { [key: string]: number } = {
...
@@ -1051,8 +1105,8 @@ let acDeviceKeyMap: { [key: string]: number } = {
"超强"
:
1
,
"超强"
:
1
,
"强"
:
2
,
"强"
:
2
,
"弱"
:
4
,
"弱"
:
4
,
"解除"
:
0
,
"解除"
:
1
,
"生效"
:
1
"生效"
:
0
}
}
/**
/**
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment