<commit_before># Copyright (C) 2017-2019  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information

import json
import traceback

from django.utils.html import escape
from rest_framework.response import Response
from rest_framework.utils.encoders import JSONEncoder

from swh.storage.exc import StorageAPIError, StorageDBError
from swh.web.api import utils
from swh.web.common.exc import BadInputExc, ForbiddenExc, LargePayloadExc, NotFoundExc
from swh.web.common.utils import gen_path_info, shorten_path
from swh.web.config import get_config

def compute_link_header(rv, options):
    """Add Link header in returned value results.

    Args:
        request: a DRF Request object
        rv (dict): dictionary with keys:

            - headers: potential headers with 'link-next' and 'link-prev'
              keys
            - results: containing the result to return

        options (dict): the initial dict to update with result if any

    Returns:
        dict: dictionary with optional keys 'link-next' and 'link-prev'

    """
    link_headers = []

    if "headers" not in rv:
        return {}

    rv_headers = rv["headers"]

    if "link-next" in rv_headers:
        link_headers.append('<%s>; rel="next"' % rv_headers["link-next"])
    if "link-prev" in rv_headers:
        link_headers.append('<%s>; rel="previous"' % rv_headers["link-prev"])

    if link_headers:
        link_header_str = ",".join(link_headers)
        headers = options.get("headers", {})
        headers.update({"Link": link_header_str})
        return headers

    return {}

def filter_by_fields(request, data):
    """Extract a request parameter 'fields' if it exists to permit the filtering on
    the data dict's keys.

    If such field is not provided, returns the data as is.

    """
    fields = request.query_params.get("fields")
    if fields:
        fields = set(fields.split(","))
        data = utils.filter_field_keys(data, fields)

    return data

def transform(rv):
    """Transform an eventual returned value with multiple layer of
    information with only what's necessary.

    If the returned value rv contains the 'results' key, this is the
    associated value which is returned.

    Otherwise, return the initial dict without the potential 'headers'
    key.

    """
    if "results" in rv:
        return rv["results"]

    if "headers" in rv:
        rv.pop("headers")

    return rv

def make_api_response(request, data, doc_data={}, options={}):
    """Generates an API response based on the requested mimetype.

    Args:
        request: a DRF Request object
        data: raw data to return in the API response
        doc_data: documentation data for HTML response
        options: optional data that can be used to generate the response

    Returns:
        a DRF Response a object

    """
    if data:
        options["headers"] = compute_link_header(data, options)
        data = transform(data)
        data = filter_by_fields(request, data)
    doc_env = doc_data
    headers = {}
    if "headers" in options:
        doc_env["headers_data"] = options["headers"]
        headers = options["headers"]

    # get request status code
    doc_env["status_code"] = options.get("status", 200)

    response_args = {
        "status": doc_env["status_code"],
        "headers": headers,
        "content_type": request.accepted_media_type,
    }

    # when requesting HTML, typically when browsing the API through its
    # documented views, we need to enrich the input data with documentation
    # related ones and inform DRF that we request HTML template rendering
    if request.accepted_media_type == "text/html":

        if data:
            data = json.dumps(
                data, cls=JSONEncoder, sort_keys=True, indent=4, separators=(",", ": ")
            )
        doc_env["response_data"] = data
        doc_env["heading"] = shorten_path(str(request.path))

        # generate breadcrumbs data
        if "route" in doc_env:
            doc_env["endpoint_path"] = gen_path_info(doc_env["route"])
            for i in range(len(doc_env["endpoint_path"]) - 1):
                doc_env["endpoint_path"][i]["path"] += "/doc/"
            if not doc_env["noargs"]:
                doc_env["endpoint_path"][-1]["path"] += "/doc/"

        response_args["data"] = doc_env
        response_args["template_name"] = "api/apidoc.html"

    # otherwise simply return the raw data and let DRF picks
    # the correct renderer (JSON or YAML)
    else:
        response_args["data"] = data

    return Response(**response_args)

def error_response(request, error, doc_data):
    """Private function to create a custom error response.

    Args:
        request: a DRF Request object
        error: the exception that caused the error
        doc_data: documentation data for HTML response

    """
    error_code = 500
    if isinstance(error, BadInputExc):
        error_code = 400
    elif isinstance(error, NotFoundExc):
        error_code = 404
    elif isinstance(error, ForbiddenExc):
        error_code = 403
    elif isinstance(error, LargePayloadExc):
        error_code = 413
    elif isinstance(error, StorageDBError):
        error_code = 503
    elif isinstance(error, StorageAPIError):
        error_code = 503

    error_opts = {"status": error_code}
    error_data = {
        "exception": error.__class__.__name__,
        "reason": str(error),
    }

    if request.accepted_media_type == "text/html":
        error_data["reason"] = escape(error_data["reason"])

    if get_config()["debug"]:
        error_data["traceback"] = traceback.format_exc()

    return make_api_response(request, error_data, doc_data, options=error_opts)
<commit_msg>api/apiresponse: Add typing information<commit_after># Copyright (C) 2017-2019  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information

import json
import traceback
from typing import Any, Dict, Optional

from django.utils.html import escape
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.utils.encoders import JSONEncoder

from swh.storage.exc import StorageAPIError, StorageDBError
from swh.web.api import utils
from swh.web.common.exc import BadInputExc, ForbiddenExc, LargePayloadExc, NotFoundExc
from swh.web.common.utils import gen_path_info, shorten_path
from swh.web.config import get_config

