inspection-host-computer/server/index.js

455 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// server/index.js
import express from 'express'
import cors from 'cors'
import path from 'path';
import 'dotenv/config';
import {
createAGVGrpcClient,
createARMGrpcClient,
createMicrophoneGrpcClient,
__dirname
} from './grpcClient.js';
import { attachAudioWebSocket } from './audioWebSocket.js';
const app = express()
app.use(express.static(path.join(__dirname, '../dist')));
app.use(cors())
app.use(express.json())
// 获取机器人麦克风音量0-100
app.post('/api/audio/microphone/volume/get', (req, res) => {
const { ip, deviceId } = req.body
if (!ip || !deviceId) {
res.status(400).json({ code: 400, message: '机器人地址和麦克风设备 ID 不能为空' })
return
}
const grpcClient = createMicrophoneGrpcClient(ip)
grpcClient.getVolume(
{ header: { device_id: deviceId } },
{ deadline: Date.now() + 5000 },
(error, feedback) => {
grpcClient.close()
if (error) {
console.error('[audio] get microphone volume failed:', error)
res.status(502).json({ code: 502, message: error.details || error.message || '获取机器人音量失败' })
return
}
if (feedback?.header?.success === false) {
res.status(502).json({
code: 502,
message: feedback.header.error_message || '机器人拒绝获取音量'
})
return
}
res.json({ code: 200, data: { volume: feedback?.volume ?? 0 } })
}
)
})
// 设置机器人麦克风音量0-100
app.post('/api/audio/microphone/volume/set', (req, res) => {
const { ip, deviceId } = req.body
const volume = Number(req.body.volume)
if (!ip || !deviceId) {
res.status(400).json({ code: 400, message: '机器人地址和麦克风设备 ID 不能为空' })
return
}
if (!Number.isInteger(volume) || volume < 0 || volume > 100) {
res.status(400).json({ code: 400, message: '音量必须是 0 到 100 的整数' })
return
}
const grpcClient = createMicrophoneGrpcClient(ip)
grpcClient.setVolume({
header: { device_id: deviceId },
volume
}, { deadline: Date.now() + 5000 }, (error, feedback) => {
grpcClient.close()
if (error) {
console.error('[audio] set microphone volume failed:', error)
res.status(502).json({ code: 502, message: error.details || error.message || '设置机器人音量失败' })
return
}
if (feedback?.header?.success === false) {
res.status(502).json({
code: 502,
message: feedback.header.error_message || '机器人拒绝设置音量'
})
return
}
res.json({ code: 200, data: { volume } })
})
})
// 获取agv运行时状态
app.post('/api/agv/getRuntimeState', async (req, res) => {
const request = {
header: {
device_id: req.body.deviceId || 'agv_src1100'
}
};
const grpcClient = createAGVGrpcClient(req.body.ip)
const resp = await new Promise((resolve, reject) => {
grpcClient.getRuntimeState(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res.state
});
}
});
})
if (resp.code === 200) {
res.json(resp)
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
// 获取机器人当前地图
app.post('/api/agv/getCurrentMap', async (req, res) => {
const grpcClient = createAGVGrpcClient(req.body.ip)
const request = {
header: {
device_id: req.body.deviceId || 'agv_src1100'
},
map_name: req.body.mapName
};
const resp = await new Promise((resolve, reject) => {
grpcClient.downloadMap(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res.content
});
}
});
})
if (resp.code === 200) {
res.json(resp)
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
// 机器人运动控制
app.post('/api/agv/moveRobot', async (req, res) => {
const grpcClient = setVelocity(req.body.ip)
const request = {
header: {
device_id: req.body.deviceId || 'agv_src1100'
},
velocity: {
vx: req.body.vx,
vy: req.body.vy,
vz: req.body.vz
}
};
const resp = await new Promise((resolve, reject) => {
grpcClient.setVelocity(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res.status
});
}
});
})
if (resp.code === 200) {
res.json({
code: 200,
data: resp
})
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
// 机器人停止运动
app.post('/api/agv/stopRobot', async (req, res) => {
const grpcClient = createAGVGrpcClient(req.body.ip)
const request = {
header: {
device_id: req.body.deviceId || 'agv_src1100'
}
};
const resp = await new Promise((resolve, reject) => {
grpcClient.stopVelocityControl(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res.status
});
}
});
})
if (resp.code === 200) {
res.json({
code: 200,
data: resp
})
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
// ==================================================
// 获取机械臂末端位姿
app.post('/api/arm/getPose', async (req, res) => {
const request = {
header: {
device_id: req.body.deviceId || 'huayan_arm'
}
};
const grpcClient = createARMGrpcClient(req.body.ip)
const resp = await new Promise((resolve, reject) => {
grpcClient.getPose(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res.pose
});
}
});
})
if (resp.code === 200) {
res.json(resp)
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
// 获取机械臂节点状态
app.post('/api/arm/getJointState', async (req, res) => {
const request = {
header: {
device_id: req.body.deviceId || 'huayan_arm'
}
};
const grpcClient = createARMGrpcClient(req.body.ip)
const resp = await new Promise((resolve, reject) => {
grpcClient.getJointState(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res.state
});
}
});
})
if (resp.code === 200) {
res.json(resp)
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
// 机械臂关节运动
app.post('/api/arm/speedJ', async (req, res) => {
const request = {
header: {
device_id: req.body.deviceId || 'huayan_arm'
},
velocity: {
velocity: req.body.velocities
},
acceleration: req.body.acceleration,
duration: req.body.duration
};
const grpcClient = createARMGrpcClient(req.body.ip)
const resp = await new Promise((resolve, reject) => {
grpcClient.speedJ(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res
});
}
});
})
if (resp.code === 200) {
res.json(resp)
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
app.post('/api/arm/speedL', async (req, res) => {
const request = {
header: {
device_id: req.body.deviceId || 'huayan_arm'
},
velocity: req.body.velocity,
acceleration: req.body.acceleration,
duration: req.body.duration
};
const grpcClient = createARMGrpcClient(req.body.ip)
const resp = await new Promise((resolve, reject) => {
grpcClient.speedL(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res
});
}
});
})
if (resp.code === 200) {
res.json(resp)
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
// 机械臂停止运动
app.post('/api/arm/stopMotion', async (req, res) => {
const request = {
// header: {
device_id: req.body.deviceId || 'huayan_arm',
// },
};
const grpcClient = createARMGrpcClient(req.body.ip)
const resp = await new Promise((resolve, reject) => {
grpcClient.stopMotion(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res.success
});
}
});
})
if (resp.code === 200) {
res.json(resp)
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
app.post('/api/arm/torqueOn', async (req, res) => {
const request = {
device_id: req.body.deviceId || 'huayan_arm',
};
const grpcClient = createARMGrpcClient(req.body.ip)
const resp = await new Promise((resolve, reject) => {
grpcClient.torqueOn(request, (err, res) => {
if (err) {
resolve({
code: 500,
data: `grpc服务端错误`
});
} else {
resolve({
code: 200,
data: res
});
}
});
})
if (resp.code === 200) {
res.json(resp)
} else {
res.status(500).json({ error: 'grpc服务端错误' })
}
})
const isProd = process.env.NODE_ENV === 'production';
const PORT = process.env.VITE_SERVICE_PORT || 3000;
// ============ 生产环境:托管 Vite 构建产物 ============
if (isProd) {
const distPath = path.resolve(__dirname, '../dist');
// 托管静态文件
app.use(express.static(distPath));
// SPA 回退:所有非 API 请求返回 index.html
app.get('/{*path}', (req, res) => {
res.sendFile(path.resolve(distPath, 'index.html'));
});
app.post('/{*path}', (req, res) => {
res.sendFile(path.resolve(distPath, 'index.html'));
});
}
// ========== 启动 =========
const httpServer = app.listen(PORT, () => {
console.log(`gRPC 桥接服务已启动: http://localhost:${PORT}`)
})
attachAudioWebSocket(httpServer)