const fs = require("node:fs"); const path = require("node:path"); const vm = require("node:vm"); const base = path.resolve(__dirname, "../../tools/est-test"); const context = vm.createContext({ structuredClone, console }); for (const file of [ "native-math.js", "native-formula.js", "native-machine.js", "native-rows.js", "native-text.js", "native-quantity.cjs", "native-wonga.js", "native-engine.js", "native-escalation.js", "native-progress.js", "native-business.js", "native-resources.js", "native-schedule.js", "native-model.js", "native-c3r.js", "native-exchange.js", "native-verify.js", ]) { vm.runInContext(fs.readFileSync(path.join(base, file), "utf8"), context, { filename: file }); } const project = JSON.parse(fs.readFileSync(path.join(base, "sample.json"), "utf8")); context.project = project; const command = process.argv[2]; let result; if (command === "machine") result = vm.runInContext( "EstMachine.calculate(project.tables.MachMainDat, project.tables.MachGijunDat, project.options)", context, ); else if (command === "head") result = vm.runInContext("EstRows.head(project.tables.HeadMainDat, project.options)", context); else if (command === "estimate") result = vm.runInContext("EstRows.estimate(project.tables.EstMainDat, project.options)", context); else if (command === "text") result = vm.runInContext( "EstText.calculate(project.tables.CostMainDat, project.options)", context, ); else if (command === "wonga") result = vm.runInContext("EstWonga.calculate(project.tables.Wonga, project.options)", context); else if (command === "all") result = vm.runInContext("EstNative.calculate(project)", context); else if (command === "text-layout") { context.cases = JSON.parse( fs.readFileSync(path.join(__dirname, "fixtures/est_text_layout_cases.json"), "utf8"), ); result = vm.runInContext( "cases.map(item=>({code:item.code,...EstText.calculate(item.rows)}))", context, ); } else if (command === "manual") { project.options.AutoCalc = false; project.tables.EstMainDat[1].GN += 100; result = require(path.join(base, "server.cjs")).calculate(project); } else if (command === "refresh") result = vm.runInContext( "project.options.SourceRefresh=true; EstNative.calculate(project)", context, ); else if (command === "repeat") result = vm.runInContext( 'project.options.SourceRefresh=true; project.tables.LaborcDat.forEach(row=>{for(const key of ["Cost1","Cost2","Cost3","Cost4","Cost5","Cost6"]) row[key] = Number(row[key] || 0)*1.1}); const a=EstNative.calculate(project); [a,EstNative.calculate(a)]', context, ); else if (command === "import") result = require(path.join(base, "import-estx.cjs")).importEstx(fs.readFileSync(process.argv[3])); else if (command === "baseline-size") { context.input = require(path.join(base, "import-estx.cjs")).importEstx( fs.readFileSync(process.argv[3]), ); const baseline = vm.runInContext("EstBusiness.baseline(input)", context); result = { bytes: Buffer.byteLength(JSON.stringify(baseline), "utf8"), archivePreserved: baseline.originalArchive === context.input.originalArchive, }; } else if (command === "blob-tail") { const { blob } = require(path.join(base, "import-estx.cjs")), bytes = Buffer.alloc(1536); bytes.writeUInt32LE(2, 516); bytes.writeUInt16LE(3, 520); bytes.writeUInt32LE(7, 522); bytes.writeUInt32LE(3, 526); bytes.write("abc", 530); bytes.writeUInt32LE(1, 1024); bytes.writeUInt16LE(3, 1032); bytes.write("xyz", 1042); const value = blob(bytes, 1, 7).toString(); bytes.writeUInt32LE(4, 526); bytes.writeUInt32LE(0, 516); let truncated = ""; try { blob(bytes, 1, 7); } catch (error) { truncated = error.message; } result = { value, truncated }; } else if (command === "extended") { context.extended = JSON.parse( fs.readFileSync(path.join(__dirname, "fixtures/est_native_extended.json"), "utf8"), ); result = vm.runInContext( `({ head: EstRows.head(extended.tables.HeadMainDat, extended.options), text: EstText.calculate(extended.tables.CostMainDat, extended.options), machine: EstMachine.calculate(extended.tables.MachMainDat, extended.tables.MachGijunDat, extended.options) })`, context, ); } else if (command === "quantity") { context.sheets = JSON.parse( fs.readFileSync(path.join(__dirname, "fixtures/est_quantity_sheets.json"), "utf8"), ); result = vm.runInContext( "sheets.map(sheet=>({Code:sheet.Code,original:sheet.items,...EstQuantity.calculate(sheet)}))", context, ); } else if (command === "business") result = vm.runInContext( ` const p=EstNative.empty(); p.tables.StuffcDat=[{Code:'M',Name:'material',Cost1:100,ValueIndex:0}]; p.tables.HeadMainDat=[{Index:1,Code:'H',Name:'head'},{Index:0,Code:'M',LinkIndex:5,GN:2}]; p.tables.HeadTitleDat=[{Code:'H',Name:'head'}]; p.tables.EstMainDat=[{PkID:1,Code:'H',Name:'item',GN:10,LinkIndex:103},{PkID:2,Name:'direct',LinkIndex:54}]; p.tables.EstMainDat.push({PkID:4,Name:'부가세',LinkIndex:58},{PkID:5,Name:'total',LinkIndex:55}); p.tables.Wonga=[{Gubun:'G',Name:'material',BaseField:'GC',Cost:0},{Gubun:'V',Name:'부가세',VSusic:'G*0.1',Youl:'10%'},{Gubun:'T',Name:'total',VSusic:'G+V',LinkIndex:55}]; let q=EstNative.calculate(p);q=EstBusiness.baseline(q); q.progress=[{id:'one',inputs:{'1':{Gum:25,GumMode:'ratio',Sa:1.5}},approved:true,locked:false}]; const first=EstBusiness.progress(q,'one');q.progress[0].result=first;q.progress[0].locked=true; q.progress.push({id:'two',inputs:{'1':{Gum:500,GumMode:'amount',Sa:1}},approved:false,locked:false}); const second=EstBusiness.progress(q,'two'); q.annual=[{name:'1차',formula:'[계약수량]*0.4'},{name:'2차',formula:'[계약수량]-[1차]'}]; const years=EstBusiness.annual(q);const resources=EstResources.calculate(q); const esc=structuredClone(q);esc.options.SourceRefresh=false; Object.assign(esc.tables.EstMainDat[0],{AGU:180,GU:240,EscPlanned:2,EscActual:3,EscProgress:1}); const escalation=EstBusiness.escalation(esc); const changed=structuredClone(q);changed.tables.StuffcDat[0].Cost1=120; changed.tables.EstMainDat.splice(1,0,{PkID:3,Code:'M',Name:'new',GN:1,LinkIndex:5,NewItem:true}); const originals=[0,1,2].map(method=>EstBusiness.original(changed,method)); const bid=EstBusiness.bid(q,[90]);const bidAgain=EstBusiness.bid(bid,[90]); const bidResources=EstResources.calculate(bid); const converted=structuredClone(q);converted.tables.HeadMainDat.push({Index:0,GN:0.5,LinkIndex:68,Size:'unit conversion'}); const convertedResources=EstResources.calculate(converted); const equipment=EstNative.empty(); equipment.tables.MachMainDat=[{Index:1,Code:'E',Name:'equipment',Unite:'hr'},{Index:-2,Name:'주연료',GN:1,TU:100}]; equipment.tables.HeadMainDat=[{Index:1,Code:'H',Name:'head'},{Index:0,LinkIndex:105,Code:'E',GN:2}]; equipment.tables.HeadTitleDat=[{Code:'H'}];equipment.tables.EstMainDat=[{PkID:1,LinkIndex:103,Code:'H',GN:10},{LinkIndex:54}]; const equipmentResources=EstResources.calculate(equipment); ({first,second,years,resources,escalation,originals,bid,bidAgain,bidResources,convertedResources,equipmentResources,project:q}); `, context, ); else if (command === "math") result = vm.runInContext(process.argv[3], context); else throw Error("unknown command"); process.stdout.write(JSON.stringify(result));