fusenapi/server/websocket/internal/logic/ws_render_image.go
laodaming 5c47a30347 11
2023-09-13 10:48:53 +08:00

649 lines
29 KiB
Go
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.

package logic
//处理websocket云渲染任务数据
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"fusenapi/constants"
"fusenapi/model/gmodel"
"fusenapi/service/repositories"
"fusenapi/utils/curl"
"fusenapi/utils/hash"
"fusenapi/utils/template_switch_info"
"fusenapi/utils/websocket_data"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
"strconv"
"time"
)
var (
//每个websocket连接渲染任务调度队列长度默认值添加任务/删除任务/修改任务属性缓冲队列长度该队列用于避免map并发读写冲突
renderImageTaskCtlChanLen = 100
//每个websocket渲染任务缓冲队列长度默认值
renderChanLen = 500
)
// 渲染处理器
type renderProcessor struct {
}
// 云渲染属性
type extendRenderProperty struct {
//renderImageTask map[string]*renderTask //需要渲染的图片任务 key是taskId val 是renderId
renderImageTaskCtlChan chan renderImageControlChanItem //渲染任务新增/回调结果移除任务/更新渲染耗时属性的控制通道由于任务map无法读写并发
renderChan chan []byte //渲染消息入口的缓冲队列
renderConsumeTickTime time.Duration //消费渲染消息时钟间隔(纳秒),用于后期控制不同类型用户渲染速度限制
}
// 渲染任务新增移除的控制通道的数据
type renderImageControlChanItem struct {
option int // 0删除 1添加 2修改耗时属性
taskId string //map的key(必须传)
renderId string // map的val(增加任务时候传)
renderNotifyImageUrl string //渲染回调数据(删除任务时候传)
taskProperty renderTask //渲染任务的属性
}
// 渲染任务属性
type renderTask struct {
renderId string //渲染id(新增任务传)
unityRenderBeginTime int64 //发送给unity时间
unityRenderEndTime int64 //unity回调结果时间
uploadUnityRenderImageTakesTime int64 //上传unity渲染结果图时间
}
// 处理分发到这里的数据
func (r *renderProcessor) allocationMessage(w *wsConnectItem, data []byte) {
//logx.Info("收到渲染任务消息:", string(data))
select {
case <-w.closeChan: //已经关闭
return
case w.extendRenderProperty.renderChan <- data: //发入到缓冲队列
return
case <-time.After(time.Second * 3): //三秒没进入缓冲队列就丢弃
return
}
}
// 消费渲染缓冲队列数据
func (w *wsConnectItem) consumeRenderImageData() {
defer func() {
if err := recover(); err != nil {
logx.Error("func renderImage err:", err)
}
}()
var duration time.Duration = 1
if w.extendRenderProperty.renderConsumeTickTime > 0 {
duration = w.extendRenderProperty.renderConsumeTickTime
}
ticker := time.NewTicker(duration)
defer ticker.Stop()
for {
select {
case <-w.closeChan: //已关闭
return
case <-ticker.C: //消费数据
w.renderImage(<-w.extendRenderProperty.renderChan)
}
}
}
// 执行渲染任务
func (w *wsConnectItem) renderImage(data []byte) {
//logx.Info("消费渲染数据:", string(data))
var renderImageData websocket_data.RenderImageReqMsg
if err := json.Unmarshal(data, &renderImageData); err != nil {
w.incomeDataFormatErrResponse("invalid format of render data:" + string(data))
logx.Error("invalid format of websocket render image message", err)
return
}
//获取产品信息(部分字段)
productInfo, err := w.logic.svcCtx.AllModels.FsProduct.FindOne(w.logic.ctx, renderImageData.RenderData.ProductId, "id,is_customization")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "该产品不存在", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
return
}
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取产品失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
logx.Error(err)
return
}
//不可定制
if *productInfo.IsCustomization == 0 {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "该产品不可定制", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
return
}
//获取用户需要渲染logo
logoInfo, err := w.logic.svcCtx.Repositories.ImageHandle.LogoInfo(w.logic.ctx, &repositories.LogoInfoReq{
UserId: w.userId,
GuestId: w.guestId,
})
if err != nil {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取用户logo素材错误", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
logx.Error(err)
return
}
if logoInfo == nil || logoInfo.LogoUrl == nil {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "用户logo素材url是空的", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
return
}
renderImageData.RenderData.Logo = *logoInfo.LogoUrl
//用户id赋值
renderImageData.RenderData.UserId = w.userId
renderImageData.RenderData.GuestId = w.guestId
var (
model3dInfo *gmodel.FsProductModel3d
productTemplate *gmodel.FsProductTemplateV2
productSize *gmodel.FsProductSize
)
//获取信息
if renderImageData.RenderData.ProductSizeId > 0 {
productSize, productTemplate, model3dInfo, err = w.getProductRelateionInfoWithSizeId(&renderImageData)
} else {
productSize, productTemplate, model3dInfo, err = w.getProductRelateionInfoWithNoSizeId(&renderImageData)
}
if err != nil {
logx.Error(err)
return
}
//获取渲染设置信息
element, err := w.logic.svcCtx.AllModels.FsProductTemplateElement.FindOneByModelId(w.logic.ctx, *productTemplate.ElementModelId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "云渲染设置不存在", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
logx.Error("element info is not found,element_model_id = ", *productTemplate.ElementModelId)
return
}
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取云渲染设置失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
logx.Error("failed to get element ,", err)
return
}
//获取模板开关信息并且对于没有默认值的给赋值默认值(但凡DIY有一个是空的就要请求默认数据)
if renderImageData.RenderData.Website == "" || renderImageData.RenderData.Phone == "" || renderImageData.RenderData.Address == "" || renderImageData.RenderData.Qrcode == "" {
templateSwitchInfo := template_switch_info.GetTemplateSwitchInfo(productTemplate.Id, productTemplate.TemplateInfo, *productTemplate.MaterialImg)
if renderImageData.RenderData.Website == "" && templateSwitchInfo.MaterialData.Website.IfShow {
renderImageData.RenderData.Website = templateSwitchInfo.MaterialData.Website.DefaultValue
}
if renderImageData.RenderData.Phone == "" && templateSwitchInfo.MaterialData.Phone.IfShow {
renderImageData.RenderData.Phone = templateSwitchInfo.MaterialData.Phone.DefaultValue
}
if renderImageData.RenderData.Address == "" && templateSwitchInfo.MaterialData.Address.IfShow {
renderImageData.RenderData.Address = templateSwitchInfo.MaterialData.Address.DefaultValue
}
if renderImageData.RenderData.Qrcode == "" && templateSwitchInfo.MaterialData.QRcode.IfShow {
renderImageData.RenderData.Qrcode = templateSwitchInfo.MaterialData.QRcode.DefaultValue
}
if renderImageData.RenderData.Slogan == "" && templateSwitchInfo.MaterialData.Slogan.IfShow {
renderImageData.RenderData.Slogan = templateSwitchInfo.MaterialData.Slogan.DefaultValue
}
}
//获取刀版图
combineReq := repositories.LogoCombineReq{
UserId: renderImageData.RenderData.UserId,
GuestId: renderImageData.RenderData.GuestId,
TemplateId: productTemplate.Id,
TemplateTag: renderImageData.RenderData.TemplateTag,
Website: renderImageData.RenderData.Website,
Slogan: renderImageData.RenderData.Slogan,
Address: renderImageData.RenderData.Address,
Phone: renderImageData.RenderData.Phone,
Qrcode: renderImageData.RenderData.Qrcode,
LogoUrl: renderImageData.RenderData.Logo,
}
res, err := w.logic.svcCtx.Repositories.ImageHandle.LogoCombine(w.logic.ctx, &combineReq)
if err != nil {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "合成刀版图失败:"+err.Error(), renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
logx.Error("合成刀版图失败,合成请求数据:", combineReq, "错误信息:", err)
return
}
combineImage := "" //刀版图
if res != nil && res.ResourceUrl != nil {
combineImage = *res.ResourceUrl
} else {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "合成的刀版图是空的地址", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
logx.Error("合成刀版图失败,合成的刀版图是空指针:", err)
return
}
//发送合图完毕阶段消息
w.sendCombineImageStepResponseMessage(renderImageData.RenderId, combineImage, productSize.Id, model3dInfo.Id, productTemplate.Id, res.DiffTimeLogoCombine, res.DiffTimeUploadFile)
//如果指定指定只返回刀版图
if renderImageData.OnlyReturnCombineImage {
logx.Info("云渲染传入only_return_combine_image = true则不走unity云渲染,只返回刀版图render_id:", renderImageData.RenderId)
return
}
//获取唯一id
taskId := w.genRenderTaskId(combineImage, renderImageData, model3dInfo, productTemplate, element)
//查询有没有缓存的资源,有就返回######################
resource, err := w.logic.svcCtx.AllModels.FsResource.FindOneById(w.logic.ctx, taskId)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, taskId, "获取unity云渲染缓存失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
logx.Error("failed to find render resource:", err)
return
}
} else {
//返回给客户端
w.sendRenderResultData(websocket_data.RenderImageRspMsg{
RenderId: renderImageData.RenderId,
Image: *resource.ResourceUrl,
RenderProcessTime: websocket_data.RenderProcessTime{
UnityRenderTakesTime: "cache",
UploadUnityRenderImageTakesTime: "cache",
},
})
return
}
//###########################################
//把需要渲染的图片任务加进去
//w.createRenderTask(taskId, renderImageData.RenderId)
//组装数据
if err = w.assembleRenderDataToUnity(taskId, combineImage, renderImageData, productTemplate, model3dInfo, element, productSize); err != nil {
logx.Error("组装数据失败:", err)
return
}
}
// 获取模板相关信息(指定尺寸)(尺寸 -> 模型 ->模板)
func (w *wsConnectItem) getProductRelateionInfoWithSizeId(renderImageData *websocket_data.RenderImageReqMsg) (productSize *gmodel.FsProductSize, productTemplate *gmodel.FsProductTemplateV2, model3d *gmodel.FsProductModel3d, err error) {
productSize, err = w.logic.svcCtx.AllModels.FsProductSize.FindOneByIdProductId(w.logic.ctx, renderImageData.RenderData.ProductSizeId, renderImageData.RenderData.ProductId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "找不到产品的指定尺寸", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, renderImageData.RenderData.ProductSizeId, 0)
logx.Error("product size is not found")
return
}
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取产品的指定尺寸失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, renderImageData.RenderData.ProductSizeId, 0)
logx.Error("failed to get product size:", err)
return nil, nil, nil, err
}
//获取模型
model3d, err = w.logic.svcCtx.AllModels.FsProductModel3d.GetOneBySizeIdTag(w.logic.ctx, productSize.Id, constants.TAG_MODEL)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "产品尺寸对应的模型不存在", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, productSize.Id, 0)
logx.Error("product model is not found")
return nil, nil, nil, err
}
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取产品尺寸对应的模型失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, productSize.Id, 0)
logx.Error("failed to get product model:", err)
return nil, nil, nil, err
}
//获取模板
productTemplate, err = w.logic.svcCtx.AllModels.FsProductTemplateV2.FindOneCloudRenderByProductIdModelIdTemplateTag(w.logic.ctx, renderImageData.RenderData.ProductId, model3d.Id, renderImageData.RenderData.TemplateTag)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "找不到对应的模板", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, model3d.Id, productSize.Id, 0)
logx.Error("找不到对应的模板")
return nil, nil, nil, err
}
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取对应的模板失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, model3d.Id, productSize.Id, 0)
logx.Error("获取对应的模板失败:", err)
return nil, nil, nil, err
}
if *productTemplate.ElementModelId <= 0 {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "模板未开启云渲染", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3d.Id, productSize.Id, 0)
return nil, nil, nil, errors.New("模板未开启云渲染")
}
if productTemplate.TemplateInfo == nil || *productTemplate.TemplateInfo == "" {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "渲染模板的json设计信息是空的", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3d.Id, productSize.Id, 0)
return nil, nil, nil, errors.New("渲染模板的json设计信息是空的")
}
return
}
// 获取模板相关信息(不指定尺寸)
func (w *wsConnectItem) getProductRelateionInfoWithNoSizeId(renderImageData *websocket_data.RenderImageReqMsg) (productSize *gmodel.FsProductSize, productTemplate *gmodel.FsProductTemplateV2, model3d *gmodel.FsProductModel3d, err error) {
//指定尺寸(尺寸 -> 模型 ->模板)
//获取模板
productTemplate, err = w.logic.svcCtx.AllModels.FsProductTemplateV2.FindOneCloudRenderByProductIdTemplateTag(w.logic.ctx, renderImageData.RenderData.ProductId, renderImageData.RenderData.TemplateTag, "sort ASC")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "找不到对应开启云渲染模板", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
return nil, nil, nil, errors.New("找不到对应开启云渲染模板")
}
logx.Error(err)
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取对应开启云渲染模板失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0)
return nil, nil, nil, errors.New("获取对应开启云渲染模板失败")
}
//根据模板找到模型
model3d, err = w.logic.svcCtx.AllModels.FsProductModel3d.FindOne(w.logic.ctx, *productTemplate.ModelId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "找不到对应模型", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, 0, 0, 0)
return nil, nil, nil, errors.New("找不到对应模型")
}
logx.Error(err)
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取对应模型失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, 0, 0, 0)
return nil, nil, nil, errors.New("获取对应模型失败")
}
//根据模型id获取尺寸信息
productSize, err = w.logic.svcCtx.AllModels.FsProductSize.FindOne(w.logic.ctx, *model3d.SizeId)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "找不到对应尺寸", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3d.Id, 0, 0)
return nil, nil, nil, errors.New("找不到对应尺寸")
}
logx.Error(err)
w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取对应尺寸失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3d.Id, 0, 0)
return nil, nil, nil, errors.New("获取对应尺寸失败")
}
renderImageData.RenderData.ProductSizeId = productSize.Id
return
}
// 组装数据发送给unity
func (w *wsConnectItem) assembleRenderDataToUnity(taskId string, combineImage string, info websocket_data.RenderImageReqMsg, productTemplate *gmodel.FsProductTemplateV2, model3dInfo *gmodel.FsProductModel3d, element *gmodel.FsProductTemplateElement, productSize *gmodel.FsProductSize) (err error) {
//组装数据
refletion := -1
if element.Refletion != nil && *element.Refletion != "" {
refletion, err = strconv.Atoi(*element.Refletion)
if err != nil {
logx.Error("err refletion")
w.renderErrResponse(info.RenderId, info.RenderData.TemplateTag, taskId, "解析云渲染设置把Refletion字段值从字符串转数字失败", info.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
return err
}
}
//组装data数据
var mode map[string]interface{}
if element.Mode != nil && *element.Mode != "" {
if err = json.Unmarshal([]byte(*element.Mode), &mode); err != nil {
logx.Error("faile to parse element mode json:", err)
w.renderErrResponse(info.RenderId, info.RenderData.TemplateTag, taskId, "解析云渲染设置字段 mode 为map对象失败", info.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
return err
}
}
tempData := make([]map[string]interface{}, 0, 3)
if element.Base != nil && *element.Base != "" {
tempData = append(tempData, map[string]interface{}{
"name": "model",
"data": "0," + combineImage + "," + *element.Base + " " + fmt.Sprintf("%d", time.Now().UTC().UnixMilli()),
"type": "other",
"layer": "0",
"is_update": 1,
"mode": mode["model"],
})
}
if element.Shadow != nil && *element.Shadow != "" {
tempData = append(tempData, map[string]interface{}{
"name": "shadow",
"data": *element.Shadow,
"type": "other",
"layer": "0",
"is_update": 0,
"mode": mode["shadow"],
})
}
if element.ModelP != nil && *element.ModelP != "" {
tempData = append(tempData, map[string]interface{}{
"name": "model_P",
"data": "0," + *element.ModelP,
"type": "other",
"layer": "0",
"is_update": 0,
"mode": mode["model_P"],
})
}
result := []interface{}{
map[string]interface{}{
"light": *element.Light,
"refletion": refletion,
"scale": *element.Scale,
"sku_id": info.RenderData.ProductId,
"tid": *element.Title,
"rotation": *element.Rotation,
"filePath": "", //todo 文件路径,针对千人千面
"data": tempData,
},
}
//发送运行阶段消息(组装数据)
w.sendAssembleRenderDataStepResponseMessage(info.RenderId)
sendData := map[string]interface{}{
"id": taskId + " " + w.uniqueId + " " + info.RenderId + " " + fmt.Sprintf("%d", time.Now().UTC().UnixMilli()), //空格分开
"order_id": 0,
"user_id": info.RenderData.UserId,
"guest_id": info.RenderData.GuestId,
"sku_ids": []int64{info.RenderData.ProductId},
"tids": []string{*element.Title},
"data": result,
"is_thousand_face": 0,
"folder": "", //todo 千人千面需要使用
}
//请求unity接口
url := w.logic.svcCtx.Config.Unity.Host + "/api/render/queue/push"
header := make(map[string]string)
header["content-type"] = "application/json"
postData := map[string]interface{}{
"group": "unity3d",
"source": "home page",
"priority": 1,
"create_at": time.Now().UTC(),
"render_data": sendData,
}
postDataBytes, _ := json.Marshal(postData)
//unityRenderBeginTime := time.Now().UTC().UnixMilli()
_, err = curl.ApiCall(url, "POST", header, bytes.NewReader(postDataBytes), time.Second*10)
if err != nil {
w.renderErrResponse(info.RenderId, info.RenderData.TemplateTag, taskId, "请求unity接口失败", info.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3dInfo.Id, productSize.Id, *productTemplate.ElementModelId)
logx.Error("failed to send data to unity")
return err
}
//记录发送到unity时间
//w.modifyRenderTaskProperty(taskId, renderTask{unityRenderBeginTime: unityRenderBeginTime})
//发送运行阶段消息
w.sendRenderDataToUnityStepResponseMessage(info.RenderId)
logx.Info("发送到unity成功,刀版图:", combineImage /*, " 请求unity的数据:", string(postDataBytes)*/)
return nil
}
// 发送合图完毕阶段通知消息
func (w *wsConnectItem) sendCombineImageStepResponseMessage(renderId, combineImage string, sizeId, modelId, templateId, combineTime, uploadTime int64) {
combineTakesTime := "cache"
uploadCombineImageTakesTime := "cache"
if combineTime > 0 {
combineTakesTime = fmt.Sprintf("%dms", combineTime)
}
if uploadTime > 0 {
uploadCombineImageTakesTime = fmt.Sprintf("%dms", uploadTime)
}
w.sendToOutChan(w.respondDataFormat(constants.WEBSOCKET_COMBINE_IMAGE, websocket_data.CombineImageRspMsg{
RenderId: renderId,
CombineImage: combineImage,
SizeId: sizeId,
ModelId: modelId,
TemplateId: templateId,
CombineProcessTime: websocket_data.CombineProcessTime{
CombineTakesTime: combineTakesTime,
UploadCombineImageTakesTime: uploadCombineImageTakesTime,
},
}))
}
// 发送组装unity渲染数据完毕阶段通知消息
func (w *wsConnectItem) sendAssembleRenderDataStepResponseMessage(renderId string) {
w.sendToOutChan(w.respondDataFormat(constants.WEBSOCKET_ASSEMBLE_RENDER_DATA, websocket_data.ToUnityRspMsg{RenderId: renderId}))
}
// 发送组装unity渲染数据完毕阶段通知消息
func (w *wsConnectItem) sendRenderDataToUnityStepResponseMessage(renderId string) {
w.sendToOutChan(w.respondDataFormat(constants.WEBSOCKET_SEND_DATA_TO_UNITY, websocket_data.AssembleRenderDataRspMsg{RenderId: renderId}))
}
// 发送渲染最终结果数据到前端
func (w *wsConnectItem) sendRenderResultData(data websocket_data.RenderImageRspMsg) {
w.sendToOutChan(w.respondDataFormat(constants.WEBSOCKET_RENDER_IMAGE, data))
}
/*// 增加渲染任务
func (w *wsConnectItem) createRenderTask(taskId, renderId string) {
if taskId == "" {
logx.Error("task_id不能为空")
return
}
if renderId == "" {
logx.Error("render_id不能为空")
return
}
data := renderImageControlChanItem{
option: 1,
taskId: taskId,
renderId: renderId,
}
select {
case <-w.closeChan: //关闭
return
case w.extendRenderProperty.renderImageTaskCtlChan <- data:
return
case <-time.After(time.Second * 3):
return
}
}
// 渲染回调处理并删除渲染任务
func (w *wsConnectItem) deleteRenderTask(taskId, renderId, renderNotifyImageUrl string) {
if taskId == "" {
logx.Error("task_id不能为空")
return
}
data := renderImageControlChanItem{
option: 0,
taskId: taskId,
renderId: renderId,
renderNotifyImageUrl: renderNotifyImageUrl,
}
select {
case <-w.closeChan: //关闭
return
case w.extendRenderProperty.renderImageTaskCtlChan <- data:
return
case <-time.After(time.Second * 3):
return
}
}
// 修改任务属性(只有耗时属性可以更新)
func (w *wsConnectItem) modifyRenderTaskProperty(taskId string, property renderTask) {
if taskId == "" {
logx.Error("task_id不能为空")
return
}
//强制设为修改任务属性
data := renderImageControlChanItem{
option: 2,
taskId: taskId,
taskProperty: property,
}
select {
case <-w.closeChan: //关闭
return
case w.extendRenderProperty.renderImageTaskCtlChan <- data:
return
case <-time.After(time.Second * 3):
return
}
}*/
// 组装渲染任务id
func (w *wsConnectItem) genRenderTaskId(combineImage string, renderImageData websocket_data.RenderImageReqMsg, model3dInfo *gmodel.FsProductModel3d, productTemplate *gmodel.FsProductTemplateV2, element *gmodel.FsProductTemplateElement) string {
//生成任务id(需要把user_id,guest_id设为0)
incomeHashParam := renderImageData.RenderData
incomeHashParam.UserId = 0 //设为0(渲染跟用户id无关)
incomeHashParam.GuestId = 0 //设为0(渲染跟用户id无关)
incomeHashBytes, _ := json.Marshal(incomeHashParam)
modelHashStr := ""
templateHashStr := ""
if model3dInfo.ModelInfo != nil {
modelHashStr = *model3dInfo.ModelInfo
}
if productTemplate.TemplateInfo != nil {
templateHashStr = *productTemplate.TemplateInfo
}
elementHashBytes, _ := json.Marshal(element)
hashMap := map[string]interface{}{
"income_param": incomeHashBytes,
"model_info": modelHashStr,
"template_info": templateHashStr,
"material_image": *productTemplate.MaterialImg,
"render_element": elementHashBytes,
"combine_image": combineImage,
}
return hash.JsonHashKey(hashMap)
}
// 处理渲染任务的增加/删除/修改耗时属性任务map不能读写并发所以放在chan里面串行执行
/*func (w *wsConnectItem) operationRenderTask() {
defer func() {
if err := recover(); err != nil {
logx.Error("operation render task panic:", err)
}
}()
for {
select {
case <-w.closeChan:
return
case data := <-w.extendRenderProperty.renderImageTaskCtlChan:
switch data.option {
case 0: //渲染结果回调,删除任务
taskData, ok := w.extendRenderProperty.renderImageTask[data.taskId]
if !ok {
//发送到出口
w.sendRenderResultData(websocket_data.RenderImageRspMsg{
RenderId: data.renderId, //没有找到任务渲染id则用传进来的
Image: data.renderNotifyImageUrl,
RenderProcessTime: websocket_data.RenderProcessTime{
UnityRenderTakesTime: "unknown",
UploadUnityRenderImageTakesTime: "unknown",
},
})
continue
}
//删除任务
delete(w.extendRenderProperty.renderImageTask, data.taskId)
//存在任务,则发送渲染结果给前端
UnityRenderTakesTime := "cache"
uploadUnityRenderImageTakesTime := "cache"
//unity渲染时间
if taskData.unityRenderBeginTime > 0 && taskData.unityRenderEndTime > 0 {
UnityRenderTakesTime = fmt.Sprintf("%dms", taskData.unityRenderEndTime-taskData.unityRenderBeginTime)
}
//上传unity渲染图耗时
if taskData.uploadUnityRenderImageTakesTime > 0 {
uploadUnityRenderImageTakesTime = fmt.Sprintf("%dms", taskData.uploadUnityRenderImageTakesTime)
}
//发送到出口
w.sendRenderResultData(websocket_data.RenderImageRspMsg{
RenderId: taskData.renderId,
Image: data.renderNotifyImageUrl,
RenderProcessTime: websocket_data.RenderProcessTime{
UnityRenderTakesTime: UnityRenderTakesTime,
UploadUnityRenderImageTakesTime: uploadUnityRenderImageTakesTime,
},
})
case 1: //新增任务
w.extendRenderProperty.renderImageTask[data.taskId] = &renderTask{
renderId: data.renderId,
}
case 2: //修改任务属性
taskData, ok := w.extendRenderProperty.renderImageTask[data.taskId]
if !ok {
continue
}
//上传渲染结果图耗时
if data.taskProperty.uploadUnityRenderImageTakesTime != 0 {
taskData.uploadUnityRenderImageTakesTime = data.taskProperty.uploadUnityRenderImageTakesTime
}
//发送unity时间
if data.taskProperty.unityRenderBeginTime != 0 {
taskData.unityRenderBeginTime = data.taskProperty.unityRenderBeginTime
}
//收到unity返回的时间
if data.taskProperty.unityRenderEndTime != 0 {
taskData.unityRenderEndTime = data.taskProperty.unityRenderEndTime
}
}
}
}
}*/