<commit_before>#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4

import rapidsms

import cgi, urlparse, traceback
from threading import Thread
from SocketServer import ThreadingMixIn
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

from django.utils.simplejson import JSONEncoder

class App(rapidsms.app.App):
    """This app has no docstring!"""

    class Server(ThreadingMixIn, HTTPServer):
        pass

    class MyJsonEncoder(JSONEncoder):
        def default(self, o):

            # if this object has its own preference
            # for JSON serialization, prioritize that
            if hasattr(o, "__json__"):
                return o.__json__()

            # otherwise, revert to the usual behavior
            return JSONEncoder.default(self, o)

    class RequestHandler(BaseHTTPRequestHandler):
        def __find_app(self, name):

            # inspect the name of each active app,
            # returning as soon as we find a match
            for app in self.server.app.router.apps:
                if app.name.lower() == name.lower():
                    return app

            # no app by that
            # name was found
            return None

        # handle both GET and POST with
        # the same method
        def do_GET(self):  return self.process()
        def do_POST(self): return self.process()

        def process(self):
            def response(code, output):
                self.send_response(code)
                self.send_header("content-type", "application/json")
                self.end_headers()

                # send back every response as JSON -- even the
                # errors -- to avoid having to check the mime
                # type of the output before displaying it.
                json = App.MyJsonEncoder().encode(output)
                self.wfile.write(json)

                # HTTP2xx represents success
                return (code>=200 and code <=299)

            # should look something like:
            #   /alpha/bravo?charlie=delta
            #
            # this request will be parsed to the "bravo"
            # method of the "alpha" app, with the params:
            #   { "charlie": ["delta"] }
            #
            # any other path format will return an http404
            # error, for the time being. params are optional.
            url = urlparse.urlparse(self.path)
            path_parts = url.path.split("/")

            # abort if the url didn't look right
            # TODO: better error message here
            if len(path_parts) != 3:
                return response(404, "FAIL.")

            # resolve the first part of the url into an app
            # (via the router), and abort if it wasn't valid
            app_name = path_parts[1]
            app = self.__find_app(app_name)
            if (app is None):
                return response(404,
                    "Invalid app: %s" % app_name)

            # same for the request name within the app
            # (FYI, self.command returns GET, POST, etc)
            meth_name = "ajax_%s_%s" % (self.command, path_parts[2])
            if not hasattr(app, meth_name):
                return response(404,
                    "Invalid method: %s" % meth_name)

            # everything appears to be well, so call the
            # target method, and return the response (as
            # a string, for now)
            try:
                method = getattr(app, meth_name)
                params = urlparse.parse_qs(url.query)
                args   = [params]

                # for post requests, we'll also need to parse
                # the form data, and hand it to the method
                if self.command == "POST":
                    form = {}

                    # parse the form data via the CGI lib. this is
                    # a horrible mess, but supports all kinds of
                    # encodings (multipart, in particular)
                    storage = cgi.FieldStorage(
                        fp = self.rfile,
                        headers = self.headers,
                        environ = {
                            "REQUEST_METHOD": "POST",
                            "CONTENT_TYPE": self.headers["content-type"] })

                    # convert the fieldstorage object into a dict,
                    # to keep it simple for the handler methods.
                    # TODO: maybe make this a util if it's useful
                    # elsewhere. it isn't, for the time being.
                    for key in storage.keys():
                        v = storage.getlist(key)

                        # where possible, just store the values as singular,
                        # to avoid CGIs usual post["id"][0] verbosity
                        if len(v) > 1: form[key] = v
                        else:          form[key] = v[0]

                    args.append(form)

                # call the method, and send back whatever data
                # structure was returned, serialized with JSON
                output = method(*args)
                return response(200, output)

            # something raised during the request, so
            # return a useless http error to the requester
            except:
                self.server.app.warning(traceback.format_exc())
                return response(500, traceback.format_exc())

        # this does nothing, except prevent HTTP
        # requests being echoed to the screen
        def log_request(*args):
            pass

    def configure(self, host=None, port=None):
        self.host = host
        self.port = port

    def start(self):
        # create the webserver, through which the
        # AJAX requests from the WebUI will arrive
        self.server = self.Server((self.host, self.port), self.RequestHandler)
        self.server.app = self

        # start the server in a separate thread, and daemonize it
        # to prevent it from hanging once the main thread terminates
        self.thread = Thread(target=self.server.serve_forever)
        self.thread.daemon = True
        self.thread.start()
<commit_msg>(finally) wrote a docstring for the ajax app.<commit_after>#!/usr/bin/env python
# vim: ai ts=4 sts=4 et sw=4

import rapidsms

import cgi, urlparse, traceback
from threading import Thread
from SocketServer import ThreadingMixIn
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

from django.utils.simplejson import JSONEncoder

class App(rapidsms.app.App):
    """This App does nothing by itself. It exists only to serve other Apps, by
       providing an easy (and standard) way for them to communicate between their
       WebUI and RapidSMS App object.

       When RapidSMS starts, this app starts an HTTPServer (port 8001 as default,
       but configurable via rapidsms.ini) in a worker thread, and watches for any
       incoming HTTP requests matching */app/method*. These requests, along with
       their GET parameters and POST form, are passed on to the named app.

       Examples:

       method  URL                  app        method             args
       ======  ===                  ===        ======             ====
       GET     /breakfast/toast     breakfast  ajax_GET_toast     { }
       POST    /breakfast/waffles   breakfast  ajax_POST_waffles  { }, { }
       POST    /breakfast/eggs?x=1  breakfast  ajax_POST_eggs     { "x": 1 }, {}

       Any data that is returned by the handler method is JSON encoded, and sent
       back to the WebUI in response. Since the _webui_ app includes jQuery with
       every view, this makes it very easy for the WebUIs of other apps to query
       their running App object for state. See the _training_ app for an example.

       But wait! AJAX can't cross domains, so a request to port 8001 from the WebUI
       won't work! This is handled by the WebUI bundled with this app, that proxies
       all requests to /ajax/(.+) to the right place, on the server side. I cannot
       conceive of a situation where this would be a problem - but keep it in mind,
       and don't forget to prepend "/ajax/" to your AJAX URLs."""

    class Server(ThreadingMixIn, HTTPServer):
        pass

    class MyJsonEncoder(JSONEncoder):
        def default(self, o):

            # if this object has its own preference
            # for JSON serialization, prioritize that
            if hasattr(o, "__json__"):
                return o.__json__()

            # otherwise, revert to the usual behavior
            return JSONEncoder.default(self, o)

    class RequestHandler(BaseHTTPRequestHandler):
        def __find_app(self, name):

            # inspect the name of each active app,
            # returning as soon as we find a match
            for app in self.server.app.router.apps:
                if app.name.lower() == name.lower():
                    return app

            # no app by that
            # name was found
            return None

        # handle both GET and POST with
        # the same method
        def do_GET(self):  return self.process()
        def do_POST(self): return self.process()

        def process(self):
            def response(code, output):
                self.send_response(code)
                self.send_header("content-type", "application/json")
                self.end_headers()

                # send back every response as JSON -- even the
                # errors -- to avoid having to check the mime
                # type of the output before displaying it.
                json = App.MyJsonEncoder().encode(output)
                self.wfile.write(json)

                # HTTP2xx represents success
                return (code>=200 and code <=299)

            # should look something like:
            #   /alpha/bravo?charlie=delta
            #
            # this request will be parsed to the "bravo"
            # method of the "alpha" app, with the params:
            #   { "charlie": ["delta"] }
            #
            # any other path format will return an http404
            # error, for the time being. params are optional.
            url = urlparse.urlparse(self.path)
            path_parts = url.path.split("/")

            # abort if the url didn't look right
            # TODO: better error message here
            if len(path_parts) != 3:
                return response(404, "FAIL.")

            # resolve the first part of the url into an app
            # (via the router), and abort if it wasn't valid
            app_name = path_parts[1]
            app = self.__find_app(app_name)
            if (app is None):
                return response(404,
                    "Invalid app: %s" % app_name)

            # same for the request name within the app
            # (FYI, self.command returns GET, POST, etc)
            meth_name = "ajax_%s_%s" % (self.command, path_parts[2])
            if not hasattr(app, meth_name):
                return response(404,
                    "Invalid method: %s" % meth_name)

            # everything appears to be well, so call the
            # target method, and return the response (as
            # a string, for now)
            try:
                method = getattr(app, meth_name)
                params = urlparse.parse_qs(url.query)
                args   = [params]

                # for post requests, we'll also need to parse
                # the form data, and hand it to the method
                if self.command == "POST":
                    form = {}

                    # parse the form data via the CGI lib. this is
                    # a horrible mess, but supports all kinds of
                    # encodings (multipart, in particular)
                    storage = cgi.FieldStorage(
                        fp = self.rfile,
                        headers = self.headers,
                        environ = {
                            "REQUEST_METHOD": "POST",
                            "CONTENT_TYPE": self.headers["content-type"] })

                    # convert the fieldstorage object into a dict,
                    # to keep it simple for the handler methods.
                    # TODO: maybe make this a util if it's useful
                    # elsewhere. it isn't, for the time being.
                    for key in storage.keys():
                        v = storage.getlist(key)

                        # where possible, just store the values as singular,
                        # to avoid CGIs usual post["id"][0] verbosity
                        if len(v) > 1: form[key] = v
                        else:          form[key] = v[0]

                    args.append(form)

                # call the method, and send back whatever data
                # structure was returned, serialized with JSON
                output = method(*args)
                return response(200, output)

            # something raised during the request, so
            # return a useless http error to the requester
            except:
                self.server.app.warning(traceback.format_exc())
                return response(500, traceback.format_exc())

        # this does nothing, except prevent HTTP
        # requests being echoed to the screen
        def log_request(*args):
            pass

    def configure(self, host=None, port=None):
        self.host = host
        self.port = port

    def start(self):
        # create the webserver, through which the
        # AJAX requests from the WebUI will arrive
        self.server = self.Server((self.host, self.port), self.RequestHandler)
        self.server.app = self

        # start the server in a separate thread, and daemonize it
        # to prevent it from hanging once the main thread terminates
        self.thread = Thread(target=self.server.serve_forever)
        self.thread.daemon = True
        self.thread.start()
