This commit is contained in:
@ -117,22 +117,18 @@ async def _(target: DepLongTaskTarget, temp: Optional[Union[int, float]] = 1):
|
|||||||
await send_ac_image(evt, ac)
|
await send_ac_image(evt, ac)
|
||||||
return
|
return
|
||||||
await ac.update_ac(temperature_delta=temp)
|
await ac.update_ac(temperature_delta=temp)
|
||||||
if ac.temperature > 40:
|
if ac.burnt:
|
||||||
# 根据温度随机出是否爆炸,40度开始,呈指数增长
|
# 打开爆炸图片
|
||||||
possibility = -math.e ** ((40-ac.temperature) / 50) + 1
|
with open(ASSETS_PATH / "img" / "other" / "boom.jpg", "rb") as f:
|
||||||
if random.random() < possibility:
|
output = BytesIO()
|
||||||
# 打开爆炸图片
|
# 爆炸抖动
|
||||||
with open(ASSETS_PATH / "img" / "other" / "boom.jpg", "rb") as f:
|
frames = wiggle_transform(np.array(Image.open(f)), intensity=5)
|
||||||
output = BytesIO()
|
pil_frames = [Image.fromarray(frame) for frame in frames]
|
||||||
# 爆炸抖动
|
pil_frames[0].save(output, format="GIF", save_all=True, append_images=pil_frames[1:], loop=0, duration=35, disposal=2)
|
||||||
frames = wiggle_transform(np.array(Image.open(f)), intensity=5)
|
output.seek(0)
|
||||||
pil_frames = [Image.fromarray(frame) for frame in frames]
|
await evt.send(await UniMessage().image(raw=output).export())
|
||||||
pil_frames[0].save(output, format="GIF", save_all=True, append_images=pil_frames[1:], loop=0, duration=35, disposal=2)
|
await evt.send("太热啦,空调炸了!")
|
||||||
output.seek(0)
|
return
|
||||||
await evt.send(await UniMessage().image(raw=output).export())
|
|
||||||
await ac.broke_ac(CrashType.BURNT)
|
|
||||||
await evt.send("太热啦,空调炸了!")
|
|
||||||
return
|
|
||||||
await send_ac_image(evt, ac)
|
await send_ac_image(evt, ac)
|
||||||
|
|
||||||
evt = on_alconna(Alconna(
|
evt = on_alconna(Alconna(
|
||||||
@ -152,11 +148,6 @@ async def _(target: DepLongTaskTarget, temp: Optional[Union[int, float]] = 1):
|
|||||||
await send_ac_image(evt, ac)
|
await send_ac_image(evt, ac)
|
||||||
return
|
return
|
||||||
await ac.update_ac(temperature_delta=-temp)
|
await ac.update_ac(temperature_delta=-temp)
|
||||||
if ac.temperature < 0:
|
|
||||||
# 根据温度随机出是否冻结,0度开始,呈指数增长
|
|
||||||
possibility = -math.e ** (ac.temperature / 50) + 1
|
|
||||||
if random.random() < possibility:
|
|
||||||
await ac.broke_ac(CrashType.FROZEN)
|
|
||||||
await send_ac_image(evt, ac)
|
await send_ac_image(evt, ac)
|
||||||
|
|
||||||
evt = on_alconna(Alconna(
|
evt = on_alconna(Alconna(
|
||||||
@ -202,3 +193,36 @@ async def _(target: DepLongTaskTarget):
|
|||||||
params=params
|
params=params
|
||||||
)
|
)
|
||||||
await evt.send(await UniMessage().image(raw=image).export())
|
await evt.send(await UniMessage().image(raw=image).export())
|
||||||
|
|
||||||
|
evt = on_alconna(Alconna(
|
||||||
|
"空调最高峰",
|
||||||
|
), use_cmd_start=True, use_cmd_sep=False, skip_for_unmatch=True)
|
||||||
|
|
||||||
|
@evt.handle()
|
||||||
|
async def _(target: DepLongTaskTarget):
|
||||||
|
result = await db_manager.query_by_sql_file(
|
||||||
|
ROOT_PATH / "sql" / "query_peak.sql"
|
||||||
|
)
|
||||||
|
if len(result) == 0:
|
||||||
|
await evt.send("没有空调记录!")
|
||||||
|
return
|
||||||
|
max_temp = result[0].get("max")
|
||||||
|
min_temp = result[0].get("min")
|
||||||
|
his_max = result[0].get("his_max")
|
||||||
|
his_min = result[0].get("his_min")
|
||||||
|
# 再从内存里的空调池中获取最高温度和最低温度
|
||||||
|
for ac in AirConditioner.InstancesPool.values():
|
||||||
|
if ac.on and not ac.burnt and not ac.frozen:
|
||||||
|
if max_temp is None or min_temp is None:
|
||||||
|
max_temp = ac.temperature
|
||||||
|
min_temp = ac.temperature
|
||||||
|
max_temp = max(max_temp, ac.temperature)
|
||||||
|
min_temp = min(min_temp, ac.temperature)
|
||||||
|
if max_temp is None or min_temp is None:
|
||||||
|
await evt.send(f"目前全部空调都被炸掉了!")
|
||||||
|
else:
|
||||||
|
await evt.send(f"全球在线空调最高温度为 {'%.1f' % max_temp}°C,最低温度为 {'%.1f' % min_temp}°C!")
|
||||||
|
if his_max is None or his_min is None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
await evt.send(f"历史最高温度为 {'%.1f' % his_max}°C,最低温度为 {'%.1f' % his_min}°C!\n(要进入历史记录,温度需至少保持 5 分钟)")
|
||||||
@ -1,7 +1,9 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
import math
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import random
|
||||||
import signal
|
import signal
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -28,13 +30,8 @@ class CrashType(Enum):
|
|||||||
|
|
||||||
driver = nonebot.get_driver()
|
driver = nonebot.get_driver()
|
||||||
|
|
||||||
ac_manager: 'AirConditionerManager' = None
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
async def register_startup_hook():
|
async def register_startup_hook():
|
||||||
global ac_manager
|
|
||||||
# 启动自动保存任务
|
|
||||||
ac_manager = AirConditionerManager(save_interval=300) # 5分钟
|
|
||||||
await ac_manager.start_auto_save()
|
await ac_manager.start_auto_save()
|
||||||
|
|
||||||
@driver.on_shutdown
|
@driver.on_shutdown
|
||||||
@ -89,10 +86,10 @@ class AirConditionerManager:
|
|||||||
try:
|
try:
|
||||||
await db_manager.execute_by_sql_file(
|
await db_manager.execute_by_sql_file(
|
||||||
ROOT_PATH / "sql" / "update_ac.sql",
|
ROOT_PATH / "sql" / "update_ac.sql",
|
||||||
(ac_instance.on, ac_instance.temperature,
|
[(ac_instance.on, ac_instance.temperature,
|
||||||
ac_instance.burnt, ac_instance.frozen, ac_id)
|
ac_instance.burnt, ac_instance.frozen, ac_id),(ac_id,)]
|
||||||
)
|
)
|
||||||
if(save_time - ac_instance.instance_get_time >= 3600):
|
if(save_time - ac_instance.instance_get_time >= 300): # 5 分钟
|
||||||
to_remove.append(ac_id)
|
to_remove.append(ac_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"保存空调 {ac_id} 失败: {e}")
|
logger.error(f"保存空调 {ac_id} 失败: {e}")
|
||||||
@ -105,6 +102,8 @@ class AirConditionerManager:
|
|||||||
|
|
||||||
logger.info(f"清理长期不活跃的空调实例完成,目前池内共有 {len(AirConditioner.InstancesPool)} 个实例")
|
logger.info(f"清理长期不活跃的空调实例完成,目前池内共有 {len(AirConditioner.InstancesPool)} 个实例")
|
||||||
|
|
||||||
|
ac_manager = AirConditionerManager(save_interval=300) # 5分钟
|
||||||
|
|
||||||
class AirConditioner:
|
class AirConditioner:
|
||||||
InstancesPool: dict[str, 'AirConditioner'] = {}
|
InstancesPool: dict[str, 'AirConditioner'] = {}
|
||||||
|
|
||||||
@ -119,7 +118,7 @@ class AirConditioner:
|
|||||||
@classmethod
|
@classmethod
|
||||||
async def get_ac(cls, id: str) -> 'AirConditioner':
|
async def get_ac(cls, id: str) -> 'AirConditioner':
|
||||||
if(id in cls.InstancesPool):
|
if(id in cls.InstancesPool):
|
||||||
cls.refresh_ac(id)
|
await cls.refresh_ac(id)
|
||||||
return cls.InstancesPool[id]
|
return cls.InstancesPool[id]
|
||||||
# 如果没有,那么从数据库重新实例化一个 AC 出来
|
# 如果没有,那么从数据库重新实例化一个 AC 出来
|
||||||
result = await db_manager.query_by_sql_file(ROOT_PATH / "sql" / "query_ac.sql", (id,))
|
result = await db_manager.query_by_sql_file(ROOT_PATH / "sql" / "query_ac.sql", (id,))
|
||||||
@ -140,16 +139,35 @@ class AirConditioner:
|
|||||||
ac = AirConditioner(id)
|
ac = AirConditioner(id)
|
||||||
await db_manager.execute_by_sql_file(
|
await db_manager.execute_by_sql_file(
|
||||||
ROOT_PATH / "sql" / "insert_ac.sql",
|
ROOT_PATH / "sql" / "insert_ac.sql",
|
||||||
(id, ac.on, ac.temperature, ac.burnt, ac.frozen)
|
[(id, ac.on, ac.temperature, ac.burnt, ac.frozen),(id,)]
|
||||||
)
|
)
|
||||||
await cls.storage_ac(id, ac)
|
await cls.storage_ac(id, ac)
|
||||||
return ac
|
return ac
|
||||||
|
|
||||||
|
async def change_ac_temp(self, temperature_delta: float) -> None:
|
||||||
|
'''
|
||||||
|
改变空调的温度
|
||||||
|
:param temperature_delta: float 温度变化量
|
||||||
|
'''
|
||||||
|
changed_temp = self.temperature + temperature_delta
|
||||||
|
random_poss = random.random()
|
||||||
|
if temperature_delta < 0 and changed_temp < 0:
|
||||||
|
# 根据温度随机出是否冻结,0度开始,呈指数增长
|
||||||
|
possibility = -math.e ** (changed_temp / 50) + 1
|
||||||
|
if random_poss < possibility:
|
||||||
|
await self.broke_ac(CrashType.FROZEN)
|
||||||
|
elif temperature_delta > 0 and changed_temp > 40:
|
||||||
|
# 根据温度随机出是否烧坏,40度开始,呈指数增长
|
||||||
|
possibility = -math.e ** ((40-changed_temp) / 50) + 1
|
||||||
|
if random_poss < possibility:
|
||||||
|
await self.broke_ac(CrashType.BURNT)
|
||||||
|
self.temperature = changed_temp
|
||||||
|
|
||||||
async def update_ac(self, state: bool = None, temperature_delta: float = None, burnt: bool = None, frozen: bool = None) -> 'AirConditioner':
|
async def update_ac(self, state: bool = None, temperature_delta: float = None, burnt: bool = None, frozen: bool = None) -> 'AirConditioner':
|
||||||
if state is not None:
|
if state is not None:
|
||||||
self.on = state
|
self.on = state
|
||||||
if temperature_delta is not None:
|
if temperature_delta is not None:
|
||||||
self.temperature += temperature_delta
|
await self.change_ac_temp(temperature_delta)
|
||||||
if burnt is not None:
|
if burnt is not None:
|
||||||
self.burnt = burnt
|
self.burnt = burnt
|
||||||
if frozen is not None:
|
if frozen is not None:
|
||||||
|
|||||||
@ -7,6 +7,17 @@ CREATE TABLE IF NOT EXISTS air_conditioner (
|
|||||||
frozen BOOLEAN NOT NULL
|
frozen BOOLEAN NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS air_conditioner_log (
|
||||||
|
log_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
log_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
id VARCHAR(128),
|
||||||
|
"on" BOOLEAN NOT NULL,
|
||||||
|
temperature REAL NOT NULL,
|
||||||
|
burnt BOOLEAN NOT NULL,
|
||||||
|
frozen BOOLEAN NOT NULL,
|
||||||
|
FOREIGN KEY (id) REFERENCES air_conditioner(id)
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS air_conditioner_crash_log (
|
CREATE TABLE IF NOT EXISTS air_conditioner_crash_log (
|
||||||
id VARCHAR(128) NOT NULL,
|
id VARCHAR(128) NOT NULL,
|
||||||
crash_type INT NOT NULL,
|
crash_type INT NOT NULL,
|
||||||
|
|||||||
@ -1,3 +1,8 @@
|
|||||||
-- 插入一台新空调
|
-- 插入一台新空调
|
||||||
INSERT INTO air_conditioner (id, "on", temperature, burnt, frozen)
|
INSERT INTO air_conditioner (id, "on", temperature, burnt, frozen)
|
||||||
VALUES (?, ?, ?, ?, ?);
|
VALUES (?, ?, ?, ?, ?);
|
||||||
|
-- 使用返回的数据插入日志
|
||||||
|
INSERT INTO air_conditioner_log (id, "on", temperature, burnt, frozen)
|
||||||
|
SELECT id, "on", temperature, burnt, frozen
|
||||||
|
FROM air_conditioner
|
||||||
|
WHERE id = ?;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
-- 查询空调状态,如果没有就插入一条新的记录
|
-- 查询空调状态
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM air_conditioner
|
FROM air_conditioner
|
||||||
WHERE id = ?;
|
WHERE id = ?;
|
||||||
13
konabot/plugins/air_conditioner/sql/query_peak.sql
Normal file
13
konabot/plugins/air_conditioner/sql/query_peak.sql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
-- 查询目前所有空调中的最高温度与最低温度与历史最高低温
|
||||||
|
SELECT
|
||||||
|
(SELECT MAX(temperature) FROM air_conditioner
|
||||||
|
WHERE "on" = TRUE AND NOT frozen AND NOT burnt) AS max,
|
||||||
|
|
||||||
|
(SELECT MIN(temperature) FROM air_conditioner
|
||||||
|
WHERE "on" = TRUE AND NOT frozen AND NOT burnt) AS min,
|
||||||
|
|
||||||
|
(SELECT MAX(temperature) FROM air_conditioner_log
|
||||||
|
WHERE "on" = TRUE AND NOT frozen AND NOT burnt) AS his_max,
|
||||||
|
|
||||||
|
(SELECT MIN(temperature) FROM air_conditioner_log
|
||||||
|
WHERE "on" = TRUE AND NOT frozen AND NOT burnt) AS his_min;
|
||||||
@ -2,3 +2,9 @@
|
|||||||
UPDATE air_conditioner
|
UPDATE air_conditioner
|
||||||
SET "on" = ?, temperature = ?, burnt = ?, frozen = ?
|
SET "on" = ?, temperature = ?, burnt = ?, frozen = ?
|
||||||
WHERE id = ?;
|
WHERE id = ?;
|
||||||
|
|
||||||
|
-- 插入日志记录(从更新后的数据获取)
|
||||||
|
INSERT INTO air_conditioner_log (id, "on", temperature, burnt, frozen)
|
||||||
|
SELECT id, "on", temperature, burnt, frozen
|
||||||
|
FROM air_conditioner
|
||||||
|
WHERE id = ?;
|
||||||
Reference in New Issue
Block a user