Compare commits

...

4 Commits

Author SHA1 Message Date
becd34d053 no message 2015-11-18 20:35:00 -06:00
6dc8e1b054 commit to present 2015-11-12 23:14:00 -06:00
b5feffc2d1 commit 11/12/2015 2015-11-12 00:00:00 -06:00
8607f82837 commit 10/31/2015 2015-10-31 00:00:00 -06:00
24 changed files with 3553 additions and 575 deletions

View File

@@ -1,21 +1,33 @@
[refresh rates]
refresh camera local = 2
refresh camera transmit = 5
refresh camera local = 10
refresh camera transmit = 15
refresh barometer local = 10
refresh barometer transmit = 5
refresh barometer local = 2
refresh barometer transmit = 2
refresh gps local = 2
refresh gps transmit = 5
[camera settings]
low quality resolution = (640, 360)
low quality compression pct = 20
refresh system = 10
[report settings]
report url = "http://10.0.1.4:5010/report"
report image url = "http://10.0.1.4:5010/photo"
[camera settings]
low quality resolution = (320, 240)
low quality compression pct = 7
high quality resolution = (2592,1944)
high quality compression pct = 100
[modem settings]
com port name = "/dev/ttyAMA0"
baud rate = "9600"
baud rate = 9600
[server settings]
use_lan = True
url = http://home.ascorrea.com
server_port = 5010
data_path = upload-data
image_path = missions
ping_path = ping
[local storage settings]
photo path = /home/pi/scripts/spaceballoon/photos
log path = /home/pi/scripts/spaceballoon/logs

35
config.py Normal file
View File

@@ -0,0 +1,35 @@
__author__ = 'asc'
import configparser
config = configparser.ConfigParser()
config.sections()
config.read('config.ini')
refresh_camera_local = int(config['refresh rates']['refresh camera local'])
refresh_camera_transmit = int(config['refresh rates']['refresh camera transmit'])
refresh_barometer_local = int(config['refresh rates']['refresh barometer local'])
refresh_barometer_transmit = int(config['refresh rates']['refresh barometer transmit'])
refresh_gps_local = int(config['refresh rates']['refresh gps local'])
refresh_gps_transmit = int(config['refresh rates']['refresh gps transmit'])
refresh_system = int(config['refresh rates']['refresh system'])
low_quality_resolution = eval(config['camera settings']['low quality resolution'])
low_quality_compression_pct = int(config['camera settings']['low quality compression pct'])
high_quality_resolution = eval(config['camera settings']['high quality resolution'])
high_quality_compression_pct = int(config['camera settings']['high quality compression pct'])
use_lan = config['server settings']['use_lan']
url = config['server settings']['url']
server_port = int(config['server settings']['server_port'])
data_path = config['server settings']['data_path']
image_path = config['server settings']['image_path']
ping_path = config['server settings']['ping_path']
com_port_name = config['modem settings']['com port name']
baud_rate = config['modem settings']['baud rate']
photo_path = config['local storage settings']['photo path']
log_path = config['local storage settings']['log path']

154
data_handling_legacy.py Normal file
View File

@@ -0,0 +1,154 @@
__author__ = 'asc'
logger
def log(self, message, message_type="text"):
if message_type == "text":
print ("%s - Log Message: %s"% (str(datetime.datetime.now()), message))
elif message_type == "image":
image = message
if image:
filename = datetime.datetime.now().strftime("Image %Y%m%d-%H%M%S.jpg")
with open(os.path.join(self.photo_path, filename), 'wb') as f:
f.write(message)
return 'success'
elif message_type == "data":
message["sent"] = str(datetime.datetime.now())
file=self.log_path
header = add_keys_if_necessary(file, message)
keys = header
log=open(file, 'a')
writer = csv.DictWriter(log, keys, extrasaction="ignore")
writer.writerow(message)
log.close()
else:
raise Exception
def _send_data(self, message, message_type):
if message_type == "ping":
#TODO send text
contentType='text/xml'
if self.use_lan:
logger.info("Sending ping using LAN")
req = request.Request("{0}:{1}/{2}".format(self.url, self.server_port, self.ping_path))
req.add_header('Content-Type', contentType)
response = request.urlopen(req,json.dumps(message).encode())
response = response.read()
pass
elif not self.use_lan:
logger.info("Sending ping using modem")
# logger.debug("attaching GPRS")
#
# if not self.inet.attachGPRS("wholesale", "", "", 1):
# logger.error("error attaching GPRS")
# return False
logger.debug("posting")
if not self.inet.httpPOST(
self.url,
self.server_port,
"/{}".format(self.ping_path),
json.dumps(message),
contentType=contentType
):
logger.error("error making HTTP post: {0}".format(self.inet.errorText))
return False
response=self.inet.httpResponse
if response is not None:
response = str(self.inet.httpResponse).replace("\n\r", "\n")
else:
response = ("empty response")
elif message_type == "image":
response=None
m = MultipartEncoder(fields={'image': ('image', message, 'image/jpeg')})
if self.use_lan:
logger.info ("Sending image using LAN")
response = requests.post("{0}:{1}/{2}".format(self.url, self.server_port, self.image_path), data=m.read(), headers={'Content-Type': m.content_type})
pass
elif not self.use_lan:
logger.info ("Sending image using modem")
# logger.debug("attaching GPRS")
# if not self.inet.attachGPRS("wholesale", "", "", 1):
# logger.error("error attaching GPRS")
# return False
logger.debug("ip = {0}".format(self.inet.ip))
logger.debug("making HTTP POST request")
if not self.inet.httpPOST(
self.url,
self.server_port,
"/{}".format(self.image_path),
m.to_string(),
contentType=m.content_type
):
logger.error("error making HTTP POST: {0}".format(self.inet.errorText))
return False
if self.inet.httpResponse is not None:
response = str(self.inet.httpResponse).replace("\n\r", "\n")
else:
response = "empty response"
elif message_type == "data":
contentType="application/json"
message["sent"] = str(datetime.datetime.now())
if self.use_lan:
logger.info("Sending data using LAN")
req = request.Request("{0}:{1}/{2}".format(self.url, self.server_port, self.data_path))
req.add_header('Content-Type', contentType)
response = request.urlopen(req,json.dumps(message).encode())
response = response.read()
pass
elif not self.use_lan:
logger.info("Sending data using modem")
logger.debug("posting")
if not self.inet.httpPOST(
self.url,
self.server_port,
"/{}".format(self.data_path),
json.dumps(message),
contentType=contentType
):
logger.error("error making HTTP POST: {0}".format(self.inet.errorText))
return False
if self.inet.httpResponse is not None:
response = str(self.inet.httpResponse).replace("\n\r", "\n")
else:
response = ("empty response")
def create_transpondence(self):
if self._transpondence is None:
self._transpondence={}
self._transpondence['mt']=self.mt.now()
def add_to_transpondence(self, data, info_type="data"):
if self._transpondence.get(info_type) is not None:
if type(self._transpondence.get(info_type)) is list:
self._transpondence[info_type].append(data)
else:
self._transpondence[info_type] = [self._transpondence[info_type]]
self._transpondence[info_type].append(data)
else:
self._transpondence[info_type]=[data]
self._transpondence

View File

