mirror of
https://github.com/RfidResearchGroup/ChameleonMini.git
synced 2026-05-12 11:20:37 -07:00
update to RevG
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
import sys
|
||||
import datetime
|
||||
import time
|
||||
import Chameleon
|
||||
|
||||
class Device:
|
||||
COMMAND_VERSION = "VERSION"
|
||||
COMMAND_UPLOAD = "UPLOAD"
|
||||
COMMAND_DOWNLOAD = "DOWNLOAD"
|
||||
COMMAND_SETTING = "SETTING"
|
||||
COMMAND_UID = "UID"
|
||||
COMMAND_CONFIG = "CONFIG"
|
||||
COMMAND_LOG_DOWNLOAD = "LOGDOWNLOAD"
|
||||
COMMAND_LOG_CLEAR = "LOGCLEAR"
|
||||
COMMAND_LOGMODE = "LOGMODE"
|
||||
COMMAND_LBUTTON = "LBUTTON"
|
||||
COMMAND_RBUTTON = "RBUTTON"
|
||||
COMMAND_GREEN_LED = "LEDGREEN"
|
||||
COMMAND_RED_LED = "LEDRED"
|
||||
|
||||
STATUS_CODE_OK = 100
|
||||
STATUS_CODE_OK_WITH_TEXT = 101
|
||||
STATUS_CODE_WAITING_FOR_XMODEM = 110
|
||||
STATUS_CODE_FALSE = 120
|
||||
STATUS_CODE_TRUE = 121
|
||||
STATUS_CODE_UNKNOWN_COMMAND = 200
|
||||
STATUS_CODE_UNKNOWN_COMMAND_USAGE = 201
|
||||
STATUS_CODE_INVALID_PARAMETER = 202
|
||||
|
||||
STATUS_CODES_SUCCESS = [
|
||||
STATUS_CODE_OK,
|
||||
STATUS_CODE_OK_WITH_TEXT,
|
||||
STATUS_CODE_WAITING_FOR_XMODEM,
|
||||
STATUS_CODE_FALSE,
|
||||
STATUS_CODE_TRUE
|
||||
]
|
||||
|
||||
STATUS_CODES_FAILURE = [
|
||||
STATUS_CODE_UNKNOWN_COMMAND,
|
||||
STATUS_CODE_UNKNOWN_COMMAND_USAGE,
|
||||
STATUS_CODE_INVALID_PARAMETER
|
||||
]
|
||||
|
||||
LINE_ENDING = "\r"
|
||||
SUGGEST_CHAR = "?"
|
||||
SET_CHAR = "="
|
||||
GET_CHAR = "?"
|
||||
|
||||
def __init__(self, verboseFunc = None):
|
||||
self.verboseFunc = verboseFunc
|
||||
self.serial = serial.Serial(None, 9600, timeout=5.0)
|
||||
self.versionString = ""
|
||||
self.supportedConfs = []
|
||||
|
||||
def verboseLog(self, text):
|
||||
if (self.verboseFunc):
|
||||
self.verboseFunc(text)
|
||||
|
||||
def listDevices():
|
||||
devices = []
|
||||
|
||||
for port in serial.tools.list_ports.grep("({0:04x}:{1:04x})|({0:04X}:{1:04X})".format(Chameleon.USB_VID, Chameleon.USB_PID)):
|
||||
devices.append(port[0])
|
||||
|
||||
return devices
|
||||
|
||||
def connect(self, comport):
|
||||
|
||||
self.serial.port = comport
|
||||
try:
|
||||
self.serial.open()
|
||||
except:
|
||||
pass
|
||||
|
||||
if (self.serial.isOpen()):
|
||||
# Send escape key to force clearing the Chameleon's input buffer
|
||||
self.serial.write(b"\x1B")
|
||||
self.verboseLog("Opening serial port {} succeeded".format(comport))
|
||||
else:
|
||||
self.verboseLog("Opening serial port {} failed".format(comport))
|
||||
return False
|
||||
|
||||
# Try to retrieve chameleons version information and supported confs
|
||||
result = self.getSetCmd(self.COMMAND_VERSION)
|
||||
|
||||
if (result is not None):
|
||||
if (result['statusCode'] == self.STATUS_CODE_OK_WITH_TEXT):
|
||||
self.versionString = result['response']
|
||||
else:
|
||||
return False
|
||||
|
||||
result = self.getCmdSuggestions(self.COMMAND_CONFIG)
|
||||
|
||||
if (result['statusCode'] == self.STATUS_CODE_OK_WITH_TEXT):
|
||||
self.supportedConfs = result['response'].split(",")
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def disconnect(self):
|
||||
self.verboseLog("Closing serial port")
|
||||
self.serial.close()
|
||||
|
||||
def isConnected(self):
|
||||
return self.serial.isOpen()
|
||||
|
||||
def read(self, size=1024, timeout=0.01):
|
||||
self.serial.timeout = timeout
|
||||
data = self.serial.read(size)
|
||||
self.serial.timeout = 5.0
|
||||
return data
|
||||
|
||||
def writeCmd(self, cmd):
|
||||
# Execute command
|
||||
cmdLine = cmd + self.LINE_ENDING
|
||||
self.serial.write(cmdLine.encode('ascii'))
|
||||
|
||||
# Get status response
|
||||
status = self.serial.readline().decode('ascii').rstrip()
|
||||
|
||||
if (len(status) == 0):
|
||||
self.verboseLog("Executing <{}>: Timeout".format(cmd))
|
||||
return None
|
||||
else:
|
||||
self.verboseLog("Executing <{}>: {}".format(cmd, status))
|
||||
|
||||
statusCode, statusText = status.split(":")
|
||||
statusCode = int(statusCode)
|
||||
|
||||
result = {'statusCode': statusCode, 'statusText': statusText, 'response': None}
|
||||
|
||||
if (statusCode == self.STATUS_CODE_OK_WITH_TEXT):
|
||||
result['response'] = self.readResponse()
|
||||
elif (statusCode == self.STATUS_CODE_TRUE):
|
||||
result['response'] = True
|
||||
elif (statusCode == self.STATUS_CODE_FALSE):
|
||||
result['response'] = False
|
||||
|
||||
return result
|
||||
|
||||
def readResponse(self):
|
||||
# Read response to command, if any
|
||||
response = self.serial.readline().decode('ascii').rstrip()
|
||||
self.verboseLog("Response: {}".format(response))
|
||||
return response
|
||||
|
||||
def execCmd(self, cmd, args=None):
|
||||
if (args is None):
|
||||
return self.writeCmd("{}".format(cmd))
|
||||
else:
|
||||
return self.writeCmd("{} {}".format(cmd, args))
|
||||
|
||||
def getSetCmd(self, cmd, arg=None):
|
||||
# Determine if set or get mode
|
||||
if (arg is None):
|
||||
return self.writeCmd("{}{}".format(cmd, self.GET_CHAR))
|
||||
else:
|
||||
return self.writeCmd("{}{}{}".format(cmd, self.SET_CHAR, arg))
|
||||
|
||||
def getCmdSuggestions(self, cmd):
|
||||
result = self.getSetCmd(cmd, self.SUGGEST_CHAR)
|
||||
if (result['response'] is not None):
|
||||
result['suggestions'] = result['response'].split(",")
|
||||
|
||||
return result
|
||||
|
||||
def cmdUploadDump(self, dataStream):
|
||||
if (self.execCmd(self.COMMAND_UPLOAD)['statusCode'] == self.STATUS_CODE_WAITING_FOR_XMODEM):
|
||||
# XMODEM started
|
||||
xmodem = Chameleon.XModem(self.serial, self.verboseFunc)
|
||||
bytesSent = xmodem.sendData(dataStream)
|
||||
return bytesSent
|
||||
else:
|
||||
return None
|
||||
|
||||
def cmdDownloadDump(self, dataStream):
|
||||
if (self.execCmd(self.COMMAND_DOWNLOAD)['statusCode'] == self.STATUS_CODE_WAITING_FOR_XMODEM):
|
||||
# XMODEM started
|
||||
xmodem = Chameleon.XModem(self.serial, self.verboseFunc)
|
||||
return xmodem.recvData(dataStream)
|
||||
else:
|
||||
return None
|
||||
|
||||
def cmdDownloadLog(self, dataStream):
|
||||
if (self.execCmd(self.COMMAND_LOG_DOWNLOAD)['statusCode'] == self.STATUS_CODE_WAITING_FOR_XMODEM):
|
||||
# XMODEM started
|
||||
xmodem = Chameleon.XModem(self.serial, self.verboseFunc)
|
||||
return xmodem.recvData(dataStream)
|
||||
else:
|
||||
return None
|
||||
|
||||
def cmdClearLog(self):
|
||||
return self.execCmd(self.COMMAND_LOG_CLEAR)
|
||||
|
||||
def cmdLogMode(self, newLogMode):
|
||||
return self.getSetCmd(self.COMMAND_LOGMODE, newLogMode)
|
||||
|
||||
def cmdVersion(self):
|
||||
return self.getSetCmd(self.COMMAND_VERSION)
|
||||
|
||||
def cmdSetting(self, newSetting = None):
|
||||
return self.getSetCmd(self.COMMAND_SETTING, newSetting)
|
||||
|
||||
def cmdUID(self, newUID = None):
|
||||
return self.getSetCmd(self.COMMAND_UID, newUID)
|
||||
|
||||
def cmdConfig(self, newConfig = None):
|
||||
if (newConfig == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_CONFIG)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_CONFIG, newConfig)
|
||||
|
||||
def cmdLButton(self, newAction = None):
|
||||
if (newAction == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_LBUTTON)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_LBUTTON, newAction)
|
||||
|
||||
def cmdRButton(self, newAction = None):
|
||||
if (newAction == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_RBUTTON)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_RBUTTON, newAction)
|
||||
|
||||
def cmdGreenLED(self, newFunction = None):
|
||||
if (newFunction == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_GREEN_LED)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_GREEN_LED, newFunction)
|
||||
|
||||
def cmdRedLED(self, newFunction = None):
|
||||
if (newFunction == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_RED_LED)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_RED_LED, newFunction)
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import struct
|
||||
import binascii
|
||||
|
||||
def noDecoder(data):
|
||||
return ""
|
||||
|
||||
def textDecoder(data):
|
||||
return data.decode('ascii')
|
||||
|
||||
def binaryDecoder(data):
|
||||
return binascii.hexlify(data).decode()
|
||||
|
||||
eventTypes = {
|
||||
0x00: { 'name': 'EMPTY', 'decoder': noDecoder },
|
||||
0x10: { 'name': 'GENERIC', 'decoder': textDecoder },
|
||||
0x11: { 'name': 'CONFIG SET', 'decoder': textDecoder },
|
||||
0x12: { 'name': 'SETTING SET', 'decoder': textDecoder },
|
||||
0x13: { 'name': 'UID SET', 'decoder': textDecoder },
|
||||
0x20: { 'name': 'RESET APP', 'decoder': noDecoder },
|
||||
|
||||
0x40: { 'name': 'CODEC RX', 'decoder': binaryDecoder },
|
||||
0x41: { 'name': 'CODEC TX', 'decoder': binaryDecoder },
|
||||
|
||||
0x80: { 'name': 'APP READ', 'decoder': binaryDecoder },
|
||||
0x81: { 'name': 'APP WRITE', 'decoder': binaryDecoder },
|
||||
0x84: { 'name': 'APP INC', 'decoder': binaryDecoder },
|
||||
0x85: { 'name': 'APP DEC', 'decoder': binaryDecoder },
|
||||
0x86: { 'name': 'APP TRANSFER', 'decoder': binaryDecoder },
|
||||
0x87: { 'name': 'APP RESTORE', 'decoder': binaryDecoder },
|
||||
|
||||
0x90: { 'name': 'APP AUTH', 'decoder': binaryDecoder },
|
||||
0x91: { 'name': 'APP HALT', 'decoder': binaryDecoder },
|
||||
0x92: { 'name': 'APP UNKNOWN', 'decoder': binaryDecoder },
|
||||
|
||||
0xA0: { 'name': 'APP AUTHING', 'decoder': binaryDecoder },
|
||||
0xA1: { 'name': 'APP AUTHED', 'decoder': binaryDecoder },
|
||||
|
||||
0xC0: { 'name': 'APP AUTH FAILED', 'decoder': binaryDecoder },
|
||||
0xC1: { 'name': 'APP CSUM FAILED', 'decoder': binaryDecoder },
|
||||
0xC2: { 'name': 'APP NOT AUTHED', 'decoder': binaryDecoder },
|
||||
}
|
||||
|
||||
TIMESTAMP_MAX = 65536
|
||||
|
||||
def parseBinary(binaryStream):
|
||||
log = []
|
||||
|
||||
# Completely read file contents and process them byte by byte
|
||||
# logFile = fileHandle.read()
|
||||
# fileIdx = 0
|
||||
lastTimestamp = 0
|
||||
|
||||
while True:
|
||||
# Read log entry header from file
|
||||
header = binaryStream.read(struct.calcsize('<BBH'))
|
||||
|
||||
if (header is None):
|
||||
# No more data available
|
||||
break
|
||||
|
||||
if (len(header) < struct.calcsize('<BBH')):
|
||||
# No more data available
|
||||
break
|
||||
|
||||
(event, dataLength, timestamp) = struct.unpack_from('>BBH', header)
|
||||
|
||||
# Break if there are no more events
|
||||
if (eventTypes[event]['name'] == 'EMPTY'):
|
||||
break
|
||||
|
||||
# Read data from file
|
||||
logData = binaryStream.read(dataLength)
|
||||
|
||||
# Decode data
|
||||
logData = eventTypes[event]['decoder'](logData)
|
||||
|
||||
# Calculate delta timestamp respecting 16 bit overflow
|
||||
deltaTimestamp = timestamp - lastTimestamp;
|
||||
lastTimestamp = timestamp
|
||||
|
||||
if (deltaTimestamp < 0):
|
||||
deltaTimestamp += TIMESTAMP_MAX;
|
||||
|
||||
# Create log entry as dict and append it to event list
|
||||
logEntry = {
|
||||
'eventName': eventTypes[event]['name'],
|
||||
'dataLength': dataLength,
|
||||
'timestamp': timestamp,
|
||||
'deltaTimestamp': deltaTimestamp,
|
||||
'data': logData
|
||||
}
|
||||
|
||||
log.append(logEntry)
|
||||
|
||||
return log
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Very lightweight implementation of XModem for Chameleon purposes
|
||||
# Because the Chameleon uses a CDC over USB, we don't expect any
|
||||
# retransmissions at all and thus don't implement it
|
||||
|
||||
import io
|
||||
import time
|
||||
|
||||
class XModem:
|
||||
BYTE_SOH = b'\x01'
|
||||
BYTE_NAK = b'\x15'
|
||||
BYTE_ACK = b'\x06'
|
||||
BYTE_EOT = b'\x04'
|
||||
|
||||
def __init__(self, ioStream, verboseFunc = None):
|
||||
self.ioStream = ioStream
|
||||
self.verboseFunc = verboseFunc
|
||||
|
||||
def verboseLog(self, text):
|
||||
if (self.verboseFunc):
|
||||
self.verboseFunc(text)
|
||||
|
||||
def recvData(self, dataStream):
|
||||
packetCounter = 1
|
||||
bytesReceived = 0
|
||||
startTime = time.time()
|
||||
|
||||
self.verboseLog("Starting XMODEM Reception")
|
||||
|
||||
# Start transmission by issuing a NAK
|
||||
self.ioStream.write(self.BYTE_NAK)
|
||||
|
||||
while True:
|
||||
pktId = self.ioStream.read(1)
|
||||
|
||||
if (pktId == self.BYTE_SOH):
|
||||
currentPacket = self.ioStream.read(2)
|
||||
|
||||
if (currentPacket[0] == (255 - currentPacket[1])):
|
||||
#frame number intact
|
||||
if (currentPacket[0] == packetCounter):
|
||||
#In order packet
|
||||
dataBlock = self.ioStream.read(128)
|
||||
checksum = self.ioStream.read(1)
|
||||
|
||||
if (int(checksum[0]) == (sum(dataBlock) % 256)):
|
||||
# checksum correct
|
||||
dataStream.write(dataBlock)
|
||||
dataStream.flush()
|
||||
packetCounter = (packetCounter + 1) % 256
|
||||
bytesReceived += 128
|
||||
self.ioStream.write(self.BYTE_ACK)
|
||||
else:
|
||||
self.ioStream.write(self.BYTE_NAK)
|
||||
elif (pktId == self.BYTE_EOT):
|
||||
# Transmission done
|
||||
self.ioStream.write(self.BYTE_ACK)
|
||||
break
|
||||
else:
|
||||
# Unknown pktId
|
||||
break
|
||||
|
||||
deltaTime = time.time() - startTime
|
||||
self.verboseLog("{} Bytes received in {:.2f} sec. ({:.0f} B/s)".format(bytesReceived, deltaTime, bytesReceived/deltaTime))
|
||||
|
||||
return bytesReceived
|
||||
|
||||
def sendData(self, dataStream):
|
||||
packetCounter = 1
|
||||
bytesSent = 0
|
||||
startTime = time.time()
|
||||
|
||||
self.verboseLog("Waiting for XMODEM Connection")
|
||||
|
||||
# Wait for NAK from receiver to start transmission
|
||||
if (self.ioStream.read(1) != self.BYTE_NAK):
|
||||
# Timeout or different char received
|
||||
return None
|
||||
|
||||
while True:
|
||||
dataBlock = dataStream.read(128)
|
||||
|
||||
if (len(dataBlock) == 128):
|
||||
# Write SOH, pktId, data and checksum
|
||||
self.ioStream.write(self.BYTE_SOH)
|
||||
self.ioStream.write(bytes([packetCounter]))
|
||||
self.ioStream.write(bytes([255 - packetCounter]))
|
||||
self.ioStream.write(dataBlock)
|
||||
self.ioStream.write(bytes([sum(dataBlock) % 256]))
|
||||
|
||||
if (self.ioStream.read(1) == self.BYTE_ACK):
|
||||
#Proceed to next packet
|
||||
packetCounter = (packetCounter + 1) % 256
|
||||
bytesSent += 128
|
||||
|
||||
else:
|
||||
# Write EOT and wait for ACK
|
||||
self.ioStream.write(self.BYTE_EOT)
|
||||
self.ioStream.read(1)
|
||||
break
|
||||
|
||||
deltaTime = time.time() - startTime
|
||||
self.verboseLog("{} Bytes sent in {:.2f} sec. ({:.0f} B/s)".format(bytesSent, deltaTime, bytesSent/deltaTime))
|
||||
|
||||
return bytesSent
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Import modules
|
||||
import Chameleon.Log
|
||||
|
||||
# Import classes
|
||||
from Chameleon.Device import Device
|
||||
from Chameleon.XModem import XModem
|
||||
|
||||
#import Chameleon.Device
|
||||
|
||||
MIN_SETTING = 1
|
||||
MAX_SETTING = 8
|
||||
VALID_SETTINGS = range(MIN_SETTING, MAX_SETTING + 1)
|
||||
|
||||
USB_VID = 0x16D0
|
||||
USB_PID = 0x04B2
|
||||
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2014, Chema García
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of pycham nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,3 @@
|
||||
ChamTool
|
||||
========
|
||||
The ChamTool is based on the pycham tool, see also the LICENSE file.
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Command line tool to analyze binary dump files from the Chameleon
|
||||
# Authors: Simon K. (simon.kueppers@rub.de)
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
import Chameleon
|
||||
import io
|
||||
import datetime
|
||||
|
||||
def verboseLog(text):
|
||||
formatString = "[{}] {}"
|
||||
timeString = datetime.datetime.utcnow()
|
||||
print(formatString.format(timeString, text), file=sys.stderr)
|
||||
|
||||
def formatText(log):
|
||||
formatString = '{timestamp:0>5d} ms <{deltaTimestamp:>+6d} ms>:'
|
||||
formatString += '{eventName:<16} ({dataLength:<3} bytes) [{data}]\n'
|
||||
text = ''
|
||||
|
||||
for logEntry in log:
|
||||
text += formatString.format(**logEntry)
|
||||
|
||||
return text
|
||||
|
||||
def formatJSON(log):
|
||||
text = json.dumps(log, sort_keys=True, indent=4)
|
||||
|
||||
return text
|
||||
|
||||
def main():
|
||||
outputTypes = {
|
||||
'text': formatText,
|
||||
'json': formatJSON
|
||||
}
|
||||
|
||||
argParser = argparse.ArgumentParser(description="Analyzes binary Chameleon logfiles")
|
||||
|
||||
group = argParser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument("-f", "--file", dest="logfile", metavar="LOGFILE")
|
||||
group.add_argument("-p", "--port", dest="port", metavar="COMPORT")
|
||||
|
||||
argParser.add_argument("-t", "--type", choices=outputTypes.keys(), default='text',
|
||||
help="specifies output type")
|
||||
argParser.add_argument("-l", "--live", dest="live", action='store_true', help="Use live logging capabilities of Chameleon")
|
||||
argParser.add_argument("-c", "--clear", dest="clear", action='store_true', help="Clear Chameleon's log memory when using -p")
|
||||
argParser.add_argument("-m", "--mode", dest="mode", metavar="LOGMODE", help="Additionally set Chameleon's log mode after reading it's memory")
|
||||
argParser.add_argument("-v", "--verbose", dest="verbose", action='store_true', default=0)
|
||||
|
||||
args = argParser.parse_args()
|
||||
|
||||
if (args.verbose):
|
||||
verboseFunc = verboseLog
|
||||
else:
|
||||
verboseFunc = None
|
||||
|
||||
if (args.live):
|
||||
# Live logging mode
|
||||
if (args.port is not None):
|
||||
chameleon = Chameleon.Device(verboseFunc)
|
||||
|
||||
if (chameleon.connect(args.port)):
|
||||
chameleon.cmdLogMode("LIVE")
|
||||
|
||||
while True:
|
||||
stream = io.BytesIO(chameleon.read())
|
||||
log = Chameleon.Log.parseBinary(stream)
|
||||
if (len(log) > 0):
|
||||
print(outputTypes[args.type](log))
|
||||
|
||||
else:
|
||||
if (args.logfile is not None):
|
||||
handle = open(args.logfile, "rb")
|
||||
elif (args.port is not None):
|
||||
chameleon = Chameleon.Device(verboseFunc)
|
||||
|
||||
if (chameleon.connect(args.port)):
|
||||
handle = io.BytesIO()
|
||||
chameleon.cmdDownloadLog(handle)
|
||||
handle.seek(0)
|
||||
|
||||
if (args.clear):
|
||||
chameleon.cmdClearLog()
|
||||
|
||||
if (args.mode is not None):
|
||||
chameleon.cmdLogMode(args.mode)
|
||||
|
||||
chameleon.disconnect()
|
||||
else:
|
||||
sys.exit(2)
|
||||
|
||||
# Parse actual logfile
|
||||
log = Chameleon.Log.parseBinary(handle)
|
||||
|
||||
# Print to console using chosen output type
|
||||
print(outputTypes[args.type](log))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Command line tool to control the Chameleon through command line
|
||||
# Authors: Simon K. (simon.kueppers@rub.de)
|
||||
|
||||
import argparse
|
||||
import Chameleon
|
||||
import sys
|
||||
import datetime
|
||||
|
||||
def verboseLog(text):
|
||||
formatString = "[{}] {}"
|
||||
timeString = datetime.datetime.utcnow()
|
||||
print(formatString.format(timeString, text), file=sys.stderr)
|
||||
|
||||
# Command funcs
|
||||
def cmdInfo(chameleon, arg):
|
||||
return "{}".format(chameleon.cmdVersion()['response'])
|
||||
|
||||
def cmdSetting(chameleon, arg):
|
||||
result = chameleon.cmdSetting(arg)
|
||||
|
||||
if (arg is None):
|
||||
return "Current Setting: {}".format(result['response'])
|
||||
else:
|
||||
if (result['statusCode'] in chameleon.STATUS_CODES_SUCCESS):
|
||||
return "Setting has been changed to {}".format(chameleon.cmdSetting()['response'])
|
||||
else:
|
||||
return "Change setting to {} failed: {}".format(arg, result['statusText'])
|
||||
return
|
||||
|
||||
def cmdUID(chameleon, arg):
|
||||
result = chameleon.cmdUID(arg)
|
||||
|
||||
if (arg is None):
|
||||
return "{}".format(result['response'])
|
||||
else:
|
||||
if (result['statusCode'] in chameleon.STATUS_CODES_SUCCESS):
|
||||
return "UID has been changed to {}".format(chameleon.cmdUID()['response'])
|
||||
else:
|
||||
return "Setting UID to {} failed: {}".format(arg, result['statusText'])
|
||||
|
||||
def cmdConfig(chameleon, arg):
|
||||
result = chameleon.cmdConfig(arg)
|
||||
|
||||
if (arg is None):
|
||||
return "Current configuration: {}".format(result['response'])
|
||||
else:
|
||||
if (arg == chameleon.SUGGEST_CHAR):
|
||||
return "Possible configurations: {}".format(", ".join(result['suggestions']))
|
||||
elif (result['statusCode'] in chameleon.STATUS_CODES_SUCCESS):
|
||||
return "Configuration has been changed to {}".format(chameleon.cmdConfig()['response'])
|
||||
else:
|
||||
return "Changing configuration to {} failed: {}".format(arg, result['statusText'])
|
||||
|
||||
def cmdUpload(chameleon, arg):
|
||||
with open(arg, 'rb') as fileHandle:
|
||||
bytesSent = chameleon.cmdUploadDump(fileHandle)
|
||||
return "{} Bytes successfully read from {}".format(bytesSent, arg)
|
||||
|
||||
def cmdDownload(chameleon, arg):
|
||||
with open(arg, 'wb') as fileHandle:
|
||||
bytesReceived = chameleon.cmdDownloadDump(fileHandle)
|
||||
return "{} Bytes successfully written to {}".format(bytesReceived, arg)
|
||||
|
||||
def cmdLog(chameleon, arg):
|
||||
with open(arg, 'wb') as fileHandle:
|
||||
bytesReceived = chameleon.cmdDownloadLog(fileHandle)
|
||||
return "{} Bytes successfully written to {}".format(bytesReceived, arg)
|
||||
|
||||
def cmdLButton(chameleon, arg):
|
||||
result = chameleon.cmdLButton(arg)
|
||||
|
||||
if (arg is None):
|
||||
return "Current left button action: {}".format(result['response'])
|
||||
else:
|
||||
if (arg == chameleon.SUGGEST_CHAR):
|
||||
return "Possible left button actions: {}".format(", ".join(result['suggestions']))
|
||||
elif (result['statusCode'] in chameleon.STATUS_CODES_SUCCESS):
|
||||
return "Left button action has been set to {}".format(chameleon.cmdLButton()['response'])
|
||||
else:
|
||||
return "Setting left button action to {} failed: {}".format(arg, result['statusText'])
|
||||
|
||||
def cmdRButton(chameleon, arg):
|
||||
result = chameleon.cmdRButton(arg)
|
||||
|
||||
if (arg is None):
|
||||
return "Current right button action: {}".format(result['response'])
|
||||
else:
|
||||
if (arg == chameleon.SUGGEST_CHAR):
|
||||
return "Possible right button actions: {}".format(", ".join(result['suggestions']))
|
||||
elif (result['statusCode'] in chameleon.STATUS_CODES_SUCCESS):
|
||||
return "Right button action has been set to {}".format(chameleon.cmdRButton()['response'])
|
||||
else:
|
||||
return "Setting right button action to {} failed: {}".format(arg, result['statusText'])
|
||||
|
||||
def cmdGreenLED(chameleon, arg):
|
||||
result = chameleon.cmdGreenLED(arg)
|
||||
|
||||
if (arg is None):
|
||||
return "Current green LED function: {}".format(result['response'])
|
||||
else:
|
||||
if (arg == chameleon.SUGGEST_CHAR):
|
||||
return "Possible green LED functions: {}".format(", ".join(result['suggestions']))
|
||||
elif (result['statusCode'] in chameleon.STATUS_CODES_SUCCESS):
|
||||
return "Green LED function has been set to {}".format(chameleon.cmdGreenLED()['response'])
|
||||
else:
|
||||
return "Setting green LED function to {} failed: {}".format(arg, result['statusText'])
|
||||
|
||||
def cmdRedLED(chameleon, arg):
|
||||
result = chameleon.cmdRedLED(arg)
|
||||
|
||||
if (arg is None):
|
||||
return "Current red LED function: {}".format(result['response'])
|
||||
else:
|
||||
if (arg == chameleon.SUGGEST_CHAR):
|
||||
return "Possible red LED functions: {}".format(", ".join(result['suggestions']))
|
||||
elif (result['statusCode'] in chameleon.STATUS_CODES_SUCCESS):
|
||||
return "Red LED function has been set to {}".format(chameleon.cmdRedLED()['response'])
|
||||
else:
|
||||
return "Setting red LED function to {} failed: {}".format(arg, result['statusText'])
|
||||
|
||||
# Custom class for argparse
|
||||
class CmdListAction(argparse.Action):
|
||||
def __init__(self, option_strings, dest, default=False, required=False,
|
||||
help=None, metavar=None, nargs=None, type=None, choices=None, const=None):
|
||||
super(CmdListAction, self).__init__(
|
||||
option_strings=option_strings, dest=dest, nargs=nargs, const=const, default=default,
|
||||
required=required, help=help, metavar=metavar, type=type, choices=choices)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
# Create new attribute cmdList if not exist and append command to list
|
||||
if not hasattr(namespace, "cmdList"):
|
||||
setattr(namespace, "cmdList", [])
|
||||
|
||||
namespace.cmdList.append([self.dest, values])
|
||||
|
||||
def main():
|
||||
argParser = argparse.ArgumentParser(description="Controls the Chameleon through the command line")
|
||||
argParser.add_argument("-v", "--verbose", dest="verbose", action="store_true", default=0, help="output verbose")
|
||||
argParser.add_argument("-p", "--port", dest="port", metavar="COMPORT", help="specify device's comport")
|
||||
|
||||
# Add the commands using custom action that populates a list in the order the arguments are given
|
||||
cmdArgGroup = argParser.add_argument_group(title="Chameleon commands", description="These arguments can appear multiple times and are executed in the order they are given on the command line. "
|
||||
"Some of these arguments can be used with '" + Chameleon.Device.SUGGEST_CHAR + "' as parameter to get a list of suggestions.")
|
||||
cmdArgGroup.add_argument("-u", "--upload", dest="upload", action=CmdListAction, metavar="DUMPFILE", help="upload a card dump")
|
||||
cmdArgGroup.add_argument("-d", "--download", dest="download", action=CmdListAction, metavar="DUMPFILE", help="download a card dump")
|
||||
cmdArgGroup.add_argument("-l", "--log", dest="log", action=CmdListAction, metavar="LOGFILE", help="download the device log")
|
||||
cmdArgGroup.add_argument("-i", "--info", dest="info", action=CmdListAction, nargs=0, help="retrieve the version information")
|
||||
cmdArgGroup.add_argument("-s", "--setting", dest="setting", action=CmdListAction, nargs='?', type=int, choices=Chameleon.VALID_SETTINGS, help="retrieve or set the current setting")
|
||||
cmdArgGroup.add_argument("-U", "--uid", dest="uid", action=CmdListAction, nargs='?', help="retrieve or set the current UID")
|
||||
cmdArgGroup.add_argument("-c", "--config", dest="config", action=CmdListAction, metavar="CFGNAME", nargs='?', help="retrieve or set the current configuration")
|
||||
cmdArgGroup.add_argument("-lb", "--lbutton", dest="lbutton", action=CmdListAction, metavar="ACTION", nargs='?', help="retrieve or set the current left button action")
|
||||
cmdArgGroup.add_argument("-rb", "--rbutton", dest="rbutton", action=CmdListAction, metavar="ACTION", nargs='?', help="retrieve or set the current right button action")
|
||||
cmdArgGroup.add_argument("-gl", "--gled", dest="gled", action=CmdListAction, metavar="FUNCTION", nargs='?', help="retrieve or set the current green led function")
|
||||
cmdArgGroup.add_argument("-rl", "--rled", dest="rled", action=CmdListAction, metavar="FUNCTION", nargs='?', help="retrieve or set the current red led function")
|
||||
args = argParser.parse_args()
|
||||
|
||||
if (args.verbose):
|
||||
verboseFunc = verboseLog
|
||||
else:
|
||||
verboseFunc = None
|
||||
|
||||
# Instantiate device object and connect
|
||||
chameleon = Chameleon.Device(verboseFunc)
|
||||
|
||||
if (args.port):
|
||||
if (chameleon.connect(args.port)):
|
||||
# Generate a jumptable and execute all commands in the order they are given on the command line
|
||||
cmdFuncs = {
|
||||
"setting" : cmdSetting,
|
||||
"info" : cmdInfo,
|
||||
"uid" : cmdUID,
|
||||
"config" : cmdConfig,
|
||||
"upload" : cmdUpload,
|
||||
"download" : cmdDownload,
|
||||
"log" : cmdLog,
|
||||
"lbutton" : cmdLButton,
|
||||
"rbutton" : cmdRButton,
|
||||
"gled" : cmdGreenLED,
|
||||
"rled" : cmdRedLED,
|
||||
}
|
||||
|
||||
if hasattr(args, "cmdList"):
|
||||
for (cmd, arg) in args.cmdList:
|
||||
result = cmdFuncs[cmd](chameleon, arg)
|
||||
print("[{}] {}".format(cmd, result))
|
||||
|
||||
# Goodbye
|
||||
chameleon.disconnect()
|
||||
else:
|
||||
print("Unable to establish communication on {}".format(args.port))
|
||||
sys.exit(2)
|
||||
else:
|
||||
#List possible Chameleon ports
|
||||
print("Use -p COMPORT to specify the communication port (see help).")
|
||||
print("List of potential Chameleons connected to the system:")
|
||||
for port in Chameleon.Device.listDevices():
|
||||
print(port)
|
||||
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user