<|endoftext|>


<commit_before>#!/usr/bin/env python
# Software License Agreement (BSD License)
#
#  Copyright (c) 2011, Georgia Institute of Technology
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions
#  are met:
#
#  * Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above
#     copyright notice, this list of conditions and the following
#     disclaimer in the documentation and/or other materials provided
#     with the distribution.
#  * Neither the name of the Georgia Institute of Technology nor the names of
#     its contributors may be used to endorse or promote products derived
#     from this software without specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#  'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
#  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
#  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
#  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
#  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#  POSSIBILITY OF SUCH DAMAGE.

import roslib; roslib.load_manifest('tabletop_pushing')
import rospy
import hrl_pr2_lib.linear_move as lm
import hrl_pr2_lib.pr2 as pr2
import hrl_lib.tf_utils as tfu
from geometry_msgs.msg import PoseStamped
import tf
import numpy as np
from tabletop_pushing.srv import *
from math import sin, cos, pi
import sys

OLD_LEFT_ARM_INIT_JOINTS = np.matrix([[1.39060109e-01, 3.65531104e-01,
                                       1.68462256e+00, -1.97664347e+00,
                                       2.17482262e+02, -1.41818799e+00,
                                       -8.64819949e+01]]).T
OLD_LEFT_ARM_READY_JOINTS = np.matrix([[1.23639478e-01, -1.30025955e-01,
                                        1.56307360e+00, -1.81768523e+00,
                                        2.16694211e+02, -1.45799866e+00,
                                        -8.64421842e+01]]).T

OLD_RIGHT_ARM_INIT_JOINTS = np.matrix([[-1.39060109e-01, 3.65531104e-01,
                                         -1.68462256e+00, -1.97664347e+00,
                                         -2.17482262e+02, -1.41818799e+00,
                                         -8.64819949e+01]]).T
OLD_RIGHT_ARM_READY_JOINTS = np.matrix([[-1.23639478e-01, -1.30025955e-01,
                                          -1.56307360e+00, -1.81768523e+00,
                                          -2.16694211e+02, -1.45799866e+00,
                                          -8.64421842e+01]]).T

LEFT_ARM_INIT_JOINTS = np.matrix([[7.0e-01, 3.65531104e-01,
                                   1.68462256e+00, -1.97664347e+00,
                                   2.17482262e+02, -1.41818799e+00,
                                   -8.64819949e+01]]).T
LEFT_ARM_READY_JOINTS = np.matrix([[5.0e-01, -1.30025955e-01,
                                    1.56307360e+00, -1.81768523e+00,
                                    2.16694211e+02, -1.45799866e+00,
                                    -8.64421842e+01]]).T

RIGHT_ARM_INIT_JOINTS = np.matrix([[-7.0e-01, 3.65531104e-01,
                                    -1.68462256e+00, -1.97664347e+00,
                                    -2.17482262e+02, -1.41818799e+00,
                                    -8.64819949e+01]]).T
RIGHT_ARM_READY_JOINTS = np.matrix([[-5.0e-01, -1.30025955e-01,
                                    -1.56307360e+00, -1.81768523e+00,
                                    -2.16694211e+02, -1.45799866e+00,
                                    -8.64421842e+01]]).T

INIT_POSE_MOVE_THRESH = 0.5
READY_POSE_MOVE_THRESH = 0.5

class TabletopPushNode:

    def __init__(self, no_arms = False):
        rospy.init_node('tabletop_push_node', log_level=rospy.DEBUG)
        self.gripper_x_offset = rospy.get_param('~gripper_push_start_x_offset',
                                                0.03)
        self.gripper_z_offset = rospy.get_param('~gripper_push_start_z_offset',
                                                0.03)
        self.tf_listener = tf.TransformListener()

        self.no_arms = no_arms

        if not no_arms:
            rospy.loginfo('Creating pr2 object')
            self.robot = pr2.PR2(self.tf_listener, arms=True, base=False)
            rospy.loginfo('Creating LinearReactiveMovement')
            # TODO: Turn off slip_controllers and slip_detection for use in simulator
            use_slip=1
            #use_slip=0
            rospy.loginfo("Setting up left arm move")
            self.left_arm_move = lm.LinearReactiveMovement('l', self.robot,
                                                           self.tf_listener,
                                                           use_slip, use_slip)
            rospy.loginfo("Setting up right arm move")
            self.right_arm_move = lm.LinearReactiveMovement('r', self.robot,
                                                            self.tf_listener,
                                                            use_slip, use_slip)

        self.push_pose_proxy = rospy.ServiceProxy("get_push_pose", PushPose)
        self.gripper_push_service = rospy.Service('gripper_push',
                                                  GripperPush,
                                                  self.gripper_push_action)
        self.gripper_sweep_service = rospy.Service('gripper_sweep',
                                                   GripperPush,
                                                   self.gripper_sweep_action)

    #
    # Behavior functions
    #
    def init_arm_pose(self, force_ready=False, which_arm='l'):
        '''
        Move the arm to the initial pose to be out of the way for viewing the
        tabletop
        '''
        if which_arm == 'l':
            push_arm = self.left_arm_move
            robot_arm = self.robot.left
            robot_gripper = self.robot.left_gripper
            init_joints = LEFT_ARM_INIT_JOINTS
            ready_joints = LEFT_ARM_READY_JOINTS
        else:
            push_arm = self.right_arm_move
            robot_arm = self.robot.right
            robot_gripper = self.robot.right_gripper
            init_joints = RIGHT_ARM_INIT_JOINTS
            ready_joints = RIGHT_ARM_READY_JOINTS

        init_diff = np.linalg.norm(pr2.diff_arm_pose(robot_arm.pose(),
                                                     init_joints))
        ready_diff = np.linalg.norm(pr2.diff_arm_pose(robot_arm.pose(),
                                                      ready_joints))
        push_arm.set_movement_mode_ik()

        # Choose to move to ready first, if it is closer, then move to init
        # TODO: This could be smarter, i.e., don't move up, then down
        if force_ready or ready_diff < init_diff:
            if ready_diff > READY_POSE_MOVE_THRESH or force_ready:
                rospy.loginfo('Moving arm to ready pose')
                robot_arm.set_pose(ready_joints, nsecs=2.0, block=True)
                rospy.loginfo('Moved arm to ready pose')
            else:
                rospy.loginfo('Arm in ready pose')

        # Test for closeness before moving
        if init_diff < INIT_POSE_MOVE_THRESH:
            rospy.loginfo('Arm in init pose')
            rospy.loginfo('init_diff is: '+str(init_diff))
        else:
            rospy.loginfo('Moving arm to init pose')
            robot_arm.set_pose(init_joints, nsecs=2.0, block=True)
            rospy.loginfo('Moved arm to init pose')

        # TODO: Check to see if the gripper needs to be closed
        rospy.loginfo('Closing gripper')
        res = robot_gripper.close(block=True)
        rospy.logdebug('Gripper status is: ' + str(res))

    def gripper_push_action(self, request):
        response = GripperPushResponse()
        push_frame = request.start_point.header.frame_id
        start_point = request.start_point.point
        wrist_yaw = request.wrist_yaw
        push_dist = request.desired_push_dist

        if request.left_arm:
            push_arm = self.left_arm_move
            robot_arm = self.robot.left
            ready_joints = LEFT_ARM_READY_JOINTS
        else:
            push_arm = self.right_arm_move
            robot_arm = self.robot.right
            ready_joints = RIGHT_ARM_READY_JOINTS

        # Offset to higher position to miss the table, but were lower to avoid
        # seeing the arm for now.
        rospy.loginfo("Moving arm to ready pose")
        push_arm.set_movement_mode_ik()
        robot_arm.set_pose(ready_joints, nsecs=2.0, block=True)

        orientation = tf.transformations.quaternion_from_euler(0.0, 0.0, wrist_yaw)
        pose = np.matrix([start_point.x, start_point.y, start_point.z])
        rot = np.matrix([orientation])

        # rospy.loginfo('Pose: ' + str(pose))
        # rospy.loginfo('Ort: ' + str(rot))

        # Move to start pose
        loc = [pose, rot]
        push_arm.set_movement_mode_cart()
        push_arm.move_absolute(loc, stop='pressure', frame=push_frame)
        rospy.loginfo('Done moving to start point')

        # Push in a straight line
        rospy.loginfo('Pushing forward')
        r, pos_error = push_arm.move_relative_gripper(
            np.matrix([push_dist, 0.0, 0.0]).T,
            stop='pressure', pressure=5000)
        rospy.loginfo('Done pushing forward')

        rospy.loginfo('Moving gripper backwards')
        push_arm.move_relative_gripper(
            np.matrix([(-push_dist + pos_error), 0.0, 0.0]).T,
            stop='pressure', pressure=5000)
        rospy.loginfo('Done moving backwards')

        self.init_arm_pose(force_ready=True)
        response.dist_pushed = push_dist - pos_error
        return response

    def gripper_sweep_action(self, request):
        response = GripperPushResponse()
        push_frame = request.start_point.header.frame_id
        start_point = request.start_point.point
        wrist_yaw = request.wrist_yaw
        push_dist = request.desired_push_dist

        if request.left_arm:
            push_arm = self.left_arm_move
            robot_arm = self.robot.left
            ready_joints = LEFT_ARM_READY_JOINTS
            push_dir = -1
        else:
            push_arm = self.right_arm_move
            robot_arm = self.robot.right
            ready_joints = LEFT_ARM_READY_JOINTS
            # ready_joints = RIGHT_ARM_READY_JOINTS
            push_dir = +1

        # Offset to higher position to miss the table, but were lower to avoid
        # seeing the arm for now.
        rospy.loginfo("Moving arm to ready pose")
        push_arm.set_movement_mode_ik()
        robot_arm.set_pose(ready_joints, nsecs=2.0, block=True)

        orientation = tf.transformations.quaternion_from_euler(0.5*pi, 0.0,
                                                               wrist_yaw)
        pose = np.matrix([start_point.x, start_point.y, start_point.z])
        rot = np.matrix([orientation])

        # TODO: Rotate wrist before moving to position
        # arm_pose = robot_arm.pose()
        # arm_pose[-1] = 0.5*pi
        # push_arm.set_movement_mode_ik()
        # robot_arm.set_pose(arm_pose, nsecs=2.0, block=True)

        # Move to offset pose
        loc = [pose, rot]
        push_arm.set_movement_mode_cart()
        push_arm.move_absolute(loc, stop='pressure', frame=push_frame)
        rospy.loginfo('Done moving to start point')

        # Sweep inward
        # TODO: Change this to split the push_dist in x and y depending on yaw
        # and move relative gripper?
        rospy.loginfo('Sweeping in')
        r, pos_error = push_arm.move_relative_base(
            np.matrix([0.0, push_dir*push_dist, 0.0]).T,
            #'l_wrist_roll_link',
            stop='pressure', pressure=5000)
        rospy.loginfo('Done sweeping in')

        rospy.loginfo('Sweeping outward')
        push_arm.move_relative_base(
            np.matrix([0.0, (-push_dir*push_dist + pos_error), 0.0]).T,
            #'l_wrist_roll_link',
            stop='pressure', pressure=5000)
        rospy.loginfo('Done sweeping outward')

        self.init_arm_pose(force_ready=True)
        response.dist_pushed = push_dist - pos_error
        return response

    #
    # Util Functions
    #
    def print_pose(self):
        cart_pose = self.left_arm_move.current_location()
        pose = self.robot.left.pose()
        rospy.loginfo(str(pose))
        rospy.loginfo('Left Cart_position: ' + str(cart_pose[0]))
        rospy.loginfo('Left Cart_orientation: ' + str(cart_pose[1]))
        cart_pose = self.right_arm_move.current_location()
        pose = self.robot.right.pose()
        rospy.loginfo(str(pose))
        rospy.loginfo('Right Cart_position: ' + str(cart_pose[0]))
        rospy.loginfo('Right Cart_orientation: ' + str(cart_pose[1]))

    #
    # Main Control Loop
    #
    def run(self):
        """
        Main control loop for the node
        """
        if not self.no_arms:
            self.init_arm_pose(True, which_arm='l')
            self.init_arm_pose(True, which_arm='r')
        rospy.spin()

