client.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # Copyright (c) 2008, Willow Garage, Inc.
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #  * Redistributions of source code must retain the above copyright
00011 #    notice, this list of conditions and the following disclaimer.
00012 #  * Redistributions in binary form must reproduce the above
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Willow Garage, Inc. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 #
00033 # Copyright (c) 2008, Willow Garage, Inc.
00034 # Revision $Id$
00035 
00036 import roslib
00037 roslib.load_manifest('industrial_io_client')
00038 import rospy
00039 import yaml
00040 # TODO:Factor out custom serialization over socket as one of multiple
00041 # possible back-ends.
00042 import socket
00043 import struct
00044 
00045 
00046 class Client:
00047     class DeviceMessage:
00048         def __init__(self):
00049             self.network_data_table = 0
00050             self.device_offset = 0
00051             self.bit_byte_offset = 0
00052             self.value = 0
00053 
00054     class Input:
00055         def __init__(self):
00056             self.name = None
00057             self.type = None
00058             self.network_data_table_code = None
00059             self.device_offset_code = None
00060             self.bit_byte_offset_code = None
00061             self.value_code = None
00062         def __repr__(self):
00063             return self.__str__()
00064         def __str__(self):
00065             return '%s: %s %s %s %s %s'%(self.name, self.type, self.network_data_table_code, self.device_offset_code, self.bit_byte_offset_code, self.value_code)
00066 
00067     class Output:
00068         def __init__(self):
00069             pass
00070 
00071     class SocketClient:
00072         def __init__(self):
00073             self.host = None
00074             self.port = None
00075             self.socket = None
00076 
00077     def __init__(self, config_file):
00078         self.inputs = {}
00079         self.outputs = {}
00080         self.device = None
00081         self.subscribers = []
00082         self.publishers = []
00083         self.load_config(config_file)
00084 
00085     def load_config(self, config_file):
00086         try:
00087             data = yaml.load(open(config_file))
00088         except yaml.YAMLError as e:
00089             print('YAML parse error')
00090             raise
00091         if 'inputs' in data:
00092             for i in data['inputs']:
00093                 inp = self.Input()
00094                 if type(i) != dict or len(i) != 1:
00095                     raise Exception('parse error')
00096                 for k,v in i.iteritems():
00097                     inp.name = rospy.resolve_name(k)
00098                     if type(v) != dict or \
00099                        not v.has_key('type') or \
00100                        not v.has_key('transformation'):
00101                         raise Exception('parse error')
00102                     inp.type = v['type']
00103                     tx = v['transformation']
00104                     if type(tx) != dict or \
00105                        not tx.has_key('network_data_table') or \
00106                        not tx.has_key('device_offset') or \
00107                        not tx.has_key('bit_byte_offset') or \
00108                        not tx.has_key('value'):
00109                         raise Exception('parse error')
00110                     inp.network_data_table_code = tx['network_data_table']
00111                     inp.device_offset_code = tx['device_offset']
00112                     inp.bit_byte_offset_code = tx['bit_byte_offset']
00113                     inp.value_code = tx['value']
00114                     self.inputs[inp.name] = inp
00115         else:
00116             print('No inputs found in configuration; not subscribing to anything')
00117 
00118         if 'outputs' in data:
00119             raise Exception('output support not implemented')
00120         else:
00121             print('No outputs found in configuration; not publishing anything')
00122 
00123         if 'device' not in data:
00124             raise Exception('no device specified')
00125         dev = data['device']
00126         if type(dev) != dict or not dev.has_key('type'):
00127             raise Exception('parse error on device')
00128         typ = dev['type']
00129         if typ == 'generic_io_socket_server':
00130             if not dev.has_key('host') or not dev.has_key('port'):
00131                 raise Exception('parse error on device')
00132             sc = self.SocketClient()
00133             sc.host = str(dev['host'])
00134             sc.port = int(dev['port'])
00135             self.device = sc
00136         else:
00137             raise Exception('unsupported device type')
00138 
00139     def handle_ros_message(self, data):
00140         print 'Received message on topic %s (type %s)'%(data._connection_header['topic'], data._connection_header['type'])
00141         if data._connection_header['topic'] not in self.inputs:
00142             print self.inputs
00143             raise Exception('received message on unexpected topic')
00144         inp = self.inputs[data._connection_header['topic']]
00145         # Set up the context that's expected by user's transformation
00146         #   m: the incoming messsage
00147         m = data
00148         # Evaluate the user's transformation for each field and force them to integers.
00149         dev_msg = self.DeviceMessage()
00150         dev_msg.network_data_table = int(eval(str(inp.network_data_table_code)))
00151         dev_msg.device_offset = int(eval(str(inp.device_offset_code)))
00152         dev_msg.bit_byte_offset = int(eval(str(inp.bit_byte_offset_code)))
00153         dev_msg.value = int(eval(str(inp.value_code)))
00154 
00155         self.write_to_device(dev_msg)
00156 
00157     def initialize_device(self):
00158         self.device.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
00159         print 'Connecting to server at %s:%d...'%(self.device.host, self.device.port)
00160         try:
00161             self.device.socket.connect((self.device.host, self.device.port))
00162         except socket.error as e: 
00163             print 'Error connecting to server'
00164             raise
00165         print 'Connected.'
00166 
00167     def write_to_device(self, dev_msg):
00168         # Pack the data in like so (from simple_message/simple_message.h):
00169         #
00170         # <PREFIX> Not considered part of the message
00171         # int LENGTH (HEADER + DATA) in bytes
00172         #
00173         # <HEADER>
00174         # int MSG_TYPE identifies type of message (standard and robot specific values)
00175         # int COMM_TYPE identified communications type
00176         # int REPLY CODE (service reply only) reply code
00177         # <BODY>
00178         # ByteArray DATA variable length data determined by message
00179         #                    type and and communications type.
00180 
00181         # For our message, we'll have prefix + header + body of 4 integers
00182         length = 1*4+3*4+4*4;
00183         fmt_str = '<IIIIIIII'
00184         # TODO: define msg_type_io in simple_message.h
00185         msg_type_io = 20
00186         comm_type_topic = 1
00187         reply_type_invalid = 0
00188         buffer = struct.pack(fmt_str,
00189                              length,
00190                              msg_type_io,
00191                              comm_type_topic,
00192                              reply_type_invalid,
00193                              dev_msg.network_data_table,
00194                              dev_msg.device_offset,
00195                              dev_msg.bit_byte_offset,
00196                              dev_msg.value)
00197         self.device.socket.send(buffer)
00198 
00199     def read_from_device(self):
00200         pass
00201 
00202     def initialize_ros(self):
00203         for inp in self.inputs.values():
00204             type_split = inp.type.split('/')
00205             if len(type_split) != 2:
00206                 raise Exception('invalid type')
00207             module = type_split[0]
00208             roslib.load_manifest(module)
00209             module += '.msg'
00210             message = type_split[1]
00211             mod = __import__(module, globals(), locals(), [message])
00212             print("Subscribing to %s (%s)"%(inp.name, inp.type))
00213             self.subscribers.append(rospy.Subscriber(inp.name, getattr(mod, message), self.handle_ros_message))


industrial_io_client
Author(s): Brian Gerkey
autogenerated on Mon Oct 6 2014 01:03:41