requestHandler.py
Go to the documentation of this file.
00001 # MIT License
00002 #
00003 # Copyright (c) <2015> <Ikergune, Etxetar>
00004 #
00005 # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
00006 # (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
00007 # publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
00008 # subject to the following conditions:
00009 #
00010 # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
00011 #
00012 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00013 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
00014 # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00015 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00016 
00017 import re
00018 import cgi
00019 import json
00020 from urlparse import urlparse, parse_qs
00021 from BaseHTTPServer import BaseHTTPRequestHandler
00022 
00023 from include.logger import Log
00024 from include.constants import SEPARATOR_CHAR, DEFAULT_CONTEXT_TYPE
00025 from include.confManager import getRobots
00026 from include.ros.rosutils import ros2Definition
00027 from include.ros.rosConfigurator import RosConfigurator, setWhiteList
00028 from include.ros.topicHandler import TopicHandler, loadMsgHandlers, ROBOT_TOPICS
00029 from include.pubsub.pubSubFactory import SubscriberFactory, QueryBuilderFactory
00030 
00031 CloudSubscriber = SubscriberFactory.create()
00032 CloudQueryBulder = QueryBuilderFactory.create()
00033 
00034 TOPIC_TIMESTAMPS = {}
00035 
00036 
00037 class RequestHandler(BaseHTTPRequestHandler):
00038     ## \brief FIROS http request handler
00039     def do_GET(self):
00040         ## \brief GET request handler
00041         # \param self
00042         path = urlparse("http://localhost" + self.path).path
00043         action = getAction(path, "GET")
00044         if action is not None:
00045             action["action"](self, action)
00046         else:
00047             self.send_response(200)
00048             self.send_header('Content-type', 'text/html')
00049             self.end_headers()
00050             self.wfile.write("GENERIC PAGE")
00051         return
00052 
00053     def do_POST(self):
00054         ## \brief POST request handler
00055         # \param self
00056         path = urlparse("http://localhost" + self.path).path
00057         action = getAction(path, "POST")
00058         if action is not None:
00059             action["action"](self, action)
00060         else:
00061             self.send_response(200)
00062             self.send_header('Content-type', 'text/html')
00063             self.end_headers()
00064             self.wfile.write("GENERIC PAGE")
00065         return
00066 
00067 
00068 def pathParams(request, regexp):
00069     ## \brief Get url path params
00070     # \param client request to parse
00071     # \param regexp to use
00072     return list(re.match(regexp, urlparse("http://localhost" + request.path).path).groups())
00073 
00074 
00075 def getParams(request):
00076     ## \brief GET requests' param parser
00077     # \param client request
00078     return parse_qs(urlparse("http://localhost" + request.path).query)
00079 
00080 
00081 def postParams(request):
00082     ## \brief POST requests' param parser
00083     # \param client request
00084     ctype, pdict = cgi.parse_header(request.headers.getheader('content-type'))
00085     if ctype == 'multipart/form-data':
00086         return cgi.parse_multipart(request.rfile, pdict)
00087     elif ctype == 'application/x-www-form-urlencoded':
00088         length = int(request.headers.getheader('content-length'))
00089         return cgi.parse_qs(request.rfile.read(length), keep_blank_values=1)
00090     elif ctype == 'application/json':
00091         json_data = request.rfile.read(int(request.headers['Content-Length']))
00092         return json.loads(json_data)
00093     else:
00094         return {}
00095 
00096 
00097 def getAction(path, method):
00098     ## \brief URL checker to find what action to execute
00099     # \param url string
00100     # \param HTTP method
00101     for route in MAPPER[method]:
00102         if re.search(route['regexp'], path):
00103             return route
00104     return None
00105 
00106 ###############################################################################
00107 #############################   Request Mapping   #############################
00108 ###############################################################################
00109 
00110 
00111 def onTopic(request, action):
00112     ## \brief Handle topic reception
00113     # \param client request
00114     # \param action
00115     try:
00116         contexts = postParams(request)
00117         contexts = contexts['contextResponses']
00118         for context in contexts:
00119             if context['statusCode']['code'] == "200":
00120                 robot = context['contextElement']
00121                 robotName = robot['id']
00122                 if robotName not in TOPIC_TIMESTAMPS:
00123                     TOPIC_TIMESTAMPS[robotName] = {}
00124                 for topic in robot['attributes']:
00125                     if topic["name"] == "COMMAND":
00126                         commands = topic["value"]
00127                         robot['attributes'].remove(topic)
00128                         break
00129                 for topic in robot['attributes']:
00130                     if topic["name"] != "descriptions" and topic["name"] in commands:
00131                         value = CloudSubscriber.parseData(topic['value'])
00132                         if (topic["name"] not in TOPIC_TIMESTAMPS[robotName]) or ("firosstamp" in value and TOPIC_TIMESTAMPS[robotName][topic["name"]] != value["firosstamp"]) or ("firosstamp" not in value):
00133                             if "firosstamp" in value:
00134                                 TOPIC_TIMESTAMPS[robotName][topic["name"]] = value["firosstamp"]
00135                                 # value.remove(value["firosstamp"])
00136                             TopicHandler.publish(robotName, topic['name'], value)
00137     except Exception as e:
00138         Log("ERROR", e)
00139     request.send_response(200)
00140     request.send_header('Content-type', 'text/plain')
00141     request.end_headers()
00142     request.wfile.write("Received by firos")
00143 
00144 
00145 def onRobots(request, action):
00146     ## \brief Handle robot list request
00147     # \param client request
00148     # \param action
00149     robots = getRobots(False, True)
00150     data = []
00151     for robot_name in robots.keys():
00152         robot_data = {"name": robot_name, "topics": []}
00153         robot = robots[robot_name]
00154         for topic_name in robot["topics"]:
00155             topic = robot["topics"][topic_name]
00156             topic_data = {
00157                 "name": topic_name,
00158                 "pubsub": topic["type"]
00159             }
00160             if type(topic["msg"]) is dict:
00161                 topic_data["type"] = "Custom"
00162                 topic_data["structure"] = topic["msg"]
00163             else:
00164                 topic_data["type"] = topic["msg"]
00165                 topic_data["structure"] = ros2Definition(ROBOT_TOPICS[robot_name][topic["type"]][topic_name]["class"]())
00166             robot_data["topics"].append(topic_data)
00167         data.append(robot_data)
00168     request.send_response(200)
00169     request.send_header('Content-type', 'application/json')
00170     setCors(request)
00171     request.end_headers()
00172     request.wfile.write(json.dumps(data))
00173 
00174 
00175 def onRobotData(request, action):
00176     ## \brief Handle robot info request
00177     # \param client request
00178     # \param action
00179     robot_name = pathParams(request, action["regexp"])[0]
00180     data = CloudQueryBulder.findById(robot_name, "ROBOT", False)
00181     if "errorCode" in data:
00182         request.send_response(int(data["errorCode"]["code"]))
00183     else:
00184         request.send_response(200)
00185         robot_list = []
00186         for context in data["contextResponses"]:
00187             for attribute in context["contextElement"]["attributes"]:
00188                 if attribute["name"] != "COMMAND" and attribute["name"] != "descriptions":
00189                     print attribute["name"]
00190                     print attribute["value"]
00191                     attribute["value"] = json.loads(attribute["value"].replace(SEPARATOR_CHAR, '"'))
00192             context["contextElement"].pop("isPattern", None)
00193             robot_list.append(context["contextElement"])
00194         data = robot_list
00195 
00196     request.send_header('Content-type', 'application/json')
00197     setCors(request)
00198 
00199     request.end_headers()
00200     request.wfile.write(json.dumps(data))
00201 
00202 
00203 def onConnect(request, action):
00204     ## \brief Launch robot search and connection
00205     # \param client request
00206     # \param action
00207     Log("INFO", "Connecting robots")
00208     loadMsgHandlers(RosConfigurator.systemTopics(True))
00209     request.send_response(200)
00210     request.end_headers()
00211     request.wfile.write("")
00212 
00213 
00214 def onDisConnect(request, action):
00215     ## \brief Disconnect robot
00216     # \param client request
00217     # \param action
00218     robot_name = pathParams(request, action["regexp"])[0]
00219     Log("INFO", "Disconnecting robot" + robot_name)
00220     if robot_name in ROBOT_TOPICS:
00221         CloudSubscriber.deleteEntity(robot_name, DEFAULT_CONTEXT_TYPE)
00222         CloudSubscriber.disconnect(robot_name, True)
00223         for topic in ROBOT_TOPICS[robot_name]["publisher"]:
00224             ROBOT_TOPICS[robot_name]["publisher"][topic]["publisher"].unregister()
00225         for topic in ROBOT_TOPICS[robot_name]["subscriber"]:
00226             ROBOT_TOPICS[robot_name]["subscriber"][topic]["subscriber"].unregister()
00227         RosConfigurator.removeRobot(robot_name)
00228     request.send_response(200)
00229     request.end_headers()
00230     request.wfile.write("")
00231 
00232 
00233 def onWhitelistWrite(request, action):
00234     ## \brief Handle robot info request
00235     # \param client request
00236     # \param action
00237     data = postParams(request)
00238     setWhiteList(data, None)
00239     request.send_response(200)
00240     request.end_headers()
00241     request.wfile.write("")
00242 
00243 
00244 def onWhitelistRemove(request, action):
00245     ## \brief Handle robot info request
00246     # \param client request
00247     # \param action
00248     data = postParams(request)
00249     setWhiteList(None, data)
00250     request.send_response(200)
00251     request.end_headers()
00252     request.wfile.write("")
00253 
00254 
00255 def onWhitelistRestore(request, action):
00256     ## \brief Handle robot info request
00257     # \param client request
00258     # \param action
00259     setWhiteList(None, None, True)
00260     request.send_response(200)
00261     request.end_headers()
00262     request.wfile.write("")
00263 
00264 
00265 # URL structure
00266 # ^/firos/(\w+)/update/*$
00267 # ^/TEXT/whatever_is_inside/TEXT/+$
00268 
00269 MAPPER = {
00270     "GET": [
00271         {"regexp": "^/robots/*$", "action": onRobots},
00272         {"regexp": "^/robot/(\w+)/*$", "action": onRobotData}],
00273     "POST": [
00274         {"regexp": "^/firos/*$", "action": onTopic},
00275         {"regexp": "^/robot/connect/*$", "action": onConnect},
00276         {"regexp": "^/robot/disconnect/(\w+)/*$", "action": onDisConnect},
00277         {"regexp": "^/whitelist/write/*$", "action": onWhitelistWrite},
00278         {"regexp": "^/whitelist/remove/*$", "action": onWhitelistRemove},
00279         {"regexp": "^/whitelist/restore/*$", "action": onWhitelistRestore}],
00280     "PUT": [],
00281     "DELETE": [],
00282 }
00283 
00284 
00285 def setCors(request):
00286     # \brief Set CORS headers in request
00287     # \param client request
00288     request.send_header("Access-Control-Allow-Credentials", True)
00289     request.send_header("Access-Control-Allow-Headers", "api-version, content-length, content-md5, content-type, date, request-id, response-time")
00290     request.send_header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE")
00291     request.send_header("Access-Control-Expose-Headers", "api-version, content-length, content-md5, content-type, date, request-id, response-time")
00292     request.send_header("Access-Control-Allow-Origin", "*")


firos
Author(s): IƱigo Gonzalez, igonzalez@ikergune.com
autogenerated on Thu Jun 6 2019 17:51:04