if __name__ == '__main__':
    node = TabletopPushNode(no_arms=False)
    node.run()
<commit_msg>Experimental code to increase the gains on the wrist roll of the cartesian controller.<commit_after>#!/usr/bin/env python
# Software License Agreement (BSD License)
#
#  Copyright (c) 2011, Georgia Institute of Technology
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions
#  are met:
#
#  * Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above
#     copyright notice, this list of conditions and the following
#     disclaimer in the documentation and/or other materials provided
#     with the distribution.
#  * Neither the name of the Georgia Institute of Technology nor the names of
#     its contributors may be used to endorse or promote products derived
#     from this software without specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#  'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
#  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
#  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
#  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
#  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
#  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#  POSSIBILITY OF SUCH DAMAGE.

import roslib; roslib.load_manifest('tabletop_pushing')
import rospy
import hrl_pr2_lib.linear_move as lm
import hrl_pr2_lib.pr2 as pr2
import hrl_lib.tf_utils as tfu
from geometry_msgs.msg import PoseStamped
import tf
import numpy as np
from tabletop_pushing.srv import *
from math import sin, cos, pi
import sys

OLD_LEFT_ARM_INIT_JOINTS = np.matrix([[1.39060109e-01, 3.65531104e-01,
                                       1.68462256e+00, -1.97664347e+00,
                                       2.17482262e+02, -1.41818799e+00,
                                       -8.64819949e+01]]).T
OLD_LEFT_ARM_READY_JOINTS = np.matrix([[1.23639478e-01, -1.30025955e-01,
                                        1.56307360e+00, -1.81768523e+00,
                                        2.16694211e+02, -1.45799866e+00,
                                        -8.64421842e+01]]).T

OLD_RIGHT_ARM_INIT_JOINTS = np.matrix([[-1.39060109e-01, 3.65531104e-01,
                                         -1.68462256e+00, -1.97664347e+00,
                                         -2.17482262e+02, -1.41818799e+00,
                                         -8.64819949e+01]]).T
OLD_RIGHT_ARM_READY_JOINTS = np.matrix([[-1.23639478e-01, -1.30025955e-01,
                                          -1.56307360e+00, -1.81768523e+00,
                                          -2.16694211e+02, -1.45799866e+00,
                                          -8.64421842e+01]]).T

LEFT_ARM_INIT_JOINTS = np.matrix([[7.0e-01, 3.65531104e-01,
                                   1.68462256e+00, -1.97664347e+00,
                                   2.17482262e+02, -1.41818799e+00,
                                   -8.64819949e+01]]).T
LEFT_ARM_READY_JOINTS = np.matrix([[5.0e-01, -1.30025955e-01,
                                    1.56307360e+00, -1.81768523e+00,
                                    2.16694211e+02, -1.45799866e+00,
                                    -8.64421842e+01]]).T

RIGHT_ARM_INIT_JOINTS = np.matrix([[-7.0e-01, 3.65531104e-01,
                                    -1.68462256e+00, -1.97664347e+00,
                                    -2.17482262e+02, -1.41818799e+00,
                                    -8.64819949e+01]]).T
RIGHT_ARM_READY_JOINTS = np.matrix([[-5.0e-01, -1.30025955e-01,
                                    -1.56307360e+00, -1.81768523e+00,
                                    -2.16694211e+02, -1.45799866e+00,
                                    -8.64421842e+01]]).T

INIT_POSE_MOVE_THRESH = 0.5
READY_POSE_MOVE_THRESH = 0.5

