mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-21 15:43:45 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c7e57a1bc1 | |||
| 353ed2a9e1 | |||
| 1db8b82f16 | |||
| e8964ce7ae | |||
| ad799442a8 | |||
| 592f062326 | |||
| 70fd56f69c | |||
| 29cd05d6ed |
@@ -23,56 +23,43 @@ env:
|
||||
CI: 1
|
||||
|
||||
jobs:
|
||||
generate_cereal_artifact:
|
||||
name: Generate cereal validation artifacts
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: true
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Build openpilot
|
||||
run: scons -j$(nproc) cereal
|
||||
- name: Dump sunnypilot schema
|
||||
run: |
|
||||
export PYTHONPATH=${{ github.workspace }}
|
||||
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py -g -f schema.json
|
||||
- name: 'Prepare artifact'
|
||||
run: |
|
||||
mkdir -p "cereal/messaging/tests/cereal_validations"
|
||||
cp cereal/messaging/tests/validate_sp_cereal_upstream.py "cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py"
|
||||
cp schema.json "cereal/messaging/tests/cereal_validations/schema.json"
|
||||
- name: 'Upload Artifact'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cereal_validations
|
||||
path: cereal/messaging/tests/cereal_validations
|
||||
|
||||
validate_cereal_with_upstream:
|
||||
name: Validate cereal with Upstream
|
||||
runs-on: ubuntu-24.04
|
||||
needs: generate_cereal_artifact
|
||||
steps:
|
||||
- name: Checkout sunnypilot
|
||||
- name: Checkout sunnypilot cereal
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout upstream openpilot
|
||||
with:
|
||||
sparse-checkout: cereal
|
||||
|
||||
- name: Init sunnypilot opendbc submodule
|
||||
run: git submodule update --init --depth 1 opendbc_repo
|
||||
|
||||
- name: Checkout upstream openpilot cereal
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: 'commaai/openpilot'
|
||||
path: openpilot
|
||||
submodules: true
|
||||
path: upstream_openpilot
|
||||
sparse-checkout: cereal
|
||||
ref: "refs/heads/master"
|
||||
- run: ./tools/op.sh setup
|
||||
- name: Build openpilot
|
||||
working-directory: openpilot
|
||||
run: scons -j$(nproc) cereal
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cereal_validations
|
||||
path: openpilot/cereal/messaging/tests/cereal_validations
|
||||
- name: 'Validate sunnypilot schema against upstream'
|
||||
|
||||
- name: Init upstream opendbc submodule
|
||||
working-directory: upstream_openpilot
|
||||
run: git submodule update --init --depth 1 opendbc_repo
|
||||
|
||||
- name: Install uv
|
||||
run: pip install uv
|
||||
|
||||
- name: Generate sunnypilot schema
|
||||
run: |
|
||||
export PYTHONPATH=${{ github.workspace }}/openpilot
|
||||
chmod +x openpilot/cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py
|
||||
python3 openpilot/cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py -r -f openpilot/cereal/messaging/tests/cereal_validations/schema.json
|
||||
PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))")
|
||||
uv run --isolated --with "pycapnp==${PYCAPNP_VER}" \
|
||||
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py \
|
||||
-g -f /tmp/sp_schema.json --cereal-dir cereal
|
||||
|
||||
- name: Validate against upstream
|
||||
run: |
|
||||
PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))")
|
||||
uv run --isolated --with "pycapnp==${PYCAPNP_VER}" \
|
||||
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py \
|
||||
-r -f /tmp/sp_schema.json --cereal-dir upstream_openpilot/cereal
|
||||
|
||||
+12
-1
@@ -1,7 +1,18 @@
|
||||
sunnypilot Version 2026.001.005 (2026-05-13)
|
||||
========================
|
||||
* What's Changed (sunnypilot/sunnypilot)
|
||||
* sunnylink: add CarParams fallback for brand-specific capabilities by @sunnyhaibin
|
||||
|
||||
sunnypilot Version 2026.001.004 (2026-05-10)
|
||||
========================
|
||||
* What's Changed (sunnypilot/sunnypilot)
|
||||
* sunnylink: switch athena domain by @DevTekVE
|
||||
* sunnylink: fix max time offroad values by @nayan8teen
|
||||
|
||||
sunnypilot Version 2026.001.003 (2026-05-08)
|
||||
========================
|
||||
* What's Changed (sunnypilot/sunnypilot)
|
||||
* manager: disable DEVELOPMENT_ONLY reset by @jason-wen
|
||||
* manager: disable DEVELOPMENT_ONLY reset by @sunnyhaibin
|
||||
|
||||
sunnypilot Version 2026.001.002 (2026-05-07)
|
||||
========================
|
||||
|
||||
@@ -13,6 +13,7 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
@@ -104,8 +105,15 @@ def collect_schema(root: Any) -> dict[str, dict]:
|
||||
return structs
|
||||
|
||||
|
||||
def dump_schema(path: str) -> None:
|
||||
from cereal import log
|
||||
def load_log(cereal_dir: str) -> Any:
|
||||
import capnp
|
||||
cereal_dir = os.path.abspath(cereal_dir)
|
||||
capnp.remove_import_hook()
|
||||
return capnp.load(os.path.join(cereal_dir, "log.capnp"), imports=[cereal_dir])
|
||||
|
||||
|
||||
def dump_schema(cereal_dir: str, path: str) -> None:
|
||||
log = load_log(cereal_dir)
|
||||
payload = {
|
||||
"root": hex_id(log.Event.schema.node.id),
|
||||
"structs": collect_schema(log.Event.schema),
|
||||
@@ -206,8 +214,8 @@ def load_peer(path: str) -> dict:
|
||||
return json.load(handle)
|
||||
|
||||
|
||||
def run_read(peer_path: str) -> int:
|
||||
from cereal import log
|
||||
def run_read(cereal_dir: str, peer_path: str) -> int:
|
||||
log = load_log(cereal_dir)
|
||||
peer_dump = load_peer(peer_path)
|
||||
local_dump = {
|
||||
"root": hex_id(log.Event.schema.node.id),
|
||||
@@ -235,16 +243,13 @@ def main() -> int:
|
||||
mode.add_argument("-g", "--generate", action="store_true", help="dump local schema to JSON")
|
||||
mode.add_argument("-r", "--read", action="store_true", help="load peer JSON and diff against local")
|
||||
parser.add_argument("-f", "--file", default="schema.json", help="JSON file path (default: schema.json)")
|
||||
parser.add_argument("--cereal-dir", required=True, help="path to cereal directory containing log.capnp")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
if args.generate:
|
||||
dump_schema(args.file)
|
||||
return 0
|
||||
return run_read(args.file)
|
||||
except ImportError as exc:
|
||||
print(f"error: cannot import cereal ({exc}). did scons build cereal?")
|
||||
return 2
|
||||
if args.generate:
|
||||
dump_schema(args.cereal_dir, args.file)
|
||||
return 0
|
||||
return run_read(args.cereal_dir, args.file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define SUNNYPILOT_VERSION "2026.001.003"
|
||||
#define SUNNYPILOT_VERSION "2026.001.005"
|
||||
|
||||
@@ -34,7 +34,7 @@ from openpilot.sunnypilot.sunnylink.utils import sunnylink_need_register, sunnyl
|
||||
from openpilot.sunnypilot.sunnylink.capabilities import generate_capabilities, CAPABILITY_LABELS
|
||||
from openpilot.sunnypilot.sunnylink.tools.generate_settings_schema import generate_schema
|
||||
|
||||
SUNNYLINK_ATHENA_HOST = os.getenv('SUNNYLINK_ATHENA_HOST', 'wss://ws.stg.api.sunnypilot.ai')
|
||||
SUNNYLINK_ATHENA_HOST = os.getenv('SUNNYLINK_ATHENA_HOST', 'wss://athena.sunnylink.ai')
|
||||
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
|
||||
LOCAL_PORT_WHITELIST = {8022}
|
||||
SUNNYLINK_LOG_ATTR_NAME = "user.sunny.upload"
|
||||
|
||||
@@ -78,6 +78,38 @@ def _bundle_field(bundle: dict | None, key: str) -> str:
|
||||
return bundle.get(key, "") if isinstance(bundle, dict) else ""
|
||||
|
||||
|
||||
def _resolve_brand_capabilities(caps: dict, bundle_platform: str, CP) -> None:
|
||||
"""Set brand-specific capabilities from bundle platform or CarParams fallback.
|
||||
|
||||
Bundle (manual car selection) is a pre-fingerprint approximation.
|
||||
CarParams (auto-fingerprint) is the authoritative post-fingerprint source.
|
||||
Mirrors the per-brand update_settings() logic in device UI layouts.
|
||||
"""
|
||||
brand = caps["brand"]
|
||||
|
||||
if brand == "hyundai":
|
||||
if bundle_platform:
|
||||
try:
|
||||
unsupported = set().union(*UNSUPPORTED_LONGITUDINAL_CAR.values())
|
||||
caps["hyundai_alpha_long_available"] = HYUNDAI_CAR[bundle_platform] not in unsupported
|
||||
except KeyError:
|
||||
cloudlog.exception(f"capabilities: unknown hyundai platform {bundle_platform!r}")
|
||||
elif CP is not None:
|
||||
caps["hyundai_alpha_long_available"] = bool(CP.alphaLongitudinalAvailable)
|
||||
|
||||
elif brand == "subaru":
|
||||
if bundle_platform:
|
||||
try:
|
||||
flags = SUBARU_CAR[bundle_platform].config.flags
|
||||
caps["subaru_has_sng"] = not bool(flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.HYBRID))
|
||||
caps["has_stop_and_go"] = caps["subaru_has_sng"]
|
||||
except KeyError:
|
||||
cloudlog.exception(f"capabilities: unknown subaru platform {bundle_platform!r}")
|
||||
elif CP is not None:
|
||||
caps["subaru_has_sng"] = not bool(CP.flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.HYBRID))
|
||||
caps["has_stop_and_go"] = caps["subaru_has_sng"]
|
||||
|
||||
|
||||
def generate_capabilities(params: Params | None = None) -> dict:
|
||||
"""Generate a SettingsCapabilities dict from CarParams + boolean params.
|
||||
|
||||
@@ -108,6 +140,7 @@ def generate_capabilities(params: Params | None = None) -> dict:
|
||||
caps["brand"] = bundle_brand
|
||||
|
||||
# CarParams-derived capabilities
|
||||
CP = None
|
||||
CP_bytes = params.get("CarParamsPersistent")
|
||||
if CP_bytes is not None:
|
||||
try:
|
||||
@@ -129,6 +162,7 @@ def generate_capabilities(params: Params | None = None) -> dict:
|
||||
# Generic SnG fallback. Brand-specific opaque flags below override.
|
||||
caps["has_stop_and_go"] = bool(CP.openpilotLongitudinalControl)
|
||||
except Exception:
|
||||
CP = None
|
||||
cloudlog.exception("capabilities: failed to deserialize CarParamsPersistent")
|
||||
|
||||
# CarParamsSP-derived capabilities
|
||||
@@ -142,23 +176,7 @@ def generate_capabilities(params: Params | None = None) -> dict:
|
||||
except Exception:
|
||||
cloudlog.exception("capabilities: failed to deserialize CarParamsSPPersistent")
|
||||
|
||||
# Brand-specific opaque flags. Mirror Raylib brand-settings logic so the
|
||||
# device and the dashboard agree on per-platform availability without
|
||||
# leaking the platform identifier over the wire.
|
||||
if caps["brand"] == "subaru" and bundle_platform:
|
||||
try:
|
||||
flags = SUBARU_CAR[bundle_platform].config.flags
|
||||
caps["subaru_has_sng"] = not bool(flags & (SubaruFlags.GLOBAL_GEN2 | SubaruFlags.HYBRID))
|
||||
caps["has_stop_and_go"] = caps["subaru_has_sng"]
|
||||
except KeyError:
|
||||
cloudlog.exception(f"capabilities: unknown subaru platform {bundle_platform!r}")
|
||||
|
||||
if caps["brand"] == "hyundai" and bundle_platform:
|
||||
try:
|
||||
unsupported = set().union(*UNSUPPORTED_LONGITUDINAL_CAR.values())
|
||||
caps["hyundai_alpha_long_available"] = HYUNDAI_CAR[bundle_platform] not in unsupported
|
||||
except KeyError:
|
||||
cloudlog.exception(f"capabilities: unknown hyundai platform {bundle_platform!r}")
|
||||
_resolve_brand_capabilities(caps, bundle_platform, CP)
|
||||
|
||||
return caps
|
||||
|
||||
|
||||
@@ -1593,47 +1593,47 @@
|
||||
"label": "Always On"
|
||||
},
|
||||
{
|
||||
"value": 1,
|
||||
"value": 5,
|
||||
"label": "5m"
|
||||
},
|
||||
{
|
||||
"value": 2,
|
||||
"value": 10,
|
||||
"label": "10m"
|
||||
},
|
||||
{
|
||||
"value": 3,
|
||||
"value": 15,
|
||||
"label": "15m"
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"value": 30,
|
||||
"label": "30m"
|
||||
},
|
||||
{
|
||||
"value": 5,
|
||||
"value": 60,
|
||||
"label": "1h"
|
||||
},
|
||||
{
|
||||
"value": 6,
|
||||
"value": 120,
|
||||
"label": "2h"
|
||||
},
|
||||
{
|
||||
"value": 7,
|
||||
"value": 180,
|
||||
"label": "3h"
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"value": 300,
|
||||
"label": "5h"
|
||||
},
|
||||
{
|
||||
"value": 9,
|
||||
"value": 600,
|
||||
"label": "10h"
|
||||
},
|
||||
{
|
||||
"value": 10,
|
||||
"value": 1440,
|
||||
"label": "24h"
|
||||
},
|
||||
{
|
||||
"value": 11,
|
||||
"value": 1800,
|
||||
"label": "30h (Default)"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -37,27 +37,27 @@ sections:
|
||||
options:
|
||||
- value: 0
|
||||
label: Always On
|
||||
- value: 1
|
||||
label: 5m
|
||||
- value: 2
|
||||
label: 10m
|
||||
- value: 3
|
||||
label: 15m
|
||||
- value: 4
|
||||
label: 30m
|
||||
- value: 5
|
||||
label: 1h
|
||||
- value: 6
|
||||
label: 2h
|
||||
- value: 7
|
||||
label: 3h
|
||||
- value: 8
|
||||
label: 5h
|
||||
- value: 9
|
||||
label: 10h
|
||||
label: 5m
|
||||
- value: 10
|
||||
label: 10m
|
||||
- value: 15
|
||||
label: 15m
|
||||
- value: 30
|
||||
label: 30m
|
||||
- value: 60
|
||||
label: 1h
|
||||
- value: 120
|
||||
label: 2h
|
||||
- value: 180
|
||||
label: 3h
|
||||
- value: 300
|
||||
label: 5h
|
||||
- value: 600
|
||||
label: 10h
|
||||
- value: 1440
|
||||
label: 24h
|
||||
- value: 11
|
||||
- value: 1800
|
||||
label: 30h (Default)
|
||||
- id: language
|
||||
title: Language
|
||||
|
||||
Reference in New Issue
Block a user