def compute_link_header(rv: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
    """Add Link header in returned value results.

    Args:
        request: a DRF Request object
        rv (dict): dictionary with keys:

            - headers: potential headers with 'link-next' and 'link-prev'
              keys
            - results: containing the result to return

        options (dict): the initial dict to update with result if any

    Returns:
        dict: dictionary with optional keys 'link-next' and 'link-prev'

    """
    link_headers = []

    if "headers" not in rv:
        return {}

    rv_headers = rv["headers"]

    if "link-next" in rv_headers:
        link_headers.append('<%s>; rel="next"' % rv_headers["link-next"])
    if "link-prev" in rv_headers:
        link_headers.append('<%s>; rel="previous"' % rv_headers["link-prev"])

    if link_headers:
        link_header_str = ",".join(link_headers)
        headers = options.get("headers", {})
        headers.update({"Link": link_header_str})
        return headers

    return {}

def filter_by_fields(request: Request, data: Dict[str, Any]) -> Dict[str, Any]:
    """Extract a request parameter 'fields' if it exists to permit the filtering on
    the data dict's keys.

    If such field is not provided, returns the data as is.

    """
    fields = request.query_params.get("fields")
    if fields:
        data = utils.filter_field_keys(data, set(fields.split(",")))

    return data

def transform(rv: Dict[str, Any]) -> Dict[str, Any]:
    """Transform an eventual returned value with multiple layer of
    information with only what's necessary.

    If the returned value rv contains the 'results' key, this is the
    associated value which is returned.

    Otherwise, return the initial dict without the potential 'headers'
    key.

    """
    if "results" in rv:
        return rv["results"]

    if "headers" in rv:
        rv.pop("headers")

    return rv

def make_api_response(
    request: Request,
    data: Dict[str, Any],
    doc_data: Optional[Dict[str, Any]] = None,
    options: Optional[Dict[str, Any]] = None,
) -> Response:
    """Generates an API response based on the requested mimetype.

    Args:
        request: a DRF Request object
        data: raw data to return in the API response
        doc_data: documentation data for HTML response
        options: optional data that can be used to generate the response

    Returns:
        a DRF Response a object

    """
    options = options or {}
    if data:
        options["headers"] = compute_link_header(data, options)
        data = transform(data)
        data = filter_by_fields(request, data)
    doc_data = doc_data or {}
    headers = {}
    if "headers" in options:
        doc_data["headers_data"] = options["headers"]
        headers = options["headers"]

    # get request status code
    doc_data["status_code"] = options.get("status", 200)

    response_args = {
        "status": doc_data["status_code"],
        "headers": headers,
        "content_type": request.accepted_media_type,
    }

    # when requesting HTML, typically when browsing the API through its
    # documented views, we need to enrich the input data with documentation
    # related ones and inform DRF that we request HTML template rendering
    if request.accepted_media_type == "text/html":
        doc_data["response_data"] = data
        if data:
            doc_data["response_data"] = json.dumps(
                data, cls=JSONEncoder, sort_keys=True, indent=4, separators=(",", ": ")
            )

        doc_data["heading"] = shorten_path(str(request.path))

        # generate breadcrumbs data
        if "route" in doc_data:
            doc_data["endpoint_path"] = gen_path_info(doc_data["route"])
            for i in range(len(doc_data["endpoint_path"]) - 1):
                doc_data["endpoint_path"][i]["path"] += "/doc/"
            if not doc_data["noargs"]:
                doc_data["endpoint_path"][-1]["path"] += "/doc/"

        response_args["data"] = doc_data
        response_args["template_name"] = "api/apidoc.html"

    # otherwise simply return the raw data and let DRF picks
    # the correct renderer (JSON or YAML)
    else:
        response_args["data"] = data

    return Response(**response_args)

def error_response(
    request: Request, error: Exception, doc_data: Dict[str, Any]
) -> Response:
    """Private function to create a custom error response.

    Args:
        request: a DRF Request object
        error: the exception that caused the error
        doc_data: documentation data for HTML response

    """
    error_code = 500
    if isinstance(error, BadInputExc):
        error_code = 400
    elif isinstance(error, NotFoundExc):
        error_code = 404
    elif isinstance(error, ForbiddenExc):
        error_code = 403
    elif isinstance(error, LargePayloadExc):
        error_code = 413
    elif isinstance(error, StorageDBError):
        error_code = 503
    elif isinstance(error, StorageAPIError):
        error_code = 503

    error_opts = {"status": error_code}
    error_data = {
        "exception": error.__class__.__name__,
        "reason": str(error),
    }

    if request.accepted_media_type == "text/html":
        error_data["reason"] = escape(error_data["reason"])

    if get_config()["debug"]:
        error_data["traceback"] = traceback.format_exc()

    return make_api_response(request, error_data, doc_data, options=error_opts)
<|endoftext|>


<commit_before>
from ..core import Basic, classes, objects

class AlgebraicExpression(Basic):
    """ Algebraic expression represents

    - a set element of an algebra
    - a result of binary operation +
    - a result of binary operation *

    An operant of the operation + has number coefficient that
    represents a repetition of the given operant in the operation.
    E.g. x * 3 means x + x + x. Number coefficient can be also
    fraction, algebraic number, or any other concept generalizing
    repetition notion.

    An operant of the operation * has integer exponent that also
    represents a repetition of the given operant in the operation.
    E.g. x ** 3 means x * x * x. The exponent must be integer.

    An element of an algebra is represented as

      AlgebraicExpression(ELEMENT, obj)

    where obj can be any Python object that is hashable.

    A result of the operation + is represented as

      AlgebraicExpression(TERMS, pairs)

    where pairs is a sequence or iterator containing pairs

      (<AlgebraicExpression instance>, <number coefficient>).

    A result of the operation * is represented as

      AlgebraicExpression(FACTORS, pairs)

    where pairs is a sequence or iterator containing pairs

      (<AlgebraicExpression instance>, <integer exponent>).

    Algebra contains identity element zero with respect to + operation.

    Algebra contains identity element one with respect to * operation.

    A number element of an algebra is defined as

      one * <number>

    and is represented as

      AlgebraicExpression(TERMS, [(one, <number>)]).

    The zero element of an algebra is represented as

      one * 0.

    Similarly, the one element of an algebra is represented as

      one * 1.

    Fractional powers are represented as powers of a minimal root
    (that has exponent with a nominator equal to 1) where the root
    expression is considered as an element of an algebra. E.g.
    x**(2/3) is equal to (x**(1/3)) ** 2 and is represented as

      AlgebraicExpression(FACTORS,
                      [(AlgebraicExpression(ELEMENT, x**(1/3)), 2)])

    where y=x**(1/3) is a solution of x==y**3 with respect to y and is
    represented as

      Root(x, 3).

    Symbolic powers are considered as an element of an algebra.
    E.g x**y is represented as

      AlgebraicExpression(ELEMENT, x**y)

    where x**y is represented as Power(x, y).

    More examples:
     x**(2/3*y) is represented as

      AlgebraicExpression(FACTORS,
                         [(AlgebraicExpression(ELEMENT, Power(x,1/3*y)) , 2)] )

     x**(2/3*y + 4/5) is rewritten as (x**(1/3*y+2/5), 2)

    Python operations + and * use the following rules when the lhs
    operant is an instance of AlgebraicExpression:

      - if rhs has the same class as lhs then result will be computed within
        given algebra.

      - if rhs is a subclass of the lhs class then it is considered
        as a number element of the lhs algebra.

      - if rhs is a superclass of tje lhs class then the lhs is considered
        as a number element of the rhs algebra.

      - if rhs is not an AlgebraicExpression instance then it will be
        converted to one using rhs.as_ae(lhs.__class__).

    Examples:

      <Number>.as_ae(AlgebraicExpression) -> AlgebraicExpression(TERMS,[(one, <Number>)])

      <Symbol>.as_ae(AlgebraicExpression) -> AlgebraicExpression(TERMS,[(s, 1)])
        where s = AlgebraicExpression(ELEMENT, <Symbol>)

      <Power(2,5/3)>.as_ae(AlgebraicExpression) -> AlgebraicExpression(FACTORS,[(s, 5)])
        where s = AlgebraicExpression(ELEMENT, <Power(2,1/3)>)
    """

    def __new__(cls, kind, expr):
        obj = object.__new__(cls)

class AlgebraicNumberExpression(AlgebraicExpression):
    """ Represents algebraic numbers.

    Here elements of the algebra are minimal roots of numbers and
    numbers. E.g. 2**(2/3) is represented as

      AlgebraicNumberExpression(FACTORS,
        [(AlgebraicNumberExpression(ELEMENT, Root(2,3)), 2)])
    """
<commit_msg>Cont. writing algebra specs. 2.<commit_after># Author: Pearu Peterson
# Created: January 2008

from ..core import Basic, classes, objects

class AlgebraicExpression(Basic):
    """ Algebraic expression represents

    - a set element of an algebra
    - a result of binary operation +
    - a result of binary operation *
    - a number element of an algebra (see definition below)

    An operant of the operation + has a number coefficient that
    represents the repetition of the given operant in the operation.
    E.g. x * 3 means x + x + x. Number coefficient can also be
    fraction, algebraic number, or any other concept generalizing
    repetition notion.

    An operant of the operation * has integer exponent that also
    represents the repetition of the given operant in the operation.
    E.g. x ** 3 means x * x * x. The exponent must be integer.

    An element of an algebra is represented as

      AlgebraicExpression(ELEMENT, obj)

    where obj can be any Python object that is hashable (this
    requirement is related to the implementation algorithm).

    The result of operation + is represented as

      AlgebraicExpression(TERMS, pairs)

    where pairs is a sequence or an iterator containing pairs

      (<AlgebraicExpression instance>, <number coefficient>).

    The result of operation * is represented as

      AlgebraicExpression(FACTORS, pairs)

    where pairs is a sequence or an iterator containing pairs

      (<AlgebraicExpression instance>, <integer exponent>).

    Algebra contains identity element zero with respect to + operation.

    Algebra contains identity element one with respect to * operation.

    A number element of an algebra is defined as

      one * <number>

    and is represented as

      AlgebraicExpression(NUMBER, <number>).

    The zero element of an algebra is represented as

      one * 0.

    Similarly, the one element of an algebra is represented as

      one * 1.

    Fractional powers are represented as powers of a minimal root
    (that has exponent with a numerator equal to 1) where the root
    expression is considered as an element of an algebra. E.g.
    x**(2/3) is equal to (x**(1/3)) ** 2 and is represented as

      AlgebraicExpression(FACTORS,
                      [(AlgebraicExpression(ELEMENT, x**(1/3)), 2)])

    where y=x**(1/3) is a solution of x==y**3 with respect to y and is
    represented as

      Root(x, 3).

    Symbolic powers are considered as elements of an algebra.
    E.g x**y is represented as

      AlgebraicExpression(ELEMENT, x**y)

    where x**y is represented as Power(x, y).

    More examples:
     x**(2/3*y) is represented as

      AlgebraicExpression(FACTORS,
                         [(AlgebraicExpression(ELEMENT, Power(x,1/3*y)) , 2)] )

     x**(2/3*y + 4/5) is rewritten as (x**(1/3*y+2/5), 2)

    Python operations + and * use the following rules when the LHS
    operant is an instance of AlgebraicExpression:

      - if RHS has the same class as LHS then result will be computed within
        given algebra.

      - if RHS is a subclass of the LHS class then it is considered
        as a number element of the LHS algebra.

      - if RHS is a superclass of the LHS class then the LHS is considered
        as a number element of the RHS algebra.

      - if RHS is not an AlgebraicExpression instance then it will be
        converted to one using RHS.as_ae(LHS.__class__).

    Examples:

      <Number>.as_ae(AlgebraicExpression) -> AlgebraicExpression(TERMS,[(one, <Number>)])

      <Symbol>.as_ae(AlgebraicExpression) -> AlgebraicExpression(TERMS,[(s, 1)])
        where s = AlgebraicExpression(ELEMENT, <Symbol>)

      <Power(2,5/3)>.as_ae(AlgebraicExpression) -> AlgebraicExpression(FACTORS,[(s, 5)])
        where s = AlgebraicExpression(ELEMENT, <Power(2,1/3)>)
    """

    def __new__(cls, kind, expr):
        obj = object.__new__(cls)

class AlgebraicNumberExpression(AlgebraicExpression):
    """ Represents algebraic numbers.

    Here elements of the algebra are minimal roots of numbers, numbers,
    and results of operations + and *. E.g. 2**(4/3) is represented as

      AlgebraicNumberExpression(FACTORS,
        [(AlgebraicNumberExpression(ELEMENT, Root(2,3)), 4)])
    """
<|endoftext|>


<commit_before>import io
import hashlib

import synapse.axon as s_axon
import synapse.daemon as s_daemon
import synapse.lib.heap as s_heap
import synapse.telepath as s_telepath
import synapse.lib.service as s_service

from synapse.tests.common import *

craphash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
asdfhash = '6a204bd89f3c8348afd5c77c717a097a'

class AxonTest(SynTest):

    def test_axon_basics(self):
        with self.getTestDir() as axondir:

            axon = s_axon.Axon(axondir)

            self.assertFalse( axon.has('md5',craphash) )
            self.assertFalse( axon.has('md5',asdfhash) )

            iden0 = axon.alloc(8)

            self.assertIsNotNone( axon.chunk(iden0,b'asdfasdf') )

            self.assertTrue( axon.has('md5',asdfhash) )
            self.assertFalse( axon.has('md5',craphash) )

            byts = b''.join( axon.bytes('md5',asdfhash) )

            self.assertEqual(byts,b'asdfasdf')

            axon.fini()

            axon = s_axon.Axon(axondir)

            self.assertTrue( axon.has('md5',asdfhash) )
            self.assertFalse( axon.has('md5',craphash) )

            byts = b''.join( axon.bytes('md5',asdfhash) )

            self.assertEqual(byts,b'asdfasdf')

            self.assertIsNone(axon.wants('md5', asdfhash, 8))
            self.assertIsNotNone(axon.wants('md5', craphash, 8))

            axon.fini()

    def test_axon_sync(self):

        with self.getTestDir() as axondir:

            byts = os.urandom(128)
            bytsmd5 = hashlib.md5(byts).hexdigest()

            axon = s_axon.Axon(axondir,syncsize=64)

            iden = axon.alloc(128)
            for chnk in chunks(byts,10):
                blob = axon.chunk(iden,chnk)

            self.assertIsNotNone(blob)

            self.assertTrue( axon.has('md5',bytsmd5) )

            axon.fini()

    def test_axon_host(self):

        self.thisHostMustNot(platform='windows')

        with self.getTestDir() as datadir:

            with open(os.path.join(datadir,'foo'),'w') as fd:
                fd.write('useless file to skip')

            host = s_axon.AxonHost(datadir)
            usage = host.usage()

            props = {
                'syncmax':s_axon.megabyte * 10,
                'bytemax':s_axon.megabyte * 10,
            }

            axfo = host.add(**props)

            self.assertIsNotNone( usage.get('total') )

            axon = host.axons.get(axfo[0])

            iden = axon.alloc(100)
            blob = axon.chunk(iden,b'V'*100)

            self.assertIsNotNone(blob)

            self.assertTrue( axon.has( 'md5', blob[1].get('hash:md5') ) )
            self.assertTrue( axon.has( 'sha1', blob[1].get('hash:sha1') ) )
            self.assertTrue( axon.has( 'sha256', blob[1].get('hash:sha256') ) )

            host.fini()

            host = s_axon.AxonHost(datadir)
            axon = host.axons.get(axfo[0])

            self.assertTrue( axon.has( 'md5', blob[1].get('hash:md5') ) )
            self.assertTrue( axon.has( 'sha1', blob[1].get('hash:sha1') ) )
            self.assertTrue( axon.has( 'sha256', blob[1].get('hash:sha256') ) )

            props = {
                'syncmax':s_axon.megabyte * 10,
            }
            self.assertRaises(NotEnoughFree, host.add, **props)

            host.fini()

    def test_axon_host_clone(self):

        self.thisHostMustNot(platform='windows')

        busurl = 'local://%s/axons' % guid()

        dmon = s_daemon.Daemon()
        dmon.listen(busurl)

        dmon.share('axons', s_service.SvcBus(), fini=True)

        with self.getTestDir() as datadir:

            dir0 = gendir(datadir,'host0')
            dir1 = gendir(datadir,'host1')
            dir2 = gendir(datadir,'host2')

            opts = {
                'axonbus':busurl,
            }

            host0 = s_axon.AxonHost(dir0,hostname='host0',**opts)
            host1 = s_axon.AxonHost(dir1,hostname='host1',**opts)
            host2 = s_axon.AxonHost(dir2,hostname='host2',**opts)

            props = {
                'syncmax':s_axon.megabyte,
                'bytemax':s_axon.megabyte,
            }

            axfo0 = host0.add(**props)

            axon0 = s_telepath.openlink( axfo0[1].get('link') )
            self.assertTrue( axon0._waitClonesReady(timeout=2) )

            iden = axon0.alloc(100)
            blob = axon0.chunk(iden,b'V'*100)

            self.assertIsNotNone(blob)

            self.assertTrue( axon0.has( 'md5', blob[1].get('hash:md5') ) )
            self.assertTrue( axon0.has( 'sha1', blob[1].get('hash:sha1') ) )
            self.assertTrue( axon0.has( 'sha256', blob[1].get('hash:sha256') ) )

            axon0.fini()

            host0.fini()
            host1.fini()
            host2.fini()

        dmon.fini()

    def test_axon_clustered(self):

        self.thisHostMustNot(platform='windows')

        busurl = 'local://%s/axons' % guid()

        dmon = s_daemon.Daemon()
        dmon.listen(busurl)

        dmon.share('axons', s_service.SvcBus(), fini=True)

        with self.getTestDir() as datadir:

            dir0 = gendir(datadir,'host0')
            dir1 = gendir(datadir,'host1')
            dir2 = gendir(datadir,'host2')

            opts = {
                'axonbus':busurl,
            }

            host0 = s_axon.AxonHost(dir0,hostname='host0',**opts)
            host1 = s_axon.AxonHost(dir1,hostname='host1',**opts)
            host2 = s_axon.AxonHost(dir2,hostname='host2',**opts)

            props = {
                'clones':2,
                'syncmax':s_axon.megabyte,
                'bytemax':s_axon.megabyte,
            }

            axfo0 = host0.add(**props)

            axon0 = s_telepath.openlink( axfo0[1].get('link') )

            # wait for clones to come online
            self.assertTrue( axon0._waitClonesReady(timeout=2) )

            #self.assertIsNotNone( usage.get('total') )
            #axon = host.axons.get(iden)

            iden = axon0.alloc(100)
            blob = axon0.chunk(iden,b'V'*100)

            self.assertIsNotNone(blob)

            self.assertTrue( axon0.has( 'md5', blob[1].get('hash:md5') ) )
            self.assertTrue( axon0.has( 'sha1', blob[1].get('hash:sha1') ) )
            self.assertTrue( axon0.has( 'sha256', blob[1].get('hash:sha256') ) )

            axon0.fini()

            host0.fini()
            host1.fini()
            host2.fini()

        dmon.fini()

    def test_axon_cluster(self):

        self.thisHostMustNot(platform='windows')

        busurl = 'local://%s/axons' % guid()

        dmon = s_daemon.Daemon()
        dmon.listen(busurl)

        dmon.share('axons', s_service.SvcBus(), fini=True)
        svcprox = s_service.openurl( busurl )

        axcluster = s_axon.AxonCluster(svcprox)

        with self.getTestDir() as datadir:

            dir0 = gendir(datadir,'host0')
            dir1 = gendir(datadir,'host1')
            dir2 = gendir(datadir,'host2')

            opts = {
                'axonbus':busurl,
            }

            host0 = s_axon.AxonHost(dir0,hostname='host0',**opts)
            host1 = s_axon.AxonHost(dir1,hostname='host1',**opts)
            host2 = s_axon.AxonHost(dir2,hostname='host2',**opts)

            props = {
                'clones':1,
                'syncmax':s_axon.megabyte,
                'bytemax':s_axon.megabyte,
            }

            axfo0 = host0.add(**props)

            self.assertFalse( axcluster.has('md5',craphash) )
            self.assertFalse( axcluster.has('md5',asdfhash) )

            buf = b'asdfasdf'
            iden = axcluster.alloc(len(buf))
            self.assertIsNotNone( axcluster.chunk(iden, buf) )

            self.assertFalse( axcluster.has('md5',craphash) )
            self.assertTrue( axcluster.has('md5',asdfhash) )

            blobs = axcluster.find('md5', asdfhash)
            self.assertEqual(len(blobs), 1)

            blob = blobs[0]
            byts = b''.join( axcluster.iterblob(blob) )
            self.assertEqual(byts, buf)

            blob[1].pop('.axon')
            byts = b''.join( axcluster.iterblob(blob) )
            self.assertEqual(byts, buf)

            self.assertIsNone(axcluster.wants('md5', asdfhash, len(buf)))
            self.assertIsNotNone(axcluster.wants('md5', craphash, len(buf)))

            host0.fini()
            host1.fini()
            host2.fini()

        dmon.fini()

    def test_axon_autorun(self):

        self.thisHostMustNot(platform='windows')

        with self.getTestDir() as dirname:

            opts = {
                'autorun':2,
                'syncmax':s_axon.megabyte,
                'bytemax':s_axon.megabyte,
            }
            host = s_axon.AxonHost(dirname,**opts)
            self.assertEqual( len(host.axons), 2 )
            host.fini()

    def test_axon_eatbytes(self):
        self.thisHostMustNot(platform='windows')

        with self.getTestDir() as dirname:
            with s_axon.Axon(dirname) as axon:

                blob0 = axon.eatbytes(b'visi')
                with io.BytesIO(b'vertex') as fd:
                    blob1 = axon.eatfd(fd)

                port = axon.getAxonInfo()[1].get('link')[1].get('port')

                with s_axon.openurl('tcp://127.0.0.1/axon', port=port) as prox:
                    blob2 = axon.eatbytes(b'hurr')
                    with io.BytesIO(b'durr') as fd:
                        blob3 = axon.eatfd(fd)

        self.eq( blob0[1].get('axon:blob'), '442f602ecf8230b2a59a44b4f845be27' )
        self.eq( blob1[1].get('axon:blob'), 'd4552906c1f6966b96d27e6fc79441b5' )
        self.eq( blob2[1].get('axon:blob'), '0d60960570ef6da0a15f68c24b420334' )
        self.eq( blob3[1].get('axon:blob'), '97c11d1057f75c9c0b79090131709f62' )

    #def test_axon_proxy(self):
<commit_msg>tweak a test to get one more line of coverage<commit_after>import io
import hashlib

import synapse.axon as s_axon
import synapse.daemon as s_daemon
import synapse.lib.heap as s_heap
import synapse.telepath as s_telepath
import synapse.lib.service as s_service

from synapse.tests.common import *

craphash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
asdfhash = '6a204bd89f3c8348afd5c77c717a097a'

class AxonTest(SynTest):

    def test_axon_basics(self):
        with self.getTestDir() as axondir:

            axon = s_axon.Axon(axondir)

            self.assertFalse( axon.has('md5',craphash) )
            self.assertFalse( axon.has('md5',asdfhash) )

            iden0 = axon.alloc(8)

            self.assertIsNotNone( axon.chunk(iden0,b'asdfasdf') )

            self.assertTrue( axon.has('md5',asdfhash) )
            self.assertFalse( axon.has('md5',craphash) )

            byts = b''.join( axon.bytes('md5',asdfhash) )

            self.assertEqual(byts,b'asdfasdf')

            axon.fini()

            axon = s_axon.Axon(axondir)

            self.assertTrue( axon.has('md5',asdfhash) )
            self.assertFalse( axon.has('md5',craphash) )

            byts = b''.join( axon.bytes('md5',asdfhash) )

            self.assertEqual(byts,b'asdfasdf')

            self.assertIsNone(axon.wants('md5', asdfhash, 8))
            self.assertIsNotNone(axon.wants('md5', craphash, 8))

            axon.fini()

    def test_axon_sync(self):

        with self.getTestDir() as axondir:

            byts = os.urandom(128)
            bytsmd5 = hashlib.md5(byts).hexdigest()

            axon = s_axon.Axon(axondir,syncsize=64)

            iden = axon.alloc(128)
            for chnk in chunks(byts,10):
                blob = axon.chunk(iden,chnk)

            self.assertIsNotNone(blob)

            self.assertTrue( axon.has('md5',bytsmd5) )

            axon.fini()

    def test_axon_host(self):

        self.thisHostMustNot(platform='windows')

        with self.getTestDir() as datadir:

            with open(os.path.join(datadir,'foo'),'w') as fd:
                fd.write('useless file to skip')

            host = s_axon.AxonHost(datadir)
            usage = host.usage()

            props = {
                'syncmax':s_axon.megabyte * 10,
                'bytemax':s_axon.megabyte * 10,
            }

            axfo = host.add(**props)

            self.assertIsNotNone( usage.get('total') )

            axon = host.axons.get(axfo[0])

            iden = axon.alloc(100)
            blob = axon.chunk(iden,b'V'*100)

            self.assertIsNotNone(blob)

            self.assertTrue( axon.has( 'md5', blob[1].get('hash:md5') ) )
            self.assertTrue( axon.has( 'sha1', blob[1].get('hash:sha1') ) )
            self.assertTrue( axon.has( 'sha256', blob[1].get('hash:sha256') ) )

            host.fini()

            host = s_axon.AxonHost(datadir)
            axon = host.axons.get(axfo[0])

            self.assertTrue( axon.has( 'md5', blob[1].get('hash:md5') ) )
            self.assertTrue( axon.has( 'sha1', blob[1].get('hash:sha1') ) )
            self.assertTrue( axon.has( 'sha256', blob[1].get('hash:sha256') ) )

            props = {
                'syncmax':s_axon.megabyte * 10,
            }
            self.assertRaises(NotEnoughFree, host.add, **props)

            host.fini()

    def test_axon_host_clone(self):

        self.thisHostMustNot(platform='windows')

        busurl = 'local://%s/axons' % guid()

        dmon = s_daemon.Daemon()
        dmon.listen(busurl)

        dmon.share('axons', s_service.SvcBus(), fini=True)

        with self.getTestDir() as datadir:

            dir0 = gendir(datadir,'host0')
            dir1 = gendir(datadir,'host1')
            dir2 = gendir(datadir,'host2')

            opts = {
                'axonbus':busurl,
            }

            host0 = s_axon.AxonHost(dir0,hostname='host0',**opts)
            host1 = s_axon.AxonHost(dir1,hostname='host1',**opts)
            host2 = s_axon.AxonHost(dir2,hostname='host2',**opts)

            props = {
                'syncmax':s_axon.megabyte,
                'bytemax':s_axon.megabyte,
            }

            axfo0 = host0.add(**props)

            axon0 = s_telepath.openlink( axfo0[1].get('link') )
            self.assertTrue( axon0._waitClonesReady(timeout=2) )

            iden = axon0.alloc(100)
            blob = axon0.chunk(iden,b'V'*100)

            self.assertIsNotNone(blob)

            self.assertTrue( axon0.has( 'md5', blob[1].get('hash:md5') ) )
            self.assertTrue( axon0.has( 'sha1', blob[1].get('hash:sha1') ) )
            self.assertTrue( axon0.has( 'sha256', blob[1].get('hash:sha256') ) )

            axon0.fini()

            host0.fini()
            host1.fini()
            host2.fini()

        dmon.fini()

    def test_axon_clustered(self):

        self.thisHostMustNot(platform='windows')

        busurl = 'local://%s/axons' % guid()

        dmon = s_daemon.Daemon()
        dmon.listen(busurl)

        dmon.share('axons', s_service.SvcBus(), fini=True)

        with self.getTestDir() as datadir:

            dir0 = gendir(datadir,'host0')
            dir1 = gendir(datadir,'host1')
            dir2 = gendir(datadir,'host2')

            opts = {
                'axonbus':busurl,
            }

            host0 = s_axon.AxonHost(dir0,hostname='host0',**opts)
            host1 = s_axon.AxonHost(dir1,hostname='host1',**opts)
            host2 = s_axon.AxonHost(dir2,hostname='host2',**opts)

            props = {
                'clones':2,
                'syncmax':s_axon.megabyte,
                'bytemax':s_axon.megabyte,
            }

            axfo0 = host0.add(**props)

            axon0 = s_telepath.openlink( axfo0[1].get('link') )

            # wait for clones to come online
            self.assertTrue( axon0._waitClonesReady(timeout=2) )

            #self.assertIsNotNone( usage.get('total') )
            #axon = host.axons.get(iden)

            iden = axon0.alloc(100)
            blob = axon0.chunk(iden,b'V'*100)

            self.assertIsNotNone(blob)

            self.assertTrue( axon0.has( 'md5', blob[1].get('hash:md5') ) )
            self.assertTrue( axon0.has( 'sha1', blob[1].get('hash:sha1') ) )
            self.assertTrue( axon0.has( 'sha256', blob[1].get('hash:sha256') ) )

            axon0.fini()

            host0.fini()
            host1.fini()
            host2.fini()

        dmon.fini()

    def test_axon_cluster(self):

        self.thisHostMustNot(platform='windows')

        busurl = 'local://%s/axons' % guid()

        dmon = s_daemon.Daemon()
        dmon.listen(busurl)

        dmon.share('axons', s_service.SvcBus(), fini=True)
        svcprox = s_service.openurl( busurl )

        axcluster = s_axon.AxonCluster(svcprox)

        with self.getTestDir() as datadir:

            dir0 = gendir(datadir,'host0')
            dir1 = gendir(datadir,'host1')
            dir2 = gendir(datadir,'host2')

            opts = {
                'axonbus':busurl,
            }

            host0 = s_axon.AxonHost(dir0,hostname='host0',**opts)
            host1 = s_axon.AxonHost(dir1,hostname='host1',**opts)
            host2 = s_axon.AxonHost(dir2,hostname='host2',**opts)

            props = {
                'clones':1,
                'syncmax':s_axon.megabyte,
                'bytemax':s_axon.megabyte,
            }

            axfo0 = host0.add(**props)

            self.assertFalse( axcluster.has('md5',craphash) )
            self.assertFalse( axcluster.has('md5',asdfhash) )

            buf = b'asdfasdf'
            iden = axcluster.alloc(len(buf))
            self.assertIsNotNone( axcluster.chunk(iden, buf) )

            self.assertFalse( axcluster.has('md5',craphash) )
            self.assertTrue( axcluster.has('md5',asdfhash) )

            blobs = axcluster.find('md5', craphash)
            self.assertEqual(len(blobs), 0)

            blobs = axcluster.find('md5', asdfhash)
            self.assertEqual(len(blobs), 1)

            blob = blobs[0]
            byts = b''.join( axcluster.iterblob(blob) )
            self.assertEqual(byts, buf)

            blob[1].pop('.axon')
            byts = b''.join( axcluster.iterblob(blob) )
            self.assertEqual(byts, buf)

            self.assertIsNone(axcluster.wants('md5', asdfhash, len(buf)))
            self.assertIsNotNone(axcluster.wants('md5', craphash, len(buf)))

            host0.fini()
            host1.fini()
            host2.fini()

        dmon.fini()

    def test_axon_autorun(self):

        self.thisHostMustNot(platform='windows')

        with self.getTestDir() as dirname:

            opts = {
                'autorun':2,
                'syncmax':s_axon.megabyte,
                'bytemax':s_axon.megabyte,
            }
            host = s_axon.AxonHost(dirname,**opts)
            self.assertEqual( len(host.axons), 2 )
            host.fini()

    def test_axon_eatbytes(self):
        self.thisHostMustNot(platform='windows')

        with self.getTestDir() as dirname:
            with s_axon.Axon(dirname) as axon:

                blob0 = axon.eatbytes(b'visi')
                with io.BytesIO(b'vertex') as fd:
                    blob1 = axon.eatfd(fd)

                port = axon.getAxonInfo()[1].get('link')[1].get('port')

                with s_axon.openurl('tcp://127.0.0.1/axon', port=port) as prox:
                    blob2 = axon.eatbytes(b'hurr')
                    with io.BytesIO(b'durr') as fd:
                        blob3 = axon.eatfd(fd)

        self.eq( blob0[1].get('axon:blob'), '442f602ecf8230b2a59a44b4f845be27' )
        self.eq( blob1[1].get('axon:blob'), 'd4552906c1f6966b96d27e6fc79441b5' )
        self.eq( blob2[1].get('axon:blob'), '0d60960570ef6da0a15f68c24b420334' )
        self.eq( blob3[1].get('axon:blob'), '97c11d1057f75c9c0b79090131709f62' )

    #def test_axon_proxy(self):
<|endoftext|>


<commit_before>"""User preference service. (Currently brainstorming the API).

Notes:

- Preferences are user-specific
- For global setting, there should be a SettingService
"""

from flask import Blueprint, url_for, request, redirect, abort
from flask.ext.login import current_user
from abilian.core.extensions import db

from .models import UserPreference

class PreferenceService(object):
  """
  Flask extension for a user-level preference service, with pluggable
  panels.
  """

  def __init__(self, app=None):
    self.panels = []
    if app:
      self.init_app(app)

  def init_app(self, app):
    self.app = app
    app.extensions['preferences'] = self
    self.setup_blueprint()

  def get_preferences(self, user=None):
    """Returns a string->value dictionnary representing the given user
    preferences.

    If no user is provided, the current user is used instead.
    """
    if user is None:
      user = current_user
    preferences = UserPreference.query.filter(UserPreference.user_id == user.id).all()
    return { pref.key: pref.value for pref in preferences}

  def set_preferences(self, user=None, **kwargs):
    """Sets preferences from keyword arguments.
    """
    print kwargs
    if user is None:
      user = current_user

    preferences = UserPreference.query.filter(UserPreference.user_id == user.id).all()
    d = { pref.key: pref for pref in preferences}
    for k, v in kwargs.items():
      if k in d:
        d[k].value = v
      else:
        d[k] = UserPreference(user=user, key=k, value=v)
        db.session.add(d[k])

  def clear_preferences(self, user=None):
    """Clears the user preferences.
    """
    if user is None:
      user = current_user

    preferences = UserPreference.query.filter(UserPreference.user_id == user.id).all()
    for pref in preferences:
      db.session.delete(pref)

  def register_panel(self, panel):
    self.panels.append(panel)
    panel.preferences = self
    rule = "/" + getattr(panel, 'path', panel.id)
    endpoint = panel.id
    if hasattr(panel, 'get'):
      self.blueprint.add_url_rule(rule, endpoint, panel.get)
    if hasattr(panel, 'post'):
      endpoint += "_post"
      self.blueprint.add_url_rule(rule, endpoint, panel.post, methods=['POST'])

  def setup_blueprint(self):
    self.blueprint = Blueprint("preferences", __name__,
                               template_folder='templates',
                               url_prefix="/preferences")

    # @self.blueprint.before_request
    # def check_security():
    #   user = current_user._get_current_object()
    #   if security.has_role(user, "admin"):
    #     return
    #   else:
    #     abort(403)

    @self.blueprint.context_processor
    def inject_menu():
      menu = []
      for panel in self.panels:
        endpoint = 'preferences.' + panel.id
        active = endpoint == request.endpoint
        entry = {'endpoint': endpoint,
                 'label': panel.label,
                 'url': url_for(endpoint),
                 'active': active}
        menu.append(entry)
      return dict(menu=menu)

    @self.blueprint.route("/")
    def index():
      """Index redirects to the first accessible panel."""

      # Work around unit test failure. FIXME.
      if current_user.is_anonymous():
        return "OK"

      for panel in self.panels:
        if panel.is_accessible():
          return redirect(url_for("preferences." + panel.id))
      else:
        # Should not happen.
        abort(500)

preferences = PreferenceService()
<commit_msg>remove print statement<commit_after>"""User preference service. (Currently brainstorming the API).

Notes:

- Preferences are user-specific
- For global setting, there should be a SettingService
"""

from flask import Blueprint, url_for, request, redirect, abort
from flask.ext.login import current_user
from abilian.core.extensions import db

from .models import UserPreference

class PreferenceService(object):
  """
  Flask extension for a user-level preference service, with pluggable
  panels.
  """

  def __init__(self, app=None):
    self.panels = []
    if app:
      self.init_app(app)

  def init_app(self, app):
    self.app = app
    app.extensions['preferences'] = self
    self.setup_blueprint()

  def get_preferences(self, user=None):
    """Returns a string->value dictionnary representing the given user
    preferences.

    If no user is provided, the current user is used instead.
    """
    if user is None:
      user = current_user
    preferences = UserPreference.query.filter(UserPreference.user_id == user.id).all()
    return { pref.key: pref.value for pref in preferences}

  def set_preferences(self, user=None, **kwargs):
    """Sets preferences from keyword arguments.
    """
    if user is None:
      user = current_user

    preferences = UserPreference.query.filter(UserPreference.user_id == user.id).all()
    d = { pref.key: pref for pref in preferences}
    for k, v in kwargs.items():
      if k in d:
        d[k].value = v
      else:
        d[k] = UserPreference(user=user, key=k, value=v)
        db.session.add(d[k])

  def clear_preferences(self, user=None):
    """Clears the user preferences.
    """
    if user is None:
      user = current_user

    preferences = UserPreference.query.filter(UserPreference.user_id == user.id).all()
    for pref in preferences:
      db.session.delete(pref)

  def register_panel(self, panel):
    self.panels.append(panel)
    panel.preferences = self
    rule = "/" + getattr(panel, 'path', panel.id)
    endpoint = panel.id
    if hasattr(panel, 'get'):
      self.blueprint.add_url_rule(rule, endpoint, panel.get)
    if hasattr(panel, 'post'):
      endpoint += "_post"
      self.blueprint.add_url_rule(rule, endpoint, panel.post, methods=['POST'])

  def setup_blueprint(self):
    self.blueprint = Blueprint("preferences", __name__,
                               template_folder='templates',
                               url_prefix="/preferences")

    # @self.blueprint.before_request
    # def check_security():
    #   user = current_user._get_current_object()
    #   if security.has_role(user, "admin"):
    #     return
    #   else:
    #     abort(403)

    @self.blueprint.context_processor
    def inject_menu():
      menu = []
      for panel in self.panels:
        endpoint = 'preferences.' + panel.id
        active = endpoint == request.endpoint
        entry = {'endpoint': endpoint,
                 'label': panel.label,
                 'url': url_for(endpoint),
                 'active': active}
        menu.append(entry)
      return dict(menu=menu)

    @self.blueprint.route("/")
    def index():
      """Index redirects to the first accessible panel."""

      # Work around unit test failure. FIXME.
      if current_user.is_anonymous():
        return "OK"

      for panel in self.panels:
        if panel.is_accessible():
          return redirect(url_for("preferences." + panel.id))
      else:
        # Should not happen.
        abort(500)

preferences = PreferenceService()
<|endoftext|>


<commit_before># -*- encoding: utf-8 -*-
##############################################################################
#
#    Account partner required module for OpenERP
#    Copyright (C) 2014 Acsone (http://acsone.eu).
#    @author Stéphane Bidoul <stephane.bidoul@acsone.eu>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

{
    'name': 'Account partner required',
    'version': '8.0.0.1.0',
    'category': 'Generic Modules/Accounting',
    'license': 'AGPL-3',
    'description': """This module adds an option "partner policy"
on account types.

You have the choice between 3 policies : optional (the default),
always (require a partner), and never (forbid a partner).

This module is useful to enforce a partner on account move lines on
customer and supplier accounts.

Module developed by Stéphane Bidoul <stephane.bidoul@acsone.eu>,
inspired by Alexis de Lattre <alexis.delattre@akretion.com>'s
account_analytic_required module.
""",
    'author': "ACSONE SA/NV,Odoo Community Association (OCA)",
    'website': 'http://acsone.eu/',
    'depends': ['account'],
    'data': ['account_view.xml'],
    'installable': True,
}
<commit_msg>[MIG] Make modules uninstallable<commit_after># -*- encoding: utf-8 -*-
##############################################################################
#
#    Account partner required module for OpenERP
#    Copyright (C) 2014 Acsone (http://acsone.eu).
#    @author Stéphane Bidoul <stephane.bidoul@acsone.eu>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

{
    'name': 'Account partner required',
    'version': '8.0.0.1.0',
    'category': 'Generic Modules/Accounting',
    'license': 'AGPL-3',
    'description': """This module adds an option "partner policy"
on account types.

You have the choice between 3 policies : optional (the default),
always (require a partner), and never (forbid a partner).

This module is useful to enforce a partner on account move lines on
customer and supplier accounts.

Module developed by Stéphane Bidoul <stephane.bidoul@acsone.eu>,
inspired by Alexis de Lattre <alexis.delattre@akretion.com>'s
account_analytic_required module.
""",
    'author': "ACSONE SA/NV,Odoo Community Association (OCA)",
    'website': 'http://acsone.eu/',
    'depends': ['account'],
    'data': ['account_view.xml'],
    'installable': False,
}
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import print_function
import time
import numpy as np
from acq4.util import Qt
from ..Stage import Stage, MoveFuture, StageInterface
from acq4.drivers.Scientifica import Scientifica as ScientificaDriver
from acq4.util.Mutex import Mutex
from acq4.util.Thread import Thread
from acq4.pyqtgraph import debug, ptime, SpinBox

class Scientifica(Stage):
    """
    A Scientifica motorized device.

    This class supports PatchStar, MicroStar, SliceScope, objective changers, etc.
    The device may be identified either by its serial port or by its description
    string:

        port: <serial port>  # eg. 'COM1' or '/dev/ttyACM0'
        name: <string>  # eg. 'SliceScope' or 'MicroStar 2'
        baudrate: <int>  #  may be 9600 or 38400

    The optional 'baudrate' parameter is used to set the baudrate of the device.
    Both valid rates will be attempted when initially connecting.
    """
    def __init__(self, man, config, name):
        # can specify
        port = config.pop('port', None)
        name = config.pop('name', None)

        # if user has not provided scale values, we can make a guess
        config.setdefault('scale', (1e-6, 1e-6, 1e-6))

        baudrate = config.pop('baudrate', None)
        ctrl_version = config.pop('version', 2)
        try:
            self.dev = ScientificaDriver(port=port, name=name, baudrate=baudrate, ctrl_version=ctrl_version)
        except RuntimeError as err:
            if hasattr(err, 'dev_version'):
                raise RuntimeError(err.message + " You must add `version=%d` to the configuration for this device and double-check any speed/acceleration parameters." % int(err.dev_version))
            else:
                raise

        # Controllers reset their baud to 9600 after power cycle
        if baudrate is not None and self.dev.getBaudrate() != baudrate:
            self.dev.setBaudrate(baudrate)

        self._lastMove = None
        man.sigAbortAll.connect(self.abort)

        Stage.__init__(self, man, config, name)

        # clear cached position for this device and re-read to generate an initial position update
        self._lastPos = None
        self.getPosition(refresh=True)

        # Set approach angle
        # Disabled--this toggles the approach bit and we can't reconfigure it from here :(
        # approach = self.dev.send('APPROACH')
        # self.dev.send('ANGLE %f' % self.pitch)
        # self.dev.send('APPROACH %s' % approach)  # reset approach bit; setting angle enables it

        # set any extra parameters specified in the config
        params = config.get('params', {})
        for param, val in params.items():
            if param == 'currents':
                assert len(val) == 2
                self.dev.setCurrents(*val)
            elif param == 'axisScale':
                assert len(val) == 3
                for i, x in enumerate(val):
                    self.dev.setAxisScale(i, x)
            else:
                self.dev.setParam(param, val)

        self.setUserSpeed(config.get('userSpeed', self.dev.getSpeed() * 1e-6))

        # whether to monitor for changes to a MOC
        self.monitorObj = config.get('monitorObjective', False)
        if self.monitorObj is True:
            self.objectiveState = None
            self._checkObjective()

        # thread for polling position changes

        self.monitor = MonitorThread(self, self.monitorObj)
        self.monitor.start()

    def axes(self):
        return ('x', 'y', 'z')

    def capabilities(self):
        """Return a structure describing the capabilities of this device"""
        if 'capabilities' in self.config:
            return self.config['capabilities']
        else:
            return {
                'getPos': (True, True, True),
                'setPos': (True, True, True),
                'limits': (False, False, False),
            }

    def stop(self):
        """Stop the manipulator immediately.
        """
        with self.lock:
            self.dev.stop()
            if self._lastMove is not None:
                self._lastMove._stopped()
            self._lastMove = None

    def abort(self):
        """Stop the manipulator immediately.
        """
        self.dev.stop()
        if self._lastMove is not None:
            self._lastMove._stopped()
            self._lastMove = None

    def setUserSpeed(self, v):
        """Set the maximum speed of the stage (m/sec) when under manual control.

        The stage's maximum speed is reset to this value when it is not under
        programmed control.
        """
        self.userSpeed = v
        self.dev.setSpeed(v * 1e6)  # requires um/s

    def _getPosition(self):
        # Called by superclass when user requests position refresh
        with self.lock:
            pos = self.dev.getPos()
            if pos != self._lastPos:
                self._lastPos = pos
                emit = True
            else:
                emit = False

        if emit:
            # don't emit signal while locked
            self.posChanged(pos)

        return pos

    def targetPosition(self):
        with self.lock:
            if self._lastMove is None or self._lastMove.isDone():
                return self.getPosition()
            else:
                return self._lastMove.targetPos

    def quit(self):
        self.monitor.stop()
        Stage.quit(self)

    def _move(self, abs, rel, speed, linear):
        with self.lock:
            if self._lastMove is not None and not self._lastMove.isDone():
                self.stop()
            pos = self._toAbsolutePosition(abs, rel)
            speed = self._interpretSpeed(speed)

            self._lastMove = ScientificaMoveFuture(self, pos, speed, self.userSpeed)
            return self._lastMove

    def deviceInterface(self, win):
        return ScientificaGUI(self, win)

    def startMoving(self, vel):
        """Begin moving the stage at a continuous velocity.
        """
        s = [int(1e8 * v) for i,v in enumerate(vel)]
        self.dev.send('VJ -%d %d %d' % tuple(s))

    def _checkObjective(self):
        with self.lock:
            obj = int(self.dev.send('obj'))
            if obj != self.objectiveState:
                self.objectiveState = obj
                self.sigSwitchChanged.emit(self, {'objective': obj})

    def getSwitch(self, name):
        if name == 'objective' and self.monitorObj:
            return self.objectiveState
        else:
            return Stage.getSwitch(self, name)

class MonitorThread(Thread):
    """Thread to poll for manipulator position changes.
    """
    def __init__(self, dev, monitorObj):
        self.dev = dev
        self.lock = Mutex(recursive=True)
        self.monitorObj = monitorObj
        self.stopped = False
        self.interval = 0.3

        Thread.__init__(self)

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

    def stop(self):
        with self.lock:
            self.stopped = True

    def setInterval(self, i):
        with self.lock:
            self.interval = i

    def run(self):
        minInterval = 100e-3
        interval = minInterval
        lastPos = None
        while True:
            try:
                with self.lock:
                    if self.stopped:
                        break
                    maxInterval = self.interval

                pos = self.dev._getPosition()  # this causes sigPositionChanged to be emitted
                if pos != lastPos:
                    # if there was a change, then loop more rapidly for a short time.
                    interval = minInterval
                    lastPos = pos
                else:
                    interval = min(maxInterval, interval*2)

                if self.monitorObj is True:
                    self.dev._checkObjective()

                time.sleep(interval)
            except:
                debug.printExc('Error in Scientifica monitor thread:')
                time.sleep(maxInterval)

class ScientificaMoveFuture(MoveFuture):
    """Provides access to a move-in-progress on a Scientifica manipulator.
    """
    def __init__(self, dev, pos, speed, userSpeed):
        MoveFuture.__init__(self, dev, pos, speed)
        self._interrupted = False
        self._errorMsg = None
        self._finished = False
        pos = np.array(pos)
        with self.dev.dev.lock:
            self.dev.dev.moveTo(pos, speed / 1e-6)
            # reset to user speed immediately after starting move
            # (the move itself will run with the previous speed)
            self.dev.dev.setSpeed(userSpeed / 1e-6)

    def wasInterrupted(self):
        """Return True if the move was interrupted before completing.
        """
        return self._interrupted

    def isDone(self):
        """Return True if the move is complete.
        """
        return self._getStatus() != 0

    def _getStatus(self):
        # check status of move unless we already know it is complete.
        # 0: still moving; 1: finished successfully; -1: finished unsuccessfully
        if self._finished:
            if self._interrupted:
                return -1
            else:
                return 1
        if self.dev.dev.isMoving():
            # Still moving
            return 0
        # did we reach target?
        pos = self.dev._getPosition()
        dif = ((np.array(pos) - np.array(self.targetPos))**2).sum()**0.5
        if dif < 1.0:
            # reached target
            self._finished = True
            return 1
        else:
            # missed
            self._finished = True
            self._interrupted = True
            self._errorMsg = "Move did not complete (target=%s, position=%s, dif=%s)." % (self.targetPos, pos, dif)
            return -1

    def _stopped(self):
        # Called when the manipulator is stopped, possibly interrupting this move.
        startTime = ptime.time()
        while True:
            status = self._getStatus()
            if status == 1:
                # finished; ignore stop
                return
            elif status == -1:
                self._errorMsg = "Move was interrupted before completion."
                return
            elif status == 0 and ptime.time() < startTime + 0.1:
                # allow 100ms to stop
                continue
            elif status == 0:
                # not actually stopped! This should not happen.
                raise RuntimeError("Interrupted move but manipulator is still running!")
            else:
                raise Exception("Unknown status: %s" % status)

    def errorMessage(self):
        return self._errorMsg

class ScientificaGUI(StageInterface):
    def __init__(self, dev, win):
        StageInterface.__init__(self, dev, win)

        # Insert Scientifica-specific controls into GUI
        self.zeroBtn = Qt.QPushButton('Zero position')
        nextRow = self.layout.rowCount()
        self.layout.addWidget(self.zeroBtn, nextRow, 0, 1, 2)
        nextRow += 1

        self.psGroup = Qt.QGroupBox('Rotary Controller')
        self.layout.addWidget(self.psGroup, nextRow, 0, 1, 2)
        nextRow += 1

        self.psLayout = Qt.QGridLayout()
        self.psGroup.setLayout(self.psLayout)
        self.speedLabel = Qt.QLabel('Speed')
        self.speedSpin = SpinBox(value=self.dev.userSpeed, suffix='m/turn', siPrefix=True, dec=True, bounds=[1e-6, 10e-3])
        self.psLayout.addWidget(self.speedLabel, 0, 0)
        self.psLayout.addWidget(self.speedSpin, 0, 1)

        self.zeroBtn.clicked.connect(self.dev.dev.zeroPosition)
        self.speedSpin.valueChanged.connect(lambda v: self.dev.setDefaultSpeed(v))

<commit_msg>Extend scientific stop timeout  Please enter the commit message for your changes. Lines starting<commit_after># -*- coding: utf-8 -*-
from __future__ import print_function
import time
import numpy as np
from acq4.util import Qt
from ..Stage import Stage, MoveFuture, StageInterface
from acq4.drivers.Scientifica import Scientifica as ScientificaDriver
from acq4.util.Mutex import Mutex
from acq4.util.Thread import Thread
from acq4.pyqtgraph import debug, ptime, SpinBox

class Scientifica(Stage):
    """
    A Scientifica motorized device.

    This class supports PatchStar, MicroStar, SliceScope, objective changers, etc.
    The device may be identified either by its serial port or by its description
    string:

        port: <serial port>  # eg. 'COM1' or '/dev/ttyACM0'
        name: <string>  # eg. 'SliceScope' or 'MicroStar 2'
        baudrate: <int>  #  may be 9600 or 38400

    The optional 'baudrate' parameter is used to set the baudrate of the device.
    Both valid rates will be attempted when initially connecting.
    """
    def __init__(self, man, config, name):
        # can specify
        port = config.pop('port', None)
        name = config.pop('name', None)

        # if user has not provided scale values, we can make a guess
        config.setdefault('scale', (1e-6, 1e-6, 1e-6))

        baudrate = config.pop('baudrate', None)
        ctrl_version = config.pop('version', 2)
        try:
            self.dev = ScientificaDriver(port=port, name=name, baudrate=baudrate, ctrl_version=ctrl_version)
        except RuntimeError as err:
            if hasattr(err, 'dev_version'):
                raise RuntimeError(err.message + " You must add `version=%d` to the configuration for this device and double-check any speed/acceleration parameters." % int(err.dev_version))
            else:
                raise

        # Controllers reset their baud to 9600 after power cycle
        if baudrate is not None and self.dev.getBaudrate() != baudrate:
            self.dev.setBaudrate(baudrate)

        self._lastMove = None
        man.sigAbortAll.connect(self.abort)

        Stage.__init__(self, man, config, name)

        # clear cached position for this device and re-read to generate an initial position update
        self._lastPos = None
        self.getPosition(refresh=True)

        # Set approach angle
        # Disabled--this toggles the approach bit and we can't reconfigure it from here :(
        # approach = self.dev.send('APPROACH')
        # self.dev.send('ANGLE %f' % self.pitch)
        # self.dev.send('APPROACH %s' % approach)  # reset approach bit; setting angle enables it

        # set any extra parameters specified in the config
        params = config.get('params', {})
        for param, val in params.items():
            if param == 'currents':
                assert len(val) == 2
                self.dev.setCurrents(*val)
            elif param == 'axisScale':
                assert len(val) == 3
                for i, x in enumerate(val):
                    self.dev.setAxisScale(i, x)
            else:
                self.dev.setParam(param, val)

        self.setUserSpeed(config.get('userSpeed', self.dev.getSpeed() * 1e-6))

        # whether to monitor for changes to a MOC
        self.monitorObj = config.get('monitorObjective', False)
        if self.monitorObj is True:
            self.objectiveState = None
            self._checkObjective()

        # thread for polling position changes

        self.monitor = MonitorThread(self, self.monitorObj)
        self.monitor.start()

    def axes(self):
        return ('x', 'y', 'z')

    def capabilities(self):
        """Return a structure describing the capabilities of this device"""
        if 'capabilities' in self.config:
            return self.config['capabilities']
        else:
            return {
                'getPos': (True, True, True),
                'setPos': (True, True, True),
                'limits': (False, False, False),
            }

    def stop(self):
        """Stop the manipulator immediately.
        """
        with self.lock:
            self.dev.stop()
            if self._lastMove is not None:
                self._lastMove._stopped()
            self._lastMove = None

    def abort(self):
        """Stop the manipulator immediately.
        """
        self.dev.stop()
        if self._lastMove is not None:
            self._lastMove._stopped()
            self._lastMove = None

    def setUserSpeed(self, v):
        """Set the maximum speed of the stage (m/sec) when under manual control.

        The stage's maximum speed is reset to this value when it is not under
        programmed control.
        """
        self.userSpeed = v
        self.dev.setSpeed(v * 1e6)  # requires um/s

    def _getPosition(self):
        # Called by superclass when user requests position refresh
        with self.lock:
            pos = self.dev.getPos()
            if pos != self._lastPos:
                self._lastPos = pos
                emit = True
            else:
                emit = False

        if emit:
            # don't emit signal while locked
            self.posChanged(pos)

        return pos

    def targetPosition(self):
        with self.lock:
            if self._lastMove is None or self._lastMove.isDone():
                return self.getPosition()
            else:
                return self._lastMove.targetPos

    def quit(self):
        self.monitor.stop()
        Stage.quit(self)

    def _move(self, abs, rel, speed, linear):
        with self.lock:
            if self._lastMove is not None and not self._lastMove.isDone():
                self.stop()
            pos = self._toAbsolutePosition(abs, rel)
            speed = self._interpretSpeed(speed)

            self._lastMove = ScientificaMoveFuture(self, pos, speed, self.userSpeed)
            return self._lastMove

    def deviceInterface(self, win):
        return ScientificaGUI(self, win)

    def startMoving(self, vel):
        """Begin moving the stage at a continuous velocity.
        """
        s = [int(1e8 * v) for i,v in enumerate(vel)]
        self.dev.send('VJ -%d %d %d' % tuple(s))

    def _checkObjective(self):
        with self.lock:
            obj = int(self.dev.send('obj'))
            if obj != self.objectiveState:
                self.objectiveState = obj
                self.sigSwitchChanged.emit(self, {'objective': obj})

    def getSwitch(self, name):
        if name == 'objective' and self.monitorObj:
            return self.objectiveState
        else:
            return Stage.getSwitch(self, name)

class MonitorThread(Thread):
    """Thread to poll for manipulator position changes.
    """
    def __init__(self, dev, monitorObj):
        self.dev = dev
        self.lock = Mutex(recursive=True)
        self.monitorObj = monitorObj
        self.stopped = False
        self.interval = 0.3

        Thread.__init__(self)

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

    def stop(self):
        with self.lock:
            self.stopped = True

    def setInterval(self, i):
        with self.lock:
            self.interval = i

    def run(self):
        minInterval = 100e-3
        interval = minInterval
        lastPos = None
        while True:
            try:
                with self.lock:
                    if self.stopped:
                        break
                    maxInterval = self.interval

                pos = self.dev._getPosition()  # this causes sigPositionChanged to be emitted
                if pos != lastPos:
                    # if there was a change, then loop more rapidly for a short time.
                    interval = minInterval
                    lastPos = pos
                else:
                    interval = min(maxInterval, interval*2)

                if self.monitorObj is True:
                    self.dev._checkObjective()

                time.sleep(interval)
            except:
                debug.printExc('Error in Scientifica monitor thread:')
                time.sleep(maxInterval)

class ScientificaMoveFuture(MoveFuture):
    """Provides access to a move-in-progress on a Scientifica manipulator.
    """
    def __init__(self, dev, pos, speed, userSpeed):
        MoveFuture.__init__(self, dev, pos, speed)
        self._interrupted = False
        self._errorMsg = None
        self._finished = False
        pos = np.array(pos)
        with self.dev.dev.lock:
            self.dev.dev.moveTo(pos, speed / 1e-6)
            # reset to user speed immediately after starting move
            # (the move itself will run with the previous speed)
            self.dev.dev.setSpeed(userSpeed / 1e-6)

    def wasInterrupted(self):
        """Return True if the move was interrupted before completing.
        """
        return self._interrupted

    def isDone(self):
        """Return True if the move is complete.
        """
        return self._getStatus() != 0

    def _getStatus(self):
        # check status of move unless we already know it is complete.
        # 0: still moving; 1: finished successfully; -1: finished unsuccessfully
        if self._finished:
            if self._interrupted:
                return -1
            else:
                return 1
        if self.dev.dev.isMoving():
            # Still moving
            return 0
        # did we reach target?
        pos = self.dev._getPosition()
        dif = ((np.array(pos) - np.array(self.targetPos))**2).sum()**0.5
        if dif < 1.0:
            # reached target
            self._finished = True
            return 1
        else:
            # missed
            self._finished = True
            self._interrupted = True
            self._errorMsg = "Move did not complete (target=%s, position=%s, dif=%s)." % (self.targetPos, pos, dif)
            return -1

    def _stopped(self):
        # Called when the manipulator is stopped, possibly interrupting this move.
        startTime = ptime.time()
        while True:
            status = self._getStatus()
            if status == 1:
                # finished; ignore stop
                return
            elif status == -1:
                self._errorMsg = "Move was interrupted before completion."
                return
            elif status == 0 and ptime.time() < startTime + 0.15:
                # allow 150ms to stop
                continue
            elif status == 0:
                # not actually stopped! This should not happen.
                raise RuntimeError("Interrupted move but manipulator is still running!")
            else:
                raise Exception("Unknown status: %s" % status)

    def errorMessage(self):
        return self._errorMsg

class ScientificaGUI(StageInterface):
    def __init__(self, dev, win):
        StageInterface.__init__(self, dev, win)

        # Insert Scientifica-specific controls into GUI
        self.zeroBtn = Qt.QPushButton('Zero position')
        nextRow = self.layout.rowCount()
        self.layout.addWidget(self.zeroBtn, nextRow, 0, 1, 2)
        nextRow += 1

        self.psGroup = Qt.QGroupBox('Rotary Controller')
        self.layout.addWidget(self.psGroup, nextRow, 0, 1, 2)
        nextRow += 1

        self.psLayout = Qt.QGridLayout()
        self.psGroup.setLayout(self.psLayout)
        self.speedLabel = Qt.QLabel('Speed')
        self.speedSpin = SpinBox(value=self.dev.userSpeed, suffix='m/turn', siPrefix=True, dec=True, bounds=[1e-6, 10e-3])
        self.psLayout.addWidget(self.speedLabel, 0, 0)
        self.psLayout.addWidget(self.speedSpin, 0, 1)

        self.zeroBtn.clicked.connect(self.dev.dev.zeroPosition)
        self.speedSpin.valueChanged.connect(lambda v: self.dev.setDefaultSpeed(v))

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import print_function
import time
import numpy as np
from acq4.util import Qt
from ..Stage import Stage, MoveFuture, StageInterface
from acq4.drivers.Scientifica import Scientifica as ScientificaDriver
from acq4.util.Mutex import Mutex
from acq4.util.Thread import Thread
from acq4.pyqtgraph import debug, ptime, SpinBox

class Scientifica(Stage):
    """
    A Scientifica motorized device.

    This class supports PatchStar, MicroStar, SliceScope, objective changers, etc.
    The device may be identified either by its serial port or by its description
    string:

        port: <serial port>  # eg. 'COM1' or '/dev/ttyACM0'
        name: <string>  # eg. 'SliceScope' or 'MicroStar 2'
        baudrate: <int>  #  may be 9600 or 38400

    The optional 'baudrate' parameter is used to set the baudrate of the device.
    Both valid rates will be attempted when initially connecting.
    """
    def __init__(self, man, config, name):
        # can specify
        port = config.pop('port', None)
        name = config.pop('name', None)

        self.scale = config.pop('scale', (1e-6, 1e-6, 1e-6))
        baudrate = config.pop('baudrate', None)
        ctrl_version = config.pop('version', 2)
        try:
            self.dev = ScientificaDriver(port=port, name=name, baudrate=baudrate, ctrl_version=ctrl_version)
        except RuntimeError as err:
            if hasattr(err, 'dev_version'):
                raise RuntimeError(err.message + " You must add `version=%d` to the configuration for this device and double-check any speed/acceleration parameters." % int(err.dev_version))
            else:
                raise

        # Controllers reset their baud to 9600 after power cycle
        if baudrate is not None and self.dev.getBaudrate() != baudrate:
            self.dev.setBaudrate(baudrate)

        self._lastMove = None
        man.sigAbortAll.connect(self.abort)

        Stage.__init__(self, man, config, name)

        # clear cached position for this device and re-read to generate an initial position update
        self._lastPos = None
        self.getPosition(refresh=True)

        # Set approach angle
        # Disabled--this toggles the approach bit and we can't reconfigure it from here :(
        # approach = self.dev.send('APPROACH')
        # self.dev.send('ANGLE %f' % self.pitch)
        # self.dev.send('APPROACH %s' % approach)  # reset approach bit; setting angle enables it

        # set any extra parameters specified in the config
        params = config.get('params', {})
        for param, val in params.items():
            if param == 'currents':
                assert len(val) == 2
                self.dev.setCurrents(*val)
            elif param == 'axisScale':
                assert len(val) == 3
                for i, x in enumerate(val):
                    self.dev.setAxisScale(i, x)
            else:
                self.dev.setParam(param, val)

        self.setUserSpeed(config.get('userSpeed', self.dev.getSpeed() * abs(self.scale[0])))

        # whether to monitor for changes to a MOC
        self.monitorObj = config.get('monitorObjective', False)
        if self.monitorObj is True:
            if self.dev._version < 3:
                raise TypeError("Scientifica motion card version %s does not support reading objective position." % self.dev._version)
            self.objectiveState = None
            self._checkObjective()

        # thread for polling position changes

        self.monitor = MonitorThread(self, self.monitorObj)
        self.monitor.start()

    def capabilities(self):
        """Return a structure describing the capabilities of this device"""
        if 'capabilities' in self.config:
            return self.config['capabilities']
        else:
            return {
                'getPos': (True, True, True),
                'setPos': (True, True, True),
                'limits': (False, False, False),
            }

    def stop(self):
        """Stop the manipulator immediately.
        """
        with self.lock:
            self.dev.stop()
            if self._lastMove is not None:
                self._lastMove._stopped()
            self._lastMove = None

    def abort(self):
        """Stop the manipulator immediately.
        """
        self.dev.stop()
        if self._lastMove is not None:
            self._lastMove._stopped()
            self._lastMove = None

    def setUserSpeed(self, v):
        """Set the maximum speed of the stage (m/sec) when under manual control.

        The stage's maximum speed is reset to this value when it is not under
        programmed control.
        """
        self.userSpeed = v
        self.dev.setSpeed(v / abs(self.scale[0]))

    def _getPosition(self):
        # Called by superclass when user requests position refresh
        with self.lock:
            pos = self.dev.getPos()
            pos = [pos[i] * self.scale[i] for i in (0, 1, 2)]
            if pos != self._lastPos:
                self._lastPos = pos
                emit = True
            else:
                emit = False

        if emit:
            # don't emit signal while locked
            self.posChanged(pos)

        return pos

    def targetPosition(self):
        with self.lock:
            if self._lastMove is None or self._lastMove.isDone():
                return self.getPosition()
            else:
                return self._lastMove.targetPos

    def quit(self):
        self.monitor.stop()
        Stage.quit(self)

    def _move(self, abs, rel, speed, linear):
        with self.lock:
            if self._lastMove is not None and not self._lastMove.isDone():
                self.stop()
            pos = self._toAbsolutePosition(abs, rel)
            speed = self._interpretSpeed(speed)

            self._lastMove = ScientificaMoveFuture(self, pos, speed, self.userSpeed)
            return self._lastMove

    def deviceInterface(self, win):
        return ScientificaGUI(self, win)

    def startMoving(self, vel):
        """Begin moving the stage at a continuous velocity.
        """
        # s = [int(-v * 1000. / 67. / self.scale[i]) for i,v in enumerate(vel)]
        # print(s)
        # self.dev.send('VJ %d %d %d C' % tuple(s))
        s = [int(1e8 * v) for i,v in enumerate(vel)]
        self.dev.send('VJ -%d %d %d' % tuple(s))

    def _checkObjective(self):
        with self.lock:
            obj = int(self.dev.send('obj'))
            if obj != self.objectiveState:
                self.objectiveState = obj
                self.sigSwitchChanged.emit(self, {'objective': obj})

    def getSwitch(self, name):
        if name == 'objective' and self.monitorObj:
            return self.objectiveState
        else:
            return Stage.getSwitch(self, name)

class MonitorThread(Thread):
    """Thread to poll for manipulator position changes.
    """
    def __init__(self, dev, monitorObj):
        self.dev = dev
        self.lock = Mutex(recursive=True)
        self.monitorObj = monitorObj
        self.stopped = False
        self.interval = 0.3

        Thread.__init__(self)

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

    def stop(self):
        with self.lock:
            self.stopped = True

    def setInterval(self, i):
        with self.lock:
            self.interval = i

    def run(self):
        minInterval = 100e-3
        interval = minInterval
        lastPos = None
        while True:
            try:
                with self.lock:
                    if self.stopped:
                        break
                    maxInterval = self.interval

                pos = self.dev._getPosition()  # this causes sigPositionChanged to be emitted
                if pos != lastPos:
                    # if there was a change, then loop more rapidly for a short time.
                    interval = minInterval
                    lastPos = pos
                else:
                    interval = min(maxInterval, interval*2)

                if self.monitorObj is True:
                    self.dev._checkObjective()

                time.sleep(interval)
            except:
                debug.printExc('Error in Scientifica monitor thread:')
                time.sleep(maxInterval)

class ScientificaMoveFuture(MoveFuture):
    """Provides access to a move-in-progress on a Scientifica manipulator.
    """
    def __init__(self, dev, pos, speed, userSpeed):
        MoveFuture.__init__(self, dev, pos, speed)
        self._interrupted = False
        self._errorMSg = None
        self._finished = False
        pos = np.array(pos) / np.array(self.dev.scale)
        with self.dev.dev.lock:
            self.dev.dev.moveTo(pos, speed / abs(self.dev.scale[0]))
            # reset to user speed immediately after starting move
            # (the move itself will run with the previous speed)
            self.dev.dev.setSpeed(userSpeed / abs(self.dev.scale[0]))

    def wasInterrupted(self):
        """Return True if the move was interrupted before completing.
        """
        return self._interrupted

    def isDone(self):
        """Return True if the move is complete.
        """
        return self._getStatus() != 0

    def _getStatus(self):
        # check status of move unless we already know it is complete.
        # 0: still moving; 1: finished successfully; -1: finished unsuccessfully
        if self._finished:
            if self._interrupted:
                return -1
            else:
                return 1
        if self.dev.dev.isMoving():
            # Still moving
            return 0
        # did we reach target?
        pos = self.dev._getPosition()
        dif = ((np.array(pos) - np.array(self.targetPos))**2).sum()**0.5
        if dif < 2.5e-6:
            # reached target
            self._finished = True
            return 1
        else:
            # missed
            self._finished = True
            self._interrupted = True
            self._errorMsg = "Move did not complete (target=%s, position=%s, dif=%s)." % (self.targetPos, pos, dif)
            return -1

    def _stopped(self):
        # Called when the manipulator is stopped, possibly interrupting this move.
        status = self._getStatus()
        if status == 1:
            # finished; ignore stop
            return
        elif status == -1:
            self._errorMsg = "Move was interrupted before completion."
        elif status == 0:
            # not actually stopped! This should not happen.
            raise RuntimeError("Interrupted move but manipulator is still running!")
        else:
            raise Exception("Unknown status: %s" % status)

    def errorMessage(self):
        return self._errorMsg

class ScientificaGUI(StageInterface):
    def __init__(self, dev, win):
        StageInterface.__init__(self, dev, win)

        # Insert Scientifica-specific controls into GUI
        self.zeroBtn = Qt.QPushButton('Zero position')
        self.layout.addWidget(self.zeroBtn, self.nextRow, 0, 1, 2)
        self.nextRow += 1

        self.psGroup = Qt.QGroupBox('Rotary Controller')
        self.layout.addWidget(self.psGroup, self.nextRow, 0, 1, 2)
        self.nextRow += 1

        self.psLayout = Qt.QGridLayout()
        self.psGroup.setLayout(self.psLayout)
        self.speedLabel = Qt.QLabel('Speed')
        self.speedSpin = SpinBox(value=self.dev.userSpeed, suffix='m/turn', siPrefix=True, dec=True, bounds=[1e-6, 10e-3])
        self.psLayout.addWidget(self.speedLabel, 0, 0)
        self.psLayout.addWidget(self.speedSpin, 0, 1)

        self.zeroBtn.clicked.connect(self.dev.dev.zeroPosition)
        self.speedSpin.valueChanged.connect(lambda v: self.dev.setDefaultSpeed(v))

<commit_msg>Allow 'obj' command for scientifica v2 devlceis<commit_after># -*- coding: utf-8 -*-
from __future__ import print_function
import time
import numpy as np
from acq4.util import Qt
from ..Stage import Stage, MoveFuture, StageInterface
from acq4.drivers.Scientifica import Scientifica as ScientificaDriver
from acq4.util.Mutex import Mutex
from acq4.util.Thread import Thread
from acq4.pyqtgraph import debug, ptime, SpinBox

class Scientifica(Stage):
    """
    A Scientifica motorized device.

    This class supports PatchStar, MicroStar, SliceScope, objective changers, etc.
    The device may be identified either by its serial port or by its description
    string:

        port: <serial port>  # eg. 'COM1' or '/dev/ttyACM0'
        name: <string>  # eg. 'SliceScope' or 'MicroStar 2'
        baudrate: <int>  #  may be 9600 or 38400

    The optional 'baudrate' parameter is used to set the baudrate of the device.
    Both valid rates will be attempted when initially connecting.
    """
    def __init__(self, man, config, name):
        # can specify
        port = config.pop('port', None)
        name = config.pop('name', None)

        self.scale = config.pop('scale', (1e-6, 1e-6, 1e-6))
        baudrate = config.pop('baudrate', None)
        ctrl_version = config.pop('version', 2)
        try:
            self.dev = ScientificaDriver(port=port, name=name, baudrate=baudrate, ctrl_version=ctrl_version)
        except RuntimeError as err:
            if hasattr(err, 'dev_version'):
                raise RuntimeError(err.message + " You must add `version=%d` to the configuration for this device and double-check any speed/acceleration parameters." % int(err.dev_version))
            else:
                raise

        # Controllers reset their baud to 9600 after power cycle
        if baudrate is not None and self.dev.getBaudrate() != baudrate:
            self.dev.setBaudrate(baudrate)

        self._lastMove = None
        man.sigAbortAll.connect(self.abort)

        Stage.__init__(self, man, config, name)

        # clear cached position for this device and re-read to generate an initial position update
        self._lastPos = None
        self.getPosition(refresh=True)

        # Set approach angle
        # Disabled--this toggles the approach bit and we can't reconfigure it from here :(
        # approach = self.dev.send('APPROACH')
        # self.dev.send('ANGLE %f' % self.pitch)
        # self.dev.send('APPROACH %s' % approach)  # reset approach bit; setting angle enables it

        # set any extra parameters specified in the config
        params = config.get('params', {})
        for param, val in params.items():
            if param == 'currents':
                assert len(val) == 2
                self.dev.setCurrents(*val)
            elif param == 'axisScale':
                assert len(val) == 3
                for i, x in enumerate(val):
                    self.dev.setAxisScale(i, x)
            else:
                self.dev.setParam(param, val)

        self.setUserSpeed(config.get('userSpeed', self.dev.getSpeed() * abs(self.scale[0])))

        # whether to monitor for changes to a MOC
        self.monitorObj = config.get('monitorObjective', False)
        if self.monitorObj is True:
            self.objectiveState = None
            self._checkObjective()

        # thread for polling position changes

        self.monitor = MonitorThread(self, self.monitorObj)
        self.monitor.start()

    def capabilities(self):
        """Return a structure describing the capabilities of this device"""
        if 'capabilities' in self.config:
            return self.config['capabilities']
        else:
            return {
                'getPos': (True, True, True),
                'setPos': (True, True, True),
                'limits': (False, False, False),
            }

    def stop(self):
        """Stop the manipulator immediately.
        """
        with self.lock:
            self.dev.stop()
            if self._lastMove is not None:
                self._lastMove._stopped()
            self._lastMove = None

    def abort(self):
        """Stop the manipulator immediately.
        """
        self.dev.stop()
        if self._lastMove is not None:
            self._lastMove._stopped()
            self._lastMove = None

    def setUserSpeed(self, v):
        """Set the maximum speed of the stage (m/sec) when under manual control.

        The stage's maximum speed is reset to this value when it is not under
        programmed control.
        """
        self.userSpeed = v
        self.dev.setSpeed(v / abs(self.scale[0]))

    def _getPosition(self):
        # Called by superclass when user requests position refresh
        with self.lock:
            pos = self.dev.getPos()
            pos = [pos[i] * self.scale[i] for i in (0, 1, 2)]
            if pos != self._lastPos:
                self._lastPos = pos
                emit = True
            else:
                emit = False

        if emit:
            # don't emit signal while locked
            self.posChanged(pos)

        return pos

    def targetPosition(self):
        with self.lock:
            if self._lastMove is None or self._lastMove.isDone():
                return self.getPosition()
            else:
                return self._lastMove.targetPos

    def quit(self):
        self.monitor.stop()
        Stage.quit(self)

    def _move(self, abs, rel, speed, linear):
        with self.lock:
            if self._lastMove is not None and not self._lastMove.isDone():
                self.stop()
            pos = self._toAbsolutePosition(abs, rel)
            speed = self._interpretSpeed(speed)

            self._lastMove = ScientificaMoveFuture(self, pos, speed, self.userSpeed)
            return self._lastMove

    def deviceInterface(self, win):
        return ScientificaGUI(self, win)

    def startMoving(self, vel):
        """Begin moving the stage at a continuous velocity.
        """
        # s = [int(-v * 1000. / 67. / self.scale[i]) for i,v in enumerate(vel)]
        # print(s)
        # self.dev.send('VJ %d %d %d C' % tuple(s))
        s = [int(1e8 * v) for i,v in enumerate(vel)]
        self.dev.send('VJ -%d %d %d' % tuple(s))

    def _checkObjective(self):
        with self.lock:
            obj = int(self.dev.send('obj'))
            if obj != self.objectiveState:
                self.objectiveState = obj
                self.sigSwitchChanged.emit(self, {'objective': obj})

    def getSwitch(self, name):
        if name == 'objective' and self.monitorObj:
            return self.objectiveState
        else:
            return Stage.getSwitch(self, name)

class MonitorThread(Thread):
    """Thread to poll for manipulator position changes.
    """
    def __init__(self, dev, monitorObj):
        self.dev = dev
        self.lock = Mutex(recursive=True)
        self.monitorObj = monitorObj
        self.stopped = False
        self.interval = 0.3

        Thread.__init__(self)

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

    def stop(self):
        with self.lock:
            self.stopped = True

    def setInterval(self, i):
        with self.lock:
            self.interval = i

    def run(self):
        minInterval = 100e-3
        interval = minInterval
        lastPos = None
        while True:
            try:
                with self.lock:
                    if self.stopped:
                        break
                    maxInterval = self.interval

                pos = self.dev._getPosition()  # this causes sigPositionChanged to be emitted
                if pos != lastPos:
                    # if there was a change, then loop more rapidly for a short time.
                    interval = minInterval
                    lastPos = pos
                else:
                    interval = min(maxInterval, interval*2)

                if self.monitorObj is True:
                    self.dev._checkObjective()

                time.sleep(interval)
            except:
                debug.printExc('Error in Scientifica monitor thread:')
                time.sleep(maxInterval)

class ScientificaMoveFuture(MoveFuture):
    """Provides access to a move-in-progress on a Scientifica manipulator.
    """
    def __init__(self, dev, pos, speed, userSpeed):
        MoveFuture.__init__(self, dev, pos, speed)
        self._interrupted = False
        self._errorMSg = None
        self._finished = False
        pos = np.array(pos) / np.array(self.dev.scale)
        with self.dev.dev.lock:
            self.dev.dev.moveTo(pos, speed / abs(self.dev.scale[0]))
            # reset to user speed immediately after starting move
            # (the move itself will run with the previous speed)
            self.dev.dev.setSpeed(userSpeed / abs(self.dev.scale[0]))

    def wasInterrupted(self):
        """Return True if the move was interrupted before completing.
        """
        return self._interrupted

    def isDone(self):
        """Return True if the move is complete.
        """
        return self._getStatus() != 0

    def _getStatus(self):
        # check status of move unless we already know it is complete.
        # 0: still moving; 1: finished successfully; -1: finished unsuccessfully
        if self._finished:
            if self._interrupted:
                return -1
            else:
                return 1
        if self.dev.dev.isMoving():
            # Still moving
            return 0
        # did we reach target?
        pos = self.dev._getPosition()
        dif = ((np.array(pos) - np.array(self.targetPos))**2).sum()**0.5
        if dif < 2.5e-6:
            # reached target
            self._finished = True
            return 1
        else:
            # missed
            self._finished = True
            self._interrupted = True
            self._errorMsg = "Move did not complete (target=%s, position=%s, dif=%s)." % (self.targetPos, pos, dif)
            return -1

    def _stopped(self):
        # Called when the manipulator is stopped, possibly interrupting this move.
        status = self._getStatus()
        if status == 1:
            # finished; ignore stop
            return
        elif status == -1:
            self._errorMsg = "Move was interrupted before completion."
        elif status == 0:
            # not actually stopped! This should not happen.
            raise RuntimeError("Interrupted move but manipulator is still running!")
        else:
            raise Exception("Unknown status: %s" % status)

    def errorMessage(self):
        return self._errorMsg

class ScientificaGUI(StageInterface):
    def __init__(self, dev, win):
        StageInterface.__init__(self, dev, win)

        # Insert Scientifica-specific controls into GUI
        self.zeroBtn = Qt.QPushButton('Zero position')
        self.layout.addWidget(self.zeroBtn, self.nextRow, 0, 1, 2)
        self.nextRow += 1

        self.psGroup = Qt.QGroupBox('Rotary Controller')
        self.layout.addWidget(self.psGroup, self.nextRow, 0, 1, 2)
        self.nextRow += 1

        self.psLayout = Qt.QGridLayout()
        self.psGroup.setLayout(self.psLayout)
        self.speedLabel = Qt.QLabel('Speed')
        self.speedSpin = SpinBox(value=self.dev.userSpeed, suffix='m/turn', siPrefix=True, dec=True, bounds=[1e-6, 10e-3])
        self.psLayout.addWidget(self.speedLabel, 0, 0)
        self.psLayout.addWidget(self.speedSpin, 0, 1)

        self.zeroBtn.clicked.connect(self.dev.dev.zeroPosition)
        self.speedSpin.valueChanged.connect(lambda v: self.dev.setDefaultSpeed(v))

<|endoftext|>