<commit_before>import json
import yaml
import rospy
import sys
import logging
# [BEGIN] ROS CODE
#from vigir_bs_msgs.srv import *
#from vigir_bs_msgs.msg import *
# [END] ROS CODE

logging.basicConfig(stream=sys.stderr, level=logging.INFO)

# Fake implementation for testing
class StateInstantiation():
    def __init__(self):
        self.state_path = ""
        self.state_class = ""
        self.outcomes = []
        self.transitions = []
        self.autonomy = ""
        self.parameter_name = []
        self.parameter_value = []
    def __str__(self):
        lines = []
        if len(self.state_path) > 0:
            lines.append("state_path = {0}".format(self.state_path))
        if len(self.state_class) > 0:
            lines.append("state_class = {0}".format(self.state_class))
        if len(self.outcomes) > 0:
            lines.append("outcomes = {0}".format(self.outcomes))
        if len(self.transitions) > 0:
            lines.append("transitions = {0}".format(self.transitions))
        if len(self.autonomy) > 0:
            lines.append("autonomy = {0}".format(self.autonomy))
        if len(self.parameter_name) > 0:
            lines.append("parameter_name = {0}".format(self.parameter_name))
        if len(self.parameter_value) > 0:
            lines.append("parameter_value = {0}".format(self.parameter_value))
        return "\n".join(lines)

def get_transitions(name, nodes):
    """
    Deduce the transition needed to go to the next states.

    @param name The name of the state to transitions away from
    @param nodes The dictionary containing all of the node information.
    @returns A dictionary, that maps next state to indices of the input
            variables that need to be true to go to that next state.

             For example:
        {
            'State1': [1, 2], # input var 1 & 2 need to be true
            'State2': [1, 3, 4],
            ...
        }
    """
    node_info = nodes[name]
    next_states = [str(x) for x in node_info['trans']]
    next_states = list(set(next_states) - set([name])) # remove self loop

    if len(next_states) == 0:
        raise Exception("This state has no exit!")
    if len(next_states) > 1:
        print("[INFO] Multiple next states for {0}".format(name))

    transitions = {}
    for next_state in next_states:
        transitions[next_state] = [idx for (idx, v) in
                                   enumerate(nodes[next_state]['in_vars'])
                                   if v == 1]
    return transitions

def reformat(nodes, n_in_vars):
    """
    Reformat automaton dictionary so that input/output variables are
    separated.  For example, if before the dictionary for a node was
    {
        ...
        "states" : [a, b, c, d, e, f, g] # a-d are input, e-g are output
        ...
    }
    where a-d corresponds to input variables and e-g corresponds to output
    variables, now it looks like
    {
        ...
        "in_vars" : [a, b, c, d]
        "out_vars" : [e, f, g]
        ...
    }
    """
    for name, dic in nodes.items():
        in_vars = dic['state'][:n_in_vars]
        out_vars = dic['state'][n_in_vars:]
        dic['in_vars'] = in_vars
        dic['out_vars'] = out_vars
        dic.pop('state', None)
    return nodes

def add_subsubstate(name, var_config, SIs):
    """
    Add a substate to the [name] concurrent substate.
    @param name       The name of the substate.
    @param var_config The configuration for this variable.
    @param SIs        The global list of state instantiations.
    """
    sub_si = StateInstantiation()
    sub_si.state_path = "/State{0}/State{1}".format(name, name)
    sub_si.state_class = var_config['class']
    # I'm not sure how concurrent things are going to work, but for
    # not just don't remap the internal sm's outputs
    sub_si.outcomes = var_config['output_mapping'].values()
    sub_si.transitions = sub_si.outcomes
    sub_si.autonomy = var_config['autonomy']
    SIs.append(sub_si)

def get_sm_out(sm_maps, sm_name, in_var):
    """
    Given a map from state machine name to its output mapping, get a
    substate's output that corresponds to a specific input variable.
    @param sm_maps Map from SM name to its config file output mapping.
    @param sm_name The name of the SM.
    @para in_var   The input variable of interest.
    """
    return sm_maps[sm_name][in_var]

# [BEGIN] ROS CODE
#def generate_sm(data):
#    json_file = data.json_file
#    yaml_file = data.yaml_file # the block that is the destination of the arm's motion
# [END] ROS CODE
def generate_sm(json_file, yaml_file):
    """
    This method takes in a JSON file describe an automaton and a YAML file
    describing how the automaton names corresponds to real state machine names
    and produced class parameters for FlexBE.

    Broadly speaking, there are two types of input variables, and two types
    of output variables.

    Input Variables
        - Sensor     - variables that are the results of a sensor reading
        - Completion - variables that say whether or not something activated
    Output Variables
        - Activation - activate something
        - Perform    - perform something other than activate (e.g. "beep" or
                       "print")

    This distinction is important because Completion and Activation variables
    are related. Specifically, only one state machine should be created for a
    given completion-activation pair. The Activation variable tells what to
    activate, and the Completion variable tells where to go once that thing is
    activated.

    Sensor and Perform variables require their own state machines.

    The algorithm is as follows:
    1. For each state X, list all the outputs of X, and find the corresponding
       state machines in the YAML file.
        a. For each SM, find (in YAML file) what each state machine output
           mean in terms of input variables. (e.g. "changed" from the
           ChangeControlModeActionState means an input variable of "step_c".)
        b. If none of the outputs of the state machine correspond to inputs,
           then that output is a Perform variable. (e.g.
    2. For each transition (X -> Y on input A),
        a. Find what SM + outputs corresponds to A.
        b. If no SM's output maps to input A, then A must come from an external
           sensor. In this case, look up the definitions in the YAML file.
        c. Since A could be multiple conditions (e.g. "found object AND stand
           up complete"), sort these conditions based on the order of the input
           variable in the JSON file.

    @param json_file Path to file containing the automaton specifications.
    @param yaml_file Path to file containing automaton configuration.
    @param outfile Path to file to save results to.
    """

    with open(yaml_file) as yf:
        config = yaml.load(yf)

    with open(json_file) as data_file:
        info = json.load(data_file)
    variables = info['variables']
    n_in_vars = len(config['input'])

    # Two short helper functions
    def get_in_var_name(i):
        """Get the input variable name at index [i] in [in_vars] dict."""
        return variables[i]
    def get_out_var_name(i):
        """Get the output variable name at index [i] in [out_vars] dict."""
        return variables[n_in_vars + i]

    nodes = reformat(info['nodes'], n_in_vars)
    input_vars = config['input']

    SIs = []
    parent_si = StateInstantiation()
    parent_si.state_path = "/"
    parent_si.state_class = "CLASS_STATEMACHINE"
    parent_si.initial_state_name = "/State0"
    parent_si.outcomes = ['finished', 'failed']
    SIs.append(parent_si)
    for name, data in sorted(nodes.items(), key=lambda x: x[0]):
        logging.debug("Data for state {0}".format(name))
        si = StateInstantiation()
        si.state_path = "/State{0}".format(name)
        si.state_class = "Concurrent_TODO_State"
        si.autonomy = config['default_autonomy']
        transitions = get_transitions(name, nodes)

        # extract what output variables the current state is outputting
        curr_state_output_vars = [get_out_var_name(i)
                                  for i, v in enumerate(data['out_vars'])
                                  if v == 1]
        perform_sms = set()
        sm_maps = {} # the map from class_name to its out_map
        in_var_to_sm = {} # what state machine goes with an input variable?
        sm_to_in_var = {} # vice versa for fast lookup

        # Internal parameters of the ConcurrentState that we need to build.
        # See 'outcomes' and 'outcome_mapping' in the documentation of
        # ConcurrentState for more detail.
        internal_outcomes = []
        internal_maps = []
        for out_var in curr_state_output_vars:
            var_config = config[out_var]
            class_name = var_config['class']
            out_map = var_config['output_mapping']
            sm_maps[class_name] = out_map

            is_activation = False
            for in_var, state_outcome in out_map.items():
                if in_var in input_vars: # This is an Activation variable
                    is_activation = True
                    in_var_to_sm[in_var] = class_name
                    sm_to_in_var[class_name] = in_var

                    add_subsubstate(name, var_config, SIs)
                    break
            if not is_activation:
                perform_sms.add(class_name)

        for next_state, condition_idxs in transitions.items():
            conditions = [get_in_var_name(i) for i in condition_idxs]
            sm_to_out = {}
            sm_out_data = []
            for in_var in conditions:
                if in_var in in_var_to_sm: # Completion variable
                    sm = in_var_to_sm[in_var]
                    sm_to_out[sm] = get_sm_out(sm_maps, sm, in_var)
                    sm_out_data.append((in_var, sm_maps[sm][in_var]))
                else: # Sensor variable
                    var_config = config[in_var]
                    class_name = var_config['class']
                    out_map = var_config['output_mapping']
                    sm_maps[class_name] = out_map
                    in_var_to_sm[in_var] = class_name
                    sm_to_in_var[class_name] = in_var

                    sm_to_out[class_name] =\
                        get_sm_out(sm_maps, class_name, in_var)
                    sm_out_data.append((in_var, out_map[in_var]))

                    add_subsubstate(name, var_config, SIs)

            #sm_out_data.sort(key=lambda (var, x): input_vars.index(var))
            #sorted_sm_out_vars = [sm_out for in_var, sm_out in sm_out_data]

            next_state_name = "State{0}".format(next_state)
            internal_outcomes.append(next_state_name)
            internal_maps.append(sm_to_out)

            # Not really needed, but it's good to be explicit
            si.outcomes.append(next_state_name)
            si.transitions.append(next_state_name)
            logging.debug("{0} -> {1} if: {2}".format(name, next_state,
                                              sm_to_out))

        # Again, see 'outcomes' and 'outcome_mapping' in the documentation of
        # ConcurrentState.
        si.parameter_name = ["outcomes", "outcome_mapping"]
        si.parameter_value = [str(internal_outcomes), str(internal_maps)]
        SIs.append(si)

        # Log things for debugging
        sorted_sms = sm_maps.keys()
        sorted_sms.sort(key=lambda x: input_vars.index(sm_to_in_var[sm]))
        logging.debug("Concurrent(")
        for sm in sorted_sms:
            logging.debug("  {0}".format(sm))
        logging.debug(")")
        for sm in perform_sms:
            logging.debug(sm)

    # [BEGIN] ROS CODE
    #return SMGenerateResponse(SIs)
    # [END] ROS CODE
    return SIs