class TabletopPushNode:

    def __init__(self, no_arms = False):
        rospy.init_node('tabletop_push_node', log_level=rospy.DEBUG)
        self.gripper_x_offset = rospy.get_param('~gripper_push_start_x_offset',
                                                0.03)
        self.gripper_z_offset = rospy.get_param('~gripper_push_start_z_offset',
                                                0.03)
        self.tf_listener = tf.TransformListener()

        self.no_arms = no_arms
        use_slip=1

        if not no_arms:
            rospy.loginfo('Creating pr2 object')
            self.robot = pr2.PR2(self.tf_listener, arms=True, base=False)
            rospy.loginfo("Setting up left arm move")
            self.left_arm_move = lm.LinearReactiveMovement('l', self.robot,
                                                           self.tf_listener,
                                                           use_slip, use_slip)
            # TODO: Change gains on wrist roll to move faster than others...
            rospy.loginfo("Changing wrist roll gains")
            self.left_arm_move.reactive_gr.joint_params.default_p[-1] = 600
            self.left_arm_move.reactive_gr.joint_params.default_d[-1] = 10
            self.left_arm_move.reactive_gr.reload_joint_controllers()
            rospy.loginfo("Setting up right arm move")
            self.right_arm_move = lm.LinearReactiveMovement('r', self.robot,
                                                            self.tf_listener,
                                                            use_slip, use_slip)

        self.push_pose_proxy = rospy.ServiceProxy("get_push_pose", PushPose)
        self.gripper_push_service = rospy.Service('gripper_push',
                                                  GripperPush,
                                                  self.gripper_push_action)
        self.gripper_sweep_service = rospy.Service('gripper_sweep',
                                                   GripperPush,
                                                   self.gripper_sweep_action)

    #
    # Behavior functions
    #
    def init_arm_pose(self, force_ready=False, which_arm='l'):
        '''
        Move the arm to the initial pose to be out of the way for viewing the
        tabletop
        '''
        if which_arm == 'l':
            push_arm = self.left_arm_move
            robot_arm = self.robot.left
            robot_gripper = self.robot.left_gripper
            init_joints = LEFT_ARM_INIT_JOINTS
            ready_joints = LEFT_ARM_READY_JOINTS
        else:
            push_arm = self.right_arm_move
            robot_arm = self.robot.right
            robot_gripper = self.robot.right_gripper
            init_joints = RIGHT_ARM_INIT_JOINTS
            ready_joints = RIGHT_ARM_READY_JOINTS

        init_diff = np.linalg.norm(pr2.diff_arm_pose(robot_arm.pose(),
                                                     init_joints))
        ready_diff = np.linalg.norm(pr2.diff_arm_pose(robot_arm.pose(),
                                                      ready_joints))
        push_arm.set_movement_mode_ik()

        # Choose to move to ready first, if it is closer, then move to init
        # TODO: This could be smarter, i.e., don't move up, then down
        if force_ready or ready_diff < init_diff:
            if ready_diff > READY_POSE_MOVE_THRESH or force_ready:
                rospy.loginfo('Moving arm to ready pose')
                robot_arm.set_pose(ready_joints, nsecs=2.0, block=True)
                rospy.loginfo('Moved arm to ready pose')
            else:
                rospy.loginfo('Arm in ready pose')

        # Test for closeness before moving
        if init_diff < INIT_POSE_MOVE_THRESH:
            rospy.loginfo('Arm in init pose')
            rospy.loginfo('init_diff is: '+str(init_diff))
        else:
            rospy.loginfo('Moving arm to init pose')
            robot_arm.set_pose(init_joints, nsecs=2.0, block=True)
            rospy.loginfo('Moved arm to init pose')

        # TODO: Check to see if the gripper needs to be closed
        rospy.loginfo('Closing gripper')
        res = robot_gripper.close(block=True)
        rospy.logdebug('Gripper status is: ' + str(res))

    def gripper_push_action(self, request):
        response = GripperPushResponse()
        push_frame = request.start_point.header.frame_id
        start_point = request.start_point.point
        wrist_yaw = request.wrist_yaw
        push_dist = request.desired_push_dist

        if request.left_arm:
            push_arm = self.left_arm_move
            robot_arm = self.robot.left
            ready_joints = LEFT_ARM_READY_JOINTS
        else:
            push_arm = self.right_arm_move
            robot_arm = self.robot.right
            ready_joints = RIGHT_ARM_READY_JOINTS

        # Offset to higher position to miss the table, but were lower to avoid
        # seeing the arm for now.
        rospy.loginfo("Moving arm to ready pose")
        push_arm.set_movement_mode_ik()
        robot_arm.set_pose(ready_joints, nsecs=2.0, block=True)

        orientation = tf.transformations.quaternion_from_euler(0.0, 0.0, wrist_yaw)
        pose = np.matrix([start_point.x, start_point.y, start_point.z])
        rot = np.matrix([orientation])

        # rospy.loginfo('Pose: ' + str(pose))
        # rospy.loginfo('Ort: ' + str(rot))

        # Move to start pose
        loc = [pose, rot]
        push_arm.set_movement_mode_cart()
        push_arm.move_absolute(loc, stop='pressure', frame=push_frame)
        rospy.loginfo('Done moving to start point')

        # Push in a straight line
        rospy.loginfo('Pushing forward')
        r, pos_error = push_arm.move_relative_gripper(
            np.matrix([push_dist, 0.0, 0.0]).T,
            stop='pressure', pressure=5000)
        rospy.loginfo('Done pushing forward')

        rospy.loginfo('Moving gripper backwards')
        push_arm.move_relative_gripper(
            np.matrix([(-push_dist + pos_error), 0.0, 0.0]).T,
            stop='pressure', pressure=5000)
        rospy.loginfo('Done moving backwards')

        self.init_arm_pose(force_ready=True)
        response.dist_pushed = push_dist - pos_error
        return response

    def gripper_sweep_action(self, request):
        response = GripperPushResponse()
        push_frame = request.start_point.header.frame_id
        start_point = request.start_point.point
        wrist_yaw = request.wrist_yaw
        push_dist = request.desired_push_dist

        if request.left_arm:
            push_arm = self.left_arm_move
            robot_arm = self.robot.left
            ready_joints = LEFT_ARM_READY_JOINTS
            push_dir = -1
        else:
            push_arm = self.right_arm_move
            robot_arm = self.robot.right
            ready_joints = LEFT_ARM_READY_JOINTS
            # ready_joints = RIGHT_ARM_READY_JOINTS
            push_dir = +1

        # Offset to higher position to miss the table, but were lower to avoid
        # seeing the arm for now.
        rospy.loginfo("Moving arm to ready pose")
        push_arm.set_movement_mode_ik()
        robot_arm.set_pose(ready_joints, nsecs=2.0, block=True)

        orientation = tf.transformations.quaternion_from_euler(0.5*pi, 0.0,
                                                               wrist_yaw)
        pose = np.matrix([start_point.x, start_point.y, start_point.z])
        rot = np.matrix([orientation])

        # TODO: Rotate wrist before moving to position
        # arm_pose = robot_arm.pose()
        # arm_pose[-1] = 0.5*pi
        # push_arm.set_movement_mode_ik()
        # robot_arm.set_pose(arm_pose, nsecs=2.0, block=True)

        # Move to offset pose
        loc = [pose, rot]
        push_arm.set_movement_mode_cart()
        push_arm.move_absolute(loc, stop='pressure', frame=push_frame)
        rospy.loginfo('Done moving to start point')

        # Sweep inward
        # TODO: Change this to split the push_dist in x and y depending on yaw
        # and move relative gripper?
        rospy.loginfo('Sweeping in')
        r, pos_error = push_arm.move_relative_base(
            np.matrix([0.0, push_dir*push_dist, 0.0]).T,
            #'l_wrist_roll_link',
            stop='pressure', pressure=5000)
        rospy.loginfo('Done sweeping in')

        rospy.loginfo('Sweeping outward')
        push_arm.move_relative_base(
            np.matrix([0.0, (-push_dir*push_dist + pos_error), 0.0]).T,
            #'l_wrist_roll_link',
            stop='pressure', pressure=5000)
        rospy.loginfo('Done sweeping outward')

        self.init_arm_pose(force_ready=True)
        response.dist_pushed = push_dist - pos_error
        return response

    #
    # Util Functions
    #
    def print_pose(self):
        cart_pose = self.left_arm_move.current_location()
        pose = self.robot.left.pose()
        rospy.loginfo(str(pose))
        rospy.loginfo('Left Cart_position: ' + str(cart_pose[0]))
        rospy.loginfo('Left Cart_orientation: ' + str(cart_pose[1]))
        cart_pose = self.right_arm_move.current_location()
        pose = self.robot.right.pose()
        rospy.loginfo(str(pose))
        rospy.loginfo('Right Cart_position: ' + str(cart_pose[0]))
        rospy.loginfo('Right Cart_orientation: ' + str(cart_pose[1]))

    #
    # Main Control Loop
    #
    def run(self):
        """
        Main control loop for the node
        """
        if not self.no_arms:
            self.init_arm_pose(True, which_arm='l')
            # self.init_arm_pose(True, which_arm='r')
        rospy.spin()

if __name__ == '__main__':
    node = TabletopPushNode(no_arms=False)
    node.run()
<|endoftext|>


<commit_before>from __future__ import absolute_import
from __future__ import unicode_literals
import openpyxl
import polib

from io import open
from memoized import memoized
from openpyxl import Workbook

from django.http import HttpResponse
from django.utils.translation import ugettext as _, ugettext_noop
from django.contrib import messages
from django.utils.decorators import method_decorator

from corehq import toggles
from corehq.apps.hqwebapp.decorators import use_select2
from corehq.apps.translations.forms import (
    ConvertTranslationsForm,
    PullResourceForm,
)
from corehq.apps.app_manager.app_translations.generators import Translation, PoFileGenerator
from corehq.util.files import safe_filename_header
from corehq.apps.domain.views import BaseDomainView
from custom.icds.translations.integrations.exceptions import ResourceMissing
from custom.icds.translations.integrations.transifex import Transifex
from custom.icds.translations.integrations.utils import get_file_content_from_workbook, \
    transifex_details_available_for_domain

class BaseTranslationsView(BaseDomainView):
    @property
    def page_context(self):
        context = {
            'transifex_details_available': self.transifex_details_available,
        }
        return context

    @property
    @memoized
    def transifex_details_available(self):
        return transifex_details_available_for_domain(self.domain)

    def transifex_integration_enabled(self, request):
        if not self.transifex_details_available:
            messages.error(request, _('Transifex integration not set for this domain'))
            return False
        return True

