From 94d34ba3256e148eaf86c03a5b27a84abf99182f Mon Sep 17 00:00:00 2001 From: Rafael Bailon-Ruiz Date: Wed, 5 Sep 2018 14:58:52 +0200 Subject: [PATCH 1/8] [services] Add a service to replace the texture of an object --- src/morse/core/blenderapi.py | 5 +++ src/morse/services/supervision_services.py | 45 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/morse/core/blenderapi.py b/src/morse/core/blenderapi.py index 920fb2379..df88e126b 100644 --- a/src/morse/core/blenderapi.py +++ b/src/morse/core/blenderapi.py @@ -184,6 +184,11 @@ def constraints(): else: return None +def app(): + if not fake: + return bge.app + else: + return None def texture(): if not fake: diff --git a/src/morse/services/supervision_services.py b/src/morse/services/supervision_services.py index 69a8c9fef..89040eb51 100644 --- a/src/morse/services/supervision_services.py +++ b/src/morse/services/supervision_services.py @@ -5,6 +5,7 @@ from morse.core.abstractobject import AbstractObject from morse.core.exceptions import * import json +import os.path def get_structured_children_of(blender_object): """ Returns a nested dictionary of the given objects children, recursively. @@ -44,6 +45,45 @@ def get_obj_by_name(name): "Object '%s' does not appear in the scene." % name) return scene.objects[name] +def load_texture(obj, path, mat_id=0, tex_id=0): + """Load a picture into a texture + + :param obj: blender object + :param string path: path to an image file + :param int mat_id: material ID (default: 0) + :param int tex_id: texture ID (default: 0) + """ + # Based on https://blender.stackexchange.com/a/79277 + + # The texture has to be stored in a place associated with other game data + # so we store it in a game property. This name includes the mat ID's and + # tex ID's so that a single object with a complex setup does not have the + # textures overwrite each other. + # If a bge.texture object already exists for this object/mat_id/tex_id, then + # we retrieve that. + + if not blenderapi.app().has_texture_ffmpeg: + raise MorseRPCInvokationError("Blender hasn't been compiled with ffmpeg support") + + prop_name = 'SHOW_PICTURE{}:{}'.format(mat_id, tex_id) + if prop_name not in obj: + tex = blenderapi.texture().Texture(obj, mat_id, tex_id) + obj[prop_name] = tex + else: + tex = obj[prop_name] + + # Load the image from the path + raw = blenderapi.texture().ImageFFmpeg(os.path.join(path)) + + # Check to see that it loaded + if raw.status == blenderapi.texture().SOURCE_ERROR: + # Error in loading image + raise MorseRPCInvokationError("Unable to load image at {}".format(path)) + + # Assign the new image to the texture and update the texture. + tex.source = raw + tex.refresh(True) + class Supervision(AbstractObject): def __init__(self): AbstractObject.__init__(self) @@ -345,5 +385,10 @@ def set_object_position(self, object_name, position, orientation = None): if orientation: blender_object.worldOrientation = orientation + @service + def set_texture(self, object_name, texture_path): + blender_object = get_obj_by_name(object_name) + load_texture(blender_object, texture_path) + def action(self): pass From 942b911510662403c8fe2091df10fa0ab6c5e0d1 Mon Sep 17 00:00:00 2001 From: Pierrick Koch Date: Thu, 27 Sep 2018 13:58:59 +0200 Subject: [PATCH 2/8] [sensors] Fix VLP row distribution list --- src/morse/builder/sensors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/morse/builder/sensors.py b/src/morse/builder/sensors.py index 475be34cf..f8c541e91 100644 --- a/src/morse/builder/sensors.py +++ b/src/morse/builder/sensors.py @@ -481,13 +481,13 @@ def __init__(self, name=None): Hence, the keep_list is a reasonnably fair approximation of the real angle of VLP16 """ cam.properties(cam_width=512, cam_height=256, cam_focal = 27.7, - keep_list=str([0, 17, 34, 51, 68, 85, 102, 120, 137, 154, 171, 188, 205, 222, 239, 256])) + keep_list=str([1, 17, 34, 51, 68, 85, 102, 120, 137, 154, 171, 188, 205, 222, 239, 255])) cam.rotate(x=(i-1) * math.pi / 3) cam.frequency(10) cam.hide_mesh() self.append(cam) - self.append_meshes(['VelodyneMesh']) + # self.append_meshes(['VelodyneMesh']) self.set_master(1) def after_renaming(self): From eb025fe625c2ae1aabfd7d4661811f05176f788a Mon Sep 17 00:00:00 2001 From: El Jawad Alaa Date: Fri, 12 Oct 2018 16:30:16 +0200 Subject: [PATCH 3/8] external force DATASTREAM_DICT key was incorrect ExternalForce was not working with ros. Turns out the datastream interface key for ExternalForce was incorrect: it is `external_force` instead of `externalForce` as the name of python file and as described [here](https://github.com/morse-simulator/morse/blob/86f3793afcdb0f9bf0eb26435ef04f0518ab1f6f/src/morse/builder/actuators.py#L330) --- src/morse/builder/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/morse/builder/data.py b/src/morse/builder/data.py index bef9133c0..7e9822e7f 100644 --- a/src/morse/builder/data.py +++ b/src/morse/builder/data.py @@ -421,7 +421,7 @@ "ros": 'morse.middleware.ros.force_torque.WrenchReader', } }, - "morse.actuators.externalForce.ExternalForce": { + "morse.actuators.external_force.ExternalForce": { "default": { "socket": INTERFACE_DEFAULT_IN, "yarp": INTERFACE_DEFAULT_IN, From 4b4f8c05588b2e8a53fd2c46a46aa0fe225c442e Mon Sep 17 00:00:00 2001 From: Julien Cornier Date: Mon, 12 Nov 2018 15:19:01 +0100 Subject: [PATCH 4/8] [vlp16] In order to use VLP16 sensor with Hardware In the Loop simulation an option to keep the exact number of points (according to the camera resolution) has been added. --- src/morse/builder/sensors.py | 7 +++--- src/morse/sensors/depth_camera.py | 6 ++++- src/morse/sensors/zbufferto3d.c | 38 ++++++++++++++++++++++--------- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/morse/builder/sensors.py b/src/morse/builder/sensors.py index f8c541e91..c866f3bfc 100644 --- a/src/morse/builder/sensors.py +++ b/src/morse/builder/sensors.py @@ -74,7 +74,7 @@ def __init__(self, name=None): mesh.scale = (.04, .04, .02) mesh.color(.3, .9, .6) self.append(mesh) - + class Magnetometer(SensorCreator): _classpath = "morse.sensors.magnetometer.Magnetometer" @@ -476,12 +476,13 @@ def __init__(self, name=None): for i in range(3): cam = DepthCamera("cam%i"%i) """ - cam_focal = 27.7 => hfov ~= 60.0. + cam_focal = 27.7 => hfov ~= 60.0. As cam_height is half the cam_width, vfov ~= 30.0, so each pixel represent around 0.117° Hence, the keep_list is a reasonnably fair approximation of the real angle of VLP16 """ cam.properties(cam_width=512, cam_height=256, cam_focal = 27.7, - keep_list=str([1, 17, 34, 51, 68, 85, 102, 120, 137, 154, 171, 188, 205, 222, 239, 255])) + keep_list=str([1, 17, 34, 51, 68, 85, 102, 120, 137, 154, 171, 188, 205, 222, 239, 255]), + keep_resolution=True) cam.rotate(x=(i-1) * math.pi / 3) cam.frequency(10) cam.hide_mesh() diff --git a/src/morse/sensors/depth_camera.py b/src/morse/sensors/depth_camera.py index 0c3badc85..cdee58892 100644 --- a/src/morse/sensors/depth_camera.py +++ b/src/morse/sensors/depth_camera.py @@ -72,6 +72,10 @@ class DepthCamera(AbstractDepthCamera): "better simulate sparse sensor such as velodyne. If not specified, keep the " "image dense") + add_property('_keep_resolution', False, 'keep_resolution', 'boolean', + "By default the clipping of the camera removes unreals points" + "This option allows adding dummy points (0,0,0) for keeping the sensor resolution") + def initialize(self): from morse.sensors.zbufferto3d import ZBufferTo3D if self._keep_list is None: @@ -82,7 +86,7 @@ def initialize(self): self.converter = ZBufferTo3D(self.local_data['intrinsic_matrix'][0][0], self.local_data['intrinsic_matrix'][1][1], self.near_clipping, self.far_clipping, - self.image_width, self.image_height, + self.image_width, self.image_height, self._keep_resolution, keep_list) def process_image(self, image): diff --git a/src/morse/sensors/zbufferto3d.c b/src/morse/sensors/zbufferto3d.c index 996a9ed37..ce09f06c4 100644 --- a/src/morse/sensors/zbufferto3d.c +++ b/src/morse/sensors/zbufferto3d.c @@ -1,4 +1,5 @@ #include +#include #include "structmember.h" // http://docs.python.org/3/extending/newtypes.html @@ -21,6 +22,7 @@ typedef struct { // The buffer to return values float * points; int points_size; + bool keep_resolution; } PyZBufferTo3D; static void @@ -56,6 +58,8 @@ ZBufferTo3D_new(PyTypeObject *type, PyObject *args, PyObject *kwds) // The buffer to return values self->points = NULL; self->points_size = 0; + // Keep points (including over Far) + self->keep_resolution = false; } return (PyObject *)self; @@ -67,9 +71,9 @@ ZBufferTo3D_init(PyZBufferTo3D* self, PyObject* args) PyObject * listObj; // Get the data as a Python object - if (!PyArg_ParseTuple(args, "ffffIIO!", &self->alpha_u, &self->alpha_v, + if (!PyArg_ParseTuple(args, "ffffIIbO!", &self->alpha_u, &self->alpha_v, &self->near, &self->far, &self->width, &self->height, - &PyList_Type, &listObj)) + &self->keep_resolution, &PyList_Type, &listObj)) { printf("Error while parsing ZBufferTo3D parameters\n"); return -1; @@ -148,17 +152,27 @@ ZBufferTo3D_recover(PyZBufferTo3D* self, PyObject* args) for (int u = 0; u < self->width; ++u, ++buf, ++u_factor) { + float x, y, z_e; float z_b = *buf; - if (z_b >= 1.0) - continue; // nothing seen within the far clipping - - float z_n = 2.0 * z_b - 1.0; - float z_e = A / (B - z_n * C); - // Use the intrinsic matrix of the camera view to get the 3D coordinates - // corresponding to each pixel, with respect to the camera - float x = z_e * *u_factor; - float y = z_e * v_factor; + if (z_b >= 1.0) + { + if(!self->keep_resolution) + continue; // nothing seen within the far clipping + + // Add a dummy point at 0,0,0 in order to keep the sensor resolution in the PointCloud + x = 0.; + y = 0.; + z_e = 0.; + }else{ + float z_n = 2.0 * z_b - 1.0; + z_e = A / (B - z_n * C); + + // Use the intrinsic matrix of the camera view to get the 3D coordinates + // corresponding to each pixel, with respect to the camera + x = z_e * *u_factor; + y = z_e * v_factor; + } // Store the x, y, z coordinates in the buffer *points = x; ++points; @@ -195,6 +209,8 @@ static PyMemberDef ZBufferTo3D_members[] = { "PyZBufferTo3D points"}, {"points_size", T_INT, offsetof(PyZBufferTo3D, points_size), 0, "PyZBufferTo3D points_size"}, + {"keep_resolution", T_BOOL, offsetof(PyZBufferTo3D, keep_resolution), 0, + "PyZBufferTo3D keep_resolution"}, {NULL} /* Sentinel */ }; From 5f5e7b7d0697a3f99dff4a4e6f09d3ffdac9b428 Mon Sep 17 00:00:00 2001 From: Daniel Reuter Date: Tue, 4 Dec 2018 13:51:16 +0100 Subject: [PATCH 5/8] Change parameter names from `async` to `asynchronous` `async` became a keyword in python 3.7 --- doc/morse/dev/services_internal.rst | 6 +++--- src/morse/core/request_manager.py | 12 ++++++------ src/morse/core/services.py | 20 ++++++++++---------- src/morse/middleware/ros_request_manager.py | 6 +++--- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/doc/morse/dev/services_internal.rst b/doc/morse/dev/services_internal.rst index 368e289c9..ca0e34c3c 100644 --- a/doc/morse/dev/services_internal.rst +++ b/doc/morse/dev/services_internal.rst @@ -65,7 +65,7 @@ Asynchronous services Registration of asynchronous services is almost the same as for synchronous services. The ``@async_service`` decorator simply calls the ``@service`` -decorator with the ``async`` parameter set to ``True``, which results in +decorator with the ``asynchronous`` parameter set to ``True``, which results in the original method being wrapped in a new method that takes an extra parameter (a callback) and calls :py:meth:`morse.core.abstractobject.AbstractObject.set_service_callback`. @@ -74,9 +74,9 @@ Simplified version of the ``@service`` decorator: .. code-block:: python - def service(fn, async=False): + def service(fn, asynchronous=False): dfn = fn - if async: + if asynchonous: def decorated_fn(self, callback, *param): self._set_service_callback(callback) fn(self, *param) diff --git a/src/morse/core/request_manager.py b/src/morse/core/request_manager.py index 61c9c3cc7..14294b872 100644 --- a/src/morse/core/request_manager.py +++ b/src/morse/core/request_manager.py @@ -137,7 +137,7 @@ def complex_computation(result_setter, param1, param2): self.register_service(component_name, callback, service_name, True) - def register_service(self, component_name, callback, service_name = None, async = False): + def register_service(self, component_name, callback, service_name = None, asynchronous = False): """ Allows a component to register a synchronous RPC method that is made publicly available to the outside world. @@ -146,10 +146,10 @@ def register_service(self, component_name, callback, service_name = None, async request. If service_name is not defined, it will also be used as the public name of the service. - If async is false (synchronous service), the method is expected to + If asynchronous is false (synchronous service), the method is expected to return immediately. In this case, its return value is immediately send back to the original caller. - :param boolean async: if true, the service is asynchronous: it can last for + :param boolean asynchronous: if true, the service is asynchronous: it can last for several cycles without blocking the communication interface. See :py:meth:`register_async_service` for details. :param service_name: if defined, service_name is used as public @@ -159,11 +159,11 @@ def register_service(self, component_name, callback, service_name = None, async if hasattr(callback, '__call__'): service_name = service_name if service_name else callback.__name__ - self._services[(component_name, service_name)] = (callback, async) + self._services[(component_name, service_name)] = (callback, asynchronous) - if self.post_registration(component_name, service_name, async): + if self.post_registration(component_name, service_name, asynchronous): logger.info(str(self) + ": " + \ - ("Asynchronous" if async else "Synchronous") + \ + ("Asynchronous" if asynchronous else "Synchronous") + \ " service '" + service_name + "' for component '" + \ component_name + "' successfully registered") else: diff --git a/src/morse/core/services.py b/src/morse/core/services.py index 4f4eeeb0f..9c6bfc1f9 100644 --- a/src/morse/core/services.py +++ b/src/morse/core/services.py @@ -107,7 +107,7 @@ def process(self): instance.process() -def do_service_registration(fn, component_name = None, service_name = None, async = False, request_managers = None): +def do_service_registration(fn, component_name = None, service_name = None, asynchronous = False, request_managers = None): if blenderapi.fake: #doc mode return @@ -122,7 +122,7 @@ def do_service_registration(fn, component_name = None, service_name = None, asyn for manager in request_managers: name = service_name if service_name else fn.__name__ logger.debug("Registering service " + name + " in " + component_name + " (using " + manager.__class__.__name__ + ")") - manager.register_service(component_name, fn, name, async) + manager.register_service(component_name, fn, name, asynchronous) def async_service(fn = None, component = None, name = None): """ The @async_service decorator. @@ -138,9 +138,9 @@ def async_service(fn = None, component = None, name = None): explaining why the initialization failed. """ - return service(fn, component, name, async = True) + return service(fn, component, name, asynchronous = True) -def service(fn = None, component = None, name = None, async = False): +def service(fn = None, component = None, name = None, asynchronous = False): """ The @service decorator. This decorator can be used to automagically register a service in @@ -163,7 +163,7 @@ def service(fn = None, component = None, name = None, async = False): functions. Cf explanation above. :param string name: by default, the name of the service is the name of the method. You can override it by setting the 'name' argument. - :param boolean async: if set to True (default value when using + :param boolean asynchronous: if set to True (default value when using @async_service), a new 'callback' parameter is added to the method. This callback is used to notify the service initiator that the service completed. The callback does not need to be build manually: @@ -180,7 +180,7 @@ def service(fn = None, component = None, name = None, async = False): # this method as a service. logger.debug("In @service: Decorating method "+ fn.__name__) dfn = fn - if async: + if asynchronous: def decorated_fn(self, callback, *param): # Stores in the callback the original calling # service. @@ -210,22 +210,22 @@ def decorated_fn(self, callback, *param): dfn._morse_service = True dfn._morse_service_name = name - dfn._morse_service_is_async = async + dfn._morse_service_is_async = asynchronous return dfn else: - if async: + if asynchronous: logger.warning("asynchronous service must be declared within a MorseObject class.") return logger.debug("In @service: Decorating free function "+ fn.__name__) # We assume it's a free function, and we register it. - do_service_registration(fn, component, name, async) + do_service_registration(fn, component, name, asynchronous) return fn else: # ...else, we build a new decorator - return partial(service, component = component, name = name, async = async) + return partial(service, component = component, name = name, asynchronous = asynchronous) def interruptible(fn): """ The @interruptible decorator. diff --git a/src/morse/middleware/ros_request_manager.py b/src/morse/middleware/ros_request_manager.py index 05821e7fa..67a7608cb 100644 --- a/src/morse/middleware/ros_request_manager.py +++ b/src/morse/middleware/ros_request_manager.py @@ -395,8 +395,8 @@ class method to be a asynchronous service, exposed as a ROS action of return partial(ros_action, type = type, name = name) fn._ros_action_type = type - return services.service(fn, component = None, name = name, async = True) - + return services.service(fn, component = None, name = name, asynchronous = True) + def ros_service(fn = None, type = None, component = None, name = None): """ The @ros_service decorator. @@ -433,4 +433,4 @@ class method to be a (synchronous) service, exposed as a ROS service of return partial(ros_service, type = type, component = component, name = name) fn._ros_service_type = type - return services.service(fn, component = component, name = name, async = False) + return services.service(fn, component = component, name = name, asynchronous = False) From e9d4c24e6a782329b82ae2f0d1d09332ef40f099 Mon Sep 17 00:00:00 2001 From: Stephen Dade Date: Sat, 8 Dec 2018 14:55:33 +1100 Subject: [PATCH 6/8] Build - fixed Windows compatibility and added build script --- CMakeLists.txt | 4 +++- src/morse/sensors/depthaggregator.c | 4 ---- winbuild.bat | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 winbuild.bat diff --git a/CMakeLists.txt b/CMakeLists.txt index bf2f13975..8ed807870 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,9 @@ option(BUILD_HLA_SUPPORT "Build HLA middleware support. Also used for distribute option(PYMORSE_SUPPORT "Build and install the Python bindings for MORSE." ON) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/config/) -find_package(PkgConfig REQUIRED) +IF(NOT WIN32) + find_package(PkgConfig REQUIRED) +endif(NOT WIN32) set(PythonInterp_FIND_VERSION 3.3) find_package(PythonInterp REQUIRED) diff --git a/src/morse/sensors/depthaggregator.c b/src/morse/sensors/depthaggregator.c index ee127ff93..b3c2f78d4 100644 --- a/src/morse/sensors/depthaggregator.c +++ b/src/morse/sensors/depthaggregator.c @@ -1,7 +1,6 @@ #include #include "structmember.h" - typedef struct { PyObject_HEAD // The buffer to return values @@ -59,9 +58,6 @@ Aggregator_merge(PyAggregator* self, PyObject* args) int len = PyList_Size(listObj); int j = 0; - struct timeval begin, end, res; - gettimeofday(&begin, NULL); - for (int i = 0; i < len; ++i) { float tx, ty, tz, rx, ry, rz; diff --git a/winbuild.bat b/winbuild.bat new file mode 100644 index 000000000..9fe6046d9 --- /dev/null +++ b/winbuild.bat @@ -0,0 +1,25 @@ +setlocal enableextensions + +rem ensure python 3.5 and cmake is on the system path, otherwise set them here +rem Python 3.5 also needs the "numpy" package, install via the command "pip install numpy --user" +rem ensure that the MORSE_BLENDER environment var is set to the blender.exe path+file +rem Also note that Blender and Python must both be 32bit or both 64bit installs. They can't be different. +rem you may also need a c compiler: "Microsoft Build Tools for Visual Studio 2017" + +rem get python paths +for /f "delims=" %%i in ('python -c "from sysconfig import get_paths; print(get_paths()['include'])"') do set PYINC=%%i +for /f "delims=" %%A in ('where python') do set PYTHONPATH=%%~dpA + +rem 32 or 64bit Python? +for /f %%i in ('python -c "import struct; bit=8*struct.calcsize('P'); print('-DCMAKE_GENERATOR_PLATFORM=x64' if bit==64 else '')"') do set ISBIT=%%i + +rem only make the build folder if it doesn't exist +if not exist "build" mkdir build + +rem and build and install into C:\morse +rem if you want a different folder for more to be installed into, change the -DCMAKE_INSTALL_PREFIX="" below + +cd build +cmake .. -DPYTHON_INCLUDE_DIR="%PYINC%" %ISBIT% -DPYTHON_LIBRARY="%PYTHONPATH%libs\python35.lib" -DCMAKE_INSTALL_PREFIX="C:\morse" +cmake --build . --config Release --target install +pause From 6388dce73ffa1d76c4d070d9ff908a32357aec45 Mon Sep 17 00:00:00 2001 From: Stephen Dade Date: Sun, 9 Dec 2018 18:42:47 +1100 Subject: [PATCH 7/8] Documentation: Updated to building on Windows --- INSTALL | 31 ++++++++++++++++++++++++++++--- RELEASE_NOTES | 1 + doc/morse/user/installation.rst | 29 +++++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/INSTALL b/INSTALL index 79974e9cb..eab671666 100644 --- a/INSTALL +++ b/INSTALL @@ -43,8 +43,7 @@ distributions. Other UNIXes systems probably work as well (like FreeBSD or Apple MacOSX). -MORSE does not currently officially support Microsoft Windows, although some -users reported success. Testers/maintainers for Windows are welcome! +Windows is supported, but not fully tested. Packaged versions ----------------- @@ -94,15 +93,21 @@ Prerequisites - python-dev package - Blender (>= 2.65) build with Python >= 3.3. You can simply get a binary from Blender website + +If building on Windows, ensure that your Python version and architecture matches +the bundled Python in Blender (currently Python 3.5.3 for Blender 2.79). Installation ++++++++++++ +Linux +----- + Download the latest version of the source code. It is stored in a git repository:: - $ git clone https://github.com/laas/morse.git + $ git clone https://github.com/morse-simulator/morse.git You can also get a tarball version here. @@ -168,6 +173,26 @@ When updating MORSE to a more recent version, you'll simply have to do:: $ cd build $ make install +Windows +------- + +Download the latest version of the source code. It is stored in a git +repository:: + + $ git clone https://github.com/morse-simulator/morse.git + +The MORSE_BLENDER environment variable should be set to the location and filename +of the Blender executable (ie "C:\Program Files\Blender\blender.exe"). + +Additionally, both cmake and Python should be on the system path. + +Go to the directory where you have previously downloaded the MORSE source. +Then run the winbuild.bat script. + +By default, MORSE will install in C:\morse. You can easily change the +install directory by editing the CMAKE_INSTALL_PREFIX variable in +winbuild.bat + Installation troubleshooting ---------------------------- diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 61e3c2cc6..b51f3c6de 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -13,6 +13,7 @@ General compute automatically the right time settings. If you meet any problem related to time, make sure to read the Time and Event documentation and / or report issue to the Morse project. +- Windows is now supported for building MORSE. Components ---------- diff --git a/doc/morse/user/installation.rst b/doc/morse/user/installation.rst index d1291a509..6b58a8c55 100644 --- a/doc/morse/user/installation.rst +++ b/doc/morse/user/installation.rst @@ -43,8 +43,7 @@ It should also work on UNIX systems more generally, such as FreeBSD and Apple Ma Some successful testing has been done on OSX 10.8 (see below for the Homebrew recipe). -As yet, there is no official support for Microsoft Windows, although some -users have reported success. Testers/maintainers for Windows are welcome! +Windows is supported, but not fully tested. Hardware @@ -121,9 +120,15 @@ Prerequisites .. note:: If you use a Blender binary, numpy is included within it. +.. note:: + If building on Windows, ensure that your Python version and architecture matches + the bundled Python in Blender (currently Python 3.5.3 for Blender 2.79). + Installation ++++++++++++ +Linux +----- Clone with ``git`` or download the latest version of the source code:: @@ -161,6 +166,26 @@ You can check your configuration is okay with:: Once MORSE is successfully installed and checked you are read for the :doc:`Quickstart<../quickstart>` tutorial! +Windows +------- + +Download the latest version of the source code. It is stored in a git +repository:: + + $ git clone https://github.com/morse-simulator/morse.git + +The MORSE_BLENDER environment variable should be set the the location and filename +of the Blender executable (ie "C:\Program Files\Blender\blender.exe"). + +Additionally, both cmake and Python should be on the system path. + +Go to the directory where you have previously downloaded the MORSE source. +Then run the winbuild.bat script. + +By default, MORSE will install in C:\morse. You can easily change the +install directory by editing the CMAKE_INSTALL_PREFIX variable in +winbuild.bat + Middleware-specific notes +++++++++++++++++++++++++ From 3800cd5a7a443dea4d34ef07a77e8b8d7e7d00ab Mon Sep 17 00:00:00 2001 From: Stephen Dade Date: Tue, 18 Dec 2018 18:28:41 +1100 Subject: [PATCH 8/8] Build - tests now run on Windows --- INSTALL | 2 +- RELEASE_NOTES | 2 +- doc/morse/dev/testing.rst | 2 ++ doc/morse/user/installation.rst | 4 ++-- src/morse/testing/testing.py | 9 +++++++-- winbuild.bat | 18 ++++++++++++++++-- 6 files changed, 29 insertions(+), 8 deletions(-) diff --git a/INSTALL b/INSTALL index eab671666..e521bec49 100644 --- a/INSTALL +++ b/INSTALL @@ -190,7 +190,7 @@ Go to the directory where you have previously downloaded the MORSE source. Then run the winbuild.bat script. By default, MORSE will install in C:\morse. You can easily change the -install directory by editing the CMAKE_INSTALL_PREFIX variable in +install directory by editing the MORSE_ROOT variable in winbuild.bat Installation troubleshooting diff --git a/RELEASE_NOTES b/RELEASE_NOTES index b51f3c6de..38ac08d38 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -13,7 +13,7 @@ General compute automatically the right time settings. If you meet any problem related to time, make sure to read the Time and Event documentation and / or report issue to the Morse project. -- Windows is now supported for building MORSE. +- Windows is now supported for building MORSE and running the unit test suite. Components ---------- diff --git a/doc/morse/dev/testing.rst b/doc/morse/dev/testing.rst index 973e6f59b..aba6ba857 100644 --- a/doc/morse/dev/testing.rst +++ b/doc/morse/dev/testing.rst @@ -110,6 +110,8 @@ This will start launching MORSE with a series of unit tests, to check that the creation of scenes and some of the components is running properly on your system. +Windows users can run the ``winbuild.bat`` file to build and run the tests +automatically. Running tests diff --git a/doc/morse/user/installation.rst b/doc/morse/user/installation.rst index 6b58a8c55..690f76e96 100644 --- a/doc/morse/user/installation.rst +++ b/doc/morse/user/installation.rst @@ -183,8 +183,8 @@ Go to the directory where you have previously downloaded the MORSE source. Then run the winbuild.bat script. By default, MORSE will install in C:\morse. You can easily change the -install directory by editing the CMAKE_INSTALL_PREFIX variable in -winbuild.bat +install directory by editing the MORSE_ROOT variable in +``winbuild.bat`` Middleware-specific notes +++++++++++++++++++++++++ diff --git a/src/morse/testing/testing.py b/src/morse/testing/testing.py index a5ea99c83..ca35a80a8 100644 --- a/src/morse/testing/testing.py +++ b/src/morse/testing/testing.py @@ -2,7 +2,7 @@ #testrunnerlogger = logging.getLogger("test.runner") testlogger = logging.getLogger("morsetesting.general") -import sys, os +import sys, os, platform from abc import ABCMeta, abstractmethod import unittest import inspect @@ -201,11 +201,16 @@ def startmorse(self, test_case): if prefix == "": cmd = 'morse' + elif platform.system() == 'Windows': + cmd = os.path.join(prefix.strip('\"').strip('\''), "bin", "morserun.py") else: cmd = prefix + "/bin/morse" self.logfile = open(self.logfile_name, 'w') - self.morse_process = subprocess.Popen([cmd, 'run', temp_builder_script], stdout=self.logfile, stderr=subprocess.STDOUT) + if platform.system() == 'Windows': + self.morse_process = subprocess.Popen(['python', cmd, 'run', temp_builder_script], stdout=self.logfile, stderr=subprocess.STDOUT) + else: + self.morse_process = subprocess.Popen([cmd, 'run', temp_builder_script], stdout=self.logfile, stderr=subprocess.STDOUT) except OSError as ose: testlogger.error("Error while launching MORSE! Check you can run it from command-line\n" + \ " and if you use the $MORSE_ROOT env variable, check it points to a correct " + \ diff --git a/winbuild.bat b/winbuild.bat index 9fe6046d9..0e50ea1ee 100644 --- a/winbuild.bat +++ b/winbuild.bat @@ -17,9 +17,23 @@ rem only make the build folder if it doesn't exist if not exist "build" mkdir build rem and build and install into C:\morse -rem if you want a different folder for more to be installed into, change the -DCMAKE_INSTALL_PREFIX="" below +rem if you want a different folder for more to be installed into, change the MORSE_ROOT below +set MORSE_ROOT=C:\morse cd build -cmake .. -DPYTHON_INCLUDE_DIR="%PYINC%" %ISBIT% -DPYTHON_LIBRARY="%PYTHONPATH%libs\python35.lib" -DCMAKE_INSTALL_PREFIX="C:\morse" +cmake .. -DPYMORSE_SUPPORT=ON -DPYTHON_INCLUDE_DIR="%PYINC%" %ISBIT% -DPYTHON_LIBRARY="%PYTHONPATH%libs\python35.lib" -DCMAKE_INSTALL_PREFIX="%MORSE_ROOT%" -DCMAKE_VERBOSE_MAKEFILE=ON cmake --build . --config Release --target install + +CHOICE /M "Run unit tests for 30min?" +IF ERRORLEVEL 2 GOTO END +IF ERRORLEVEL 1 GOTO RUNTESTS +GOTO END + +:RUNTESTS +rem the unit tests take ~30min to run, so optional for user to run these +set PYTHONPATH=%PYTHONPATH%;%MORSE_ROOT%\Lib\site-packages\ +"C:\Program Files\CMake\bin\ctest" . --verbose -C Release +GOTO END + +:END pause