@@ -6,226 +6,276 @@ import requests
import json
from cell_connection import initializeUartPort, baseOperations, initializeLogs
import logging
# logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
LOGGER_LEVEL=logging.DEBUG
CONSOLE_LOGGER_LEVEL=logging.DEBUG
from requests_toolbelt import MultipartEncoder
import csv
import base64
SCRIPTDIR = os.path.dirname(os.path.abspath(__file__))
logger = logging.getLogger(__name__)
class Datalogger_Debug ():
def __init__(self, path=SCRIPTDIR):
pass
def log(self, message, type="text"):
if type == "text":
print ("%s - Log Message: %s"% (str(datetime.datetime.now()), message))
elif type == "image":
print ("%s - Log Image: %s"% (str(datetime.datetime.now()), message))
elif type == "data":
print ("%s - Log Message: %s"% (str(datetime.datetime.now()), message))
else:
raise Exception
class Datareporter_Debug ():
def __init__(self, path=SCRIPTDIR):
#TODO communication
pass
@property
def status (self):
return (0, "Data reporter functioning properly")
def send(self, message, type="text"):
if type == "text":
#TODO send text
print ("%s - Sent Message: %s"% (str(datetime.datetime.now()), message))
elif type == "image":
#todo send image
print ("%s - Sent Image: %s"% (str(datetime.datetime.now()), message))
else:
#todo error handling
raise Exception
class Datareporter_Debug2 ():
#Debug 2 sends to server
def __init__(self, path=SCRIPTDIR,
report_url = "http://10.0.1.4:5010/report",
report_image_url = "http://10.0.1.4:5010/photo",
com_port_name = "/dev/ttyAMA0",
baud_rate = "9600"):
self.report_url = report_url
self.report_image_url = report_image_url
self.com_port_name = com_port_name,
self.baud_rate = baud_rate
pass
@property
def status (self):
#TODO status check
def add_keys_if_necessary(existing_file, test_dict):
all=None
try:
check = json.loads(request.urlopen(self.report_url).read().decode()).get('status')
pass
if not check:
return (0, "Data reporter functioning properly")
else:
return (1, check)
except Exception as e:
return (1, "Data reporter error: %s" % e)
with open(existing_file,'r') as f:
reader = csv.reader(f)
header = []
try:
header = next(reader)
except StopIteration as e:
pass
if len(header) == 0:
header = list(test_dict.keys())
writer = csv.DictWriter(existing_file, test_dict.keys(), extrasaction='ignore')
writer.writeheader()
# if not header >= list(test_dict.keys()) or not header <= list(test_dict.keys()):
if len(set (header)-set(test_dict.keys()))>0:
existing_keys =set (header)
data_keys = set (test_dict.keys())
keys_to_add = data_keys - existing_keys
all = []
for key in keys_to_add:
header.append(key)
all.append(header)
for row in reader:
all.append(row)
pass
except (OSError, IOError, TypeError) as e:
header = list(test_dict.keys())
f=open(existing_file, 'a+')
writer = csv.DictWriter(f, test_dict.keys(), extrasaction='ignore')
writer.writeheader()
f.close()
if all is not None:
with open (existing_file, 'w') as f:
writer = csv.writer(f)
writer.writerows(all)
return header
class Datalogger():
def __init__(self,
missiontime,
text_path,
log_path,
photo_path):
self._mt = missiontime
self.text_path=text_path
self.log_path=os.path.join(log_path, 'MISSION {}.csv'.format(self._mt.name))
self.photo_path=photo_path
pass
def send(self, message, type="text"):
try:
if type == "text":
#TODO send text
print ("%s - Sent Message: %s"% (str(datetime.datetime.now()), message))
elif type == "image":
#todo send image
response = requests.post(self.report_url, files={'file': message})
print ("%s - Sent Image: %s"% (str(datetime.datetime.now()), message))
elif type == "data":
#add date to message
message['sent']=str(datetime.datetime.now())
req = request.Request(self.report_image_url)
req.add_header('Content-Type', 'application/json')
response = request.urlopen(req,json.dumps(message).encode())
the_page = response.read()
return 0
except Exception as e:
#todo error handling
pass
class Datareporter_Debug3 ():
#Debug 2 sends from cell to server
def log(self, r):
self._record = r._get_dict()
# logger.info('Recording {}'.format(self._record))
for k in self._record.keys():
if k is 'b':
if self._record[k] is not list:
l = [self._record[k]]
else:
l = self._record[k]
for item in l:
logger.debug("item: {}".format(self._record), extra={'MISSION_TIME': self._mt.now(), 'MISSION_ID':self._mt.name})
item['at']=self._mt.to_absolutetime(item['mt'])
item['mid']=self._mt.name
header = add_keys_if_necessary(self.log_path, item)
keys = header
log=open(self.log_path, 'a')
writer = csv.DictWriter(log, keys, extrasaction='ignore')
writer.writerow(item)
log.close()
logger.info('Barometer data recorded',extra={'MISSION_TIME': self._mt.now(), 'MISSION_ID':self._mt.name})
elif k is 'i':
if self._record[k] is tuple:
l = [self._record[k]]
else:
l = self._record[k]
for item in l:
# Form is ('name', file object, 'type')
filename = item[0]
file = base64.b64decode(bytes(item[1],'ascii'))
folder = os.path.join(self.photo_path, self._mt.mid)
file_path = os.path.join(self.photo_path, folder, filename)
if not os.path.exists(folder):
os.makedirs(folder)
with open(file_path, 'wb') as f:
f.write(file)
# file.save(os.path.join(self.photo_path, filename))
logger.info('Image data recorded',extra={'MISSION_TIME': self._mt.now(), 'MISSION_ID':self._mt.name})
return 'success'
class Datareporter():
from lib.sim900.inetgsm import SimInetGSM
def __init__(self, path=SCRIPTDIR,
report_url = "http://10.0.1.4:5010/report",
report_image_url = "http://10.0.1.4:5010/upload-file",
com_port_name = "/dev/ttyAMA0",
baud_rate = "9600",
content_type = None):
def __init__(self,
missiontime,
url,
data_path,
image_path,
ping_path,
server_port,
com_port_name=None,
baud_rate=None,
use_lan = False,
path=SCRIPTDIR):
#TODO communication
self.report_url = report_url
self.report_image_url = report_image_url
self.mt = missiontime
self.url = url
self.server_port=server_port
self.image_path = image_path
self.data_path = data_path
self.com_port_name = com_port_name
self.baud_rate = baud_rate
self.content_type = content_type
pass
self.ping_path = ping_path
self.use_lan = use_lan
self._transpondence=None
if not use_lan:
self.port = initializeUartPort(portName=self.com_port_name, baudrate=self.baud_rate)
d = baseOperations(self.port, logger)
if not d is None:
(self.gsm, self.imei) = d
self.inet = self.SimInetGSM(self.port, logger)
logger.info('ip = {0}'.format(self.inet.ip), extra={'MISSION_TIME': self.mt.now(), 'MISSION_ID':self.mt.name})
logger.debug('attaching GPRS', extra={'MISSION_TIME': self.mt.now(), 'MISSION_ID':self.mt.name})
if not self.inet.attachGPRS('wholesale', '', '', 1):
logger.error('error attaching GPRS', extra={'MISSION_TIME': self.mt.now(), 'MISSION_ID':self.mt.name})
return False
#register mission number to server
intiate = Record(str(self.mt.timezero),'zt')
# intiate.add(self.mt.mid,'mid')
self.send(intiate, _intiating_report=True)
@property
def status (self):
#TODO status check
try:
check = json.loads(request.urlopen(self.report_url).read().decode()).get('status')
pass
if not check:
return (0, "Data reporter functioning properly")
else:
return (1, check)
return (0, 'Data reporter functioning properly')
except Exception as e:
return (1, "Data reporter error: %s" % e)
return (1, 'Data reporter error: %s' % e)
def send(self, message, type="text"):
if type == "text":
#TODO send text
print ("%s - Sent Message: %s"% (str(datetime.datetime.now()), message))
elif type == "image":
#todo send image
# response = requests.post(self.report_image_url, files={'file': message})
print ("%s - Sent Image: %s"% (str(datetime.datetime.now()), message))
#adding & initializing port object
port = initializeUartPort(portName=self.com_port_name, baudrate=self.baud_rate)
def _send_data(self, message, _intiating_report=False):
response=None
m = MultipartEncoder(fields=message)
#initializing logger
(formatter, logger, consoleLogger,) = initializeLogs(LOGGER_LEVEL, CONSOLE_LOGGER_LEVEL)
if _intiating_report:
path = '{0}'.format(self.image_path)
else:
path = '{0}/{1}'.format(self.image_path,self.mt.mid)
#making base operations
d = baseOperations(port, logger)
if d is None:
return False
if self.use_lan:
logger.info ('Sending transpondence using LAN', extra={'MISSION_TIME': self.mt.now(), 'MISSION_ID':self.mt.name})
response = requests.post('{0}:{1}/{2}'.format(self.url, self.server_port, path), data=m.read(), headers={'Content-Type': m.content_type})
pass
(gsm, imei) = d
elif not self.use_lan:
logger.info ('Sending transpondence using modem', extra={'MISSION_TIME': self.mt.now(), 'MISSION_ID':self.mt.name})
# logger.debug('attaching GPRS')
# if not self.inet.attachGPRS('wholesale', '', '', 1):
# logger.error('error attaching GPRS')
# return False
inet = self.SimInetGSM(port, logger)
logger.debug('ip = {0}'.format(self.inet.ip), extra={'MISSION_TIME': self.mt.now(), 'MISSION_ID':self.mt.name})
logger.info("attaching GPRS")
if not inet.attachGPRS("wholesale", "", "", 1):
logger.error("error attaching GPRS")
return False
logger.debug('making HTTP POST request', extra={'MISSION_TIME': self.mt.now(), 'MISSION_ID':self.mt.name})
logger.info("ip = {0}".format(inet.ip))
#making HTTP GET request
logger.info("making HTTP POST request")
if not inet.httpPOST_DATA(
"home.ascorrea.com",
5010,
"/upload-file",
# content=self.content_type,
parameters="{0}".format(message)
if not self.inet.httpPOST(
self.url,
self.server_port,
"/{}".format(path),
m.to_string(),
contentType=m.content_type
):
logger.error("error making HTTP GET post: {0}".format(inet.errorText))
logger.error('error making HTTP POST: {0}'.format(self.inet.errorText), extra={'MISSION_TIME': self.mt.now(), 'MISSION_ID':self.mt.name})
return False
logger.info("httpResult = {0}".format(inet.httpResult))
if inet.httpResponse is not None:
response = str(inet.httpResponse).replace("\n\r", "\n")
logger.info("response: \"{0}\"".format(response))
if self.inet.httpResponse is not None:
response = str(self.inet.httpResponse).replace('\n\r', '\n')
else:
logger.info("empty response")
elif type == "data":
#adding & initializing port object
port = initializeUartPort(portName=self.com_port_name, baudrate=self.baud_rate)
response = 'empty response'
#initializing logger
(formatter, logger, consoleLogger,) = initializeLogs(LOGGER_LEVEL, CONSOLE_LOGGER_LEVEL)
#making base operations
d = baseOperations(port, logger)
if d is None:
return False
(gsm, imei) = d
inet = self.SimInetGSM(port, logger)
logger.info("attaching GPRS")
if not inet.attachGPRS("internet", "", "", 1):
logger.error("error attaching GPRS")
return False
logger.info("ip = {0}".format(inet.ip))
#making HTTP GET request
logger.info("making HTTP POST request")
if not inet.httpPOST(
"home.ascorrea.com",
5010,
"/report-encoded",
parse.urlencode(message)
):
logger.error("error making HTTP GET post: {0}".format(inet.errorText))
return False
logger.info("httpResult = {0}".format(inet.httpResult))
if inet.httpResponse is not None:
response = str(inet.httpResponse).replace("\n\r", "\n")
logger.info("response: \"{0}\"".format(response))
if _intiating_report:
if type (response) is str:
mid = response
else:
logger.info("empty response")
mid = response.text
self.mt.set_mid(mid)
return response
def send(self, t, _intiating_report=False):
self._transpondence = t._get_dict()
self._transpondence['mt'] = self.mt.now()
# print ('Send transpondence {}'.format(self._transpondence))
for k in self._transpondence.keys():
# if self._transpondence[k] is list or self._transpondence[k] is dict or self._transpondence[k] is float:
if type(self._transpondence) is dict:
self._transpondence[k] = json.dumps(self._transpondence[k])
r = self._send_data(message=self._transpondence, _intiating_report=_intiating_report)
#On error, do not clear transpondence
if not r:
self._transpondence = self._transpondence
return None
class Record():
def __init__(self,first_item=None, first_item_type=None):
# if self._transpondence is None:
self._transpondence={}
if first_item and first_item_type:
self.add(first_item, first_item_type)
def add(self, data, info_type='data'):
if type(data) is not list:
if self._transpondence.get(info_type) is not None:
if type(self._transpondence.get(info_type)) is list:
self._transpondence[info_type].append(data)
else:
self._transpondence[info_type] = [self._transpondence[info_type]]
self._transpondence[info_type].append(data)
else:
self._transpondence[info_type]=data
else:
if self._transpondence.get(info_type) is not None:
if type(self._transpondence.get(info_type)) is list:
self._transpondence[info_type] + data
else:
self._transpondence[info_type] = [self._transpondence[info_type]]
self._transpondence[info_type] + data
else:
self._transpondence[info_type]=data
def _get_dict(self):
return self._transpondence

View File

@@ -1,112 +1,100 @@
__author__ = 'asc'
debug = True
from random import random
import Adafruit_BMP.BMP085 as BMP085
import picamera
import configparser
import time
import logging
import base64
logger = logging.getLogger(__name__)
class Barometer_Debug ():
def __init__(self):
pass
@property
def status (self):
return (0, "Barometer functioning properly")
@property
def temperature (self):
if self.status[0]:
return 'error'
if debug:
temp = random()*100
else:
raise Exception ('Not Debug')
return temp
@property
def pressure (self):
if (self.status[0]):
return 'error'
if debug:
press = random()
else:
raise Exception ('Not Debug')
return press
@property
def altitude (self):
if self.status[0]:
return 'error'
if debug:
alt = random()*100000
else:
raise Exception ('Not Debug')
return alt
class Camera_Debug():
def __init__(self):
pass
@property
def status (self):
return (0, "Camera functioning properly")
def capture (self):
#todo capture image
return ({"file":open("image2.jpg", 'rb')})
#camera debug 2 uses actual camera
class Camera_Debug2():
def __init__(self, low_quality_resolution,
low_quality_compression_pct,
high_quality_resolution=None,
high_qualitycompression_pct=85):
logger.debug("Initializing camera")
class Camera:
def __init__(self,
low_quality_resolution=(320,240),
low_quality_compression_pct=5,
high_quality_resolution=(2592,1944),
high_quality_compression_pct=85,
debug = False,
**kwargs):
logger.info("Initializing camera", extra={'MISSION_TIME': "", 'MISSION_ID':""})
time.sleep(1)
self.low_quality_resolution = low_quality_resolution
self.low_quality_compression_pct = low_quality_compression_pct
self._cam = picamera.PiCamera()
logger.debug("Camera intialized")
self.high_quality_resolution = high_quality_resolution
self.high_quality_compression_pct = high_quality_compression_pct
self.kwargs=kwargs
self._debug = debug
if not self._debug:
import picamera
self._cam = picamera.PiCamera(resolution=high_quality_resolution)
# if "vflip" in kwargs.keys():
for k in kwargs.keys():
setattr(self._cam, k, kwargs.get(k))
logger.debug("Camera intialized", extra={'MISSION_TIME': "", 'MISSION_ID':""})
pass
@property
def status (self):
return (0, "Camera functioning properly")
def capture (self, no_low_quality=False, no_high_quality=False):
def capture (self, name, no_low_quality=False, no_high_quality=False, **kwargs):
#todo image adjustments
img_hi = None
img_lo = None
if not no_high_quality:
logger.debug('Taking high quality photo')
self._cam.capture("temp_img_hi.jpg")
img_hi = open("temp_img_hi.jpg", 'rb')
logger.debug('High quality photo taken, file: {}'.format(img_hi))
if not no_low_quality:
logger.debug('Taking low quality photo (Resolution: {}, JPEG Quality: {}%'.format(self.low_quality_resolution, self.low_quality_compression_pct))
self._cam.capture("temp_img_lo.jpg",
resize=self.low_quality_resolution,
quality=self.low_quality_compression_pct)
img_lo = open("temp_img_lo.jpg", 'rb')
logger.debug('Low quality photo taken, file: {}'.format(img_lo))
r = {}
if not self._debug:
if not no_high_quality:
logger.debug('Taking high quality photo', extra={'MISSION_TIME': "", 'MISSION_ID':""})
self._cam.capture("temp_img_hi.jpg",
resize=self.high_quality_resolution,
quality=self.high_quality_compression_pct,
**kwargs
)
with open("temp_img_hi.jpg", 'rb') as f:
# img_hi = base64.b64encode(f.read())
img_hi = str(base64.b64encode(f.read()),'ascii')
r['hi']=("{}_hi.jpg".format(name), img_hi, "image/jpeg")
logger.debug('High quality photo taken', extra={'MISSION_TIME': "", 'MISSION_ID':""})
return ({"hi":img_hi,
"lo":img_lo})
if not no_low_quality:
time.sleep(1)
logger.debug('Taking low quality photo (Resolution: {}, JPEG Quality: {}%)'.format(self.low_quality_resolution, self.low_quality_compression_pct))
self._cam.capture("temp_img_lo.jpg",
resize=self.low_quality_resolution,
quality=self.low_quality_compression_pct,
**kwargs
)
with open("temp_img_lo.jpg", 'rb') as f:
img_lo = str(base64.b64encode(f.read()),'ascii')
# img_lo = str(f.read())
r['lo']=("{}_lo.jpg".format(name), img_lo, "image/jpeg")
logger.debug('Low quality photo taken', extra={'MISSION_TIME': "", 'MISSION_ID':""})
else:
if not no_high_quality:
with open("temp_img_hi.jpg", 'rb') as f:
img_hi = str(base64.b64encode(f.read()),'ascii')
# img_hi = str(f.read(),'ascii')
r['hi']=("{}_hi.jpg".format(name), img_hi, "image/jpeg")
logger.info('High quality photo taken', extra={'MISSION_TIME': "", 'MISSION_ID':""})
#barometerdebug2 uses actual barometer
class Barometer_Debug2 ():
def __init__(self):
logger.debug("Intializing barometer")
self.bmp = BMP085.BMP085()
logger.debug("Barometer intilized")
if not no_low_quality:
with open("temp_img_lo.jpg", 'rb') as f:
img_lo = str(base64.b64encode(f.read()),'ascii')
# img_lo = str(f.read())
r['lo']=("{}_lo.jpg".format(name), img_lo, "image/jpeg")
logger.info('Low quality photo taken', extra={'MISSION_TIME': "", 'MISSION_ID':""})
return r
class Barometer:
def __init__(self, debug=False):
logger.debug("Intializing barometer", extra={'MISSION_TIME': "", 'MISSION_ID':""})
if not debug:
import Adafruit_BMP.BMP085 as BMP085
self.bmp = BMP085.BMP085()
logger.debug("Barometer intilized", extra={'MISSION_TIME': "", 'MISSION_ID':""})
pass
@property
@@ -139,19 +127,77 @@ class Barometer_Debug2 ():
alt = self.bmp.read_altitude()
return alt
def read(self):
logger.debug("Reading from barometer", extra={'MISSION_TIME': "", 'MISSION_ID':""})
#refresh each instrument
if not debug:
alt = self.altitude
press = self.pressure
temp = self.temperature
else:
temp = random()*100
press = random()
alt = random()*100000
result = {"a":alt,
"t":temp,
"p":press,
}
logger.debug("Barometer reads {}".format(result), extra={'MISSION_TIME': "", 'MISSION_ID':""})
return result
class Gps:
def __init__(self):
logger.debug("Intializing GPS")
# self.bmp = BMP085.BMP085()
logger.debug("Gps intilized")
pass
@property
def status (self):
return (0, "Barometer functioning properly")
@property
def temperature (self):
if self.status[0]:
return 'error'
temp = self.bmp.read_temperature()
return temp
@property
def pressure (self):
if (self.status[0]):
return 'error'
# press = 100000*random()
press = self.bmp.read_pressure()
return press
@property
def altitude (self):
if self.status[0]:
return 'error'
#TODO Set the altitude of your current location in meter
alt = self.bmp.read_altitude()
return alt
def read(self):
logger.debug("Reading from barometer")
#refresh each instrument
alt = self.altitude
press = self.pressure
temp = self.temperature
result = {"altitude":alt,
"temperature":temp,
"pressure":press,
result = {"a":alt,
"t":temp,
"p":press,
}
logger.debug("Barometer reads {}".format(result))
logger.debug("Gps reads {}".format(result))
return result
# class Gps:

View File

View File

View File

@@ -0,0 +1,60 @@
__author__ = 'Bohdan'
import time
class AminisLastErrorHolder:
def __init__(self):
self.errorText = ""
self.__hasError = False
def clearError(self):
self.errorText = ""
self.__hasError = False
def setError(self, errorText):
self.errorText = errorText
self.__hasError = True
@property
def hasError(self):
return self.__hasError
def timeDelta(timeBegin):
end = time.time()
secs = end - timeBegin
msecs = (end - timeBegin) * 1000.0
return secs*1000 + msecs
def splitAndFilter(value, separator):
items = str(value).split(separator)
ret = []
for item in items:
item = str(item).strip()
if len(item) == 0:
continue
ret += [item]
return ret
def isFloat(value):
try:
float(value)
return True
except ValueError:
return False
def strToFloat(value):
value = str(value).strip()
if len(value) == 0:
return None
value = value.replace(",", ".")
try:
return float(value)
except ValueError:
return None

661
lib/sim900.bak/gsm.py Normal file
View File

@@ -0,0 +1,661 @@
#The MIT License (MIT)
#
#Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua )
#
#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.
"""
This file is part of sim-module package. Implements basic functions of SIM900 modules.
sim-module package allows to communicate with SIM 900 modules: send SMS, make HTTP requests and use other
functions of SIM 900 modules.
Copyright (C) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) All Rights Reserved.
"""
import time
import serial
import logging
from lib.sim900.simshared import *
class GsmSpecialCharacters:
ctrlz = 26 #//Ascii character for ctr+z. End of a SMS.
cr = 0x0d #//Ascii character for carriage return.
lf = 0x0a #//Ascii character for line feed.
class SimGsmState:
UNKNOWN = 0
ERROR = 1
IDLE = 2
READY = 3
ATTACHED = 4
TCPSERVERWAIT = 5
TCPCONNECTEDSERVER = 6
TCPCONNECTEDCLIENT = 7
class SimGsmPinRequestState:
UNKNOWN = -1
NOPINNEEDED = 0
SIM_PIN = 1
SIM_PUK = 2
PH_SIM_PIN = 3
PH_SIM_PUK = 4
SIM_PIN2 = 5
SIM_PUK2 = 6
class SimGsmSerialPortHandler(AminisLastErrorHolderWithLogging):
def __init__(self, serial, logger = None):
AminisLastErrorHolderWithLogging.__init__(self, logger)
self.input = bytearray()
self.__serial = serial
#stores last executed command result
self.lastResult = None
def openPort(self):
try:
self.__serial.open()
self.flush()
except Exception as e:
self.setError("exception till port openning: {0}".format(e))
return False
except:
self.setError("error opening port")
return False
return True
def __sendRawBytes(self, data, maxWaitTime = 1000):
"""
Sends raw bytes to the SIM module
:param data: data which must be send
:param maxWaitTime: max wait time for sending sequence
:return: True if data was send, otherwise returns False
"""
bytesToSend = len(data)
sentBytes = 0
start = time.time()
self.logger.debug("{0}, sending: {1}".format(inspect.stack()[0][3], data))
while sentBytes < bytesToSend:
if timeDelta(start) >= maxWaitTime:
self.setWarn("__sendRawBytes(): timed out")
return False
sentBytes += self.__serial.write(data[sentBytes : ])
if sentBytes == 0:
time.sleep(0.001)
continue
return True
def print(self, commandString, encoding = "ascii"):
"""
Sends string data to the SIM module
:param commandString: data what must be sent
:param encoding: before sending string it will be converted to the bytearray with this encoding
:return: True if everything is OK, otherwise returns false
"""
data = bytearray(commandString, encoding)
return self.__sendRawBytes(data)
def simpleWrite(self, commandLine, encoding = "ascii"):
"""
Just alias for print() method
:param commandLine: data which must be sent
:param encoding: before sending string it will be converted to the bytearray with this encoding
:return: True if data sent, otherwise returns False
"""
return self.print(commandLine, encoding)
def printLn(self, commandString, encoding = "ascii"):
"""
Sends string data and CR/LF in the end to the SIM module
:param commandString: data which must be sent
:param encoding: before sending string it will be converted to the bytearray with this encoding
:return: True if data sent, otherwise returns False
"""
# print('am in println: {}'.format(commandString))
if type(commandString) is bytearray:
data = commandString + bytearray([GsmSpecialCharacters.cr, GsmSpecialCharacters.lf])
else:
data = bytearray(commandString, encoding) + bytearray([GsmSpecialCharacters.cr, GsmSpecialCharacters.lf])
r = self.__sendRawBytes(data)
# print('am leavin println: {}'.format(data))
return r
def simpleWriteLn(self, commandLine, encoding = "ascii"):
"""
Just alias for printLn() method
:param commandLine: data which must be sent
:param encoding: before sending string it will be converted to the bytearray with this encoding
:return: True if data sent, otherwise returns False
"""
# print('am in simplewriteln')
r = self.printLn(commandLine, encoding)
# print('am leaving simplewriteln')
return r
def flushInput(self):
"""
Flushes input buffer
:return: nothing
"""
try:
self.__serial.flushInput()
except Exception as e:
self.setError("error flushing: {0}".format(e))
except:
self.setError("error flushing")
def flushOutput(self):
"""
Flushes output buffer
:return: nothing
"""
try:
self.__serial.flushOutput()
except Exception as e:
self.setError("error flushing: {0}".format(e))
except:
self.setError("error flushing")
def readFixedSzieByteArray(self, bytesCount, maxWaitTime):
start = time.time()
buffer = bytearray()
try:
while True:
#checking for timeout
if timeDelta(start) >= maxWaitTime:
return None
receivedBytesQty = 0
while True:
bytesToRead = 10 if ((bytesCount - len(buffer)) >= 10) else 1
b = self.__serial.read(bytesToRead)
if (b is None) or (len(b) == 0):
break
buffer += bytearray(b)
receivedBytesQty += len(b)
if len(buffer) == bytesCount:
return buffer
#if we have nothing in input - let's go sleep for some time
if receivedBytesQty == 0:
time.sleep(0.003)
#comming there by timeout
return None
except Exception as e:
self.setError(e)
return None
except:
self.setError("reading error...")
return None
def readNullTerminatedLn(self, maxWaitTime = 5000, codepage = "ascii"):
start = time.time()
start = time.time()
buffer = bytearray()
try:
while True:
#checking for timeout
if timeDelta(start) >= maxWaitTime:
return None
receivedBytesQty = 0
while True:
b = self.__serial.read(1)
if (b is None) or (len(b) == 0):
break
#checking that we have NULL symbol in
idx = b.find(0x00)
if idx != -1:
buffer.extend(b[:idx])
return buffer.decode(codepage)
buffer += bytearray(b)
receivedBytesQty += len(b)
#if we have nothing in input - let's go sleep for some time
if receivedBytesQty == 0:
time.sleep(0.003)
#comming there by timeout
return None
except Exception as e:
self.setError(e)
return None
except:
self.setError("reading error...")
return None
def readLn(self, maxWaitTime = 5000, codepage = "ascii"):
"""
Returns text string from SIM module. Can return even empty strings.
:param maxWaitTime: max wait interval for operation
:param codepage: code page of result string
:return: received string
"""
start = time.time()
buffer = bytearray()
try:
while True:
#checking for timeout
if timeDelta(start) >= maxWaitTime:
return None
receivedBytesQty = 0
while True:
b = self.__serial.read(1)
if (b is None) or (len(b) == 0):
break
buffer += bytearray(b)
receivedBytesQty += len(b)
if codepage is not None:
#checking for line end symbols
line = buffer.decode(codepage)
if '\n' in line:
return line.strip()
elif ord('\n') in buffer:
return buffer
#if we have nothing in input - let's go sleep for some time
if receivedBytesQty == 0:
time.sleep(0)
#comming there by timeout
return None
except Exception as e:
self.setError(e)
return None
except:
self.setError("reading error...")
return None
def readDataLine(self, maxWaitTime = 500, codepage = "ascii"):
"""
Returns non empty data string. So, if it will receive empty string function will continue non empty string
retrieving
:param maxWaitTime: max wait time for receiving
:param codepage: code page of result string, if it's a None - will return a bytearray
:return: received string
"""
ret = None
start = time.time()
while True:
#checking for timeout
if timeDelta(start) >= maxWaitTime:
break
#reading string
#TODO: need to fix timeout (substract already spent time interval)
line = self.readLn(maxWaitTime, codepage)
#removing garbage symbols
if line is not None:
line = str(line).strip()
#if we have non empty string let's return it
if len(line) > 0:
return line
else:
#if we have empty line - let's continue reading
continue
else:
#returning None if None received
if line is None:
return None
continue
#we will come here by timeout
return None
def flush(self):
"""
Flushes input and output buffers
:return: nothing
"""
try:
self.__serial.flush()
except Exception as e:
self.setError("error flushing: {0}".format(e))
except:
self.setError("error flushing")
def closePort(self):
"""
Closes COM port
:return: nothing
"""
try:
self.__serial.close()
except Exception as e:
self.setError("error closing port: {0}".format(e))
except:
self.setError("error closing port")
@staticmethod
def isCrLf(symbol):
"""
Returns True when parameter is CR/LF symbol, otherwise returns False
:param symbol: symbol for analysis
:return: True when CR/LF symbol, otherwise returns False
"""
return (symbol == GsmSpecialCharacters.cr) or (symbol == GsmSpecialCharacters.lf)
@staticmethod
def getLastNonEmptyString(strings):
"""
Parses strings array and returns last non empty string from array
:param strings: strings array for analysis
:return: last non empty string, otherwise None
"""
#if there is no data - returning None
if strings is None:
return None
qty = len(strings)
if qty == 0:
return None
#looking for last non empty string
for i in range(qty):
s = str(strings[-(i+1)]).strip()
if len(s) > 0:
return s
return None
@staticmethod
def removeEndResult(strings, targetString):
"""
Searches and removes last string which contains result
:param strings:
:param targetString:
:return:
"""
ret = ""
#searching for target string
while len(strings) > 0:
s = str(strings[-1]).strip()
strings.pop(len(strings)-1)
if s == targetString:
break
#compiling result
qty = len(strings)
for i in range(qty):
ret += strings[i]
return ret
@staticmethod
def parseStrings(buffer, encoding = "ascii"):
"""
Parses string (from given encoding), looks for cr/lf and retutrns strings array
:param buffer: input string
:param encoding: encoding
:return: strings array
"""
#decoding
bigString = buffer.decode(encoding)
#searching for cr/lf and making strings array
if "\r" in bigString:
ret = bigString.split("\r")
else:
ret = [bigString]
return ret
def commandAndStdResult(self, commandText, maxWaitTime = 10000, possibleResults = None):
self.lastResult = None
#setting up standard results
if possibleResults is None:
possibleResults = ["OK", "ERROR"]
start = time.time()
buffer = bytearray()
self.flush()
#sending command
self.simpleWriteLn(commandText)
try:
while True:
if timeDelta(start) >= maxWaitTime:
break
readBytesQty = 0
while True:
b = self.__serial.read(100)
if (b is not None) and (len(b) >= 1):
buffer += bytearray(b)
self.logger.debug("{0}: buffer = {1}".format(inspect.stack()[0][3], buffer))
readBytesQty += len(b)
continue
else:
break
#if we have no data - let's go sleep for tiny amount of time
if readBytesQty == 0:
time.sleep(0.005)
continue
#parsing result strings
strings = SimGsm.parseStrings(buffer[:])
self.logger.debug("{0}: strings = {1}".format(inspect.stack()[0][3], strings))
if strings is None:
time.sleep(0.01)
continue
#if we have some strings let's parse it
if len(strings) > 0:
lastString = SimGsm.getLastNonEmptyString(strings[:])
if lastString in possibleResults:
self.lastResult = lastString
return SimGsm.removeEndResult(strings[:], lastString)
time.sleep(0.05)
return None
except Exception as e:
self.setError(e)
return None
except:
self.setError("reading error...")
return None
def execSimpleCommand(self, commandText, result, timeout = 500):
ret = self.commandAndStdResult(commandText, timeout, [result])
if (ret is None) or (self.lastResult != result):
return False
return True
def execSimpleOkCommand(self, commandText, timeout = 500):
self.logger.debug("executing command '{0}'".format(commandText))
ret = self.commandAndStdResult(commandText, timeout, ["OK", "ERROR"])
if (ret is None) or (self.lastResult != "OK"):
return False
return True
def execSimpleCommandsList(self, commandsList):
for command in commandsList:
if not self.execSimpleOkCommand(command[0], command[1]):
return False
return True
class SimGsm(SimGsmSerialPortHandler):
def __init__(self, serial, logger = None):
SimGsmSerialPortHandler.__init__(self, serial, logger)
self.__state = SimGsmState.UNKNOWN
self.pinState = SimGsmPinRequestState.UNKNOWN
def begin(self, numberOfAttempts = 5):
ok = False
self.flush()
needDisableEcho = False
for i in range(numberOfAttempts):
self.printLn("AT")
line = self.readDataLine(2000, "ascii")
#if we do not have something in input - let's go sleep
if line is None:
time.sleep(0.2)
continue
#we have echo, need to reconfigure
if line == "AT":
#we have ECHO, need reconfigure
needDisableEcho = True
line = self.readDataLine(500, "ascii")
if line == "OK":
ok = True
break
elif line == "OK":
ok = True
break
if not ok:
return False
#disabling echo if needed
if needDisableEcho:
self.logger.info("Disabling echo, calling 'ATE0'")
self.simpleWriteLn("ATE0")
time.sleep(0.5)
self.flush()
commands = [
["ATV1", 500], #short answer for commands
["AT+CMEE=0", 500], #disabling error report
["AT", 5000] #checking state
]
for cmd in commands:
self.logger.debug("configuring, calling: {0}".format(cmd[0]))
if not self.execSimpleOkCommand(commandText=cmd[0],timeout=cmd[1]):
return False
#checking PIN state
if not self.__checkPin():
return False
return True
def __checkPin(self):
msg = self.commandAndStdResult("AT+CPIN?")
if msg is None:
return False
if self.lastResult != "OK":
return False
msg = str(msg).strip()
values = splitAndFilter(msg, ":")
msg.split(":")
if len(values) < 2:
self.setError("Wrong response for PIN state request")
return False
if values[0] != "+CPIN":
self.setError("Wrong response for PIN state request. First value = '{0}'".format(values[0]))
return False
v = " ".join([v for v in values[1:]])
if v == "READY":
self.pinState = SimGsmPinRequestState.NOPINNEEDED
elif v == "SIM PIN":
self.pinState = SimGsmPinRequestState.SIM_PIN
elif v == "SIM PUK":
self.pinState = SimGsmPinRequestState.SIM_PUK
elif v == "PH_SIM PIN":
self.pinState = SimGsmPinRequestState.PH_SIM_PIN
elif v == "PH_SIM PUK":
self.pinState = SimGsmPinRequestState.PH_SIM_PUK
elif v == "SIM PIN2":
self.pinState = SimGsmPinRequestState.SIM_PIN2
elif v == "SIM PUK2":
self.pinState = SimGsmPinRequestState.SIM_PUK2
else:
self.pinState = SimGsmPinRequestState.UNKNOWN
self.setError("Unknown PIN request answer: {0}".format(v))
return False
return True
def enterPin(self, pinCode):
return self.execSimpleOkCommand("AT+CPIN=\"{0}\"".format(pinCode))

45
lib/sim900.bak/imei.py Normal file
View File

@@ -0,0 +1,45 @@
#The MIT License (MIT)
#
#Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua )
#
#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.
"""
This file is part of sim-module package. Can be used for SIM900 module IMEI retrieving
sim-module package allows to communicate with SIM 900 modules: send SMS, make HTTP requests and use other
functions of SIM 900 modules.
Copyright (C) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) All Rights Reserved.
"""
from lib.sim900.gsm import SimGsm
class SimImeiRetriever(SimGsm):
def __init__(self, port, logger):
SimGsm.__init__(self, port, logger)
def getIMEI(self):
self.logger.debug("retrieving IMEI")
data = self.commandAndStdResult("AT+GSN", 1000)
if data is None:
return None
return str(data).strip()

622
lib/sim900.bak/inetgsm.py Normal file
View File

@@ -0,0 +1,622 @@
#The MIT License (MIT)
#
#Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua )
#
#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.
"""
This file is part of sim-module package. Can be used for HTTP requests making.
sim-module package allows to communicate with SIM 900 modules: send SMS, make HTTP requests and use other
functions of SIM 900 modules.
Copyright (C) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) All Rights Reserved.
"""
from lib.sim900.gsm import *
class SimInetGSMConnection:
inetUnknown = -1
inetConnecting = 0
inetConnected = 1
inetClosing = 2
inetClosed = 3
class SimInetGSM(SimGsm):
def __init__(self, port, logger):
SimGsm.__init__(self, port, logger)
self.__ip = None
#user agent
self.__userAgent = "Aminis SIM-900 module client (version 0.1)"
self.__connectionState = SimInetGSMConnection.inetUnknown
self.__httpResult = 0
self.__httpResponse = None
@property
def connectionState(self):
return self.__connectionState
@property
def httpResult(self):
return self.__httpResult
@property
def httpResponse(self):
return self.__httpResponse
@property
def ip(self):
return self.__ip
@property
def userAgent(self):
return self.__userAgent
@userAgent.setter
def userAgent(self, value):
self.__userAgent = value
def checkGprsBearer(self, bearerNumber = 1):
"""
Checks GPRS connection. After calling of this method
:param bearerNumber: bearer number
:return: True if checking was without mistakes, otherwise returns False
"""
self.logger.debug("checking GPRS bearer connection")
ret = self.commandAndStdResult(
"AT+SAPBR=2,{0}".format(bearerNumber),
1000,
["OK"]
)
if (ret is None) or (self.lastResult != "OK"):
self.setError("{0}: error, lastResult={1}, ret={2}".format(inspect.stack()[0][3], self.lastResult, ret))
return False
ret = str(ret).strip()
self.logger.debug("{0}: result = {1}".format(inspect.stack()[0][3], ret))
response = str(ret).split(":")
if len(response) < 2:
self.setError("{0}:error, wrong response length, ret = {1}".format(inspect.stack()[0][3], ret))
return False
#parsing string like:
# +SAPBR: 1,1,"100.80.75.124" - when connected (channel 1)
# +SAPBR: 1,3,"0.0.0.0" - when disconnected (channel 1)
if response[0] != "+SAPBR":
self.setWarn("{0}: warning, response is not '+SAPBR', response = {1}".format(inspect.stack()[0][3], response[0]))
return False
response = splitAndFilter(response[1], ",")
self.logger.debug("{0}: sapbr result = \"{1}\"".format(inspect.stack()[0][3], response))
if len(response) < 3:
self.setError("{0}: wrong SAPBR result length, (sapbr result = '{1}')".format(inspect.stack()[0][3], response[1]))
return False
if response[0] != str(bearerNumber):
return
self.__ip = None
if response[1] == "0":
self.__connectionState = SimInetGSMConnection.inetConnecting
elif response[1] == "1":
self.__connectionState = SimInetGSMConnection.inetConnected
self.__ip = response[2].strip("\"").strip()
elif response[1] == "2":
self.__connectionState = SimInetGSMConnection.inetClosing
elif response[1] == "3":
self.__connectionState = SimInetGSMConnection.inetClosed
else:
self.__connectionState = SimInetGSMConnection.inetUnknown
return True
def attachGPRS(self, apn, user=None, password=None, bearerNumber = 1):
"""
Attaches GPRS connection for SIM module
:param apn: Access Point Name
:param user: User name (Login)
:param password: Password
:param bearerNumber: Bearer number
:return: True if everything was OK, otherwise returns False
"""
#checking current connection state
if not self.checkGprsBearer(bearerNumber):
return False
#going out if already connected
if self.connectionState == SimInetGSMConnection.inetConnected:
return True
#Closing the GPRS PDP context. We dont care of result
self.execSimpleOkCommand("AT+CIPSHUT", 500)
#initialization sequence for GPRS attaching
commands = [
["AT+SAPBR=3,{0},\"CONTYPE\",\"GPRS\"".format(bearerNumber), 1000 ],
["AT+SAPBR=3,{0},\"APN\",\"{1}\"".format(bearerNumber, apn), 500 ],
["AT+SAPBR=3,{0},\"USER\",\"{1}\"".format(bearerNumber, user), 500 ],
["AT+SAPBR=3,{0},\"PWD\",\"{1}\"".format(bearerNumber, password), 500 ],
["AT+SAPBR=1,{0}".format(bearerNumber), 10000 ]
]
#executing commands sequence
if not self.execSimpleCommandsList(commands):
return False
#returning GPRS checking sequence
return self.checkGprsBearer()
def disconnectTcp(self):
"""
Disconnects TCP connection
:return:
"""
return self.commandAndStdResult("AT+CIPCLOSE", 1000, ["OK"])
def dettachGPRS(self, bearerNumber = 1):
"""
Detaches GPRS connection
:param bearerNumber: bearer number
:return: True if de
"""
#Disconnecting TCP. Ignoring result
self.disconnectTcp()
#checking current GPRS connection state
if self.checkGprsBearer(bearerNumber):
if self.connectionState == SimInetGSMConnection.inetClosed:
return True
#disconnecting GPRS connection for given bearer number
return self.execSimpleOkCommand("AT+SAPBR=0,{0}".format(bearerNumber), 1000)
def terminateHttpRequest(self):
"""
Terminates current HTTP request.
:return: True if when operation processing was without errors, otherwise returns False
"""
return self.execSimpleOkCommand("AT+HTTPTERM", 500)
def __parseHttpResult(self, httpResult, bearerChannel = None):
"""
Parses http result string.
:param httpResult: string to parse
:param bearerChannel: bearer channel
:return: returns http result code and response length
"""
self.logger.debug("{0}: dataLine = {1}".format(inspect.stack()[0][3], httpResult))
response = splitAndFilter(httpResult, ":")
if len(response) < 2:
self.setWarn("{0}: wrong HTTP response length, length = {1}".format(inspect.stack()[0][3], len(response)))
return None
if response[0] != "+HTTPACTION":
self.setWarn("{0}: http response is not a '+HTTPACTION', response = '{1}'".format(inspect.stack()[0][3], response[0]))
return None
response = splitAndFilter(response[1], ",")
if len(response) < 3:
self.setWarn("{0}: wrong response length".format(inspect.stack()[0][3]))
return None
#checking bearer channel if necessary
if bearerChannel is not None:
if response[0] != str(bearerChannel):
self.setWarn("{0}: bad bearer number".format(inspect.stack()[0][3]))
return None
httpResultCode = str(response[1])
if not httpResultCode.isnumeric():
self.setWarn("{0}: response code is not numeric!".format(inspect.stack()[0][3]))
return None
httpResultCode = int(httpResultCode)
if httpResultCode != 200:
return [httpResultCode, 0]
responseLength = str(response[2])
if not responseLength.isnumeric():
self.setWarn("{0}: response length is not numeric".format(inspect.stack()[0][3]))
return False
return [httpResultCode, int(responseLength)]
def __readHttpResponse(self, httpMethodCode, responseLength):
"""
Reads http response data from SIM module buffer
:param httpMethodCode: ?
:param responseLength: response length
:return: True if reading was successful, otherwise returns false
"""
self.logger.debug("asking for http response (length = {0})".format(responseLength))
#trying to read HTTP response data
ret = self.commandAndStdResult(
"AT+HTTPREAD={0},{1}".format(httpMethodCode, responseLength),
10000,
["OK"]
)
if (ret is None) or (self.lastResult != "OK"):
self.setError("{0}: error reading http response data".format(inspect.stack()[0][3]))
return False
#removing leading \n symbols
#TODO: we must remove only 1 \n, not all! Fix it!
ret = str(ret).strip()
#reading first string in response (it must be "+HTTPREAD")
httpReadResultString = ""
while True:
if len(ret) == 0:
break
httpReadResultString += ret[0]
ret = ret[1:]
if "\n" in httpReadResultString:
break
httpReadResultString = str(httpReadResultString).strip()
if len(httpReadResultString) == 0:
self.setError("{0}: wrong http response. Result is empty".format(inspect.stack()[0][3]))
return False
httpReadResult = str(httpReadResultString).strip()
self.logger.debug("{0}: httpReadResult = {1}".format(inspect.stack()[0][3], httpReadResult))
httpReadResult = splitAndFilter(httpReadResult, ":")
if (len(httpReadResult) < 2) or (httpReadResult[0] != "+HTTPREAD"):
self.setError("{0}: bad response (cant find '+HTTPREAD'".format(inspect.stack()[0][3]))
return False
if int(httpReadResult[1]) != responseLength:
self.setWarn("{0}: bad response, wrong responseLength = {1}".format(inspect.stack()[0][3], responseLength))
return False
self.__httpResponse = ret
return True
@staticmethod
def ___isOkHttpResponseCode(code):
"""
Checks that given HTTP return code is successful result code
:param code: http result code for checking
:return: true if given code is HTTP operation successful
"""
return code in [200, 201, 202, 203, 204, 205, 206, 207, 226]
@staticmethod
def __isNoContentResponse(code):
"""
Checks that HTTP result code is 'NO CONTENT' result code
:param code: code for analysis
:return: true when code is 'NO CONTENT' code, otherwise returns false
"""
return code == 204
@staticmethod
def ___isHttpResponseCodeReturnsData(code):
"""
Checks that http operation returns data by given http result code
:param code: given http call result code
:return: true if http request must return data, otherwise returns false
"""
return code in [200, 206]
def httpGet(self, server, port = 80, path = "/", bearerChannel = 1):
"""
Makes HTTP GET request to the given server and script
:param server: server (host) address
:param port: http port
:param path: path to the script
:param bearerChannel: bearer channel number
:return: true if operation was successfully finished. Otherwise returns false
"""
self.__clearHttpResponse()
#TODO: close only when opened
self.terminateHttpRequest()
#HTTP GET request sequence
simpleCommands = [
[ "AT+HTTPINIT", 2000 ],
[ "AT+HTTPPARA=\"CID\",\"{0}\"".format(bearerChannel), 1000 ],
[ "AT+HTTPPARA=\"URL\",\"{0}:{2}{1}\"".format(server, path,port), 500 ],
[ "AT+HTTPPARA=\"UA\",\"{0}\"".format(self.userAgent), 500 ],
[ "AT+HTTPPARA=\"REDIR\",\"1\"", 500 ],
[ "AT+HTTPPARA=\"TIMEOUT\",\"45\"", 500 ],
[ "AT+HTTPACTION=0", 10000 ]
]
#executing http get sequence
if not self.execSimpleCommandsList(simpleCommands):
self.setError("error executing HTTP GET sequence")
return False
#reading HTTP request result
dataLine = self.readDataLine(10000)
if dataLine is None:
return False
#parsing string like this "+HTTPACTION:0,200,15"
httpResult = self.__parseHttpResult(dataLine, 0)
if httpResult is None:
return False
#assigning HTTP result code
self.__httpResult = httpResult[0]
#it's can be bad http code, let's check it
if not self.___isOkHttpResponseCode(self.httpResult):
self.terminateHttpRequest()
return True
#when no data from server we just want go out, everything if OK
if not self.___isHttpResponseCodeReturnsData(self.httpResult):
self.terminateHttpRequest()
return True
responseLength = httpResult[1]
if responseLength == 0:
self.terminateHttpRequest()
return True
self.logger.debug("reading http response data")
if not self.__readHttpResponse(0, responseLength):
return False
return True
def __clearHttpResponse(self):
self.__httpResponse = None
self.__httpResult = 0
def httpPOST(self, server, port, path, parameters, bearerChannel = 1, contentType="application/x-www-form-urlencoded"):
"""
Makes HTTP POST request to the given server and script
:param server: server (host) address
:param port: server port
:param path: path to the script
:param parameters: POST parameters
:param bearerChannel: bearer channel number
:return: true if operation was successfully finished. Otherwise returns false
"""
self.__clearHttpResponse()
#TODO: close only when opened
self.terminateHttpRequest()
#HTTP POST request commands sequence
simpleCommands = [
[ "AT+HTTPINIT", 2000 ],
[ "AT+HTTPPARA=\"CID\",\"{0}\"".format(bearerChannel), 1000 ],
[ "AT+HTTPPARA=\"URL\",\"{0}:{1}{2}\"".format(server, port, path), 500 ],
[ "AT+HTTPPARA=\"CONTENT\",\"{}\"".format(contentType), 500 ],
#[ "AT+HTTPPARA=\"CONTENT\",\"{}\"".format(""), 500 ],
[ "AT+HTTPPARA=\"UA\",\"{0}\"".format(self.userAgent), 500 ],
[ "AT+HTTPPARA=\"REDIR\",\"1\"", 500 ],
[ "AT+HTTPPARA=\"TIMEOUT\",\"45\"", 500 ]
]
#executing commands sequence
if not self.execSimpleCommandsList(simpleCommands):
return False
#uploading data
self.logger.debug("uploading HTTP POST data")
ret = self.commandAndStdResult(
"AT+HTTPDATA={0},10000".format(len(parameters)),
7000,
["DOWNLOAD", "ERROR"]
)
if (ret is None) or (self.lastResult != "DOWNLOAD"):
self.setError("{0}: can't upload HTTP POST data 1".format(inspect.stack()[0][3]))
return False
self.simpleWriteLn(parameters)
dataLine = self.readDataLine(500)
if (dataLine is None) or (dataLine != "OK"):
self.setError("{0}: can't upload HTTP POST data".format(inspect.stack()[0][3]))
return
self.logger.debug("actually making request")
#TODO: check CPU utilization
if not self.execSimpleOkCommand("AT+HTTPACTION=1", 15000):
return False
#reading HTTP request result
dataLine = self.readDataLine(15000)
if dataLine is None:
self.setError("{0}: empty HTTP request result string".format(inspect.stack()[0][3]))
return False
#parsing string like this "+HTTPACTION:0,200,15"
httpResult = self.__parseHttpResult(dataLine, bearerChannel)
if httpResult is None:
return False
#assigning HTTP result code
self.__httpResult = httpResult[0]
#it's can be bad http code, let's check it
if not self.___isOkHttpResponseCode(self.httpResult):
self.terminateHttpRequest()
return True
#when no data from server we just want go out, everything if OK
if (
(self.__isNoContentResponse(self.httpResult)) or
(not self.___isHttpResponseCodeReturnsData(self.httpResult))
):
self.terminateHttpRequest()
return True
responseLength = httpResult[1]
if responseLength == 0:
self.terminateHttpRequest()
return True
self.logger.debug("reading http request response data")
if not self.__readHttpResponse(0, responseLength):
return False
return True
# self.disconnectTcp()
#
# return True
def httpPOST_DATA(self, server, port, path, parameters, bearerChannel = 1):
"""
Makes HTTP POST request to the given server and script
:param server: server (host) address
:param port: server port
:param path: path to the script
:param parameters: POST parameters
:param bearerChannel: bearer channel number
:return: true if operation was successfully finished. Otherwise returns false
"""
self.__clearHttpResponse()
#TODO: close only when opened
self.terminateHttpRequest()
#HTTP POST request commands sequence
simpleCommands = [
[ "AT+HTTPINIT", 2000 ],
[ "AT+HTTPPARA=\"CID\",\"{0}\"".format(bearerChannel), 1000 ],
[ "AT+HTTPPARA=\"URL\",\"{0}:{1}{2}\"".format(server, port, path), 500 ],
[ "AT+HTTPPARA=\"CONTENT\",\"multipart/form-data\"", 500 ],
[ "AT+HTTPPARA=\"UA\",\"{0}\"".format(self.userAgent), 500 ],
[ "AT+HTTPPARA=\"REDIR\",\"1\"", 500 ],
[ "AT+HTTPPARA=\"TIMEOUT\",\"45\"", 500 ]
]
#executing commands sequence
if not self.execSimpleCommandsList(simpleCommands):
return False
#uploading data
self.logger.debug("uploading HTTP POST data")
ret = self.commandAndStdResult(
"AT+HTTPDATA={0},10000".format(len(parameters)),
7000,
["DOWNLOAD", "ERROR"]
)
if (ret is None) or (self.lastResult != "DOWNLOAD"):
self.setError("{0}: can't upload HTTP POST data".format(inspect.stack()[0][3]))
return False
self.simpleWriteLn(parameters)
dataLine = self.readDataLine(500)
if (dataLine is None) or (dataLine != "OK"):
self.setError("{0}: can't upload HTTP POST data".format(inspect.stack()[0][3]))
return
self.logger.debug("actually making request")
#TODO: check CPU utilization
if not self.execSimpleOkCommand("AT+HTTPACTION=1", 15000):
return False
#reading HTTP request result
dataLine = self.readDataLine(15000)
if dataLine is None:
self.setError("{0}: empty HTTP request result string".format(inspect.stack()[0][3]))
return False
#parsing string like this "+HTTPACTION:0,200,15"
httpResult = self.__parseHttpResult(dataLine, bearerChannel)
if httpResult is None:
return False
#assigning HTTP result code
self.__httpResult = httpResult[0]
#it's can be bad http code, let's check it
if not self.___isOkHttpResponseCode(self.httpResult):
self.terminateHttpRequest()
return True
#when no data from server we just want go out, everything if OK
if (
(self.__isNoContentResponse(self.httpResult)) or
(not self.___isHttpResponseCodeReturnsData(self.httpResult))
):
self.terminateHttpRequest()
return True
responseLength = httpResult[1]
if responseLength == 0:
self.terminateHttpRequest()
return True
self.logger.debug("reading http request response data")
if not self.__readHttpResponse(0, responseLength):
return False
return True
# self.disconnectTcp()
#
# return True
#
# int res= gsm.read(result, resultlength);
# //gsm.disconnectTCP();
# return res;

View File

@@ -0,0 +1,62 @@
#The MIT License (MIT)
#
#Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua )
#
#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.
"""
This file is part of sim-module package. Shared functions for sim-module package.
sim-module package allows to communicate with SIM 900 modules: send SMS, make HTTP requests and use other
functions of SIM 900 modules.
Copyright (C) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) All Rights Reserved.
"""
import os
import logging
import inspect
### conditional import ###
# our company uses big file amshared.py which is not needed for this library. so here we will import only needful
# functions
if os.path.exists(os.path.abspath(os.path.join(os.path.dirname(__file__), "__stand_alone__.py"))):
from lib.sim900.amsharedmini import *
else:
from lib.amshared import *
class AminisLastErrorHolderWithLogging(AminisLastErrorHolder):
def __init__(self, logger = None):
AminisLastErrorHolder.__init__(self)
self.logger = logger
if self.logger is None:
self.logger = logging.getLogger(__name__)
def setError(self, value):
AminisLastErrorHolder.setError(self, value)
self.logger.error(value)
def setWarn(self, value):
AminisLastErrorHolder.setError(self, value)
self.logger.warn(value)
def noneToEmptyString(value):
return '' if value is None else value

View File

@@ -0,0 +1,653 @@
#The MIT License (MIT)
#
#Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua )
#
#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.
"""
This file is part of sim-module package. SMS processing classes and functions.
sim-module package allows to communicate with SIM 900 modules: send SMS, make HTTP requests and use other
functions of SIM 900 modules.
Copyright (C) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) All Rights Reserved.
"""
from lib.sim900.gsm import SimGsm
from lib.sim900.simshared import *
import binascii
import random
class SimSmsPduCompiler(AminisLastErrorHolder):
def __init__(self, smsCenterNumber="", targetPhoneNumber="", smsTextMessage=""):
AminisLastErrorHolder.__init__(self)
#sms center number
self.__smsCenterNumber = self.__preprocessPhoneNumber(smsCenterNumber)
#sms recipient number
self.__smsRecipientNumber = self.__preprocessPhoneNumber(targetPhoneNumber)
#sms text
self.smsText = smsTextMessage
self.flashMessage = False
#validation period for message
self.__validationPeriod = None
def clear(self):
"""
Clears all internal buffers
:return: nothing
"""
self.clear()
self.__smsCenterNumber = ""
self.__smsRecipientNumber = ""
self.smsText = ""
self.flashMessage = False
self.__validationPeriod = None
@property
def smsCenterNumber(self):
"""
SMS center number
:return: returns SMS center number
"""
return self.__smsCenterNumber
@staticmethod
def __preprocessPhoneNumber(value):
value = noneToEmptyString(value)
value = str(value).strip()
value = value.replace(" ", "")
return value.replace("\t", "")
@smsCenterNumber.setter
def smsCenterNumber(self, value):
"""
Sets SMS center number
:param value: new SMS center number
:return: nothing
"""
self.__smsCenterNumber = self.__preprocessPhoneNumber(value)
@property
def smsRecipientNumber(self):
"""
Returns SMS recipient number
:return: SMS recipient number
"""
return self.__smsRecipientNumber
@smsRecipientNumber.setter
def smsRecipientNumber(self, value):
"""
Sets SMS recipient number
:param value: SMS recipient number
:return: nothig
"""
self.__smsRecipientNumber = self.__preprocessPhoneNumber(value)
@staticmethod
def __clientPhoneNumberLength(number):
"""
Returns phone number without '+' symbol and without padding 'F' at end
:param number: number for length calculation
:return: number length
"""
num = str(number).strip()
num = num.replace("+", "")
return len(num)
@staticmethod
def __encodePhoneNumber(number):
"""
Encodes phone number according to PDU rules
:param number: phone number for encoding
:return: encoded phone number
"""
num = str(number).strip()
num = num.replace("+", "")
#adding pad byte
if (len(num) % 2) != 0:
num += 'F'
#calculating reverted result, according to the
result = ""
i = 0
while i < len(num):
result += num[i+1] + num[i]
i += 2
return result
def __compileScaPart(self):
"""
Compiles SCA part of PDU request.
:return: compiled request
"""
if len(self.smsCenterNumber) == 0:
return "00"
smsCenterNumber = SimSmsPduCompiler.__encodePhoneNumber(self.smsCenterNumber)
sca = SimSmsPduCompiler.__byteToHex ( ((len(smsCenterNumber) // 2) + 1)) + "91" + smsCenterNumber
return sca
def __canUse7BitsEncoding(self, text = None):
"""
Checks that message can be encoded in 7 bits.
:param text: optional argument - text for checking, when not specified whole sms text will be checked
:return: true when text can be encoded in 7 bits, otherwise returns false
"""
if text is None:
return all(ord(c) < 128 for c in self.smsText)
return all(ord(c) < 128 for c in text)
@staticmethod
def __encodeMessageIn7Bits(text):
"""
Encodes ASCII text message block with 7 bit's encoding. So, each 8 symbols of message will be encoded in 7 bytes
:param text: text for encoding
:return: 7-bit encoded message
"""
data = bytearray(text.encode("ascii"))
#encoding
i = 1
while i < len(data):
j = len(data) - 1
while j>=i:
firstBit = 0x80 if ((data[j] % 2) > 0) else 0x00
data[j-1] = (data[j-1] & 0x7f) | firstBit
data[j] = data[j] >> 1
j -= 1
i += 1
#looking for first 0x00 byte
index = 0
for b in data:
if b == 0x00:
break
index += 1
data = data[:index]
# 'hellohello' must be encoded as "E8329BFD4697D9EC37"
return binascii.hexlify(data).decode("ascii").upper()
def __encodeMessageAsUcs2(self, text):
"""
Encodes message with UCS2 encoding
:param text: text for encoding
:return: UCS2 encoded message
"""
try:
d = binascii.hexlify(text.encode("utf-16-be"))
return d.decode("ascii").upper()
except Exception as e:
self.setError("error encoding text: {0}".format(e))
return None
def __compilePduTypePart(self, isMultupartMessage):
"""
Returns PDU Type part.
:param isMultupartMessage: must be true when message is multupart
:return: encoded PDU-Type
"""
#returning PDU-Type when validation period is not specified
if self.__validationPeriod is None:
if not isMultupartMessage:
return "01"
return "41"
#special value when multi-part message
if isMultupartMessage:
return "51"
return "11"
def __compilePduTpVpPart(self):
"""
Returns TP-VP part (validity period for SMS)
:return:
"""
# TP- VP — TP-Validity-Period/ "AA" means 4 days. Note: This octet is optional, see bits 4 and 3 of the first octet
return self.__validationPeriod
def setValidationPeriodInMinutes(self, value):
"""
Set message validation period in minutes interval. Up to 12 hours.
:param value: minutes count
:return: true if everything is OK, otherwise returns false
"""
#0-143 (TP-VP + 1) x 5 minutes 5, 10, 15 minutes ... 11:55, 12:00 hours
count = value // 5
if count > 143:
self.setError("Wrong interval, must be between 1 and 720 minutes")
return False
self.__validationPeriod = self.__byteToHex(count)
return True
def setValidationPeriodInHours(self, value):
"""
Set validation period in hours (up to 24 hours) with 0.5 hour step
:param value: hours count (float), must be >= 12 and <= 24
:return: true if everything is OK, otherwise returns false
"""
#144-167 (12 + (TP-VP - 143) / 2 ) hours 12:30, 13:00, ... 23:30, 24:00 hours
if (value < 12) or (value > 24):
self.setError("Value must be between 12 and 24 hours")
return False
value = value - 12
count = int(value)
if (value - count) >= 0.5:
count = count*2 + 1
else:
count = count*2
if count>23:
count = 23
self.__validationPeriod = self.__byteToHex(count + 144)
return True
def setValidationPeriodInDays(self, value):
"""
Can set message validation period in days (2-30 days)
:param value: days count (must be >=2 and <=30)
:return: true when value is OK, otherwise returns false
"""
#168-196 (TP-VP - 166) days 2, 3, 4, ... 30 days
if (value < 2) or (value > 30):
self.setError("Bad interval, value must be >= 2 days and <= 30 days")
return False
self.__validationPeriod = self.__byteToHex(value + 166)
return True
def setValidationPeriodInWeeks(self, value):
"""
Set validation period in weeks (from 5 to 63 weeks)
:param value: weeks count (must be >=5 and <= 63)
:return: true if everything is OK, otherwise returns false
"""
# 197-255 (TP-VP - 192) weeks 5, 6, 7, ... 63 weeks
if (value < 5) or (value > 63):
self.setError("Wrong value, value must be >= 5 and <= 63 weeks")
return False
value = value - 5
self.__validationPeriod = self.__byteToHex(value + 197)
return True
def __compileTpdu(self, pieceNumber, totalPiecesCount, pieceText, messageId = None):
"""
Compiles TPDU part of PDU message request.
:return: compiled TPDU
"""
# TPDU = "PDU-Type" + "TP-MR" + "TP-DA" + "TP-PID" + "TP-DCS" + "TP-VP" + "TP-UDL" + "TP-UD"
# PDU-Type is the same as SMS-SUBMIT-PDU
ret = ""
#checking that message have more than one part
isMultipartMessage = totalPiecesCount > 1
#adding PDU-Type
ret += self.__compilePduTypePart(isMultipartMessage)
#adding TP-MR (TP-Message-Reference).
ret += self.__byteToHex(pieceNumber+100)
# if totalPiecesCount > 1:
# #setting message reference manually
# ret += self.__byteToHex(pieceNumber)
# else:
# #The "00" value here lets the phone set the message reference number itself.
# ret += "00"
#encoding TP-DA (TP-Destination-Address - recipient address)
ret += self.__byteToHex(self.__clientPhoneNumberLength(self.smsRecipientNumber)) + "91" + self.__encodePhoneNumber(self.smsRecipientNumber)
#adding TP-PID (TP-Protocol ID)
ret += "00"
#adding TP-DCS (TP-Data-Coding-Scheme)
#00h: 7-bit encoding (160 symbols [after packing], but only ASCII)
#08h: UCS2 encoding (Unicode), 70 symbols, 2 bytes per symbol
#If first octet is "1" message will not be saved in mobile but only flashed on the screen
#10h: Flash-message with 7-bit encoding
#18h: Flash-message with UCS2 encoding
#checking that message CAN be encoded in 7 bits encoding
canBe7BitsEncoded = self.__canUse7BitsEncoding()
if canBe7BitsEncoded:
tpDcs = "00"
else:
tpDcs = "08"
if self.flashMessage:
tpDcs[0] = "1"
ret += tpDcs
#adding TP-VP (TP-Validity-Period) is it's specified
if self.__validationPeriod is not None:
ret += self.__compilePduTpVpPart()
#encoding message (7-bit or UCS2)
if canBe7BitsEncoded:
encodedMessage = self.__encodeMessageIn7Bits(pieceText)
else:
encodedMessage = self.__encodeMessageAsUcs2(pieceText)
#checking that message was encoded correctly
if encodedMessage is None:
self.setError("error encoding message: {0}".format(self.errorText))
return None
#adding TP-UDL (TP-User-Data-Length - message length)
if not isMultipartMessage:
if canBe7BitsEncoded:
#adding TEXT LENGTH IN SYMBOLS
ret += self.__byteToHex(len(self.smsText))
else:
ret += self.__byteToHex(len(encodedMessage)//2)
else:
if canBe7BitsEncoded:
ret += self.__byteToHex(len(pieceText) + 8)
else:
ret += self.__byteToHex(len(encodedMessage)//2 + 6)
#adding UDHL + UDH for multipart messages
if isMultipartMessage:
if canBe7BitsEncoded:
#length of UDH
udhl = bytearray([0x06])
#UDI IED entry type
iei = bytearray([0x08])
#length of UDH IED
iedl = bytearray([0x04])
# messageId
ied1Lo = messageId & 0xff
ied1Hi = ((messageId & 0xff00) >> 8)
ied1 = bytearray([ied1Hi, ied1Lo])
#total pieces count
ied2 = bytearray([totalPiecesCount])
#piece number
ied3 = bytearray([pieceNumber])
#compiling IED
ied = ied1 + ied2 + ied3
#compiling UDH
udh = iei + iedl + ied
else:
#length of UDH
udhl = bytearray([0x05])
#UDI IED entry type
iei = bytearray([0x00])
#length of UDH IED
iedl = bytearray([0x03])
#message id
ied1Lo = messageId & 0xff
ied1 = bytearray([ied1Lo])
#total pieces count
ied2 = bytearray([totalPiecesCount])
#piece number
ied3 = bytearray([pieceNumber])
#compiling IED
ied = ied1 + ied2 + ied3
#compiling UDH
udh = iei + iedl + ied
cudh = binascii.hexlify(udhl + udh).decode("ascii").upper()
print("cudh = '{0}'".format(cudh))
ret += cudh
#adding TP-UD (TP-User-Data - SMS message encoded as described in TP-DCS)
ret += encodedMessage
return ret
def messagesCount(self):
if self.__canUse7BitsEncoding():
symbolsCount = len(self.smsText)
if symbolsCount <= 160:
return 1
messagesCount = symbolsCount // 152
if symbolsCount % 152:
messagesCount += 1
return messagesCount
else:
symbolsCount = len(self.smsText)
if symbolsCount <= 70:
return 1
messagesCount = symbolsCount // 67
if symbolsCount % 67:
messagesCount += 1
return messagesCount
@staticmethod
def __byteToHex(value):
"""
Returns two-symbold hex-string representation of byte.
:param value: byte for encoding
:return: encoded value
"""
return "{:02X}".format(value)
def compile(self):
"""
Compiles PDU request (SCA + TPDU)
:return: SMS request in PDU format
"""
ret = []
symbolsCount = len(self.smsText)
msgCount = self.messagesCount()
isUcs2 = not self.__canUse7BitsEncoding()
if isUcs2:
symbolsInPiece = 67
else:
symbolsInPiece = 152
#generating message id for multi-part messages
messageId = None
if msgCount > 1:
messageId = random.randint(0, 65535)
for i in range(msgCount):
if msgCount == 1:
textPiece = self.smsText
else:
minIndex = i * symbolsInPiece
maxIndex = (minIndex + symbolsInPiece) if (minIndex + symbolsInPiece) < symbolsCount else (symbolsCount)
textPiece = self.smsText[minIndex : maxIndex]
ret += [(self.__compileScaPart(), self.__compileTpdu(i+1, msgCount, textPiece, messageId),)]
return ret
class SimGsmSmsHandler(SimGsm):
def __init__(self, port, logger):
SimGsm.__init__(self, port, logger)
self.sendingResult = ""
def clear(self):
SimGsm.clearError(self)
self.sendingResult = ""
def sendSms(self, phoneNumber, messageText, numberOfAttempts = 3):
tuneCommands = [
["AT+CMGS=?", 300], #checking that sms supported
["AT+CMGF=1", 1000]
]
self.logger.debug("initializing SIM module for SMS sending")
for cmd in tuneCommands:
if not self.execSimpleOkCommand(commandText=cmd[0],timeout=cmd[1]):
return False
for i in range(numberOfAttempts):
ret = self.commandAndStdResult(
"AT+CMGS=\"{0}\"".format(phoneNumber),
1000,
[">"]
)
if (ret is None) or (self.lastResult != ">"):
continue
ret = self.commandAndStdResult(
"{0}\n\x1a".format(messageText),
10000,
["ERROR", "OK"]
)
if (ret is None) or (self.lastResult not in ["OK"]):
continue
return True
self.setError("error sending sms...")
return False
def __sendPduMessageLow(self, sca, pdu, numberOfAttempts = 3):
tuneCommands = [
["AT+CSCS=\"GSM\"", 500],
# ["AT+CMGS?", 500], #checking that sms supported
["AT+CMGF=0", 1000]
]
self.logger.debug("initializing SIM module for SMS sending in PDU mode")
for cmd in tuneCommands:
if not self.execSimpleOkCommand(commandText=cmd[0], timeout=cmd[1]):
self.setError("error tuning module for sms sending")
return False
for i in range(numberOfAttempts):
ret = self.commandAndStdResult(
"AT+CMGS={0}".format(len(pdu) // 2),
1000,
[">"]
)
if (ret is None) or (self.lastResult != ">"):
continue
ret = self.commandAndStdResult(
"{0}\x1a".format(sca + pdu),
10000,
["ERROR", "OK"]
)
if (ret is None) or (self.lastResult != "OK"):
continue
self.sendingResult = ret.strip()
return True
return False
def sendPduMessage(self, pduHelper, numberOfAttempts = 3):
d = pduHelper.compile()
if d is None:
self.setError("error compiling PDU sms")
return False
piece = 1
for (sca, pdu,) in d:
self.logger.info("sendSms(): sca + pdu = \"{0}\"".format(sca + pdu))
if not self.__sendPduMessageLow(sca, pdu, numberOfAttempts):
return False
self.logger.info("Sending result = {0}".format(self.sendingResult))
return True

View File

@@ -0,0 +1,126 @@
#The MIT License (MIT)
#
#Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua )
#
#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.
"""
This file is part of sim-module package. USSD requests processing classes and functions.
sim-module package allows to communicate with SIM 900 modules: send SMS, make HTTP requests and use other
functions of SIM 900 modules.
Copyright (C) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) All Rights Reserved.
"""
from lib.sim900.gsm import SimGsm
from lib.sim900.simshared import *
class SimUssdHandler(SimGsm):
def __init__(self, port, logger):
SimGsm.__init__(self, port, logger)
self.lastUssdResult = None
@staticmethod
def __parseResult(value):
#parsing strings like '+CUSD: 0,"data string"'
#searching and removing '+CUSD' prefix
idx = value.find(":")
if idx == -1:
return None
left = value[:idx]
left = str(left).strip()
if left != "+CUSD":
return None
data = value[(idx+1):]
data = str(data).strip()
#searching and removing numeric parameter
idx = data.find(",")
if idx == -1:
return None
#also, we can use this code. But I dont know how
code = data[:idx]
data = data[(idx+1):]
data = str(data).strip()
data = data.rstrip(',')
data = data.strip('"')
return data
def runUssdCode(self, ussdCode):
cmd = "AT+CUSD=1,\"{0}\",15".format(ussdCode)
self.logger.info("running command = '{0}'".format(cmd))
#executing command, also we can retrieve result right here
result = self.commandAndStdResult(cmd, 20000)
if (result is None) or (self.lastResult != 'OK'):
self.setWarn("error running USSD command '{0}'".format(ussdCode))
return False
result = str(result).strip()
#checking that we have result here
if len(result) > 0:
self.lastUssdResult = self.__parseResult(result)
if self.lastUssdResult is None:
self.setWarn("error parsing USSD command result")
return False
return True
#reading data line
dataLine = self.readNullTerminatedLn(20000)
if dataLine is None:
self.setWarn("error waiting for USSD command result")
return False
dataLine = str(dataLine).strip()
#reading bytes in the end of response
data = self.readFixedSzieByteArray(1, 500)
if data == bytes([0xff]):
data = None
endLine = self.readLn(500)
if (data is not None) or (endLine is not None):
endLine = noneToEmptyString(data) + noneToEmptyString(endLine)
endLine = str(endLine).strip()
if len(endLine) > 0:
dataLine += endLine
#parsing CUSD result
self.lastUssdResult = self.__parseResult(dataLine)
if self.lastUssdResult is None:
return False
return True

View File

@@ -33,7 +33,6 @@ import time
import serial
import logging
from lib.sim900.simshared import *
import struct
class GsmSpecialCharacters:
ctrlz = 26 #//Ascii character for ctr+z. End of a SMS.
@@ -133,7 +132,7 @@ class SimGsmSerialPortHandler(AminisLastErrorHolderWithLogging):
"""
return self.print(commandLine, encoding)
def printLn(self, commandString, bytes=None, encoding = "ascii"):
def printLn(self, commandString, encoding = "ascii"):
"""
Sends string data and CR/LF in the end to the SIM module
@@ -141,16 +140,33 @@ class SimGsmSerialPortHandler(AminisLastErrorHolderWithLogging):
:param encoding: before sending string it will be converted to the bytearray with this encoding
:return: True if data sent, otherwise returns False
"""
if bytes is not None:
data = bytearray(commandString, encoding) + bytes + bytearray([GsmSpecialCharacters.cr, GsmSpecialCharacters.lf])
if not isinstance(commandString, bytes):
data = bytearray(commandString, encoding) + bytearray([GsmSpecialCharacters.cr, GsmSpecialCharacters.lf])
else:
data= bytearray(commandString, encoding) + bytearray([GsmSpecialCharacters.cr, GsmSpecialCharacters.lf])
# print("Print Line data: {}".format(data))
data = commandString + bytearray([GsmSpecialCharacters.cr, GsmSpecialCharacters.lf])
return self.__sendRawBytes(data)
def simpleWriteLn(self, commandLine, bytes=None, encoding = "ascii"):
def simpleWriteLns(self, commandLines, encoding = "ascii"):
for i, l in enumerate(commandLines):
if i < len(commandLines)-1:
if not isinstance(l, bytes):
data = bytearray(l, encoding)
else:
data = l
else:
if not isinstance(l, bytes):
data = bytearray(l, encoding) + bytearray([GsmSpecialCharacters.cr, GsmSpecialCharacters.lf])
else:
data = l + bytearray([GsmSpecialCharacters.cr, GsmSpecialCharacters.lf])
r = self.__sendRawBytes(data)
if r is False:
return r
return r
def simpleWriteLn(self, commandLine, encoding = "ascii"):
"""
Just alias for printLn() method
@@ -159,7 +175,7 @@ class SimGsmSerialPortHandler(AminisLastErrorHolderWithLogging):
:return: True if data sent, otherwise returns False
"""
return self.printLn(commandLine, encoding=encoding, bytes=bytes)
return self.printLn(commandLine, encoding)
def flushInput(self):
"""

View File

@@ -38,6 +38,10 @@ class SimInetGSMConnection:
inetClosing = 2
inetClosed = 3
def chunks(l, n):
n = max(1, n)
return [l[i:i + n] for i in range(0, len(l), n)]
class SimInetGSM(SimGsm):
def __init__(self, port, logger):
SimGsm.__init__(self, port, logger)
@@ -172,13 +176,54 @@ class SimInetGSM(SimGsm):
#returning GPRS checking sequence
return self.checkGprsBearer()
def disconnectTcp(self):
"""
Disconnects TCP connection
:return:
"""
def attachCIP(self, apn, user=None, password=None, bearerNumber = 1):
"""
Attaches GPRS connection for SIM module
return self.commandAndStdResult("AT+CIPCLOSE", 1000, ["OK"])
:param apn: Access Point Name
:param user: User name (Login)
:param password: Password
:param bearerNumber: Bearer number
:return: True if everything was OK, otherwise returns False
"""
#checking current connection state
# if not self.checkGprsBearer(bearerNumber):
# return False
#
# #going out if already connected
# if self.connectionState == SimInetGSMConnection.inetConnected:
# return True
#Closing the GPRS PDP context. We dont care of result
self.execSimpleOkCommand("AT+CIPSHUT", 500)
#initialization sequence for GPRS attaching
commands = [
["AT+CGATT=1", 1000 ],
["AT+CSNS=4,", 500 ],
["AT+CSTT=\"{}\",\"{}\",\"{}\"".format(apn, user, password), 500 ],
["AT+CIICR", 500 ],
["AT+CIFSR", 10000 ]
# ["AT+CIPSTART=\"{}\",\"{}\",\"{}\"".format("TCP", {})]
]
#executing commands sequence
if not self.execSimpleCommandsList(commands):
return False
return 1
#returning GPRS checking sequence
# return self.checkGprsBearer()
def disconnectTcp(self):
"""
Disconnects TCP connection
:return:
"""
return self.commandAndStdResult("AT+CIPCLOSE", 1000, ["OK"])
def dettachGPRS(self, bearerNumber = 1):
"""
@@ -409,7 +454,7 @@ class SimInetGSM(SimGsm):
self.__httpResponse = None
self.__httpResult = 0
def httpPOST(self, server, port, path, parameters, bearerChannel = 1):
def httpPOST(self, server, port, path, parameters, contentType="application/x-www-form-urlencoded", bearerChannel = 1):
"""
Makes HTTP POST request to the given server and script
@@ -431,10 +476,10 @@ class SimInetGSM(SimGsm):
[ "AT+HTTPINIT", 2000 ],
[ "AT+HTTPPARA=\"CID\",\"{0}\"".format(bearerChannel), 1000 ],
[ "AT+HTTPPARA=\"URL\",\"{0}:{1}{2}\"".format(server, port, path), 500 ],
[ "AT+HTTPPARA=\"CONTENT\",\"application/x-www-form-urlencoded\"", 500 ],
[ "AT+HTTPPARA=\"CONTENT\",\"{0}\"".format(contentType), 500 ],
[ "AT+HTTPPARA=\"UA\",\"{0}\"".format(self.userAgent), 500 ],
[ "AT+HTTPPARA=\"REDIR\",\"1\"", 500 ],
[ "AT+HTTPPARA=\"TIMEOUT\",\"45\"", 500 ]
[ "AT+HTTPPARA=\"TIMEOUT\",\"45\"", 500 ],
]
#executing commands sequence
@@ -445,7 +490,7 @@ class SimInetGSM(SimGsm):
#uploading data
self.logger.debug("uploading HTTP POST data")
ret = self.commandAndStdResult(
"AT+HTTPDATA={0},10000".format(len(parameters)),
"AT+HTTPDATA={0},60000".format(len(parameters)),
7000,
["DOWNLOAD", "ERROR"]
)
@@ -456,9 +501,14 @@ class SimInetGSM(SimGsm):
self.simpleWriteLn(parameters)
dataLine = self.readDataLine(500)
# if len(parameters)<32:
# self.simpleWriteLn(parameters)
# else:
# parameters = self.simpleWriteLns(chunks(parameters,32))
dataLine = self.readDataLine(maxWaitTime=20000)
if (dataLine is None) or (dataLine != "OK"):
self.setError("{0}: can't upload HTTP POST data".format(inspect.stack()[0][3]))
self.setError("{0}: can't upload HTTP POST data {1}".format(inspect.stack()[0][3], dataLine))
return
self.logger.debug("actually making request")
@@ -512,121 +562,115 @@ class SimInetGSM(SimGsm):
#
# return True
def httpPOST_DATA(self, server, port, path, parameters, bearerChannel = 1):
"""
Makes HTTP POST request to the given server and script
#
# int res= gsm.read(result, resultlength);
# //gsm.disconnectTCP();
# return res;
:param server: server (host) address
:param port: server port
:param path: path to the script
:param parameters: POST parameters
:param bearerChannel: bearer channel number
:return: true if operation was successfully finished. Otherwise returns false
"""
def httpPOST_CIP(self, server, port, path, parameters, contentType="application/x-www-form-urlencoded", bearerChannel = 1):
"""
Makes HTTP POST request to the given server and script
# self.__clearHttpResponse()
:param server: server (host) address
:param port: server port
:param path: path to the script
:param parameters: POST parameters
:param bearerChannel: bearer channel number
:return: true if operation was successfully finished. Otherwise returns false
"""
#TODO: close only when opened
# self.terminateHttpRequest()
self.__clearHttpResponse()
#HTTP POST request commands sequence
# simpleCommands = [
# [ "AT+HTTPINIT", 2000 ],
# [ "AT+HTTPPARA=\"CID\",\"{0}\"".format(bearerChannel), 1000 ],
# [ "AT+HTTPPARA=\"URL\",\"{0}:{1}{2}\"".format(server, port, path), 500 ],
# [ "AT+HTTPPARA=\"CONTENT\",\"multipart/form-data\"", 500 ],
# [ "AT+HTTPPARA=\"UA\",\"{0}\"".format(self.userAgent), 500 ],
# [ "AT+HTTPPARA=\"REDIR\",\"1\"", 500 ],
# [ "AT+HTTPPARA=\"TIMEOUT\",\"45\"", 500 ]
# ]
simpleCommands = [
[ "AT+CGATT?", 2000 ],
[ "AT+CIPCLOSE", 1000 ],
# [ "AT+CIPMUX=0", 1000 ],
[ "AT+CSTT=\"{0}\"".format('wholesale'), 1000],
[ "AT+CIICR", 500 ],
[ "AT+CIFSR", 500 ],
[ "AT+CIPSTART=\"{0}\",\"{1}\",\"{2}\"".format("TCP", server, port), 500 ],
[ "AT+CIPSEND", 500 ],
# [ "AT+HTTPPARA=\"TIMEOUT\",\"45\"", 500 ]
]
#executing commands sequence
if not self.execSimpleCommandsList(simpleCommands):
return False
#uploading data
# self.logger.debug("uploading HTTP POST data")
# ret = self.commandAndStdResult(
# "AT+HTTPDATA={0},10000".format(len(parameters)),
# 7000,
# ["DOWNLOAD", "ERROR"]
# )
# if (ret is None) or (self.lastResult != "DOWNLOAD"):
# self.setError("{0}: can't upload HTTP POST data".format(inspect.stack()[0][3]))
# return False
self.simpleWriteLn(parameters)
dataLine = self.readDataLine(500)
if (dataLine is None) or (dataLine != "OK"):
self.setError("{0}: can't upload HTTP POST data".format(inspect.stack()[0][3]))
return
self.logger.debug("actually making request")
#TODO: check CPU utilization
if not self.execSimpleOkCommand("AT+HTTPACTION=1", 15000):
return False
#reading HTTP request result
dataLine = self.readDataLine(15000)
if dataLine is None:
self.setError("{0}: empty HTTP request result string".format(inspect.stack()[0][3]))
return False
#parsing string like this "+HTTPACTION:0,200,15"
httpResult = self.__parseHttpResult(dataLine, bearerChannel)
if httpResult is None:
return False
#assigning HTTP result code
self.__httpResult = httpResult[0]
#it's can be bad http code, let's check it
if not self.___isOkHttpResponseCode(self.httpResult):
#TODO: close only when opened
self.terminateHttpRequest()
#HTTP POST request commands sequence
simpleCommands = [
# ["AT+CGATT=1", 1000 ],
# ["AT+CSNS=4,", 500 ],
# ["AT+CSTT=\"{}\",\"{}\",\"{}\"".format("wholesale", "", ""), 500 ],
# ["AT+CIICR", 500 ],
# ["AT+CIFSR", 10000 ]
[ "AT+CIPSTART=\"TCP\",\"{0}{2}\",\"{1}\"".format(server, port, path), 500 ],
[ "AT+CIPSEND=", 500 ],
]
#executing commands sequence
if not self.execSimpleCommandsList(simpleCommands):
return False
#uploading data
self.logger.debug("uploading HTTP POST data")
# ret = self.commandAndStdResult(
# "AT+HTTPDATA={0},10000".format(len(parameters)),
# 7000,
# ["DOWNLOAD", "ERROR"]
# )
# if (ret is None) or (self.lastResult != "DOWNLOAD"):
# self.setError("{0}: can't upload HTTP POST data".format(inspect.stack()[0][3]))
# return False
self.simpleWriteLn(parameters)
dataLine = self.readDataLine(500)
if (dataLine is None) or (dataLine != "OK"):
self.setError("{0}: can't upload HTTP POST data".format(inspect.stack()[0][3]))
return
# self.logger.debug("actually making request")
# #TODO: check CPU utilization
# if not self.execSimpleOkCommand("AT+HTTPACTION=1", 15000):
# return False
#reading HTTP request result
dataLine = self.readDataLine(15000)
if dataLine is None:
self.setError("{0}: empty HTTP request result string".format(inspect.stack()[0][3]))
return False
#parsing string like this "+HTTPACTION:0,200,15"
httpResult = self.__parseHttpResult(dataLine, bearerChannel)
if httpResult is None:
return False
#assigning HTTP result code
self.__httpResult = httpResult[0]
#it's can be bad http code, let's check it
if not self.___isOkHttpResponseCode(self.httpResult):
self.terminateHttpRequest()
return True
#when no data from server we just want go out, everything if OK
if (
(self.__isNoContentResponse(self.httpResult)) or
(not self.___isHttpResponseCodeReturnsData(self.httpResult))
):
self.terminateHttpRequest()
return True
responseLength = httpResult[1]
if responseLength == 0:
self.terminateHttpRequest()
return True
self.logger.debug("reading http request response data")
if not self.__readHttpResponse(0, responseLength):
return False
return True
#when no data from server we just want go out, everything if OK
if (
(self.__isNoContentResponse(self.httpResult)) or
(not self.___isHttpResponseCodeReturnsData(self.httpResult))
):
self.terminateHttpRequest()
return True
responseLength = httpResult[1]
if responseLength == 0:
self.terminateHttpRequest()
return True
# self.disconnectTcp()
#
# return True
self.logger.debug("reading http request response data")
if not self.__readHttpResponse(0, responseLength):
return False
return True
# self.disconnectTcp()
#
# return True
#
# int res= gsm.read(result, resultlength);
# //gsm.disconnectTCP();
# return res;
#
# int res= gsm.read(result, resultlength);
# //gsm.disconnectTCP();
# return res;

52
logging.ini Normal file
View File

@@ -0,0 +1,52 @@
[loggers]
keys=root,datahandling,instruments
[handlers]
keys=consoleHandler, rootHandler, instrumentsHandler, datahandlingHandler
[formatters]
keys=myFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler, rootHandler
[logger_datahandling]
level=INFO
handlers=datahandlingHandler
qualname=datahandling
[logger_instruments]
level=INFO
handlers=instrumentsHandler
qualname=instruments
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=myFormatter
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
formatter=myFormatter
args=("config.log",)
[handler_rootHandler]
class=FileHandler
formatter=myFormatter
args=("main_log.txt",)
[handler_instrumentsHandler]
class=FileHandler
formatter=myFormatter
args=("instruments_log.txt",)
[handler_datahandlingHandler]
class=FileHandler
formatter=myFormatter
args=("datahandling_log.txt",)
[formatter_myFormatter]
format=[%(asctime)-15s] [%(MISSION_TIME)-10s] %(MISSION_ID)-10s %(name)-25s %(levelname)-8s %(message)s
datefmt=%Y-%m-%d %H:%M:%S

212
main.py
View File

@@ -1,116 +1,174 @@
__author__ = 'asc'
DEBUG = True
import logging
import logging.handlers,logging.config
if DEBUG:
from instruments import Barometer_Debug2 as Barometer
from instruments import Camera_Debug2 as Camera
from datahandling import Datalogger_Debug as Datalogger
from datahandling import Datareporter_Debug2 as Datareporter
from system import System_Debug as System
import threading
import datetime
from instruments import Barometer, Camera
from datahandling import Datalogger, Datareporter
from system import System as System
from threading import Timer
from datahandling import Record
from mission import Mission
import random
import string
import configparser
# class ContextFilter(logging.Filter):
# """
# This is a filter which injects contextual information into the log.
# """
# def filter(self, record):
# record.MYVAR = MYVAR
# return True
# logging.basicConfig(level=logging.DEBUG, format=("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
# log = logging.getLogger("instruments")
# log.setLevel(logging.DEBUG)
# log.
# # log.setFo
log = logging.getLogger("instruments")
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)-15s %(name)-5s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
log.addHandler(handler)
log.setLevel(logging.DEBUG)
logging.config.fileConfig('logging.ini')
log = logging.getLogger('root')
log.addFilter(logging.Filter('root'))
# log.addFilter(ContextFilter())
#start-up
#log denotes write to local, report denotes sending to server
#TODO startup message
#loadconfig
config = configparser.ConfigParser()
config.sections()
config.read('config.ini')
refresh_camera_local = float(config['refresh rates']['refresh camera local'])
refresh_camera_transmit = float(config['refresh rates']['refresh camera transmit'])
from config import *
refresh_barometer_local = float(config['refresh rates']['refresh barometer local'])
refresh_barometer_transmit = float(config['refresh rates']['refresh barometer transmit'])
mission = Mission()
refresh_gps_local = float(config['refresh rates']['refresh gps local'])
refresh_gps_transmit = float(config['refresh rates']['refresh gps transmit'])
#todo test cell connection
reporter = Datareporter (
missiontime = mission,
use_lan = True,
url = url,
server_port = 5010,
data_path = data_path,
image_path = image_path,
ping_path = ping_path,
com_port_name=com_port_name,
baud_rate = baud_rate
)
low_quality_resolution = eval(config['camera settings']['low quality resolution'])
low_quality_compression_pct = int(config['camera settings']['low quality compression pct'])
t = Record({'zt':mission.timezero})
# mid = reporter.send(t) #reply of first message is the mission number
report_url = config['report settings']['report url']
report_image_url = config['report settings']['report image url']
# mission.set_mid(mid)
com_port_name = config['modem settings']['com port name']
baud_rate = config['modem settings']['baud rate']
notebook = Datalogger (
missiontime = mission,
text_path=None,
log_path=log_path,
photo_path=photo_path)
logger = Datalogger ()
log.debug ("System started")
# logger.log("Log Intiated")
log.debug ("System started", extra={'MISSION_TIME': "", 'MISSION_ID':""})
#system check
system = System()
system_status = system.status
log.debug (system.stats, extra={'MISSION_TIME': "", 'MISSION_ID':""})
# logger.log (system_status[1])
#set mission time
#todo test cell connection, log, report
reporter = Datareporter()
reporter_status = reporter.status
# logger.log (reporter_status[1])
reporter.send(reporter_status[1])
reporter.send(system_status[1])
log.info("Mission {} started, time zero is {}".format(mission.name, mission.timezero), extra={'MISSION_TIME': mission.now(), 'MISSION_ID':mission.name})
#TODO test camera, log, report
#intiate mission
# log.info('Sent {} to server'.format(intiate))
#TODO test camera
camera = Camera (low_quality_compression_pct=low_quality_compression_pct,
low_quality_resolution=low_quality_resolution)
camera_status = camera.status
low_quality_resolution=low_quality_resolution,
high_quality_compression_pct=high_quality_compression_pct,
high_quality_resolution=high_quality_resolution,
vflip=False,
hflip=False,
exposure_mode='sports',
debug=True
)
#todo test barometer, log, report
barometer = Barometer()
# barometer_status = barometer.status
# logger.log (barometer_status[1])
# reporter.send(barometer_status[1])
#todo test barometer
barometer = Barometer(debug=True)
#todo test GPS, log, report
#todo check for errors, throw exception if error
if(system_status[0] or reporter_status[0] or camera_status[0]):
raise Exception ('Error')
class scheduler ():
def scheduler (interval, worker_func, iterations = 0):
if iterations != 1:
threading.Timer (
interval,
scheduler, [interval, worker_func, 0 if iterations == 0 else iterations-1]
).start ()
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.is_running = False
self.start()
worker_func ()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
#while 1:
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
self._timer.start()
self.is_running = True
image = camera.capture()
data = barometer.read()
def stop(self):
self._timer.cancel()
self.is_running = False
def update_barometer_local():
global bar
bar = barometer.read()
bar.update({'mt':mission.now()})
record = Record(bar.copy(),'b')
log.info('Updating barometer...',extra={'MISSION_TIME': mission.now(), 'MISSION_ID':mission.name})
notebook.log(record)
def update_image_local():
global img
img = camera.capture(name=mission.now())
record = Record([img.get('hi'), img.get('lo')], 'i')
log.info('Updating image...', extra={'MISSION_TIME': mission.now(), 'MISSION_ID':mission.name})
notebook.log(record)
def submit_report():
global bar
global img
global counter
global transpondence
# reporter.create_transpondence()
if not transpondence:
log.info('Creating transpondence',extra={'MISSION_TIME': mission.now(), 'MISSION_ID':mission.name})
transpondence = Record()
if (counter % refresh_barometer_transmit) == 0:
log.info('Adding barometer data to transpondence',extra={'MISSION_TIME': mission.now(), 'MISSION_ID':mission.name})
transpondence.add(bar,'b')
if (counter % refresh_camera_transmit) == 0:
log.info('Adding image to transpondence',extra={'MISSION_TIME': mission.now(), 'MISSION_ID':mission.name})
transpondence.add(img.get('lo'),'i')
log.info('Sending transpondence', extra={'MISSION_TIME': mission.now(), 'MISSION_ID':mission.name})
transpondence = reporter.send(transpondence) #returns none on success, (bad practice?)
counter += 1
log.debug('Counter = {}'.format(counter), extra={'MISSION_TIME': mission.now(), 'MISSION_ID':mission.name})
counter=1
img = None
bar = None
transpondence = None
scheduler(1, update_barometer_local)
scheduler(refresh_camera_local, update_image_local)
scheduler(2, submit_report)
# logger.log(image.get('hi'), type='image')
# logger.log(barometer, type='data')
reporter.send(image.get('lo'), type='data')
reporter.send(barometer, type = 'data')
#todo break apart log and report refresh
# scheduler(refresh_barometer_local, update_barometer, 2)
# scheduler(refresh_camera_local, update_camera, 2)
pass

43
mission.py Normal file
View File

@@ -0,0 +1,43 @@
__author__ = 'asc'
import time
import datetime
import random, string
# def generate_mid():
# new_mission_id = ''.join(random.SystemRandom().choice(string.ascii_uppercase) for _ in range(2))
# return new_mission_id
class Mission():
def __init__(self, timezero=None, mid=None):
if not timezero:
timezero = datetime.datetime.now()
self._zero=timezero
self._mid = mid
# self._mid=generate_mid()
def set_mid(self, mid):
self._mid = mid
def now(self):
#returns a string
d=datetime.datetime.now()-self._zero
return d.total_seconds()
@property
def timezero(self):
return self._zero
def to_absolutetime(self, mission_time):
return self._zero + datetime.timedelta(seconds=float(mission_time))
@property
def mid(self):
if self._mid:
mid = self._mid
else:
mid= None
return self._mid
@property
def name(self):
return self._mid

28
post_encode.py Normal file
View File

@@ -0,0 +1,28 @@
__author__ = 'asc'
def encode_multipart_formdata(fields, files):
"""
fields is a sequence of (name, value) elements for regular form fields.
files is a sequence of (name, filename, value) elements for
data to be uploaded as files
Return (content_type, body) ready for http.client connection instance
"""
BOUNDARY_STR = '----------ThIs_Is_tHe_bouNdaRY_$'
CRLF = bytes("\r\n","ASCII")
L = []
for (key, value) in fields:
L.append(bytes("--" + BOUNDARY_STR,"ASCII"))
L.append(bytes('Content-Disposition: form-data; name="%s"' % key,"ASCII"))
L.append(b'')
L.append(bytes(value,"ASCII"))
for (key, filename, value) in files:
L.append(bytes('--' + BOUNDARY_STR,"ASCII"))
L.append(bytes('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename),"ASCII"))
L.append(bytes('Content-Type: %s' % get_content_type(filename),"ASCII"))
L.append(b'')
L.append(value)
L.append(bytes('--' + BOUNDARY_STR + '--',"ASCII"))
L.append(b'')
body = CRLF.join(L)
content_type = 'multipart/form-data; boundary=' + BOUNDARY_STR
return content_type, body

View File

@@ -3,7 +3,7 @@ import os
import psutil
class System_Debug():
class System():
def __init__(self):
#TODO add system initialization
@@ -17,7 +17,7 @@ class System_Debug():
import os
# Return CPU temperature as a character string
def getCPUtemperature():
def getCPUtemperature(self):
res = os.popen('vcgencmd measure_temp').readline()
return(res.replace("temp=","").replace("'C\n",""))
@@ -25,22 +25,20 @@ class System_Debug():
p=psutil.cpu_percent(interval=1)
return p
def getRAMinfo():
p = psutil.virtual_memory()
p = psutil.swap_memory()
def getRAMinfo(self):
p = dict(psutil.virtual_memory()._asdict())
p = dict(psutil.swap_memory()._asdict())
return p
def getDiskSpace():
p = psutil.disk_usage('/')
def getDiskSpace(self):
p = dict(psutil.disk_usage('/')._asdict())
return p
@property
def stats (self):
#courtesy of Phillipe
# https://www.raspberrypi.org/forums/memberlist.php?mode=viewprofile&u=40834&sid=dd38cc12161ac10b324ed2a2238972d3
# CPU informatiom
CPU_temp = self.getCPUtemperature()
CPU_usage = self.getCPUuse()
CPU_usage = self.getCPUusage()
# RAM information
# Output is in kb, here I convert it in Mb for readability
@@ -50,7 +48,13 @@ class System_Debug():
# Disk information
DISK_stats = self.getDiskSpace()
pid = os.getpid()
# print(type(DISK_stats._asdict()))
return {"cpu_temp":CPU_temp,
"cpu_usage":CPU_usage,
"ram_stats":RAM_stats,
"disk_stats":DISK_stats}
"disk_stats":DISK_stats,
"PID":pid}

160
test.py
View File

@@ -1,80 +1,110 @@
import csv, requests
from instruments import Camera_Debug2 as Camera
from datahandling import Datareporter_Debug3 as Datareporter
import serial
import sys
import json
__author__ = 'asc'
DEBUG = True
import logging
import logging.handlers,logging.config
REPORTIMAGETOURL = "http://10.0.1.4:5010/photo"
from instruments import Barometer, Camera
from datahandling import Datalogger, Datareporter
from system import System as System
import threading
from datahandling import Record
from mission import Mission
import random
import string
LOG_FILE = "log2.txt"
logging.config.fileConfig('logging.ini')
import binascii
log = logging.getLogger(__name__)
# log = logging.basicConfig()
# log1 = logging.getLogger("instruments")
# log2 = logging.getLogger("datahandling")
# handler = logging.handlers.RotatingFileHandler('spaceballoon.log', backupCount=5)
import base64
from base64 import urlsafe_b64encode
formatter = logging.Formatter('[%(asctime)-25s] %(name)-15s %(levelname)-8s %(message)s')
# handler.setFormatter(formatter)
# log.addHandler(handler)
# log1.addHandler(handler)
# log2.addHandler(handler)
log.setLevel(logging.DEBUG)
# log1.setLevel(logging.DEBUG)
# log2.setLevel(logging.INFO)
with open('log2.txt','rb') as f:
g=f.read()
e=urlsafe_b64encode(g)
#start-up
#log denotes write to local, report denotes sending to server
# a=binascii.b2a_uu(f)
#loadconfig
from config import *
# e = f.encode('base64')
mission = Mission()
# e = str(e, "ascii")
#todo test cell connection
reporter = Datareporter (
missiontime = mission,
use_lan = True,
url = "http://10.0.1.4",
server_port = 5010,
data_path = "upload-data",
image_path = "missions",
ping_path = "ping",
com_port_name="/dev/ttyAMA0",
baud_rate = 9600
)
data = {"temp":1,
"press":3,
"altitude":2,
"image":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
#intiate mission
# data = json.dumps(data,ensure_ascii=True)
# camera = Camera(low_quality_resolution=(320, 180),
# low_quality_compression_pct=50)
#TODO test camera
camera = Camera (low_quality_compression_pct=low_quality_compression_pct,
low_quality_resolution=low_quality_resolution,
high_quality_compression_pct=high_quality_compression_pct,
high_quality_resolution=high_quality_resolution,
vflip=False,
hflip=False,
exposure_mode='sports',
# debug=True
)
# image = camera.capture()
#todo test barometer
barometer = Barometer()
# print (image)
#todo test GPS, log, report
#todo check for errors, throw exception if error
def scheduler (interval, worker_func, iterations = 0):
if iterations != 1:
threading.Timer (
interval,
scheduler, [interval, worker_func, 0 if iterations == 0 else iterations-1]
).start ()
worker_func ()
def update_barometer_local():
global bar
bar = barometer.read()
bar.update({'mt':mission.now()})
record = Record(bar.copy(),'b')
# notebook.log(record)
def update_image_local():
global img
img = camera.capture(name=mission.now(), thumbnail=None)
record = Record([img.get('hi'), img.get('lo')], 'i')
# notebook.log(record)
transpondence = Record()
update_image_local()
transpondence.add(img.get('lo'),'i')
transpondence = reporter.send(transpondence) #returns none on success, (bad practice?)
counter=1
img = None
bar = None
transpondence = None
pass
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
# i=open('resized_image.jpg','rb')
# r=i.read()
# def callback(encoder, bytes_read):
# # Do something with this information
# pass
#
# monitor = MultipartEncoderMonitor.from_fields(
# fields={'field0': 'value0'}, callback
# )
# m = MultipartEncoder(fields={"field":("image", open("resized_image.jpg", 'rb'), "image/jpeg")})
#
reporter = Datareporter (content_type="form-data")
#
# f = open('resized_image.jpg','rb')
# r=f.read()
#report image
# response = requests.post("http://home.ascorrea.com:5010/upload-file", data=m.read())
# print(type(r))
# reporter.send(str(r)[2:-5].replace("\\\", "\\"), type="image")
# reporter.send("Content-Disposition: {0}; name=\"{1}\"; filename=\"{2}\" {4}".format(
# 'application/octet-stream',
# 'field',
# 'resized_image',
# 'image/jpeg',""), type="image")
reporter.send(data, type="image")
pass

89
test_http.py Normal file
View File

@@ -0,0 +1,89 @@
#!/usr/bin/python3
from test_shared import *
from lib.sim900.inetgsm import SimInetGSM
COMPORT_NAME = "/dev/ttyAMA0"
BAUD_RATE = 9600
#logging levels
CONSOLE_LOGGER_LEVEL = logging.INFO
LOGGER_LEVEL = logging.INFO
def main():
"""
Tests HTTP GET and POST requests.
:return: true if everything was OK, otherwise returns false
"""
#adding & initializing port object
port = initializeUartPort(portName=COMPORT_NAME, baudrate=BAUD_RATE)
#initializing logger
(formatter, logger, consoleLogger,) = initializeLogs(LOGGER_LEVEL, CONSOLE_LOGGER_LEVEL)
#making base operations
d = baseOperations(port, logger)
if d is None:
return False
(gsm, imei) = d
inet = SimInetGSM(port, logger)
logger.info("attaching GPRS")
if not inet.attachGPRS("internet", "", "", 1):
logger.error("error attaching GPRS")
return False
logger.info("ip = {0}".format(inet.ip))
#making HTTP GET request
logger.info("making HTTP GET request")
if not inet.httpGet(
"http://httpbin.org",
80,
"/ip"
):
logger.error("error making HTTP GET request: {0}".format(inet.errorText))
return False
logger.info("httpResult = {0}".format(inet.httpResult))
if inet.httpResponse is not None:
response = str(inet.httpResponse).replace("\n\r", "\n")
logger.info("response: \"{0}\"".format(response))
else:
logger.info("empty response")
#making 3 http post requests
for i in range(3):
logger.info("making HTTP POST request #{0}".format(i))
if not inet.httpPOST(
"home.ascorrea.com",
5010,
"/report-encoded?action=change&ip=test2",
"action=change&ip=test"
):
print("[FAILED]")
return False
logger.info("httpResult = {0}".format(inet.httpResult))
if inet.httpResponse is not None:
response = str(inet.httpResponse).replace("\n\r", "\n")
logger.info("response: \"{0}\"".format(response))
else:
logger.info("empty response")
logger.debug("detaching GPRS")
if not inet.dettachGPRS():
logger.error("error detaching GRPS: {0}".format(inet.errorText))
return False
gsm.closePort()
return True
if __name__ == "__main__":
main()
print("DONE")

88
test_http_2.py Normal file
View File

@@ -0,0 +1,88 @@
#!/usr/bin/python3
from test_shared import *
from lib.sim900.inetgsm import SimInetGSM
COMPORT_NAME = "/dev/ttyAMA0"
BAUD_RATE = 9600
#logging levels
CONSOLE_LOGGER_LEVEL = logging.INFO
LOGGER_LEVEL = logging.INFO
def main():
"""
Tests HTTP GET and POST requests.
:return: true if everything was OK, otherwise returns false
"""
#adding & initializing port object
port = initializeUartPort(portName=COMPORT_NAME, baudrate=BAUD_RATE)
#initializing logger
(formatter, logger, consoleLogger,) = initializeLogs(LOGGER_LEVEL, CONSOLE_LOGGER_LEVEL)
#making base operations
d = baseOperations(port, logger)
if d is None:
return False
(gsm, imei) = d
inet = SimInetGSM(port, logger)
logger.info("attaching GPRS")
if not inet.attachGPRS("internet", "", "", 1):
logger.error("error attaching GPRS")
return False
logger.info("ip = {0}".format(inet.ip))
#making HTTP GET request
logger.info("making HTTP GET request")
if not inet.httpGet(
"http://httpbin.org",
80,
"/ip"
):
logger.error("error making HTTP GET request: {0}".format(inet.errorText))
return False
logger.info("httpResult = {0}".format(inet.httpResult))
if inet.httpResponse is not None:
response = str(inet.httpResponse).replace("\n\r", "\n")
logger.info("response: \"{0}\"".format(response))
else:
logger.info("empty response")
if not inet.httpPOST(
"home.ascorrea.com",
5010,
"/upload-file",
content="image/jpeg",
parameters=str({"data":open('temp_img_lo.jpg','rb').read()))
):
print("[FAILED]")
return False
logger.info("httpResult = {0}".format(inet.httpResult))
if inet.httpResponse is not None:
response = str(inet.httpResponse).replace("\n\r", "\n")
logger.info("response: \"{0}\"".format(response))
else:
logger.info("empty response")
logger.debug("detaching GPRS")
if not inet.dettachGPRS():
logger.error("error detaching GRPS: {0}".format(inet.errorText))
return False
gsm.closePort()
return True
if __name__ == "__main__":
main()
print("DONE")