mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 00:53:45 +08:00
EV6 mismatch
This commit is contained in:
@@ -20,6 +20,8 @@ from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.ray_pedal import create_ray_pedal_command, ray_pedal_gas, ray_pedal_enabled
|
||||
from opendbc.car.hyundai.ioniq6_longitudinal import Ioniq6LongitudinalPolicy
|
||||
from opendbc.car.hyundai.canfd_dash_icons import CanFDDashIcons
|
||||
from opendbc.car.hyundai.canfd_stock_aol import qualified as qualified_canfd_stock_aol
|
||||
from opendbc.car.hyundai.ev6_aol import qualified as qualified_ev6_aol
|
||||
from opendbc.car.hyundai.g90_longitudinal import G90LongitudinalPolicy
|
||||
from opendbc.car.hyundai.gv70_longitudinal import is_gv70, scc_request, suppress_stock_cancel
|
||||
from opendbc.car.hyundai.g90_lead import G90LeadState, eligible as g90_lead_eligible
|
||||
@@ -135,7 +137,8 @@ class CarController(CarControllerBase):
|
||||
ioniq6_status = (self.ioniq6_longitudinal is not None and
|
||||
int(CP.safetyConfigs[-1].safetyParam) in (0x8015, 0x8095, 0x8815, 0x8895))
|
||||
self.ioniq6_bsm_enabled = ioniq6_status
|
||||
self.canfd_dash_icons = CanFDDashIcons() if ioniq6_status or self.torque_ev_scc_enabled else None
|
||||
self.ev6_aol_status = CP.carFingerprint == CAR.KIA_EV6 and (qualified_canfd_stock_aol(CP) or qualified_ev6_aol(CP))
|
||||
self.canfd_dash_icons = CanFDDashIcons() if ioniq6_status or self.torque_ev_scc_enabled or self.ev6_aol_status else None
|
||||
self.ioniq6_bsm_counter = 0
|
||||
self.ioniq6_bsm_last_can_ns = 0
|
||||
self._ray_pedal = ray_pedal_enabled(CP)
|
||||
@@ -456,7 +459,8 @@ class CarController(CarControllerBase):
|
||||
)
|
||||
else:
|
||||
steering_status_active = CC.enabled
|
||||
if self.ioniq6_longitudinal is not None and self.CP.safetyConfigs[-1].safetyParam in (0x8815, 0x8895):
|
||||
if self.ev6_aol_status or (self.ioniq6_longitudinal is not None and
|
||||
self.CP.safetyConfigs[-1].safetyParam in (0x8815, 0x8895)):
|
||||
# AOL may steer while ordinary cruise engagement remains off.
|
||||
steering_status_active |= CC.latActive
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN,
|
||||
|
||||
@@ -1326,21 +1326,32 @@ class Car:
|
||||
self.CP.carFingerprint == GM_CAR.CHEVROLET_VOLT_ASCM and self.CP.pcmCruise and
|
||||
not self.CP.openpilotLongitudinalControl and gm_lane_centering_supported(self.CP)))
|
||||
|
||||
def ev6_startup_keepalive(self):
|
||||
from opendbc.car.hyundai.ev6_startup import EV6Startup, required
|
||||
from opendbc.car.hyundai.ecu_startup import Outcome
|
||||
owner = getattr(getattr(self, 'vehicle_startup', None), 'owner', None)
|
||||
return (isinstance(owner, EV6Startup) and owner.outcome is Outcome.SENT_UNCONFIRMED and
|
||||
owner.ready and owner.published and owner.ci is self.CI and not owner.closed and
|
||||
required(self.CP))
|
||||
|
||||
def controls_update(self, CS: car.CarState, CC: car.CarControl, *, initialize_only=False):
|
||||
"""control update loop, driven by carControl"""
|
||||
|
||||
if initialize_only:
|
||||
ev6_startup = self.ev6_startup_keepalive()
|
||||
now_ns = time.monotonic_ns()
|
||||
source_ns = int(self.sm.logMonoTime['carControl'])
|
||||
receipt_ns = int(self.sm.recv_time['carControl'] * 1e9)
|
||||
# Disabled torque/accel are commands; inactive curvature is only telemetry.
|
||||
if (not self.aol_replay or not native_bootstrap_supported(self.CP) or self.vehicle_startup.owner is not None or
|
||||
if (not (ev6_startup or (self.aol_replay and native_bootstrap_supported(self.CP) and self.vehicle_startup.owner is None)) or
|
||||
not CS.canValid or CS.canTimeout or
|
||||
not (self.sm.seen['carControl'] and self.sm.valid['carControl'] and self.sm.alive['carControl']) or
|
||||
not (0 < source_ns <= now_ns and now_ns - source_ns <= 150_000_000 and
|
||||
0 < receipt_ns <= now_ns and now_ns - receipt_ns <= 150_000_000) or
|
||||
CC.enabled or CC.latActive or CC.longActive or CC.actuators.torque != 0.0 or CC.actuators.accel != 0.0):
|
||||
return
|
||||
if ev6_startup and not self.vehicle_startup.owner.prepared_for(self.CP):
|
||||
return
|
||||
|
||||
if getattr(self, 'volt_cc_selected', False):
|
||||
if not self.volt_cc_control_current():
|
||||
@@ -1407,11 +1418,16 @@ class Car:
|
||||
self.ci_initialized = True
|
||||
|
||||
if initialize_only:
|
||||
if (isinstance(self.CI, GMInterface) and self.volt_startup_keepalive() and
|
||||
if ev6_startup and any(ps.faults for ps in self.sm['pandaStates']):
|
||||
return
|
||||
if (((isinstance(self.CI, GMInterface) and self.volt_startup_keepalive()) or ev6_startup) and
|
||||
self.startup_panda_configured(inactive_keepalive=True)):
|
||||
inactive = car.CarControl.new_message().as_reader()
|
||||
self.last_actuators_output, can_sends = self.CI.apply(
|
||||
inactive, time.clock_gettime_ns(time.CLOCK_BOOTTIME), startup_keepalive=True)
|
||||
now_boot_ns = time.clock_gettime_ns(time.CLOCK_BOOTTIME)
|
||||
if ev6_startup:
|
||||
self.last_actuators_output, can_sends = self.CI.apply(inactive, now_boot_ns)
|
||||
else:
|
||||
self.last_actuators_output, can_sends = self.CI.apply(inactive, now_boot_ns, startup_keepalive=True)
|
||||
self.publish_sendcan(can_sends, valid=CS.canValid)
|
||||
return
|
||||
|
||||
@@ -1484,9 +1500,9 @@ class Car:
|
||||
self.sm.seen['onroadEvents'])
|
||||
if not self.CP.passive and initialized:
|
||||
self.controls_update(CS, self.sm['carControl'])
|
||||
elif ((not self.ci_initialized or self.volt_startup_keepalive()) and self.sm.seen['onroadEvents'] and
|
||||
elif ((not self.ci_initialized or self.volt_startup_keepalive() or self.ev6_startup_keepalive()) and self.sm.seen['onroadEvents'] and
|
||||
any(e.name == EventName.selfdriveInitializing for e in self.sm['onroadEvents']) and
|
||||
self.aol_replay and native_bootstrap_supported(self.CP)):
|
||||
((self.aol_replay and native_bootstrap_supported(self.CP)) or self.ev6_startup_keepalive())):
|
||||
self.controls_update(CS, self.sm['carControl'], initialize_only=True)
|
||||
|
||||
radar_can = self._deferred_radar_can
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
"""EV6 stock cruise retains separate steering intent and truthful dash status."""
|
||||
import os
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.hyundai.interface import CarInterface
|
||||
from opendbc.car.hyundai.values import CAR, DBC
|
||||
from opendbc.can import CANPacker, CANParser
|
||||
from openpilot.cereal import messaging
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.prefix import OpenpilotPrefix
|
||||
from openpilot.selfdrive.car import card as card_module
|
||||
from openpilot.starpilot.aol.intent import AOL_TOGGLE
|
||||
from openpilot.starpilot.aol.tests import test_canfd_stock_profiles as stock
|
||||
from openpilot.starpilot.tests import test_ev6_startup as startup
|
||||
|
||||
|
||||
def packets(ci, tick, *, main=0, lkas=0):
|
||||
result = []
|
||||
for parser in ci.can_parsers.values():
|
||||
packer = CANPacker(parser.dbc.name)
|
||||
for address in sorted(parser.addresses):
|
||||
name = parser.dbc.addr_to_msg[address].name
|
||||
values = {} if name in ('FR_CMR_02_100ms', 'DOORS_SEATBELTS', 'BLINKERS') else {'COUNTER': tick % 16}
|
||||
if name == ci.CS.cruise_btns_msg_canfd:
|
||||
values.update(ADAPTIVE_CRUISE_MAIN_BTN=main, LDA_BTN=lkas)
|
||||
elif name == 'SCC_CONTROL':
|
||||
values.update(MainMode_ACC=1, ACCMode=0)
|
||||
elif name == ci.CS.gear_msg_canfd:
|
||||
values['GEAR'] = 5
|
||||
elif name == 'DOORS_SEATBELTS':
|
||||
values['DRIVER_SEATBELT'] = 1
|
||||
result.append(packer.make_can_msg(name, parser.bus, values))
|
||||
return result
|
||||
|
||||
|
||||
@pytest.mark.parametrize('main_action', [0, AOL_TOGGLE])
|
||||
def test_actual_card_stock_lkas_then_main_respects_saved_binding(main_action):
|
||||
with OpenpilotPrefix(), patch.dict(os.environ, {'SIMULATION': '1', 'AOL_REPLAY_RUNTIME': '0'}):
|
||||
saved = Params()
|
||||
for key in ('AlwaysOnLateral', 'OpenpilotEnabledToggle'):
|
||||
saved.put_bool(key, True, block=True)
|
||||
saved.put('MainCruiseButtonControl', main_action, block=True)
|
||||
saved.put('LKASButtonControl', AOL_TOGGLE, block=True)
|
||||
cp = stock.params(CAR.KIA_EV6)
|
||||
|
||||
def factory(*args, pre_create_hook, **kwargs):
|
||||
selected = pre_create_hook(cp.as_reader().as_builder(), cp.carFingerprint, {}, [])
|
||||
return CarInterface(selected)
|
||||
|
||||
with patch.object(card_module.messaging, 'recv_one_retry', return_value=SimpleNamespace(can=[1])), \
|
||||
patch.object(card_module, 'get_car', side_effect=factory):
|
||||
card = card_module.Car()
|
||||
card.ci_initialized = True
|
||||
card.publish_sendcan = lambda *args, **kwargs: None
|
||||
card.CI.update([]) # Register lazy subscriptions before supplying the warmup batch.
|
||||
try:
|
||||
for tick, (main, lkas) in enumerate([(0, 0)] * 20 + [(0, 1), (0, 0), (1, 0), (0, 0)]):
|
||||
now = 10_000_000_000 + tick * 10_000_000
|
||||
batch = messaging.log_from_bytes(card_module.can_list_to_can_capnp(packets(card.CI, tick, main=main, lkas=lkas))).as_builder()
|
||||
batch.logMonoTime = now
|
||||
with patch.object(card_module, 'REPLAY', False), \
|
||||
patch.object(card_module.messaging, 'drain_sock_raw', return_value=[batch.to_bytes()]), \
|
||||
patch.object(card_module.time, 'monotonic_ns', return_value=now), \
|
||||
patch.object(card_module.time, 'monotonic', return_value=now / 1e9), patch.object(card.sm, 'update'):
|
||||
card.step()
|
||||
assert card.CS_prev.canValid and not card.CS_prev.canTimeout
|
||||
assert card.CS_prev.cruiseState.available
|
||||
if tick == 21:
|
||||
assert card.aol_card_intent.allowed_latch
|
||||
assert card.aol_card_intent.allowed_latch == (main_action == 0)
|
||||
assert not card.last_actuators_output.torqueOutputCan # No native permission was provided.
|
||||
finally:
|
||||
card.vehicle_startup.close()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('alpha', [False, True])
|
||||
def test_actual_controller_dash_follows_aol_without_changing_actuation(alpha):
|
||||
if alpha:
|
||||
ci, _, _ = startup.TestEV6Startup().exercise(mode='sent_restore')
|
||||
ci.CP.alternativeExperience = 32
|
||||
else:
|
||||
ci = CarInterface(stock.params(CAR.KIA_EV6))
|
||||
ci.CP.safetyConfigs[-1].safetyParam |= 0x800
|
||||
parser = CANParser(DBC[CAR.KIA_EV6][Bus.pt], [('LKAS', 0)], ci.CC.CAN.ACAN)
|
||||
for tick in range(120):
|
||||
now = 10_000_000_000 + tick * 10_000_000
|
||||
ci.update([(now, packets(ci, tick))])
|
||||
command = structs.CarControl(enabled=40 <= tick < 80, latActive=20 <= tick < 60)
|
||||
_, frames = ci.apply(command.as_reader(), now)
|
||||
steering = [frame for frame in frames if frame[0] == 0x50 and frame[2] == ci.CC.CAN.ACAN]
|
||||
assert len(steering) == 1
|
||||
parser.update([(now, steering)])
|
||||
values = parser.vl['LKAS']
|
||||
assert values['LKA_SysIndReq'] == (2 if command.enabled or command.latActive else 1)
|
||||
assert values['ActToiSta'] == command.latActive
|
||||
assert values['StrTqReqVal'] == 0
|
||||
cluster = [frame for frame in frames if frame[0] == 0x1E0]
|
||||
assert len(cluster) == int(alpha and tick % 5 == 0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('variation', ['normal', 'aol_off', 'active', 'stale', 'native_mismatch', 'native_fault', 'invalid_can', 'changed_cp'])
|
||||
def test_actual_card_replaces_disabled_adrv_during_selfdrive_initialization(variation):
|
||||
from opendbc.car.hyundai.radar_interface import RadarInterface
|
||||
from openpilot.starpilot.vehicle_startup import VehicleStartupOwner
|
||||
|
||||
with OpenpilotPrefix(), patch.dict(os.environ, {'SIMULATION': '1', 'AOL_REPLAY_RUNTIME': '0'}):
|
||||
saved = Params()
|
||||
saved.put_bool('OpenpilotEnabledToggle', True, block=True)
|
||||
saved.put_bool('AlwaysOnLateral', variation != 'aol_off', block=True)
|
||||
# The fixture normally seals for its own assertions; Card owns the real seal here.
|
||||
with patch.object(VehicleStartupOwner, 'seal_publication'):
|
||||
ci, holder, _ = startup.TestEV6Startup().exercise(mode='sent_restore')
|
||||
card = card_module.Car(CI=ci, RI=RadarInterface(ci.CP), startup_owner=holder.owner)
|
||||
assert card.ev6_startup_keepalive()
|
||||
holder.owner.published = False
|
||||
assert not card.ev6_startup_keepalive()
|
||||
holder.owner.published = True
|
||||
holder.owner.ci = None
|
||||
assert not card.ev6_startup_keepalive()
|
||||
holder.owner.ci = ci
|
||||
card.CI.update([])
|
||||
emitted = []
|
||||
card.publish_sendcan = lambda frames, valid=True: emitted.extend((card.sm.frame, frame, valid) for frame in frames)
|
||||
try:
|
||||
for tick in range(620):
|
||||
now = 10_000_000_000 + tick * 10_000_000
|
||||
batch = messaging.log_from_bytes(card_module.can_list_to_can_capnp(packets(card.CI, tick))).as_builder()
|
||||
batch.logMonoTime = now
|
||||
control = messaging.new_message('carControl', valid=True, logMonoTime=now - (200_000_000 if variation == 'stale' else 0))
|
||||
if variation == 'active':
|
||||
control.carControl.enabled = control.carControl.latActive = True
|
||||
control.carControl.actuators.torque = .1
|
||||
events = messaging.new_message('onroadEvents', 1, valid=True, logMonoTime=now)
|
||||
events.onroadEvents[0].name = 'selfdriveInitializing'
|
||||
events.onroadEvents[0].noEntry = True
|
||||
pandas = messaging.new_message('pandaStates', 1, valid=True, logMonoTime=now)
|
||||
panda = pandas.pandaStates[0]
|
||||
config = card.CP.safetyConfigs[0]
|
||||
panda.safetyModel = config.safetyModel if tick >= 5 else 'elm327'
|
||||
panda.safetyParam = (config.safetyParam ^ int(variation == 'native_mismatch')) if tick >= 5 else 1
|
||||
panda.alternativeExperience = card.CP.alternativeExperience if tick >= 5 else 0
|
||||
panda.ignitionLine = True
|
||||
if variation == 'changed_cp':
|
||||
card.CP.mass += 1
|
||||
if variation == 'native_fault':
|
||||
panda.faults = ['relayMalfunction']
|
||||
card.sm.update_msgs(now / 1e9, [control.as_reader(), events.as_reader(), pandas.as_reader()])
|
||||
with patch.object(card_module, 'REPLAY', False), \
|
||||
patch.object(card_module.messaging, 'drain_sock_raw', return_value=[batch.to_bytes()]), \
|
||||
patch.object(card_module.time, 'monotonic_ns', return_value=now), \
|
||||
patch.object(card_module.time, 'monotonic', return_value=now / 1e9), \
|
||||
patch.object(card_module.time, 'clock_gettime_ns', return_value=now), patch.object(card.sm, 'update'):
|
||||
if variation == 'invalid_can':
|
||||
with patch.object(card.CI, 'update', wraps=card.CI.update) as parse:
|
||||
def invalid(*args, **kwargs):
|
||||
state = parse._mock_wraps(*args, **kwargs)
|
||||
state.canValid = False
|
||||
return state
|
||||
parse.side_effect = invalid
|
||||
card.step()
|
||||
else:
|
||||
card.step()
|
||||
assert card.CS_prev.canValid == (variation != 'invalid_can') and not card.CS_prev.canTimeout
|
||||
if variation not in ('normal', 'aol_off'):
|
||||
assert not emitted
|
||||
return
|
||||
assert card.ci_initialized and saved.get_bool('ControlsReady')
|
||||
assert emitted and all(valid for _, _, valid in emitted)
|
||||
assert min(frame for frame, _, _ in emitted) <= 6
|
||||
scc = [(frame, msg) for frame, msg, _ in emitted if msg[0] == 0x1A0]
|
||||
testers = [frame for frame, msg, _ in emitted if msg[0] == 0x730]
|
||||
assert len(scc) >= 300 and len(testers) >= 6
|
||||
assert max(b - a for a, b in zip(testers, testers[1:], strict=False)) <= 100
|
||||
parser = CANParser(DBC[CAR.KIA_EV6][Bus.pt], [('SCC_CONTROL', 0)], card.CI.CC.CAN.ECAN)
|
||||
for frame, msg in scc:
|
||||
parser.update([(10_000_000_000 + frame * 10_000_000, [msg])])
|
||||
values = parser.vl['SCC_CONTROL']
|
||||
assert (values['ACCMode'], values['aReqRaw'], values['aReqValue'], values['StopReq']) == (0, 0., 0., 0)
|
||||
assert card.last_actuators_output.torqueOutputCan == 0
|
||||
finally:
|
||||
card.vehicle_startup.close()
|
||||
|
||||
|
||||
def test_stock_held_main_rebinding_and_cancel_keep_physical_edges():
|
||||
from dataclasses import replace
|
||||
from openpilot.starpilot.aol.intent import AolSettings
|
||||
from openpilot.starpilot.car.hyundai.aol import create_intent
|
||||
|
||||
owner = create_intent(stock.params(CAR.KIA_EV6), AolSettings(True, 0., AOL_TOGGLE, 0, (0, 0, 0), (0, 0, 0)))
|
||||
cs = structs.CarState(canValid=True, gearShifter='drive')
|
||||
cs.cruiseState.available = True
|
||||
def update(events, now):
|
||||
cs.buttonEvents = [structs.CarState.ButtonEvent(type=kind, pressed=pressed) for kind, pressed in events]
|
||||
owner.update(cs, fault_active=False, now_ns=now)
|
||||
update([], 1)
|
||||
update([('lkas', True)], 2)
|
||||
update([('lkas', False)], 3)
|
||||
update([('mainCruise', True)], 4)
|
||||
assert owner.allowed_latch
|
||||
owner.settings = replace(owner.settings, main_action=AOL_TOGGLE)
|
||||
update([('mainCruise', True)], 5)
|
||||
assert owner.allowed_latch # A new mapping cannot turn an already held level into another press.
|
||||
update([('mainCruise', False)], 6)
|
||||
update([('mainCruise', True)], 7)
|
||||
assert not owner.allowed_latch
|
||||
owner.settings = replace(owner.settings, main_action=0)
|
||||
update([('mainCruise', False)], 8)
|
||||
update([('lkas', True)], 9)
|
||||
assert owner.allowed_latch
|
||||
update([('cancel', True)], 10)
|
||||
assert not owner.allowed_latch
|
||||
@@ -257,6 +257,9 @@ def native_latch_rejected(CP, native) -> bool:
|
||||
|
||||
def create_intent(CP, settings):
|
||||
from opendbc.car.hyundai.ev6_aol import qualified as ev6_qualified
|
||||
if CP.carFingerprint == HyundaiCAR.KIA_EV6 and qualified_canfd_stock(CP):
|
||||
from openpilot.starpilot.car.hyundai.ev6_intent import StockEv6CardIntent
|
||||
return StockEv6CardIntent(settings)
|
||||
if ev6_qualified(CP):
|
||||
from openpilot.starpilot.car.hyundai.ev6_intent import Ev6CardIntent
|
||||
return Ev6CardIntent(settings)
|
||||
|
||||
@@ -4,6 +4,25 @@ from opendbc.car.hyundai.ev6_aol import Ev6PhysicalAuthorization
|
||||
from openpilot.starpilot.aol.intent import AolCardIntent, AOL_TOGGLE, ButtonType
|
||||
|
||||
|
||||
class StockEv6CardIntent(AolCardIntent):
|
||||
def __init__(self, settings):
|
||||
super().__init__(settings, explicit_latch=True)
|
||||
|
||||
def update(self, CS, *, consumed_buttons=frozenset(), fault_active=None, now_ns=0, native_rejection_ns=0,
|
||||
standard_enabled=False):
|
||||
if self.settings.main_action == 0 and self.settings.lkas_action == AOL_TOGGLE:
|
||||
# An unassigned cruise MAIN owns stock ACC, not the separate LKAS latch.
|
||||
for event in CS.buttonEvents:
|
||||
if event.type == ButtonType.mainCruise:
|
||||
self._main_held = bool(event.pressed)
|
||||
ordinary = CS.as_reader().as_builder()
|
||||
ordinary.buttonEvents = [event for event in CS.buttonEvents if event.type != ButtonType.mainCruise]
|
||||
else:
|
||||
ordinary = CS
|
||||
super().update(ordinary, consumed_buttons=consumed_buttons, fault_active=fault_active, now_ns=now_ns,
|
||||
native_rejection_ns=native_rejection_ns, standard_enabled=standard_enabled)
|
||||
|
||||
|
||||
class Ev6CardIntent(AolCardIntent):
|
||||
observe_stock_engagement = True
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
extern const uint8_t gitversion[29];
|
||||
const uint8_t gitversion[29] = "DEV-9de940e9-P92eb5651-DEBUG";
|
||||
const uint8_t gitversion[29] = "DEV-89430a70-P92eb5651-DEBUG";
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
DEV-9de940e9-P92eb5651-DEBUG
|
||||
DEV-89430a70-P92eb5651-DEBUG
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "6df82300319bbfdea3da54bf32ce2c3a255aecbe4020755fdec9a69cf359674c", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "9f8785c8ab618682316768289a489c024a5d2cbfc62772aedc2c2b96334a16bc", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user