<commit_before>#-----------------------------------------------------------------------------
# Copyright (c) 2013, The BiPy Developers.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------

from __future__ import absolute_import, division, print_function

import numpy as np

from .base import Ordination, OrdinationResults
from .utils import corr, svd_rank, scale

class CCA(Ordination):
    short_method_name = 'CCA'
    long_method_name = 'Canonical Correspondence Analysis'

    def __init__(self, Y, X):
        r"""Compute constrained (also known as canonical) correspondence
        analysis.

        Canonical (or constrained) correspondence analysis is a
        multivariate ordination technique.

        In exploratory data analysis, ordination (or multivariate
        gradient analysis) complements clustering by arranging objects
        (species, samples...) along gradients so that similar ones are
        closer and dissimilar ones are further. There's a good overview
        of the available techniques in
        http://ordination.okstate.edu/overview.htm.

        Parameters
        ----------
        Y : array_like
            Community data matrix of shape (n, m): a contingency table
            for m species at n sites.
        X : array_like
            Constraining matrix of shape (n, q): q quantitative
            environmental variables at n sites.

        Notes
        -----
        There are a couple of techniques to search for multivariate
        relationships between two datasets with very similar names, so
        this can get confusing:

        - Canonical correlation analysis is a statistical tool that,
          given two vectors of random variables, finds linear
          combinations that have maximum correlation with each other. In
          some sense, assumes linear responses of *species* to
          *environmental variables*. This is not implemented here.

        - Canonical (or constrained) correspondence analysis appeared in
          community ecology [1]_ and relates community composition to
          the variation in the environment (or in other factors). It
          works from data on abundances or counts of individuals and
          environmental variables, and outputs ordination axes that
          maximize niche separation among species. It is better suited
          to extract the niches of taxa than linear multivariate methods
          like canonical correlation analysis because it assumes
          unimodal response curves (habitat preferences are often
          unimodal functions of habitat variables [2]_).

        References
        ----------

        .. [1] Cajo J. F. Ter Braak, "Canonical Correspondence
            Analysis: A New Eigenvector Technique for Multivariate
            Direct Gradient Analysis", Ecology 67.5 (1986),
            pp. 1167-1179.

        .. [2] Cajo J.F. Braak and Piet F.M. Verdonschot, "Canonical
            correspondence analysis and related multivariate methods
            in aquatic ecology", Aquatic Sciences 57.3 (1995),
            pp. 255-289.

        """

        self.Y = np.asarray(Y, dtype=np.float64)
        self.X = np.asarray(X, dtype=np.float64)
        self._cca()

    def _cca(self):
        X, Y = self.X, self.Y
        if X.shape[0] != Y.shape[0]:
            raise ValueError("Contingency and environmental tables must have"
                             " the same number of rows (sites)")
        if Y.min() < 0:
            raise ValueError("Contingency table must be nonnegative")
        row_max = Y.max(axis=1)
        if np.any(row_max <= 0):
            # Or else the lstsq call to compute Y_hat breaks
            raise ValueError("Contingency table cannot contain row of only 0s")

        # Step 1 (similar to Pearson chi-square statistic)
        grand_total = Y.sum()
        Q = Y / grand_total  # Relative frequencies of X (contingency table)

        # Species and site weights (marginal totals)
        column_marginals = Q.sum(axis=0)
        row_marginals = Q.sum(axis=1)

        # Formula 9.32 in Lagrange & Lagrange (1998). Notice that it's an
        # scaled version of the contribution of each cell towards Pearson
        # chi-square statistic.
        expected = np.outer(row_marginals, column_marginals)
        Q_bar = (Q - expected) / np.sqrt(expected)

        # Step 2. Standardize columns of Y with respect to site weights,
        # using the maximum likelyhood variance estimator (Legendre &
        # Legendre 1998, p. 595)
        X = scale(X, weights=row_marginals, ddof=0)

        # Step 3. Weighted multiple regression.
        X_weighted = row_marginals[:, None]**0.5 * X
        B, _, rank_lstsq, _ = np.linalg.lstsq(X_weighted, Q_bar)
        Y_hat = X_weighted.dot(B)
        Y_res = Q_bar - Y_hat

        # Step 4. Eigenvalue decomposition
        u, s, vt = np.linalg.svd(Y_hat, full_matrices=False)
        rank = svd_rank(Y_hat.shape, s)
        s = s[:rank]
        u = u[:, :rank]
        vt = vt[:rank]
        U = vt.T

        # Step 5. Eq. 9.38
        U_hat = Q_bar.dot(U) * s**-1

        # Residuals analysis
        u_res, s_res, vt_res = np.linalg.svd(Y_res, full_matrices=False)
        rank = svd_rank(Y_res.shape, s_res)
        s_res = s_res[:rank]
        u_res = u_res[:, :rank]
        vt_res = vt_res[:rank]

        U_res = vt_res.T
        U_hat_res = Q_bar.dot(U_res) * s_res**-1

        self.column_marginals = column_marginals
        self.row_marginals = row_marginals
        self.U = U
        # Storing values needed to compute scores (better way to do this?)
        for val_name, val in (('column_marginals', column_marginals), ('row_marginals', row_marginals), ('U', U), ('U_res', U_res), ('U_hat', U_hat), ('U_hat_res', U_hat_res), ('u', u), ('Y_hat', Y_hat), ('s', s), ('s_res', s_res), ('X_weighted', X_weighted[:, :rank_lstsq])):
            setattr(self, val_name, val)

        self.eigenvalues = np.r_[s, s_res]**2

    def scores(self, scaling):
        if scaling not in {1, 2}:
            raise NotImplementedError(
                "Scaling {0} not implemented.".format(scaling))
        # In this case scores are also a bit intertwined, so we'll
        # almost compute them both and then choose.

        ## Scalings (p. 596 L&L 1998)
        # Species scores, scaling 1
        V = (self.column_marginals**-0.5)[:, None] * self.U
        # Site scores, scaling 2
        V_hat = (self.row_marginals**-0.5)[:, None] * self.U_hat
        # Site scores, scaling 1
        F = V_hat * self.s
        # Species scores, scaling 2
        F_hat = V * self.s
        # Site scores which are linear combinations of environmental
        # variables
        Z_scaling1 = (self.row_marginals**-0.5)[:, None] * self.Y_hat.dot(self.U)
        Z_scaling2 = Z_scaling1 * self.s**-1

        # Species residual scores, scaling 1
        V_res = (self.column_marginals**-0.5)[:, None] * self.U_res
        # Site residual scores, scaling 2
        V_hat_res = (self.row_marginals**-0.5)[:, None] * self.U_hat_res
        # Site residual scores, scaling 1
        F_res = V_hat_res * self.s_res
        # Species residual scores, scaling 2
        F_hat_res = V_res * self.s_res

        eigvals = self.eigenvalues
        species_scores = np.hstack([(V, V_res),
                                    (F_hat, F_hat_res)][scaling - 1])
        site_scores = np.hstack([(F, F_res),
                                 (V_hat, V_hat_res)][scaling - 1])
        site_constraints = np.hstack([(Z_scaling1, F_res),
                                      (Z_scaling2, V_hat_res)][scaling - 1])
        biplot_scores = corr(self.X_weighted, self.u)
        return OrdinationResults(eigvals=eigvals,
                                 species=species_scores,
                                 site=site_scores,
                                 biplot=biplot_scores,
                                 site_constraints=site_constraints)
<commit_msg>Improve error message (@ElDeveloper )<commit_after>#-----------------------------------------------------------------------------
# Copyright (c) 2013, The BiPy Developers.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------

from __future__ import absolute_import, division, print_function

import numpy as np

from .base import Ordination, OrdinationResults
from .utils import corr, svd_rank, scale

