{
 "metadata": {
  "name": "",
  "signature": "sha256:7a1296f1470068a26e12a6e9eae79f37b84cd8aa3b3b14d6a900280ca6850afd"
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "heading",
     "level": 1,
     "metadata": {},
     "source": [
      "Accessing pypot REST API through HTTP requests"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Introduction"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "In this example, we will illustrate how to start an HTTP server permitting the remote access of any Poppy creature through the pypot [REST API](https://github.com/poppy-project/pypot/blob/REST-API-2.0/REST-APIs.md). More precisely, we will show how we can retrieve motor position and set new ones."
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "For this, we will use the simulated version of the Poppy humanoid. Thus, anyone even without having a \"real\" poppy creature on its table, will be able to try. Adapting this tutorial to a \"real\" robot should be straightforward."
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "The client side of the example - meaning the HTTP requests - will be realised using *curl*."
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "*Note: For this tutorial you will need:*\n",
      "* [V-REP](http://www.coppeliarobotics.com)\n",
      "* [pypot](https://github.com/poppy-project/pypot) >= 2.x\n",
      "* [poppytools](https://github.com/poppy-project/poppy-software)\n",
      "* [bottle](http://bottlepy.org/docs/dev/index.html)"
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Create the Robot"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "First, we will instantiate a robot using V-REP (see [this post](https://forum.poppy-project.org/t/howto-connect-pypot-to-your-simulated-version-of-poppy-humanoid-in-v-rep/332/) for details on how this can be done)."
     ]
    },
    {
     "cell_type": "heading",
     "level": 3,
     "metadata": {},
     "source": [
      "Launch V-REP"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "First launch a V-REP instance. We will assume below that the remoteApi is bind to *'127.0.0.1'* using the port *19997* (its default configuration)."
     ]
    },
    {
     "cell_type": "heading",
     "level": 3,
     "metadata": {},
     "source": [
      "Connect pypot to the V-REP scene"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import os\n",
      "import json\n",
      "\n",
      "import pypot\n",
      "import poppytools\n",
      "\n",
      "from pypot.vrep import from_vrep\n",
      "\n",
      "# Load the robot configuration\n",
      "configfile = os.path.join(os.path.dirname(poppytools.__file__),\n",
      "                          'configuration', 'poppy_config.json')\n",
      "\n",
      "with open(configfile) as f:\n",
      "    robot_config = json.load(f)\n",
      "    \n",
      "# Load a V-REP scene with poppy sitting\n",
      "scene = os.path.join(os.path.dirname(pypot.__file__), \n",
      "                     '..', 'samples', 'notebooks', 'poppy-sitting.ttt')\n",
      "\n",
      "# Connect to the simulated robot\n",
      "robot = from_vrep(robot_config, '127.0.0.1', 19997, scene,\n",
      "                  tracked_objects=['head_visual'])"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 1
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Create the HTTPServer"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "We will now starts the HTTP server providing the remote access of our robot."
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "It will run on *http://127.0.0.1:8080/*"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "from pypot.server.httpserver import HTTPRobotServer\n",
      "\n",
      "server = HTTPRobotServer(robot, host='127.0.0.1', port=8080)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 2
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "By default, the bottle server is launch using [tornado](http://www.tornadoweb.org/en/stable/) - for effiency puposes. Yet, as [IPython notebook](http://ipython.org/notebook.html) also uses the tornado server, it seems to cause trouble. Here, we use the [wsgiref](https://docs.python.org/2/library/wsgiref.html) server instead."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "from threading import Thread\n",
      "\n",
      "Thread(target=lambda: server.run(quiet=True, server='wsgiref')).start()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 3
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "As the [run method](http://poppy-project.github.io/pypot/pypot.server.html#pypot.server.httpserver.HTTPServer.run) of the server is blocking we launch it inside a thread. Thus, we can use the same notebook to send requests."
     ]
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Sending requests"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Now that the server is launched, you should be able to directly access the robot using the [REST API](https://github.com/poppy-project/pypot/blob/REST-API-2.0/REST-APIs.md)."
     ]
    },
    {
     "cell_type": "heading",
     "level": 3,
     "metadata": {},
     "source": [
      "Getting values"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "For instance, to retrieve the list of all motors name:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "!curl http://127.0.0.1:8080/motor/list.json"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "{\"motors\": [\"l_elbow_y\", \"r_elbow_y\", \"r_knee_y\", \"head_y\", \"head_z\", \"r_arm_z\", \"r_ankle_y\", \"r_shoulder_x\", \"r_shoulder_y\", \"r_hip_z\", \"r_hip_x\", \"r_hip_y\", \"l_arm_z\", \"l_hip_x\", \"l_hip_y\", \"l_hip_z\", \"abs_x\", \"abs_y\", \"abs_z\", \"l_ankle_y\", \"bust_y\", \"bust_x\", \"l_knee_y\", \"l_shoulder_x\", \"l_shoulder_y\"]}"
       ]
      }
     ],
     "prompt_number": 4
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Or to access a specific register of a motor:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "!curl http://127.0.0.1:8080/motor/head_z/register/present_position"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "{\"present_position\": 0.2}"
       ]
      }
     ],
     "prompt_number": 5
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Similarly, you can retrieve the list of sensors and their respective registers:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "!curl http://127.0.0.1:8080/sensor/list.json"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "{\"sensors\": [\"head_visual\"]}"
       ]
      }
     ],
     "prompt_number": 6
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "!curl http://127.0.0.1:8080/sensor/head_visual/register/list.json"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "{\"registers\": [\"position\", \"orientation\"]}"
       ]
      }
     ],
     "prompt_number": 7
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "!curl http://127.0.0.1:8080/sensor/head_visual/register/position"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "{\"position\": [-0.011824678629636765, -0.029312146827578545, 0.53340655565261841]}"
       ]
      }
     ],
     "prompt_number": 8
    },
    {
     "cell_type": "heading",
     "level": 3,
     "metadata": {},
     "source": [
      "Sending values"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "The following requests should make the head of the robot moves."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "!curl -X POST -d \"100\" http://127.0.0.1:8080/motor/head_z/register/goal_position/value.json \\\n",
      "        --header \"Content-Type:application/json\""
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "{}"
       ]
      }
     ],
     "prompt_number": 9
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "!curl -X POST -d \"-100\" http://127.0.0.1:8080/motor/head_z/register/goal_position/value.json \\\n",
      "        --header \"Content-Type:application/json\""
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "{}"
       ]
      }
     ],
     "prompt_number": 10
    },
    {
     "cell_type": "heading",
     "level": 2,
     "metadata": {},
     "source": [
      "Using the [requests](http://docs.python-requests.org/en/latest/) module instead"
     ]
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Similar behaviors can be achieved directly in Python, for instance using the [request](http://docs.python-requests.org/en/latest/) module."
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import requests"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 11
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "To retrieve a position:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "r = requests.get('http://127.0.0.1:8080/motor/head_z/register/present_position')\n",
      "r.json()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 12,
       "text": [
        "{u'present_position': -100.3}"
       ]
      }
     ],
     "prompt_number": 12
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "To set a new position:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "import time\n",
      "\n",
      "pos = 50\n",
      "\n",
      "for _ in range(3):\n",
      "    requests.post('http://127.0.0.1:8080/motor/head_z/register/goal_position/value.json', \n",
      "                  data=json.dumps(pos), \n",
      "                  headers={'content-type': 'application/json'})\n",
      "    \n",
      "    pos *= -1\n",
      "    time.sleep(1.)"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [],
     "prompt_number": 13
    },
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Just as a very basic benchmark:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "%%timeit\n",
      "\n",
      "r = requests.get('http://127.0.0.1:8080/motor/head_z/register/present_position')"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "output_type": "stream",
       "stream": "stdout",
       "text": [
        "100 loops, best of 3: 3.37 ms per loop\n"
       ]
      }
     ],
     "prompt_number": 14
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [],
     "language": "python",
     "metadata": {},
     "outputs": []
    }
   ],
   "metadata": {}
  }
 ]
}