Files
Pier_v1/RAG/design-model-box-sample_2/tools/distribute_body_tie_quantities.py
T
ahnyongjooandClaude Sonnet 5.5 964a841de1 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>
2026-10-28 11:20:18 +09:00

80 lines
6.2 KiB
Python

"""Publish explicitly authorized drawing-N distributions after rigid checks."""
import argparse,copy,json,math,os,subprocess,sys,tempfile,time
from pathlib import Path
root=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(root/'src/backend' if (root/'src/backend').is_dir() else root))
def run(job_id,node,apply=False):
import server
from cad_rebar_recipe import load_sources
from cad_sheet_regions import source_regions
from local_fabrication_shape import build_fabrication_local
from quantity_body_ties import distribute,target_segments
from body_tie_engagement import actual_bar_contacts
from body_tie_geometry import source_body_tie_domain
server.require_modeling_rules()
if len(job_id)!=24 or any(c not in '0123456789abcdef' for c in job_id):raise ValueError('invalid job')
path=Path('/tmp/drawing-model-studio-8097-jobs')/(job_id+'.json');raw=path.read_bytes();job=json.loads(raw);plan=job['result']
if job.get('status')=='running':raise ValueError('active job')
sources=load_sources(job['sourceContext']['analysis']);inventory=plan['drawingEvidence']['cad_fabrication_inventory']['rows']
kernel=root/'model_kernel.js'
if not kernel.is_file():kernel=root/'src/frontend/src/model_kernel.js'
replacements={};reports=[]
for old in plan['rebar_groups']:
if not old.get('source_body_tie_binding') or old['fabrication_measurement'].get('count_pass'):continue
report={'id':old['id'],'before':len(old['instance_paths_m']),'applied':False};reports.append(report)
try:
block=int(old['rebar_block'].replace('BLOCK',''))
rows=[r for r in inventory if r.get('blocks')==[block] and r['material']['mark']==old['mark'] and r.get('fabrication_local')]
if len(rows)!=1:raise ValueError('own source fabrication row not unique')
row=rows[0];source=sources[row['source_sha256']]
if row.get('source_region_id'):
source=next(r for r in source_regions(source) if r.region_id==row['source_region_id'])
detail=build_fabrication_local(source,row);expected=[math.dist(a,b) for a,b in zip(detail['points_m'],detail['points_m'][1:])]
count=row['material']['count'];report['drawing']=count
new=distribute(plan,old,count,authorized=True)
for p in new['instance_paths_m']:
lengths=[math.dist(a,b)for a,b in zip(p,p[1:])]
if len(lengths)!=len(expected) or min(max(abs(a-b)for a,b in zip(lengths,order))for order in (expected,expected[::-1]))>.001:
raise ValueError('fabrication leg length changed')
script="const fs=require('fs');require(process.argv[1]);const g=JSON.parse(fs.readFileSync(0,'utf8'));process.stdout.write(JSON.stringify(g.instance_paths_m.map((p,i)=>ModelKernel.bendPath(p,g,i))));"
bends=json.loads(subprocess.run([node,'-e',script,str(kernel)],input=json.dumps(new),capture_output=True,text=True,check=True).stdout)
domain=source_body_tie_domain(plan,old['host_component_ids'][0])
outside,first=server.tube_path_outside_component_union(new['instance_paths_m'],new['diameter_mm'],domain,sample_step_m=.025)
rounded_outside,rounded_first=server.tube_path_outside_component_union([b['path']for b in bends],new['diameter_mm'],domain,sample_step_m=.025)
segments=target_segments(plan,new);rounded_contact=[]
for b,record in zip(bends,new['quantity_distribution']['records']):
witnesses=record['contact']['contacts']
rounded_contact.append(actual_bar_contacts(b['path'],segments,witnesses,new['diameter_mm']))
report.update(after=new['count'],outside=outside,rounded_outside=rounded_outside,
rounded_contact_pass=sum(c['pass']for c in rounded_contact),warnings=sum(bool(b['warnings'])for b in bends))
if outside or rounded_outside or report['warnings'] or report['rounded_contact_pass']!=count:raise ValueError('geometry/contact validation failed')
# Raw and rounded contact are independently retained; no approval is implied.
new['quantity_distribution']['rounded_contact']=rounded_contact
new['placement_geometry_check']={'pass':True,'tube_outside_samples':0,'rounded_tube_outside_samples':0,'scope':'own source concrete union; straight and viewer-rounded paths'}
new['source_review_authorization']={'explicit_request':True,'scope':'drawing_quantity_distribution','export_allowed':False}
new['placement_status']='quantity_distributed_feedback_pending_engineering_review'
new['section_projection_check']={'pass':False,'reason':'user-authorized quantity distribution is not a literal drawing-station reconstruction'}
replacements[old['id']]=new;report['applied']=apply
except (ValueError,KeyError,StopIteration)as error:report['error']=str(error)
print(json.dumps(report,ensure_ascii=False),flush=True)
if apply and replacements:
result=copy.deepcopy(plan);result['rebar_groups']=[replacements.get(g.get('id'),g)for g in result['rebar_groups']]
preview=result.get('drawingEvidence',{}).get('preview_geometry')
if isinstance(preview,dict):preview['rebar_groups']=copy.deepcopy(result['rebar_groups'])
result['bodyTieQuantityDistribution']={'explicit_user_authorization':True,'reports':reports,'full_model_approved':False}
result['geometryRevision']='quantity-distribution-'+str(time.time_ns())
job.update(result=result,partialResult=copy.deepcopy(result),geometryRevision=time.time_ns())
if path.read_bytes()!=raw:raise ValueError('job changed during validation')
path.with_name(path.stem+'.before-tie-quantity-'+str(time.time_ns())+'.json').write_bytes(raw)
with tempfile.NamedTemporaryFile(mode='w',dir=path.parent,delete=False,encoding='utf8')as out:
json.dump(job,out,ensure_ascii=False,allow_nan=False);temp=out.name
os.replace(temp,path)
return reports
if __name__=='__main__':
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('job');parser.add_argument('--node',required=True);parser.add_argument('--apply',action='store_true')
args=parser.parse_args();run(args.job,args.node,args.apply)