class CCA(Ordination):
    short_method_name = 'CCA'
    long_method_name = 'Canonical Correspondence Analysis'

    def __init__(self, Y, X):
        r"""Compute constrained (also known as canonical) correspondence
        analysis.

        Canonical (or constrained) correspondence analysis is a
        multivariate ordination technique.

        In exploratory data analysis, ordination (or multivariate
        gradient analysis) complements clustering by arranging objects
        (species, samples...) along gradients so that similar ones are
        closer and dissimilar ones are further. There's a good overview
        of the available techniques in
        http://ordination.okstate.edu/overview.htm.

        Parameters
        ----------
        Y : array_like
            Community data matrix of shape (n, m): a contingency table
            for m species at n sites.
        X : array_like
            Constraining matrix of shape (n, q): q quantitative
            environmental variables at n sites.

        Notes
        -----
        There are a couple of techniques to search for multivariate
        relationships between two datasets with very similar names, so
        this can get confusing:

        - Canonical correlation analysis is a statistical tool that,
          given two vectors of random variables, finds linear
          combinations that have maximum correlation with each other. In
          some sense, assumes linear responses of *species* to
          *environmental variables*. This is not implemented here.

        - Canonical (or constrained) correspondence analysis appeared in
          community ecology [1]_ and relates community composition to
          the variation in the environment (or in other factors). It
          works from data on abundances or counts of individuals and
          environmental variables, and outputs ordination axes that
          maximize niche separation among species. It is better suited
          to extract the niches of taxa than linear multivariate methods
          like canonical correlation analysis because it assumes
          unimodal response curves (habitat preferences are often
          unimodal functions of habitat variables [2]_).

        References
        ----------

        .. [1] Cajo J. F. Ter Braak, "Canonical Correspondence
            Analysis: A New Eigenvector Technique for Multivariate
            Direct Gradient Analysis", Ecology 67.5 (1986),
            pp. 1167-1179.

        .. [2] Cajo J.F. Braak and Piet F.M. Verdonschot, "Canonical
            correspondence analysis and related multivariate methods
            in aquatic ecology", Aquatic Sciences 57.3 (1995),
            pp. 255-289.

        """

        self.Y = np.asarray(Y, dtype=np.float64)
        self.X = np.asarray(X, dtype=np.float64)
        self._cca()

    def _cca(self):
        X, Y = self.X, self.Y
        if X.shape[0] != Y.shape[0]:
            raise ValueError("Contingency and environmental tables must have"
                             " the same number of rows (sites). X has {xrows}"
                             " rows but Y has {yrows}".format(X.shape[0],
                                                              Y.shape[0]))
        if Y.min() < 0:
            raise ValueError("Contingency table must be nonnegative")
        row_max = Y.max(axis=1)
        if np.any(row_max <= 0):
            # Or else the lstsq call to compute Y_hat breaks
            raise ValueError("Contingency table cannot contain row of only 0s")

        # Step 1 (similar to Pearson chi-square statistic)
        grand_total = Y.sum()
        Q = Y / grand_total  # Relative frequencies of X (contingency table)

        # Species and site weights (marginal totals)
        column_marginals = Q.sum(axis=0)
        row_marginals = Q.sum(axis=1)

        # Formula 9.32 in Lagrange & Lagrange (1998). Notice that it's an
        # scaled version of the contribution of each cell towards Pearson
        # chi-square statistic.
        expected = np.outer(row_marginals, column_marginals)
        Q_bar = (Q - expected) / np.sqrt(expected)

        # Step 2. Standardize columns of Y with respect to site weights,
        # using the maximum likelyhood variance estimator (Legendre &
        # Legendre 1998, p. 595)
        X = scale(X, weights=row_marginals, ddof=0)

        # Step 3. Weighted multiple regression.
        X_weighted = row_marginals[:, None]**0.5 * X
        B, _, rank_lstsq, _ = np.linalg.lstsq(X_weighted, Q_bar)
        Y_hat = X_weighted.dot(B)
        Y_res = Q_bar - Y_hat

        # Step 4. Eigenvalue decomposition
        u, s, vt = np.linalg.svd(Y_hat, full_matrices=False)
        rank = svd_rank(Y_hat.shape, s)
        s = s[:rank]
        u = u[:, :rank]
        vt = vt[:rank]
        U = vt.T

        # Step 5. Eq. 9.38
        U_hat = Q_bar.dot(U) * s**-1

        # Residuals analysis
        u_res, s_res, vt_res = np.linalg.svd(Y_res, full_matrices=False)
        rank = svd_rank(Y_res.shape, s_res)
        s_res = s_res[:rank]
        u_res = u_res[:, :rank]
        vt_res = vt_res[:rank]

        U_res = vt_res.T
        U_hat_res = Q_bar.dot(U_res) * s_res**-1

        self.column_marginals = column_marginals
        self.row_marginals = row_marginals
        self.U = U
        # Storing values needed to compute scores (better way to do this?)
        for val_name, val in (('column_marginals', column_marginals), ('row_marginals', row_marginals), ('U', U), ('U_res', U_res), ('U_hat', U_hat), ('U_hat_res', U_hat_res), ('u', u), ('Y_hat', Y_hat), ('s', s), ('s_res', s_res), ('X_weighted', X_weighted[:, :rank_lstsq])):
            setattr(self, val_name, val)

        self.eigenvalues = np.r_[s, s_res]**2

    def scores(self, scaling):
        if scaling not in {1, 2}:
            raise NotImplementedError(
                "Scaling {0} not implemented.".format(scaling))
        # In this case scores are also a bit intertwined, so we'll
        # almost compute them both and then choose.

        ## Scalings (p. 596 L&L 1998)
        # Species scores, scaling 1
        V = (self.column_marginals**-0.5)[:, None] * self.U
        # Site scores, scaling 2
        V_hat = (self.row_marginals**-0.5)[:, None] * self.U_hat
        # Site scores, scaling 1
        F = V_hat * self.s
        # Species scores, scaling 2
        F_hat = V * self.s
        # Site scores which are linear combinations of environmental
        # variables
        Z_scaling1 = (self.row_marginals**-0.5)[:, None] * self.Y_hat.dot(self.U)
        Z_scaling2 = Z_scaling1 * self.s**-1

        # Species residual scores, scaling 1
        V_res = (self.column_marginals**-0.5)[:, None] * self.U_res
        # Site residual scores, scaling 2
        V_hat_res = (self.row_marginals**-0.5)[:, None] * self.U_hat_res
        # Site residual scores, scaling 1
        F_res = V_hat_res * self.s_res
        # Species residual scores, scaling 2
        F_hat_res = V_res * self.s_res

        eigvals = self.eigenvalues
        species_scores = np.hstack([(V, V_res),
                                    (F_hat, F_hat_res)][scaling - 1])
        site_scores = np.hstack([(F, F_res),
                                 (V_hat, V_hat_res)][scaling - 1])
        site_constraints = np.hstack([(Z_scaling1, F_res),
                                      (Z_scaling2, V_hat_res)][scaling - 1])
        biplot_scores = corr(self.X_weighted, self.u)
        return OrdinationResults(eigvals=eigvals,
                                 species=species_scores,
                                 site=site_scores,
                                 biplot=biplot_scores,
                                 site_constraints=site_constraints)
<|endoftext|>


<commit_before>from .__about__ import (__title__, __package_name__, __version__, __summary__, __url__,
                        __keywords__, __email__, __author__, __license__, __copyright__)
<commit_msg>let's see how travis feels<commit_after>from .__about__ import (__title__, __package_name__, __version__, __summary__, __url__,
                        __keywords__, __email__, __author__, __license__, __copyright__)

from . import config
from . import logger

__all__ = [
    'config',
    'logger',
]
<|endoftext|>


<commit_before># Distributed under the terms of GPL version 2 or any later
# Copyright (C) Alexey Nezhdanov 2004
# router, presence tracker and probes responder for xmppd.py

# $Id: router.py,v 1.5 2004/10/24 04:36:28 snakeru Exp $

from xmpp import *
from xmppd import *
from constants import *
from types import *
from filter import *

import os

class Router(PlugIn):
    """ The first entity that gets access to arrived stanza. """
    NS='presence'
    def plugin(self,server):
        self._data = {}
        # For the common folk
	#server.Dispatcher.RegisterNamespace(NS_COMPONENT_ACCEPT)

	server.Dispatcher.RegisterHandler('presence',self.presenceHandler)

	server.Dispatcher.RegisterNamespaceHandler(NS_CLIENT,self.routerHandler)
        server.Dispatcher.RegisterNamespaceHandler(NS_SERVER,self.routerHandler)

	#server.Dispatcher.RegisterNamespaceHandler(NS_COMPONENT_ACCEPT,self.routerHandler) # Mine
	#server.Dispatcher.RegisterHandler('handshake',component.Component.componentHandler,xmlns=NS_COMPONENT_ACCEPT)  # Mine
	#server.Dispatcher.RegisterHandler('message',self.routerHandler,xmlns=NS_COMPONENT_ACCEPT)  # Mine

	self.server = server

	#Init filters
	for name in server.router_filter_names:
		"""
		if name[-3:] == ".py":
			name = name[:-3]
			#name = name + ".py"
		#name = 'xmppd' + '.' + name

		try:
			module = __import__(name, fromlist="xmppd")
		except:
			self.DEBUG("Could not import filter module "+str(name),"error")
			break
		for f in vars(module).itervalues():
			if type(f)==ClassType and issubclass(f, Filter):
				try:
					filter = f(self)
					self.DEBUG("Filter "+ str(name) + " loaded","info")
					server.router_filters.append(filter)
				except:
					self.DEBUG("Could not load filter "+str(name),"error")
        	"""
		if issubclass(name,Filter):
			try:
				filter = name(self)
				self.DEBUG("Filter "+ str(name) + " loaded","info")
				server.router_filters.append(filter)
			except:
				self.DEBUG("Could not load filter "+str(name),"error")

    def presenceHandler(self,session,stanza):
