Clarisse 5.0 SP8 SDK  5.0.5.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Using Clarisse/CNode Command Port

Table of Contents

This topic covers how to use Clarisse/CNode Command Port.

What is the Command Port?

Clarisse/CNode Command Port allows to control a Clarisse host remotely from any Python-enabled application that can reach the host through the network.

Using the Command Port you can control a Clarisse or CNode host from the same machine or from a completely different one such as a tablet or a even mobile phone... Basically you can use any connected device that is able to run Python.

Enabling the Command Port

By default, the command port is disabled on both Clarisse and CNode. To activate it in Clarisse, go to Edit > Preferences > Command Port and select Enabled. To change the port, just modify the port value.

Warning
Modifying the port number when a client is connected will automatically close the client connection.

To activate it in CNode, you need to launch CNode with the -enable_command_port argument.

Note
You can optionnally set the command port by specifying a port number to -enable_command_port argument.

Example:

cnode -interactive -enable_command_port 698

This opens an interactive (shell) session of CNode while enabling its command port on port 698.

Python Command Port Helper

Inside python folder in Clarisse binary directory there's a python helper class file named clarisse_net.py. This helper script allows you to communicate with Clarisse or CNode. It defines the ClarisseNet class which allows to connect and feed using python commands or script a remote host.

This helper file has no dependencies. It should run perfectly on any default python distribution on any platform.

Note
The Command Port protocol is extremely simple. The provided Python script can be used as reference to port the connection to another language. You can find the fully working and commented python class describing the protocol at the end of this page.

Connecting to the Command Port

Establishing a connection to a command port is handled by creating a ClarisseNet instance. You can also specify the host name and the port number as arguments to ClarisseNet constructor.

1 import clarisse_net as ix
2 rclarisse = ix.ClarisseNet()

In this example, the script connects to a Clarisse that runs on localhost using the port 55000.

There can be only one active connection to a specific remote host at the time. By calling ClarisseNet.close, you can close and free the connection. Only then another host can connect to the remote one.

Sending commands to a remote host

ClarisseNet class provides two different methods to send python commands:

Running a script remotely

ClarisseNet.run allows to send a block of python code as a string that will be executed on the remote host. This method doesn't return results.

ClarisseNet.run should be used if you want to run a script remotely. Such scripts are run as if they were run locally on the remote machine.

Examples:

1 rclarisse.run('print "Hello World!"')

This outputs Hello World! inside the remote host log window.

1 rclarisse.run('execfile("/opt/isotropix/clarisse/scripts/my_script.py")')

This executes on the remote host the script file located locally on /opt/isotropix/clarisse/scripts/my_script.py.

Evaluating a statement remotely

ClarisseNet.evaluate allows to send a block of python code as a string that will be evaluated on the remote host.

Contrarily to ClarisseNet.run, this method returns results as a string.

Example:

1 print rclarisse.evaluate('ix.create_object("sphere", "GeometrySphere")')

This prints project://scene/sphere in the client log.

Actually, ix.create_object returns the created OfObject. If the object path is returned, it's because return values are converted to string: in Python, the string convertion of an OfObject returns a Clarisse project path.

Note
ClarisseNet.evaluate only evaluates Python statements. You can't declare variables with an evaluate call. For example, rclarisse.evaluate('a = 5') will return a syntax error.

Remote Host Variable Lifetime

When you connect to a remote host, an execution context is immediately created. Any call or variable created are kept until the command port is closed. The command port gets automatically closed when the script ends on the client side.

If you connect to the command port using an interactive python shell, you'll need to call ClarisseNet.close to tell the remote host the execution of the script has ended. It will then cleanup its callstack. If you fail to do so, the callstack will be kept on the remote host until the end of the interactive python shell process.

1 >>> import clarisse_net as ix
2 >>> rclarisse = ix.ClarisseNet()
3 >>> rclarisse.run('obj = ix.create_object("sphere", "GeometrySphere")')
4 ...

The variable obj stays valid on the remote host until rclarisse.close() is called. So, nothing prevents you to do execute the following code:

1 ...
2 >>> rclarisse.run('print obj')
3 project://scene/sphere

Python Command Port Class Helper

The following code can be used as reference to write a Command Port helper in another language.

1 #
2 # Copyright (C) 2009 - 2013 Isotropix SAS. All rights reserved.
3 #
4 # The information in this file is provided for the exclusive use of
5 # the software licensees of Isotropix. Contents of this file may not
6 # be distributed, copied or duplicated in any form, in whole or in
7 # part, without the prior written permission of Isotropix SAS.
8 #
9 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
12 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
13 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
14 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
15 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
16 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
17 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
18 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
19 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 #
21 
22 ## @package clarisse_net
23 # This module defines to python Clarisse helpers for remote connection to Clarisse Command Port.
24 #
25 #
26 
27 import struct
28 import socket
29 
30 ## Remote Error handler
31 class ClarisseNetError(Exception):
32  def __init__(self, command, value):
33  self.value = value
34  self.command = command
35  def __str__(self):
36  return '%s\n%s' % (self.value, self.command)
37 
38  def get_error(self):
39  return '%s\n%s' % (self.value, self.command)
40 
41 ## Remote connection handler. By default, it will try to connect to localhost on port 55000
42 class ClarisseNet:
43  ## Internal class used as connection status enum
44  class Status:
45  Ok = 1
46  Error = -1
47 
48  ## Internal class used as execution mode enum
49  class Mode:
50  Script = 0
51  Statement = 1
52 
53  ## Default constructor. By default, tries to connect to localhost:55000
54  def __init__(self, host = "localhost", port=55000):
55  self.status = self.Status.Error
56  self.connect(host, port)
57 
58  ## Connect to the command port of a Clarisse/CNode host.
59  # @param host The name or the IP address of the remote Clarisse host.
60  # @param port The command port set on the remote Clarisse host.
61  def connect(self, host, port):
62  self.close()
63  self.status = self.Status.Error
64  try:
65  self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
66  self._socket.connect((host, port))
67  except:
68  raise ValueError('Failed to connect to ' + host + ':' + str(port))
69  self.status = self.Status.Ok
70 
71  ## Run the specifed python script on a Clarisse/CNode host.
72  # @param script A block of python code (as string) to execute on the remote Clarisse host.
73  # @return The method doesn't return result.
74  def run(self, script):
75  # Execute a block of python code on the remote host
76  self._send(script, self.Mode.Script)
77 
78  ## Evaluate the specifed python statemement on a Clarisse/CNode host.
79  # @param statement A block of python code (as string) to execute on the remote Clarisse host.
80  # @return The result is returned as string.
81  def evaluate(self, statement):
82  # Evaluate the input statement on the remote host and return the result as string.
83  return self._send(statement, self.Mode.Statement)
84 
85  ## Close the connection to the command port.
86  def close(self):
87  if (self.status == self.Status.Ok):
88  self._socket.close()
89 
90  ## Make sure the connection is properly closed
91  def __del__(self):
92  self.close()
93 
94  ## internal method used to communicate with the remove command port
95  def _send(self, command, mode):
96  if (self.status != self.Status.Ok):
97  raise RuntimeError('Not connected to Clarisse')
98  ## send the command
99  command_size = len(command) + 1
100  command_size = struct.pack("<I", command_size)
101  self._socket.send(command_size)
102  packet = str(mode) + command
103  self._socket.send(packet)
104  ## receive result size
105  result_size = self._socket.recv(4)
106  result_size = struct.unpack("<I", result_size)[0]
107  ## receive result
108  must_recv = True
109  result = ''
110  remaining = result_size
111  while (must_recv):
112  result += self._socket.recv(remaining)
113  remaining = result_size - len(result)
114  if remaining == 0: must_recv = False
115 
116  if (result[0] == '0'):
117  raise ClarisseNetError(result[1:], command)
118  else:
119  result = result[1:]
120  if (result == ''):
121  return None
122  else:
123  return result