from pathlib import Path
import json,hashlib,zipfile,xml.etree.ElementTree as E
R=Path(__file__).resolve().parent;B=R.parent;H=B/'engineering_handoff_local';T=R/'firmware/固件/lamp_target';sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest();build=json.loads((T/'build/build_verification.json').read_text())
assert build['PCA_fault_monitor_linked'] and not build['PCA_fault_monitor_runtime_called'] and not build['PCA_read_callback_bound'] and not build['led_output_enabled']
assert 'PASS: 24576' in (R/'actual_fault_test.log').read_text()
safety=B/'lamp_IR_PCB_A39/board/netlist.xml';root=E.parse(safety).getroot();pins={(c.get('ref'),c.get('pin')):n.get('name') for n in root.findall('./nets/net') for c in n}
assert pins['J2','3']==pins['U1','1']=='/I2C_SDA' and pins['J2','4']==pins['U1','20']=='/I2C_SCL'
audit={'status':'A39_I2C_INTERFACE_NETLIST_AUDITED_TRANSPORT_AND_BANK_UNBOUND','safety_connector':{'J2.3':'I2C_SDA','J2.4':'I2C_SCL'},'MCU_package_pad':{'U1.1':'PB7/PB8 symbol aliases; active default/AF mapping must be verified for transport','U1.20':'PB3/PB4/PB5/PB6 symbol aliases; active default/AF mapping must be verified for transport'},'target_I2C_initialized':False,'actual_driver_bank_board_current_verified':False,'driver_unicast_straps':None,'test_address_0x20':'mock only; not a selected board address','REXT_and_actual_PWM_IREF_LEDOUT_load_eligibility':'UNQUALIFIED','LED_RESET_N_in_current_target':'held low; never released by A46','legacy_PCA_netlist_attempt':'old work/package-v05 led_driver.xml read timed out; no source recovery or board verification claimed','next_gate':'Localize/select PCA bank exact CAD+straps, verify physical pin/AF mapping and bounded I2C transport before runtime reads'}
(R/'I2C_interface_and_bank_gate.json').write_text(json.dumps(audit,indent=2)+'\n')
checks={'status':'A46_PCA_FAULT_MONITOR_PROTOCOL_LOCAL_PASS_TRANSPORT_UNBOUND_FABRICATION_HOLD','MODE_channel_code_cases':24576,'read_failure_positions_tested':8,'full_fault_snapshot_reads':8,'collection_timeout_ms':20,'poll_gap_limit_ms':5,'decoding':{'0':'no observed error','1':'short','2':'open','3':'DNE invalid'},'runtime_monitor_called':False,'physical_read_callback_bound':False,'coverage_complete_grants_output_permission':False,'outputs_enabled':False,'build':build,'physical_verified_requirements':0,'reference_requirements':20}
(R/'checks.json').write_text(json.dumps(checks,indent=2)+'\n')
(R/'source_references.json').write_text(json.dumps({'NXP_datasheet':'https://www.nxp.com/docs/en/data-sheet/PCA9956B.pdf','PDF_revision':'1.2 10June2020','PDF_sha256':sha(R/'NXP_PCA9956B_Rev1p2.pdf'),'used_pages':[16,25,26],'scope':'PCA9956B only; MODE2/EFLAG fields and detection prerequisites; not PCA9955/IS31 or real register reads'},indent=2)+'\n')
report=f'''# PCA9956B灯具故障回读协议模块 A46

状态：新增模块的寄存器读取顺序/解码/时效检查通过，已编入目标映像；实际I²C传输和驱动板地址/检测资格尚未绑定，运行时未调用。FABRICATION_HOLD，不能把“模块编入”称为已读取实体LED驱动器。三款20项功能效果仍0项实测确认。

## 本轮实际实现

独立`pca9956b_fault_monitor`以单芯片为单位，通过有界单字节读回调每次poll只读一个寄存器，顺序MODE2→EFLAG0..5→MODE2，共8次。回调合同为地址未移位7bit的已确认单播器件、寄存器指针写/重复START/读单字节/NACK/STOP；回调必须在调度预算内结束。这只是传输接口合同，当前没有MCU回调实现，也没有总线ACK/STOP/拉伸/电压波形验证。测试0x20是模拟地址，不能用作实际板配置。

解码MODE2 bit7过温、bit6全局错误和0x41–0x46的24组双位标志：00无已观测错误、01短路、10开路、11不存在/无效编码。保留全24通道信息和全局/通道摘要不一致标志，不静默忽略未请求通道错误。只读状态，不写寄存器数据、不清CLRERR、不修改PWM/IREF/LEDOUT。

前后MODE2变化、单次读失败、轮询间隔超过5ms、集合超过20ms进入失败态并撤销缓存；新一轮读取开始即撤销旧完整结果，避免旧“无错误”数据在新集合读取中继续有效。全8次成功后发布完整结构及最早/完成时间；get失败不改调用者结构。MODE2相同不证明6个EFLAG是原子快照，芯片内部寄存器可在读取期间变化，本报告不作一致硬件状态保证。

## 检测覆盖不能等同健康许可

NXP规定输出关闭或PWM<8不进行开短路检测，REXT较大也可能误报开路。caller必须从实际已核对的LEDOUT/IREF/PWM、电源/输出和REXT条件提供检测资格；全零标志不能产生这个资格。requested/eligible掩码仅记录上游覆盖前提，即使coverage_complete也不授予led_driver_ok或LED许可。当前target没有这些实际资格信息，继续保持所有输出锁定和驱动复位低。

## 当前证据

- 24,576组模拟读取序列：24通道×4种双位编码×256种MODE2值，覆盖错误位/通道位序、摘要/覆盖范围及32bit计时回绕；这是软件解码检查，不是256种实体寄存器状态都有物理意义。
- 8个读取位置分别注入通信失败，测试MODE2首末变化、旧缓存撤销、单次及时但总集合超时、轮询间隔/倒退/过期及空指针/非法地址/掩码输入。
- 故意交换开短路编码的合成负对照被短路掩码断言拒绝；没有宣称实体驱动器故障已测。
- 原有4096温度码、36,855名义诊断数值、72组ADC/诊断/温度模型、IR/启动/guard/票据/UART/UI/输出适配器回归通过，ASan/UBSan通过。
- ARM完整构建及符号核对通过，同时静态检查这些新接口尚未从main或安全采样路径调用。BIN {build['flash_binary_bytes']}B，FLASH17512/32768、RAM2832/8192（静态链接，非运行峰值），BIN SHA256 `{build['sha256_bin']}`。
- A39实际网表J2.3 SDA→U1.1、J2.4 SCL→U1.20已核对，但符号包含复用别名，实际默认引脚/AF和寄存器配置必须进一步核实。旧PCA驱动板网表读取遇到iCloud超时，未把目录库存当本地恢复或真实板银行配置。本轮未修改CAD/BOM。

## 下一步与完整目标

接有界I²C读后端前先落定PCA驱动板本地源、单播地址/REXT和实际引脚复用，避免用模拟地址烧录；随后做单/多芯片状态回读、校准判据与输出闭环。PCA9956B与IS31分支协议不同，不能混用此模块。本轮并未完成驱动板银行装配或读取实际寄存器，也未释放LED复位、踢看门狗或授予输出。

仍缺NTC安装/整板校准、LED光学固化/热/AI链路及电磨/吸尘器完整负载效果，每款1000台完整整机成本和实机验收也待闭合。保持原目标，不以读回协议模块代替整机效果。无采购、外联、烧录、通电或制造下单。

工程包含可编辑完整源码、ELF/BIN/MAP、NXP官方PDF、测试和日志、SHA清单；`sh run_checks.sh`需cc/Python3/arm-none-eabi工具链。包内继承NTC曲线的模型及模板不代表新的实体装配证据。
'''
(R/'README.md').write_text(report);(H/'A46-lamp-PCA-fault-monitor.md').write_text(report)
files=[p for p in R.rglob('*') if p.is_file() and p.name!='package.py' and not p.name.startswith('.') and all(x not in p.parts for x in ['host_test','adc_host_test','measurement_host_test','ntc_host_test','fault_host_test']) and p.suffix in ['.c','.h','.s','.ld','.sh','.py','.md','.json','.log','.txt','.csv','.elf','.bin','.map','.pdf']]
manifest=''.join(f'{sha(p)}  {p.relative_to(R).as_posix()}\n' for p in sorted(files));(H/'A46-SHA256SUMS.txt').write_text(manifest)
z=H/'Lamp-PCA-fault-monitor-protocol-review-A46.zip'
with zipfile.ZipFile(z,'w',zipfile.ZIP_DEFLATED) as a:
 for p in files:a.write(p,p.relative_to(R).as_posix())
 a.writestr('SHA256SUMS.txt',manifest)
