167 lines
9.4 KiB
Python
167 lines
9.4 KiB
Python
# coding=utf-8
|
||
|
||
import os
|
||
import time
|
||
|
||
CAIHUI_DATABASE = {
|
||
'name': os.environ.get('DISH_DB_NAME', 'goods'),
|
||
'host': os.environ.get('CARC_CRAWLER_DB_HOST', '127.0.0.1'),
|
||
'port': int(os.environ.get('CARC_CRAWLER_DB_PORT', 3306)),
|
||
'user': os.environ.get('CARC_CRAWLER_DB_USER', 'root'),
|
||
'password': os.environ.get('CARC_CRAWLER_DB_PASSWORD', '123456'),
|
||
}
|
||
|
||
# SQLite 本地临时缓存(识别读此库;变更后从 MySQL 拉取刷新)
|
||
DISH_SQLITE_PATH = os.environ.get(
|
||
'DISH_SQLITE_PATH',
|
||
os.path.join(os.path.dirname(__file__), 'dish_templates.db'),
|
||
)
|
||
|
||
# 本机基地编码(学菜写入 source_base_id;结果 API 的 base_id 默认也用它)
|
||
# 建议填管理系统返回的 code,例如「上海路基地-1788059884828-e55vu」
|
||
DISH_BASE_CODE = os.environ.get('DISH_BASE_CODE', 'BASE_LOCAL_01')
|
||
DISH_BASE_NAME = os.environ.get('DISH_BASE_NAME', '奥体基地')
|
||
DISH_BASE_REGION = os.environ.get('DISH_BASE_REGION', '')
|
||
|
||
# 管理系统:公开接口(X-API-Key)
|
||
# 完整 Key 见 docs/第三方接口文档_mgmt.md(含 dish_ 前缀)
|
||
DISH_MGMT_API_ORIGIN = os.environ.get(
|
||
'DISH_MGMT_API_ORIGIN',
|
||
'https://cpsb.ruimeida.cc',
|
||
).rstrip('/')
|
||
DISH_MGMT_API_URL = os.environ.get(
|
||
'DISH_MGMT_API_URL',
|
||
f'{DISH_MGMT_API_ORIGIN}/api/public/bases',
|
||
)
|
||
DISH_MGMT_RESULT_API_URL = os.environ.get(
|
||
'DISH_MGMT_RESULT_API_URL',
|
||
f'{DISH_MGMT_API_ORIGIN}/api/public/recognition-orders',
|
||
)
|
||
DISH_MGMT_UNRECOGNIZED_API_URL = os.environ.get(
|
||
'DISH_MGMT_UNRECOGNIZED_API_URL',
|
||
f'{DISH_MGMT_API_ORIGIN}/api/public/unrecognized-samples',
|
||
)
|
||
DISH_MGMT_MEAL_API_URL = os.environ.get(
|
||
'DISH_MGMT_MEAL_API_URL',
|
||
f'{DISH_MGMT_API_ORIGIN}/api/public/meal-schedules/today',
|
||
)
|
||
DISH_MGMT_DEVICE_STATUS_URL = os.environ.get(
|
||
'DISH_MGMT_DEVICE_STATUS_URL',
|
||
f'{DISH_MGMT_API_ORIGIN}/api/public/devices/status',
|
||
)
|
||
DISH_MGMT_DISHES_API_URL = os.environ.get(
|
||
'DISH_MGMT_DISHES_API_URL',
|
||
f'{DISH_MGMT_API_ORIGIN}/api/public/dishes',
|
||
)
|
||
DISH_MGMT_DISH_LEARNING_STATUS_URL = os.environ.get(
|
||
'DISH_MGMT_DISH_LEARNING_STATUS_URL',
|
||
f'{DISH_MGMT_API_ORIGIN}/api/public/dishes/learning-status',
|
||
)
|
||
DISH_MGMT_API_KEY = os.environ.get(
|
||
'DISH_MGMT_API_KEY',
|
||
'dish_a4f7a1287198179fdc6a6571aa14064c2afc6338c6730e2b668ac5be15bdf769',
|
||
)
|
||
DISH_MGMT_API_TIMEOUT = float(os.environ.get('DISH_MGMT_API_TIMEOUT', '10'))
|
||
DISH_MGMT_SYNC_ON_START = os.environ.get('DISH_MGMT_SYNC_ON_START', '1') == '1'
|
||
DISH_MGMT_MEAL_ON_START = os.environ.get('DISH_MGMT_MEAL_ON_START', '1') == '1'
|
||
|
||
# 本机设备(上报设备状态)
|
||
DISH_DEVICE_CODE = os.environ.get('DISH_DEVICE_CODE', '') or os.environ.get('DISH_BASE_CODE', DISH_BASE_CODE)
|
||
DISH_DEVICE_NAME = os.environ.get('DISH_DEVICE_NAME', '') or os.environ.get('DISH_BASE_NAME', DISH_BASE_NAME)
|
||
DISH_DEVICE_STATUS_INTERVAL = int(os.environ.get('DISH_DEVICE_STATUS_INTERVAL', '60')) # 秒
|
||
DISH_DEVICE_STATUS_ENABLED = os.environ.get('DISH_DEVICE_STATUS_ENABLED', '1') == '1'
|
||
|
||
# 启动时是否从 MySQL 拉取;后台定时同步间隔(秒,含上传队列 flush + 拉取)
|
||
DISH_SYNC_ON_START = os.environ.get('DISH_SYNC_ON_START', '1') == '1'
|
||
DISH_SYNC_INTERVAL = int(os.environ.get('DISH_SYNC_INTERVAL', 300)) # 默认 5 分钟
|
||
|
||
# 识别框是否显示「似xx 检xx」(默认关;需要时 set DISH_SHOW_BOX_SCORES=1)
|
||
DISH_SHOW_BOX_SCORES = os.environ.get('DISH_SHOW_BOX_SCORES', '0') == '1'
|
||
|
||
# 当前餐段匹配:有当日排餐时仅在「今日当前餐段」菜品中比对,未命中直接未知(不回退大库)
|
||
# 1) 最高分 ≥ 0.80 → 认定;2) 未达 0.80 但最高分 > 0.75 → 仍认定;否则未知
|
||
DISH_MEAL_SMALL_LIB_THRESHOLD = float(os.environ.get('DISH_MEAL_SMALL_LIB_THRESHOLD', '0.82'))
|
||
DISH_MEAL_SMALL_LIB_SOFT_THRESHOLD = float(os.environ.get('DISH_MEAL_SMALL_LIB_SOFT_THRESHOLD', '0.82'))
|
||
# 早于该时刻且尚无当日排餐 → 不做当前餐段限制,直接走大库
|
||
DISH_MEAL_CUTOFF_HOUR = int(os.environ.get('DISH_MEAL_CUTOFF_HOUR', '7'))
|
||
|
||
# 主食小库:不在每日排餐中,常驻候选;与「当前餐段菜品小库」双路匹配
|
||
# 环境变量 DISH_STAPLE_NAMES 逗号分隔覆盖;仅当本地模板库已有同名模板时才参与比对
|
||
_DISH_STAPLE_NAMES_DEFAULT = (
|
||
'米饭,白米饭,杂粮饭,黑米饭,馒头,花卷,包子,面条,汤面,炒饭,盖浇饭,粥,白粥,小米粥,馄饨,饺子,'
|
||
'酸奶,香蕉,苹果,梨子,葡萄,营养汤,土豆,黄瓜,山药,红薯,玉米'
|
||
)
|
||
DISH_STAPLE_NAMES = [
|
||
x.strip() for x in os.environ.get('DISH_STAPLE_NAMES', _DISH_STAPLE_NAMES_DEFAULT).split(',')
|
||
if x.strip()
|
||
]
|
||
DISH_STAPLE_LIB_THRESHOLD = float(os.environ.get('DISH_STAPLE_LIB_THRESHOLD', '0.80'))
|
||
DISH_STAPLE_LIB_SOFT_THRESHOLD = float(os.environ.get('DISH_STAPLE_LIB_SOFT_THRESHOLD', '0.81'))
|
||
# 主食与餐段菜都达标时:主食分 ≥ 菜分 - margin 则优先认主食(减轻主食被菜品抢走)
|
||
DISH_STAPLE_PRIORITY_MARGIN = float(os.environ.get('DISH_STAPLE_PRIORITY_MARGIN', '0.03'))
|
||
# 每餐开始前多少分钟拉取排餐(默认 60 = 开餐前 1 小时)
|
||
DISH_MEAL_PREFETCH_MINUTES = int(os.environ.get('DISH_MEAL_PREFETCH_MINUTES', '60'))
|
||
# 尚无餐段信息时的兜底拉取时刻(启动后第一次排餐未到位时用)
|
||
DISH_MEAL_REFRESH_HOUR = int(os.environ.get('DISH_MEAL_REFRESH_HOUR', '6'))
|
||
DISH_MEAL_REFRESH_MINUTE = int(os.environ.get('DISH_MEAL_REFRESH_MINUTE', '0'))
|
||
|
||
# 实时扫描:未知菜写入待学习前的最短稳定秒数(客人放盘短,默认 1 秒)
|
||
# 盘子移走时不刷入,避免移盘过程中的误检未知
|
||
DISH_LIVE_PENDING_STABLE_SECONDS = float(os.environ.get('DISH_LIVE_PENDING_STABLE_SECONDS', '1.0'))
|
||
# 运动判定:从动到静后,连续多少帧与静止锚点相似,才触发 YOLO 一次(按相机帧,不按秒)。
|
||
# 持续静止不再识别,须再经历「静→动→静」。默认 4 帧(约对应原 0.2s@20Hz 轮询)。
|
||
DISH_LIVE_STILL_HOLD_FRAMES = int(os.environ.get('DISH_LIVE_STILL_HOLD_FRAMES', '4'))
|
||
# 已有订单时,菜品区域相似度阈值(更低=更宽松,手在中间晃仍算静止,减少误触发)
|
||
DISH_LIVE_MOTION_SIM_WITH_ORDER = float(os.environ.get('DISH_LIVE_MOTION_SIM_WITH_ORDER', '0.970'))
|
||
# 订单防抖:菜品名称集合去重 + 时间间隔(秒),避免同一次晃动重复上传
|
||
# 默认 2s(建议 2~3s)
|
||
DISH_LIVE_ORDER_DEBOUNCE_SECONDS = float(os.environ.get('DISH_LIVE_ORDER_DEBOUNCE_SECONDS', '2'))
|
||
# 无餐盘持续该秒数后清场解锁,允许下一盘(含相同菜名)重新存图/上报
|
||
DISH_LIVE_ORDER_CLEAR_HOLD_SECONDS = float(os.environ.get('DISH_LIVE_ORDER_CLEAR_HOLD_SECONDS', '1.5'))
|
||
# 同场景去重:按预览帧计数(约 25fps,默认 40 帧 ≈ 1.6s)。
|
||
# 窗口内即使个别菜名不同,只要检测框布局相近也视为同一单(不重复上报)
|
||
DISH_LIVE_ORDER_SAME_SCENE_FRAMES = int(os.environ.get('DISH_LIVE_ORDER_SAME_SCENE_FRAMES', '40'))
|
||
# 框匹配 IoU 阈值;匹配对数占比阈值(0~1)
|
||
DISH_LIVE_ORDER_LAYOUT_IOU = float(os.environ.get('DISH_LIVE_ORDER_LAYOUT_IOU', '0.40'))
|
||
DISH_LIVE_ORDER_LAYOUT_MATCH_RATIO = float(os.environ.get('DISH_LIVE_ORDER_LAYOUT_MATCH_RATIO', '0.70'))
|
||
|
||
# YOLO 检测需直接丢弃的类别(不裁剪、不识别、不上报),逗号分隔
|
||
DISH_DETECT_FILTER_NAMES = [
|
||
x.strip() for x in os.environ.get('DISH_DETECT_FILTER_NAMES', '手,空盘').split(',')
|
||
if x.strip()
|
||
]
|
||
# 贴边不完整框过滤:托盘刚入画只露一角时 YOLO 常会检出,直接丢弃
|
||
# 框任一边缘距画面边界 ≤ margin 像素,视为贴边
|
||
DISH_DETECT_EDGE_MARGIN_PX = int(os.environ.get('DISH_DETECT_EDGE_MARGIN_PX', '8'))
|
||
# 贴边且框面积占整图比例低于此值 → 丢弃(默认 2%)
|
||
DISH_DETECT_EDGE_MIN_AREA_RATIO = float(os.environ.get('DISH_DETECT_EDGE_MIN_AREA_RATIO', '0.02'))
|
||
# 贴边且框较短边像素低于此值 → 丢弃
|
||
DISH_DETECT_EDGE_MIN_SIDE_PX = int(os.environ.get('DISH_DETECT_EDGE_MIN_SIDE_PX', '48'))
|
||
# 任意框(不论是否贴边)面积占比低于此值也丢弃;0=关闭
|
||
DISH_DETECT_MIN_AREA_RATIO = float(os.environ.get('DISH_DETECT_MIN_AREA_RATIO', '0.008'))
|
||
|
||
# 用本地视频文件夹代替相机实时流(调试/回放,主要给海康入口用)。
|
||
# 1=读 DISH_VIDEO_SOURCE_DIR,0=读相机。usb_camera_app 固定读 USB 相机,不受此项影响。
|
||
DISH_USE_VIDEO_FOLDER = os.environ.get('DISH_USE_VIDEO_FOLDER', '0') == '1'
|
||
DISH_VIDEO_SOURCE_DIR = os.environ.get('DISH_VIDEO_SOURCE_DIR', 'videos')
|
||
|
||
# 相机预览流本地录像:相机连接后自动录,只保留最近 N 分钟(滚动删除旧段)
|
||
# 视频回放模式下默认关闭,避免边读边写同一目录
|
||
_STREAM_RECORD_DEFAULT = '0' if DISH_USE_VIDEO_FOLDER else '1'
|
||
DISH_STREAM_RECORD_ENABLED = os.environ.get(
|
||
'DISH_STREAM_RECORD_ENABLED', _STREAM_RECORD_DEFAULT,
|
||
) == '1'
|
||
DISH_STREAM_RECORD_DIR = os.environ.get('DISH_STREAM_RECORD_DIR', 'videos')
|
||
DISH_STREAM_RETENTION_MINUTES = float(os.environ.get('DISH_STREAM_RETENTION_MINUTES', '3000'))
|
||
# 单段时长(秒),默认 5 分钟一切,便于清理
|
||
DISH_STREAM_SEGMENT_SECONDS = float(os.environ.get('DISH_STREAM_SEGMENT_SECONDS', '350'))
|
||
DISH_STREAM_RECORD_FPS = float(os.environ.get('DISH_STREAM_RECORD_FPS', '10'))
|
||
DISH_STREAM_MAX_WIDTH = int(os.environ.get('DISH_STREAM_MAX_WIDTH', '1280'))
|
||
|
||
|
||
DEBUG = True
|
||
|
||
|
||
LOG_DIR = os.path.join(os.getcwd(), f'log/{time.strftime("%Y-%m-%d")}.log')
|
||
LOG_FORMAT = '<level>{level: <8}</level> <green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> - <cyan>{name}</cyan>:<cyan>{function}</cyan> - <level>{message}</level>'
|