Skip to content

DeadlineRPC

BaseDeadlineRPCJobManager

This is a base class for exposing commonly used deadline function on RPC

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
class BaseDeadlineRPCJobManager:
    """
    This is a base class for exposing commonly used deadline function on RPC
    """

    def __init__(self):
        """
        Constructor
        """
        # get the instance of the deadline plugin from the python globals
        self._deadline_plugin = self.__get_instance_from_globals()

        # Get the current running job
        self._job = self._deadline_plugin.GetJob()
        self._is_connected = False

        # Track all completed tasks
        self._completed_tasks = set()

    def connect(self):
        """
        First mode of contact to the rpc server. It is very critical the
        client calls this function first as it will let the Deadline process
        know a client has connected and to wait on the task to complete.
        Else, Deadline will assume the connection was never made and requeue
        the job after a few minutes
        :return: bool representing the connection
        """
        self._is_connected = True
        print("Server connection established!")
        return self._is_connected

    def is_connected(self):
        """
        Returns the connection status to a client
        :return:
        """
        return self._is_connected

    def is_task_complete(self, task_id):
        """
        Checks and returns if a task has been marked as complete
        :param task_id: job task id
        :return: return True/False if the task id is present
        """
        return task_id in self._completed_tasks

    @staticmethod
    def __get_instance_from_globals():
        """
        Get the instance of the Deadline plugin from the python globals.
        Since this class is executed in a thread, this was the best method to
        get the plugin instance to the class without pass it though several
        layers of abstraction
        :return:
        """
        import __main__

        try:
            return __main__.__deadline_plugin_instance__
        except AttributeError as err:
            raise RuntimeError(
                f"Could not get deadline plugin instance from globals. "
                f"\n\tError: {err}"
            )

    def get_job_id(self):
        """
        Returns the current JobID
        :return: Job ID
        """
        return self._job.JobId

    def get_task_frames(self):
        """
        Returns the frames rendered by ths task
        :return:
        """
        return [
            self._deadline_plugin.GetStartFrame(),
            self._deadline_plugin.GetEndFrame()
        ]

    def get_job_extra_info_key_value(self, name):
        """
        Returns the value of a key in the job extra info property
        :param name: Extra Info Key
        :return: Returns Extra Info Value
        """
        # This function is probably the most important function in the class.
        # This allows you to store different types of data and retrieve the
        # data from the other side. This is what makes the Unreal plugin a bit
        # more feature/task agnostic
        return self._job.GetJobExtraInfoKeyValue(name)

    def fail_render(self, message):
        """
        Fail a render job with a message
        :param message: Failure message
        """
        self._deadline_plugin.FailRender(message.strip("\n"))
        return True

    def set_status_message(self, message):
        """
        Sets the message on the job status
        :param message: Status Message
        """
        self._deadline_plugin.SetStatusMessage(message)
        return True

    def set_progress(self, progress):
        """
        Sets the job progress
        :param progress: job progress
        """
        self._deadline_plugin.SetProgress(progress)
        return True

    def log_warning(self, message):
        """
        Logs a warning message
        :param message: Log message
        """
        self._deadline_plugin.LogWarning(message)
        return True

    def log_info(self, message):
        """
        Logs an informational message
        :param message: Log message
        """
        self._deadline_plugin.LogInfo(message)
        return True

    def get_task_id(self):
        """
        Returns the current Task ID
        :return:
        """
        return self._deadline_plugin.GetCurrentTaskId()

    def get_job_user(self):
        """
        Return the job user
        :return:
        """
        return self._job.JobUserName

    def complete_task(self, task_id):
        """
        Marks a task as complete. This function should be called when a task
        is complete. This will allow the Deadline render taskl process to end
        and get the next render task. If this is not called, deadline will
        render the task indefinitely
        :param task_id: Task ID to mark as complete
        :return:
        """
        self._completed_tasks.add(task_id)
        return True

    def update_job_output_filenames(self, filenames):
        """
        Updates the file names for the current job
        :param list filenames: list of filenames
        """
        if not isinstance(filenames, list):
            filenames = list(filenames)

        self._deadline_plugin.LogInfo(
            "Setting job filenames: {filename}".format(
                filename=", ".join(filenames)
            )
        )

        # Set the file names on the job
        RepositoryUtils.UpdateJobOutputFileNames(self._job, filenames)

        # Make sure to save the settings just in case
        RepositoryUtils.SaveJob(self._job)

    def update_job_output_directories(self, directories):
        """
        Updates the output directories on job
        :param list directories: List of directories
        """
        if not isinstance(directories, list):
            directories = list(directories)

        self._deadline_plugin.LogInfo(
            "Setting job directories: {directories}".format(
                directories=", ".join(directories)
            )
        )

        # Set the directory on the job
        RepositoryUtils.SetJobOutputDirectories(self._job, directories)

        # Make sure to save the settings just in case
        RepositoryUtils.SaveJob(self._job)

    def check_path_mappings(self, paths):
        """
        Resolves any path mappings set on input path
        :param [str] paths: Path string with tokens
        :return: Resolved path mappings
        """
        if not isinstance(paths, list):
            paths = list(paths)

        # Deadline returns a System.String[] object here. Convert to a proper
        # list
        path_mapped_strings = RepositoryUtils.CheckPathMappingForMultiplePaths(
            paths,
            forceSeparator="/",
            verbose=False
        )

        return [str(path) for path in path_mapped_strings]