class ConvertTranslations(BaseTranslationsView):
    page_title = _('Convert Translations')
    urlname = 'convert_translations'
    template_name = 'convert_translations.html'
    section_name = ugettext_noop("Translations")

    @property
    @memoized
    def convert_translation_form(self):
        if self.request.POST:
            return ConvertTranslationsForm(self.request.POST, self.request.FILES)
        else:
            return ConvertTranslationsForm()

    @property
    @memoized
    def _uploaded_file_name(self):
        uploaded_file = self.convert_translation_form.cleaned_data.get('upload_file')
        return uploaded_file.name

    @staticmethod
    def _parse_excel_sheet(worksheet):
        """
        :return: the rows and the index for expected columns
        """
        rows = [row for row in worksheet.iter_rows()]
        headers = [cell.value for cell in rows[0]]
        source = headers.index('source')
        translation = headers.index('translation')
        occurrence = headers.index('occurrence') if 'occurrence' in headers else None
        context = headers.index('context') if 'context' in headers else None
        return rows, source, translation, occurrence, context

    def _generate_translations_for_po(self, worksheet):
        """
        :return: a list of Translation objects
        """
        rows, source, translation, occurrence, context = self._parse_excel_sheet(worksheet)
        translations = {worksheet.title: []}
        for index, row in enumerate(rows[1:]):
            _occurrence = row[occurrence].value if occurrence is not None else ''
            _context = row[context].value if context is not None else ''
            translations[worksheet.title].append(
                Translation(
                    row[source].value,
                    row[translation].value,
                    [(_occurrence, None)],
                    _context)
            )
        return translations

    def _generate_po_file(self, worksheet):
        """
        extract translations from worksheet and converts to a po file
        :return: PoFileGenerator object
        """
        translations = self._generate_translations_for_po(worksheet)
        po_file_generator = PoFileGenerator()
        po_file_generator.generate_translation_files(translations, {})
        return po_file_generator

    def _generate_excel_file(self):
        """
        extract translations from po file and converts to a xlsx file
        :return: Workbook object
        """
        uploaded_file = self.convert_translation_form.cleaned_data.get('upload_file')
        po_file = polib.pofile(uploaded_file.read())
        wb = openpyxl.Workbook()
        ws = wb.worksheets[0]
        ws.title = "Translations"
        ws.append(['source', 'translation', 'context', 'occurrence'])
        for po_entry in po_file:
            ws.append([po_entry.msgid, po_entry.msgstr, po_entry.msgctxt,
                       po_entry.occurrences[0][0] if po_entry.occurrences else ''])
        return wb

    def _po_file_response(self):
        uploaded_file = self.convert_translation_form.cleaned_data.get('upload_file')
        worksheet = openpyxl.load_workbook(uploaded_file).worksheets[0]
        po_file_generator = self._generate_po_file(worksheet)
        content = open(po_file_generator.generated_files[0][1], 'r', encoding="utf-8").read()
        po_file_generator.cleanup()
        response = HttpResponse(content, content_type="text/html; charset=utf-8")
        response['Content-Disposition'] = safe_filename_header(worksheet.title, 'po')
        return response

    def _excel_file_response(self):
        wb = self._generate_excel_file()
        content = get_file_content_from_workbook(wb)
        response = HttpResponse(content, content_type="text/html; charset=utf-8")
        response['Content-Disposition'] = safe_filename_header(self._uploaded_file_name.split('.po')[0], 'xlsx')
        return response

    def post(self, request, *args, **kwargs):
        if self.convert_translation_form.is_valid():
            uploaded_filename = self._uploaded_file_name
            if uploaded_filename.endswith('.xls') or uploaded_filename.endswith('.xlsx'):
                return self._po_file_response()
            elif uploaded_filename.endswith('.po'):
                return self._excel_file_response()
        return self.get(request, *args, **kwargs)

    def section_url(self):
        return self.page_url

    @property
    def page_context(self):
        context = super(ConvertTranslations, self).page_context
        context['convert_translations_form'] = self.convert_translation_form
        return context

@method_decorator([toggles.APP_TRANSLATIONS_WITH_TRANSIFEX.required_decorator()], name='dispatch')
class PullResource(BaseTranslationsView):
    page_title = _('Pull Resource')
    urlname = 'pull_resource'
    template_name = 'pull_resource.html'
    section_name = ugettext_noop("Translations")

    @use_select2
    def dispatch(self, request, *args, **kwargs):
        return super(PullResource, self).dispatch(request, *args, **kwargs)

    def section_url(self):
        return self.page_url

    @property
    @memoized
    def pull_resource_form(self):
        if self.request.POST:
            return PullResourceForm(self.domain, self.request.POST)
        else:
            return PullResourceForm(self.domain)

    @property
    def page_context(self):
        context = super(PullResource, self).page_context
        if context['transifex_details_available']:
            context['pull_resource_form'] = self.pull_resource_form
        return context

    def _generate_excel_file(self, domain, resource_slug):
        """
        extract translations from po file pulled from transifex and converts to a xlsx file
        :return: Workbook object
        """
        target_lang = self.pull_resource_form.cleaned_data['target_lang']
        transifex = Transifex(domain=domain, app_id=None,
                              source_lang=target_lang,
                              project_slug=self.pull_resource_form.cleaned_data['transifex_project_slug'],
                              version=None)
        wb = Workbook(write_only=True)
        ws = wb.create_sheet(title='translations')
        ws.append(['context', 'source', 'translation', 'occurrence'])
        for po_entry in transifex.client.get_translation(resource_slug, target_lang, False):
            ws.append([po_entry.msgctxt, po_entry.msgid, po_entry.msgstr,
                       po_entry.occurrences[0][0] if po_entry.occurrences else ''])
        return wb

    def _pull_resource(self, request):
        resource_slug = self.pull_resource_form.cleaned_data['resource_slug']
        wb = self._generate_excel_file(request.domain, resource_slug)
        content = get_file_content_from_workbook(wb)
        response = HttpResponse(content, content_type="text/html; charset=utf-8")
        response['Content-Disposition'] = safe_filename_header(resource_slug, 'xlsx')
        return response

    def post(self, request, *args, **kwargs):
        if self.transifex_integration_enabled(request):
            if self.pull_resource_form.is_valid():
                try:
                    return self._pull_resource(request)
                except ResourceMissing:
                    messages.add_message(request, messages.ERROR, 'Resource not found')
        return self.get(request, *args, **kwargs)
<commit_msg>use with open instead<commit_after>from __future__ import absolute_import
from __future__ import unicode_literals
import openpyxl
import polib

from io import open
from memoized import memoized
from openpyxl import Workbook

from django.http import HttpResponse
from django.utils.translation import ugettext as _, ugettext_noop
from django.contrib import messages
from django.utils.decorators import method_decorator

from corehq import toggles
from corehq.apps.hqwebapp.decorators import use_select2
from corehq.apps.translations.forms import (
    ConvertTranslationsForm,
    PullResourceForm,
)
from corehq.apps.app_manager.app_translations.generators import Translation, PoFileGenerator
from corehq.util.files import safe_filename_header
from corehq.apps.domain.views import BaseDomainView
from custom.icds.translations.integrations.exceptions import ResourceMissing
from custom.icds.translations.integrations.transifex import Transifex
from custom.icds.translations.integrations.utils import get_file_content_from_workbook, \
    transifex_details_available_for_domain

class BaseTranslationsView(BaseDomainView):
    @property
    def page_context(self):
        context = {
            'transifex_details_available': self.transifex_details_available,
        }
        return context

    @property
    @memoized
    def transifex_details_available(self):
        return transifex_details_available_for_domain(self.domain)

    def transifex_integration_enabled(self, request):
        if not self.transifex_details_available:
            messages.error(request, _('Transifex integration not set for this domain'))
            return False
        return True

class ConvertTranslations(BaseTranslationsView):
    page_title = _('Convert Translations')
    urlname = 'convert_translations'
    template_name = 'convert_translations.html'
    section_name = ugettext_noop("Translations")

    @property
    @memoized
    def convert_translation_form(self):
        if self.request.POST:
            return ConvertTranslationsForm(self.request.POST, self.request.FILES)
        else:
            return ConvertTranslationsForm()

    @property
    @memoized
    def _uploaded_file_name(self):
        uploaded_file = self.convert_translation_form.cleaned_data.get('upload_file')
        return uploaded_file.name

    @staticmethod
    def _parse_excel_sheet(worksheet):
        """
        :return: the rows and the index for expected columns
        """
        rows = [row for row in worksheet.iter_rows()]
        headers = [cell.value for cell in rows[0]]
        source = headers.index('source')
        translation = headers.index('translation')
        occurrence = headers.index('occurrence') if 'occurrence' in headers else None
        context = headers.index('context') if 'context' in headers else None
        return rows, source, translation, occurrence, context

    def _generate_translations_for_po(self, worksheet):
        """
        :return: a list of Translation objects
        """
        rows, source, translation, occurrence, context = self._parse_excel_sheet(worksheet)
        translations = {worksheet.title: []}
        for index, row in enumerate(rows[1:]):
            _occurrence = row[occurrence].value if occurrence is not None else ''
            _context = row[context].value if context is not None else ''
            translations[worksheet.title].append(
                Translation(
                    row[source].value,
                    row[translation].value,
                    [(_occurrence, None)],
                    _context)
            )
        return translations

    def _generate_po_file(self, worksheet):
        """
        extract translations from worksheet and converts to a po file
        :return: PoFileGenerator object
        """
        translations = self._generate_translations_for_po(worksheet)
        po_file_generator = PoFileGenerator()
        po_file_generator.generate_translation_files(translations, {})
        return po_file_generator

    def _generate_excel_file(self):
        """
        extract translations from po file and converts to a xlsx file
        :return: Workbook object
        """
        uploaded_file = self.convert_translation_form.cleaned_data.get('upload_file')
        po_file = polib.pofile(uploaded_file.read())
        wb = openpyxl.Workbook()
        ws = wb.worksheets[0]
        ws.title = "Translations"
        ws.append(['source', 'translation', 'context', 'occurrence'])
        for po_entry in po_file:
            ws.append([po_entry.msgid, po_entry.msgstr, po_entry.msgctxt,
                       po_entry.occurrences[0][0] if po_entry.occurrences else ''])
        return wb

    def _po_file_response(self):
        uploaded_file = self.convert_translation_form.cleaned_data.get('upload_file')
        worksheet = openpyxl.load_workbook(uploaded_file).worksheets[0]
        po_file_generator = self._generate_po_file(worksheet)
        with open(po_file_generator.generated_files[0][1], 'r', encoding="utf-8") as f:
            content = f.read()
        po_file_generator.cleanup()
        response = HttpResponse(content, content_type="text/html; charset=utf-8")
        response['Content-Disposition'] = safe_filename_header(worksheet.title, 'po')
        return response

    def _excel_file_response(self):
        wb = self._generate_excel_file()
        content = get_file_content_from_workbook(wb)
        response = HttpResponse(content, content_type="text/html; charset=utf-8")
        response['Content-Disposition'] = safe_filename_header(self._uploaded_file_name.split('.po')[0], 'xlsx')
        return response

    def post(self, request, *args, **kwargs):
        if self.convert_translation_form.is_valid():
            uploaded_filename = self._uploaded_file_name
            if uploaded_filename.endswith('.xls') or uploaded_filename.endswith('.xlsx'):
                return self._po_file_response()
            elif uploaded_filename.endswith('.po'):
                return self._excel_file_response()
        return self.get(request, *args, **kwargs)

    def section_url(self):
        return self.page_url

    @property
    def page_context(self):
        context = super(ConvertTranslations, self).page_context
        context['convert_translations_form'] = self.convert_translation_form
        return context