#       filter out presences that should not influate our 'roster'
#       This is presences, that's addressed:
#        1) any other server
#        2) any user
	self.DEBUG("Presence handler called", "info")

        to=stanza['to']
        if to and ( to.getDomain() not in self._owner.servernames ):
		# or to.getNode() ):
                return

        typ=stanza.getType()
        jid=session.peer
        try: barejid,resource=jid.split('/')
        except: raise NodeProcessed # Closure of not yet bound session

	if typ=='subscribe':
	    # Presence Subscription
	    self.DEBUG("Presence subscription found from "+barejid+" to "+str(to), "warn")
	    # Modify sender's roster to reflect the subscription
	    try:
	    	self.server.rosterPlugIn.makeSubscription(barejid, str(to), session)
	        self.DEBUG('Roster of client '+barejid+' updated', 'ok')
		s = self.server.getsession(to)
		if s:
			stanza.setFrom(barejid)
			s.enqueue(stanza)

	    except:
		self.DEBUG('Could NOT update roster from ' + barejid, 'error')
	    raise NodeProcessed

	elif typ=='subscribed':
	    # Presence Subscription confirmation
	    self.DEBUG("Presence subscription confirmation found from "+barejid+" to "+str(to), "warn")

	    # Until here, all ok

	    # Modify sender's roster to reflect the subscription confirmation
	    try:
	    	self.server.rosterPlugIn.makeSubscription(str(to), barejid, session, subs='to')

	    	raise NodeProcessed  # TEMPORARILY

	        self.DEBUG('Roster of client '+str(to)+' updated', 'ok')
		# Route stanza
		s = self.server.getsession(to)
		if s:
			stanza.setFrom(barejid)
			#s.enqueue(stanza)
	    except:
		self.DEBUG('Could NOT route back presence subscription confirmation from ' + barejid, 'error')
	    raise NodeProcessed

        elif not typ or typ=='available':
            if not self._data.has_key(barejid): self._data[barejid]={}
            if not self._data[barejid].has_key(resource): self._data[barejid][resource]=Presence(frm=jid,typ=typ)
            bp=self._data[barejid][resource]

            try: priority=int(stanza.getTagData('priority'))
            except: priority=0
            bp.T.priority=`priority`
            self._owner.activatesession(session)

            show=stanza.getTag('show')
            if show: bp.T.show=show
            status=stanza.getTag('status')
            if status: bp.T.show=status
            bp.setTimestamp()
            self.update(barejid)
        elif typ=='unavailable' or typ=='error':
            if not self._data.has_key(barejid): raise NodeProcessed
            if self._data[barejid].has_key(resource): del self._data[barejid][resource]
            self.update(barejid)
            if not self._data[barejid]: del self._data[barejid]
            self._owner.deactivatesession(session.peer)
        elif typ=='probe':
            try:
                resources=[stanza.getTo().getResource()]
                if not resources[0]: resources=self._data[barejid].keys()
                flag=1
                for resource in resources:
                    p=Presence(to=stanza.getFrom(),frm=session.peer,node=self._data[barejid][resource])
                    if flag:
                        self._owner.Privacy(session.peer,p)
                        flag=None
                    session.enqueue(p)
            except KeyError: session.enqueue(Presence(to=stanza.getFrom(),frm=jid,typ='unavailable'))
        else: return
        raise NodeProcessed

    def update(self,barejid):
        pri=-1
        s=None
        for resource in self._data[barejid].keys():
            rpri=int(self._data[barejid][resource].getTagData('priority'))
            if rpri>pri: s=self._owner.getsession(barejid+'/'+resource)
        if s: self._owner.activatesession(s,barejid)
        else: self._owner.deactivatesession(barejid)

    def safeguard(self,session,stanza):
        if stanza.getNamespace() not in [NS_CLIENT,NS_SERVER,NS_COMPONENT_ACCEPT]: return # this is not XMPP stanza
        #if stanza.getNamespace() not in [NS_CLIENT,NS_SERVER]: return # this is not XMPP stanza

	# This is ugly and unorthodox, but it works: bypass security for handshakes
	if stanza.getNamespace()==NS_COMPONENT_ACCEPT and stanza.getName()=='handshake':
		self.DEBUG("safeguard: handshake received, calling handler", 'info')
		return

	if session._session_state<SESSION_AUTHED: # NOT AUTHED yet (stream's stuff already done)
            session.terminate_stream(STREAM_NOT_AUTHORIZED)
            raise NodeProcessed

        frm=stanza['from']
        to=stanza['to']
        if stanza.getNamespace()==NS_SERVER:
            if not frm or not to \
              or frm.getDomain()<>session.peer \
              or to.getDomain()<>session.ourname:
                session.terminate_stream(STREAM_IMPROPER_ADDRESSING)
                raise NodeProcessed
        else:
            if frm and frm<>session.peer:   # if the from address specified and differs
                if frm.getResource() or not frm.bareMatch(session.peer): # ...it can differ only while comparing inequally
                    session.terminate_stream(STREAM_INVALID_FROM)
                    raise NodeProcessed

            if session._session_state<SESSION_BOUND: # NOT BOUND yet (bind stuff already done)
                if stanza.getType()<>'error': session.send(Error(stanza,ERR_NOT_AUTHORIZED))
                raise NodeProcessed

            if name=='presence' and session._session_state<SESSION_OPENED:
                if stanza.getType()<>'error': session.send(Error(stanza,ERR_NOT_ALLOWED))
                raise NodeProcessed
            stanza.setFrom(session.peer)

    def routerHandler(self,session,stanza):
        """ XMPP-Core 9.1.1 rules """
        name=stanza.getName()
        self.DEBUG('Router handler called','info')

	# Safeguard for dispatcher mistakes - rererouting of presence stanzas
	if name == 'presence':
		self.presenceHandler(session, stanza)
	# We hope that presenceHandler will raise a NodeProcessed exception

	#Apply filters
	for f in self._owner.router_filters:
		if f.test(stanza):
			try:
				result = f.filter(session,stanza)
			except:
				self.DEBUG("Filter "+str(f)+ " failed",'warn')
				result=stanza

			if result == None:
				self.DEBUG("stanza treated by filter "+ str(f) +". NodeProcessed", "warn")
				raise NodeProcessed
			else:
				stanza = result

        to=stanza['to']

        if stanza.getNamespace()==NS_CLIENT and \
            (not to or to==session.ourname) and \
            stanza.props in ( [NS_AUTH], [NS_REGISTER], [NS_BIND], [NS_SESSION] ):
              return

        if not session.trusted:	self.safeguard(session,stanza)

        if not to: return # stanza.setTo(session.ourname)

        domain=to.getDomain()

        getsession=self._owner.getsession
        if domain in self._owner.servernames:
            node=to.getNode()
            if not node: return
            self._owner.Privacy(session.peer,stanza) # it will raise NodeProcessed if needed
            bareto=node+'@'+domain
	    dbg_msg='BARETO: ' + bareto
	    self.DEBUG(dbg_msg, 'info')
            resource=to.getResource()
# 1. If the JID is of the form <user@domain/resource> and an available resource matches the full JID,
#    the recipient's server MUST deliver the stanza to that resource.
            if resource:
                to=bareto+'/'+resource
                s=getsession(to)
                if s:
                    s.enqueue(stanza)
                    raise NodeProcessed
# 2. Else if the JID is of the form <user@domain> or <user@domain/resource> and the associated user account
#    does not exist, the recipient's server (a) SHOULD silently ignore the stanza (i.e., neither deliver it
#    nor return an error) if it is a presence stanza, (b) MUST return a <service-unavailable/> stanza error
#    to the sender if it is an IQ stanza, and (c) SHOULD return a <service-unavailable/> stanza error to the
#    sender if it is a message stanza.
            if not self._owner.AUTH.isuser(node,domain):
                if name in ['iq','message']:
                    if stanza.getType()<>'error': session.enqueue(Error(stanza,ERR_SERVICE_UNAVAILABLE))
                raise NodeProcessed
# 3. Else if the JID is of the form <user@domain/resource> and no available resource matches the full JID,
#    the recipient's server (a) SHOULD silently ignore the stanza (i.e., neither deliver it nor return an
#    error) if it is a presence stanza, (b) MUST return a <service-unavailable/> stanza error to the sender
#    if it is an IQ stanza, and (c) SHOULD treat the stanza as if it were addressed to <user@domain> if it
#    is a message stanza.
            if resource and name<>'message':
                if name=='iq' and stanza.getType()<>'error': session.enqueue(Error(stanza,ERR_SERVICE_UNAVAILABLE))
                raise NodeProcessed
# 4. Else if the JID is of the form <user@domain> and there is at least one available resource available
#    for the user, the recipient's server MUST follow these rules:
            s=getsession(bareto)
            if s:
#       1. For message stanzas, the server SHOULD deliver the stanza to the highest-priority available
#          resource (if the resource did not provide a value for the <priority/> element, the server SHOULD
#          consider it to have provided a value of zero). If two or more available resources have the same
#          priority, the server MAY use some other rule (e.g., most recent connect time, most recent
#          activity time, or highest availability as determined by some hierarchy of <show/> values)
#          to choose between them or MAY deliver the message to all such resources. However, the server
#          MUST NOT deliver the stanza to an available resource with a negative priority; if the only
#          available resource has a negative priority, the server SHOULD handle the message as if there
#          were no available resources (defined below). In addition, the server MUST NOT rewrite the 'to'
#          attribute (i.e., it MUST leave it as <user@domain> rather than change it to <user@domain/resource>).
                if name=='message':
                    s.enqueue(stanza)
                    raise NodeProcessed
#       2. For presence stanzas other than those of type "probe", the server MUST deliver the stanza to all
#          available resources; for presence probes, the server SHOULD reply based on the rules defined in
#          Presence Probes. In addition, the server MUST NOT rewrite the 'to' attribute (i.e., it MUST leave
#          it as <user@domain> rather than change it to <user@domain/resource>).
                elif name=='presence':
                    # all probes already processed so safely assuming "other" type
                    for resource in self._data[bareto].keys():
                        s=getsession(bareto+'/'+resource)
                        if s: s.enqueue(stanza)
                    raise NodeProcessed
#       3. For IQ stanzas, the server itself MUST reply on behalf of the user with either an IQ result or an
#          IQ error, and MUST NOT deliver the IQ stanza to any of the available resources. Specifically, if
#          the semantics of the qualifying namespace define a reply that the server can provide, the server
#          MUST reply to the stanza on behalf of the user; if not, the server MUST reply with a
#          <service-unavailable/> stanza error.
                return
# 5. Else if the JID is of the form <user@domain> and there are no available resources associated with
#    the user, how the stanza is handled depends on the stanza type:
            else:
#       1. For presence stanzas of type "subscribe", "subscribed", "unsubscribe", and "unsubscribed",
#          the server MUST maintain a record of the stanza and deliver the stanza at least once (i.e., when
#          the user next creates an available resource); in addition, the server MUST continue to deliver
#          presence stanzas of type "subscribe" until the user either approves or denies the subscription
#          request (see also Presence Subscriptions).
                if name=='presence':
                    if stanza.getType() in ["subscribe", "subscribed", "unsubscribe", "unsubscribed"]:
                        self._owner.DB.store(domain,node,stanza,id=stanza.getType().strip('un'))
#       2. For all other presence stanzas, the server SHOULD silently ignore the stanza by not storing it
#          for later delivery or replying to it on behalf of the user.
                    raise NodeProcessed
#       3. For message stanzas, the server MAY choose to store the stanza on behalf of the user and deliver
#          it when the user next becomes available, or forward the message to the user via some other means
#          (e.g., to the user's email account). However, if offline message storage or message forwarding
#          is not enabled, the server MUST return to the sender a <service-unavailable/> stanza error. (Note:
#          Offline message storage and message forwarding are not defined in XMPP, since they are strictly a
#          matter of implementation and service provisioning.)
                elif name=='message':
                    #self._owner.DB.store(domain,node,stanza)
                    if stanza.getType()<>'error': session.enqueue(Error(stanza,ERR_RECIPIENT_UNAVAILABLE))
                    raise NodeProcessed
#       4. For IQ stanzas, the server itself MUST reply on behalf of the user with either an IQ result or
#          an IQ error. Specifically, if the semantics of the qualifying namespace define a reply that the
#          server can provide, the server MUST reply to the stanza on behalf of the user; if not, the server
#          MUST reply with a <service-unavailable/> stanza error.
                return
        else:
            s=getsession(domain)
            if not s:
		self.DEBUG("S2S: Message to another server", 'info')
                s=self._owner.S2S(session.ourname,domain)
            s.enqueue(stanza)
            raise NodeProcessed

