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,131 @@
|
||||
"""Append original-CAD face-layout review groups; never approve fabrication."""
|
||||
import argparse,copy,hashlib,json,os,sys,tempfile,time
|
||||
from pathlib import Path
|
||||
sys.path.insert(0,str(Path(__file__).resolve().parent.parent))
|
||||
import server_8097
|
||||
from adaptive_block1_rebar import build_current_drawing_nonwing_groups
|
||||
from fresh_rebar_8097 import _compact_nonwing_rebar_taxonomy
|
||||
from tools.check_common_rebar_replay import compare_groups
|
||||
|
||||
|
||||
def unsafe_pair_issues(pair):
|
||||
return [issue for issue in pair.get('issues',[]) if any(term in issue for term in
|
||||
('collapse','duplicate physical path','same source symbol','both slab face rows are not installed'))]
|
||||
|
||||
|
||||
def safe_new_faces(original,compiled,groups):
|
||||
"""Keep unresolved hosts unchanged; never waive their failed checks."""
|
||||
old_ids={g.get('id')for g in original.get('rebar_groups',[])}
|
||||
blocked={p['host_id']:p['issues']for p in compiled.get('slabPairAudit',{}).get('objects',[])
|
||||
if unsafe_pair_issues(p)}
|
||||
selected=[];held=[]
|
||||
for group in groups:
|
||||
if group.get('id')in old_ids:continue
|
||||
if not group.get('standard_section_placement',{}).get('row_binding',{}).get('source_auto_bound'):continue
|
||||
bad={host:blocked[host]for host in group.get('host_component_ids',[])if host in blocked}
|
||||
if bad:held.append({'group_id':group['id'],'failed_hosts':bad})
|
||||
else:selected.append(group)
|
||||
return selected,held
|
||||
|
||||
|
||||
def append_faces(original,compiled,groups):
|
||||
if compiled['concrete_components']!=original['concrete_components']:
|
||||
raise ValueError('locked concrete changed')
|
||||
result=copy.deepcopy(original)
|
||||
old_ids={g.get('id')for g in original.get('rebar_groups',[])}
|
||||
new=[copy.deepcopy(g)for g in groups if g.get('id')not in old_ids and
|
||||
g.get('standard_section_placement',{}).get('row_binding',{}).get('source_auto_bound')]
|
||||
if len({g.get('id')for g in new})!=len(new):raise ValueError('duplicate group identities')
|
||||
new_hosts={host for g in new for host in g.get('host_component_ids',[])}
|
||||
for pair in compiled.get('slabPairAudit',{}).get('objects',[]):
|
||||
if pair['host_id']in new_hosts and unsafe_pair_issues(pair):
|
||||
raise ValueError('slab face collision/height ordering failed; original model preserved')
|
||||
for g in new:
|
||||
check=g.get('placement_geometry_check',{})
|
||||
if (not check.get('pass') or check.get('tube_outside_samples')!=0 or
|
||||
g.get('display_authorized') or g['fabrication_measurement']['fixed_length_pass']):
|
||||
raise ValueError('requires source layout and containment; not fabricated-piece approval')
|
||||
_compact_nonwing_rebar_taxonomy(result,new)
|
||||
result['rebar_groups']=[*result.get('rebar_groups',[]),*new]
|
||||
from box_member_schema import member_model
|
||||
result['memberModel']=member_model(result)
|
||||
evidence=result.setdefault('drawingEvidence',{})
|
||||
# Candidate layout coordinates of held/unchanged rows must not replace
|
||||
# the source metadata belonging to their saved geometry.
|
||||
layouts=evidence.setdefault('standard_section_layouts',{})
|
||||
for key,layout in compiled['drawingEvidence'].get('standard_section_layouts',{}).items():
|
||||
block=key.removeprefix('BLOCK')
|
||||
accepted={mark:row for mark,row in layout.get('rows',{}).items()
|
||||
if any(g['id']==f'adaptive-block1-b{block}-{mark}'for g in new)}
|
||||
if not accepted:continue
|
||||
current=layouts.setdefault(key,{**copy.deepcopy(layout),'rows':{}})
|
||||
current['rows'].update(copy.deepcopy(accepted))
|
||||
evidence['cad_fabrication_inventory']=copy.deepcopy(compiled['drawingEvidence']['cad_fabrication_inventory'])
|
||||
preview=evidence.get('preview_geometry')
|
||||
if isinstance(preview,dict):preview['rebar_groups']=copy.deepcopy(result['rebar_groups'])
|
||||
report={'added_groups':[{'id':g['id'],'paths':len(g['instance_paths_m'])}for g in new],
|
||||
'concrete_unchanged':True,'previous_rebar_unchanged':True,'approved':False,
|
||||
'pending':'fabrication cut-piece/lap positions, full cover and remaining members',
|
||||
'external_calls':0,'source_candidate_audit':compiled['commonRebarCandidateAudit']}
|
||||
result['localFaceFeedback']=report
|
||||
if compiled.get('slabPairAudit'):
|
||||
# The candidate may contain held rows. Audit exactly the merged model,
|
||||
# never attach a candidate's complete-pair result to a partial display.
|
||||
from adaptive_block1_rebar import _frames
|
||||
from slab_pair_contract import audit_slab_pairs
|
||||
result['slabPairAudit']=audit_slab_pairs({f['block']:f for f in _frames(result)},result['rebar_groups'])
|
||||
report['slab_pair_pass']=result['slabPairAudit']['pass']
|
||||
for pair in result['slabPairAudit']['objects']:
|
||||
if pair['host_id']in new_hosts and unsafe_pair_issues(pair):
|
||||
raise ValueError('merged display lacks safe paired slab faces; original model preserved')
|
||||
result.setdefault('unresolved',[]).insert(0,
|
||||
'슬래브·벽체 도면 설치선 검토용 표시: 이음부 제작 분할·전체 피복 및 나머지 부재 배근은 미완료입니다. 확정본 아님.')
|
||||
return result,report
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('job_id');parser.add_argument('--apply',action='store_true')
|
||||
parser.add_argument('--safe-new-faces',action='store_true',
|
||||
help='append only new source-bound review rows without failed host collision checks')
|
||||
args=parser.parse_args()
|
||||
if len(args.job_id)!=24 or any(c not in '0123456789abcdef'for c in args.job_id):raise ValueError('invalid job')
|
||||
server=server_8097.server;server.require_modeling_rules()
|
||||
path=server.JOB_ROOT/(args.job_id+'.json');raw=path.read_bytes();job=json.loads(raw)
|
||||
if job.get('status')=='running':raise ValueError('active worker')
|
||||
from box_member_schema import migrate_payload_types
|
||||
migrate_payload_types(job)
|
||||
original=job['result'];compiled={k:copy.deepcopy(original[k])for k in
|
||||
('concrete_components','drawingEvidence','global_parameters','category_id')if k in original}
|
||||
(compiled.get('drawingEvidence',{}).get('preview_geometry')or{}).pop('rebar_groups',None)
|
||||
groups=build_current_drawing_nonwing_groups(compiled,job['sourceContext']['analysis'],collect_errors=True)
|
||||
old_ids={g.get('id')for g in original.get('rebar_groups',[])}
|
||||
replay=compare_groups(original['rebar_groups'],[g for g in groups if g.get('id')in old_ids])
|
||||
if not args.safe_new_faces and any(not r['paths_equal_to_micrometer'] or not r['diameter_equal']for r in replay):
|
||||
raise ValueError('existing rebar replay regressed; original model preserved')
|
||||
held=[]
|
||||
if args.safe_new_faces:
|
||||
groups,held=safe_new_faces(original,compiled,groups)
|
||||
result,report=append_faces(original,compiled,groups)
|
||||
report['held_new_groups']=held
|
||||
if result['rebar_groups'][:len(original['rebar_groups'])]!=original['rebar_groups']:
|
||||
raise ValueError('append changed saved rebar')
|
||||
print(json.dumps({k:v for k,v in report.items()if k!='source_candidate_audit'},ensure_ascii=False),flush=True)
|
||||
if not args.apply or not report['added_groups']:return
|
||||
from cad_source_access import source_manifest
|
||||
for s in source_manifest(job['sourceContext']['analysis'],server.UPLOAD_ROOT)['sources']:
|
||||
if hashlib.sha256(Path(s['path']).read_bytes()).hexdigest()!=s['sha256']:raise ValueError('CAD changed')
|
||||
job.update(result=result,status='awaiting_feedback',verified=False,geometryRevision=time.time_ns(),
|
||||
workflowStage='rebar',stage='도면 기반 슬래브·벽체 설치선 표시 · 난간벽 등 나머지 배근 미완료')
|
||||
job.setdefault('partialResult',{})['drawingEvidence']=copy.deepcopy(result['drawingEvidence'])
|
||||
if path.read_bytes()!=raw:raise ValueError('job changed during local compilation')
|
||||
backup=path.with_name(path.stem+f'.before-local-face-feedback-{time.time_ns()}.json')
|
||||
with backup.open('xb')as stream:stream.write(raw)
|
||||
with tempfile.NamedTemporaryFile(mode='w',dir=path.parent,delete=False)as stream:
|
||||
json.dump(job,stream,ensure_ascii=False,allow_nan=False);temporary=stream.name
|
||||
if path.read_bytes()!=raw:raise ValueError('job changed before append')
|
||||
os.replace(temporary,path)
|
||||
print(json.dumps({'published_feedback':True,'backup':str(backup),'approved':False}))
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
Reference in new issue
Block a user