Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Issue #102 #103

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
91 changes: 57 additions & 34 deletions otbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import base64
import argparse
import logging
import sys

try:
from PIL import Image
Expand Down Expand Up @@ -84,7 +85,7 @@ def __init__(self, otbox):

self.firmware_eui64_retrieval = os.path.join(os.path.dirname(__file__), 'bootloaders', 'opentestbed',
'01bsp_eui64_prog.ihex')
self.firmware_temp = os.path.join(os.path.dirname(__file__), 'bootloaders', 'opentestbed', 'firmware_mote.ihex')
self.firmware_temp_dir = os.path.join(os.path.dirname(__file__), 'bootloaders', 'opentestbed')

def bootload_mote(self, serialport, firmware_file):
return subprocess.Popen(
Expand Down Expand Up @@ -126,10 +127,19 @@ def _mqtt_handler_picturetoscreen(self, deviceType, deviceId, payload):
{{TESTBED}}/deviceType/box/deviceId/box1/cmd/picturetoscreen
'''
assert deviceType==DEVICETYPE_BOX
image = Image.open(requests.get(json.loads(payload)['url'], stream=True).raw)
image.thumbnail((480,320),Image.ANTIALIAS)
self._otbox.change_image_queue.put(image)
self._otbox.change_image_queue.join()
try:
image_url = json.loads(payload)['url']
response = requests.get(image_url, stream=True)
image = Image.open(response.raw)
except IOError:
# the image is not available now; skip this one
print('image at {0} is not available, '.format(image_url)
+ 'HTTP response code {0}'.format(response.status_code))
sys.stdout.flush()
else:
image.thumbnail((480,320),Image.ANTIALIAS)
self._otbox.change_image_queue.put(image)
self._otbox.change_image_queue.join()
return {}

def _mqtt_handler_colortoscreen(self, deviceType, deviceId, payload):
Expand Down Expand Up @@ -171,11 +181,7 @@ def bootload_mote(self, serialport, firmware_file):
stderr=subprocess.PIPE)

def on_mqtt_connect(self):
# in case of IoT-LAB mote discovery is started immediately upon `otbox.py` startup
payload_status = {
'token': 123
}
self._otbox._mqtt_handler_discovermotes('box', 'all', json.dumps(payload_status))
pass

class WilabTestbed(Testbed):

Expand All @@ -202,11 +208,7 @@ def bootload_mote(self, serialport, firmware_file):
return subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

def on_mqtt_connect(self):
# in case of WiLab mote discovery is started immediately upon `otbox.py` startup
payload_status = {
'token': 123
}
self._otbox._mqtt_handler_discovermotes('box', 'all', json.dumps(payload_status))
pass


AVAILABLE_TESTBEDS = {
Expand Down Expand Up @@ -307,13 +309,15 @@ def _on_mqtt_connect(self, client, userdata, flags, rc):

self.tb.on_mqtt_connect()

# discover motes
self._execute_command_safely('box', 'all', json.dumps({'token': 123}), 'discovermotes')

def _on_mqtt_message(self, client, userdata, message):

# call the handler
self._excecute_commands(message.topic, message.payload)
self._execute_commands(message.topic, message.payload)

def _excecute_commands(self, topic, payload):
def _execute_commands(self, topic, payload):
# parse the topic to extract deviceType, deviceId and cmd ([0-9\-]+)
try:
m = re.search('{0}/deviceType/([a-z]+)/deviceId/([\w,\-]+)/cmd/([a-z]+)'.format(self.testbed), topic)
Expand Down Expand Up @@ -342,15 +346,15 @@ def _excecute_commands(self, topic, payload):
for d in device_to_comand:
commands_handlers += [threading.Thread(
name = '{0}_command_{1}'.format(d, cmd),
target = self._excecute_command_safely,
target = self._execute_command_safely,
args = (deviceType, d, payload, cmd))
]
for handler in commands_handlers:
handler.start()
except:
self.logger.exception("Could not parse command with topic %s", topic)

def _excecute_command_safely(self, deviceType, deviceId, payload, cmd):
def _execute_command_safely(self, deviceType, deviceId, payload, cmd):
'''
Executes the handler of a command in a try/except environment so exception doesn't crash server.
'''
Expand Down Expand Up @@ -483,6 +487,7 @@ def _mqtt_handler_discovermotes(self, deviceType, deviceId, payload):
break
else:
self.logger.debug("No EUI64 found in '%s'", line)
ser.close()

for e in self.motesinfo:
if 'EUI64' in e:
Expand Down Expand Up @@ -549,15 +554,25 @@ def _mqtt_handler_program(self, deviceType, deviceId, payload):

# disconnect from the serialports
self.SerialportHandlers[mote['serialport']].disconnectSerialPort()
time.sleep(2) # wait 2 seconds to release the serial ports

if isinstance(self.tb, OpenTestbed):
# For OpenTestbed, we will use a separate firmware_temp
# for each deviceId. Although other testbeds may need to
# do the same, we keep the original implementation for
# them for now.
firmware_name = '{0}_{1}'.format(deviceId, payload['description'])
firmware_temp = os.path.join(self.tb.firmware_temp_dir,
firmware_name)
else:
firmware_temp = self.tb.firmware_temp

if 'url' in payload and payload['url'].startswith("ftp://"):
# use urllib to get firmware from ftp server (requests doesn't support for ftp)
urllib.urlretrieve(payload['url'],self.tb.firmware_temp)
urllib.urlretrieve(payload['url'],firmware_temp)
urllib.urlcleanup()
else:
# store the firmware to load into a temporary file
with open(self.tb.firmware_temp, 'wb') as f:
with open(firmware_temp, 'wb') as f:
if 'url' in payload: # download file from url if present
file = requests.get(payload['url'], allow_redirects=True)
f.write(file.content)
Expand All @@ -570,7 +585,7 @@ def _mqtt_handler_program(self, deviceType, deviceId, payload):
# bootload the mote
bootload_success = self._bootload_motes(
serialports = [mote['serialport']],
firmware_file = self.tb.firmware_temp,
firmware_file = firmware_temp,
)

assert len(bootload_success)==1
Expand All @@ -593,6 +608,7 @@ def _mqtt_handler_tomoteserialbytes(self, deviceType, deviceId, payload):
mote = self._eui64_to_moteinfoelem(deviceId)
serialHandler = serial.Serial(mote['serialport'], baudrate=self.tb.baudrate, xonxoff=True)
serialHandler.write(bytearray(payload['serialbytes']))
serialHandler.close()
self.SerialportHandlers[mote['serialport']].connectSerialPort()

def _mqtt_handler_reset(self, deviceType, deviceId, payload):
Expand All @@ -603,14 +619,7 @@ def _mqtt_handler_reset(self, deviceType, deviceId, payload):

mote = self._eui64_to_moteinfoelem(deviceId)
self.SerialportHandlers[mote['serialport']].disconnectSerialPort()
pyserialHandler = serial.Serial(mote['serialport'], baudrate=self.tb.baudrate)
pyserialHandler.setDTR(False)
pyserialHandler.setRTS(True)
time.sleep(0.2)
pyserialHandler.setDTR(True)
pyserialHandler.setRTS(False)
time.sleep(0.2)
pyserialHandler.setDTR(False)
self._reset_mote(mote['serialport'])

## start serial
self.SerialportHandlers[mote['serialport']].connectSerialPort()
Expand Down Expand Up @@ -638,13 +647,13 @@ def _mqtt_handler_disable(self, deviceType, deviceId, payload):

def _heartbeatthread_func(self):
while True:
# wait a bit
time.sleep(OtBox.HEARTBEAT_PERIOD)
# publish a heartbeat message
self.mqttclient.publish(
topic = '{0}/heartbeat'.format(self.mqtttopic_box_notif_prefix),
payload = json.dumps({'software_version': OTBOX_VERSION}),
)
# wait a bit
time.sleep(OtBox.HEARTBEAT_PERIOD)

#=== helpers

Expand All @@ -661,6 +670,9 @@ def _bootload_motes(self, serialports, firmware_file):
# simply the name
port = serialport.split('/')[-1]

# reset the mote first
self._reset_mote(serialport)

# stop serial reader
ongoing_bootloads[port] = self.tb.bootload_mote(serialport, firmware_file)

Expand All @@ -675,6 +687,8 @@ def _bootload_motes(self, serialports, firmware_file):

# record whether bootload worked or not
returnVal += [ongoing_bootloads[ongoing_bootload].returncode== 0]
if not returnVal[-1]:
raise RuntimeError(stdout, stderr)

self.logger.debug("Finished bootload_motes with returnVal %s", str(returnVal))
return returnVal
Expand All @@ -695,7 +709,16 @@ def _reboot_function(self):
time.sleep(3)
subprocess.call(["sudo","reboot"])


def _reset_mote(self, serial_port):
pyserialHandler = serial.Serial(serial_port, baudrate=self.tb.baudrate)
pyserialHandler.setDTR(False)
pyserialHandler.setRTS(True)
time.sleep(0.2)
pyserialHandler.setDTR(True)
pyserialHandler.setRTS(False)
time.sleep(0.2)
pyserialHandler.setDTR(False)
pyserialHandler.close()

def _discover_serialports_availables(self):
serialports_available = []
Expand Down