<commit_msg>packaging<commit_after># Distributed under the terms of GPL version 2 or any later
# Copyright (C) Alexey Nezhdanov 2004
# router, presence tracker and probes responder for xmppd.py

# $Id: router.py,v 1.5 2004/10/24 04:36:28 snakeru Exp $

from xmpp import *
from xmppd import *
from constants import *
from types import *
from filter import *

import os

class Router(PlugIn):
    """ The first entity that gets access to arrived stanza. """
    NS='presence'
    def plugin(self,server):
        self._data = {}
        # For the common folk
	#server.Dispatcher.RegisterNamespace(NS_COMPONENT_ACCEPT)

	server.Dispatcher.RegisterHandler('presence',self.presenceHandler)

	server.Dispatcher.RegisterNamespaceHandler(NS_CLIENT,self.routerHandler)
        server.Dispatcher.RegisterNamespaceHandler(NS_SERVER,self.routerHandler)

	#server.Dispatcher.RegisterNamespaceHandler(NS_COMPONENT_ACCEPT,self.routerHandler) # Mine
	#server.Dispatcher.RegisterHandler('handshake',component.Component.componentHandler,xmlns=NS_COMPONENT_ACCEPT)  # Mine
	#server.Dispatcher.RegisterHandler('message',self.routerHandler,xmlns=NS_COMPONENT_ACCEPT)  # Mine

	self.server = server

	#Init filters
	for name in server.router_filter_names:
		"""
		if name[-3:] == ".py":
			name = name[:-3]
			#name = name + ".py"
		#name = 'xmppd' + '.' + name

		try:
			module = __import__(name, fromlist="xmppd")
		except:
			self.DEBUG("Could not import filter module "+str(name),"error")
			break
		for f in vars(module).itervalues():
			if type(f)==ClassType and issubclass(f, Filter):
				try:
					filter = f(self)
					self.DEBUG("Filter "+ str(name) + " loaded","info")
					server.router_filters.append(filter)
				except:
					self.DEBUG("Could not load filter "+str(name),"error")
        	"""
		if issubclass(name,Filter):
			try:
				filter = name(self)
				self.DEBUG("Filter "+ str(name) + " loaded","info")
				server.router_filters.append(filter)
			except:
				self.DEBUG("Could not load filter "+str(name),"error")

    def presenceHandler(self,session,stanza):
#       filter out presences that should not influate our 'roster'
#       This is presences, that's addressed:
#        1) any other server
#        2) any user
	self.DEBUG("Presence handler called", "info")

        to=stanza['to']
        if to and ( to.getDomain() not in self._owner.servernames ):
		# or to.getNode() ):
                return

        typ=stanza.getType()
        jid=session.peer
        try: barejid,resource=jid.split('/')
        except: raise NodeProcessed # Closure of not yet bound session

	if typ=='subscribe':
	    # Presence Subscription
	    self.DEBUG("Presence subscription found from "+barejid+" to "+str(to), "warn")
	    # Modify sender's roster to reflect the subscription
	    try:
	    	self.server.rosterPlugIn.makeSubscription(barejid, str(to), session)
	        self.DEBUG('Roster of client '+barejid+' updated', 'ok')
		s = self.server.getsession(to)
		if s:
			stanza.setFrom(barejid)
			s.enqueue(stanza)

	    except:
		self.DEBUG('Could NOT update roster from ' + barejid, 'error')
	    raise NodeProcessed

	elif typ=='subscribed':
	    # Presence Subscription confirmation
	    self.DEBUG("Presence subscription confirmation found from "+barejid+" to "+str(to), "warn")

	    # Until here, all ok

	    # Modify sender's roster to reflect the subscription confirmation
	    try:
	    	#raise NodeProcessed  # TEMPORARILY

		# Route stanza
		s = self.server.getsession(to)
		if s:
			stanza.setFrom(barejid)
			s.enqueue(stanza)
	    		self.server.rosterPlugIn.makeSubscription(str(to), barejid, session, subs='to')
	        	self.DEBUG('Roster of client '+str(to)+' updated', 'ok')
	    except:
		self.DEBUG('Could NOT route back presence subscription confirmation from ' + barejid, 'error')
	    raise NodeProcessed

        elif not typ or typ=='available':
            if not self._data.has_key(barejid): self._data[barejid]={}
            if not self._data[barejid].has_key(resource): self._data[barejid][resource]=Presence(frm=jid,typ=typ)
            bp=self._data[barejid][resource]

            try: priority=int(stanza.getTagData('priority'))
            except: priority=0
            bp.T.priority=`priority`
            self._owner.activatesession(session)

            show=stanza.getTag('show')
            if show: bp.T.show=show
            status=stanza.getTag('status')
            if status: bp.T.show=status
            bp.setTimestamp()
            self.update(barejid)
        elif typ=='unavailable' or typ=='error':
            if not self._data.has_key(barejid): raise NodeProcessed
            if self._data[barejid].has_key(resource): del self._data[barejid][resource]
            self.update(barejid)
            if not self._data[barejid]: del self._data[barejid]
            self._owner.deactivatesession(session.peer)
        elif typ=='probe':
            try:
                resources=[stanza.getTo().getResource()]
                if not resources[0]: resources=self._data[barejid].keys()
                flag=1
                for resource in resources:
                    p=Presence(to=stanza.getFrom(),frm=session.peer,node=self._data[barejid][resource])
                    if flag:
                        self._owner.Privacy(session.peer,p)
                        flag=None
                    session.enqueue(p)
            except KeyError: session.enqueue(Presence(to=stanza.getFrom(),frm=jid,typ='unavailable'))
        else: return
        raise NodeProcessed

    def update(self,barejid):
        pri=-1
        s=None
        for resource in self._data[barejid].keys():
            rpri=int(self._data[barejid][resource].getTagData('priority'))
            if rpri>pri: s=self._owner.getsession(barejid+'/'+resource)
        if s: self._owner.activatesession(s,barejid)
        else: self._owner.deactivatesession(barejid)

    def safeguard(self,session,stanza):
        if stanza.getNamespace() not in [NS_CLIENT,NS_SERVER,NS_COMPONENT_ACCEPT]: return # this is not XMPP stanza
        #if stanza.getNamespace() not in [NS_CLIENT,NS_SERVER]: return # this is not XMPP stanza

	# This is ugly and unorthodox, but it works: bypass security for handshakes
	if stanza.getNamespace()==NS_COMPONENT_ACCEPT and stanza.getName()=='handshake':
		self.DEBUG("safeguard: handshake received, calling handler", 'info')
		return

	if session._session_state<SESSION_AUTHED: # NOT AUTHED yet (stream's stuff already done)
            session.terminate_stream(STREAM_NOT_AUTHORIZED)
            raise NodeProcessed

        frm=stanza['from']
        to=stanza['to']
        if stanza.getNamespace()==NS_SERVER:
            if not frm or not to \
              or frm.getDomain()<>session.peer \
              or to.getDomain()<>session.ourname:
                session.terminate_stream(STREAM_IMPROPER_ADDRESSING)
                raise NodeProcessed
        else:
            if frm and frm<>session.peer:   # if the from address specified and differs
                if frm.getResource() or not frm.bareMatch(session.peer): # ...it can differ only while comparing inequally
                    session.terminate_stream(STREAM_INVALID_FROM)
                    raise NodeProcessed

            if session._session_state<SESSION_BOUND: # NOT BOUND yet (bind stuff already done)
                if stanza.getType()<>'error': session.send(Error(stanza,ERR_NOT_AUTHORIZED))
                raise NodeProcessed

            if name=='presence' and session._session_state<SESSION_OPENED:
                if stanza.getType()<>'error': session.send(Error(stanza,ERR_NOT_ALLOWED))
                raise NodeProcessed
            stanza.setFrom(session.peer)

    def routerHandler(self,session,stanza):
        """ XMPP-Core 9.1.1 rules """
        name=stanza.getName()
        self.DEBUG('Router handler called','info')

	# Safeguard for dispatcher mistakes - rererouting of presence stanzas
	if name == 'presence':
		self.presenceHandler(session, stanza)
	# We hope that presenceHandler will raise a NodeProcessed exception

	#Apply filters
	for f in self._owner.router_filters:
		if f.test(stanza):
			try:
				result = f.filter(session,stanza)
			except:
				self.DEBUG("Filter "+str(f)+ " failed",'warn')
				result=stanza

			if result == None:
				self.DEBUG("stanza treated by filter "+ str(f) +". NodeProcessed", "warn")
				raise NodeProcessed
			else:
				stanza = result

        to=stanza['to']

        if stanza.getNamespace()==NS_CLIENT and \
            (not to or to==session.ourname) and \
            stanza.props in ( [NS_AUTH], [NS_REGISTER], [NS_BIND], [NS_SESSION] ):
              return

        if not session.trusted:	self.safeguard(session,stanza)

        if not to: return # stanza.setTo(session.ourname)

        domain=to.getDomain()

        getsession=self._owner.getsession
        if domain in self._owner.servernames:
            node=to.getNode()
            if not node: return
            self._owner.Privacy(session.peer,stanza) # it will raise NodeProcessed if needed
            bareto=node+'@'+domain
	    dbg_msg='BARETO: ' + bareto
	    self.DEBUG(dbg_msg, 'info')
            resource=to.getResource()
# 1. If the JID is of the form <user@domain/resource> and an available resource matches the full JID,
#    the recipient's server MUST deliver the stanza to that resource.
            if resource:
                to=bareto+'/'+resource
                s=getsession(to)
                if s:
                    s.enqueue(stanza)
                    raise NodeProcessed
# 2. Else if the JID is of the form <user@domain> or <user@domain/resource> and the associated user account
#    does not exist, the recipient's server (a) SHOULD silently ignore the stanza (i.e., neither deliver it
#    nor return an error) if it is a presence stanza, (b) MUST return a <service-unavailable/> stanza error
#    to the sender if it is an IQ stanza, and (c) SHOULD return a <service-unavailable/> stanza error to the
#    sender if it is a message stanza.
            if not self._owner.AUTH.isuser(node,domain):
                if name in ['iq','message']:
                    if stanza.getType()<>'error': session.enqueue(Error(stanza,ERR_SERVICE_UNAVAILABLE))
                raise NodeProcessed