@method_decorator([toggles.APP_TRANSLATIONS_WITH_TRANSIFEX.required_decorator()], name='dispatch')
class PullResource(BaseTranslationsView):
    page_title = _('Pull Resource')
    urlname = 'pull_resource'
    template_name = 'pull_resource.html'
    section_name = ugettext_noop("Translations")

    @use_select2
    def dispatch(self, request, *args, **kwargs):
        return super(PullResource, self).dispatch(request, *args, **kwargs)

    def section_url(self):
        return self.page_url

    @property
    @memoized
    def pull_resource_form(self):
        if self.request.POST:
            return PullResourceForm(self.domain, self.request.POST)
        else:
            return PullResourceForm(self.domain)

    @property
    def page_context(self):
        context = super(PullResource, self).page_context
        if context['transifex_details_available']:
            context['pull_resource_form'] = self.pull_resource_form
        return context

    def _generate_excel_file(self, domain, resource_slug):
        """
        extract translations from po file pulled from transifex and converts to a xlsx file
        :return: Workbook object
        """
        target_lang = self.pull_resource_form.cleaned_data['target_lang']
        transifex = Transifex(domain=domain, app_id=None,
                              source_lang=target_lang,
                              project_slug=self.pull_resource_form.cleaned_data['transifex_project_slug'],
                              version=None)
        wb = Workbook(write_only=True)
        ws = wb.create_sheet(title='translations')
        ws.append(['context', 'source', 'translation', 'occurrence'])
        for po_entry in transifex.client.get_translation(resource_slug, target_lang, False):
            ws.append([po_entry.msgctxt, po_entry.msgid, po_entry.msgstr,
                       po_entry.occurrences[0][0] if po_entry.occurrences else ''])
        return wb

    def _pull_resource(self, request):
        resource_slug = self.pull_resource_form.cleaned_data['resource_slug']
        wb = self._generate_excel_file(request.domain, resource_slug)
        content = get_file_content_from_workbook(wb)
        response = HttpResponse(content, content_type="text/html; charset=utf-8")
        response['Content-Disposition'] = safe_filename_header(resource_slug, 'xlsx')
        return response

    def post(self, request, *args, **kwargs):
        if self.transifex_integration_enabled(request):
            if self.pull_resource_form.is_valid():
                try:
                    return self._pull_resource(request)
                except ResourceMissing:
                    messages.add_message(request, messages.ERROR, 'Resource not found')
        return self.get(request, *args, **kwargs)
<|endoftext|>


<commit_before>from __future__ import print_function, division
import time, threading, functools
import numpy as np
import scipy.optimize
from acq4.pyqtgraph import ptime
from ...Manager import getManager
from acq4.util import Qt
from acq4.util.Thread import Thread
from acq4.util.Mutex import Mutex
from acq4.util.debug import printExc

class TestPulseThread(Thread):
    """Background thread that runs periodic test pulses on a single patch clamp channel.
    """

    sigTestPulseFinished = Qt.Signal(object, object)  # device, result

    class StopRequested(Exception):
        pass

    def __init__(self, dev, params):
        Thread.__init__(self, name="TestPulseThread(%s)"%dev.name())
        self.dev = dev
        self._stop = False
        self.params = {
            'testPulseClass': TestPulse,
            'clampMode': None,
            'interval': None,
            'autoBiasEnabled': True,
            'autoBiasTarget': -70e-3,  # if None, use VC holding
            'autoBiasFollowRate': 0.5,
            'autoBiasMinCurrent': -1.5e-9,
            'autoBiasMaxCurrent': 1.5e9,
            'autoBiasVCCarryover': 0.7,
            'sampleRate': 500000,
            'downsample': 20,
            'vcPreDuration': 5e-3,
            'vcPulseDuration': 10e-3,
            'vcPostDuration': 5e-3,
            'vcHolding': None,
            'vcAmplitude': -10e-3,
            'vcAverage': 4,
            'icPreDuration': 10e-3,
            'icPulseDuration': 80e-3,
            'icPostDuration': 80e-3,
            'icHolding': None,
            'icAmplitude': -10e-12,
            'icAverage': 4,
            '_index': 0,
        }
        self._lastTask = None

        self._clampDev = self.dev.clampDevice
        self._daqName = list(self._clampDev.listChannels().values())[0]['device']  ## Just guess the DAQ by checking one of the clamp's channels
        self._clampName = self._clampDev.name()
        self._manager = getManager()

        self.setParameters(**params)

    def setParameters(self, **kwds):
        newParams = self.params.copy()
        for k,v in kwds.items():
            if k not in self.params:
                raise KeyError("Unknown parameter %s" % k)
            newParams[k] = v
        newParams['_index'] += 1
        self.params = newParams

    def getParameter(self, param):
        return self.params[param]

    def start(self):
        self._stop = False
        Thread.start(self)

    def stop(self, block=False):
        self._stop = True
        if block:
            if not self.wait(10000):
                raise RuntimeError("Timed out waiting for test pulse thread exit.")

    def run(self):
        while True:
            try:
                self._checkStop()
                start = ptime.time()
                self.runOnce(_checkStop=True)

                interval = self.params['interval']
                if interval is None:
                    # start again immediately
                    continue

                # otherwise, wait until interval is over
                while True:
                    nextRun = start + self.params['interval']
                    now = ptime.time()
                    if now >= nextRun:
                        break
                    time.sleep(min(0.03, nextRun-now))
                    self._checkStop()
            except self.StopRequested:
                break
            except Exception:
                printExc("Error in test pulse thread:")
                time.sleep(2.0)

    def runOnce(self, _checkStop=False):
        currentMode = self._clampDev.getMode()
        params = self.params
        runMode = currentMode if params['clampMode'] is None else params['clampMode']
        if runMode == 'I=0':
            runMode = 'IC'

        # Can't reuse tasks yet; remove this when we can.
        self._lastTask = None

        if self._lastTask is None or self._lastTask._paramIndex != params['_index'] or self._lastTask._clampMode != runMode:
            taskParams = params.copy()

            # select parameters to use based on clamp mode
            for k in params:
                # rename like icPulseDuration => pulseDuration
                if k[:2] == runMode.lower():
                    taskParams[k[2].lower() + k[3:]] = taskParams[k]
                # remove all ic__ and vc__ params
                if k[:2] in ('ic', 'vc'):
                    taskParams.pop(k)
                taskParams['clampMode'] = runMode

            task = self.createTask(taskParams)
            task._paramIndex = params['_index']
            task._clampMode = runMode
            self._lastTask = task
            self._lastTaskParams = taskParams
        else:
            task = self._lastTask
            taskParams = self._lastTaskParams

        # if clamp mode changed while we were fiddling around, then abort.
        task.reserveDevices()
        try:
            if self._clampDev.getMode() != currentMode:
                task.releaseDevices()
                return

            task.execute()

            while not task.isDone():
                if _checkStop:
                    self._checkStop()
                time.sleep(0.01)

            tp = None
            if params['autoBiasEnabled']:
                # update bias before unlocking
                result = task.getResult()
                tp = self._makeTpResult(taskParams, result)
                self.updateAutoBias(tp)
        finally:
            task.releaseDevices()

        if tp is None:
            # no auto bias, release before doing analysis
            result = task.getResult()
            tp = self._makeTpResult(taskParams, result)

        self.sigTestPulseFinished.emit(self.dev, tp)

    def _makeTpResult(self, taskParams, result):
        tpClass = taskParams['testPulseClass']
        tp = tpClass(self._clampDev, taskParams, result)
        tp.analysis()
        return tp

    def createTask(self, params):
        duration = params['preDuration'] + params['pulseDuration'] + params['postDuration']
        numPts = int(float(duration * params['sampleRate']) * params['downsample']) // params['downsample']
        params['numPts'] = numPts  # send this back for analysis
        mode = params['clampMode']

        cmdData = np.empty(numPts * params['average'])
        holding = params['holding'] or self._clampDev.getHolding(mode)
        cmdData[:] = holding

        for i in range(params['average']):
            start = (numPts * i) + int(params['preDuration'] * params['sampleRate'])
            stop = start + int(params['pulseDuration'] * params['sampleRate'])
            cmdData[start:stop] += params['amplitude']

        cmd = {
            'protocol': {'duration': duration * params['average']},
            self._daqName: {'rate': params['sampleRate'], 'numPts': numPts * params['average'], 'downsample': params['downsample']},
            self._clampName: {
                'mode': mode,
                'command': cmdData,
                'recordState': ['BridgeBalResist', 'BridgeBalEnable'],
            }
        }
        if params['holding'] is not None:
            cmd[self._clampName]['holding'] = params['holding']

        return self._manager.createTask(cmd)

    def _checkStop(self):
        if self._stop:
            raise self.StopRequested()

    def updateAutoBias(self, tp):
        analysis = tp.analysis()
        mode = tp.clampMode()
        if mode.lower() == 'vc':
            # set ic holding from baseline current, multiplied by some factor for a little more added safety.
            self.dev.clampDevice.setHolding('IC', analysis['baselineCurrent'] * self.params['autoBiasVCCarryover'])
        else:
            target = self.params['autoBiasTarget']
            if target is None:
                target = self.params['vcHolding']

            rm = np.clip(analysis['steadyStateResistance'], 1e6, 10e9)
            vm = analysis['baselinePotential']

            dv = target - vm
            di = dv / rm

            holding = self.dev.clampDevice.getHolding(mode)
            newHolding = holding + di * self.params['autoBiasFollowRate']
            newHolding = np.clip(newHolding, self.params['autoBiasMinCurrent'], self.params['autoBiasMaxCurrent'])

            self.dev.clampDevice.setHolding(mode, newHolding)

