157 lines
5.7 KiB
Python
157 lines
5.7 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""园区数据模拟引擎 —— 由前端 parkData.js 移植,后端统一产生数据
|
|||
|
|
前端通过 GET /api/dashboard/snapshot 获取,或订阅 MQTT opc/dashboard/tick
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import random
|
|||
|
|
import threading
|
|||
|
|
import time
|
|||
|
|
|
|||
|
|
MODEL_NAMES = ["DeepSeek-V3", "通义千问", "智谱 GLM-4", "豆包", "讯飞星火"]
|
|||
|
|
TOOL_NAMES = ["文档生成", "数据查询", "图像创作", "代码执行", "语音合成"]
|
|||
|
|
COMPANY_NAMES = [
|
|||
|
|
"云南派音人工智能科技", "米勒克尔蓝宝石珠宝", "中泰研学合作", "云南宸中低空经济",
|
|||
|
|
"昆明智海银高文化科技", "云南廷秀文旅康养", "瀚颖AI+教育信息咨询",
|
|||
|
|
"仰光客厅", "云南上古绝学文化", "中越生物医疗", "酷享野农AI农业",
|
|||
|
|
"滇缅国际设计", "昆明舒诺生物科技", "达岸教育管理", "花仙子园艺肥料",
|
|||
|
|
"研X同行者网络", "鬼才明AI创意工作室", "朵哈·玫瑰特色产业链", "昆明云韵体育",
|
|||
|
|
"南菌优培食用菌", "五华区丽裳文化", "云南星瑞航空", "综合直播私域平台",
|
|||
|
|
"昆明屿澈电商", "蓝智科技", "云品出滇·纸享万家", "启元人工智能科技",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _pick(arr):
|
|||
|
|
return arr[random.randrange(len(arr))]
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _now_time():
|
|||
|
|
return time.strftime("%H:%M:%S")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def make_series(base, growth, noise, n=30):
|
|||
|
|
out = []
|
|||
|
|
v = base
|
|||
|
|
for _ in range(n):
|
|||
|
|
v = v * (1 + growth) + (random.random() - 0.5) * noise
|
|||
|
|
out.append(round(max(1, v * 100) / 100, 2))
|
|||
|
|
return out
|
|||
|
|
|
|||
|
|
|
|||
|
|
def gen_event():
|
|||
|
|
r = random.random
|
|||
|
|
pool = [
|
|||
|
|
{"icon": "bolt", "text": f"AI 推理任务完成 · 消耗 {round(800 + r() * 9000):,} tokens({_pick(MODEL_NAMES)})"},
|
|||
|
|
{"icon": "wrench", "text": f"工具「{_pick(TOOL_NAMES)}」被调用 {round(10 + r() * 90)} 次"},
|
|||
|
|
{"icon": "building", "text": f"「{_pick(COMPANY_NAMES)}」提交入驻申请 · 进入评审流程"},
|
|||
|
|
{"icon": "users", "text": f"「{_pick(COMPANY_NAMES)}」新增招聘岗位 {round(1 + r() * 5)} 个"},
|
|||
|
|
{"icon": "coin", "text": f"园区企业完成一笔 ¥{(0.5 + r() * 9):.1f}万 交易"},
|
|||
|
|
{"icon": "robot", "text": f"「{_pick(MODEL_NAMES)}」模型完成一次微调任务"},
|
|||
|
|
]
|
|||
|
|
e = _pick(pool)
|
|||
|
|
return {"id": f"{int(time.time()*1000)}-{random.random()}", "icon": e["icon"], "text": e["text"], "time": _now_time()}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def init_feed():
|
|||
|
|
return [
|
|||
|
|
{"id": 1, "icon": "bolt", "text": "云南派音AI 完成音频向量嵌入任务 · 消耗 12,480 tokens", "time": _now_time()},
|
|||
|
|
{"id": 2, "icon": "building", "text": "「启元人工智能科技」通过评审 · 正式入驻 OPC 创业空间", "time": _now_time()},
|
|||
|
|
{"id": 3, "icon": "users", "text": "「昆明舒护安养老服务」新增招聘岗位 2 个", "time": _now_time()},
|
|||
|
|
{"id": 4, "icon": "coin", "text": "园区企业完成一笔 ¥3.6万 交易", "time": _now_time()},
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
|
|||
|
|
def init_snapshot():
|
|||
|
|
return {
|
|||
|
|
"t": 0,
|
|||
|
|
"token": {"today": 128.64, "total": 12840, "rate": 84.6, "series": make_series(82, 0.012, 9)},
|
|||
|
|
"tools": {"today": 3568, "total": 365204, "success": 98.7},
|
|||
|
|
"projects": {"inPark": 158, "cum": 208, "todayNew": 2},
|
|||
|
|
"jobs": {"total": 2186, "todayNew": 3},
|
|||
|
|
"revenue": {"today": 38.6, "total": 20800, "growth": 8.2, "series": make_series(30, 0.006, 4)},
|
|||
|
|
"park": {"devices": 98.6, "energy": 386, "people": 127, "desk": 76, "meeting": 3, "nodes": 12},
|
|||
|
|
"feed": init_feed(),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def next_snapshot(s):
|
|||
|
|
r = random.random
|
|||
|
|
t_delta = round(0.26 + r() * 0.34, 2)
|
|||
|
|
|
|||
|
|
token = {
|
|||
|
|
"today": round(s["token"]["today"] + t_delta, 2),
|
|||
|
|
"total": round(s["token"]["total"] + t_delta, 2),
|
|||
|
|
"rate": round(76 + r() * 20, 1),
|
|||
|
|
}
|
|||
|
|
tool_delta = round(13 + r() * 22)
|
|||
|
|
tools = {
|
|||
|
|
"today": s["tools"]["today"] + tool_delta,
|
|||
|
|
"total": s["tools"]["total"] + tool_delta,
|
|||
|
|
"success": round(98.1 + r() * 1.2, 1),
|
|||
|
|
}
|
|||
|
|
rev_delta = round(0.6 + r() * 1.7, 1)
|
|||
|
|
revenue = {
|
|||
|
|
"today": round(s["revenue"]["today"] + rev_delta, 1),
|
|||
|
|
"total": round(s["revenue"]["total"] + rev_delta, 1),
|
|||
|
|
"growth": round(7.2 + r() * 2.2, 1),
|
|||
|
|
}
|
|||
|
|
park = {
|
|||
|
|
"devices": round(97.6 + r() * 1.6, 1),
|
|||
|
|
"energy": round(320 + r() * 130),
|
|||
|
|
"people": round(80 + r() * 95),
|
|||
|
|
"desk": round(62 + r() * 24),
|
|||
|
|
"meeting": round(2 + r() * 4),
|
|||
|
|
"nodes": 12,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
projects = dict(s["projects"])
|
|||
|
|
if r() < 0.055:
|
|||
|
|
projects["todayNew"] += 1
|
|||
|
|
if r() < 0.035:
|
|||
|
|
projects["inPark"] += 1
|
|||
|
|
projects["cum"] += 1
|
|||
|
|
jobs = dict(s["jobs"])
|
|||
|
|
if r() < 0.08:
|
|||
|
|
jobs["todayNew"] += 1
|
|||
|
|
if r() < 0.05:
|
|||
|
|
jobs["total"] += 1
|
|||
|
|
|
|||
|
|
t_series = list(s["token"]["series"])
|
|||
|
|
t_series[-1] = round(t_series[-1] + t_delta, 2)
|
|||
|
|
if s["t"] % 12 == 11:
|
|||
|
|
t_series = t_series[1:] + [round(72 + r() * 30, 2)]
|
|||
|
|
token["series"] = t_series
|
|||
|
|
|
|||
|
|
r_series = list(s["revenue"]["series"])
|
|||
|
|
r_series[-1] = round(r_series[-1] + rev_delta, 1)
|
|||
|
|
if s["t"] % 12 == 11:
|
|||
|
|
r_series = r_series[1:] + [round(26 + r() * 9, 1)]
|
|||
|
|
revenue["series"] = r_series
|
|||
|
|
|
|||
|
|
feed = [gen_event()] + s["feed"][:6] if s["t"] % 3 == 2 else s["feed"]
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"t": s["t"] + 1,
|
|||
|
|
"token": token, "tools": tools, "revenue": revenue,
|
|||
|
|
"park": park, "projects": projects, "jobs": jobs, "feed": feed,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
class SimEngine:
|
|||
|
|
"""带锁的快照引擎:单例供 API 与 MQTT tick 共用"""
|
|||
|
|
|
|||
|
|
def __init__(self):
|
|||
|
|
self._lock = threading.RLock()
|
|||
|
|
self._snap = init_snapshot()
|
|||
|
|
|
|||
|
|
def snapshot(self):
|
|||
|
|
with self._lock:
|
|||
|
|
return dict(self._snap)
|
|||
|
|
|
|||
|
|
def tick(self):
|
|||
|
|
with self._lock:
|
|||
|
|
self._snap = next_snapshot(self._snap)
|
|||
|
|
return dict(self._snap)
|
|||
|
|
|
|||
|
|
|
|||
|
|
sim_engine = SimEngine()
|