# 3. Else if the JID is of the form <user@domain/resource> and no available resource matches the full JID,
#    the recipient's server (a) SHOULD silently ignore the stanza (i.e., neither deliver it nor return an
#    error) if it is a presence stanza, (b) MUST return a <service-unavailable/> stanza error to the sender
#    if it is an IQ stanza, and (c) SHOULD treat the stanza as if it were addressed to <user@domain> if it
#    is a message stanza.
            if resource and name<>'message':
                if name=='iq' and stanza.getType()<>'error': session.enqueue(Error(stanza,ERR_SERVICE_UNAVAILABLE))
                raise NodeProcessed
# 4. Else if the JID is of the form <user@domain> and there is at least one available resource available
#    for the user, the recipient's server MUST follow these rules:
            s=getsession(bareto)
            if s:
#       1. For message stanzas, the server SHOULD deliver the stanza to the highest-priority available
#          resource (if the resource did not provide a value for the <priority/> element, the server SHOULD
#          consider it to have provided a value of zero). If two or more available resources have the same
#          priority, the server MAY use some other rule (e.g., most recent connect time, most recent
#          activity time, or highest availability as determined by some hierarchy of <show/> values)
#          to choose between them or MAY deliver the message to all such resources. However, the server
#          MUST NOT deliver the stanza to an available resource with a negative priority; if the only
#          available resource has a negative priority, the server SHOULD handle the message as if there
#          were no available resources (defined below). In addition, the server MUST NOT rewrite the 'to'
#          attribute (i.e., it MUST leave it as <user@domain> rather than change it to <user@domain/resource>).
                if name=='message':
                    s.enqueue(stanza)
                    raise NodeProcessed
#       2. For presence stanzas other than those of type "probe", the server MUST deliver the stanza to all
#          available resources; for presence probes, the server SHOULD reply based on the rules defined in
#          Presence Probes. In addition, the server MUST NOT rewrite the 'to' attribute (i.e., it MUST leave
#          it as <user@domain> rather than change it to <user@domain/resource>).
                elif name=='presence':
                    # all probes already processed so safely assuming "other" type
                    for resource in self._data[bareto].keys():
                        s=getsession(bareto+'/'+resource)
                        if s: s.enqueue(stanza)
                    raise NodeProcessed
#       3. For IQ stanzas, the server itself MUST reply on behalf of the user with either an IQ result or an
#          IQ error, and MUST NOT deliver the IQ stanza to any of the available resources. Specifically, if
#          the semantics of the qualifying namespace define a reply that the server can provide, the server
#          MUST reply to the stanza on behalf of the user; if not, the server MUST reply with a
#          <service-unavailable/> stanza error.
                return
# 5. Else if the JID is of the form <user@domain> and there are no available resources associated with
#    the user, how the stanza is handled depends on the stanza type:
            else:
#       1. For presence stanzas of type "subscribe", "subscribed", "unsubscribe", and "unsubscribed",
#          the server MUST maintain a record of the stanza and deliver the stanza at least once (i.e., when
#          the user next creates an available resource); in addition, the server MUST continue to deliver
#          presence stanzas of type "subscribe" until the user either approves or denies the subscription
#          request (see also Presence Subscriptions).
                if name=='presence':
                    if stanza.getType() in ["subscribe", "subscribed", "unsubscribe", "unsubscribed"]:
                        self._owner.DB.store(domain,node,stanza,id=stanza.getType().strip('un'))
#       2. For all other presence stanzas, the server SHOULD silently ignore the stanza by not storing it
#          for later delivery or replying to it on behalf of the user.
                    raise NodeProcessed
#       3. For message stanzas, the server MAY choose to store the stanza on behalf of the user and deliver
#          it when the user next becomes available, or forward the message to the user via some other means
#          (e.g., to the user's email account). However, if offline message storage or message forwarding
#          is not enabled, the server MUST return to the sender a <service-unavailable/> stanza error. (Note:
#          Offline message storage and message forwarding are not defined in XMPP, since they are strictly a
#          matter of implementation and service provisioning.)
                elif name=='message':
                    #self._owner.DB.store(domain,node,stanza)
                    if stanza.getType()<>'error': session.enqueue(Error(stanza,ERR_RECIPIENT_UNAVAILABLE))
                    raise NodeProcessed
#       4. For IQ stanzas, the server itself MUST reply on behalf of the user with either an IQ result or
#          an IQ error. Specifically, if the semantics of the qualifying namespace define a reply that the
#          server can provide, the server MUST reply to the stanza on behalf of the user; if not, the server
#          MUST reply with a <service-unavailable/> stanza error.
                return
        else:
            s=getsession(domain)
            if not s:
		self.DEBUG("S2S: Message to another server", 'info')
                s=self._owner.S2S(session.ourname,domain)
            s.enqueue(stanza)
            raise NodeProcessed

<|endoftext|>


<commit_before>#!/usr/bin/env python

# Intro to Robotics - EE5900 - Spring 2017
#             Final Project
#          Philip (Team Lead)
#                 Ian
#                Akhil
#
# Revision: v1.3

# imports
import rospy
import smach
import smach_ros
import egg_detect
import joystick
import wall_follow
import discovery
import origin_detect
import start_goal
import waypoint_states
import map_save

def main():
    rospy.init_node('master_state_machine')

    # Create a SMACH state machine
    sm = smach.StateMachine(outcomes=['0'])

    # Open the container
    with sm:
        # Add states to the container
        smach.StateMachine.add('Start_Pause', joystick.JoystickButtonPause('/bluetooth_teleop/joy', 0), # X
                               transitions={'BUTTON_PRESSED':'ENABLE_DISCOVERY',
                                            'BUTTON_NEVER_PRESSED': '0'})

        smach.StateMachine.add('ENABLE_DISCOVERY', waypoint_states.EnableWaypointDiscovery(),
                               transitions={'WAYPOINTS_ENABLED':'ORIGIN_DETECT'})

        smach.StateMachine.add('ORIGIN_DETECT', origin_detect.jackal_origin_detect(),
                               transitions={'ORIGIN_DETECTED':'EXPLORATION',
                                            'ORIGIN_NOT_DETECTED':'0'},
                               remapping={'origin':'sm_origin'})

        smach.StateMachine.add('EXPLORATION', wall_follow.wallFollow(),
                               transitions={'EXPLORATION_COMPLETE':'START_GOAL',
                                            'EXPLORATION_INCOMPLETE':'0'})

        smach.StateMachine.add('START_GOAL', start_goal.jackal_start_goal(),
                               transitions={'GOAL_REACHED':'MAP_SAVER',
                                            'GOAL_NOT_REACHED':'0'},
                               remapping={'destination':'sm_origin'})

        smach.StateMachine.add('MAP_SAVER', map_save.bunny_map_save(),
                               transitions={'MAP_COMPLETE':'DISABLE_DISCOVERY',
                                            'MAP_INCOMPLETE':'0'})

        smach.StateMachine.add('DISABLE_DISCOVERY', waypoint_states.DisableWaypointDiscovery(),
                               transitions={'WAYPOINTS_DISABLED':'MARKER_DISPLAY_WAIT'})

        smach.StateMachine.add('MARKER_DISPLAY_WAIT', joystick.JoystickButtonPause('/bluetooth_teleop/joy', 0), # X
                               transitions={'BUTTON_PRESSED':'SELECT_BUNNY',
                                            'BUTTON_NEVER_PRESSED': '0'})

        smach.StateMachine.add('SELECT_BUNNY', waypoint_states.WaypointSelect(),
                               transitions={'WAYPOINT_SELECTED':'EGG_DETECT',
                                            'WAYPOINT_UNAVAILABLE':'0'},
                               remapping={'marker_dest':'marker_dest',
                                          'marker_ID':'marker_ID'})

        smach.StateMachine.add('BUNNY_NAV', waypoint_states.WaypointNav(),
                               transitions={'WAYPOINT_REACHED':'EGG_DETECT',
                                            'FAILED_WAYPOINT':'START_GOAL'},
                               remapping={'marker_dest':'marker_dest'})

        smach.StateMachine.add('EGG_DETECT', egg_detect.bunny_egg_detect(),
                               transitions={'EGGS_DETECTED':'START_GOAL',
                                            'EGGS_NOT_DETECTED':'START_GOAL'})

    # Execute SMACH plan
    outcome = sm.execute()

# standard boilerplate
if __name__ == '__main__':
    main()
<commit_msg>Fixing state orientation.<commit_after>#!/usr/bin/env python

# Intro to Robotics - EE5900 - Spring 2017
#             Final Project
#          Philip (Team Lead)
#                 Ian
#                Akhil
#
# Revision: v1.3

# imports
import rospy
import smach
import smach_ros
import egg_detect
import joystick
import wall_follow
import discovery
import origin_detect
import start_goal
import waypoint_states
import map_save

