|
import queue as Queue
|
|
import json
|
|
import os
|
|
import socket
|
|
import time
|
|
|
|
from Deadline.Plugins import *
|
|
from Deadline.Scripting import *
|
|
from FranticX.Processes import *
|
|
from System import *
|
|
from System.Diagnostics import *
|
|
from System.IO import *
|
|
from System.Text import *
|
|
from System.Text.RegularExpressions import *
|
|
|
|
|
|
######################################################################
|
|
## This is the function that Deadline calls to get an instance of the
|
|
## main DeadlinePlugin class.
|
|
######################################################################
|
|
def GetDeadlinePlugin():
|
|
return IcarusVredPlugin()
|
|
|
|
|
|
######################################################################
|
|
## This is the function that Deadline calls when the plugin is no
|
|
## longer in use so that it can get cleaned up.
|
|
######################################################################
|
|
def CleanupDeadlinePlugin(deadlinePlugin):
|
|
deadlinePlugin.Cleanup()
|
|
|
|
|
|
# Used to deal with my IDE clean up messing up my jam.
|
|
try:
|
|
p = Plugins()
|
|
except:
|
|
pass
|
|
|
|
|
|
######################################################################
|
|
## This is the main DeadlinePlugin class for IcarusVredPlugin.
|
|
######################################################################
|
|
class IcarusVredPlugin(DeadlinePlugin):
|
|
## Variable to hold the Managed Process object.
|
|
Process = None
|
|
|
|
## Hook up the callbacks in the constructor.
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.InitializeProcessCallback += self.InitializeProcess
|
|
self.StartJobCallback += self.StartJob
|
|
self.RenderTasksCallback += self.RenderTasks
|
|
self.EndJobCallback += self.EndJob
|
|
self.mSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.mSocket.setblocking(True)
|
|
self.mReadSocket = True
|
|
self.mErrorSocketMessage = ""
|
|
self.mInitializedSockets = False
|
|
self.callback_queue = Queue.Queue()
|
|
self.readData = bytearray()
|
|
self.mFailedJob = False
|
|
self.mInitializeOK = False
|
|
self.mIcarusNodeStatus = ""
|
|
self.mIcarusNodeStatusCode = 0
|
|
self.renderTaskInProgress = False
|
|
self.mIcarusTaskId = ""
|
|
self.mJobId = ""
|
|
self.mJobPlugin = ""
|
|
self.mScenePath = ""
|
|
self.mTemplatePath = ""
|
|
self.mVredExePath = ""
|
|
self.mProjectId = ""
|
|
self.mRenderTaskDone = False
|
|
self.mForceRestartVred = False
|
|
|
|
## Clean up the plugin.
|
|
def Cleanup(self):
|
|
del self.InitializeProcessCallback
|
|
del self.StartJobCallback
|
|
del self.RenderTasksCallback
|
|
del self.EndJobCallback
|
|
|
|
# Clean up the managed process object.
|
|
if self.Process:
|
|
self.Process.Cleanup()
|
|
del self.Process
|
|
self.mInitializeOK = False
|
|
|
|
if self.mSocket:
|
|
if self.mRenderTaskDone:
|
|
self.sendMessage({"cmd": 190})
|
|
else:
|
|
self.sendMessage({"cmd": 195})
|
|
time.sleep(5)
|
|
self.mSocket.close()
|
|
|
|
self.mSocket = None
|
|
self.mReadSocket = False
|
|
self.mSocketWorker = None
|
|
self.mSocketInitializer = None
|
|
|
|
def getSocketData(self):
|
|
if self.mSocket is not None:
|
|
data = self.mSocket.recv(1024)
|
|
self.readData = self.readData + data
|
|
if len(data) == 0:
|
|
self.mErrorSocketMessage = "Lost connection to render node manager"
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
return self.getMessagesFromData(self.readData)
|
|
else:
|
|
self.mErrorSocketMessage = "Lost connection to render node manager"
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
return []
|
|
|
|
def getMessagesFromData(self, msgs):
|
|
okMesg = ""
|
|
msgList = []
|
|
m = str(msgs.decode())
|
|
c = 0
|
|
for letter in m:
|
|
c = c + 1
|
|
if letter != "|":
|
|
okMesg = okMesg + letter
|
|
else:
|
|
msgList.append(json.loads(okMesg))
|
|
okMesg = ""
|
|
if len(msgs) > 0:
|
|
if msgs[-1] != "|":
|
|
self.readData = okMesg.encode()
|
|
else:
|
|
self.readData = bytearray()
|
|
return msgList
|
|
|
|
def handleCreateSocket(self):
|
|
try:
|
|
self.mSocket.connect(("localhost", 35000))
|
|
except Exception as e:
|
|
self.mErrorSocketMessage = "Could not connect to Icarus Node, error : " + str(e)
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
# self.mSocketWorker.start()
|
|
|
|
def InitializeProcess(self):
|
|
# Set the plugin specific settings.
|
|
self.SingleFramesOnly = False
|
|
self.PluginType = PluginType.Advanced
|
|
# self.mSocketInitializer.start()
|
|
self.handleCreateSocket()
|
|
|
|
def remapPaths(self, value):
|
|
self.LogInfo("Remapping path before : " + value)
|
|
for a in RepositoryUtils.GetPathMappings():
|
|
#self.LogInfo("Remap Values : : " +a[0]+"----"+a[1])
|
|
value = value.replace(a[0], a[1])
|
|
value = value.replace("\\","/")
|
|
self.LogInfo("Remapping path after : " + value)
|
|
return value
|
|
|
|
def StartJob(self):
|
|
self.mJobInitializationBegin = True
|
|
job = self.GetJob()
|
|
|
|
#### First we get vred exe and validate that it exists
|
|
ver = self.GetPluginInfoEntryWithDefault("Version", "Pro_2021_0")
|
|
exe = self.GetConfigEntryWithDefault("Vred_Executables" + ver, "-1")
|
|
self.LogInfo("Searching for Exe : " + "Vred_Executables"+ver)
|
|
vredExe = FileUtils.SearchFileList(exe)
|
|
|
|
if vredExe == "":
|
|
self.mErrorSocketMessage = "Could not find vred Executable on This system : " + exe + " \n Version : " + ver
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
|
|
if exe == "-1":
|
|
self.mErrorSocketMessage = "Could not get Valid Vred executable from repository : " + ver
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
return
|
|
|
|
if not os.path.isfile(vredExe):
|
|
self.mErrorSocketMessage = "Vred appear to not exist on disk : " + str(vredExe)
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
|
|
#### Then we get scene
|
|
scene = self.GetPluginInfoEntryWithDefault("SceneFile", "-1")
|
|
scene = self.remapPaths(scene)
|
|
if scene == "-1":
|
|
self.mErrorSocketMessage = "Could not get Valid Vred Scene File"
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
if not os.path.isfile(scene):
|
|
self.mErrorSocketMessage = "Scene appear to not exist on disk : " + str(scene)
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
|
|
#### Then we get Template
|
|
templatePath = "-1"
|
|
for entry in self.GetAuxiliaryFilenames():
|
|
templatePath = str(entry)
|
|
break
|
|
|
|
templatePath = self.remapPaths(templatePath)
|
|
|
|
if templatePath == "-1":
|
|
self.mErrorSocketMessage = "Could not get valid template path"
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
|
|
if not os.path.isfile(templatePath):
|
|
# self.mErrorSocketMessage = "Template appear to not exist on disk : " + str(templatePath)
|
|
# self.mFailedJob = True
|
|
# self.FailRender(self.mErrorSocketMessage)
|
|
self.LogInfo("Template appear to not exist on disk : " + str(templatePath))
|
|
|
|
#### Then we send initial request to node with command list and details of job
|
|
self.mJobId = self.GetPluginInfoEntryWithDefault("icProjectId", str(job.JobId))
|
|
self.mJobPlugin = job.JobPlugin
|
|
self.mScenePath = scene
|
|
self.mTemplatePath = templatePath
|
|
|
|
self.mForceRestartVred = self.GetPluginInfoEntryWithDefault("icForceRestart", "False")
|
|
if self.mForceRestartVred == "False":
|
|
self.mForceRestartVred = False
|
|
else:
|
|
self.mForceRestartVred = True
|
|
|
|
self.sendMessage({
|
|
"cmd": 5,
|
|
"icJobType": "IcarusVred",
|
|
"icId": self.mJobId,
|
|
"icSender": "deadline",
|
|
"icDeadlinePlugin": self.mJobPlugin,
|
|
"icExtraFiles": [], # extraFiles,
|
|
"icAppExecutablePath": vredExe,
|
|
"icScenePath": scene,
|
|
"icTemplatePath": templatePath
|
|
})
|
|
jobStartupDone = False
|
|
c = 0
|
|
while not jobStartupDone:
|
|
c = c + 1
|
|
msg = ""
|
|
msgList = self.getSocketData()
|
|
for inx, data in enumerate(msgList):
|
|
cmd = data["cmd"]
|
|
if cmd == 6:
|
|
self.mErrorSocketMessage = "Render node refused this project, error : " + str(data["icIcarusNodeReply"])
|
|
self.mFailedJob = True
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
break
|
|
elif cmd == 7:
|
|
msgList.pop(inx)
|
|
self.LogInfo("----")
|
|
self.LogInfo("Icarus Node has accepted this project")
|
|
self.LogInfo("----")
|
|
if len(msgList) > 0:
|
|
self.handleMessages(msgList)
|
|
self.mInitializeOK = True
|
|
c = 500
|
|
jobStartupDone = True
|
|
break
|
|
else:
|
|
self.handleMessages([data])
|
|
|
|
def handleMessages(self, msgList):
|
|
for msg in msgList:
|
|
if "cmd" in msg:
|
|
cmd = msg["cmd"]
|
|
if cmd == 100:
|
|
for data in msg["icIcarusNodeReply"]:
|
|
self.LogStdout("Application Status Update : " + data)
|
|
elif cmd == 10:
|
|
self.mIcarusNodeStatus = msg["icIcarusNodeReply"]
|
|
self.mIcarusNodeStatusCode = msg["icIcarusNodeProjectCode"]
|
|
self.LogInfo("Icarus Node Status : " + self.mIcarusNodeStatus)
|
|
self.SetStatusMessage(self.mIcarusNodeStatus)
|
|
if self.mIcarusNodeStatusCode > 5000:
|
|
self.mFailedJob = True
|
|
self.mErrorSocketMessage = "Failed job, error :" + self.mIcarusNodeStatus
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
elif self.mIcarusNodeStatusCode == 1400:
|
|
self.mRenderTaskDone = True
|
|
elif cmd == 205 or cmd == 210 or cmd == 215:
|
|
self.mFailedJob = True
|
|
self.mErrorSocketMessage = "Failed job, error :" + msg["icIcarusNodeReply"]
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
elif cmd == 2000:
|
|
self.LogInfo(msg["icIcarusNodeReply"])
|
|
elif cmd == 1310:
|
|
self.SetProgress(float(msg["icIcarusNodeReply"]))
|
|
|
|
return None
|
|
|
|
def sendMessage(self, msg):
|
|
# Make generic consistent message
|
|
data = {
|
|
"cmd": 0,
|
|
"icJobType": "IcarusVred",
|
|
"icId": self.mJobId,
|
|
"icSender": "deadline",
|
|
"icLayerId": self.mIcarusTaskId,
|
|
"icDeadlinePlugin": self.mJobPlugin,
|
|
"icExtraFiles": [],
|
|
"icAppExecutablePath": self.mVredExePath,
|
|
"icScenePath": self.mScenePath,
|
|
"icTemplatePath": self.mTemplatePath
|
|
}
|
|
# Add extra/change existing
|
|
data.update(msg)
|
|
data = json.dumps(data) + "|"
|
|
data = data.encode()
|
|
|
|
try:
|
|
self.mSocket.send(data)
|
|
except Exception as e:
|
|
self.mFailedJob = True
|
|
self.mErrorSocketMessage = "Error communicating with Icarus Node : " + str(e)
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
|
|
## Called by Deadline for each task the Slave renders.
|
|
def RenderTasks(self):
|
|
if self.mFailedJob:
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
if self.mInitializeOK:
|
|
self.mRenderTaskDone = False
|
|
self.renderTaskInProgress = True
|
|
self.mIcarusTaskId = str(self.GetPluginInfoEntryWithDefault("icLayerId", "-1"))
|
|
if self.mIcarusTaskId == "-1":
|
|
self.FailRender("Invalid Icarus Task id")
|
|
if self.mIcarusNodeStatus == "":
|
|
pass
|
|
remaps = []
|
|
for a in RepositoryUtils.GetPathMappings():
|
|
remaps.append([a[0], a[1]])
|
|
self.sendMessage({"cmd": 200, "icLayerId": self.mIcarusTaskId, "icRemapPathMapping": remaps})
|
|
while self.renderTaskInProgress:
|
|
time.sleep(0.1)
|
|
self.handleMessages(self.getSocketData())
|
|
if self.mRenderTaskDone:
|
|
self.renderTaskInProgress = False
|
|
else:
|
|
self.FailRender(self.mErrorSocketMessage)
|
|
|
|
## Called by Deadline when the job ends.
|
|
def EndJob(self):
|
|
if self.mForceRestartVred:
|
|
self.mInitializeOK = False
|
|
self.sendMessage({"cmd": 195})
|
|
|
|
def printSpace(self):
|
|
self.LogInfo(" ")
|
|
self.LogInfo(" ")
|
|
self.LogInfo(" ")
|
|
self.LogInfo(" ")
|