class TestPulse(object):
    """Represents a single test pulse run, used to analyze and extract features.
    """
    def __init__(self, dev, taskParams, result):
        self.dev = dev
        self.devName = dev.name()
        self.taskParams = taskParams
        self.result = result
        self._analysis = None
        self._analysisLock = Mutex()
        self._average = None

    @property
    def data(self):
        if self._average is None:
            params = self.taskParams
            result = self.result[self.devName]
            if params['average'] == 1:
                self._average = result
            else:
                numPts = params['numPts'] // params['downsample']
                pri = result['Channel': 'primary']
                avg = np.zeros(numPts)
                for i in range(params['average']):
                    avg += pri[i*numPts:(i+1)*numPts]
                avg /= params['average']
                self._average = result['Time':0:numPts].copy()
                self._average['Channel': 'primary']._data[:] = avg

        return self._average

    def startTime(self):
        return self.result[self.devName]._info[-1]['startTime']

    def clampMode(self):
        return self.taskParams['clampMode']

    def analysis(self):
        with self._analysisLock:
            if self._analysis is not None:
                return self._analysis
            analysis = {}
            params = self.taskParams
            pri = self.data['Channel': 'primary']

            base = pri['Time': 0:params['preDuration']]
            peakStart = params['preDuration']
            peakStop = peakStart + 2e-3
            peak = pri['Time': peakStart:peakStop]
            ssStop = params['preDuration'] + params['pulseDuration']
            ssStart = ssStop - 2e-3
            steady  = pri['Time': ssStart:ssStop]

            peakValue = peak.max()
            steadyValue = np.median(steady)
            baseValue = np.median(base)

            if params['clampMode'] == 'VC':
                analysis['baselinePotential'] = params['holding'] or 0
                analysis['baselineCurrent'] = baseValue
                analysis['peakResistance'] = params['amplitude'] / (peakValue - baseValue)
                analysis['steadyStateResistance'] = np.abs(params['amplitude'] / (steadyValue - baseValue))
                tauGuess = 3e-3
            else:
                bridge = self.data._info[-1]['ClampState']['ClampParams']['BridgeBalResist']
                bridgeOn = self.data._info[-1]['ClampState']['ClampParams']['BridgeBalEnable']
                if not bridgeOn:
                    bridge = 0.0
                analysis['baselineCurrent'] = params['holding'] or 0
                analysis['baselinePotential'] = baseValue
                analysis['peakResistance'] = bridge + (peakValue - baseValue) / params['amplitude']
                analysis['steadyStateResistance'] = np.abs(bridge + (steadyValue - baseValue) / params['amplitude'])
                tauGuess = 15e-3

            analysis['peakResistance'] = np.clip(analysis['peakResistance'], 0, 20e9)
            analysis['steadyStateResistance'] = np.clip(analysis['steadyStateResistance'], 0, 20e9)

            # do curve fitting
            pulseStart = params['preDuration'] + 150e-6
            pulseStop = params['preDuration'] + params['pulseDuration']
            pulse = pri['Time': pulseStart:pulseStop]
            t = pulse.xvals('Time')
            guess = (
                peakValue - steadyValue,  # amp
                tauGuess,  # tau
                steadyValue,  # yoffset
            )
            xoffset = params['preDuration']
            fit = scipy.optimize.curve_fit(exp, t-xoffset, pulse.asarray(), guess)
            amp, tau, yoffset = fit[0]
            analysis['fitExpAmp'] = amp
            analysis['fitExpTau'] = tau
            analysis['fitExpYOffset'] = yoffset
            analysis['fitExpXOffset'] = xoffset

            if params['clampMode'] == 'VC':
                # VC capacitance calculation adapted from Santos-Sacchi 1993
                # (not very accurate, probably because Q is calculated incorrectly)
                Q = (pulse.asarray() - yoffset).sum() * (t[-1]-t[0]) / len(pulse)
                Rin = analysis['steadyStateResistance']
                Vc = params['amplitude']
                Rs_denom = (Q * Rin + tau * Vc)
                if Rs_denom != 0.0:
                    Rs = (Rin * tau * Vc) / Rs_denom
                    Rm = Rin - Rs
                    Cm = (Rin**2 * Q) / (Rm**2 * Vc)
                else:
                    Rs = 0
                    Rm = 0
                    Cm = 0
                analysis['capacitance'] = Cm
            else:
                analysis['capacitance'] = tau / analysis['steadyStateResistance']

            self._analysis = analysis
            return analysis

    def getFitData(self):
        params = self.taskParams
        analysis = self.analysis()
        pri = self.data['Channel': 'primary']
        pulseStart = params['preDuration'] + 150e-6
        pulseStop = params['preDuration'] + params['pulseDuration']
        pulse = pri['Time': pulseStart:pulseStop]
        t = pulse.xvals('Time')
        y = exp(t - analysis['fitExpXOffset'], analysis['fitExpAmp'], analysis['fitExpTau'], analysis['fitExpYOffset'])
        return t,y

def exp(t, amp, tau, yoffset):
    return yoffset + amp * np.exp(-t / tau)<commit_msg>Clean up TP exp fitting<commit_after>from __future__ import print_function, division
import time, threading, functools
import numpy as np
import scipy.optimize
from acq4.pyqtgraph import ptime
from ...Manager import getManager
from acq4.util import Qt
from acq4.util.Thread import Thread
from acq4.util.Mutex import Mutex
from acq4.util.debug import printExc