def main():
    rospy.init_node('master_state_machine')

    # Create a SMACH state machine
    sm = smach.StateMachine(outcomes=['0'])

    # Open the container
    with sm:
        # Add states to the container
        smach.StateMachine.add('Start_Pause', joystick.JoystickButtonPause('/bluetooth_teleop/joy', 0), # X
                               transitions={'BUTTON_PRESSED':'ENABLE_DISCOVERY',
                                            'BUTTON_NEVER_PRESSED': '0'})

        smach.StateMachine.add('ENABLE_DISCOVERY', waypoint_states.EnableWaypointDiscovery(),
                               transitions={'WAYPOINTS_ENABLED':'ORIGIN_DETECT'})

        smach.StateMachine.add('ORIGIN_DETECT', origin_detect.jackal_origin_detect(),
                               transitions={'ORIGIN_DETECTED':'EXPLORATION',
                                            'ORIGIN_NOT_DETECTED':'0'},
                               remapping={'origin':'sm_origin'})

        smach.StateMachine.add('EXPLORATION', wall_follow.wallFollow(),
                               transitions={'EXPLORATION_COMPLETE':'START_GOAL',
                                            'EXPLORATION_INCOMPLETE':'0'})

        smach.StateMachine.add('START_GOAL', start_goal.jackal_start_goal(),
                               transitions={'GOAL_REACHED':'MAP_SAVER',
                                            'GOAL_NOT_REACHED':'0'},
                               remapping={'destination':'sm_origin'})

        smach.StateMachine.add('MAP_SAVER', map_save.bunny_map_save(),
                               transitions={'MAP_COMPLETE':'DISABLE_DISCOVERY',
                                            'MAP_INCOMPLETE':'0'})

        smach.StateMachine.add('DISABLE_DISCOVERY', waypoint_states.DisableWaypointDiscovery(),
                               transitions={'WAYPOINTS_DISABLED':'MARKER_DISPLAY_WAIT'})

        smach.StateMachine.add('MARKER_DISPLAY_WAIT', joystick.JoystickButtonPause('/bluetooth_teleop/joy', 0), # X
                               transitions={'BUTTON_PRESSED':'SELECT_BUNNY',
                                            'BUTTON_NEVER_PRESSED': '0'})

        smach.StateMachine.add('SELECT_BUNNY', waypoint_states.WaypointSelect(),
                               transitions={'WAYPOINT_SELECTED':'BUNNY_NAV',
                                            'WAYPOINT_UNAVAILABLE':'0'},
                               remapping={'marker_dest':'marker_dest',
                                          'marker_ID':'marker_ID'})

        smach.StateMachine.add('BUNNY_NAV', waypoint_states.WaypointNav(),
                               transitions={'WAYPOINT_REACHED':'EGG_DETECT',
                                            'FAILED_WAYPOINT':'START_GOAL'},
                               remapping={'marker_dest':'marker_dest'})

        smach.StateMachine.add('EGG_DETECT', egg_detect.bunny_egg_detect(),
                               transitions={'EGGS_DETECTED':'START_GOAL',
                                            'EGGS_NOT_DETECTED':'START_GOAL'})

    # Execute SMACH plan
    outcome = sm.execute()

# standard boilerplate
if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>#!/usr/bin/env python

# Intro to Robotics - EE5900 - Spring 2017
#            Final Project
#          Philip (Team Lead)
#               Ian
#              Akhil
#
# Revision: v1.5

# imports
import rospy
import actionlib
import random
import sys
import time
import roslaunch
import os
import math
import cv_bridge
import cv2
import smach
import smach_ros
import numpy as np

from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist, Point, Quaternion
from nav_msgs.msg import MapMetaData, Odometry
from move_base_msgs.msg import MoveBaseGoal, MoveBaseAction

# wall following routine
class wallFollow(smach.State):
    # init state machine
    def __init__(self):
        smach.State.__init__(self, outcomes=['EXPLORATION_COMPLETE','EXPLORATION_INCOMPLETE'])

    # define executation stage
    def execute(self, userdata):
        # init status variable
        self.done = 0
        self.i = 0
        self.j = 0
        self.k = 0

        # define speed and turning coefficients
        self.turnCoef = [(x ** 2 - 8750) / 7500000.0 for x in xrange(-90, 0)] + [(-x ** 2 + 8750) / 7500000.0 for x in xrange(0, 91)]
        self.speedCoef = [(-x ** 2 + 8750) / 7500000.0 for x in xrange(-90,91)]

        # define random spin time
        # self.rand = random.randrange(30, 50)
        self.rand = random.randrange(90, 130)
        self.spin_dir = random.sample(['-0.36', '0.36'],1)

        # define publisher to Twist
        self.pub = rospy.Publisher("/cmd_vel", Twist, queue_size=10)

        # subscribe to laserscan
        self.lidar = rospy.Subscriber("/scan", LaserScan, self.function)

        rate = rospy.Rate(1)

        while not rospy.is_shutdown():
            # completed exploration?
            if self.done:
                print('EXPLORATION_COMPLETE')
                return 'EXPLORATION_COMPLETE'

            # else:
            #     return 'EXPLORATION_INCOMPLETE'

            rate.sleep()

    # function to switch between states
    def function(self, data):
        self.i += 1
        self.rand = random.randrange(90, 130)
        if self.i > self.rand:
            self.jackalSpin(data)
            # self.Callback(data)
        else:
            self.Callback(data)

        if self.k >= 1:
            self.lidar.unregister()
            print ('unregistered')
            self.done = 1

    # spin jackal 360 deg
    def jackalSpin(self, data):
        # publish spin msg
        self.cmd.linear.x = 0
        self.cmd.angular.z = (float(self.spin_dir[0]))
        # self.cmd.angular.z = 0.35
        self.pub.publish(self.cmd)

        # increment j on every iteration
        self.j += 1

        # reset 'i' to get out of spin
        if self.j > 170:
            self.i = 0
            self.k += 1

    def Callback(self, data):
        # define initial varibles
        turnVal = 0
        speedVal = 0
        # define scan zones
        right_zone = data.ranges[0:65]
        front_zone = data.ranges[65:115]
        left_zone = data.ranges[115:180]

        front_zone_avg = sum(front_zone) / len(front_zone)
        right_zone_avg = sum(right_zone) / len(right_zone)
        left_zone_avg  = sum(left_zone) / len(left_zone)

        # If average is really REALLY close, might want to back up instead
        if front_zone_avg < 1 or min(front_zone) < 0.6:
            speedVal = -0.1
            if left_zone_avg > right_zone_avg and min(left_zone) > min(right_zone):
                # rospy.loginfo("Backing up to the left...")
                turnVal = 0.4
            else:
                # rospy.loginfo("Backing up to the right...")
                turnVal = -0.4

        else:
            # rospy.loginfo("Normal hallway")
            for p in range(0, 180):
                # Inf range return means its over 10m from the LIDAR
                if math.isinf(data.ranges[p]) or math.isnan(data.ranges[p]):
                    speedVal = speedVal + (self.speedCoef[p] * 10)
                    # Don't account long ranges into turn calcs
                else:
                    speedVal = speedVal + (self.speedCoef[p] * data.ranges[p])

                    # Turn away from walls
                    turnVal = turnVal + (self.turnCoef[p] * data.ranges[p])

            speedVal = min(speedVal * 1.2, 0.35) # sets max speed
            turnVal = turnVal * 1

            if front_zone_avg < 2.2:
                turnVal = turnVal * 2.0
                speedVal = speedVal * 1.1

        # rospy.loginfo('left_zone_avg:'+str(left_zone_avg))
        # rospy.loginfo('right_zone_avg:'+str(right_zone_avg))
        # rospy.loginfo('SpeedVal: ' + str(speedVal))
        # rospy.loginfo('turnVal: ' + str(turnVal))

        self.cmd = Twist()
        self.cmd.linear.x = speedVal
        self.cmd.angular.z = turnVal

        # rospy.loginfo(self.cmd)
        self.pub.publish(self.cmd)
        self.j = 0
<commit_msg>super hot fix that shouldn't have to happen............................................<commit_after>#!/usr/bin/env python

# Intro to Robotics - EE5900 - Spring 2017
#            Final Project
#          Philip (Team Lead)
#               Ian
#              Akhil
#
# Revision: v1.5

# imports
import rospy
import actionlib
import random
import sys
import time
import roslaunch
import os
import math
import cv_bridge
import cv2
import smach
import smach_ros
import numpy as np

from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist, Point, Quaternion
from nav_msgs.msg import MapMetaData, Odometry
from move_base_msgs.msg import MoveBaseGoal, MoveBaseAction

# wall following routine
class wallFollow(smach.State):
    # init state machine
    def __init__(self):
        smach.State.__init__(self, outcomes=['EXPLORATION_COMPLETE','EXPLORATION_INCOMPLETE'])

    # define executation stage
    def execute(self, userdata):
        # init status variable
        self.done = 0
        self.i = 0
        self.j = 0
        self.k = 0

        # define speed and turning coefficients
        self.turnCoef = [(x ** 2 - 8750) / 7500000.0 for x in xrange(-90, 0)] + [(-x ** 2 + 8750) / 7500000.0 for x in xrange(0, 91)]
        self.speedCoef = [(-x ** 2 + 8750) / 7500000.0 for x in xrange(-90,91)]

        # define random spin time
        # self.rand = random.randrange(30, 50)
        self.rand = random.randrange(90, 130)
        self.spin_dir = random.sample(['-0.36', '0.36'],1)

        # define publisher to Twist
        self.pub = rospy.Publisher("/cmd_vel", Twist, queue_size=10)

        # subscribe to laserscan
        self.lidar = rospy.Subscriber("/scan", LaserScan, self.function)

        rate = rospy.Rate(1)

        while not rospy.is_shutdown():
            # completed exploration?
            if self.done:
                print('EXPLORATION_COMPLETE')
                return 'EXPLORATION_COMPLETE'

            # else:
            #     return 'EXPLORATION_INCOMPLETE'

            rate.sleep()

    # function to switch between states
    def function(self, data):
        self.i += 1
        self.rand = random.randrange(90, 130)
        if self.i > self.rand:
            self.jackalSpin(data)
            # self.Callback(data)
        else:
            self.Callback(data)

        if self.k >= 6:
            self.lidar.unregister()
            print ('unregistered')
            self.done = 1

    # spin jackal 360 deg
    def jackalSpin(self, data):
        # publish spin msg
        self.cmd.linear.x = 0
        self.cmd.angular.z = (float(self.spin_dir[0]))
        # self.cmd.angular.z = 0.35
        self.pub.publish(self.cmd)

        # increment j on every iteration
        self.j += 1

        # reset 'i' to get out of spin
        if self.j > 170:
            self.i = 0
            self.k += 1

    def Callback(self, data):
        # define initial varibles
        turnVal = 0
        speedVal = 0
        # define scan zones
        right_zone = data.ranges[0:65]
        front_zone = data.ranges[65:115]
        left_zone = data.ranges[115:180]

        front_zone_avg = sum(front_zone) / len(front_zone)
        right_zone_avg = sum(right_zone) / len(right_zone)
        left_zone_avg  = sum(left_zone) / len(left_zone)

        # If average is really REALLY close, might want to back up instead
        if front_zone_avg < 1 or min(front_zone) < 0.6:
            speedVal = -0.1
            if left_zone_avg > right_zone_avg and min(left_zone) > min(right_zone):
                # rospy.loginfo("Backing up to the left...")
                turnVal = 0.4
            else:
                # rospy.loginfo("Backing up to the right...")
                turnVal = -0.4

        else:
            # rospy.loginfo("Normal hallway")
            for p in range(0, 180):
                # Inf range return means its over 10m from the LIDAR
                if math.isinf(data.ranges[p]) or math.isnan(data.ranges[p]):
                    speedVal = speedVal + (self.speedCoef[p] * 10)
                    # Don't account long ranges into turn calcs
                else:
                    speedVal = speedVal + (self.speedCoef[p] * data.ranges[p])

                    # Turn away from walls
                    turnVal = turnVal + (self.turnCoef[p] * data.ranges[p])

            speedVal = min(speedVal * 1.2, 0.35) # sets max speed
            turnVal = turnVal * 1

            if front_zone_avg < 2.2:
                turnVal = turnVal * 2.0
                speedVal = speedVal * 1.1

        # rospy.loginfo('left_zone_avg:'+str(left_zone_avg))
        # rospy.loginfo('right_zone_avg:'+str(right_zone_avg))
        # rospy.loginfo('SpeedVal: ' + str(speedVal))
        # rospy.loginfo('turnVal: ' + str(turnVal))

        self.cmd = Twist()
        self.cmd.linear.x = speedVal
        self.cmd.angular.z = turnVal

        # rospy.loginfo(self.cmd)
        self.pub.publish(self.cmd)
        self.j = 0