if __name__ == "__main__":
    # [BEGIN] ROS CODE
    #rospy.init_node('bs_sm_generate')
    #s = rospy.Service('sm_generate', SMGenerate, generate_sm)
    # [END] ROS CODE

    #json_file = "examples/all_modes_pickup/pickup.json"
    #yaml_file = "examples/all_modes_pickup/pickup.yaml"
    json_file = "examples/object_pickup/object_pickup.json"
    yaml_file = "examples/object_pickup/object_pickup.yaml"
    SIs = generate_sm(json_file, yaml_file)
    for si in SIs:
        print(si)
<commit_msg>Clean up code by using StateInstantiation initializer and creating separate variable for what to pass to initializer<commit_after>import json
import yaml
import rospy
import sys
import logging
# [BEGIN] ROS CODE
#from vigir_bs_msgs.srv import *
#from vigir_bs_msgs.msg import *
# [END] ROS CODE

logging.basicConfig(stream=sys.stderr, level=logging.INFO)

# Fake implementation for testing
class StateInstantiation():
    def __init__(self, state_path, state_class, outcomes, transitions,
        initial_state=""):
        self.state_path = state_path
        self.state_class = state_class
        self.outcomes = outcomes
        self.transitions = transitions
        self.initial_state = initial_state
        self.autonomy = ""
        self.parameter_name = []
        self.parameter_value = []

    def __str__(self):
        lines = []
        if len(self.state_path) > 0:
            lines.append("state_path = {0}".format(self.state_path))
        if len(self.state_class) > 0:
            lines.append("state_class = {0}".format(self.state_class))
        if len(self.outcomes) > 0:
            lines.append("outcomes = {0}".format(self.outcomes))
        if len(self.transitions) > 0:
            lines.append("transitions = {0}".format(self.transitions))
        if len(self.initial_state) > 0:
            lines.append("initial_state = {0}".format(self.initial_state))
        if len(self.autonomy) > 0:
            lines.append("autonomy = {0}".format(self.autonomy))
        if len(self.parameter_name) > 0:
            lines.append("parameter_name = {0}".format(self.parameter_name))
        if len(self.parameter_value) > 0:
            lines.append("parameter_value = {0}".format(self.parameter_value))
        return "\n".join(lines)

def get_transitions(name, nodes):
    """
    Deduce the transition needed to go to the next states.

    @param name The name of the state to transitions away from
    @param nodes The dictionary containing all of the node information.
    @returns A dictionary, that maps next state to indices of the input
            variables that need to be true to go to that next state.

             For example:
        {
            'State1': [1, 2], # input var 1 & 2 need to be true
            'State2': [1, 3, 4],
            ...
        }
    """
    node_info = nodes[name]
    next_states = [str(x) for x in node_info['trans']]
    next_states = list(set(next_states) - set([name])) # remove self loop

    if len(next_states) == 0:
        raise Exception("This state has no exit!")
    if len(next_states) > 1:
        print("[INFO] Multiple next states for {0}".format(name))

    transitions = {}
    for next_state in next_states:
        transitions[next_state] = [idx for (idx, v) in
                                   enumerate(nodes[next_state]['in_vars'])
                                   if v == 1]
    return transitions

def reformat(nodes, n_in_vars):
    """
    Reformat automaton dictionary so that input/output variables are
    separated.  For example, if before the dictionary for a node was
    {
        ...
        "states" : [a, b, c, d, e, f, g] # a-d are input, e-g are output
        ...
    }
    where a-d corresponds to input variables and e-g corresponds to output
    variables, now it looks like
    {
        ...
        "in_vars" : [a, b, c, d]
        "out_vars" : [e, f, g]
        ...
    }
    """
    for name, dic in nodes.items():
        in_vars = dic['state'][:n_in_vars]
        out_vars = dic['state'][n_in_vars:]
        dic['in_vars'] = in_vars
        dic['out_vars'] = out_vars
        dic.pop('state', None)
    return nodes

def add_subsubstate(name, var_config, SIs):
    """
    Add a substate to the [name] concurrent substate.
    @param name       The name of the substate.
    @param var_config The configuration for this variable.
    @param SIs        The global list of state instantiations.
    """
    state_path = "/State{0}/State{1}".format(name, name)
    state_class = var_config['class']
    # For Substates within a ConcurrentState, you don't need to remap their
    # outcomes.
    # outcomes = var_config['output_mapping'].values()
    sub_si = StateInstantiation(state_path, state_class, [], [])
    sub_si.autonomy = var_config['autonomy']
    SIs.append(sub_si)

def get_sm_out(sm_maps, sm_name, in_var):
    """
    Given a map from state machine name to its output mapping, get a
    substate's output that corresponds to a specific input variable.
    @param sm_maps Map from SM name to its config file output mapping.
    @param sm_name The name of the SM.
    @para in_var   The input variable of interest.
    """
    return sm_maps[sm_name][in_var]

# [BEGIN] ROS CODE
#def generate_sm(data):
#    json_file = data.json_file
#    yaml_file = data.yaml_file # the block that is the destination of the arm's motion
# [END] ROS CODE
def generate_sm(json_file, yaml_file):
    """
    This method takes in a JSON file describe an automaton and a YAML file
    describing how the automaton names corresponds to real state machine names
    and produced class parameters for FlexBE.

    Broadly speaking, there are two types of input variables, and two types
    of output variables.

    Input Variables
        - Sensor     - variables that are the results of a sensor reading
        - Completion - variables that say whether or not something activated
    Output Variables
        - Activation - activate something
        - Perform    - perform something other than activate (e.g. "beep" or
                       "print")

    This distinction is important because Completion and Activation variables
    are related. Specifically, only one state machine should be created for a
    given completion-activation pair. The Activation variable tells what to
    activate, and the Completion variable tells where to go once that thing is
    activated.

    Sensor and Perform variables require their own state machines.

    The algorithm is as follows:
    1. For each state X, list all the outputs of X, and find the corresponding
       state machines in the YAML file.
        a. For each SM, find (in YAML file) what each state machine output
           mean in terms of input variables. (e.g. "changed" from the
           ChangeControlModeActionState means an input variable of "step_c".)
        b. If none of the outputs of the state machine correspond to inputs,
           then that output is a Perform variable. (e.g.
    2. For each transition (X -> Y on input A),
        a. Find what SM + outputs corresponds to A.
        b. If no SM's output maps to input A, then A must come from an external
           sensor. In this case, look up the definitions in the YAML file.
        c. Since A could be multiple conditions (e.g. "found object AND stand
           up complete"), sort these conditions based on the order of the input
           variable in the JSON file.

    @param json_file Path to file containing the automaton specifications.
    @param yaml_file Path to file containing automaton configuration.
    @param outfile Path to file to save results to.
    """

    with open(yaml_file) as yf:
        config = yaml.load(yf)

    with open(json_file) as data_file:
        info = json.load(data_file)
    variables = info['variables']
    n_in_vars = len(config['input'])

    # Two short helper functions
    def get_in_var_name(i):
        """Get the input variable name at index [i] in [in_vars] dict."""
        return variables[i]
    def get_out_var_name(i):
        """Get the output variable name at index [i] in [out_vars] dict."""
        return variables[n_in_vars + i]

    nodes = reformat(info['nodes'], n_in_vars)
    input_vars = config['input']

    # Initialize list of StateInstantiation's with parent SI.
    SIs = [StateInstantiation("/", "CLASS_STATEMACHINE", ['done', 'failed'],
                              [], initial_state = "/State0")]
    for name, data in sorted(nodes.items(), key=lambda x: x[0]):
        logging.debug("Data for state {0}".format(name))
        transitions = get_transitions(name, nodes)

        # extract what output variables the current state is outputting
        curr_state_output_vars = [get_out_var_name(i)
                                  for i, v in enumerate(data['out_vars'])
                                  if v == 1]
        perform_sms = set()
        sm_maps = {} # the map from class_name to its out_map
        in_var_to_sm = {} # what state machine goes with an input variable?
        sm_to_in_var = {} # vice versa for fast lookup

        # Internal parameters of the ConcurrentState that we need to build.
        # See 'outcomes' and 'outcome_mapping' in the documentation of
        # ConcurrentState for more detail.
        internal_outcomes = []
        internal_maps = []
        for out_var in curr_state_output_vars:
            var_config = config[out_var]
            class_name = var_config['class']
            out_map = var_config['output_mapping']
            sm_maps[class_name] = out_map

            is_activation = False
            for in_var, state_outcome in out_map.items():
                if in_var in input_vars: # This is an Activation variable
                    is_activation = True
                    in_var_to_sm[in_var] = class_name
                    sm_to_in_var[class_name] = in_var

                    add_subsubstate(name, var_config, SIs)
                    break
            if not is_activation:
                perform_sms.add(class_name)

        si_outcomes = []
        si_transitions = []
        for next_state, condition_idxs in transitions.items():
            conditions = [get_in_var_name(i) for i in condition_idxs]
            sm_to_out = {}
            sm_out_data = []
            for in_var in conditions:
                if in_var in in_var_to_sm: # Completion variable
                    sm = in_var_to_sm[in_var]
                    sm_to_out[sm] = get_sm_out(sm_maps, sm, in_var)
                    sm_out_data.append((in_var, sm_maps[sm][in_var]))
                else: # Sensor variable
                    var_config = config[in_var]
                    class_name = var_config['class']
                    out_map = var_config['output_mapping']
                    sm_maps[class_name] = out_map
                    in_var_to_sm[in_var] = class_name
                    sm_to_in_var[class_name] = in_var

                    sm_to_out[class_name] =\
                        get_sm_out(sm_maps, class_name, in_var)
                    sm_out_data.append((in_var, out_map[in_var]))

                    add_subsubstate(name, var_config, SIs)

            #sm_out_data.sort(key=lambda (var, x): input_vars.index(var))
            #sorted_sm_out_vars = [sm_out for in_var, sm_out in sm_out_data]

            next_state_name = "State{0}".format(next_state)
            internal_outcomes.append(next_state_name)
            internal_maps.append(sm_to_out)

            # Not really needed, but it's good to be explicit
            si_outcomes.append(next_state_name)
            si_transitions.append(next_state_name)
            logging.debug("{0} -> {1} if: {2}".format(name, next_state,
                                              sm_to_out))

        # Again, see 'outcomes' and 'outcome_mapping' in the documentation of
        # ConcurrentState.
        si = StateInstantiation("/State{0}".format(name), "ConcurrentState",
                                si_outcomes, si_transitions)
        si.autonomy = config['default_autonomy']
        si.parameter_name = ["outcomes", "outcome_mapping"]
        si.parameter_value = [str(internal_outcomes), str(internal_maps)]
        SIs.append(si)

        # Log things for debugging
        sorted_sms = sm_maps.keys()
        sorted_sms.sort(key=lambda x: input_vars.index(sm_to_in_var[sm]))
        logging.debug("Concurrent(")
        for sm in sorted_sms:
            logging.debug("  {0}".format(sm))
        logging.debug(")")
        for sm in perform_sms:
            logging.debug(sm)

    # [BEGIN] ROS CODE
    #return SMGenerateResponse(SIs)
    # [END] ROS CODE
    return SIs