class TestPulseThread(Thread):
    """Background thread that runs periodic test pulses on a single patch clamp channel.
    """

    sigTestPulseFinished = Qt.Signal(object, object)  # device, result

    class StopRequested(Exception):
        pass

    def __init__(self, dev, params):
        Thread.__init__(self, name="TestPulseThread(%s)"%dev.name())
        self.dev = dev
        self._stop = False
        self.params = {
            'testPulseClass': TestPulse,
            'clampMode': None,
            'interval': None,
            'autoBiasEnabled': True,
            'autoBiasTarget': -70e-3,  # if None, use VC holding
            'autoBiasFollowRate': 0.5,
            'autoBiasMinCurrent': -1.5e-9,
            'autoBiasMaxCurrent': 1.5e9,
            'autoBiasVCCarryover': 0.7,
            'sampleRate': 500000,
            'downsample': 20,
            'vcPreDuration': 5e-3,
            'vcPulseDuration': 10e-3,
            'vcPostDuration': 5e-3,
            'vcHolding': None,
            'vcAmplitude': -10e-3,
            'vcAverage': 4,
            'icPreDuration': 10e-3,
            'icPulseDuration': 80e-3,
            'icPostDuration': 80e-3,
            'icHolding': None,
            'icAmplitude': -10e-12,
            'icAverage': 4,
            '_index': 0,
        }
        self._lastTask = None

        self._clampDev = self.dev.clampDevice
        self._daqName = list(self._clampDev.listChannels().values())[0]['device']  ## Just guess the DAQ by checking one of the clamp's channels
        self._clampName = self._clampDev.name()
        self._manager = getManager()

        self.setParameters(**params)

    def setParameters(self, **kwds):
        newParams = self.params.copy()
        for k,v in kwds.items():
            if k not in self.params:
                raise KeyError("Unknown parameter %s" % k)
            newParams[k] = v
        newParams['_index'] += 1
        self.params = newParams

    def getParameter(self, param):
        return self.params[param]

    def start(self):
        self._stop = False
        Thread.start(self)

    def stop(self, block=False):
        self._stop = True
        if block:
            if not self.wait(10000):
                raise RuntimeError("Timed out waiting for test pulse thread exit.")

    def run(self):
        while True:
            try:
                self._checkStop()
                start = ptime.time()
                self.runOnce(_checkStop=True)

                interval = self.params['interval']
                if interval is None:
                    # start again immediately
                    continue

                # otherwise, wait until interval is over
                while True:
                    nextRun = start + self.params['interval']
                    now = ptime.time()
                    if now >= nextRun:
                        break
                    time.sleep(min(0.03, nextRun-now))
                    self._checkStop()
            except self.StopRequested:
                break
            except Exception:
                printExc("Error in test pulse thread:")
                time.sleep(2.0)

    def runOnce(self, _checkStop=False):
        currentMode = self._clampDev.getMode()
        params = self.params
        runMode = currentMode if params['clampMode'] is None else params['clampMode']
        if runMode == 'I=0':
            runMode = 'IC'

        # Can't reuse tasks yet; remove this when we can.
        self._lastTask = None

        if self._lastTask is None or self._lastTask._paramIndex != params['_index'] or self._lastTask._clampMode != runMode:
            taskParams = params.copy()

            # select parameters to use based on clamp mode
            for k in params:
                # rename like icPulseDuration => pulseDuration
                if k[:2] == runMode.lower():
                    taskParams[k[2].lower() + k[3:]] = taskParams[k]
                # remove all ic__ and vc__ params
                if k[:2] in ('ic', 'vc'):
                    taskParams.pop(k)
                taskParams['clampMode'] = runMode

            task = self.createTask(taskParams)
            task._paramIndex = params['_index']
            task._clampMode = runMode
            self._lastTask = task
            self._lastTaskParams = taskParams
        else:
            task = self._lastTask
            taskParams = self._lastTaskParams

        # if clamp mode changed while we were fiddling around, then abort.
        task.reserveDevices()
        try:
            if self._clampDev.getMode() != currentMode:
                task.releaseDevices()
                return

            task.execute()

            while not task.isDone():
                if _checkStop:
                    self._checkStop()
                time.sleep(0.01)

            tp = None
            if params['autoBiasEnabled']:
                # update bias before unlocking
                result = task.getResult()
                tp = self._makeTpResult(taskParams, result)
                self.updateAutoBias(tp)
        finally:
            task.releaseDevices()

        if tp is None:
            # no auto bias, release before doing analysis
            result = task.getResult()
            tp = self._makeTpResult(taskParams, result)

        self.sigTestPulseFinished.emit(self.dev, tp)

    def _makeTpResult(self, taskParams, result):
        tpClass = taskParams['testPulseClass']
        tp = tpClass(self._clampDev, taskParams, result)
        tp.analysis()
        return tp

    def createTask(self, params):
        duration = params['preDuration'] + params['pulseDuration'] + params['postDuration']
        numPts = int(float(duration * params['sampleRate']) * params['downsample']) // params['downsample']
        params['numPts'] = numPts  # send this back for analysis
        mode = params['clampMode']

        cmdData = np.empty(numPts * params['average'])
        holding = params['holding'] or self._clampDev.getHolding(mode)
        cmdData[:] = holding

        for i in range(params['average']):
            start = (numPts * i) + int(params['preDuration'] * params['sampleRate'])
            stop = start + int(params['pulseDuration'] * params['sampleRate'])
            cmdData[start:stop] += params['amplitude']

        cmd = {
            'protocol': {'duration': duration * params['average']},
            self._daqName: {'rate': params['sampleRate'], 'numPts': numPts * params['average'], 'downsample': params['downsample']},
            self._clampName: {
                'mode': mode,
                'command': cmdData,
                'recordState': ['BridgeBalResist', 'BridgeBalEnable'],
            }
        }
        if params['holding'] is not None:
            cmd[self._clampName]['holding'] = params['holding']

        return self._manager.createTask(cmd)

    def _checkStop(self):
        if self._stop:
            raise self.StopRequested()

    def updateAutoBias(self, tp):
        analysis = tp.analysis()
        mode = tp.clampMode()
        if mode.lower() == 'vc':
            # set ic holding from baseline current, multiplied by some factor for a little more added safety.
            self.dev.clampDevice.setHolding('IC', analysis['baselineCurrent'] * self.params['autoBiasVCCarryover'])
        else:
            target = self.params['autoBiasTarget']
            if target is None:
                target = self.params['vcHolding']

            rm = np.clip(analysis['steadyStateResistance'], 1e6, 10e9)
            vm = analysis['baselinePotential']

            dv = target - vm
            di = dv / rm

            holding = self.dev.clampDevice.getHolding(mode)
            newHolding = holding + di * self.params['autoBiasFollowRate']
            newHolding = np.clip(newHolding, self.params['autoBiasMinCurrent'], self.params['autoBiasMaxCurrent'])

            self.dev.clampDevice.setHolding(mode, newHolding)

class TestPulse(object):
    """Represents a single test pulse run, used to analyze and extract features.
    """
    def __init__(self, dev, taskParams, result):
        self.dev = dev
        self.devName = dev.name()
        self.taskParams = taskParams
        self.result = result
        self._analysis = None
        self._analysisLock = Mutex()
        self._average = None

    @property
    def data(self):
        if self._average is None:
            params = self.taskParams
            result = self.result[self.devName]
            if params['average'] == 1:
                self._average = result
            else:
                numPts = params['numPts'] // params['downsample']
                pri = result['Channel': 'primary']
                avg = np.zeros(numPts)
                for i in range(params['average']):
                    avg += pri[i*numPts:(i+1)*numPts]
                avg /= params['average']
                self._average = result['Time':0:numPts].copy()
                self._average['Channel': 'primary']._data[:] = avg

        return self._average

    def startTime(self):
        return self.result[self.devName]._info[-1]['startTime']

    def clampMode(self):
        return self.taskParams['clampMode']

    def analysis(self):
        with self._analysisLock:
            if self._analysis is not None:
                return self._analysis
            analysis = {}
            params = self.taskParams
            pri = self.data['Channel': 'primary']

            base = pri['Time': 0:params['preDuration']]
            peakStart = params['preDuration']
            peakStop = peakStart + 2e-3
            peak = pri['Time': peakStart:peakStop]
            ssStop = params['preDuration'] + params['pulseDuration']
            ssStart = ssStop - 2e-3
            steady  = pri['Time': ssStart:ssStop]

            if params['amplitude'] > 0:
                peakValue = peak.max()
            else:
                peakValue = peak.min()
            steadyValue = np.median(steady)
            baseValue = np.median(base)

            if params['clampMode'] == 'VC':
                analysis['baselinePotential'] = params['holding'] or 0
                analysis['baselineCurrent'] = baseValue
                analysis['peakResistance'] = params['amplitude'] / (peakValue - baseValue)
                analysis['steadyStateResistance'] = np.abs(params['amplitude'] / (steadyValue - baseValue))
                tauGuess = 3e-3
                yOffsetBounds = [-10e-9, 10e-9]
            else:
                bridge = self.data._info[-1]['ClampState']['ClampParams']['BridgeBalResist']
                bridgeOn = self.data._info[-1]['ClampState']['ClampParams']['BridgeBalEnable']
                if not bridgeOn:
                    bridge = 0.0
                analysis['baselineCurrent'] = params['holding'] or 0
                analysis['baselinePotential'] = baseValue
                analysis['peakResistance'] = bridge + (peakValue - baseValue) / params['amplitude']
                analysis['steadyStateResistance'] = np.abs(bridge + (steadyValue - baseValue) / params['amplitude'])
                tauGuess = 15e-3
                yOffsetBounds = [-150e-3, 100e-3]

            analysis['peakResistance'] = np.clip(analysis['peakResistance'], 0, 20e9)
            analysis['steadyStateResistance'] = np.clip(analysis['steadyStateResistance'], 0, 20e9)

            # do curve fitting
            pulseStart = params['preDuration'] + 150e-6
            pulseStop = params['preDuration'] + params['pulseDuration']
            pulse = pri['Time': pulseStart:pulseStop]
            t = pulse.xvals('Time')
            guess = (
                peakValue - steadyValue,  # amp
                tauGuess,  # tau
                steadyValue,  # yoffset
            )
            bounds = (
                np.array([yOffsetBounds[0], 50e-6, yOffsetBounds[0]]),
                np.array([yOffsetBounds[1], tauGuess*100, yOffsetBounds[1]])
            )
            xoffset = params['preDuration']
            try:
                fit = scipy.optimize.curve_fit(exp, t-xoffset, pulse.asarray(), guess, maxfev=1000)  # uses leastsq
                # fit = scipy.optimize.curve_fit(exp, t-xoffset, pulse.asarray(), guess, bounds=bounds, max_nfev=1000)  # uses least_squares
                amp, tau, yoffset = fit[0]
            except RuntimeError:
                amp, tau, yoffset = np.nan
                raise

            analysis['fitExpAmp'] = amp
            analysis['fitExpTau'] = tau
            analysis['fitExpYOffset'] = yoffset
            analysis['fitExpXOffset'] = xoffset

            if params['clampMode'] == 'VC':
                # VC capacitance calculation adapted from Santos-Sacchi 1993
                # (not very accurate, probably because Q is calculated incorrectly)
                Q = (pulse.asarray() - yoffset).sum() * (t[-1]-t[0]) / len(pulse)
                Rin = analysis['steadyStateResistance']
                Vc = params['amplitude']
                Rs_denom = (Q * Rin + tau * Vc)
                if Rs_denom != 0.0:
                    Rs = (Rin * tau * Vc) / Rs_denom
                    Rm = Rin - Rs
                    Cm = (Rin**2 * Q) / (Rm**2 * Vc)
                else:
                    Rs = 0
                    Rm = 0
                    Cm = 0
                analysis['capacitance'] = Cm
            else:
                analysis['capacitance'] = tau / analysis['steadyStateResistance']

            self._analysis = analysis
            return analysis

    def getFitData(self):
        params = self.taskParams
        analysis = self.analysis()
        pri = self.data['Channel': 'primary']
        pulseStart = params['preDuration'] + 150e-6
        pulseStop = params['preDuration'] + params['pulseDuration']
        pulse = pri['Time': pulseStart:pulseStop]
        t = pulse.xvals('Time')
        y = exp(t - analysis['fitExpXOffset'], analysis['fitExpAmp'], analysis['fitExpTau'], analysis['fitExpYOffset'])
        return t,y

def exp(t, amp, tau, yoffset):
    return yoffset + amp * np.exp(-t / tau)<|endoftext|>