__get_instance_from_globals() staticmethod

Get the instance of the Deadline plugin from the python globals. Since this class is executed in a thread, this was the best method to get the plugin instance to the class without pass it though several layers of abstraction :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@staticmethod
def __get_instance_from_globals():
    """
    Get the instance of the Deadline plugin from the python globals.
    Since this class is executed in a thread, this was the best method to
    get the plugin instance to the class without pass it though several
    layers of abstraction
    :return:
    """
    import __main__

    try:
        return __main__.__deadline_plugin_instance__
    except AttributeError as err:
        raise RuntimeError(
            f"Could not get deadline plugin instance from globals. "
            f"\n\tError: {err}"
        )

__init__()

Constructor

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
14
15
16
17
18
19
20
21
22
23
24
25
26
def __init__(self):
    """
    Constructor
    """
    # get the instance of the deadline plugin from the python globals
    self._deadline_plugin = self.__get_instance_from_globals()

    # Get the current running job
    self._job = self._deadline_plugin.GetJob()
    self._is_connected = False

    # Track all completed tasks
    self._completed_tasks = set()

check_path_mappings(paths)

Resolves any path mappings set on input path :param [str] paths: Path string with tokens :return: Resolved path mappings

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
def check_path_mappings(self, paths):
    """
    Resolves any path mappings set on input path
    :param [str] paths: Path string with tokens
    :return: Resolved path mappings
    """
    if not isinstance(paths, list):
        paths = list(paths)

    # Deadline returns a System.String[] object here. Convert to a proper
    # list
    path_mapped_strings = RepositoryUtils.CheckPathMappingForMultiplePaths(
        paths,
        forceSeparator="/",
        verbose=False
    )

    return [str(path) for path in path_mapped_strings]

complete_task(task_id)

Marks a task as complete. This function should be called when a task is complete. This will allow the Deadline render taskl process to end and get the next render task. If this is not called, deadline will render the task indefinitely :param task_id: Task ID to mark as complete :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
158
159
160
161
162
163
164
165
166
167
168
def complete_task(self, task_id):
    """
    Marks a task as complete. This function should be called when a task
    is complete. This will allow the Deadline render taskl process to end
    and get the next render task. If this is not called, deadline will
    render the task indefinitely
    :param task_id: Task ID to mark as complete
    :return:
    """
    self._completed_tasks.add(task_id)
    return True

connect()

First mode of contact to the rpc server. It is very critical the client calls this function first as it will let the Deadline process know a client has connected and to wait on the task to complete. Else, Deadline will assume the connection was never made and requeue the job after a few minutes :return: bool representing the connection

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
28
29
30
31
32
33
34
35
36
37
38
39
def connect(self):
    """
    First mode of contact to the rpc server. It is very critical the
    client calls this function first as it will let the Deadline process
    know a client has connected and to wait on the task to complete.
    Else, Deadline will assume the connection was never made and requeue
    the job after a few minutes
    :return: bool representing the connection
    """
    self._is_connected = True
    print("Server connection established!")
    return self._is_connected

fail_render(message)

Fail a render job with a message :param message: Failure message

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
104
105
106
107
108
109
110
def fail_render(self, message):
    """
    Fail a render job with a message
    :param message: Failure message
    """
    self._deadline_plugin.FailRender(message.strip("\n"))
    return True

get_job_extra_info_key_value(name)

Returns the value of a key in the job extra info property :param name: Extra Info Key :return: Returns Extra Info Value

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def get_job_extra_info_key_value(self, name):
    """
    Returns the value of a key in the job extra info property
    :param name: Extra Info Key
    :return: Returns Extra Info Value
    """
    # This function is probably the most important function in the class.
    # This allows you to store different types of data and retrieve the
    # data from the other side. This is what makes the Unreal plugin a bit
    # more feature/task agnostic
    return self._job.GetJobExtraInfoKeyValue(name)