with zipfile.ZipFile(z) as a:
 assert a.testzip() is None
 for line in manifest.splitlines():
  digest,name=line.split('  ',1);assert hashlib.sha256(a.read(name)).hexdigest()==digest
 count=len(a.namelist())
ip=H/'LOCAL-PACKAGES.json';idx=json.loads(ip.read_text());idx.update(status=checks['status'],latest_report='A46-lamp-PCA-fault-monitor.md',latest_machine_report='../lamp_fault_monitor_A46/checks.json',latest_manifest='A46-SHA256SUMS.txt')
e={'archive':z.name,'sha256':sha(z),'bytes':z.stat().st_size,'zip_entries':count,'manifest_entries':len(files),'zip_crc':'PASS','manifest_readback':'PASS','manufacturing_status':'FABRICATION_HOLD'}
idx['packages']=[p for p in idx['packages'] if p.get('archive')!=z.name]+[e]
idx['latest_lamp_safe_firmware_A46']={'source':'../lamp_fault_monitor_A46/firmware/固件/lamp_target','binary_sha256':build['sha256_bin'],'PCA_fault_monitor_linked':True,'PCA_runtime_read_bound':False,'outputs_enabled':False,'physical_test_release':False}
idx['latest_lamp_power_frontend_A43']['firmware_pair']='A46 same active A45 diagnostics, new PCA monitor linked but transport/runtime unbound'
idx['latest_lamp_IR_hardware_A39']['bound_firmware_candidate']='../lamp_fault_monitor_A46/firmware/固件/lamp_target'
idx['latest_lamp_IR_policy_A35']['linked_candidate']='../lamp_fault_monitor_A46/firmware/固件/lamp_target'
idx['lamp_clean_build']={'report':'../lamp_fault_monitor_A46/firmware/固件/lamp_target/build/build_verification.json','binary_sha256':build['sha256_bin'],'physical_validation':'NOT_RUN_NO_SAMPLE','led_output_enabled':False,'scope':'A46 PCA hard-off image; monitor compiled only, physical I2C reads and driver health absent'}
idx['manufacturing_exclusions']['lamp_A46']='PCA monitor callback protocol/decoder tested and linked only; actual I2C transport/bank source+straps/coverage+health unbound; no actual read, output or physical validation. FABRICATION_HOLD.'
ip.write_text(json.dumps(idx,ensure_ascii=False,indent=2)+'\n');print(json.dumps(e,indent=2))
