poll() as a python '@staticmethod' was too limiting and didnt allow useful base class...
[blender.git] / release / scripts / io / netrender / operators.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 import bpy
20 import sys, os
21 import http, http.client, http.server, urllib, socket
22 import webbrowser
23
24 import netrender
25 from netrender.utils import *
26 import netrender.client as client
27 import netrender.model
28
29 class RENDER_OT_netslave_bake(bpy.types.Operator):
30     '''NEED DESCRIPTION'''
31     bl_idname = "render.netslavebake"
32     bl_label = "Bake all in file"
33
34     @classmethod
35     def poll(cls, context):
36         return True
37
38     def execute(self, context):
39         scene = context.scene
40         netsettings = scene.network_render
41
42         filename = bpy.data.filepath
43         path, name = os.path.split(filename)
44         root, ext = os.path.splitext(name)
45         default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
46         relative_path = os.sep + os.sep + "blendcache_" + root + os.sep
47
48         # Force all point cache next to the blend file
49         for object in bpy.data.objects:
50             for modifier in object.modifiers:
51                 if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
52                     modifier.settings.path = relative_path
53                     bpy.ops.fluid.bake({"active_object": object, "scene": scene})
54                 elif modifier.type == "CLOTH":
55                     modifier.point_cache.step = 1
56                     modifier.point_cache.disk_cache = True
57                     modifier.point_cache.external = False
58                 elif modifier.type == "SOFT_BODY":
59                     modifier.point_cache.step = 1
60                     modifier.point_cache.disk_cache = True
61                     modifier.point_cache.external = False
62                 elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
63                     modifier.domain_settings.point_cache_low.step = 1
64                     modifier.domain_settings.point_cache_low.disk_cache = True
65                     modifier.domain_settings.point_cache_low.external = False
66                     modifier.domain_settings.point_cache_high.step = 1
67                     modifier.domain_settings.point_cache_high.disk_cache = True
68                     modifier.domain_settings.point_cache_high.external = False
69
70             # particles modifier are stupid and don't contain data
71             # we have to go through the object property
72             for psys in object.particle_systems:
73                 psys.point_cache.step = 1
74                 psys.point_cache.disk_cache = True
75                 psys.point_cache.external = False
76                 psys.point_cache.filepath = relative_path
77
78         bpy.ops.ptcache.bake_all()
79
80         #bpy.ops.wm.save_mainfile(filepath = path + os.sep + root + "_baked.blend")
81
82         return {'FINISHED'}
83
84     def invoke(self, context, event):
85         return self.execute(context)
86
87 class RENDER_OT_netclientanim(bpy.types.Operator):
88     '''Start rendering an animation on network'''
89     bl_idname = "render.netclientanim"
90     bl_label = "Animation on network"
91
92     @classmethod
93     def poll(cls, context):
94         return True
95
96     def execute(self, context):
97         scene = context.scene
98         netsettings = scene.network_render
99
100         conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
101
102         if conn:
103             # Sending file
104             scene.network_render.job_id = client.clientSendJob(conn, scene, True)
105             conn.close()
106
107         bpy.ops.render.render('INVOKE_AREA', animation=True)
108
109         return {'FINISHED'}
110
111     def invoke(self, context, event):
112         return self.execute(context)
113
114 class RENDER_OT_netclientrun(bpy.types.Operator):
115     '''Start network rendering service'''
116     bl_idname = "render.netclientstart"
117     bl_label = "Start Service"
118
119     @classmethod
120     def poll(cls, context):
121         return True
122
123     def execute(self, context):
124         bpy.ops.render.render('INVOKE_AREA', animation=True)
125
126         return {'FINISHED'}
127
128     def invoke(self, context, event):
129         return self.execute(context)
130
131 class RENDER_OT_netclientsend(bpy.types.Operator):
132     '''Send Render Job to the Network'''
133     bl_idname = "render.netclientsend"
134     bl_label = "Send job"
135
136     @classmethod
137     def poll(cls, context):
138         return True
139
140     def execute(self, context):
141         scene = context.scene
142         netsettings = scene.network_render
143
144         try:
145             conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
146
147             if conn:
148                 # Sending file
149                 scene.network_render.job_id = client.clientSendJob(conn, scene, True)
150                 conn.close()
151                 self.report('INFO', "Job sent to master")
152         except Exception as err:
153             self.report('ERROR', str(err))
154
155
156         return {'FINISHED'}
157
158     def invoke(self, context, event):
159         return self.execute(context)
160
161 class RENDER_OT_netclientsendframe(bpy.types.Operator):
162     '''Send Render Job with current frame to the Network'''
163     bl_idname = "render.netclientsendframe"
164     bl_label = "Send current frame job"
165
166     @classmethod
167     def poll(cls, context):
168         return True
169
170     def execute(self, context):
171         scene = context.scene
172         netsettings = scene.network_render
173
174         try:
175             conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
176
177             if conn:
178                 # Sending file
179                 scene.network_render.job_id = client.clientSendJob(conn, scene, False)
180                 conn.close()
181                 self.report('INFO', "Job sent to master")
182         except Exception as err:
183             self.report('ERROR', str(err))
184
185
186         return {'FINISHED'}
187
188     def invoke(self, context, event):
189         return self.execute(context)
190
191 class RENDER_OT_netclientstatus(bpy.types.Operator):
192     '''Refresh the status of the current jobs'''
193     bl_idname = "render.netclientstatus"
194     bl_label = "Client Status"
195
196     @classmethod
197     def poll(cls, context):
198         return True
199
200     def execute(self, context):
201         netsettings = context.scene.network_render
202         conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
203
204         if conn:
205             conn.request("GET", "/status")
206
207             response = conn.getresponse()
208             print( response.status, response.reason )
209
210             jobs = (netrender.model.RenderJob.materialize(j) for j in eval(str(response.read(), encoding='utf8')))
211
212             while(len(netsettings.jobs) > 0):
213                 netsettings.jobs.remove(0)
214
215             netrender.jobs = []
216
217             for j in jobs:
218                 netrender.jobs.append(j)
219                 netsettings.jobs.add()
220                 job = netsettings.jobs[-1]
221
222                 j.results = j.framesStatus() # cache frame status
223
224                 job.name = j.name
225
226         return {'FINISHED'}
227
228     def invoke(self, context, event):
229         return self.execute(context)
230
231 class RENDER_OT_netclientblacklistslave(bpy.types.Operator):
232     '''Operator documentation text, will be used for the operator tooltip and python docs.'''
233     bl_idname = "render.netclientblacklistslave"
234     bl_label = "Client Blacklist Slave"
235
236     @classmethod
237     def poll(cls, context):
238         return True
239
240     def execute(self, context):
241         netsettings = context.scene.network_render
242
243         if netsettings.active_slave_index >= 0:
244
245             # deal with data
246             slave = netrender.slaves.pop(netsettings.active_slave_index)
247             netrender.blacklist.append(slave)
248
249             # deal with rna
250             netsettings.slaves_blacklist.add()
251             netsettings.slaves_blacklist[-1].name = slave.name
252
253             netsettings.slaves.remove(netsettings.active_slave_index)
254             netsettings.active_slave_index = -1
255
256         return {'FINISHED'}
257
258     def invoke(self, context, event):
259         return self.execute(context)
260
261 class RENDER_OT_netclientwhitelistslave(bpy.types.Operator):
262     '''Operator documentation text, will be used for the operator tooltip and python docs.'''
263     bl_idname = "render.netclientwhitelistslave"
264     bl_label = "Client Whitelist Slave"
265
266     @classmethod
267     def poll(cls, context):
268         return True
269
270     def execute(self, context):
271         netsettings = context.scene.network_render
272
273         if netsettings.active_blacklisted_slave_index >= 0:
274
275             # deal with data
276             slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index)
277             netrender.slaves.append(slave)
278
279             # deal with rna
280             netsettings.slaves.add()
281             netsettings.slaves[-1].name = slave.name
282
283             netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index)
284             netsettings.active_blacklisted_slave_index = -1
285
286         return {'FINISHED'}
287
288     def invoke(self, context, event):
289         return self.execute(context)
290
291
292 class RENDER_OT_netclientslaves(bpy.types.Operator):
293     '''Refresh status about available Render slaves'''
294     bl_idname = "render.netclientslaves"
295     bl_label = "Client Slaves"
296
297     @classmethod
298     def poll(cls, context):
299         return True
300
301     def execute(self, context):
302         netsettings = context.scene.network_render
303         conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
304
305         if conn:
306             conn.request("GET", "/slaves")
307
308             response = conn.getresponse()
309             print( response.status, response.reason )
310
311             slaves = (netrender.model.RenderSlave.materialize(s) for s in eval(str(response.read(), encoding='utf8')))
312
313             while(len(netsettings.slaves) > 0):
314                 netsettings.slaves.remove(0)
315
316             netrender.slaves = []
317
318             for s in slaves:
319                 for i in range(len(netrender.blacklist)):
320                     slave = netrender.blacklist[i]
321                     if slave.id == s.id:
322                         netrender.blacklist[i] = s
323                         netsettings.slaves_blacklist[i].name = s.name
324                         break
325                 else:
326                     netrender.slaves.append(s)
327
328                     netsettings.slaves.add()
329                     slave = netsettings.slaves[-1]
330                     slave.name = s.name
331
332         return {'FINISHED'}
333
334     def invoke(self, context, event):
335         return self.execute(context)
336
337 class RENDER_OT_netclientcancel(bpy.types.Operator):
338     '''Cancel the selected network rendering job.'''
339     bl_idname = "render.netclientcancel"
340     bl_label = "Client Cancel"
341
342     @classmethod
343     def poll(cls, context):
344         netsettings = context.scene.network_render
345         return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
346
347     def execute(self, context):
348         netsettings = context.scene.network_render
349         conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
350
351         if conn:
352             job = netrender.jobs[netsettings.active_job_index]
353
354             conn.request("POST", cancelURL(job.id))
355
356             response = conn.getresponse()
357             print( response.status, response.reason )
358
359             netsettings.jobs.remove(netsettings.active_job_index)
360
361         return {'FINISHED'}
362
363     def invoke(self, context, event):
364         return self.execute(context)
365
366 class RENDER_OT_netclientcancelall(bpy.types.Operator):
367     '''Cancel all running network rendering jobs.'''
368     bl_idname = "render.netclientcancelall"
369     bl_label = "Client Cancel All"
370
371     @classmethod
372     def poll(cls, context):
373         return True
374
375     def execute(self, context):
376         netsettings = context.scene.network_render
377         conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
378
379         if conn:
380             conn.request("POST", "/clear")
381
382             response = conn.getresponse()
383             print( response.status, response.reason )
384
385             while(len(netsettings.jobs) > 0):
386                 netsettings.jobs.remove(0)
387
388         return {'FINISHED'}
389
390     def invoke(self, context, event):
391         return self.execute(context)
392
393 class netclientdownload(bpy.types.Operator):
394     '''Download render results from the network'''
395     bl_idname = "render.netclientdownload"
396     bl_label = "Client Download"
397
398     @classmethod
399     def poll(cls, context):
400         netsettings = context.scene.network_render
401         return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
402
403     def execute(self, context):
404         netsettings = context.scene.network_render
405         rd = context.scene.render
406
407         conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
408
409         if conn:
410             job = netrender.jobs[netsettings.active_job_index]
411
412             for frame in job.frames:
413                 client.requestResult(conn, job.id, frame.number)
414                 response = conn.getresponse()
415
416                 if response.status != http.client.OK:
417                     print("missing", frame.number)
418                     continue
419
420                 print("got back", frame.number)
421
422                 f = open(netsettings.path + "%06d" % frame.number + ".exr", "wb")
423                 buf = response.read(1024)
424
425                 while buf:
426                     f.write(buf)
427                     buf = response.read(1024)
428
429                 f.close()
430
431             conn.close()
432
433         return {'FINISHED'}
434
435     def invoke(self, context, event):
436         return self.execute(context)
437
438 class netclientscan(bpy.types.Operator):
439     '''Listen on network for master server broadcasting its address and port.'''
440     bl_idname = "render.netclientscan"
441     bl_label = "Client Scan"
442
443     @classmethod
444     def poll(cls, context):
445         return True
446
447     def execute(self, context):
448         address, port = clientScan(self.report)
449
450         if address:
451             scene = context.scene
452             netsettings = scene.network_render
453             netsettings.server_address = address
454             netsettings.server_port = port
455
456         return {'FINISHED'}
457
458     def invoke(self, context, event):
459         return self.execute(context)
460
461 class netclientweb(bpy.types.Operator):
462     '''Open new window with information about running rendering jobs'''
463     bl_idname = "render.netclientweb"
464     bl_label = "Open Master Monitor"
465
466     @classmethod
467     def poll(cls, context):
468         netsettings = context.scene.network_render
469         return netsettings.server_address != "[default]"
470
471     def execute(self, context):
472         netsettings = context.scene.network_render
473
474
475         # open connection to make sure server exists
476         conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
477
478         if conn:
479             conn.close()
480
481             webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port))
482
483         return {'FINISHED'}
484
485     def invoke(self, context, event):
486         return self.execute(context)