if __name__ == "__main__":
    # [BEGIN] ROS CODE
    #rospy.init_node('bs_sm_generate')
    #s = rospy.Service('sm_generate', SMGenerate, generate_sm)
    # [END] ROS CODE

    #json_file = "examples/all_modes_pickup/pickup.json"
    #yaml_file = "examples/all_modes_pickup/pickup.yaml"
    json_file = "examples/object_pickup/object_pickup.json"
    yaml_file = "examples/object_pickup/object_pickup.yaml"
    SIs = generate_sm(json_file, yaml_file)
    for si in SIs:
        print("")
        print(si)
<|endoftext|>


<commit_before>#!/usr/bin/env python
import argparse
import mosquitto

DEFAULT_HOST = "localhost"
DEFAULT_PORT = 1883

class System(object):
    def __init__(self, name, controls):
        self.name = name
        self.controls = {}
        for order, control in enumerate(controls, 1):
            control.bind(self, order)
            self.controls[control.name] = control

    def topic(self, *tail):
        return "/".join(("/devices", self.name) + tail)

    def publish(self, client):
        client.publish(self.topic("meta/name"), self.name, 2, True)
        # publish metadata first, then values
        for control in self.controls.values():
            control.publish(client)
        for control in self.controls.values():
            control.publish_value()

    def subscribe(self, client):
        for control in self.controls.values():
            control.subscribe(client)

    def post_value(self, target, payload):
        target_control = self.controls[target]
        if target_control.value_str() != payload:
            target_control.post_value(payload)

    def on_message(self, topic, payload):
        for client in self.controls.values():
            client.on_message(topic, payload)

class Control(object):
    type = None

    def __init__(self, name):
        assert isinstance(name, str)
        self.name = name

    def bind(self, system, order):
        self.system = system
        self.order = order

    def topic(self, *tail):
        return self.system.topic("controls", self.name, *tail)

    def publish(self, client):
        assert self.type is not None, "control without a type"
        self.client = client
        self.client.publish(self.topic("meta/type"), self.type, 2, True)
        self.client.publish(self.topic("meta/order"), str(self.order), 2, True)

    def publish_value(self):
        value_str = self.value_str()
        print "publish: %r = %r" % (self.name, value_str)
        if value_str is not None:
            self.client.publish(self.topic(), value_str, 0, True)

    def post_value(self, payload):
        print "%r <-- %r" % (self.name, payload)
        self.update(payload)
        self.publish_value()

    def value_str(self):
        return None

    def subscribe(self, client):
        self.client = client

    def on_message(self, topic, payload):
        pass

    def update(self, payload):
        raise NotImplementedError

class WritableControl(Control):
    def __init__(self, name, target=None, **kwargs):
        super(WritableControl, self).__init__(name, **kwargs)
        print "WritableControl %r: kwargs: %r target: %r" % \
            (self.name, kwargs, target)
        self.target = target

    def subscribe(self, client):
        super(WritableControl, self).subscribe(client)
        client.subscribe(self.topic("on"))

    def on_message(self, topic, payload):
        if topic == self.topic("on"):
            self.post_value(payload)

    def post_value(self, payload):
        super(WritableControl, self).post_value(payload)
        if self.target is not None:
            self.system.post_value(self.target, self.value_str())

class ReadOnlyControl(Control):
    def publish(self, client):
        super(ReadOnlyControl, self).publish(client)
        self.client.publish(self.topic("meta/readonly"), "1", 2, True)

class SwitchControlBase(Control):
    type = "switch"

    def __init__(self, name, is_on=False, **kwargs):
        super(SwitchControlBase, self).__init__(name, **kwargs)
        self.is_on = bool(is_on)

    def value_str(self):
        return "1" if self.is_on else "0"

    def update(self, payload):
        self.is_on = (payload == "1")

class SwitchControl(WritableControl, SwitchControlBase):
    pass

class ReadOnlySwitchControl(ReadOnlyControl, SwitchControlBase):
    pass

class NumericControl(Control):
    def __init__(self, name, value=0, **kwargs):
        super(NumericControl, self).__init__(name, **kwargs)
        self.value = value

    def value_str(self):
        return str(self.value) if self.value is not None else 0

    def update(self, payload):
        self.value = float(payload)

class RangeControl(NumericControl, WritableControl):
    type = "range"

    def __init__(self, name, max, value=0, **kwargs):
        super(RangeControl, self).__init__(name, value=value, **kwargs)
        self.max = max

    def publish(self, client):
        super(RangeControl, self).publish(client)
        self.client.publish(self.topic("meta/max"), str(self.max), 2, True)

class TemperatureControl(ReadOnlyControl, NumericControl):
    type = "temperature"

class RGBControl(WritableControl):
    type = "rgb"

    def __init__(self, name, red=0, green=0, blue=0, **kwargs):
        super(RGBControl, self).__init__(name, **kwargs)
        self.red = red
        self.green = green
        self.blue = blue

    def value_str(self):
        return "%d;%d;%d" % (self.red, self.green, self.blue)

    def update(self, payload):
        parts = payload.split(";")
        try:
            if len(parts) != 3:
                raise ValueError
            self.red = int(parts[0])
            self.green = int(parts[1])
            self.blue = int(parts[2])
        except ValueError:
            print "RGBControl: invalid payload: %r" % payload

systems = [
    System("Relays", [
        SwitchControl("Relay 1"),
        SwitchControl("Relay 2", target="Relay 2 Status"),
        ReadOnlySwitchControl("Relay 2 Status")
    ]),
    System("Temperature", [
        TemperatureControl("Temp 1"),
        RangeControl("Set Temp 1", max=100, target="Temp 1"),
        TemperatureControl("Temp 2"),
        RangeControl("Set Temp 2", max=100, target="Temp 2")
    ]),
    System("Dimmer", [
        RGBControl("RGB"),
        RangeControl("White", max=255)
    ])
]

def on_connect(client, userdata, rc):
    if rc != 0:
        return
    for s in systems:
        s.publish(client)
        s.subscribe(client)

def on_message(client, userdata, msg):
    print "message received: %r %r" % (msg.topic, msg.payload)
    for s in systems:
        s.on_message(msg.topic, msg.payload)

def main():
    parser = argparse.ArgumentParser(description="homA device simulator", add_help=False)
    parser.add_argument("-h", "--host", dest="host", type=str,
                        help="MQTT host", default="localhost")
    parser.add_argument("-p", "--port", dest="port", type=int,
                        help="MQTT port", default="1883")
    args = parser.parse_args()

    client = mosquitto.Mosquitto("simulate")
    client.on_connect = on_connect
    client.on_message = on_message
    client.connect(args.host, args.port)

    while 1:
        rc = client.loop()
        if rc != 0:
            break

if __name__ == "__main__":
    main()
<commit_msg>Updated RGB_All control range to match similar DDL24 parameter.<commit_after>#!/usr/bin/env python
import argparse
import mosquitto

DEFAULT_HOST = "localhost"
DEFAULT_PORT = 1883

class System(object):
    def __init__(self, name, controls):
        self.name = name
        self.controls = {}
        for order, control in enumerate(controls, 1):
            control.bind(self, order)
            self.controls[control.name] = control

    def topic(self, *tail):
        return "/".join(("/devices", self.name) + tail)

    def publish(self, client):
        client.publish(self.topic("meta/name"), self.name, 2, True)
        # publish metadata first, then values
        for control in self.controls.values():
            control.publish(client)
        for control in self.controls.values():
            control.publish_value()

    def subscribe(self, client):
        for control in self.controls.values():
            control.subscribe(client)

    def post_value(self, target, payload):
        target_control = self.controls[target]
        if target_control.value_str() != payload:
            target_control.post_value(payload)

    def on_message(self, topic, payload):
        for client in self.controls.values():
            client.on_message(topic, payload)

class Control(object):
    type = None

    def __init__(self, name):
        assert isinstance(name, str)
        self.name = name

    def bind(self, system, order):
        self.system = system
        self.order = order

    def topic(self, *tail):
        return self.system.topic("controls", self.name, *tail)

    def publish(self, client):
        assert self.type is not None, "control without a type"
        self.client = client
        self.client.publish(self.topic("meta/type"), self.type, 2, True)
        self.client.publish(self.topic("meta/order"), str(self.order), 2, True)

    def publish_value(self):
        value_str = self.value_str()
        print "publish: %r = %r" % (self.name, value_str)
        if value_str is not None:
            self.client.publish(self.topic(), value_str, 0, True)

    def post_value(self, payload):
        print "%r <-- %r" % (self.name, payload)
        self.update(payload)
        self.publish_value()

    def value_str(self):
        return None

    def subscribe(self, client):
        self.client = client

    def on_message(self, topic, payload):
        pass

    def update(self, payload):
        raise NotImplementedError

