3
11
This repository has been archived on 2024-05-16. You can view files and clone it, but cannot push or open issues or pull requests.
blender-addons-contrib/netrender/model.py

500 lines
14 KiB
Python

# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import netrender.versioning as versioning
from netrender.utils import *
import time
# Jobs status
JOB_WAITING = 0 # before all data has been entered
JOB_PAUSED = 1 # paused by user
JOB_FINISHED = 2 # finished rendering
JOB_QUEUED = 3 # ready to be dispatched
JOB_STATUS_TEXT = {
JOB_WAITING: "Waiting",
JOB_PAUSED: "Paused",
JOB_FINISHED: "Finished",
JOB_QUEUED: "Queued"
}
JOB_TRANSITION_STARTED = "Started"
JOB_TRANSITION_PAUSED = "Paused"
JOB_TRANSITION_RESUMED = "Resumed"
JOB_TRANSITION_FINISHED = "Finished"
JOB_TRANSITION_RESTARTED = "Restarted"
JOB_TRANSITIONS = {
(JOB_WAITING, JOB_QUEUED) : JOB_TRANSITION_STARTED,
(JOB_QUEUED, JOB_PAUSED) : JOB_TRANSITION_PAUSED,
(JOB_PAUSED, JOB_QUEUED) : JOB_TRANSITION_RESUMED,
(JOB_QUEUED, JOB_FINISHED) : JOB_TRANSITION_FINISHED,
(JOB_FINISHED, JOB_QUEUED) : JOB_TRANSITION_RESTARTED
}
# Job types (depends on the dependency type)
JOB_BLENDER = 1
JOB_PROCESS = 2
JOB_VCS = 3
JOB_TYPES = {
JOB_BLENDER: "Blender",
JOB_PROCESS: "Process",
JOB_VCS: "Versioned",
}
JOB_SUB_RENDER = 1
JOB_SUB_BAKING = 2
# Job subtypes
JOB_SUBTYPES = {
JOB_SUB_RENDER: "Render",
JOB_SUB_BAKING: "Baking",
}
# Frames status
FRAME_QUEUED = 0
FRAME_DISPATCHED = 1
FRAME_DONE = 2
FRAME_ERROR = 3
FRAME_STATUS_TEXT = {
FRAME_QUEUED: "Queued",
FRAME_DISPATCHED: "Dispatched",
FRAME_DONE: "Done",
FRAME_ERROR: "Error"
}
# Tags
TAG_BAKING = "baking"
TAG_RENDER = "render"
TAG_ALL = set((TAG_BAKING, TAG_RENDER))
class LogFile:
def __init__(self, job_id = 0, slave_id = 0, frames = []):
self.job_id = job_id
self.slave_id = slave_id
self.frames = frames
def serialize(self):
return {
"job_id": self.job_id,
"slave_id": self.slave_id,
"frames": self.frames
}
@staticmethod
def materialize(data):
if not data:
return None
logfile = LogFile()
logfile.job_id = data["job_id"]
logfile.slave_id = data["slave_id"]
logfile.frames = data["frames"]
return logfile
class RenderSlave:
_slave_map = {}
def __init__(self, info = None):
self.id = ""
self.total_done = 0
self.total_error = 0
self.last_seen = 0.0
if info:
self.name = info.name
self.address = info.address
self.stats = info.stats
self.tags = info.tags
else:
self.name = ""
self.address = ("",0)
self.stats = ""
self.tags = set()
def serialize(self):
return {
"id": self.id,
"name": self.name,
"address": self.address,
"stats": self.stats,
"total_done": self.total_done,
"total_error": self.total_error,
"last_seen": self.last_seen,
"tags": tuple(self.tags)
}
@staticmethod
def materialize(data, cache = True):
if not data:
return None
slave_id = data["id"]
if cache and slave_id in RenderSlave._slave_map:
return RenderSlave._slave_map[slave_id]
slave = RenderSlave()
slave.id = slave_id
slave.name = data["name"]
slave.address = data["address"]
slave.stats = data["stats"]
slave.total_done = data["total_done"]
slave.total_error = data["total_error"]
slave.last_seen = data["last_seen"]
slave.tags = set(data["tags"])
if cache:
RenderSlave._slave_map[slave_id] = slave
return slave
class VersioningInfo:
def __init__(self, info = None):
self._system = None
self.wpath = ""
self.rpath = ""
self.revision = ""
@property
def system(self):
return self._system
@system.setter
def system(self, value):
self._system = versioning.SYSTEMS[value]
def update(self):
self.system.update(self)
def serialize(self):
return {
"wpath": self.wpath,
"rpath": self.rpath,
"revision": self.revision,
"system": self.system.name
}
@staticmethod
def generate(system, path):
vs = VersioningInfo()
vs.wpath = path
vs.system = system
vs.rpath = vs.system.path(path)
vs.revision = vs.system.revision(path)
return vs
@staticmethod
def materialize(data):
if not data:
return None
vs = VersioningInfo()
vs.wpath = data["wpath"]
vs.rpath = data["rpath"]
vs.revision = data["revision"]
vs.system = data["system"]
return vs
class RenderFile:
def __init__(self, filepath = "", index = 0, start = -1, end = -1, signature = 0):
self.filepath = filepath
self.original_path = filepath
self.signature = signature
self.index = index
self.start = start
self.end = end
self.force = False
def serialize(self):
return {
"filepath": self.filepath,
"original_path": self.original_path,
"index": self.index,
"start": self.start,
"end": self.end,
"signature": self.signature,
"force": self.force
}
@staticmethod
def materialize(data):
if not data:
return None
rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"], data["signature"])
rfile.original_path = data["original_path"]
rfile.force = data["force"]
return rfile
class RenderJob:
def __init__(self, info = None):
self.id = ""
self.resolution = None
self.usage = 0.0
self.last_dispatched = 0.0
self.frames = []
self.transitions = []
self._status = None
if info:
self.type = info.type
self.subtype = info.subtype
self.name = info.name
self.category = info.category
self.tags = info.tags
self.status = info.status
self.files = info.files
self.chunks = info.chunks
self.priority = info.priority
self.blacklist = info.blacklist
self.version_info = info.version_info
self.render = info.render
else:
self.type = JOB_BLENDER
self.subtype = JOB_SUB_RENDER
self.name = ""
self.category = "None"
self.tags = set()
self.status = JOB_WAITING
self.files = []
self.chunks = 0
self.priority = 0
self.blacklist = []
self.version_info = None
self.render = "BLENDER_RENDER"
@property
def status(self):
"""Status of the job (waiting, paused, finished or queued)"""
return self._status
@status.setter
def status(self, value):
transition = JOB_TRANSITIONS.get((self.status, value), None)
if transition:
self.transitions.append((transition, time.time()))
self._status = value
@property
def time_started(self):
started_time = None
for transition, time_value in self.transitions:
if transition == JOB_TRANSITION_STARTED:
started_time = time_value
break
return started_time
@property
def time_finished(self):
finished_time = None
if self.status == JOB_FINISHED:
for transition, time_value in self.transitions:
if transition == JOB_TRANSITION_FINISHED:
finished_time = time_value
return finished_time
def hasRenderResult(self):
return self.subtype == JOB_SUB_RENDER
def rendersWithBlender(self):
return self.subtype == JOB_SUB_RENDER
def addFile(self, file_path, start=-1, end=-1, signed=True):
def isFileInFrames():
if start == end == -1:
return True
for rframe in self.frames:
if start <= rframe.number<= end:
return True
return False
if isFileInFrames():
if signed:
signature = hashFile(file_path)
else:
signature = None
self.files.append(RenderFile(file_path, len(self.files), start, end, signature))
def addFrame(self, frame_number, command = ""):
frame = RenderFrame(frame_number, command)
self.frames.append(frame)
return frame
def __len__(self):
return len(self.frames)
def countFrames(self, status=FRAME_QUEUED):
total = 0
for f in self.frames:
if f.status == status:
total += 1
return total
def countSlaves(self):
return len(set((frame.slave for frame in self.frames if frame.status == FRAME_DISPATCHED)))
def statusText(self):
return JOB_STATUS_TEXT[self.status]
def framesStatus(self):
results = {
FRAME_QUEUED: 0,
FRAME_DISPATCHED: 0,
FRAME_DONE: 0,
FRAME_ERROR: 0
}
for frame in self.frames:
results[frame.status] += 1
return results
def __contains__(self, frame_number):
for f in self.frames:
if f.number == frame_number:
return True
else:
return False
def __getitem__(self, frame_number):
for f in self.frames:
if f.number == frame_number:
return f
else:
return None
def serialize(self, frames = None,withFiles=True,withFrames=True):
min_frame = min((f.number for f in frames)) if frames else -1
max_frame = max((f.number for f in frames)) if frames else -1
data={
"id": self.id,
"type": self.type,
"subtype": self.subtype,
"name": self.name,
"category": self.category,
"tags": tuple(self.tags),
"status": self.status,
"transitions": self.transitions,
"chunks": self.chunks,
"priority": self.priority,
"usage": self.usage,
"blacklist": self.blacklist,
"last_dispatched": self.last_dispatched,
"version_info": self.version_info.serialize() if self.version_info else None,
"resolution": self.resolution,
"render": self.render
}
if (withFiles):
data["files"]=[f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)]
if (withFrames):
data["frames"]=[f.serialize() for f in self.frames if not frames or f in frames]
return data
@staticmethod
def materialize(data):
if not data:
return None
job = RenderJob()
job.id = data["id"]
job.type = data["type"]
job.subtype = data["subtype"]
job.name = data["name"]
job.category = data["category"]
job.tags = set(data["tags"])
job.status = data["status"]
job.transitions = data["transitions"]
job.files = [RenderFile.materialize(f) for f in data["files"]]
job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
job.chunks = data["chunks"]
job.priority = data["priority"]
job.usage = data["usage"]
job.blacklist = data["blacklist"]
job.last_dispatched = data["last_dispatched"]
job.resolution = data["resolution"]
job.render=data["render"]
version_info = data.get("version_info", None)
if version_info:
job.version_info = VersioningInfo.materialize(version_info)
return job
class RenderFrame:
def __init__(self, number = 0, command = ""):
self.number = number
self.time = 0
self.status = FRAME_QUEUED
self.slave = None
self.command = command
self.results = [] # List of filename of result files associated with this frame
def statusText(self):
return FRAME_STATUS_TEXT[self.status]
def serialize(self):
return {
"number": self.number,
"time": self.time,
"status": self.status,
"slave": None if not self.slave else self.slave.serialize(),
"command": self.command,
"results": self.results
}
@staticmethod
def materialize(data):
if not data:
return None
frame = RenderFrame()
frame.number = data["number"]
frame.time = data["time"]
frame.status = data["status"]
frame.slave = RenderSlave.materialize(data["slave"])
frame.command = data["command"]
frame.results = data["results"]
return frame