get_job_id()

Returns the current JobID :return: Job ID

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
75
76
77
78
79
80
def get_job_id(self):
    """
    Returns the current JobID
    :return: Job ID
    """
    return self._job.JobId

get_job_user()

Return the job user :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
151
152
153
154
155
156
def get_job_user(self):
    """
    Return the job user
    :return:
    """
    return self._job.JobUserName

get_task_frames()

Returns the frames rendered by ths task :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
82
83
84
85
86
87
88
89
90
def get_task_frames(self):
    """
    Returns the frames rendered by ths task
    :return:
    """
    return [
        self._deadline_plugin.GetStartFrame(),
        self._deadline_plugin.GetEndFrame()
    ]

get_task_id()

Returns the current Task ID :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
144
145
146
147
148
149
def get_task_id(self):
    """
    Returns the current Task ID
    :return:
    """
    return self._deadline_plugin.GetCurrentTaskId()

is_connected()

Returns the connection status to a client :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
41
42
43
44
45
46
def is_connected(self):
    """
    Returns the connection status to a client
    :return:
    """
    return self._is_connected

is_task_complete(task_id)

Checks and returns if a task has been marked as complete :param task_id: job task id :return: return True/False if the task id is present

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
48
49
50
51
52
53
54
def is_task_complete(self, task_id):
    """
    Checks and returns if a task has been marked as complete
    :param task_id: job task id
    :return: return True/False if the task id is present
    """
    return task_id in self._completed_tasks

log_info(message)

Logs an informational message :param message: Log message

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
136
137
138
139
140
141
142
def log_info(self, message):
    """
    Logs an informational message
    :param message: Log message
    """
    self._deadline_plugin.LogInfo(message)
    return True

log_warning(message)

Logs a warning message :param message: Log message

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
128
129
130
131
132
133
134
def log_warning(self, message):
    """
    Logs a warning message
    :param message: Log message
    """
    self._deadline_plugin.LogWarning(message)
    return True

set_progress(progress)

Sets the job progress :param progress: job progress

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
120
121
122
123
124
125
126
def set_progress(self, progress):
    """
    Sets the job progress
    :param progress: job progress
    """
    self._deadline_plugin.SetProgress(progress)
    return True

set_status_message(message)

Sets the message on the job status :param message: Status Message

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
112
113
114
115
116
117
118
def set_status_message(self, message):
    """
    Sets the message on the job status
    :param message: Status Message
    """
    self._deadline_plugin.SetStatusMessage(message)
    return True

update_job_output_directories(directories)

Updates the output directories on job :param list directories: List of directories

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def update_job_output_directories(self, directories):
    """
    Updates the output directories on job
    :param list directories: List of directories
    """
    if not isinstance(directories, list):
        directories = list(directories)

    self._deadline_plugin.LogInfo(
        "Setting job directories: {directories}".format(
            directories=", ".join(directories)
        )
    )

    # Set the directory on the job
    RepositoryUtils.SetJobOutputDirectories(self._job, directories)

    # Make sure to save the settings just in case
    RepositoryUtils.SaveJob(self._job)

update_job_output_filenames(filenames)

Updates the file names for the current job :param list filenames: list of filenames

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def update_job_output_filenames(self, filenames):
    """
    Updates the file names for the current job
    :param list filenames: list of filenames
    """
    if not isinstance(filenames, list):
        filenames = list(filenames)

    self._deadline_plugin.LogInfo(
        "Setting job filenames: {filename}".format(
            filename=", ".join(filenames)
        )
    )

    # Set the file names on the job
    RepositoryUtils.UpdateJobOutputFileNames(self._job, filenames)

    # Make sure to save the settings just in case
    RepositoryUtils.SaveJob(self._job)

DeadlineRPCServerManager

Bases: BaseRPCServerManager