class WritableControl(Control):
    def __init__(self, name, target=None, **kwargs):
        super(WritableControl, self).__init__(name, **kwargs)
        print "WritableControl %r: kwargs: %r target: %r" % \
            (self.name, kwargs, target)
        self.target = target

    def subscribe(self, client):
        super(WritableControl, self).subscribe(client)
        client.subscribe(self.topic("on"))

    def on_message(self, topic, payload):
        if topic == self.topic("on"):
            self.post_value(payload)

    def post_value(self, payload):
        super(WritableControl, self).post_value(payload)
        if self.target is not None:
            self.system.post_value(self.target, self.value_str())

class ReadOnlyControl(Control):
    def publish(self, client):
        super(ReadOnlyControl, self).publish(client)
        self.client.publish(self.topic("meta/readonly"), "1", 2, True)

class SwitchControlBase(Control):
    type = "switch"

    def __init__(self, name, is_on=False, **kwargs):
        super(SwitchControlBase, self).__init__(name, **kwargs)
        self.is_on = bool(is_on)

    def value_str(self):
        return "1" if self.is_on else "0"

    def update(self, payload):
        self.is_on = (payload == "1")

class SwitchControl(WritableControl, SwitchControlBase):
    pass

class ReadOnlySwitchControl(ReadOnlyControl, SwitchControlBase):
    pass

class NumericControl(Control):
    def __init__(self, name, value=0, **kwargs):
        super(NumericControl, self).__init__(name, **kwargs)
        self.value = value

    def value_str(self):
        return str(self.value) if self.value is not None else 0

    def update(self, payload):
        self.value = float(payload)

class RangeControl(NumericControl, WritableControl):
    type = "range"

    def __init__(self, name, max, value=0, **kwargs):
        super(RangeControl, self).__init__(name, value=value, **kwargs)
        self.max = max

    def publish(self, client):
        super(RangeControl, self).publish(client)
        self.client.publish(self.topic("meta/max"), str(self.max), 2, True)

class TemperatureControl(ReadOnlyControl, NumericControl):
    type = "temperature"

class RGBControl(WritableControl):
    type = "rgb"

    def __init__(self, name, red=0, green=0, blue=0, **kwargs):
        super(RGBControl, self).__init__(name, **kwargs)
        self.red = red
        self.green = green
        self.blue = blue

    def value_str(self):
        return "%d;%d;%d" % (self.red, self.green, self.blue)

    def update(self, payload):
        parts = payload.split(";")
        try:
            if len(parts) != 3:
                raise ValueError
            self.red = int(parts[0])
            self.green = int(parts[1])
            self.blue = int(parts[2])
        except ValueError:
            print "RGBControl: invalid payload: %r" % payload

systems = [
    System("Relays", [
        SwitchControl("Relay 1"),
        SwitchControl("Relay 2", target="Relay 2 Status"),
        ReadOnlySwitchControl("Relay 2 Status")
    ]),
    System("Temperature", [
        TemperatureControl("Temp 1"),
        RangeControl("Set Temp 1", max=100, target="Temp 1"),
        TemperatureControl("Temp 2"),
        RangeControl("Set Temp 2", max=100, target="Temp 2")
    ]),
    System("Dimmer", [
        RGBControl("RGB"),
        RangeControl("RGB_All", max=100),
        RangeControl("White", max=255)
    ])
]

def on_connect(client, userdata, rc):
    if rc != 0:
        return
    for s in systems:
        s.publish(client)
        s.subscribe(client)

def on_message(client, userdata, msg):
    print "message received: %r %r" % (msg.topic, msg.payload)
    for s in systems:
        s.on_message(msg.topic, msg.payload)

def main():
    parser = argparse.ArgumentParser(description="homA device simulator", add_help=False)
    parser.add_argument("-h", "--host", dest="host", type=str,
                        help="MQTT host", default="localhost")
    parser.add_argument("-p", "--port", dest="port", type=int,
                        help="MQTT port", default="1883")
    args = parser.parse_args()

    client = mosquitto.Mosquitto("simulate")
    client.on_connect = on_connect
    client.on_message = on_message
    client.connect(args.host, args.port)

    while 1:
        rc = client.loop()
        if rc != 0:
            break

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>import json, random, datetime
from django.shortcuts import render
from django.shortcuts import render_to_response, redirect, HttpResponse, render, get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import authenticate, login, logout
from deck.models import User, Deck, card_to_dict

CARDS = ['AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', '0S', 'JS', 'QS', 'KS',
        'AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', '0D', 'JD', 'QD', 'KD',
        'AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', '0C', 'JC', 'QC', 'KC',
        'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', '0H', 'JH', 'QH', 'KH']

def _get_request_var(request, key, default=1):
    if request.method == 'POST':
        return request.POST.get(key, default)
    else:
        return request.GET.get(key, default)

def shuffle(request, key=''):
    return new_deck(request, key, True)

def new_deck(request, key='', shuffle=False):
    deck_count = int(_get_request_var(request, 'deck_count'))
    deck_cards = _get_request_var(request, 'cards', None)
    if deck_count > 20:
        response = HttpResponse(json.dumps({'success':False,'error':'The max number of Decks is 20.'}), content_type="application/json")
        response['Access-Control-Allow-Origin'] = '*'
        return response
    if key:
        try:
            deck = Deck.objects.get(key=key)
        except Deck.DoesNotExist:
            response = HttpResponse(json.dumps({'success':False,'error':'Deck ID does not exist.'}), content_type="application/json", status=404)
            response['Access-Control-Allow-Origin'] = '*'
            return response
    else:
        deck = Deck()
        deck.deck_count = deck_count
    deck.open_new(deck_cards)
    deck.shuffled = False
    if shuffle:
        random.shuffle(deck.stack)
        deck.shuffled = True
    deck.save() #save the deck_count.

    resp = {'success':True, 'deck_id':deck.key, 'remaining':len(deck.stack), 'shuffled':deck.shuffled}

    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def deck_info(request, key=0):
    success = True
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        response = HttpResponse(json.dumps({'success':False, 'error':'Deck ID does not exist.'}), content_type="application/json", status=404)
        response['Access-Control-Allow-Origin'] = '*'
        return response

    resp = {'success':True, 'deck_id':deck.key, 'remaining':len(deck.stack), 'shuffled':deck.shuffled}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def draw(request, key=None):
    success = True
    card_count = int(_get_request_var(request, 'count'))
    if not key:
        deck = Deck()
        deck.deck_count = int(_get_request_var(request, 'deck_count'))
        deck.open_new()
        random.shuffle(deck.stack)
        deck.shuffled = True
        deck.save()
    else:
        try:
            deck = Deck.objects.get(key=key)
        except Deck.DoesNotExist:
            response = HttpResponse(json.dumps({'success':False,'error':'Deck ID does not exist.'}), content_type="application/json", status=404)
            response['Access-Control-Allow-Origin'] = '*'
            return response
    if card_count > len(deck.stack):
        success = False
    cards = deck.stack[0:card_count]
    deck.stack = deck.stack[card_count:]
    deck.save()

    a = []
    for card in cards:
        a.append(card_to_dict(card))
    if not success:
        resp = {'success':success, 'deck_id':deck.key, 'cards':a, 'remaining':len(deck.stack), 'error':'Not enough cards remaining to draw '+str(card_count)+' additional'}
    else:
        resp = {'success':success, 'deck_id':deck.key, 'cards':a, 'remaining':len(deck.stack)}

    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def add_to_pile(request, key, pile):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return HttpResponse(json.dumps({'success':False,'error':'Deck ID does not exist.'}), content_type="application/json", status=404)

    cards = _get_request_var(request, 'cards', None)
    if cards is None:
        response = HttpResponse(json.dumps({'success':False,'error':'You must specify cards to add to the pile.'}), content_type="application/json", status=404)
        response['Access-Control-Allow-Origin'] = '*'
        return response
    else:
        # Ignore case
        cards = cards.upper()
        # Only allow real cards
        cards = [x for x in cards.split(',') if x in CARDS]

    if not deck.piles:
        deck.piles = {}
    for key in deck.piles: #iterate through all the piles and remove any specified cards from those piles.
        p = deck.piles[key] #times like these that I question if I should have just made piles a model instead of a json field...
        for card in cards:
            if card in pile:
                p.remove(card)

    try: #try to add to the pile
        deck.piles[pile].extend(cards)
    except KeyError as e:#the pile is brand new
        deck.piles[pile] = cards #add the specified cards to the new pile
    deck.save()

    piles = {}
    for k in deck.piles:
        piles[k] = {"remaining":len(deck.piles[k])}

    resp = {'success':True, 'deck_id':deck.key, 'remaining':len(deck.stack), 'piles':piles}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def draw_from_pile(request, key, pile):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return HttpResponse(json.dumps({'success':False,'error':'Deck ID does not exist.'}), content_type="application/json", status=404)

    cards = _get_request_var(request, 'cards', None)
    cards_in_response = []

    p = deck.piles[pile] #times like these that I question if I should have just made piles a model instead of a json field...

    if cards:
        # Ignore case
        cards = cards.upper()
        # Only allow real cards
        cards = [x for x in cards.split(',') if x in CARDS]

        for card in cards:
            try:
                p.remove(card)
                cards_in_response.append(card)
            except:
                response = HttpResponse(json.dumps({'success':False,'error':'The pile, '+pile+\
                    ' does not contain the requested cards.'}), content_type="application/json", status=404)
                response['Access-Control-Allow-Origin'] = '*'
                return response
    else:
        card_count = int(_get_request_var(request, 'count'))
        if card_count > len(p):
            response = HttpResponse(json.dumps({'success':False,'error':'Not enough cards remaining to draw '+\
                str(card_count)+' additional'}), content_type="application/json", status=404)
            response['Access-Control-Allow-Origin'] = '*'
            return response

        cards_in_response = p[-card_count:]
        p = p[:-card_count]
    deck.piles[pile] = p
    deck.save()

    a = []

    for card in cards_in_response:
        a.append(card_to_dict(card))

    piles = {}
    for k in deck.piles:
        piles[k] = {"remaining":len(deck.piles[k])}

    resp = {'success':True, 'deck_id':deck.key, 'cards':a, 'piles':piles}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response<commit_msg>Fixes pile issue.<commit_after>import json, random, datetime
