Skip to content

Commit

Permalink
added rest server connector
Browse files Browse the repository at this point in the history
  • Loading branch information
andrea-83 committed Feb 12, 2016
1 parent 88abbff commit 252647f
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.log
*.pyc
.DS_Store
13 changes: 13 additions & 0 deletions ciao/conf/rest.json.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name" : "rest",
"enabled": false,
"type" : "managed",
"commands": {
"start": ["/usr/lib/python2.7/ciao/connectors/rest/rest.py"],
"stop": ["/usr/bin/killall","-s", "HUP","rest.py"]
},
"implements" : {
"read" : { "direction": "in", "has_params": false },
"writeresponse" : { "direction": "out", "has_params": true }
}
}
14 changes: 14 additions & 0 deletions ciao/connectors/rest/rest.json.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name" : "rest",
"description" : "REST connector for the Ciao Core",
"version" : "0.0.1",
"ciao": {
"host" : "127.0.0.1",
"port" : 8900
},
"params" : {
"host" : "",
"port" : 5555,
"timeout" : 1
}
}
138 changes: 138 additions & 0 deletions ciao/connectors/rest/rest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/python -u
###
# This file is part of Arduino Ciao
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Copyright 2015 Arduino Srl (http://www.arduino.org/)
#
# authors:
# _andrea[at]arduino[dot]org
# _giuseppe[at]arduino[dot]org
#
#
###

import os, sys, signal, asyncore, socket, time
from thread import *
import json, logging
from Queue import Queue
import ciaotools

from restciao import RESTCiao

# function to handle SIGHUP/SIGTERM

def restserver_handler(conn, shd,logger):

message = conn.recv(1024)
logger.debug("Message %s" % message)
if message != "" :
entry = {"data" : [str(message).rstrip('\r\n')]}
socket_queue.put(entry)

timeout = time.time() + shd["conf"]["params"]["timeout"] # 1 seconds response timeout
while time.time() <= timeout:
if not rest_queue.empty():
entry = rest_queue.get()
if entry['type'] == "response":
original_checksum = entry["source_checksum"]
if not original_checksum in shd["requests"]:
logger.warning("Received response to unknown checksum %s" % original_checksum)
original_message = shd["requests"][original_checksum]
reply = str(entry['data'][0])
logger.debug("data send %s" % reply)
conn.send(reply)
break
conn.send('\r\n')
conn.close()

def signal_handler(signum, frame):
global logger
logger.info("SIGNAL CATCHED %d" % signum)
global shd
shd["loop"] = False

#shared dictionary
shd = {}
shd["loop"] = True
shd["basepath"] = os.path.dirname(os.path.abspath(__file__)) + os.sep


#read configuration
#TODO
# verify configuration is a valid JSON
json_conf = open(shd["basepath"]+"rest.json.conf").read()
shd["conf"] = json.loads(json_conf)
#init log

logger = ciaotools.get_logger("rest", logconf=shd["conf"], logdir=shd["basepath"])

#forking to make process standalone
try:
pid = os.fork()
if pid > 0:
# Save child pid to file and exit parent process
runfile = open("/var/run/rest-ciao.pid", "w")
runfile.write("%d" % pid)
runfile.close()
sys.exit(0)

except OSError, e:
logger.critical("Fork failed")
sys.exit(1)

rest_queue = Queue()
socket_queue = Queue()

signal.signal(signal.SIGINT, signal.SIG_IGN) #ignore SIGINT(ctrl+c)
signal.signal(signal.SIGHUP, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)

shd["requests"] = {}

ciaoclient = RESTCiao(shd, rest_queue, socket_queue)
ciaoclient.start()

try:
HOST = shd["conf"]["params"]["host"]
PORT = shd["conf"]["params"]["port"] # Luci port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
s.bind((HOST, PORT))
except socket.error as msg:
logger.error('Bind failed. Error Code : '+ str(msg[0]) +' Message ' + msg[1])
sys.exit()
#Start listening on socket
logger.info("REST server connector started")
s.listen(10)
while shd["loop"] :
conn, addr = s.accept()
start_new_thread(restserver_handler ,(conn, shd,logger,))
#restserver_handler (conn, shd,logger)

except Exception, e:
s.close()
logger.info("Exception while creating REST server: %s" % e)
sys.exit(1)

else:
s.close()
logger.info("REST server connector is closing")
sys.exit(0)
74 changes: 74 additions & 0 deletions ciao/connectors/rest/restciao.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
###
# This file is part of Arduino Ciao
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Copyright 2015 Arduino Srl (http://www.arduino.org/)
#
# authors:
# _andrea[at]arduino[dot]org
# _giuseppe[at]arduino[dot]org
#
###

import logging,time
import socket, asyncore
import json

from ciaotools import CiaoThread

class RESTCiao(CiaoThread):
# overriding native asyncore function to handle message received via socket
def handle_read(self):
#self.logger.debug("Handle READ")
#start = time.time()
data = self.recv(2048)
# calling decode_multiple (from ciaoThread) to handle multiple string at once from Core
for data_decoded in self.decode_multiple(data):
if "status" in data_decoded:
if self.write_pending:
self.shd["requests"][data_decoded["checksum"]] = self.data_pending
self.data_pending = None
self.write_pending = False
else:
self.logger.warning("result msg but not write_pending: %s" % data)
else:
self.connector_queue.put(data_decoded)
#roundtrip = time.time() - start
#self.logger.debug("time read: %s" % roundtrip)

# writable/handle_write are function useful ONLY
# if the connector offers communication from OUTSIDE WORLD to MCU
def writable(self):
if not self.shd["loop"]:
raise asyncore.ExitNow('Connector is quitting!')
if not self.ciao_queue.empty() and not self.write_pending:
return True
return False

def handle_write(self):
#self.logger.debug("Handle WRITE")
#start = time.time()
entry = self.ciao_queue.get()

# we wait a feedback (status + checksum) from ciao
self.write_pending = True
self.data_pending = entry
self.send(json.dumps(entry))
#roundtrip = time.time() - start
#self.logger.debug("time write: %s" % roundtrip)

0 comments on commit 252647f

Please sign in to comment.