diff --git a/release/scripts/io/netrender/master.py b/release/scripts/io/netrender/master.py index 404faa015ce..cf71e410cde 100644 --- a/release/scripts/io/netrender/master.py +++ b/release/scripts/io/netrender/master.py @@ -67,11 +67,13 @@ class MRenderJob(netrender.model.RenderJob): if self.type == netrender.model.JOB_PROCESS: self.chunks = 1 + # Force WAITING status on creation + self.status = JOB_WAITING + # special server properties self.last_update = 0 self.save_path = "" self.files = [MRenderFile(rfile.filepath, rfile.index, rfile.start, rfile.end) for rfile in job_info.files] - self.status = JOB_WAITING def save(self): if self.save_path: diff --git a/release/scripts/io/netrender/master_html.py b/release/scripts/io/netrender/master_html.py index d6e0867ad01..e556943609f 100644 --- a/release/scripts/io/netrender/master_html.py +++ b/release/scripts/io/netrender/master_html.py @@ -94,6 +94,7 @@ def get(handler): "priority", "usage", "wait", + "status", "length", "done", "dispatched", @@ -114,6 +115,7 @@ def get(handler): job.priority, "%0.1f%%" % (job.usage * 100), "%is" % int(time.time() - job.last_dispatched), + job.statusText(), len(job), results[DONE], results[DISPATCHED], diff --git a/release/scripts/io/netrender/model.py b/release/scripts/io/netrender/model.py index b731b32481e..f541b0c5e1a 100644 --- a/release/scripts/io/netrender/model.py +++ b/release/scripts/io/netrender/model.py @@ -132,6 +132,7 @@ class RenderJob: self.type = JOB_BLENDER self.name = "" self.category = "None" + self.status = JOB_WAITING self.files = [] self.chunks = 0 self.priority = 0 @@ -145,6 +146,7 @@ class RenderJob: self.type = job_info.type self.name = job_info.name self.category = job_info.category + self.status = job_info.status self.files = job_info.files self.chunks = job_info.chunks self.priority = job_info.priority @@ -172,6 +174,9 @@ class RenderJob: def countSlaves(self): return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED))) + def statusText(self): + return JOB_STATUS_TEXT[self.status] + def framesStatus(self): results = { QUEUED: 0, @@ -207,6 +212,7 @@ class RenderJob: "type": self.type, "name": self.name, "category": self.category, + "status": self.status, "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)], "frames": [f.serialize() for f in self.frames if not frames or f in frames], "chunks": self.chunks, @@ -226,6 +232,7 @@ class RenderJob: job.type = data["type"] job.name = data["name"] job.category = data["category"] + job.status = data["status"] job.files = [RenderFile.materialize(f) for f in data["files"]] job.frames = [RenderFrame.materialize(f) for f in data["frames"]] job.chunks = data["chunks"] @@ -245,7 +252,7 @@ class RenderFrame: self.command = command def statusText(self): - return STATUS_TEXT[self.status] + return FRAME_STATUS_TEXT[self.status] def serialize(self): return { diff --git a/release/scripts/io/netrender/operators.py b/release/scripts/io/netrender/operators.py index 87b3a4f180d..ec229fbb471 100644 --- a/release/scripts/io/netrender/operators.py +++ b/release/scripts/io/netrender/operators.py @@ -97,15 +97,12 @@ class RENDER_OT_netclientanim(bpy.types.Operator): scene = context.scene netsettings = scene.network_render - try: - conn = clientConnection(netsettings.server_address, netsettings.server_port) + 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() - except Exception as err: - self.report('ERROR', str(err)) - conn = None bpy.ops.screen.render('INVOKE_AREA', animation=True) @@ -128,12 +125,13 @@ class RENDER_OT_netclientsend(bpy.types.Operator): netsettings = scene.network_render try: - conn = clientConnection(netsettings.server_address, netsettings.server_port) + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) - # Sending file - scene.network_render.job_id = client.clientSendJob(conn, scene, True) - conn.close() - self.report('INFO', "Job sent to master") + 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)) @@ -154,7 +152,7 @@ class RENDER_OT_netclientstatus(bpy.types.Operator): def execute(self, context): netsettings = context.scene.network_render - conn = clientConnection(netsettings.server_address, netsettings.server_port) + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) if conn: conn.request("GET", "/status") @@ -255,7 +253,7 @@ class RENDER_OT_netclientslaves(bpy.types.Operator): def execute(self, context): netsettings = context.scene.network_render - conn = clientConnection(netsettings.server_address, netsettings.server_port) + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) if conn: conn.request("GET", "/slaves") @@ -301,7 +299,7 @@ class RENDER_OT_netclientcancel(bpy.types.Operator): def execute(self, context): netsettings = context.scene.network_render - conn = clientConnection(netsettings.server_address, netsettings.server_port) + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) if conn: job = netrender.jobs[netsettings.active_job_index] @@ -329,7 +327,7 @@ class RENDER_OT_netclientcancelall(bpy.types.Operator): def execute(self, context): netsettings = context.scene.network_render - conn = clientConnection(netsettings.server_address, netsettings.server_port) + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) if conn: conn.request("POST", "/clear") @@ -359,7 +357,7 @@ class netclientdownload(bpy.types.Operator): netsettings = context.scene.network_render rd = context.scene.render_data - conn = clientConnection(netsettings.server_address, netsettings.server_port) + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) if conn: job = netrender.jobs[netsettings.active_job_index] @@ -400,7 +398,7 @@ class netclientscan(bpy.types.Operator): return True def execute(self, context): - address, port = clientScan() + address, port = clientScan(self.report) if address: scene = context.scene @@ -427,7 +425,7 @@ class netclientweb(bpy.types.Operator): # open connection to make sure server exists - conn = clientConnection(netsettings.server_address, netsettings.server_port) + conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report) if conn: conn.close() diff --git a/release/scripts/io/netrender/ui.py b/release/scripts/io/netrender/ui.py index 040f9140a54..5ef02fad17c 100644 --- a/release/scripts/io/netrender/ui.py +++ b/release/scripts/io/netrender/ui.py @@ -85,7 +85,8 @@ class RENDER_PT_network_job(RenderButtonsPanel): def poll(self, context): scene = context.scene - return super().poll(context) and scene.network_render.mode == "RENDER_CLIENT" + return (super().poll(context) + and scene.network_render.mode == "RENDER_CLIENT") def draw(self, context): layout = self.layout @@ -98,9 +99,10 @@ class RENDER_PT_network_job(RenderButtonsPanel): split = layout.split() col = split.column() - col.operator("render.netclientanim", icon='RENDER_ANIMATION') - col.operator("render.netclientsend", icon='FILE_BLEND') - col.operator("render.netclientweb", icon='QUESTION') + if scene.network_render.server_address != "[default]": + col.operator("render.netclientanim", icon='RENDER_ANIMATION') + col.operator("render.netclientsend", icon='FILE_BLEND') + col.operator("render.netclientweb", icon='QUESTION') col.prop(scene.network_render, "job_name") col.prop(scene.network_render, "job_category") row = col.row() @@ -114,7 +116,9 @@ class RENDER_PT_network_slaves(RenderButtonsPanel): def poll(self, context): scene = context.scene - return super().poll(context) and scene.network_render.mode == "RENDER_CLIENT" + return (super().poll(context) + and scene.network_render.mode == "RENDER_CLIENT" + and scene.network_render.server_address != "[default]") def draw(self, context): layout = self.layout @@ -150,7 +154,9 @@ class RENDER_PT_network_slaves_blacklist(RenderButtonsPanel): def poll(self, context): scene = context.scene - return super().poll(context) and scene.network_render.mode == "RENDER_CLIENT" + return (super().poll(context) + and scene.network_render.mode == "RENDER_CLIENT" + and scene.network_render.server_address != "[default]") def draw(self, context): layout = self.layout @@ -185,7 +191,9 @@ class RENDER_PT_network_jobs(RenderButtonsPanel): def poll(self, context): scene = context.scene - return super().poll(context) and scene.network_render.mode == "RENDER_CLIENT" + return (super().poll(context) + and scene.network_render.mode == "RENDER_CLIENT" + and scene.network_render.server_address != "[default]") def draw(self, context): layout = self.layout diff --git a/release/scripts/io/netrender/utils.py b/release/scripts/io/netrender/utils.py index 0a10b9b48ba..3da15a33b65 100644 --- a/release/scripts/io/netrender/utils.py +++ b/release/scripts/io/netrender/utils.py @@ -28,7 +28,7 @@ try: except: bpy = None -VERSION = b"0.7" +VERSION = bytes("0.7", encoding='utf8') # Jobs status JOB_WAITING = 0 # before all data has been entered @@ -36,13 +36,21 @@ 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" + } + + # Frames status QUEUED = 0 DISPATCHED = 1 DONE = 2 ERROR = 3 -STATUS_TEXT = { +FRAME_STATUS_TEXT = { QUEUED: "Queued", DISPATCHED: "Dispatched", DONE: "Done", @@ -57,40 +65,66 @@ def rnaOperator(rna_op): if bpy: bpy.ops.add(rna_op) return rna_op -def clientScan(): - try: - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - s.settimeout(30) +def reporting(report, message, errorType = None): + if errorType: + t = 'ERROR' + else: + t = 'INFO' + + if report: + report(t, message) + return None + elif errorType: + raise errorType(message) + else: + return None - s.bind(('', 8000)) - - buf, address = s.recvfrom(64) - - print("received:", buf) - - address = address[0] - port = int(str(buf, encoding='utf8')) - return (address, port) - except socket.timeout: - print("no server info") - return ("", 8000) # return default values +def clientScan(report = None): + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + s.settimeout(30) -def clientConnection(address, port): - if address == "[default]": + s.bind(('', 8000)) + + buf, address = s.recvfrom(64) + + address = address[0] + port = int(str(buf, encoding='utf8')) + + reporting(report, "Master server found") + + return (address, port) + except socket.timeout: + reporting(report, "No master server on network", IOError) + + return ("", 8000) # return default values + +def clientConnection(address, port, report = None): + if address == "[default]": # calling operator from python is fucked, scene isn't in context # if bpy: # bpy.ops.render.netclientscan() # else: - address, port = clientScan() - - conn = http.client.HTTPConnection(address, port) - - if clientVerifyVersion(conn): - return conn - else: - conn.close() - raise IOError("Wrong version on master") + address, port = clientScan() + if address == "": + return None + + try: + conn = http.client.HTTPConnection(address, port) + + if conn: + if clientVerifyVersion(conn): + return conn + else: + conn.close() + reporting(report, "Incorrect master version", ValueError) + except Exception as err: + if report: + report('ERROR', str(err)) + return None + else: + raise def clientVerifyVersion(conn): conn.request("GET", "/version") @@ -104,7 +138,7 @@ def clientVerifyVersion(conn): if server_version != VERSION: print("Incorrect server version!") - print("expected", VERSION, "received", server_version) + print("expected", str(VERSION, encoding='utf8'), "received", str(server_version, encoding='utf8')) return False return True