from django.shortcuts import render
from django.shortcuts import render_to_response, redirect, HttpResponse, render, get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import authenticate, login, logout
from deck.models import User, Deck, card_to_dict

CARDS = ['AS', '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', '0S', 'JS', 'QS', 'KS',
        'AD', '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', '0D', 'JD', 'QD', 'KD',
        'AC', '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', '0C', 'JC', 'QC', 'KC',
        'AH', '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', '0H', 'JH', 'QH', 'KH']

def _get_request_var(request, key, default=1):
    if request.method == 'POST':
        return request.POST.get(key, default)
    else:
        return request.GET.get(key, default)

def shuffle(request, key=''):
    return new_deck(request, key, True)

def new_deck(request, key='', shuffle=False):
    deck_count = int(_get_request_var(request, 'deck_count'))
    deck_cards = _get_request_var(request, 'cards', None)
    if deck_count > 20:
        response = HttpResponse(json.dumps({'success':False,'error':'The max number of Decks is 20.'}), content_type="application/json")
        response['Access-Control-Allow-Origin'] = '*'
        return response
    if key:
        try:
            deck = Deck.objects.get(key=key)
        except Deck.DoesNotExist:
            response = HttpResponse(json.dumps({'success':False,'error':'Deck ID does not exist.'}), content_type="application/json", status=404)
            response['Access-Control-Allow-Origin'] = '*'
            return response
    else:
        deck = Deck()
        deck.deck_count = deck_count
    deck.open_new(deck_cards)
    deck.shuffled = False
    if shuffle:
        random.shuffle(deck.stack)
        deck.shuffled = True
    deck.save() #save the deck_count.

    resp = {'success':True, 'deck_id':deck.key, 'remaining':len(deck.stack), 'shuffled':deck.shuffled}

    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def deck_info(request, key=0):
    success = True
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        response = HttpResponse(json.dumps({'success':False, 'error':'Deck ID does not exist.'}), content_type="application/json", status=404)
        response['Access-Control-Allow-Origin'] = '*'
        return response

    resp = {'success':True, 'deck_id':deck.key, 'remaining':len(deck.stack), 'shuffled':deck.shuffled}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def draw(request, key=None):
    success = True
    card_count = int(_get_request_var(request, 'count'))
    if not key:
        deck = Deck()
        deck.deck_count = int(_get_request_var(request, 'deck_count'))
        deck.open_new()
        random.shuffle(deck.stack)
        deck.shuffled = True
        deck.save()
    else:
        try:
            deck = Deck.objects.get(key=key)
        except Deck.DoesNotExist:
            response = HttpResponse(json.dumps({'success':False,'error':'Deck ID does not exist.'}), content_type="application/json", status=404)
            response['Access-Control-Allow-Origin'] = '*'
            return response
    if card_count > len(deck.stack):
        success = False
    cards = deck.stack[0:card_count]
    deck.stack = deck.stack[card_count:]
    deck.save()

    a = []
    for card in cards:
        a.append(card_to_dict(card))
    if not success:
        resp = {'success':success, 'deck_id':deck.key, 'cards':a, 'remaining':len(deck.stack), 'error':'Not enough cards remaining to draw '+str(card_count)+' additional'}
    else:
        resp = {'success':success, 'deck_id':deck.key, 'cards':a, 'remaining':len(deck.stack)}

    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def add_to_pile(request, key, pile):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return HttpResponse(json.dumps({'success':False,'error':'Deck ID does not exist.'}), content_type="application/json", status=404)

    cards = _get_request_var(request, 'cards', None)
    if cards is None:
        response = HttpResponse(json.dumps({'success':False,'error':'You must specify cards to add to the pile.'}), content_type="application/json", status=404)
        response['Access-Control-Allow-Origin'] = '*'
        return response
    else:
        # Ignore case
        cards = cards.upper()
        # Only allow real cards
        cards = [x for x in cards.split(',') if x not in deck.stack and x in CARDS] #the card needs to be drawn before it can be added to a pile.

    if not deck.piles:
        deck.piles = {}
    for key in deck.piles: #iterate through all the piles and remove any specified cards from those piles.
        p = deck.piles[key] #times like these that I question if I should have just made piles a model instead of a json field...
        for card in cards:
            if card in pile:
                p.remove(card)

    try: #try to add to the pile
        deck.piles[pile].extend(cards)
    except KeyError as e:#the pile is brand new
        deck.piles[pile] = cards #add the specified cards to the new pile
    deck.save()

    piles = {}
    for k in deck.piles:
        piles[k] = {"remaining":len(deck.piles[k])}

    resp = {'success':True, 'deck_id':deck.key, 'remaining':len(deck.stack), 'piles':piles}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def draw_from_pile(request, key, pile):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return HttpResponse(json.dumps({'success':False,'error':'Deck ID does not exist.'}), content_type="application/json", status=404)

    cards = _get_request_var(request, 'cards', None)
    cards_in_response = []

    p = deck.piles[pile] #times like these that I question if I should have just made piles a model instead of a json field...

    if cards:
        # Ignore case
        cards = cards.upper()
        # Only allow real cards
        cards = [x for x in cards.split(',') if x in CARDS]

        for card in cards:
            try:
                p.remove(card)
                cards_in_response.append(card)
            except:
                response = HttpResponse(json.dumps({'success':False,'error':'The pile, '+pile+\
                    ' does not contain the requested cards.'}), content_type="application/json", status=404)
                response['Access-Control-Allow-Origin'] = '*'
                return response
    else:
        card_count = int(_get_request_var(request, 'count'))
        if card_count > len(p):
            response = HttpResponse(json.dumps({'success':False,'error':'Not enough cards remaining to draw '+\
                str(card_count)+' additional'}), content_type="application/json", status=404)
            response['Access-Control-Allow-Origin'] = '*'
            return response

        cards_in_response = p[-card_count:]
        p = p[:-card_count]
    deck.piles[pile] = p
    deck.save()

    a = []

    for card in cards_in_response:
        a.append(card_to_dict(card))

    piles = {}
    for k in deck.piles:
        piles[k] = {"remaining":len(deck.piles[k])}

    resp = {'success':True, 'deck_id':deck.key, 'cards':a, 'piles':piles}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response<|endoftext|>


<commit_before>"""
    decorstate
    ~~~~~~~~~~

    Simple "state machines" with Python decorators.

    :copyright: (c) 2015-2017 Andrew Hawker.
    :license: Apache 2.0, See LICENSE file.
"""
from __future__ import unicode_literals, print_function

import collections
import contextlib
import functools
import logging
import sys
import threading
import time

__all__ = ['transition']

LOGGER = logging.getLogger(__name__)

if sys.version_info[0] == 2:
    Condition = threading._Condition
    str_types = basestring
else:
    Condition = threading.Condition
    str_types = str

def noop(*args, **kwargs):
    """
    Dummy method that just returns :bool:`True`.
    """
    return True

def iterable(item):
    """
    Return an iterable that contains the given item or itself if it already is one.
    """
    if isinstance(item, collections.Iterable) and not isinstance(item, str_types):
        return item
    return [item] if item is not None else []

class TransitionChangedEvent(Condition):
    """
    Threading condition that is notified whenever a machine performs a state transition.
    """

    def __enter__(self):
        return super(TransitionChangedEvent, self).__enter__()

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.notify_all()
        return super(TransitionChangedEvent, self).__exit__(exc_type, exc_val, exc_tb)