<|endoftext|>


<commit_before>import logging
from typing import Any, Optional, Final, List, Dict, Union

from PyQt5 import QtCore

from .choices import GeometryChoices
from .preset import GeometryPreset
from ..component import Component

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

class Geometry(QtCore.QAbstractItemModel, Component):
    """Describes current and possible geometries

    Geometry settings are stored as "presets", this class also presents a flat Qt item model of them. The first preset
    is special: it is always the *current* one, therefore it cannot be deleted, nor its name changed.

    The current values of the geometry settings are stored in config['geometry'], to be accessed there by the rest of
    the program. Additionally, there is an entry in config['geometry']['presets'] for each preset. Activating a preset
    simply means the copying of the values of the desired preset to the corresponding entries in config['geometry'].
    Changing geometry parameters does not update the stored preset values.

    """
    CURRENTPRESETNAME: Final[str] = '-- Current settings --'
    choices: GeometryChoices
    currentPresetChanged = QtCore.pyqtSignal(str, object)
    presets: Dict[str, GeometryPreset]
    currentpreset: GeometryPreset

    def __init__(self, **kwargs):
        self.presets = {}
        super().__init__(**kwargs)  # this implies self.loadFromConfig()
        if 'presets' not in self.config['geometry']:
            self.config['geometry']['presets'] = {}
        self.choices = GeometryChoices(config=self.config)

    def rowCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return len(self.presets)+1

    def columnCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return 1

    def parent(self, child: QtCore.QModelIndex) -> QtCore.QModelIndex:
        return QtCore.QModelIndex()

    def index(self, row: int, column: int, parent: QtCore.QModelIndex = ...) -> QtCore.QModelIndex:
        return self.createIndex(row, column, None)

    def flags(self, index: QtCore.QModelIndex) -> QtCore.Qt.ItemFlag:
        if index.row() == 0:
            # the first preset is special: it is the current preset. It cannot be renamed.
            return QtCore.Qt.ItemNeverHasChildren | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
        else:
            return QtCore.Qt.ItemNeverHasChildren | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | \
                   QtCore.Qt.ItemIsSelectable

    def data(self, index: QtCore.QModelIndex, role: int = ...) -> Any:
        if (role == QtCore.Qt.DisplayRole) or (role == QtCore.Qt.EditRole):
            if index.row() == 0:
                return self.CURRENTPRESETNAME
            else:
                return sorted(self.presets.keys())[index.row()-1]
        return None

    def setData(self, index: QtCore.QModelIndex, value: Any, role: int = ...) -> bool:
        if index.row() == 0:
            raise RuntimeError('Cannot change the name of the current settings.')
        oldname = sorted(self.presets.keys())[index.row()-1]
        self.saveToConfig()
        return self.renamePreset(oldname, value)

    def renamePreset(self, oldname: str, newname: str) -> bool:
        if oldname == newname:
            return False
        if newname in self.presetNames():
            raise ValueError(f'Cannot rename preset "{oldname}" to an already existing name "{newname}"')
        self.beginResetModel()
        data = self.presets[oldname]
        self.presets[str(newname)] = data
        del self.presets[oldname]
        self.endResetModel()
        self.saveToConfig()
        return True

    def savePreset(self, name: str):
        """Save the current settings under a preset name"""
        self.presets[name] = self.currentpreset
        logger.info(f'Saved current geometry preset under name {name}')

    def addPreset(self, name: str, preset: Optional[GeometryPreset] = None) -> str:
        if name in self.presetNames():
            i = 1
            while f'{name}{i}' in self.presets:
                i += 1
            name = f'{name}{i}'
        if preset is None:
            preset = GeometryPreset(self.config)
        self.beginResetModel()
        self.presets[name] = preset
        self.endResetModel()
        logger.info(f'Added a new geometry preset {name}')
        self.saveToConfig()
        return name

    def removePreset(self, name: str):
        if name == self.CURRENTPRESETNAME:
            raise ValueError('Cannot remove current preset')
        if name not in self.presets:
            raise ValueError(f'Cannot remove nonexistent preset {name}')
        self.beginResetModel()
        del self.presets[name]
        self.endResetModel()
        self.saveToConfig()
        logger.info(f'Removed geometry preset {name}.')

    def onConfigChanged(self, path, value):
        if path[:2] == ('geometry', 'presets'):
            self.beginResetModel()
            self.endResetModel()
        logger.debug(path)

    def onCurrentPresetChanged(self, propertyname: str, newvalue: str):
        self.saveToConfig()
        self.currentPresetChanged.emit(propertyname, newvalue)

    def loadFromConfig(self):
        """Load presets and current state from the config"""
        if 'presets' not in self.config['geometry']:
            # initialize 'presets' dictionary if it does not exist.
            self.config['geometry']['presets'] = {}

        # construct the current preset from geometrical data
        currpres = GeometryPreset(
            self.config,
            self.config['geometry'].setdefault('l1_elements', []),
            self.config['geometry'].setdefault('l2_elements', []),
            self.config['geometry'].setdefault('pinhole_1', 0.0),
            self.config['geometry'].setdefault('pinhole_2', 0.0),
            self.config['geometry'].setdefault('pinhole_3', 0.0),
            self.config['geometry'].setdefault('flightpipes', []),
            self.config['geometry'].setdefault('beamstop', 0.0),
            (self.config['geometry'].setdefault('dist_sample_det', 0.0),
             self.config['geometry'].setdefault('dist_sample_det.err', 0.0)),
            (self.config['geometry'].setdefault('beamposx', 0.0),
             self.config['geometry'].setdefault('beamposx.err', 0.0)),
            (self.config['geometry'].setdefault('beamposy', 0.0),
             self.config['geometry'].setdefault('beamposy.err', 0.0)),
            self.config['geometry'].setdefault('mask', ''),
            self.config['geometry'].setdefault('description', 'Dummy preset'),
        )

        if hasattr(self, 'currentpreset'):
            # if we already have a current preset, disconnect from it and safely delete.
            self.currentpreset.changed.disconnect(self.onCurrentPresetChanged)
            self.currentpreset.deleteLater()
            del self.currentpreset
        # delete the presets list
        self.presets = {}
        # load the presets
        for name in self.config['geometry']['presets']:
            self.presets[name] = GeometryPreset.fromDict(self.config, self.config['geometry']['presets'][name])
            # check if this is the current preset
            if (self.presets[name] == currpres) and (not hasattr(self, 'currentpreset')):
                self.currentpreset = self.presets[name]
                self.currentpreset.changed.connect(self.onCurrentPresetChanged)
        if not hasattr(self, 'currentpreset'):
            # none of the presets was equal to the current settings
            self.currentpreset = currpres
            self.addPreset('Current', currpres)
            self.currentpreset.changed.connect(self.onCurrentPresetChanged)

    def saveToConfig(self):
        """Save the geometry values of the current preset to the config"""
        self.config['geometry']['l1_elements'] = tuple(self.currentpreset.l1_elements)
        self.config['geometry']['l2_elements'] = tuple(self.currentpreset.l2_elements)
        self.config['geometry']['flightpipes'] = tuple(self.currentpreset.flightpipes)
        self.config['geometry']['pinhole_1'] = self.currentpreset.pinhole1
        self.config['geometry']['pinhole_2'] = self.currentpreset.pinhole2
        self.config['geometry']['pinhole_3'] = self.currentpreset.pinhole3
        self.config['geometry']['beamstop'] = self.currentpreset.beamstop
        self.config['geometry']['dist_sample_det'] = self.currentpreset.sd[0]
        self.config['geometry']['dist_sample_det.err'] = self.currentpreset.sd[1]
        self.config['geometry']['beamposx'] = self.currentpreset.beamposx[0]
        self.config['geometry']['beamposx.err'] = self.currentpreset.beamposx[1]
        self.config['geometry']['beamposy'] = self.currentpreset.beamposy[0]
        self.config['geometry']['beamposy.err'] = self.currentpreset.beamposy[1]
        self.config['geometry']['mask'] = self.currentpreset.mask
        self.config['geometry']['description'] = self.currentpreset.description
        for preset in self.presets:
            self.config['geometry']['presets'][preset] = self.presets[preset].toDict()
        for name in self.config['geometry']['presets']:
            if name not in self.presets:
                del self.config['geometry']['presets']

    def setCurrentPreset(self, preset: Union[GeometryPreset, str]):
        if isinstance(preset, str):
            preset = self.presets[preset]
        assert isinstance(preset, GeometryPreset)
        self.currentpreset.changed.disconnect(self.onCurrentPresetChanged)
        self.currentpreset = preset
        self.currentpreset.changed.connect(self.onCurrentPresetChanged)
        dic = self.currentpreset.toDict()
        for key in dic:
            self.currentPresetChanged.emit(key, getattr(self.currentpreset, key))
        self.saveToConfig()

    def presetNames(self) -> List[str]:
        return [self.CURRENTPRESETNAME] + list(self.config['geometry']['presets'].keys())