RPC server manager class. This class is responsible for registering a server thread class and starting the thread. This can be a blocking or non-blocking thread

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
class DeadlineRPCServerManager(BaseRPCServerManager):
    """
    RPC server manager class. This class is responsible for registering a
    server thread class and starting the thread. This can be a blocking or
    non-blocking thread
    """

    def __init__(self, deadline_plugin, port):
        super(DeadlineRPCServerManager, self).__init__()
        self.name = "DeadlineRPCServer"
        self.port = port
        self.is_started = False
        self.__make_plugin_instance_global(deadline_plugin)

    @staticmethod
    def __make_plugin_instance_global(deadline_plugin_instance):
        """
        Puts an instance of the deadline plugin in the python globals. This
        allows the server thread to get the plugin instance without having
        the instance passthrough abstraction layers
        :param deadline_plugin_instance: Deadline plugin instance
        :return:
        """
        import __main__

        if not hasattr(__main__, "__deadline_plugin_instance__"):
            __main__.__deadline_plugin_instance__ = None

        __main__.__deadline_plugin_instance__ = deadline_plugin_instance

    def start(self, threaded=True):
        """
        Starts the server thread
        :param threaded: Run as threaded or blocking
        :return:
        """
        super(DeadlineRPCServerManager, self).start(threaded=threaded)
        self.is_started = True

    def client_connected(self):
        """
        Check if there is a client connected
        :return:
        """
        if self.server_thread:
            return self.server_thread.deadline_job_manager.is_connected()
        return False

    def get_temporary_client_proxy(self):
        """
        This returns client proxy and is not necessarily expected to be used
        for server communication but for mostly queries.
        NOTE: This behavior is implied
        :return: RPC client proxy
        """
        from ue_utils.rpc.client import RPCClient

        # Get the port the server is using
        server = self.get_server()
        _, server_port = server.socket.getsockname()
        return RPCClient(port=int(server_port)).proxy

    def shutdown(self):
        """
        Stops the server and shuts down the thread
        :return:
        """
        super(DeadlineRPCServerManager, self).shutdown()
        self.is_started = False

__make_plugin_instance_global(deadline_plugin_instance) staticmethod

Puts an instance of the deadline plugin in the python globals. This allows the server thread to get the plugin instance without having the instance passthrough abstraction layers :param deadline_plugin_instance: Deadline plugin instance :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
@staticmethod
def __make_plugin_instance_global(deadline_plugin_instance):
    """
    Puts an instance of the deadline plugin in the python globals. This
    allows the server thread to get the plugin instance without having
    the instance passthrough abstraction layers
    :param deadline_plugin_instance: Deadline plugin instance
    :return:
    """
    import __main__

    if not hasattr(__main__, "__deadline_plugin_instance__"):
        __main__.__deadline_plugin_instance__ = None

    __main__.__deadline_plugin_instance__ = deadline_plugin_instance

client_connected()

Check if there is a client connected :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
290
291
292
293
294
295
296
297
def client_connected(self):
    """
    Check if there is a client connected
    :return:
    """
    if self.server_thread:
        return self.server_thread.deadline_job_manager.is_connected()
    return False

get_temporary_client_proxy()

This returns client proxy and is not necessarily expected to be used for server communication but for mostly queries. NOTE: This behavior is implied :return: RPC client proxy

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
299
300
301
302
303
304
305
306
307
308
309
310
311
def get_temporary_client_proxy(self):
    """
    This returns client proxy and is not necessarily expected to be used
    for server communication but for mostly queries.
    NOTE: This behavior is implied
    :return: RPC client proxy
    """
    from ue_utils.rpc.client import RPCClient

    # Get the port the server is using
    server = self.get_server()
    _, server_port = server.socket.getsockname()
    return RPCClient(port=int(server_port)).proxy

shutdown()

Stops the server and shuts down the thread :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
313
314
315
316
317
318
319
def shutdown(self):
    """
    Stops the server and shuts down the thread
    :return:
    """
    super(DeadlineRPCServerManager, self).shutdown()
    self.is_started = False

start(threaded=True)

Starts the server thread :param threaded: Run as threaded or blocking :return:

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
281
282
283
284
285
286
287
288
def start(self, threaded=True):
    """
    Starts the server thread
    :param threaded: Run as threaded or blocking
    :return:
    """
    super(DeadlineRPCServerManager, self).start(threaded=threaded)
    self.is_started = True

DeadlineRPCServerThread

Bases: RPCServerThread

Deadline server thread

Source code in client/ayon_deadline/repository/custom/plugins/UnrealEngine5/DeadlineRPC.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
class DeadlineRPCServerThread(RPCServerThread):
    """
    Deadline server thread
    """

    deadline_job_manager = None

    def __init__(self, name, port):
        super(DeadlineRPCServerThread, self).__init__(name, port)
        if self.deadline_job_manager:
            self.deadline_job_manager = self.deadline_job_manager()
        else:
            self.deadline_job_manager = BaseDeadlineRPCJobManager()

        # Register our instance on the server
        self.server.register_instance(
            self.deadline_job_manager,
            allow_dotted_names=True
        )