class Transition(object):
    """
    Object that implements the descriptor protocol to define a transition between two states of a machine.

    Transitions are defined as a decorated methods on an instance method.

    Transitions may also define three additional decorated methods to be called during specific phases of a transition.
    The following are supported:
        @transition.guard -- Callable to return a `bool` that determines if the transition should execute.
        @transition.before -- Callable to be invoked before starting the transition.
        @transition.after -- Callable to be invoked after completing the transition.
        @transition.exit -- Callable to be invoked before the next transition is invoked to change machine state.

    Example:

    Let's take the example of a simple light switch.

    import decorstate

    class LightSwitch(object):
        state = 'off'

        @decorstate.transition('off', 'on')
        def on(self, *args, **kwargs):
            print 'Let there be light!'

        @decorstate.transition('on', 'off')
        def off(self, *args, **kwargs):
            print 'The darkness engulfs you'

    >>> switch = LightSwitch()
    >>> switch.state
    'off'
    >>> switch.on()
    >>> switch.state
    'on'
    >>> switch.off()
    >>> switch.state
    'off
    """

    #: Default state of the machine if the :attr:`state` is not defined.
    default_state = None

    #: Default transition of the machine if the :attr:`transition` is not defined.
    default_transition = None

    #: Default transition event of the machine to create :attr:`transition_event` instances.
    default_transition_event = TransitionChangedEvent()

    def __init__(self, current_state=None, next_state=None, action=None, condition=None,
                 enter=None, exit=None, before=None, after=None):
        self.current_state = current_state
        self.next_state = next_state
        self.action = action
        self.condition = condition or noop
        self.on_enter = enter or noop
        self.on_exit = exit or noop
        self.on_before = before or noop
        self.on_after = after or noop

    def __call__(self, func, *args, **kwargs):
        if func is not None:
            functools.update_wrapper(self, func)
            LOGGER.debug('Creating transition for callable "{}"'.format(func.__name__))
        return self.update(action=func)

    def __get__(self, instance, owner=None):
        if instance is None:
            return self
        return self.wrapper(instance)

    @property
    def name(self):
        return self.action.__name__ if self.action else ''

    def update(self, action=None, condition=None, enter=None, exit=None, before=None, after=None):
        """
        Create a new :class:`~decorstate.Transition` instance composed of the updated transition functions.
        """
        return type(self)(current_state=self.current_state,
                          next_state=self.next_state,
                          action=action or self.action,
                          condition=condition or self.condition,
                          enter=enter or self.on_enter,
                          exit=exit or self.on_exit,
                          before=before or self.on_before,
                          after=after or self.on_after)

    def guard(self, func):
        """
        Wrapped function which is called to determine if the state machine should perform this transition.
        """
        LOGGER.debug('Registering guard callable for "{}" transition'.format(self.name))
        return self.update(condition=func)

    def enter(self, func):
        """
        Wrapped function called when transition into a new state.
        """
        LOGGER.debug('Registering enter callable for "{}" transition'.format(self.name))
        return self.update(enter=func)

    def exit(self, func):
        """
        Wrapped function called when transitioning out of the current state and to a new one.
        """
        LOGGER.debug('Registering exit callable for "{}" transition'.format(self.name))
        return self.update(exit=func)

    def before(self, func):
        """
        Wrapped function called before executing the transition action.
        """
        LOGGER.debug('Registering before callable for "{}" transition'.format(self.name))
        return self.update(before=func)

    def after(self, func):
        """
        Wrapped function called after executing the transaction action.
        """
        LOGGER.debug('Registering after callable for "{}" transition'.format(self.name))
        return self.update(after=func)

    @contextlib.contextmanager
    def transition_from(self, machine, *args, **kwargs):
        """
        Perform process of transitioning out of the current state.
        """
        with machine.transition_event:
            if machine.transition:
                machine.transition.on_exit(machine, *args, **kwargs)
            yield machine
            machine.transition = self
            machine.state = self.next_state

    @contextlib.contextmanager
    def transition_into(self, machine, *args, **kwargs):
        """
        Perform process of transitioning from the current state to a new one.

        This means performing all exit handling of the previous transition as well as the enter handling
        and action of the new transition.
        """
        # Perform exit process of the current transition before starting the enter process of the next.
        with self.transition_from(machine, *args, **kwargs) as machine:
            try:
                self.on_before(machine, *args, **kwargs)
                yield self
            except:
                raise
            finally:
                self.on_after(machine, *args, **kwargs)

    def wrapper(self, machine):
        """
        Returns a function which executes the entire flow of a transition.

        :param machine: Instance of the object whose functions are decorated with :func:`~decorstate.transition`.
        """
        def decorator(*args, **kwargs):
            """
            Decorator which encapsulates execution of a single transition.
            """
            state = machine.state

            # Do not perform transition if the current state of the machine
            # and the required current state of the transition do not match.
            if state not in iterable(self.current_state):
                LOGGER.debug('Transition not supported from state "{}"'.format(state))
                return state

            # Ignore transitions whose guard conditions fail to return True.
            if self.condition and not bool(self.condition(machine, *args, **kwargs)):
                LOGGER.debug('Guard condition failed for transition from state "{}"'.format(state))
                return state

            LOGGER.debug('Transitioning from "{}" to "{}"'.format(state, self.next_state))

            # Perform the transition from the previous state to new.
            # This includes calling performing the exit phase of the previous state/transition
            # and the enter/action of the new one.
            with self.transition_into(machine, *args, **kwargs) as new:
                new.action(machine, *args, **kwargs)
                return new.next_state

        # Attach descriptor and helper functions to the decorator for easy use.
        decorator = functools.update_wrapper(decorator, self.action)
        decorator.wait = functools.partial(wait_for_state, machine, self.next_state)
        decorator.transition = self

        # Set default values on the instance that is using this descriptors if they don't already have them set.
        set_attr_default(machine, 'state', self.default_state)
        set_attr_default(machine, 'transition', self.default_transition)
        set_attr_default(machine, 'transition_event', self.default_transition_event)

        return decorator

transition = Transition

def wait_for_state(machine, state, timeout=None):
    """
    Wait until the given machine transitions into a specific state.

    :param machine: Machine to wait for a transition into a specific state
    :param state: State to wait for the machine to transition into
    :param timeout: Timeout in seconds to wait for transition; default: None
    """
    end = None
    remaining = timeout

    with machine.transition_event:
        while machine.state != state:
            if timeout is not None:
                if end is None:
                    end = time.time() + timeout
                else:
                    remaining = end - time.time()
                    if remaining <= 0:
                        return False
            machine.transition_event.wait(remaining)

    return True

def set_attr_default(obj, name, default=None):
    """
    Set the value of an attribute on the given object if it isn't yet defined.
    """
    if not hasattr(obj, name):
        setattr(obj, name, default)
<commit_msg>Add 'initialized' method to check machine object is ready to use.<commit_after>"""
    decorstate
    ~~~~~~~~~~

    Simple "state machines" with Python decorators.

    :copyright: (c) 2015-2017 Andrew Hawker.
    :license: Apache 2.0, See LICENSE file.
"""
from __future__ import unicode_literals, print_function

import collections
import contextlib
import functools
import logging
import sys
import threading
import time

__all__ = ['transition']

LOGGER = logging.getLogger(__name__)

if sys.version_info[0] == 2:
    Condition = threading._Condition
    str_types = basestring
else:
    Condition = threading.Condition
    str_types = str

def noop(*args, **kwargs):
    """
    Dummy method that just returns :bool:`True`.
    """
    return True

def iterable(item):
    """
    Return an iterable that contains the given item or itself if it already is one.
    """
    if isinstance(item, collections.Iterable) and not isinstance(item, str_types):
        return item
    return [item] if item is not None else []

class TransitionChangedEvent(Condition):
    """
    Threading condition that is notified whenever a machine performs a state transition.
    """

    def __enter__(self):
        return super(TransitionChangedEvent, self).__enter__()

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.notify_all()
        return super(TransitionChangedEvent, self).__exit__(exc_type, exc_val, exc_tb)

class Transition(object):
    """
    Object that implements the descriptor protocol to define a transition between two states of a machine.

    Transitions are defined as a decorated methods on an instance method.

    Transitions may also define three additional decorated methods to be called during specific phases of a transition.
    The following are supported:
        @transition.guard -- Callable to return a `bool` that determines if the transition should execute.
        @transition.before -- Callable to be invoked before starting the transition.
        @transition.after -- Callable to be invoked after completing the transition.
        @transition.exit -- Callable to be invoked before the next transition is invoked to change machine state.

    Example:

    Let's take the example of a simple light switch.

    import decorstate

    class LightSwitch(object):
        state = 'off'

        @decorstate.transition('off', 'on')
        def on(self, *args, **kwargs):
            print 'Let there be light!'

        @decorstate.transition('on', 'off')
        def off(self, *args, **kwargs):
            print 'The darkness engulfs you'

    >>> switch = LightSwitch()
    >>> switch.state
    'off'
    >>> switch.on()
    >>> switch.state
    'on'
    >>> switch.off()
    >>> switch.state
    'off
    """

    #: Default state of the machine if the :attr:`state` is not defined.
    default_state = None

    #: Default transition of the machine if the :attr:`transition` is not defined.
    default_transition = None

    #: Default transition event of the machine to create :attr:`transition_event` instances.
    default_transition_event = TransitionChangedEvent()

    def __init__(self, current_state=None, next_state=None, action=None, condition=None,
                 enter=None, exit=None, before=None, after=None):
        self.current_state = current_state
        self.next_state = next_state
        self.action = action
        self.condition = condition or noop
        self.on_enter = enter or noop
        self.on_exit = exit or noop
        self.on_before = before or noop
        self.on_after = after or noop

    def __call__(self, func, *args, **kwargs):
        if func is not None:
            functools.update_wrapper(self, func)
            LOGGER.debug('Creating transition for callable "{}"'.format(func.__name__))
        return self.update(action=func)

    def __get__(self, instance, owner=None):
        if instance is None:
            return self
        return self.wrapper(instance)

    @property
    def name(self):
        return self.action.__name__ if self.action else ''

    def update(self, action=None, condition=None, enter=None, exit=None, before=None, after=None):
        """
        Create a new :class:`~decorstate.Transition` instance composed of the updated transition functions.
        """
        return type(self)(current_state=self.current_state,
                          next_state=self.next_state,
                          action=action or self.action,
                          condition=condition or self.condition,
                          enter=enter or self.on_enter,
                          exit=exit or self.on_exit,
                          before=before or self.on_before,
                          after=after or self.on_after)

    def guard(self, func):
        """
        Wrapped function which is called to determine if the state machine should perform this transition.
        """
        LOGGER.debug('Registering guard callable for "{}" transition'.format(self.name))
        return self.update(condition=func)

    def enter(self, func):
        """
        Wrapped function called when transition into a new state.
        """
        LOGGER.debug('Registering enter callable for "{}" transition'.format(self.name))
        return self.update(enter=func)

    def exit(self, func):
        """
        Wrapped function called when transitioning out of the current state and to a new one.
        """
        LOGGER.debug('Registering exit callable for "{}" transition'.format(self.name))
        return self.update(exit=func)

    def before(self, func):
        """
        Wrapped function called before executing the transition action.
        """
        LOGGER.debug('Registering before callable for "{}" transition'.format(self.name))
        return self.update(before=func)

    def after(self, func):
        """
        Wrapped function called after executing the transaction action.
        """
        LOGGER.debug('Registering after callable for "{}" transition'.format(self.name))
        return self.update(after=func)

    @contextlib.contextmanager
    def transition_from(self, machine, *args, **kwargs):
        """
        Perform process of transitioning out of the current state.
        """
        with machine.transition_event:
            if machine.transition:
                machine.transition.on_exit(machine, *args, **kwargs)
            yield machine
            machine.transition = self
            machine.state = self.next_state

    @contextlib.contextmanager
    def transition_into(self, machine, *args, **kwargs):
        """
        Perform process of transitioning from the current state to a new one.

        This means performing all exit handling of the previous transition as well as the enter handling
        and action of the new transition.
        """
        # Perform exit process of the current transition before starting the enter process of the next.
        with self.transition_from(machine, *args, **kwargs) as machine:
            try:
                self.on_before(machine, *args, **kwargs)
                yield self
            except:
                raise
            finally:
                self.on_after(machine, *args, **kwargs)

    def wrapper(self, machine):
        """
        Returns a function which executes the entire flow of a transition.

        :param machine: Instance of the object whose functions are decorated with :func:`~decorstate.transition`.
        """
        def decorator(*args, **kwargs):
            """
            Decorator which encapsulates execution of a single transition.
            """
            state = machine.state

            # Do not perform transition if the current state of the machine
            # and the required current state of the transition do not match.
            if state not in iterable(self.current_state):
                LOGGER.debug('Transition not supported from state "{}"'.format(state))
                return state

            # Ignore transitions whose guard conditions fail to return True.
            if self.condition and not bool(self.condition(machine, *args, **kwargs)):
                LOGGER.debug('Guard condition failed for transition from state "{}"'.format(state))
                return state

            LOGGER.debug('Transitioning from "{}" to "{}"'.format(state, self.next_state))

            # Perform the transition from the previous state to new.
            # This includes calling performing the exit phase of the previous state/transition
            # and the enter/action of the new one.
            with self.transition_into(machine, *args, **kwargs) as new:
                new.action(machine, *args, **kwargs)
                return new.next_state

        # Attach descriptor and helper functions to the decorator for easy use.
        decorator = functools.update_wrapper(decorator, self.action)
        decorator.wait = functools.partial(wait_for_state, machine, self.next_state)
        decorator.transition = self

        # Set default values on the instance that is using this descriptors if they don't already have them set.
        set_attr_default(machine, 'state', self.default_state)
        set_attr_default(machine, 'transition', self.default_transition)
        set_attr_default(machine, 'transition_event', self.default_transition_event)

        return decorator

