chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환 - Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용 - pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관) - worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md) - D:\Rhino-MCP 경로 참조를 새 위치로 치환 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
964a841de1
3978 files changed
+58013356
No files matched your search
@@ -0,0 +1,77 @@
|
||||
"""Read-only saved-model audit of shared body-tie placement and preservation."""
|
||||
import argparse
|
||||
from collections import Counter
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
import sys
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(root/'src/backend' if (root/'src/backend').is_dir() else root))
|
||||
from quantity_review_validation import bend_paths
|
||||
from tie_end_spacing import inspect_visible_ends
|
||||
|
||||
|
||||
def audit(job_path, baseline_path):
|
||||
job=json.loads(Path(job_path).read_text());before=json.loads(Path(baseline_path).read_text())
|
||||
plan=job['result'];old=before['result'];reports=[];changed=set()
|
||||
for group in plan['rebar_groups']:
|
||||
distribution=group.get('quantity_distribution',{})
|
||||
rhythm=distribution.get('rhythm',{})
|
||||
if 'end_balance_required' not in rhythm:continue
|
||||
changed.add(group['id'])
|
||||
records=distribution['records'];paths=group['instance_paths_m']
|
||||
ends=inspect_visible_ends(plan,group,[b['path'] for b in bend_paths(group)])
|
||||
if not ends['pass'] and rhythm.get('end_spacing_priority') == 'equal_ends':
|
||||
raise ValueError('visible concrete-to-tie end spacing differs: '+json.dumps(ends))
|
||||
counts=Counter(r['lane'] for r in records)
|
||||
repeat_errors=[]
|
||||
for lane in counts:
|
||||
xs=sorted(r['x_m'] for r in records if r['lane']==lane)
|
||||
repeat_errors.extend(abs(b-a-rhythm['repeat_m']) for a,b in zip(xs,xs[1:]))
|
||||
phase_errors=[]
|
||||
for side in rhythm['side_lanes']:
|
||||
events=sorted((r['x_m'],r['lane']) for r in records if r['lane'] in side)
|
||||
for (x,lane),(y,next_lane) in zip(events,events[1:]):
|
||||
if lane==next_lane:raise ValueError('staggered lane repeat')
|
||||
phase_errors.append(abs(y-x-rhythm['phase_m']))
|
||||
lengths=[sum(math.dist(a,b) for a,b in zip(path,path[1:])) for path in paths]
|
||||
detail=group['detail_shape_binding']['fabrication']['points_m']
|
||||
expected=[math.dist(a,b) for a,b in zip(detail,detail[1:])]
|
||||
for path in paths:
|
||||
measured=[math.dist(a,b) for a,b in zip(path,path[1:])]
|
||||
if len(measured)!=len(expected) or max(abs(a-b) for a,b in zip(measured,expected))>.001:
|
||||
raise ValueError('fabrication segments changed')
|
||||
if len(paths)!=group['schedule_count']:raise ValueError('drawing count changed')
|
||||
if max(repeat_errors+phase_errors+[0])>1e-6:raise ValueError('fixed rhythm changed')
|
||||
if max(abs(v-group['unit_length_m']) for v in lengths)>.001:raise ValueError('fabrication length changed')
|
||||
if not group['placement_geometry_check'].get('pass'):raise ValueError('geometry gate missing')
|
||||
if not all(r['contact']['pass'] for r in records):raise ValueError('raw contact failed')
|
||||
if not all(r['pass'] for r in distribution['rounded_contact']):raise ValueError('rounded contact failed')
|
||||
a,b=rhythm['side_lengths_m'];n,m=rhythm['side_counts']
|
||||
if abs(a-b)>1e-5 and (a-b)*(n-m)<=0:raise ValueError('long side not more populated')
|
||||
reports.append({'block':group['rebar_block'],'mark':group['mark'],'count':len(paths),
|
||||
'side_counts':rhythm['side_counts'],'side_lengths_m':rhythm['side_lengths_m'],
|
||||
'repeat_m':rhythm['repeat_m'],'phase_m':rhythm['phase_m'],
|
||||
'end_balance_required':rhythm['end_balance_required'],'end_margins_m':rhythm['end_margins_m'],
|
||||
'visible_end_check':ends,
|
||||
'skew_contact_bars':sum('skew' in r.get('contact_domains',[]) for r in records),
|
||||
'max_unit_length_error_m':max(abs(v-group['unit_length_m']) for v in lengths),
|
||||
'geometry_and_contacts_pass':True})
|
||||
def unchanged(groups):
|
||||
return Counter(json.dumps({k:v for k,v in g.items() if k!='rebar_assembly_label'},sort_keys=True)
|
||||
for g in groups if g.get('id') not in changed)
|
||||
if plan['concrete_components']!=old['concrete_components']:raise ValueError('concrete changed')
|
||||
if unchanged(plan['rebar_groups'])!=unchanged(old['rebar_groups']):raise ValueError('unrelated rebar changed')
|
||||
return {'job':Path(job_path).stem,'baseline':str(baseline_path),'geometryRevision':job.get('geometryRevision'),
|
||||
'sha256':hashlib.sha256(Path(job_path).read_bytes()).hexdigest(),'groups':reports,
|
||||
'unrelated_rebar_and_concrete_preserved':True,'engineering_approval':False}
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('job');parser.add_argument('baseline');parser.add_argument('--output',required=True)
|
||||
args=parser.parse_args();report=audit(args.job,args.baseline)
|
||||
with open(args.output,'x') as stream:json.dump(report,stream,ensure_ascii=False,indent=2)
|
||||
print(json.dumps({'groups':len(report['groups']),'bars':sum(g['count'] for g in report['groups']),
|
||||
'unrelated_preserved':True},ensure_ascii=False))
|
||||
Reference in new issue
Block a user