@@ -10,6 +10,7 @@ import { spawn } from 'child_process';
import { WebcamClient } from './webcamClient.js' ;
import { assignByZRange , removeMarkerAssignment , alignSetToMeasured , assignMarkerId , adoptXAxis , assignFixedMarkersToLink , setJointOriginYZ , setArmMarkerSpin } from './editRobot.js' ;
import { runHoming } from './homingOrchestrator.js' ;
import { fetchRobot , robotCachePath } from './robotConfig.js' ;
const _ _filename = fileURLToPath ( import . meta . url ) ;
const _ _dirname = path . dirname ( _ _filename ) ;
@@ -439,8 +440,6 @@ app.post('/api/calibration/compute', async (req, res) => {
const boardDataDir = path . join ( _ _dirname , '..' , 'data' , 'board' ) ;
const homingDataDir = path . join ( _ _dirname , '..' , 'data' , 'homing' ) ;
const ROBOT _JSON = process . env . ROBOT _JSON
|| path . join ( _ _dirname , '..' , 'scripts' , 'robot_1781069752019.json' ) ;
const SCRIPT _1 = path . join ( _ _dirname , '..' , 'scripts' , '1_detect_aruco_observations.py' ) ;
const SCRIPT _2 = path . join ( _ _dirname , '..' , 'scripts' , '2_estimate_camera_from_observations.py' ) ;
const SCRIPT _3B = path . join ( _ _dirname , '..' , 'scripts' , '3b_corner_marker_poses.py' ) ;
@@ -496,6 +495,12 @@ function runScript(args, send) {
* @param {{ refSet?: string }} [opts]
*/
async function runBoardPipeline ( runDir , send , { refSet } = { } ) {
try {
await fetchRobot ( ) ;
} catch ( err ) {
send ( { type : 'log' , text : ` ⚠ robot.json-Cache: Driver nicht erreichbar – nutze lokale Datei ( ${ err . message } ) ` } ) ;
}
// Kameras ermitteln
if ( ! WEBCAM _URL ) throw new Error ( 'WEBCAM_URL nicht konfiguriert' ) ;
const camData = await new WebcamClient ( WEBCAM _URL ) . getCameras ( ) ;
@@ -538,7 +543,7 @@ async function runBoardPipeline(runDir, send, { refSet } = {}) {
SCRIPT _1 ,
'-i' , imgPath ,
'-npz' , npzPath ,
'-robot' , ROBOT _JSON ,
'-robot' , robotCachePath ,
'-cameraId' , camId ,
'-outDir' , runDir ,
'--saveDebugImage' ,
@@ -556,7 +561,7 @@ async function runBoardPipeline(runDir, send, { refSet } = {}) {
continue ;
}
send ( { type : 'log' , text : '\n▷ 2_estimate_camera_from_observations' } ) ;
const script2Args = [ SCRIPT _2 , '-i' , detJson , '-robot' , ROBOT _JSON , '-outDir' , runDir ] ;
const script2Args = [ SCRIPT _2 , '-i' , detJson , '-robot' , robotCachePath , '-outDir' , runDir ] ;
if ( refSet ) script2Args . push ( '--refSet' , refSet ) ;
const exit2 = await runScript ( script2Args , send ) ;
if ( exit2 !== 0 ) send ( { type : 'log' , text : ` ❌ Script 2 Exit ${ exit2 } ` } ) ;
@@ -574,7 +579,7 @@ async function runBoardPipeline(runDir, send, { refSet } = {}) {
const exit3b = await runScript ( [
SCRIPT _3B ,
'--evalDir' , runDir ,
'--robot' , ROBOT _JSON ,
'--robot' , robotCachePath ,
] , send ) ;
if ( exit3b !== 0 ) send ( { type : 'log' , text : ` ❌ Script 3b Exit ${ exit3b } ` } ) ;
} else {
@@ -612,13 +617,13 @@ app.post('/api/board/run', async (req, res) => {
// Robot-JSON laden und Marker-Anzahl loggen
let robotData = null ;
try { robotData = JSON . parse ( await fsPromises . readFile ( ROBOT _JSON , 'utf8' ) ) ; } catch { }
try { robotData = JSON . parse ( await fsPromises . readFile ( robotCachePath , 'utf8' ) ) ; } catch { }
const boardMarkers = robotData ? . links ? . Board ? . markers ? ? [ ] ;
const boardMarkerCount = boardMarkers . length ;
const refMarkerCount = refSet
? boardMarkers . filter ( m => m . set === refSet ) . length
: boardMarkerCount ;
send ( { type : 'log' , text : ` ▶ Robot-JSON: ${ ROBOT _JSON } ` } ) ;
send ( { type : 'log' , text : ` ▶ Robot-JSON: ${ robotCachePath } ` } ) ;
send ( { type : 'log' , text : ` ▶ Board-Marker: ${ boardMarkerCount } (links.Board.markers) ` } ) ;
send ( { type : 'log' , text : ` ▶ Referenz-Set: ${ refSet ? ` " ${ refSet } " ( ${ refMarkerCount } Marker) ` : 'alle' } ` } ) ;
send ( { type : 'log' , text : '' } ) ;
@@ -703,7 +708,7 @@ app.get('/api/board/latest', async (req, res) => {
const runDir = path . join ( dataDir , runName ) ;
let robot = null ;
try { robot = JSON . parse ( await fsPromises . readFile ( ROBOT _JSON , 'utf8' ) ) ; } catch { }
try { robot = JSON . parse ( await fsPromises . readFile ( robotCachePath , 'utf8' ) ) ; } catch { }
let files = [ ] ;
try { files = await fsPromises . readdir ( runDir ) ; } catch { }
@@ -746,7 +751,7 @@ app.get('/api/board/latest', async (req, res) => {
measuredMarkers = JSON . parse ( raw ) ;
} catch { }
return res . json ( { runDir : runName , robotFile : path . basename ( ROBOT _JSON ) , robot , detections , cameraPoses , measuredMarkers } ) ;
return res . json ( { runDir : runName , robotFile : path . basename ( robotCachePath ) , robot , detections , cameraPoses , measuredMarkers } ) ;
} catch ( err ) {
return res . status ( 500 ) . json ( { error : String ( err ) } ) ;
}
@@ -771,7 +776,7 @@ app.post('/api/homing/run', async (req, res) => {
try {
await fsPromises . mkdir ( homingDataDir , { recursive : true } ) ;
await runHoming ( {
robotJsonPath : ROBOT _JSON ,
robotJsonPath : robotCachePath ,
homingDir : homingDataDir ,
send ,
runScript ,
@@ -890,7 +895,7 @@ app.post('/api/robot/assign-by-z', async (req, res) => {
}
} catch { /* kein 3b-Output vorhanden – nur bestehende robot.json-Marker bearbeiten */ }
const result = await assignByZRange ( ROBOT _JSON , { zMin , zMax , set , link , extraMarkers } ) ;
const result = await assignByZRange ( robotCachePath , { zMin , zMax , set , link , extraMarkers } ) ;
const added = result . changes . filter ( c => c . action === 'added' ) . length ;
const updated = result . changes . filter ( c => c . action === 'updated' ) . length ;
console . log ( ` robot/assign-by-z z=[ ${ zMin } .. ${ zMax } ] set=" ${ set } " link=" ${ link } " → ${ updated } aktualisiert, ${ added } neu (von ${ extraMarkers . length } 3b-Markern) ` ) ;
@@ -915,7 +920,7 @@ app.post('/api/robot/remove-marker', async (req, res) => {
if ( ! [ 'set' , 'link' ] . includes ( removeFrom ) ) {
return res . status ( 400 ) . json ( { error : 'removeFrom muss "set" oder "link" sein' } ) ;
}
const result = await removeMarkerAssignment ( ROBOT _JSON , { markerId , removeFrom } ) ;
const result = await removeMarkerAssignment ( robotCachePath , { markerId , removeFrom } ) ;
console . log ( ` robot/remove-marker id= ${ markerId } from= ${ removeFrom } → changed= ${ result . changed } ` ) ;
return res . json ( result ) ;
} catch ( err ) {
@@ -930,7 +935,7 @@ app.post('/api/robot/remove-marker', async (req, res) => {
*/
app . get ( '/api/robot' , async ( req , res ) => {
try {
const robot = JSON . parse ( await fsPromises . readFile ( ROBOT _JSON , 'utf8' ) ) ;
const robot = await fetchRobot ( ) ;
return res . json ( robot ) ;
} catch ( err ) {
return res . status ( 500 ) . json ( { error : String ( err ) } ) ;
@@ -944,7 +949,7 @@ app.get('/api/robot', async (req, res) => {
*/
app . get ( '/api/robot/board-sets' , async ( req , res ) => {
try {
const robot = JSON . parse ( await fsPromises . readFile ( ROBOT _JSON , 'utf8' ) ) ;
const robot = await fetchRobot ( ) ;
const markers = robot ? . links ? . Board ? . markers ? ? [ ] ;
const sets = [ ... new Set ( markers . map ( m => m . set ) . filter ( Boolean ) ) ] . sort ( ) ;
return res . json ( { sets } ) ;
@@ -975,7 +980,7 @@ app.post('/api/robot/align-sets', async (req, res) => {
}
} catch { /* kein 3b-Output vorhanden */ }
const result = await alignSetToMeasured ( ROBOT _JSON , { setToMove , extraMarkers } ) ;
const result = await alignSetToMeasured ( robotCachePath , { setToMove , extraMarkers } ) ;
if ( result . error ) return res . status ( 400 ) . json ( result ) ;
console . log (
@@ -1010,7 +1015,7 @@ app.post('/api/robot/assign-id', async (req, res) => {
}
} catch { /* kein 3b-Output vorhanden */ }
const result = await assignMarkerId ( ROBOT _JSON , { markerId , set , link , extraMarkers } ) ;
const result = await assignMarkerId ( robotCachePath , { markerId , set , link , extraMarkers } ) ;
if ( ! result . changed && result . error ) return res . status ( 400 ) . json ( result ) ;
console . log (
@@ -1036,7 +1041,7 @@ app.post('/api/robot/adopt-x-axis', async (req, res) => {
if ( ! Array . isArray ( direction ) || direction . length < 3 ) {
return res . status ( 400 ) . json ( { error : '"direction" muss ein Array [vx,vy,vz] sein.' } ) ;
}
const result = await adoptXAxis ( ROBOT _JSON , { direction } ) ;
const result = await adoptXAxis ( robotCachePath , { direction } ) ;
console . log (
` robot/adopt-x-axis dir=[ ${ direction . map ( v => Number ( v ) . toFixed ( 4 ) ) . join ( ', ' ) } ] ` +
` → ${ result . numChanged } Marker, Ursprung=[ ${ result . origin . join ( ', ' ) } ] ` +
@@ -1064,7 +1069,7 @@ app.post('/api/robot/assign-fixed-markers', async (req, res) => {
if ( ! targetLink ) {
return res . status ( 400 ) . json ( { error : '"targetLink" muss angegeben werden.' } ) ;
}
const result = await assignFixedMarkersToLink ( ROBOT _JSON , { markerIds , targetLink , measuredPositions } ) ;
const result = await assignFixedMarkersToLink ( robotCachePath , { markerIds , targetLink , measuredPositions } ) ;
console . log (
` robot/assign-fixed-markers [ ${ markerIds . join ( ',' ) } ] → ${ targetLink } ` +
` added= ${ result . numAdded } alreadyPresent= ${ result . numAlreadyPresent } ` ,
@@ -1089,7 +1094,7 @@ app.post('/api/robot/set-joint-origin', async (req, res) => {
if ( ! Number . isFinite ( Number ( y ) ) || ! Number . isFinite ( Number ( z ) ) ) {
return res . status ( 400 ) . json ( { error : '"y" und "z" müssen Zahlen sein.' } ) ;
}
const result = await setJointOriginYZ ( ROBOT _JSON , { linkName , y : Number ( y ) , z : Number ( z ) } ) ;
const result = await setJointOriginYZ ( robotCachePath , { linkName , y : Number ( y ) , z : Number ( z ) } ) ;
if ( ! result . changed ) {
return res . status ( 400 ) . json ( { error : result . error } ) ;
}
@@ -1115,7 +1120,7 @@ app.post('/api/robot/set-arm-marker-spin', async (req, res) => {
if ( ! linkName ) return res . status ( 400 ) . json ( { error : '"linkName" muss angegeben werden.' } ) ;
if ( markerId == null ) return res . status ( 400 ) . json ( { error : '"markerId" muss angegeben werden.' } ) ;
if ( ! Number . isFinite ( Number ( spin ) ) ) return res . status ( 400 ) . json ( { error : '"spin" muss eine Zahl sein.' } ) ;
const result = await setArmMarkerSpin ( ROBOT _JSON , { linkName , markerId , spin : Number ( spin ) } ) ;
const result = await setArmMarkerSpin ( robotCachePath , { linkName , markerId , spin : Number ( spin ) } ) ;
if ( ! result . changed ) return res . status ( 400 ) . json ( { error : result . error } ) ;
console . log ( ` robot/set-arm-marker-spin ${ linkName } # ${ markerId } : ${ result . oldSpin } ° → ${ result . newSpin } ° ` ) ;
return res . json ( result ) ;
@@ -1268,6 +1273,13 @@ async function startServer() {
await checkServiceReachability ( 'BODYTRACKER_URL' , new URL ( '/v1/health' , BODYTRACKER _URL ) . toString ( ) ) ;
}
try {
await fetchRobot ( ) ;
console . log ( '✅ robot.json geladen und gecacht.' ) ;
} catch ( err ) {
console . warn ( ` ⚠ robot.json: Driver nicht erreichbar – nutze lokale Datei: ${ err . message } ` ) ;
}
const server = await createHttpsServer ( ) ;
const isHttps = Boolean ( server ) ;
const listenServer = server || app ;