transition = Transition

def wait_for_state(machine, state, timeout=None):
    """
    Wait until the given machine transitions into a specific state.

    :param machine: Machine to wait for a transition into a specific state
    :param state: State to wait for the machine to transition into
    :param timeout: Timeout in seconds to wait for transition; default: None
    """
    end = None
    remaining = timeout

    if not initialized(machine):
        return False

    with machine.transition_event:
        while machine.state != state:
            if timeout is not None:
                if end is None:
                    end = time.time() + timeout
                else:
                    remaining = end - time.time()
                    if remaining <= 0:
                        return False
            machine.transition_event.wait(remaining)

    return True

def set_attr_default(obj, name, default=None):
    """
    Set the value of an attribute on the given object if it isn't yet defined.
    """
    if not hasattr(obj, name):
        setattr(obj, name, default)

def initialized(machine):
    """
    Check to see if the given machine is initialized.

    :param machine: Machine to check to see if default attributes are set
    :return: `True` if the machine has its attributes set, `False` otherwise
    """
    return all(hasattr(machine, attr) for attr in ('state', 'transition', 'transition_event'))
<|endoftext|>


<commit_before>import argparse, os, re
from jinja2 import Environment, PackageLoader
from yaml import load, dump, Loader
from uuid import uuid4

def get_config():
    parser = argparse.ArgumentParser(description='Construct Delphi classes from a YAML template.')
    parser.add_argument('source_files', metavar='SOURCE', type=file, nargs='+',
                        help='A YAML template file from which Delphi source will be generated')
    return parser.parse_args()

def split_on_space_and_upper(target):
    return re.sub(r"([A-Z])", r" \1", target['name']).split()

def augment_name(target):
    if 'name' in target:
        split_name = split_on_space_and_upper(target)
        target['name_camelcase'] = split_name[0] + ''.join(x.title() for x in split_name[1:])
        target['name_titlecase'] = ''.join(x.title() for x in split_name)
        target['name_snakecase'] = '_'.join(x.lower() for x in split_name)

def augment_names(data):
    if 'name' in data['type']:
        augment_name(data['type'])
    if 'requirements' in data['type']:
        for requirement in data['type']['requirements']:
            augment_name(requirement)
    if 'variables' in data['type']:
        for variable in data['type']['variables']:
            augment_name(variable)

def augment_uuids(data):
    data['type']['uuid'] = str(uuid4()).upper()

def augment_data(data):
    if 'type' in data:
        augment_uuids(data)
        augment_names(data)

def expand_template(config, template, template_file_name, type_data):
    with open(template_file_name % type_data, 'wb') as dest_file:
        dest_file.write(template.render(type_data))

def expand_templates(config, env, source_file):
    data = load(source_file, Loader=Loader)
    augment_data(data)
    for template_file_name in os.listdir('templates'):
        if template_file_name[-4:] == '.pas':
            augment_uuids(data)
            expand_template(config, env.get_template(template_file_name), template_file_name, data['type'])

def main():
    config = get_config()
    env = Environment(lstrip_blocks=True, trim_blocks=True, loader=PackageLoader('delphi-395', 'templates'))
    for source_file in config.source_files:
        expand_templates(config, env, source_file)

if __name__ == "__main__":
    main()<commit_msg>Names can be split on underscore<commit_after>import argparse, os, re
from jinja2 import Environment, PackageLoader
from yaml import load, dump, Loader
from uuid import uuid4

def get_config():
    parser = argparse.ArgumentParser(description='Construct Delphi classes from a YAML template.')
    parser.add_argument('source_files', metavar='SOURCE', type=file, nargs='+',
                        help='A YAML template file from which Delphi source will be generated')
    return parser.parse_args()

def split_on_space_underscore_and_upper(name):
    return ((re.sub(r"([A-Z])", r" \1", name)).replace('_', ' ')).split()

def augment_name(target):
    if 'name' in target:
        split_name = split_on_space_underscore_and_upper(target['name'])
        target['name_camelcase'] = split_name[0] + ''.join(x.title() for x in split_name[1:])
        target['name_titlecase'] = ''.join(x.title() for x in split_name)
        target['name_snakecase'] = '_'.join(x.lower() for x in split_name)

def augment_names(data):
    if 'name' in data['type']:
        augment_name(data['type'])
    if 'requirements' in data['type']:
        for requirement in data['type']['requirements']:
            augment_name(requirement)
    if 'variables' in data['type']:
        for variable in data['type']['variables']:
            augment_name(variable)

def augment_uuids(data):
    data['type']['uuid'] = str(uuid4()).upper()

def augment_data(data):
    if 'type' in data:
        augment_uuids(data)
        augment_names(data)

def expand_template(config, template, template_file_name, type_data):
    with open(template_file_name % type_data, 'wb') as dest_file:
        dest_file.write(template.render(type_data))

def expand_templates(config, env, source_file):
    data = load(source_file, Loader=Loader)
    augment_data(data)
    for template_file_name in os.listdir('templates'):
        if template_file_name[-4:] == '.pas':
            augment_uuids(data)
            expand_template(config, env.get_template(template_file_name), template_file_name, data['type'])

def main():
    config = get_config()
    env = Environment(lstrip_blocks=True, trim_blocks=True, loader=PackageLoader('delphi-395', 'templates'))
    for source_file in config.source_files:
        expand_templates(config, env, source_file)

if __name__ == "__main__":
    main()<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import os
import pytest
from koheron_tcp_client import KClient
from ..utilities import HTTPInterface
from ..drivers import Oscillo

@pytest.mark.real
class TestConnection:

    def test(self):
        current_path = os.getcwd()
        host = os.getenv('HOST','192.168.1.100')
        http = HTTPInterface(host)
        http.install_instrument('oscillo')
        client = KClient(host)
        assert client.is_connected
        driver = Oscillo(client)
        current = 30 #mA
        driver.set_laser_current(current)
        driver.start_laser()
        assert driver.get_laser_power() > 400
<commit_msg>Update test_connection.py<commit_after># -*- coding: utf-8 -*-

import os
import pytest
from koheron_tcp_client import KClient
from ..utilities import install_instrument
from ..drivers import Oscillo

@pytest.mark.real
class TestConnection:

    def test(self):
        current_path = os.getcwd()
        host = os.getenv('HOST','192.168.1.100')
        install_instrument(host,'oscillo')
        client = KClient(host)
        assert client.is_connected
        driver = Oscillo(client)
        current = 30 #mA
        driver.set_laser_current(current)
        driver.start_laser()
        assert driver.get_laser_power() > 400
<|endoftext|>


<commit_before>from learntools.core import *

import pandas as pd
import geopandas as gpd

congrats_map_completion = "Thank you for creating a map!"
correct_message_map_completion = ""

# Q1
birds_df = pd.read_csv("../input/geospatial-learn-course-data/purple_martin.csv", parse_dates=['timestamp'])
birds = gpd.GeoDataFrame(birds_df, geometry=gpd.points_from_xy(birds_df["location-long"], birds_df["location-lat"]))
birds.crs = {'init' :'epsg:4326'}

# Q3
end_df = birds.groupby("tag-local-identifier")['geometry'].apply(list).apply(lambda x: x[-1]).reset_index()
end_gdf = gpd.GeoDataFrame(end_df, geometry=end_df.geometry)
end_gdf.crs = {'init' :'epsg:4326'}

# Q5
protected_filepath = "../input/geospatial-learn-course-data/SAPA_Aug2019-shapefile/SAPA_Aug2019-shapefile/SAPA_Aug2019-shapefile-polygons.shp"
protected_areas = gpd.read_file(protected_filepath)

# Q7
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
americas = world.loc[world['continent'].isin(['North America', 'South America'])]
south_america = americas.loc[americas['continent']=='South America']
totalArea = sum(south_america.geometry.to_crs(epsg=3035).area) / 10**6

class Q1(EqualityCheckProblem):
    _var = "birds"
    _expected = birds
    _hint = ("Use `gpd.GeoDataFrame()`, and use `gpd.points_from_xy()` to create `Point` objects "
        "from the latitude and longitude locations.")
    _solution = CS(
"""# Create the GeoDataFrame
birds = gpd.GeoDataFrame(birds_df, geometry=gpd.points_from_xy(birds_df["location-long"], birds_df["location-lat"]))

# Set the CRS to {'init': 'epsg:4326'}
birds.crs = {'init' :'epsg:4326'}
""")

