diff options
Diffstat (limited to 'netrender/operators.py')
-rw-r--r-- | netrender/operators.py | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/netrender/operators.py b/netrender/operators.py new file mode 100644 index 00000000..563da1c5 --- /dev/null +++ b/netrender/operators.py @@ -0,0 +1,570 @@ +# ##### 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 bpy +import sys, os +import http, http.client, http.server, urllib, socket +import webbrowser +import json + +import netrender +from netrender.utils import * +import netrender.client as client +import netrender.model +import netrender.versioning as versioning + +class RENDER_OT_netslave_bake(bpy.types.Operator): + '''NEED DESCRIPTION''' + bl_idname = "render.netslavebake" + bl_label = "Bake all in file" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + scene = context.scene + netsettings = scene.network_render + + filename = bpy.data.filepath + path, name = os.path.split(filename) + root, ext = os.path.splitext(name) + default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that + relative_path = os.sep + os.sep + "blendcache_" + root + os.sep + + # Force all point cache next to the blend file + for object in bpy.data.objects: + for modifier in object.modifiers: + if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN": + modifier.settings.path = relative_path + bpy.ops.fluid.bake({"active_object": object, "scene": scene}) + elif modifier.type == "CLOTH": + modifier.point_cache.frame_step = 1 + modifier.point_cache.use_disk_cache = True + modifier.point_cache.use_external = False + elif modifier.type == "SOFT_BODY": + modifier.point_cache.frame_step = 1 + modifier.point_cache.use_disk_cache = True + modifier.point_cache.use_external = False + elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN": + modifier.domain_settings.point_cache.use_step = 1 + modifier.domain_settings.point_cache.use_disk_cache = True + modifier.domain_settings.point_cache.use_external = False + + # particles modifier are stupid and don't contain data + # we have to go through the object property + for psys in object.particle_systems: + psys.point_cache.use_step = 1 + psys.point_cache.use_disk_cache = True + psys.point_cache.use_external = False + psys.point_cache.filepath = relative_path + + bpy.ops.ptcache.bake_all() + + #bpy.ops.wm.save_mainfile(filepath = path + os.sep + root + "_baked.blend") + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class RENDER_OT_netclientanim(bpy.types.Operator): + '''Start rendering an animation on network''' + bl_idname = "render.netclientanim" + bl_label = "Animation on network" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + scene = context.scene + netsettings = scene.network_render + + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) + + if conn: + # Sending file + scene.network_render.job_id = client.clientSendJob(conn, scene, True) + conn.close() + + bpy.ops.render.render('INVOKE_AREA', animation=True) + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class RENDER_OT_netclientrun(bpy.types.Operator): + '''Start network rendering service''' + bl_idname = "render.netclientstart" + bl_label = "Start Service" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + bpy.ops.render.render('INVOKE_AREA', animation=True) + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class RENDER_OT_netclientsend(bpy.types.Operator): + '''Send Render Job to the Network''' + bl_idname = "render.netclientsend" + bl_label = "Send job" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + scene = context.scene + netsettings = scene.network_render + + try: + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) + + if conn: + # Sending file + scene.network_render.job_id = client.clientSendJob(conn, scene, True) + conn.close() + self.report('INFO', "Job sent to master") + except Exception as err: + self.report('ERROR', str(err)) + + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class RENDER_OT_netclientsendframe(bpy.types.Operator): + '''Send Render Job with current frame to the Network''' + bl_idname = "render.netclientsendframe" + bl_label = "Send current frame job" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + scene = context.scene + netsettings = scene.network_render + + try: + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) + + if conn: + # Sending file + scene.network_render.job_id = client.clientSendJob(conn, scene, False) + conn.close() + self.report('INFO', "Job sent to master") + except Exception as err: + self.report('ERROR', str(err)) + + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class RENDER_OT_netclientstatus(bpy.types.Operator): + '''Refresh the status of the current jobs''' + bl_idname = "render.netclientstatus" + bl_label = "Client Status" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + netsettings = context.scene.network_render + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) + + if conn: + conn.request("GET", "/status") + + response = conn.getresponse() + content = response.read() + print( response.status, response.reason ) + + jobs = (netrender.model.RenderJob.materialize(j) for j in json.loads(str(content, encoding='utf8'))) + + while(len(netsettings.jobs) > 0): + netsettings.jobs.remove(0) + + netrender.jobs = [] + + for j in jobs: + netrender.jobs.append(j) + netsettings.jobs.add() + job = netsettings.jobs[-1] + + j.results = j.framesStatus() # cache frame status + + job.name = j.name + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class RENDER_OT_netclientblacklistslave(bpy.types.Operator): + '''Operator documentation text, will be used for the operator tooltip and python docs.''' + bl_idname = "render.netclientblacklistslave" + bl_label = "Client Blacklist Slave" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + netsettings = context.scene.network_render + + if netsettings.active_slave_index >= 0: + + # deal with data + slave = netrender.slaves.pop(netsettings.active_slave_index) + netrender.blacklist.append(slave) + + # deal with rna + netsettings.slaves_blacklist.add() + netsettings.slaves_blacklist[-1].name = slave.name + + netsettings.slaves.remove(netsettings.active_slave_index) + netsettings.active_slave_index = -1 + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class RENDER_OT_netclientwhitelistslave(bpy.types.Operator): + '''Operator documentation text, will be used for the operator tooltip and python docs.''' + bl_idname = "render.netclientwhitelistslave" + bl_label = "Client Whitelist Slave" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + netsettings = context.scene.network_render + + if netsettings.active_blacklisted_slave_index >= 0: + + # deal with data + slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index) + netrender.slaves.append(slave) + + # deal with rna + netsettings.slaves.add() + netsettings.slaves[-1].name = slave.name + + netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index) + netsettings.active_blacklisted_slave_index = -1 + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + + +class RENDER_OT_netclientslaves(bpy.types.Operator): + '''Refresh status about available Render slaves''' + bl_idname = "render.netclientslaves" + bl_label = "Client Slaves" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + netsettings = context.scene.network_render + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) + + if conn: + conn.request("GET", "/slaves") + + response = conn.getresponse() + content = response.read() + print( response.status, response.reason ) + + slaves = (netrender.model.RenderSlave.materialize(s) for s in json.loads(str(content, encoding='utf8'))) + + while(len(netsettings.slaves_blacklist) > 0): + netsettings.slaves_blacklist.remove(0) + + while(len(netsettings.slaves) > 0): + netsettings.slaves.remove(0) + + netrender.slaves = [] + + for s in slaves: + for i in range(len(netrender.blacklist)): + slave = netrender.blacklist[i] + if slave.id == s.id: + netrender.blacklist[i] = s + netsettings.slaves_blacklist.add() + slave = netsettings.slaves_blacklist[-1] + slave.name = s.name + break + else: + netrender.slaves.append(s) + + netsettings.slaves.add() + slave = netsettings.slaves[-1] + slave.name = s.name + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class RENDER_OT_netclientcancel(bpy.types.Operator): + '''Cancel the selected network rendering job.''' + bl_idname = "render.netclientcancel" + bl_label = "Client Cancel" + + @classmethod + def poll(cls, context): + netsettings = context.scene.network_render + return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0 + + def execute(self, context): + netsettings = context.scene.network_render + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) + + if conn: + job = netrender.jobs[netsettings.active_job_index] + + conn.request("POST", cancelURL(job.id), json.dumps({'clear':False})) + + response = conn.getresponse() + response.read() + print( response.status, response.reason ) + + netsettings.jobs.remove(netsettings.active_job_index) + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class RENDER_OT_netclientcancelall(bpy.types.Operator): + '''Cancel all running network rendering jobs.''' + bl_idname = "render.netclientcancelall" + bl_label = "Client Cancel All" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + netsettings = context.scene.network_render + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) + + if conn: + conn.request("POST", "/clear", json.dumps({'clear':False})) + + response = conn.getresponse() + response.read() + print( response.status, response.reason ) + + while(len(netsettings.jobs) > 0): + netsettings.jobs.remove(0) + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class netclientdownload(bpy.types.Operator): + '''Download render results from the network''' + bl_idname = "render.netclientdownload" + bl_label = "Client Download" + + @classmethod + def poll(cls, context): + netsettings = context.scene.network_render + return netsettings.active_job_index >= 0 and len(netsettings.jobs) > netsettings.active_job_index + + def execute(self, context): + netsettings = context.scene.network_render + rd = context.scene.render + + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) + + if conn: + job_id = netrender.jobs[netsettings.active_job_index].id + + conn.request("GET", "/status", headers={"job-id":job_id}) + + response = conn.getresponse() + + if response.status != http.client.OK: + self.report('ERROR', "Job ID %i not defined on master" % job_id) + return {'ERROR'} + + content = response.read() + + job = netrender.model.RenderJob.materialize(json.loads(str(content, encoding='utf8'))) + + conn.close() + + finished_frames = [] + + nb_error = 0 + nb_missing = 0 + + for frame in job.frames: + if frame.status == DONE: + finished_frames.append(frame.number) + elif frame.status == ERROR: + nb_error += 1 + else: + nb_missing += 1 + + if not finished_frames: + return + + frame_ranges = [] + + first = None + last = None + + for i in range(len(finished_frames)): + current = finished_frames[i] + + if not first: + first = current + last = current + elif last + 1 == current: + last = current + + if last + 1 < current or i + 1 == len(finished_frames): + if first < last: + frame_ranges.append((first, last)) + else: + frame_ranges.append((first,)) + + first = current + last = current + + getResults(netsettings.server_address, netsettings.server_port, job_id, job.resolution[0], job.resolution[1], job.resolution[2], frame_ranges) + + if nb_error and nb_missing: + self.report('ERROR', "Results downloaded but skipped %i frames with errors and %i unfinished frames" % (nb_error, nb_missing)) + elif nb_error: + self.report('ERROR', "Results downloaded but skipped %i frames with errors" % nb_error) + elif nb_missing: + self.report('WARNING', "Results downloaded but skipped %i unfinished frames" % nb_missing) + else: + self.report('INFO', "All results downloaded") + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class netclientscan(bpy.types.Operator): + '''Listen on network for master server broadcasting its address and port.''' + bl_idname = "render.netclientscan" + bl_label = "Client Scan" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + address, port = clientScan(self.report) + + if address: + scene = context.scene + netsettings = scene.network_render + netsettings.server_address = address + netsettings.server_port = port + netrender.valid_address = True + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + +class netclientvcsguess(bpy.types.Operator): + '''Guess VCS setting for the current file''' + bl_idname = "render.netclientvcsguess" + bl_label = "VCS Guess" + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + netsettings = context.scene.network_render + + system = versioning.SYSTEMS.get(netsettings.vcs_system, None) + + if system: + wpath, name = os.path.split(os.path.abspath(bpy.data.filepath)) + + rpath = system.path(wpath) + revision = system.revision(wpath) + + netsettings.vcs_wpath = wpath + netsettings.vcs_rpath = rpath + netsettings.vcs_revision = revision + + + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) + + +class netclientweb(bpy.types.Operator): + '''Open new window with information about running rendering jobs''' + bl_idname = "render.netclientweb" + bl_label = "Open Master Monitor" + + @classmethod + def poll(cls, context): + netsettings = context.scene.network_render + return netsettings.server_address != "[default]" + + def execute(self, context): + netsettings = context.scene.network_render + + + # open connection to make sure server exists + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) + + if conn: + conn.close() + + webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port)) + + return {'FINISHED'} + + def invoke(self, context, event): + return self.execute(context) |