简介
stp2browser2.0提供云端step文件的预渲染功能,将step文件中拓扑几何结构和材质信息解析成能直接导入webgl读取的2进制文件。此解析将能保留step文件装配体中的逻辑结构,对面和线做分别处理。
目前,stp2browser2.0服务器的应用方式主要是搭配其他位于同一服务器中的应用共同使用。stp2browser2.0应用与其所搭配的应用需要使用同一个文件系统(或者通过虚拟机共享文件夹的形式)。
HTTP请求
stp2browser2.0接受2个http请求,一个是转化申请请求(POST /step/convert body: FormData),一个是状态查询请求(GET step/status/<uid>)。转化申请请求将step文件发送给stp2browser2.0服务器,状态查询请求用于查询指定文件转化的状态。以下,将对这两个请求做详细说明。
转化申请请求
转化申请请求会向服务器发送一个step文件,请求转化运算。请求发送http示例代码如下:
function processFile(file, uid)
{
let uid = uid;
let formData = new FormData();
formData.append("file", file);
formData.append("uid", uid);
fetch('/step/convert/', {method: "POST", body: formData}).then(result => result.json())
.then(json => {
if(json.result === 'ok'){
//上传文件成功,3s后可以开始查询此任务的状态
}else if(json.result === 'error'){
if(json.msg === 'unknown'){
throw('unknown error')
}
else if(json.msg === 'wrong file format')
{
//上传文件uid失败,错误的step文件格式
throw('wrong step file format')
}
else{
//try again after 10 seconds
//服务器忙碌,10秒后重新尝试
...
}
}
})
.catch(err => {
//未知错误处理
console.log('we have problem...:', err);
})
}
如示例代码所示,转化申请请求是一个post类型的http请求,它的body中有个formData结构。formData结构中包含两条信息,file和uid。file是通过浏览器上传的File格式数据,uid是一个字符串类型数据,指的是本次转化请求的唯一标识符。如果本次请求转化与之前请求过的任务的唯一标志符重复,则本次请求所产生的信息将会完全覆盖上一次请求所产生的信息。
转化申请请求返回的数据有两大类的情况。第一,请求成功。返回如下json数据:
{
result: 'ok',
}
第二,请求失败。有如下几种可能的情况:
1.文件格式错误
{
"result": "error",
"msg": "wrong file format"
}
推荐处理方式:取消本次请求,检查step文件。
2. http请求格式错误
{
"result": "error",
"msg": "wrong request"
}
推荐处理方式:取消本次请求,检查http请求格式是否符合本文上一段落的描述
3. 服务器忙碌
{
"result": "error",
"msg": "busy"
}
推荐处理方式:稍等10秒钟之后再次尝试转化请求
注意:stp2browser服务器最多只能同时处理10个转化任务,当转化任务达到10个时,再发送转化请求,则会返回此错误。
4. 指定uid的任务正在转化运算中(正在转化运算中的任务不能覆盖)
{
"result": "error",
"msg": "converting"
}
推荐处理方式:稍等10秒钟之后再次尝试转化请求
5. 未知的错误
{
"result": "error",
"msg": "unknown"
}
推荐处理方式:取消本次请求
状态查询请求
状态查询请求负责向服务器查询uid指定任务的转化情况。每当转化请求成功后,服务器就会创建一个转化运算任务。此转化运算任务有如下几种状态:
- error, 转化任务运算发生错误
- pending, 转化任务正在排队
- converting, 转化任务正在进行运算
- finished, 转化任务已经完成
状态查询请求示例代码如下:
function getGltfUntilSuccess(uid, t){
fetch(`/step/status/${uid}`).then(result => result.json())
.then(json => {
if(json.status === 'pending'){
//set message pending
//文件${uid}的处理任务正在服务器排队..
console.log('pending...')
//等待10s之后再次请求
...
}else if(json.status === 'converting'){
let pendingTime = json.pendingTime
//set message converting
console.log('converting...')
console.log('pendingTime:', pendingTime, ' seconds')
//文件${uid}的处理任务正在运算!排队耗时 ${pendingTime} 秒
//等待3s之后再次请求
...
}else if(json.status === 'finished'){
let pendingTime = json.pendingTime
let convertTime = json.convertTime
console.log('finished')
console.log('pendingTime:', pendingTime, ' seconds')
console.log('convertTime:', convertTime, ' seconds')
//文件${uid}的处理任务已经完成!排队耗时 ${pendingTime} 秒,运算耗时 ${convertTime} 秒
//向服务器直接请求预渲染之后的文件
//HaoyuTuoPlugin.setObject('gltf/' + uid + '/' + uid, null, uid)
}else{
//error
console.log('convert failed, please check your step file');
//文件${uid}的处理任务失败,请检查step文件格式是否正确
}
}).catch((e) => {
console.log('unknown problems');
//文件${uid}的处理任务失败,未知的错误!
})
}
状态查询请求为GET类型的http请求,请求url为/step/status/<uid>。这里的uid与转化请求中的uid是同一个唯一标识符的概念。这个请求的返回值有以下4种情况:
- 任务正在排队
{
"status": "pending",
"result": "ok"
}
推荐处理方式: 10s后再次请求 - 任务正在运算
{
"status": "converting",
"result": "ok",
"pendingTime": pendingTime_In_Seconds
}
推荐处理方式:3s后再次请求 - 任务完成
{
"status": "finished",
"result": "ok",
"pendingTime": pendingTime_In_Seconds,
"convertTime": convertTime_In_Seconds
}
推荐处理方式:直接向服务器发送读取预渲染文件的请求(关于预渲染文件在服务器上的存在形式请参照下一个段落) - 任务错误
{
"status": "error",
"result": "ok",
"pendingTime": pendingTime_In_Seconds,
"convertTime": convertTime_In_Seconds
}
推荐处理方式: 取消本次请求,提示转化错误信息,检查并重新上传step文件
预渲染文件的存储
目前的stp2browser2.0服务器采用的是直接将预渲染文件存储在服务器本地文件系统的方案。预渲染文件的位置可以依据部署环境而自定义。自定义的方式也依具体部署方式方法而定。由于配置方法涉及到更多的服务器相关技术应用,本文暂不对如何配置预渲染文件位置的方法做描述,具体的位置配置由北京拓扑拓科技有限公司负责完成。
与stp2browser2.0服务器搭配的应用可以直接以访问静态资源的形式或许预渲染文件。
整体代码示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="author" content="北京拓扑拓科技有限公司">
<meta name="keywords" content="3D建模 移动APP 外包 WebAPP 智能建模APP">
<meta name="theme-color" content="#000000" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: blob: 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' http://* ata: content: data: blob:;">
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" type="text/css" href="w3.css">
<title>Step Browser Simple Demo Production</title>
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
#root{
width: 640px;
height: 640px;
position: relative
}
@media only screen and (max-width: 600px) {
#root {
width: 100vw;
height: 60vh;
}
}
#msg{
position: absolute;
left: 0px;
bottom:100%;
width: 100%;
max-height: 320px;
padding: 16px;
overflow-y: auto;
background-color: rgba(120,120,120,0.3);
box-shadow: 1px 1px 2px 2px rgba(0,0,0,0.1);
display: none
}
</style>
<script src="js/three.min.js"></script>
<script src="tuo-three.6013da4e.js"></script>
<script src="haoyu-tuo-plugin.b062e342.js"></script>
</head>
<body>
<div class="w3-contanier">
<input class="w3-input" onchange="onFileChange(event)" type='file'/>
<button class="w3-button w3-white w3-margin" onclick="onUpload(event)">Upload </button>
</div>
<div id="root"></div>
<div class="w3-contanier" style="position:relative">
<button class="w3-button w3-white w3-margin" id="detail"
onclick="switchMsgStat(event)">show detail</button>
<div
id = "msg"
>
</div>
</div>
<script>
HaoyuTuoPlugin.init("root")
var msg = ''
var isMsgOpen = false
function updateMsg(){
msgNode = document.getElementById('msg')
msgNode.innerText = msg
msgNode.scrollTop = msgNode.scrollHeight;
if(isMsgOpen === false)
showMsg()
}
function showMsg(){
msgNode = document.getElementById('msg')
msgNode.style.display = "block"
msgButton = document.getElementById('detail')
msgButton.innerText = 'hide detail'
isMsgOpen = true
}
function hideMsg(){
msgNode = document.getElementById('msg')
msgNode.style.display = "none"
msgButton = document.getElementById('detail')
msgButton.innerText = 'show detail'
isMsgOpen = false
}
function switchMsgStat(){
if(isMsgOpen)
hideMsg()
else
showMsg()
}
function appendMsg(text){
let dtext = '\n' + new Date() + ': ' + text
msg += dtext
updateMsg()
}
let stepFile
function onFileChange(event){
if(event.target.files.length > 0){
stepFile = event.target.files[0]
}
else
stepFile = null
}
function onUpload(event){
if(stepFile){
uploadFile(stepFile)
//fetch('/convert/upload', {method: "GET"})
}
}
function getDelayPromise(delay){
return new Promise((resolve, reject) => {
setTimeout(resolve, delay || 1000)
})
}
var curTime
function getGltfUntilSuccess(uid, t){
//to prevent repeat
if(t !== curTime)
return
fetch(`/step/status/${uid}`).then(result => result.json())
.then(json => {
if(json.status === 'pending'){
//set message pending
appendMsg(`文件${uid}的处理任务正在服务器排队..`)
console.log('pending...')
return getDelayPromise(10000).then(() => getGltfUntilSuccess(uid, t))
}else if(json.status === 'converting'){
let pendingTime = json.pendingTime
//set message converting
console.log('converting...')
console.log('pendingTime:', pendingTime, ' seconds')
appendMsg(`文件${uid}的处理任务正在运算!排队耗时 ${pendingTime} 秒`)
return getDelayPromise(3000).then(() => getGltfUntilSuccess(uid, t))
}else if(json.status === 'finished'){
let pendingTime = json.pendingTime
let convertTime = json.convertTime
console.log('finished')
console.log('pendingTime:', pendingTime, ' seconds')
console.log('convertTime:', convertTime, ' seconds')
appendMsg(`文件${uid}的处理任务正在运算!排队耗时 ${pendingTime} 秒`)
if(t !== curTime)
return
appendMsg(`文件${uid}的处理任务已经完成!排队耗时 ${pendingTime} 秒,运算耗时 ${convertTime} 秒`)
HaoyuTuoPlugin.setObject('gltf/' + uid + '/' + uid, null, uid)
}else{
//error
HaoyuTuoPlugin.hideLoadingElement()
console.log('convert failed, please check your step file');
appendMsg(`文件${uid}的处理任务失败,请检查step文件格式是否正确`)
}
}).catch((e) => {
HaoyuTuoPlugin.hideLoadingElement()
console.log('unknown problems');
appendMsg(`文件${uid}的处理任务失败,未知的错误!`)
})
}
function uploadFile(file){
curTime = new Date().getTime()
processFile(file, curTime)
}
function processFile(file, t)
{
if(t !== curTime)
return
let uid = file.name;
let formData = new FormData();
formData.append("file", file);
formData.append("uid", uid);
appendMsg('开始上传step文件:' + uid)
HaoyuTuoPlugin.setLoadingElement('转化中')
fetch('/step/convert/', {method: "POST", body: formData}).then(result => result.json())
.then(json => {
if(json.result === 'ok'){
appendMsg(`上传文件${uid}成功!`)
return getDelayPromise(3000).then(() => getGltfUntilSuccess(uid, t))
}else if(json.result === 'error'){
if(json.msg === 'unknown'){
appendMsg(`上传文件${uid}失败,未知错误!`)
throw('unknown error')
}
else if(json.msg === 'wrong file format')
{
appendMsg(`上传文件${uid}失败,错误的step文件格式!`)
throw('wrong step file format')
}
else{
//show message why convert failed
//try again after 10 seconds
appendMsg(`服务器忙碌,10秒后重新尝试`)
return getDelayPromise(10000).then(() => processFile(file, t))
}
}
})
.catch(err => {
HaoyuTuoPlugin.hideLoadingElement()
console.log('we have problem...:', err);
})
/*
if(r.result === 'ok'){
//show picture
HaoyuTuoPlugin.setObject('gltf/' + uid + '/' + uid, null, file.name)
}
else{
//HaoyuTuoPlugin.setObject('gltf/' + uid, null, file.name)
HaoyuTuoPlugin.openMessageDialog(r.msg)
HaoyuTuoPlugin.hideLoadingElement()
}
*/
}
</script>
</body>
</html>