class Q2(CodingProblem):
    _hint = "Use the `plot()` method of each GeoDataFrame."
    _solution = CS(
"""ax = americas.plot(figsize=(10,10), color='white', linestyle=':', edgecolor='gray')
birds.plot(ax=ax, markersize=10)

# Uncomment to zoom in
#ax.set_xlim([-110, -30])
#ax.set_ylim([-30, 60])
""")
    _congrats = congrats_map_completion
    _correct_message = correct_message_map_completion
    def check(self):
        pass

class Q3(CodingProblem):
    _var = "end_gdf"
    _hint = "The code that you need to write is very similar to the code that was used to create `start_gdf`."
    _solution = CS(
"""end_df = birds.groupby("tag-local-identifier")['geometry'].apply(list).apply(lambda x: x[-1]).reset_index()
end_gdf = gpd.GeoDataFrame(end_df, geometry=end_df.geometry)
end_gdf.crs = {'init': 'epsg:4326'}
""")
    def check(self, results):
        assert type(results) == gpd.geodataframe.GeoDataFrame, \
        "`end_gdf` should be a GeoDataFrame."
        assert results.crs == {'init': 'epsg:4326'}, \
        "Don't forget to set the CRS to `{'init': 'epsg:4326'}`."
        sorted_end_gdf = end_gdf.sort_values(by='tag-local-identifier')
        sorted_results = results.sort_values(by='tag-local-identifier')
        assert sorted_end_gdf.equals(sorted_results), \
        "The results don't look right.  Please try again."

class Q4(CodingProblem):
    _hint = "Use the `plot()` method of each GeoDataFrame."
    _solution = CS(
"""ax = americas.plot(figsize=(10, 10), color='white', linestyle=':', edgecolor='gray')

start_gdf.plot(ax=ax, color='red',  markersize=30)
path_gdf.plot(ax=ax, cmap='tab20b', linestyle='-', linewidth=1, zorder=1)
end_gdf.plot(ax=ax, color='black', markersize=30)
""")
    _congrats = congrats_map_completion
    _correct_message = correct_message_map_completion
    def check(self):
        pass

class Q5(EqualityCheckProblem):
    _var = "protected_areas"
    _expected = protected_areas
    _hint = ("Use `gpd.read_file()`.")
    _solution = CS(
"""protected_areas = gpd.read_file(protected_filepath)
""")

class Q6(CodingProblem):
    _hint = ("Use the `plot()` method of the `south_america` and `protected_areas` GeoDataFrames.")
    _solution = CS(
"""# Plot protected areas in South America
ax = south_america.plot(figsize=(10,10), color='white', edgecolor='gray')
protected_areas.plot(ax=ax, alpha=0.4)
""")
    _congrats = congrats_map_completion
    _correct_message = correct_message_map_completion
    def check(self):
        pass

class Q7(EqualityCheckProblem):
    _var = "totalArea"
    _expected = totalArea
    _hint = ("Use the `to_crs()` method to change the CRS to EPSG 3035.")
    _solution = CS(
"""totalArea = sum(south_america.geometry.to_crs(epsg=3035).area) / 10**6
""")

class Q8(CodingProblem):
    _hint = "When in South America, the birds are south of the equator."
    _solution = CS(
"""ax = south_america.plot(figsize=(10,10), color='white', edgecolor='gray')
protected_areas[protected_areas['MARINE']!='2'].plot(ax=ax, alpha=0.4, zorder=1)
birds[birds.geometry.y < 0].plot(ax=ax, color='red', alpha=0.6, markersize=10, zorder=2)
""")
    _congrats = congrats_map_completion
    _correct_message = correct_message_map_completion
    def check(self):
        pass

qvars = bind_exercises(globals(), [
    Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8
    ],
    var_format='q_{n}',
    )
__all__ = list(qvars)
<commit_msg>Update ex2.py<commit_after>from learntools.core import *

import pandas as pd
import geopandas as gpd

congrats_map_completion = "Thank you for creating a map!"
correct_message_map_completion = ""

def check_gdf_equal(gdf1, gdf2, name):
    assert type(gdf1)==gpd.geodataframe.GeoDataFrame, "`{}` is not a GeoDataFrame.".format(name)
    df1 = pd.DataFrame(gdf1.drop(columns='geometry'))
    df2 = pd.DataFrame(gdf2.drop(columns='geometry'))
    assert df1.equals(df2), "The results don't look right.  Try again."
    geom1 = gdf1.geometry
    geom2 = gdf2.geometry
    assert geom1.equals(geom2).all(), "The results don't look right.  Try again."

# Q1
birds_df = pd.read_csv("../input/geospatial-learn-course-data/purple_martin.csv", parse_dates=['timestamp'])
birds = gpd.GeoDataFrame(birds_df, geometry=gpd.points_from_xy(birds_df["location-long"], birds_df["location-lat"]))
birds.crs = {'init' :'epsg:4326'}

# Q3
end_df = birds.groupby("tag-local-identifier")['geometry'].apply(list).apply(lambda x: x[-1]).reset_index()
end_gdf = gpd.GeoDataFrame(end_df, geometry=end_df.geometry)
end_gdf.crs = {'init' :'epsg:4326'}

# Q5
protected_filepath = "../input/geospatial-learn-course-data/SAPA_Aug2019-shapefile/SAPA_Aug2019-shapefile/SAPA_Aug2019-shapefile-polygons.shp"
protected_areas = gpd.read_file(protected_filepath)

# Q7
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
americas = world.loc[world['continent'].isin(['North America', 'South America'])]
south_america = americas.loc[americas['continent']=='South America']
totalArea = sum(south_america.geometry.to_crs(epsg=3035).area) / 10**6

class Q1(CodingProblem):
    _var = "birds"
    _hint = ("Use `gpd.GeoDataFrame()`, and use `gpd.points_from_xy()` to create `Point` objects "
        "from the latitude and longitude locations.")
    _solution = CS(
"""# Create the GeoDataFrame
birds = gpd.GeoDataFrame(birds_df, geometry=gpd.points_from_xy(birds_df["location-long"], birds_df["location-lat"]))

# Set the CRS to {'init': 'epsg:4326'}
birds.crs = {'init' :'epsg:4326'}
""")
    def check(self, birds_submit):
        check_gdf_equal(birds_submit, birds, "birds")

class Q2(CodingProblem):
    _hint = "Use the `plot()` method of each GeoDataFrame."
    _solution = CS(
"""ax = americas.plot(figsize=(10,10), color='white', linestyle=':', edgecolor='gray')
birds.plot(ax=ax, markersize=10)

# Uncomment to zoom in
#ax.set_xlim([-110, -30])
#ax.set_ylim([-30, 60])
""")
    _congrats = congrats_map_completion
    _correct_message = correct_message_map_completion
    def check(self):
        pass

class Q3(CodingProblem):
    _var = "end_gdf"
    _hint = "The code that you need to write is very similar to the code that was used to create `start_gdf`."
    _solution = CS(
"""end_df = birds.groupby("tag-local-identifier")['geometry'].apply(list).apply(lambda x: x[-1]).reset_index()
end_gdf = gpd.GeoDataFrame(end_df, geometry=end_df.geometry)
end_gdf.crs = {'init': 'epsg:4326'}
""")
    def check(self, results):
        assert type(results) == gpd.geodataframe.GeoDataFrame, \
        "`end_gdf` should be a GeoDataFrame."
        assert results.crs == {'init': 'epsg:4326'}, \
        "Don't forget to set the CRS to `{'init': 'epsg:4326'}`."
        sorted_end_gdf = end_gdf.sort_values(by='tag-local-identifier')
        sorted_results = results.sort_values(by='tag-local-identifier')
        assert sorted_end_gdf.equals(sorted_results), \
        "The results don't look right.  Please try again."

class Q4(CodingProblem):
    _hint = "Use the `plot()` method of each GeoDataFrame."
    _solution = CS(
"""ax = americas.plot(figsize=(10, 10), color='white', linestyle=':', edgecolor='gray')

start_gdf.plot(ax=ax, color='red',  markersize=30)
path_gdf.plot(ax=ax, cmap='tab20b', linestyle='-', linewidth=1, zorder=1)
end_gdf.plot(ax=ax, color='black', markersize=30)
""")
    _congrats = congrats_map_completion
    _correct_message = correct_message_map_completion
    def check(self):
        pass

class Q5(CodingProblem):
    _var = "protected_areas"
    _hint = ("Use `gpd.read_file()`.")
    _solution = CS(
"""protected_areas = gpd.read_file(protected_filepath)
""")
    def check(self, protected_areas_submit):
        check_gdf_equal(protected_areas_submit, protected_areas, "protected_areas")

class Q6(CodingProblem):
    _hint = ("Use the `plot()` method of the `south_america` and `protected_areas` GeoDataFrames.")
    _solution = CS(
"""# Plot protected areas in South America
ax = south_america.plot(figsize=(10,10), color='white', edgecolor='gray')
protected_areas.plot(ax=ax, alpha=0.4)
""")
    _congrats = congrats_map_completion
    _correct_message = correct_message_map_completion
    def check(self):
        pass

class Q7(CodingProblem):
    _var = "totalArea"
    _hint = ("Use the `to_crs()` method to change the CRS to EPSG 3035.")
    _solution = CS(
"""totalArea = sum(south_america.geometry.to_crs(epsg=3035).area) / 10**6
""")
    def check(self, totalArea_submit):
        check_gdf_equal(totalArea_submit, totalArea, "totalArea")

class Q8(CodingProblem):
    _hint = "When in South America, the birds are south of the equator."
    _solution = CS(
"""ax = south_america.plot(figsize=(10,10), color='white', edgecolor='gray')
protected_areas[protected_areas['MARINE']!='2'].plot(ax=ax, alpha=0.4, zorder=1)
birds[birds.geometry.y < 0].plot(ax=ax, color='red', alpha=0.6, markersize=10, zorder=2)
""")
    _congrats = congrats_map_completion
    _correct_message = correct_message_map_completion
    def check(self):
        pass

qvars = bind_exercises(globals(), [
    Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8
    ],
    var_format='q_{n}',
    )
__all__ = list(qvars)
<|endoftext|>