新应用 src/pCanBridge(独立 MOOS App,单通道): - CanEndpoint:TCP Client 连接 CANET/USBCAN-8E-U 工作端口 (目标 IP/端口可配置,默认 192.168.0.222:4001=CAN0), 接收线程流式缓冲按 13 字节切帧,过滤无效 DLC(>8), 断线自动重连(非阻塞 connect+超时 / SO_RCVTIMEO 读超时)。 - 帧协议(CANET-8E-U 手册 8.1):byte0=帧信息(bit7 FF/bit6 RTR/ bit3~0 DLC),byte1~4=ID 大端,byte5~12=数据。 - MOOS 映射:变量名 CAN_0x%08X=CAN ID(不缩减),m_sVal=二进制 data(逐帧透传不限流),m_sSrcAux=通道名(can_channel_name), m_dfVal2=原始帧信息字节(用于区分标准/扩展、数据/远程帧)。 - CanDbStore:每帧落库 SQLite(can_frame 表:time/channel/ frame_info/can_id/dlc/hex,含 time、can_id 索引),参考 pCCU DbStore:预处理语句 + WAL + synchronous=NORMAL,路径由 dbpath 配置;复用 pPowerManger 的 sqlite3 amalgamation。 - AppCast 输出连接状态/收帧/发布/落库统计。 配套: - missions/h100.moos 新增 pCanBridge 配置块(板卡 dbpath 指向 data 目录)。 - build-board.sh / deploy.sh 支持 pCanBridge:产物部署 + pCanBridge.service 安装,服务列表加入第 5 个服务。 - .gitignore 忽略 bin/pCanBridge 与本机默认数据库。 已验证(板卡 192.168.0.223 实测):服务 active,连接设备正常, ~60 帧/s 透传,落库 9000+ 行 integrity ok、无丢帧。
291 lines
11 KiB
Bash
Executable File
291 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
||
#=======================================================================
|
||
# FILE: scripts/deploy.sh
|
||
# DESC: 部署 pPowerManger / pPowerMangerHost / pCCU / pCanBridge(aarch64 二进制 +
|
||
# mission)到目标板卡 (RK3588 / Ubuntu 22.04),并用五个独立
|
||
# systemd 服务(moosdb / pPowerManger / pPowerMangerHost / pCCU /
|
||
# pCanBridge)管理。只安装服务 + daemon-reload,默认不启动、不自启。
|
||
#
|
||
# 用法:
|
||
# ./scripts/deploy.sh setup-ssh 一次性:生成 ed25519 key + ssh-copy-id
|
||
# ./scripts/deploy.sh 默认:推送二进制 + mission + 装 4 个
|
||
# systemd 服务 + daemon-reload(不启动)
|
||
# ./scripts/deploy.sh --start 部署后按依赖顺序启动服务
|
||
# ./scripts/deploy.sh status 板卡 4 个服务状态
|
||
# ./scripts/deploy.sh stop 停止 4 个服务
|
||
# ./scripts/deploy.sh help 显示帮助
|
||
#
|
||
# 参数:
|
||
# --host <ip> 目标主机(默认 192.168.0.223)
|
||
# --user <user> 登录用户(默认 root)
|
||
# --board-dir <dir> 板卡部署根目录(默认 /root/work/h100)
|
||
# --start 部署后立即启动服务
|
||
# --ccu-host <ip> 覆盖 mission 中 pPowerManger 的 CCU 地址 (ccuhost)
|
||
# --ccu-port <n> 覆盖 mission 中 pPowerManger 的 CCU 端口 (ccuport)
|
||
#
|
||
# 板卡布局:
|
||
# /root/work/h100/bin/ aarch64 二进制
|
||
# /root/work/h100/missions/ 板卡 mission h100.moos
|
||
# /root/work/h100/data/ 数据库存储目录 (power_data.db 等)
|
||
# /etc/systemd/system/ moosdb.service / pPowerManger.service /
|
||
# pPowerMangerHost.service / pCCU.service /
|
||
# pCanBridge.service
|
||
#=======================================================================
|
||
set -uo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
PROJECT_ROOT="$(dirname "${SCRIPT_DIR}")"
|
||
|
||
HOST="192.168.0.223"
|
||
USER="root"
|
||
BOARD_DIR="/root/work/h100"
|
||
DO_START=0
|
||
CCU_HOST=""
|
||
CCU_PORT=""
|
||
|
||
SERVICES=(moosdb pPowerManger pPowerMangerHost pCCU pCanBridge)
|
||
|
||
info() { printf '\033[1;36m[deploy]\033[0m %s\n' "$*"; }
|
||
ok() { printf '\033[1;32m[deploy]\033[0m %s\n' "$*"; }
|
||
warn() { printf '\033[1;33m[deploy]\033[0m %s\n' "$*"; }
|
||
err() { printf '\033[1;31m[deploy]\033[0m %s\n' "$*"; }
|
||
|
||
usage() {
|
||
sed -n '2,31p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
||
exit 0
|
||
}
|
||
|
||
#-------------------------------------------------------------------
|
||
# 参数解析
|
||
#-------------------------------------------------------------------
|
||
ACTION=""
|
||
while [ $# -gt 0 ]; do
|
||
case "$1" in
|
||
--host) [ $# -ge 2 ] || { err "--host 需要参数"; exit 1; }; HOST="$2"; shift 2 ;;
|
||
--user) [ $# -ge 2 ] || { err "--user 需要参数"; exit 1; }; USER="$2"; shift 2 ;;
|
||
--board-dir)[ $# -ge 2 ] || { err "--board-dir 需要参数"; exit 1; }; BOARD_DIR="$2"; shift 2 ;;
|
||
--start) DO_START=1; shift ;;
|
||
--ccu-host) [ $# -ge 2 ] || { err "--ccu-host 需要参数"; exit 1; }; CCU_HOST="$2"; shift 2 ;;
|
||
--ccu-port) [ $# -ge 2 ] || { err "--ccu-port 需要参数"; exit 1; }; CCU_PORT="$2"; shift 2 ;;
|
||
setup-ssh|status|stop|help|-h|--help) ACTION="$1"; shift ;;
|
||
"") shift ;;
|
||
*) err "未知参数: $1(见 help)"; exit 1 ;;
|
||
esac
|
||
done
|
||
[ -n "${ACTION}" ] || ACTION="deploy"
|
||
|
||
case "${CCU_PORT}" in
|
||
""|*[!0-9]*) [ -z "${CCU_PORT}" ] || { err "--ccu-port 必须是数字端口: ${CCU_PORT}"; exit 1; } ;;
|
||
esac
|
||
|
||
SSH_TARGET="${USER}@${HOST}"
|
||
SSH="ssh -o BatchMode=yes -o ConnectTimeout=5"
|
||
SCP="scp -o BatchMode=yes -o ConnectTimeout=5"
|
||
|
||
#-------------------------------------------------------------------
|
||
# 命令: setup-ssh
|
||
#-------------------------------------------------------------------
|
||
cmd_setup_ssh() {
|
||
local key="$HOME/.ssh/id_ed25519"
|
||
if [ ! -f "${key}" ]; then
|
||
info "未发现 SSH key,生成 ed25519 key ..."
|
||
mkdir -p "$HOME/.ssh" && chmod 700 "$HOME/.ssh"
|
||
ssh-keygen -t ed25519 -N '' -f "${key}"
|
||
fi
|
||
info "上传公钥到 ${SSH_TARGET}(会提示输入密码,仅此一次)..."
|
||
ssh-copy-id -o ConnectTimeout=5 "${SSH_TARGET}"
|
||
ok "免密 SSH 已配置。验证: ssh ${SSH_TARGET}"
|
||
}
|
||
|
||
#-------------------------------------------------------------------
|
||
# 前置检查
|
||
#-------------------------------------------------------------------
|
||
check_local() {
|
||
local missing=0
|
||
for b in pPowerManger pPowerMangerHost pCCU pCanBridge; do
|
||
if [ ! -f "${PROJECT_ROOT}/bin/arm64/${b}" ]; then
|
||
err "缺少本地 aarch64 二进制: bin/arm64/${b}(先运行 ./scripts/build-arm64.sh)"; missing=1
|
||
elif ! file "${PROJECT_ROOT}/bin/arm64/${b}" | grep -q "aarch64"; then
|
||
err "bin/arm64/${b} 不是 aarch64 二进制"; missing=1
|
||
fi
|
||
done
|
||
if [ ! -f "${PROJECT_ROOT}/missions/h100.moos" ]; then
|
||
err "缺少板卡 mission: missions/h100.moos"; missing=1
|
||
fi
|
||
[ "${missing}" -eq 0 ] || return 1
|
||
ok "本地产物校验通过 (aarch64 + mission)"
|
||
}
|
||
|
||
check_ssh() {
|
||
if ! ${SSH} "${SSH_TARGET}" 'true' 2>/dev/null; then
|
||
err "无法免密 SSH 到 ${SSH_TARGET}"
|
||
err "请先执行: ./scripts/deploy.sh setup-ssh"
|
||
return 1
|
||
fi
|
||
ok "SSH 连接正常: ${SSH_TARGET}"
|
||
}
|
||
|
||
#-------------------------------------------------------------------
|
||
# 生成 systemd unit(含板卡绝对路径)
|
||
#-------------------------------------------------------------------
|
||
gen_moosdb_unit() {
|
||
# MOOSDB 路径在板卡探测;探测失败给出提示
|
||
local moosdb_bin
|
||
moosdb_bin="$(${SSH} "${SSH_TARGET}" 'command -v MOOSDB || true' 2>/dev/null)"
|
||
if [ -z "${moosdb_bin}" ]; then
|
||
moosdb_bin="$(command -v MOOSDB || true)"
|
||
warn "板卡未在 PATH 找到 MOOSDB,回退本机路径 ${moosdb_bin:-"(未找到)"}"
|
||
[ -n "${moosdb_bin}" ] || { err "MOOSDB 路径未知,无法生成 moosdb.service"; return 1; }
|
||
fi
|
||
ok "MOOSDB 路径: ${moosdb_bin}" >&2
|
||
|
||
cat <<EOF
|
||
[Unit]
|
||
Description=MOOSDB for H100 Power Manager
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
ExecStart=${moosdb_bin} --moos_no_colour ${BOARD_DIR}/missions/h100.moos
|
||
Restart=on-failure
|
||
RestartSec=5
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
}
|
||
|
||
gen_app_unit() {
|
||
local app="$1" deps=""
|
||
[ "${app}" = "moosdb" ] || deps="After=moosdb.service\nRequires=moosdb.service"
|
||
cat <<EOF
|
||
[Unit]
|
||
Description=${app} for H100 Power Manager
|
||
$(printf '%b' "${deps}")
|
||
|
||
[Service]
|
||
Type=simple
|
||
WorkingDirectory=${BOARD_DIR}/bin
|
||
ExecStart=${BOARD_DIR}/bin/${app} --alias=${app} ${BOARD_DIR}/missions/h100.moos
|
||
Restart=on-failure
|
||
RestartSec=5
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
}
|
||
|
||
#-------------------------------------------------------------------
|
||
# mission 中插入/替换 "key = value"(键已存在则替换,否则插到第一个
|
||
# 顶层 '}' 之前,即 pPowerManger 配置块末尾)
|
||
#-------------------------------------------------------------------
|
||
upsert_moos_param() {
|
||
local f="$1" key="$2" val="$3"
|
||
if grep -qE "^[[:space:]]*${key}[[:space:]]*=" "${f}"; then
|
||
sed -i -E "s|^([[:space:]]*)${key}[[:space:]]*=.*|\1${key} = ${val}|" "${f}"
|
||
else
|
||
awk -v kv=" ${key} = ${val}" \
|
||
'!d && /^[[:space:]]*}[[:space:]]*$/ { print kv; d=1 } { print }' \
|
||
"${f}" > "${f}.tmp" && mv "${f}.tmp" "${f}"
|
||
fi
|
||
}
|
||
|
||
#-------------------------------------------------------------------
|
||
# 命令: deploy
|
||
#-------------------------------------------------------------------
|
||
cmd_deploy() {
|
||
check_local || return 1
|
||
check_ssh || return 1
|
||
|
||
# 1. 推送二进制 + mission
|
||
info "创建板卡目录 ${BOARD_DIR}/{bin,missions,data} ..."
|
||
${SSH} "${SSH_TARGET}" "mkdir -p ${BOARD_DIR}/bin ${BOARD_DIR}/missions ${BOARD_DIR}/data" || { err "创建目录失败"; return 1; }
|
||
|
||
info "推送 aarch64 二进制 ..."
|
||
${SCP} -q "${PROJECT_ROOT}/bin/arm64/pPowerManger" \
|
||
"${PROJECT_ROOT}/bin/arm64/pPowerMangerHost" \
|
||
"${PROJECT_ROOT}/bin/arm64/pCCU" \
|
||
"${PROJECT_ROOT}/bin/arm64/pCanBridge" \
|
||
"${SSH_TARGET}:${BOARD_DIR}/bin/" || { err "推送二进制失败"; return 1; }
|
||
|
||
info "推送 mission h100.moos ..."
|
||
local mission_src="${PROJECT_ROOT}/missions/h100.moos"
|
||
local mission_push="${mission_src}" tmp_moos=""
|
||
if [ -n "${CCU_HOST}" ] || [ -n "${CCU_PORT}" ]; then
|
||
tmp_moos="$(mktemp)"
|
||
cp "${mission_src}" "${tmp_moos}"
|
||
[ -n "${CCU_HOST}" ] && upsert_moos_param "${tmp_moos}" ccuhost "${CCU_HOST}"
|
||
[ -n "${CCU_PORT}" ] && upsert_moos_param "${tmp_moos}" ccuport "${CCU_PORT}"
|
||
mission_push="${tmp_moos}"
|
||
info "CCU 地址覆盖: ccuhost=${CCU_HOST:-<保持原值>} ccuport=${CCU_PORT:-<保持原值>}"
|
||
fi
|
||
${SCP} -q "${mission_push}" \
|
||
"${SSH_TARGET}:${BOARD_DIR}/missions/" || { err "推送 mission 失败"; [ -n "${tmp_moos}" ] && rm -f "${tmp_moos}"; return 1; }
|
||
[ -n "${tmp_moos}" ] && rm -f "${tmp_moos}"
|
||
|
||
${SSH} "${SSH_TARGET}" "chmod +x ${BOARD_DIR}/bin/pPowerManger ${BOARD_DIR}/bin/pPowerMangerHost ${BOARD_DIR}/bin/pCCU ${BOARD_DIR}/bin/pCanBridge"
|
||
ok "二进制与 mission 已推送"
|
||
|
||
# 2. 生成并安装 4 个 systemd unit
|
||
local unit
|
||
for s in "${SERVICES[@]}"; do
|
||
info "生成 ${s}.service ..."
|
||
if [ "${s}" = "moosdb" ]; then
|
||
unit="$(gen_moosdb_unit)" || return 1
|
||
else
|
||
unit="$(gen_app_unit "${s}")"
|
||
fi
|
||
printf '%s\n' "${unit}" | ${SSH} "${SSH_TARGET}" "cat > /etc/systemd/system/${s}.service"
|
||
ok "${s}.service 已安装"
|
||
done
|
||
|
||
# 3. daemon-reload(不 enable,不自启)
|
||
info "systemctl daemon-reload ..."
|
||
${SSH} "${SSH_TARGET}" "systemctl daemon-reload && systemctl reset-failed ${SERVICES[*]} 2>/dev/null || true"
|
||
ok "服务已安装但未启用自启(systemctl is-enabled 应输出 disabled)"
|
||
|
||
# 4. 可选启动
|
||
if [ "${DO_START}" = "1" ]; then
|
||
cmd_start
|
||
else
|
||
echo ""
|
||
info "部署完成。手动启动(或加 --start):"
|
||
echo " systemctl start moosdb pPowerManger pPowerMangerHost pCCU pCanBridge"
|
||
echo " 浏览器: http://${HOST}:18080 (上位机模拟)"
|
||
echo " http://${HOST}:8090 (pPowerManger)"
|
||
echo " http://${HOST}:8080 (pCCU)"
|
||
fi
|
||
}
|
||
|
||
#-------------------------------------------------------------------
|
||
# 命令: start / status / stop
|
||
#-------------------------------------------------------------------
|
||
cmd_start() {
|
||
info "启动服务 (moosdb -> pPowerManger / pPowerMangerHost / pCCU / pCanBridge) ..."
|
||
${SSH} "${SSH_TARGET}" "systemctl start moosdb && sleep 1 && \
|
||
systemctl start pPowerManger pPowerMangerHost pCCU pCanBridge" || { err "启动失败"; return 1; }
|
||
ok "服务已启动"
|
||
cmd_status
|
||
}
|
||
|
||
cmd_status() {
|
||
${SSH} "${SSH_TARGET}" "systemctl --no-pager status ${SERVICES[*]}" || true
|
||
}
|
||
|
||
cmd_stop() {
|
||
${SSH} "${SSH_TARGET}" "systemctl stop pCanBridge pPowerMangerHost pPowerManger pCCU moosdb" && \
|
||
ok "服务已停止" || err "停止服务失败"
|
||
}
|
||
|
||
#-------------------------------------------------------------------
|
||
# 命令入口
|
||
#-------------------------------------------------------------------
|
||
case "${ACTION}" in
|
||
deploy) cmd_deploy ;;
|
||
setup-ssh) cmd_setup_ssh ;;
|
||
status) cmd_status ;;
|
||
stop) cmd_stop ;;
|
||
help|-h|--help) usage ;;
|
||
*) usage ;;
|
||
esac
|