commit 10/31/2015

This commit is contained in:
2015-10-31 00:00:00 -06:00
committed by Anthony Correa
parent 8d45de4f2d
commit 5e9b03a651
21 changed files with 3109 additions and 395 deletions

View File

@@ -1,21 +1,33 @@
[refresh rates]
refresh camera local = 2
refresh camera transmit = 5
refresh camera local = 5
refresh camera transmit = 10
refresh barometer local = 10
refresh barometer local = 1
refresh barometer transmit = 5
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 = 10
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 = "upload-file"
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']

View File

@@ -6,28 +6,124 @@ import requests
import json
from cell_connection import initializeUartPort, baseOperations, initializeLogs
import logging
from requests_toolbelt import MultipartEncoder
import post_encode
import base64
import binascii
import array
import csv
# logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
LOGGER_LEVEL=logging.DEBUG
CONSOLE_LOGGER_LEVEL=logging.DEBUG
# LOGGER_LEVEL=logging.DEBUG
# CONSOLE_LOGGER_LEVEL=logging.DEBUG
#
SCRIPTDIR = os.path.dirname(os.path.abspath(__file__))
logger = logging.getLogger(__name__)
# handler = logging.StreamHandler()
# formatter = logging.Formatter('[%(asctime)-15s] %(name)-5s %(levelname)-8s %(message)s')
# handler.setFormatter(formatter)
# logger.addHandler(handler)
# logger.setLevel(logging.INFO)
# logger_gsm = logging.getLogger("cell_connection")
# handler = logging.StreamHandler()
# formatter = logging.Formatter('[%(asctime)-15s] %(name)-5s %(levelname)-8s %(message)s')
# handler.setFormatter(formatter)
# logger_gsm.addHandler(handler)
# logger_gsm.setLevel(logging.DEBUG)
# logger_1 = logging.getLogger("gsm")
# handler = logging.StreamHandler()
# formatter = logging.Formatter('[%(asctime)-15s] %(name)-5s %(levelname)-8s %(message)s')
# handler.setFormatter(formatter)
# logger_1.addHandler(handler)
# logger_1.setLevel(logging.DEBUG)
#
# logger_2 = logging.getLogger("intetgsm")
# handler = logging.StreamHandler()
# formatter = logging.Formatter('[%(asctime)-15s] %(name)-5s %(levelname)-8s %(message)s')
# handler.setFormatter(formatter)
# logger_2.addHandler(handler)
# logger_2.setLevel(logging.DEBUG)
def add_keys_if_necessary(existing_file, test_dict):
all=None
try:
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_Debug ():
def __init__(self, path=SCRIPTDIR):
def __init__(self, text_path,
log_path,
photo_path):
self.text_path=text_path
self.log_path=os.path.join(log_path, datetime.datetime.now().strftime("Log %Y%m%d-%H%M%S.csv"))
self.photo_path=photo_path
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":
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
@@ -58,7 +154,7 @@ class Datareporter_Debug2 ():
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"):
baud_rate = 9600):
self.report_url = report_url
self.report_image_url = report_image_url
self.com_port_name = com_port_name,
@@ -106,126 +202,168 @@ class Datareporter_Debug3 ():
#Debug 2 sends from cell to server
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,
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.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
self.ping_path = ping_path
self.use_lan = use_lan
self.port = initializeUartPort(portName=self.com_port_name, baudrate=self.baud_rate)
if not use_lan:
d = baseOperations(self.port, logger)
if not d is None:
# return None
(self.gsm, self.imei) = d
self.inet = self.SimInetGSM(self.port, logger)
logger.info("ip = {0}".format(self.inet.ip))
pass
@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)
def send(self, message, type="text"):
if type == "text":
def send(self, message, message_type):
# logger.debug("Message.read is {}".format(message.read()))
if message_type == "ping":
#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))
contentType='text/xml'
if self.use_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("making HTTP POST request from cell")
logger.info("attaching GPRS")
if not self.inet.attachGPRS("wholesale", "", "", 1):
logger.error("error attaching GPRS")
return False
if not self.inet.httpPOST(
self.url,
self.server_port,
"/{}".format(self.ping_path),
json.dumps(message),
contentType=contentType
):
logger.error("error making HTTP GET 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":
contentType="mulipart/form-data"
re=message
response=None
m = MultipartEncoder(fields={'image': ('image', message, 'image/jpeg')})
if self.use_lan:
# req.add_header('Content-Type', contentType)
response = requests.post("{0}:{1}/{2}".format(self.url, self.server_port, self.image_path), data=m.read(), headers={'Content-Type': m.content_type})
# response = requests.post("{0}:{1}/{2}".format(self.url, self.server_port, self.image_path), data=m)
# response = response.text()
pass
elif not self.use_lan:
logger.info("attaching GPRS")
if not self.inet.attachGPRS("wholesale", "", "", 1):
logger.error("error attaching GPRS")
return False
logger.info("ip = {0}".format(self.inet.ip))
#making HTTP GET request
logger.info("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"
#adding & initializing port object
port = initializeUartPort(portName=self.com_port_name, baudrate=self.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 = self.SimInetGSM(port, logger)
logger.info("attaching GPRS")
if not inet.attachGPRS("wholesale", "", "", 1):
logger.error("error attaching GPRS")
return False
logger.info("ip = {0}".format(inet.ip))
elif message_type == "data":
#making HTTP GET request
logger.info("making HTTP POST request")
contentType="application/json"
message["sent"] = str(datetime.datetime.now())
if not inet.httpPOST_DATA(
"home.ascorrea.com",
5010,
"/upload-file",
# content=self.content_type,
parameters="{0}".format(message)
):
logger.error("error making HTTP GET post: {0}".format(inet.errorText))
return False
if self.use_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
logger.info("httpResult = {0}".format(inet.httpResult))
if inet.httpResponse is not None:
response = str(inet.httpResponse).replace("\n\r", "\n")
elif not self.use_lan:
logger.info("making HTTP POST request from cell")
logger.info("attaching GPRS")
if not self.inet.attachGPRS("wholesale", "", "", 1):
logger.error("error attaching GPRS")
return False
if not self.inet.httpPOST(
self.url,
self.server_port,
"/{}".format(self.data_path),
json.dumps(message),
contentType=contentType
):
logger.error("error making HTTP GET post: {0}".format(self.inet.errorText))
return False
if response is not None:
response = str(self.inet.httpResponse).replace("\n\r", "\n")
else:
response = ("empty response")
if response is not None:
logger.info("response: \"{0}\"".format(response))
else:
logger.info("empty response")
elif type == "data":
#adding & initializing port object
port = initializeUartPort(portName=self.com_port_name, baudrate=self.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 = 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))
else:
else:
logger.info("empty response")

View File

@@ -65,15 +65,24 @@ class Camera_Debug():
#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):
def __init__(self, low_quality_resolution=(320,240),
low_quality_compression_pct=5,
high_quality_resolution=(2592,1944),
high_quality_compression_pct=85,
**kwargs):
logger.debug("Initializing camera")
time.sleep(1)
self.low_quality_resolution = low_quality_resolution
self.low_quality_compression_pct = low_quality_compression_pct
self._cam = picamera.PiCamera()
self.high_quality_resolution = high_quality_resolution
self.high_quality_compression_pct = high_quality_compression_pct
self.kwargs=kwargs
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")
pass
@@ -81,22 +90,33 @@ class Camera_Debug2():
def status (self):
return (0, "Camera functioning properly")
def capture (self, no_low_quality=False, no_high_quality=False):
def capture (self, 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")
self._cam.capture("temp_img_hi.jpg",
resize=self.high_quality_resolution,
quality=self.high_quality_compression_pct,
# **kwargs
)
img_hi = open("temp_img_hi.jpg", 'rb')
logger.debug('High quality photo taken, file: {}'.format(img_hi))
with open("temp_img_hi.jpg", 'rb') as f:
img_hi = f.read()
logger.debug('High quality photo taken')
if not no_low_quality:
logger.debug('Taking low quality photo (Resolution: {}, JPEG Quality: {}%'.format(self.low_quality_resolution, self.low_quality_compression_pct))
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)
img_lo = open("temp_img_lo.jpg", 'rb')
logger.debug('Low quality photo taken, file: {}'.format(img_lo))
quality=self.low_quality_compression_pct,
# **kwargs
)
with open("temp_img_lo.jpg", 'rb') as f:
img_lo = f.read()
logger.debug('Low quality photo taken')
return ({"hi":img_hi,
"lo":img_lo})

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;

135
main.py
View File

@@ -6,9 +6,10 @@ 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 datahandling import Datareporter_Debug3 as Datareporter
from system import System_Debug as System
import threading
import datetime
import configparser
@@ -19,13 +20,19 @@ import configparser
# log.
# # log.setFo
log = logging.getLogger("instruments")
handler = logging.StreamHandler()
log = logging.getLogger(__name__)
log1 = logging.getLogger("instruments")
log2 = logging.getLogger("datahandling")
handler = logging.FileHandler('myapp.log')
formatter = logging.Formatter('%(asctime)-15s %(name)-5s %(levelname)-8s %(message)s')
formatter = logging.Formatter('[%(asctime)-15s] %(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)
#start-up
#log denotes write to local, report denotes sending to server
@@ -33,62 +40,48 @@ log.setLevel(logging.DEBUG)
#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'])
refresh_gps_local = float(config['refresh rates']['refresh gps local'])
refresh_gps_transmit = float(config['refresh rates']['refresh gps transmit'])
low_quality_resolution = eval(config['camera settings']['low quality resolution'])
low_quality_compression_pct = int(config['camera settings']['low quality compression pct'])
report_url = config['report settings']['report url']
report_image_url = config['report settings']['report image url']
com_port_name = config['modem settings']['com port name']
baud_rate = config['modem settings']['baud rate']
logger = Datalogger ()
notebook = Datalogger (text_path=None,
log_path=log_path,
photo_path=photo_path)
log.debug ("System started")
# logger.log("Log Intiated")
#system check
system = System()
system_status = system.status
# logger.log (system_status[1])
log.debug (system.stats)
#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])
#todo test cell connection
reporter = Datareporter (
use_lan = True,
url = "http://home.ascorrea.com",
server_port = 5010,
data_path = "upload-data",
image_path = "upload-file",
ping_path = "ping",
com_port_name="/dev/ttyAMA0",
baud_rate = 9600)
#TODO test camera, log, report
#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=True,
hflip=True,
exposure_mode='sports'
)
#todo test barometer, log, report
#todo test barometer
barometer = Barometer()
# barometer_status = barometer.status
# logger.log (barometer_status[1])
# reporter.send(barometer_status[1])
#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')
def scheduler (interval, worker_func, iterations = 0):
if iterations != 1:
threading.Timer (
@@ -98,19 +91,55 @@ def scheduler (interval, worker_func, iterations = 0):
worker_func ()
#while 1:
image = camera.capture()
data = barometer.read()
def update_barometer_local():
global bar
bar = barometer.read()
notebook.log(bar, message_type="data")
# logger.log(image.get('hi'), type='image')
# logger.log(barometer, type='data')
reporter.send(image.get('lo'), type='data')
reporter.send(barometer, type = 'data')
def update_image_local():
global img
img = camera.capture()
notebook.log(img.get('hi'), message_type="image")
#todo break apart log and report refresh
# scheduler(refresh_barometer_local, update_barometer, 2)
# scheduler(refresh_camera_local, update_camera, 2)
scheduler(2, update_barometer_local)
scheduler(5, update_image_local)
counter=1
while counter>0:
global img
global bar
# bar=None
# img=None
try:
reporter.send(1, message_type="ping")
# if (counter % refresh_camera_local) == 0:
# if not img:
# img = camera.capture()
# notebook.log(img.get('hi'), message_type="image")
if (counter % refresh_barometer_transmit) == 0:
if not bar:
bar = barometer.read()
reporter.send(bar, message_type="data")
if (counter % refresh_camera_transmit) == 0:
# log.debug('Need to transmit picture, {}'.format(img))
if not img:
img = camera.capture()
reporter.send(img.get('lo'), message_type="image")
if (counter % refresh_system) == 0:
log.debug(system.stats)
except Exception as e:
log.debug("Exception: {}".format(e))
continue
counter += 1
log.debug("Counter = {}".format(counter))
pass

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

@@ -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}

112
test.py
View File

@@ -1,80 +1,62 @@
import csv, requests
from instruments import Camera_Debug2 as Camera
from datahandling import Datareporter_Debug3 as Datareporter
from system import System_Debug
import serial
import sys
import json
REPORTIMAGETOURL = "http://10.0.1.4:5010/photo"
LOG_FILE = "log2.txt"
import binascii
import logging
import base64
from base64 import urlsafe_b64encode
from requests_toolbelt import MultipartEncoder
with open('log2.txt','rb') as f:
g=f.read()
e=urlsafe_b64encode(g)
logging.getLogger("datahandling")
# a=binascii.b2a_uu(f)
# e = f.encode('base64')
# e = str(e, "ascii")
data = {"temp":1,
"press":3,
"altitude":2,
"image":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
# data = json.dumps(data,ensure_ascii=True)
# camera = Camera(low_quality_resolution=(320, 180),
# low_quality_compression_pct=50)
# image = camera.capture()
# print (image)
data = {"temp":1,"press":3,"altitude":2,"cheetas":"just enough"}
pass
from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
com_port_name = "/dev/ttyAMA0"
image = open("resized_image.jpg", 'rb')
# image = image.read()
# e=base64.b64encode(image)
# e=b'alksdjnfkljanslkjnaklsfnglkanlkfgnlakalksdjnaklsfnglkajnaklsfnglkanlkfgnlakalksdjnfkljanslkjnglkjnaklsfnglkanlkfgnlakalksdjnfkljanslkjnglknlkfgnlakalksdjnfkljanslkjnglkanlkfgnlakalksdjnfkljanngnlakalksdjnfkljanslkjnglkanlkfgnlakalksdjnfkljannlkanlkfgnlakalksdjnfkljannglkanlkfgnlakalksdjnfkljannglkanlkfgnlakalksdjnfkl=='
# m = MultipartEncoder(fields={'image': ('image', image, 'image/jpeg')})
report = Datareporter (
use_lan = True,
url = "http://home.ascorrea.com",
server_port = 5010,
data_path = "upload-data",
image_path = "upload-file",
com_port_name="/dev/ttyAMA0",
ping_path="ping",
baud_rate = 9600)
camera = Camera (low_quality_compression_pct=10,
low_quality_resolution=(320, 240),
high_quality_compression_pct=100,
high_quality_resolution=(2592,1944),
vflip=True,
hflip=True,
exposure_mode='sports'
)
# print (report.check())
# print (image)
# report.send(m.to_string(), message_type="image")
# report.send(data, message_type="data")
img = camera.capture()
report.send(img.get('lo'), message_type="image")
# report.send(image, message_type="image")
# i=open('resized_image.jpg','rb')
# r=i.read()
# execfile("test_sms.py")
# 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")
# report.send("1",message_type="ping")
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")