<commit_msg>Debugging geometry presets #3<commit_after>import logging
from typing import Any, Optional, Final, List, Dict, Union

from PyQt5 import QtCore

from .choices import GeometryChoices
from .preset import GeometryPreset
from ..component import Component

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

class Geometry(QtCore.QAbstractItemModel, Component):
    """Describes current and possible geometries

    Geometry settings are stored as "presets", this class also presents a flat Qt item model of them. The first preset
    is special: it is always the *current* one, therefore it cannot be deleted, nor its name changed.

    The current values of the geometry settings are stored in config['geometry'], to be accessed there by the rest of
    the program. Additionally, there is an entry in config['geometry']['presets'] for each preset. Activating a preset
    simply means the copying of the values of the desired preset to the corresponding entries in config['geometry'].
    Changing geometry parameters does not update the stored preset values.

    """
    CURRENTPRESETNAME: Final[str] = '-- Current settings --'
    choices: GeometryChoices
    currentPresetChanged = QtCore.pyqtSignal(str, object)
    presets: Dict[str, GeometryPreset]
    currentpreset: GeometryPreset

    def __init__(self, **kwargs):
        self.presets = {}
        super().__init__(**kwargs)  # this implies self.loadFromConfig()
        if 'presets' not in self.config['geometry']:
            self.config['geometry']['presets'] = {}
        self.choices = GeometryChoices(config=self.config)

    def rowCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return len(self.presets)+1

    def columnCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return 1

    def parent(self, child: QtCore.QModelIndex) -> QtCore.QModelIndex:
        return QtCore.QModelIndex()

    def index(self, row: int, column: int, parent: QtCore.QModelIndex = ...) -> QtCore.QModelIndex:
        return self.createIndex(row, column, None)

    def flags(self, index: QtCore.QModelIndex) -> QtCore.Qt.ItemFlag:
        if index.row() == 0:
            # the first preset is special: it is the current preset. It cannot be renamed.
            return QtCore.Qt.ItemNeverHasChildren | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
        else:
            return QtCore.Qt.ItemNeverHasChildren | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | \
                   QtCore.Qt.ItemIsSelectable

    def data(self, index: QtCore.QModelIndex, role: int = ...) -> Any:
        if (role == QtCore.Qt.DisplayRole) or (role == QtCore.Qt.EditRole):
            if index.row() == 0:
                return self.CURRENTPRESETNAME
            else:
                return sorted(self.presets.keys())[index.row()-1]
        return None

    def setData(self, index: QtCore.QModelIndex, value: Any, role: int = ...) -> bool:
        if index.row() == 0:
            raise RuntimeError('Cannot change the name of the current settings.')
        oldname = sorted(self.presets.keys())[index.row()-1]
        self.saveToConfig()
        return self.renamePreset(oldname, value)

    def renamePreset(self, oldname: str, newname: str) -> bool:
        if oldname == newname:
            return False
        if newname in self.presetNames():
            raise ValueError(f'Cannot rename preset "{oldname}" to an already existing name "{newname}"')
        self.beginResetModel()
        data = self.presets[oldname]
        self.presets[str(newname)] = data
        del self.presets[oldname]
        self.endResetModel()
        self.saveToConfig()
        return True

    def savePreset(self, name: str):
        """Save the current settings under a preset name"""
        self.presets[name] = self.currentpreset
        logger.info(f'Saved current geometry preset under name {name}')

    def addPreset(self, name: str, preset: Optional[GeometryPreset] = None) -> str:
        if name in self.presetNames():
            i = 1
            while f'{name}{i}' in self.presets:
                i += 1
            name = f'{name}{i}'
        if preset is None:
            preset = GeometryPreset(self.config)
        self.beginResetModel()
        self.presets[name] = preset
        self.endResetModel()
        logger.info(f'Added a new geometry preset {name}')
        self.saveToConfig()
        return name

    def removePreset(self, name: str):
        if name == self.CURRENTPRESETNAME:
            raise ValueError('Cannot remove current preset')
        if name not in self.presets:
            raise ValueError(f'Cannot remove nonexistent preset {name}')
        self.beginResetModel()
        del self.presets[name]
        self.endResetModel()
        self.saveToConfig()
        logger.info(f'Removed geometry preset {name}.')

    def onConfigChanged(self, path, value):
        if path[:2] == ('geometry', 'presets'):
            self.beginResetModel()
            self.endResetModel()
        logger.debug(path)

    def onCurrentPresetChanged(self, propertyname: str, newvalue: str):
        self.saveToConfig()
        self.currentPresetChanged.emit(propertyname, newvalue)

    def loadFromConfig(self):
        """Load presets and current state from the config"""
        if 'presets' not in self.config['geometry']:
            # initialize 'presets' dictionary if it does not exist.
            self.config['geometry']['presets'] = {}

        # construct the current preset from geometrical data
        currpres = GeometryPreset(
            self.config,
            self.config['geometry'].setdefault('l1_elements', []),
            self.config['geometry'].setdefault('l2_elements', []),
            self.config['geometry'].setdefault('pinhole_1', 0.0),
            self.config['geometry'].setdefault('pinhole_2', 0.0),
            self.config['geometry'].setdefault('pinhole_3', 0.0),
            self.config['geometry'].setdefault('flightpipes', []),
            self.config['geometry'].setdefault('beamstop', 0.0),
            (self.config['geometry'].setdefault('dist_sample_det', 0.0),
             self.config['geometry'].setdefault('dist_sample_det.err', 0.0)),
            (self.config['geometry'].setdefault('beamposx', 0.0),
             self.config['geometry'].setdefault('beamposx.err', 0.0)),
            (self.config['geometry'].setdefault('beamposy', 0.0),
             self.config['geometry'].setdefault('beamposy.err', 0.0)),
            self.config['geometry'].setdefault('mask', ''),
            self.config['geometry'].setdefault('description', 'Dummy preset'),
        )

        if hasattr(self, 'currentpreset'):
            # if we already have a current preset, disconnect from it and safely delete.
            self.currentpreset.changed.disconnect(self.onCurrentPresetChanged)
            self.currentpreset.deleteLater()
            del self.currentpreset
        # delete the presets list
        self.presets = {}
        # load the presets
        for name in self.config['geometry']['presets']:
            self.presets[name] = GeometryPreset.fromDict(self.config, self.config['geometry']['presets'][name])
            # check if this is the current preset
            if (self.presets[name] == currpres) and (not hasattr(self, 'currentpreset')):
                self.currentpreset = self.presets[name]
                self.currentpreset.changed.connect(self.onCurrentPresetChanged)
        if not hasattr(self, 'currentpreset'):
            # none of the presets was equal to the current settings
            self.currentpreset = currpres
            self.addPreset('Current', currpres)
            self.currentpreset.changed.connect(self.onCurrentPresetChanged)

    def saveToConfig(self):
        """Save the geometry values of the current preset to the config"""
        self.config['geometry']['l1_elements'] = tuple(self.currentpreset.l1_elements)
        self.config['geometry']['l2_elements'] = tuple(self.currentpreset.l2_elements)
        self.config['geometry']['flightpipes'] = tuple(self.currentpreset.flightpipes)
        self.config['geometry']['pinhole_1'] = self.currentpreset.pinhole1
        self.config['geometry']['pinhole_2'] = self.currentpreset.pinhole2
        self.config['geometry']['pinhole_3'] = self.currentpreset.pinhole3
        self.config['geometry']['beamstop'] = self.currentpreset.beamstop
        self.config['geometry']['dist_sample_det'] = self.currentpreset.sd[0]
        self.config['geometry']['dist_sample_det.err'] = self.currentpreset.sd[1]
        self.config['geometry']['beamposx'] = self.currentpreset.beamposx[0]
        self.config['geometry']['beamposx.err'] = self.currentpreset.beamposx[1]
        self.config['geometry']['beamposy'] = self.currentpreset.beamposy[0]
        self.config['geometry']['beamposy.err'] = self.currentpreset.beamposy[1]
        self.config['geometry']['mask'] = self.currentpreset.mask
        self.config['geometry']['description'] = self.currentpreset.description
        for preset in self.presets:
            self.config['geometry']['presets'][preset] = self.presets[preset].toDict()
        for name in self.config['geometry']['presets']:
            if name not in self.presets:
                del self.config['geometry']['presets'][name]

    def setCurrentPreset(self, preset: Union[GeometryPreset, str]):
        if isinstance(preset, str):
            preset = self.presets[preset]
        assert isinstance(preset, GeometryPreset)
        self.currentpreset.changed.disconnect(self.onCurrentPresetChanged)
        self.currentpreset = preset
        self.currentpreset.changed.connect(self.onCurrentPresetChanged)
        dic = self.currentpreset.toDict()
        for key in dic:
            self.currentPresetChanged.emit(key, getattr(self.currentpreset, key))
        self.saveToConfig()

    def presetNames(self) -> List[str]:
        return [self.CURRENTPRESETNAME] + list(self.config['geometry']['presets'].keys())
<|endoftext|>


<commit_before>"""
Base Class for Parsers - ensures that make shift parsers implement the
same standard methods as NLTK parsers.
"""

class BaseParser(object):

    def __init__(self):
        pass

    def parse(self, sentence):
        """Method to parse a sentence - required to be implemented """
        raise NotImplementedError("Parse method has not been implemented")<commit_msg>add parse boundary iterator to parser base<commit_after>"""
Base Class for Parsers - ensures that make shift parsers implement the
same standard methods as NLTK parsers.
"""

class BaseParser(object):

    def __init__(self):
        pass

    def parse(self, sentence):
        """Method to parse a sentence - required to be implemented """
        raise NotImplementedError("Parse method has not been implemented")

    def parseBoundaries(self, sentence):
        """Method to parse a sentence and return the boundaries of a given
           tag type - such as relationalPhrases """

        pass<|endoftext|>