aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDonald Delmar Davis <don@suspectdevices.com>2012-10-16 17:04:16 -0700
committerDonald Delmar Davis <don@suspectdevices.com>2012-10-16 17:04:16 -0700
commitcef652ac1861410a885f8398f9c35d286f4f8a8d (patch)
treeb18824751aa30225a99f71cc5e0ee646c7f5ea5d
parentd60d28c60d450ad797308b4883ead3d57905146a (diff)
plodding right along
-rw-r--r--dumblogger/Monitor.cpp4
-rw-r--r--dumblogger/datalogger.cpp15
-rw-r--r--dumblogger/utilities/AMAv2.py360
-rw-r--r--dumblogger/utilities/ConsoleApp.py (renamed from dumblogger/ConsoleApp.py)55
-rw-r--r--dumblogger/utilities/confignetwork.py (renamed from dumblogger/confignetwork.py)0
-rw-r--r--dumblogger/utilities/console-1.icnsbin0 -> 107513 bytes
-rw-r--r--dumblogger/utilities/console.icnsbin0 -> 107513 bytes
-rw-r--r--dumblogger/utilities/developer_32x32x32.pngbin0 -> 1728 bytes
-rw-r--r--dumblogger/utilities/graph.icnsbin0 -> 34995 bytes
-rw-r--r--dumblogger/utilities/icons.py83
-rw-r--r--dumblogger/utilities/setup.py31
-rw-r--r--dumblogger/utilities/software_update.icnsbin0 -> 41957 bytes
-rw-r--r--dumblogger/utilities/software_update.icobin0 -> 67646 bytes
-rw-r--r--dumblogger/utilities/software_update_32x32x32.pngbin0 -> 1945 bytes
14 files changed, 536 insertions, 12 deletions
diff --git a/dumblogger/Monitor.cpp b/dumblogger/Monitor.cpp
index 3ad9059..6b8297d 100644
--- a/dumblogger/Monitor.cpp
+++ b/dumblogger/Monitor.cpp
@@ -104,7 +104,9 @@ uint8_t NOPaction(uint8_t source) {
return COMMAND_FORWARDED;
}
- else {
+ else if (source == DEVICE) {
+ toConsole((char *)deviceComm.line);
+ } else {
return COMMAND_IGNORED;
}
}
diff --git a/dumblogger/datalogger.cpp b/dumblogger/datalogger.cpp
index 1ff28f9..d5cee0f 100644
--- a/dumblogger/datalogger.cpp
+++ b/dumblogger/datalogger.cpp
@@ -172,7 +172,7 @@ static void serialTasks() {
if ((((ch=SerialUSB.read())!='\r') && (ch!='\n'))
&&(consoleComm.len<MAX_COMMAND_LINE_LENGTH)
) {
- consoleComm.line[consoleComm.len++] = ch;
+ consoleComm.line[consoleComm.len++] = ch;
} else {
if (consoleComm.len) {
consoleComm.gotline = true;
@@ -181,6 +181,19 @@ static void serialTasks() {
}
//SerialUSB.write(ch);
}
+ while ((deviceComm.gotline==false) && (Serial1.available())) {
+ if ((((ch=Serial1.read())!='\r') && (ch!='\n'))
+ &&(deviceComm.len<MAX_COMMAND_LINE_LENGTH)
+ ) {
+ deviceComm.line[deviceComm.len++] = ch;
+ } else {
+ if (deviceComm.len) {
+ deviceComm.gotline = true;
+ }
+ deviceComm.line[deviceComm.len]='\0';
+ }
+ //SerialUSB.write(ch);
+ }
}
// Force init to be called *first*, i.e. before static object allocation.
diff --git a/dumblogger/utilities/AMAv2.py b/dumblogger/utilities/AMAv2.py
new file mode 100644
index 0000000..9b6f7ea
--- /dev/null
+++ b/dumblogger/utilities/AMAv2.py
@@ -0,0 +1,360 @@
+#!/usr/bin/python
+"""
+*-----------------------------------------------------------------------AMAv2.py
+* XtractWork
+*
+* Created by Donald D Davis on 9/11/12.
+* Copyright 2012 Suspect Device, for xtractSolutions . All rights reserved.
+*
+* Python Class to simplify interaction between the monitor on the AMA and
+* programs to monitor and control it, the class uses a dispatch table to
+* define the actions. The open command can be used to identify both the ama
+* port and the logger and to identify an ama that is attached through the logger.
+*
+* the class/behaviour model should be used to create the next gen of bom code.
+*
+* you can use this to identify the logger or you can superclass the AMAv2
+* to create at BOM5k class. (I dont know which is more appropriate yet)
+*
+* The test case for the class below demonstrates how to attach the a
+* function to the dispatch table using the log keyword to push data to
+* the google app.
+*
+*
+"""
+
+import re
+import string
+import datetime
+import serial
+import os
+import os.path
+import shutil
+import platform
+import sys
+import time
+import re
+
+def list_serial_ports():
+ def full_port_name(portname):
+ """ Given a port-name (of the form COM7, COM12, CNCA0, etc.) returns a full
+ name suitable for opening with the Serial class.
+ http://eli.thegreenplace.net/2009/07/31/listing-all-serial-ports-on-windows-with-python/
+ """
+ m = re.match('^COM(\d+)$', portname)
+ if m and int(m.group(1)) < 10:
+ return portname
+ return '\\\\.\\' + portname
+
+ plat_sys = platform.system()
+ plat_bits = platform.architecture()[0]
+
+ if plat_sys == 'Windows':
+ import _winreg as reg
+ p = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
+ k = reg.OpenKey(reg.HKEY_LOCAL_MACHINE, p)
+ possible_paths = []
+ i = 0
+ while True:
+ try:
+ possible_paths.append(full_port_name(reg.EnumValue(k, i)[1]))
+ i += 1
+ except WindowsError:
+ break
+ else:
+ if plat_sys == 'Linux':
+ file_prefix='ttyACM'
+ elif plat_sys == 'Darwin':
+ file_prefix='cu.usbmodem'
+
+ possible_paths = [os.path.join('/dev', x) for x in os.listdir('/dev') \
+ if x.startswith(file_prefix)]
+ if len(possible_paths) == 0:
+ return None
+ return possible_paths
+
+
+#from SDserialutilities import list_serial_ports
+
+class AMAv2:
+
+ def __init__(self, portname=None, baudrate=9600, logger_only=False):
+ self.ser=None
+ self.attached=False
+ self.lastcontact=None
+ self.lastTimestamp=None
+ self.command_state=-1
+ self.acknowleged=False
+ self.dispatch_table={
+ 'SSN':self.ssn,'HWV':self.hwv,'SWV':self.swv,'LOG':self.log,
+ 'LSV':self.lsv,'ACK':self.ack,'DVP':self.dvp,'CMD':self.cmd,
+ 'DBG':self.dbg,'ALT':self.alt,'WRN':self.alt,'STC':self.alt
+ }
+ self.status={
+ 'TS1':None,'TS2':None,'TS3':None,'TS4':None,'TS5':None,
+ 'FAP':None,'FAN':None,'CHL':None,'PWR':None,'THR':None,
+ 'EC1':None,'EC2':None,'EC2':None
+ }
+
+ for var in self.status.keys():
+ self.dispatch_table[var]=self.update_state_variable
+
+ #for var in ['FAN','CHL']:
+ # self.dispatch_table[var]=self.update_state_integer
+ #for var in ['TS1','TS2','TS3','TS4','TS5']:
+ # self.dispatch_table[var]=self.update_state_float
+
+ self.open(portname,baudrate,logger_only)
+
+
+ def yank_timestamp(self,value):
+ if value is None:
+ return None
+ try:
+
+ ll=value.split(',',2)
+ #print value,"->",ll,len(ll)
+ if len(ll) == 1:
+ self.lastcontact=datetime.datetime.utcnow()
+ return ll[0]
+ else:
+ self.lastcontact=datetime.datetime.utcnow()
+ self.lastTimestamp=ll[0]
+ return ll[1]
+ except Exception as e:
+ print "error in yank timestamp:", str(e)
+ return None
+
+ def update_state_variable(self,key,verb,value):
+ self.status[key]=self.yank_timestamp(value)
+ #should maybe flag the update somewhere
+ #may want ot return something.
+
+ def set_dispatch_function(self,key,d_fun):
+ #should check key value and callable here....
+ self_dispatch_table[key]=d_fun
+
+ def log(self,key='LOG',verb=':',value=None):
+ if value is not None:
+ print "LOG:"+value.rstrip('\r\n')
+ return value
+
+
+ def alt(self,key='ALT',verb='?',value=None):
+ if value is not None:
+ print key+":"+value.rstrip('\r\n')
+ return value
+
+ def ssn(self,key='SSN',verb='?',value=None):
+ if (value is not None):
+ ssn=self.yank_timestamp(value).rstrip('\r\n')
+ if ( len(ssn)>9 and all(char in string.hexdigits for char in ssn) ):
+ self.device_ssn_string=ssn
+ else:
+ self.sendCMD("SSN?") #we got garbage ask again
+ return self.device_ssn_string
+
+ def hwv(self,key='HWV',verb='?',value=None):
+ if value is not None:
+ self.device_hwv_string=self.yank_timestamp(value)
+ return self.device_hwv_string
+
+ def swv(self,key='SWV',verb='?',value=None):
+ if value is not None:
+ self.device_swv_string=self.yank_timestamp(value)
+ return self.device_swv_string
+
+ def lsv(self,key='LSV',verb=':',value=None):
+ if value is not None:
+ print "LSV",
+ self.logger_swv_string=self.yank_timestamp(value)
+ print self.logger_swv_string
+ return self.logger_swv_string
+
+ def ack(self,key='ACK',verb='?',value=None):
+ if value is not None:
+ self.yank_timestamp(value)
+ self.acknowleged=True
+ return self.acknowleged
+
+ def cmd(self,key='CMD',verb='?',value=None):
+ if value is not None:
+ self.command_state= "ON" in self.yank_timestamp(value)
+ return self.command_state
+
+ def dvp(self,key='DVP',verb='?',value=None):
+ if value is not None:
+ self.device_on_logger = not "NOT" in self.yank_timestamp(value)
+ return self.device_on_logger
+
+ def dbg(self,key='DBG',verb=':',value=None):
+ if value is not None:
+ print key+":"+value
+ #return something probably
+
+ def sendCMD(self,cmd=None):
+ if cmd is not None and self.ser is not None:
+ self.ser.write(cmd+'\r\n')
+ #return something probably
+
+ def nop(self,key='NOP',verb=':',value=None):
+ print key,"DBG:("+key+")not implimented!!!"
+
+
+ def open(self,portname,baudrate,logger_only=False,printDebug=True):
+ attempts=2
+ line=''
+ self.version_string=None
+ self.device_ssn_string=None
+ self.device_swv_string=None
+ self.device_dbg_level=None
+ self.device_log_level=None
+ self.logger_swv_string=None
+ self.device_on_logger=False
+ self.portname=None
+ self.logger_portname=None
+ self.lastconact=None
+ self.initialContact=None
+ self.ser=None
+ while attempts and self.portname is None:
+ ports=list_serial_ports()
+ if ports is None:
+ break
+ else:
+ for port in ports:
+ try:
+ self.initialContact=[]
+ ser=serial.Serial(port,9600,timeout=1)#,dsrdtr=True,rtscts=True)
+ if not ser.isOpen:
+ continue
+ ser.flushInput()
+ time.sleep(.1)
+ try_no=1
+ while not ser.inWaiting():
+ time.sleep(.15)
+ try_no=try_no+1
+ if try_no > 15:
+ break
+
+ try_no=1
+ ser.write('SYN!\r\nLSV?\r\nDVP?\r\nDLG!0\r\nSSN?\r\nSWV?\r\nHWV?\r\n')
+ while not ser.inWaiting():
+ time.sleep(.15)
+ try_no=try_no+1
+ if try_no > 15:
+ break
+
+ while ser.inWaiting():
+ line=unicode(ser.readline(100).rstrip('\r\n'), errors='ignore')
+ line=line.encode('ascii','ignore')
+ if len(line.split('\r'))>1:
+ line=line.split('\r')[1] # if input echo is on strip input.
+ #print '\r\n'+repr(line)+'\r\n'
+ if len(line) > 3 and line[3] == ':' :
+ self.dispatch(line)
+ else:
+ print "DBG:GARBAGE >>"+line+"<<"
+ #check stream for rev 1 hardware
+ self.initialContact.append(line)
+ #time.sleep(.1)
+ if not logger_only:
+ time.sleep(.35)
+
+ except Exception as e:
+ print line
+ print "Error in open :", str(e)
+
+ if self.logger_swv_string is not None:
+ print "DBG:Found a Logger : version =", self.logger_swv_string
+ self.logger_portname=port
+ if not self.device_on_logger:
+ self.device_swv_string=None
+ if logger_only:
+ self.portname=port
+ self.ser=ser
+
+ if self.device_swv_string is not None:
+ print "DBG:Found an AMA : version =", self.device_swv_string
+ print "DBG:SSN =", self.device_ssn_string
+ self.portname=port
+ self.ser=ser
+ break
+ if self.portname is None:
+ ser.close()
+ attempts=attempts-1
+ self.attached = (self.ser is not None)
+ print ":", self.attached, self.logger_swv_string,self.ser
+
+ def dispatch(self,buffer):
+ if buffer is None:
+ print "DBG:short read"
+ return
+ if (len(buffer) < 4) or buffer=='':
+ print "DBG:"+repr(buffer.rstrip('\r\n'))
+ return
+ key=buffer[0:3]
+ verb=buffer[3]
+ if len(buffer) > 4:
+ value=buffer[4:].rstrip('\r\n')
+ else:
+ value=''
+ self.dispatch_table.get(key,self.nop)(key,verb,value)
+"""
+*----------------------------------------------------------------------test code
+*
+*
+"""
+
+
+if __name__ == "__main__":
+
+
+ import httplib, urllib
+
+ def log_to_app(key,verb=':',value=None):
+ if value is not None and device.device_ssn_string is not None :
+ try:
+ print "LOG:(" + device.device_ssn_string + ")" + value
+ ssn=device.device_ssn_string
+ (timestamp,ambient,chiller1,chiller2,air1,air2,chiller,
+ fan,state) = value.split(',')
+ #print "SANITY CHECK", ssn, timestamp, ambient
+ params = urllib.urlencode({#'ssn': ssn,
+ 'timestamp':timestamp,
+ 'ts1': ambient,
+ 'ts2': chiller1,'ts3': chiller2,
+ 'ts4': air1, 'ts5': air2,
+ 'chiller': chiller,
+ 'fan': fan,
+ 'state': state
+
+ })
+ headers = {"Content-type": "application/x-www-form-urlencoded",
+ "Accept": "text/plain"}
+ conn = httplib.HTTPConnection("patch-bay.appspot.com")
+ # need to deal with bogus ssn value
+ conn.request("POST", "/log?ssn="+ssn, params, headers)
+ response = conn.getresponse()
+ print "DBG:"+str(response.status),response.reason,
+ data = response.read()
+ print data.rstrip('\r\n')
+ conn.close()
+ except Exception as e:
+ print "DBG:NETWORK?"+str(e)
+
+ def test_stc (key,verb,value):
+ print key,verb,value,device.device_ssn_string
+
+
+ device=AMAv2(logger_only=True)
+ if device.ser is None:
+ print "FOUND NO AMA DEVICE"
+ exit()
+ device.dispatch_table['LOG']=log_to_app
+ while True:
+ if device.ser.inWaiting():
+ line=device.ser.readline(100).rstrip('\r\n')
+ line=line.encode('ascii', 'ignore')
+
+ device.dispatch(line)
+
diff --git a/dumblogger/ConsoleApp.py b/dumblogger/utilities/ConsoleApp.py
index 64897f3..3993f6b 100644
--- a/dumblogger/ConsoleApp.py
+++ b/dumblogger/utilities/ConsoleApp.py
@@ -5,10 +5,14 @@ import subprocess
import datetime
import time
import os
+from wx.lib.embeddedimage import PyEmbeddedImage
from AMAv2 import AMAv2
+import icons
+#self.button1 = wx.BitmapButton(self.panel1, id=-1, bitmap=image1,
-build_dir=os.path.dirname(os.path.realpath(__file__)) + os.sep + '../../libmaple/'
+libmaple_directory=os.path.dirname(os.path.realpath(__file__)) + os.sep + '../../libmaple/'
+program_code_directory='../programs/dumblogger'
my_dir=os.path.dirname(os.path.realpath(__file__))
@@ -91,7 +95,7 @@ class ConsoleFrame(wx.Frame):
item = FileMenu.Append(wx.ID_EXIT, text = "&Exit")
self.Bind(wx.EVT_MENU, self.OnQuit, item)
- item = FileMenu.Append(wx.ID_ANY, text = "&Open")
+ item = FileMenu.Append(wx.ID_ANY, text = "&Open Source Code")
self.Bind(wx.EVT_MENU, self.OnOpen, item)
item = FileMenu.Append(wx.ID_PREFERENCES, text = "&Preferences")
@@ -141,11 +145,18 @@ class ConsoleFrame(wx.Frame):
dlg.Destroy()
def OnOpen(self, event):
- dlg = wx.MessageDialog(self, "This would be an open Dialog\n"
- "If there was anything to open\n",
- "Open File", wx.OK | wx.ICON_INFORMATION)
- dlg.ShowModal()
- dlg.Destroy()
+ dialog = wx.DirDialog(None, "Directory containing code :", style=wx.DD_DIR_MUST_EXIST )
+ if dialog.ShowModal() == wx.ID_OK:
+ #libmaple_directory = dialog.GetPath()
+ program_code_directory=dialog.GetPath()
+ self.OnREBUILDButton(event)
+ dialog.Destroy()
+
+# dlg = wx.MessageDialog(self, "This would be an open Dialog\n"
+# "If there was anything to open\n",
+# "Open File", wx.OK | wx.ICON_INFORMATION)
+# dlg.ShowModal()
+# dlg.Destroy()
def OnPrefs(self, event):
dlg = wx.MessageDialog(self, "This would be an preferences Dialog\n"
@@ -164,12 +175,12 @@ class ConsoleFrame(wx.Frame):
self.device.attached=False
try:
self.OUTPUT.AppendText(subprocess.check_output("make install",
- env={'BOARD': 'maple_mini','USER_MODULES': '../programs/dumblogger',
+ env={'BOARD': 'maple_mini','USER_MODULES': program_code_directory,
'PATH': '/usr/local/arm-none-eabi/bin/:/usr/local/bin/:/usr/bin:/bin/:$PATH'},
#stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
- cwd=build_dir))
+ cwd=libmaple_directory))
self.OUTPUT.AppendText('\n------------------- SUCCESS ------------------\n')
except CalledProcessError as e:
@@ -180,7 +191,8 @@ class ConsoleFrame(wx.Frame):
time.sleep(1)
self.OnReconnect()
#self.device.attached=True # should be handled by ama class
- event.Skip()
+ if event is not None:
+ event.Skip()
def OnCMDText(self, event):
comm=self.CMD.GetValue().rstrip('\r\n')
@@ -196,6 +208,9 @@ class ConsoleFrame(wx.Frame):
self.device.ser.close()
self.statusBar1.SetStatusText("...Searching...",1)
self.device.open(self.device.portname,9600,logger_only=True)
+ print "?",self.device.attached, self.device.logger_swv_string, self.device.ser
+ if self.device.ser is not None and self.device.ser.isOpen():
+ self.statusBar1.SetStatusText(self.device.logger_swv_string,1)
if event is not None:
event.Skip()
@@ -221,6 +236,26 @@ class ConsoleFrame(wx.Frame):
self.device_was_already_attached=self.device.attached
if event is not None:
event.Skip()
+
+# stubs for mac events...
+
+
+ def MacOpenFile(self, filename):
+ """Called for files droped on dock icon, or opened via finders context menu"""
+ print filename
+ print "%s dropped on app"%(filename) #code to load filename goes here.
+ program_code_directory = os.path.dirname(os.path.realpath(filename))
+ self.OnREBUILDButton()
+
+ def MacReopenApp(self):
+ """Called when the doc icon is clicked, and ???"""
+ self.BringWindowToFront()
+
+ def MacNewFile(self):
+ pass
+
+ def MacPrintFile(self, file_path):
+ pass
class ConsoleApp(wx.App):
diff --git a/dumblogger/confignetwork.py b/dumblogger/utilities/confignetwork.py
index 3123ab2..3123ab2 100644
--- a/dumblogger/confignetwork.py
+++ b/dumblogger/utilities/confignetwork.py
diff --git a/dumblogger/utilities/console-1.icns b/dumblogger/utilities/console-1.icns
new file mode 100644
index 0000000..0328430
--- /dev/null
+++ b/dumblogger/utilities/console-1.icns
Binary files differ
diff --git a/dumblogger/utilities/console.icns b/dumblogger/utilities/console.icns
new file mode 100644
index 0000000..0328430
--- /dev/null
+++ b/dumblogger/utilities/console.icns
Binary files differ
diff --git a/dumblogger/utilities/developer_32x32x32.png b/dumblogger/utilities/developer_32x32x32.png
new file mode 100644
index 0000000..947dc14
--- /dev/null
+++ b/dumblogger/utilities/developer_32x32x32.png
Binary files differ
diff --git a/dumblogger/utilities/graph.icns b/dumblogger/utilities/graph.icns
new file mode 100644
index 0000000..2e8df9c
--- /dev/null
+++ b/dumblogger/utilities/graph.icns
Binary files differ
diff --git a/dumblogger/utilities/icons.py b/dumblogger/utilities/icons.py
new file mode 100644
index 0000000..346616d
--- /dev/null
+++ b/dumblogger/utilities/icons.py
@@ -0,0 +1,83 @@
+#----------------------------------------------------------------------
+# This file was generated by /usr/local/bin/img2py
+#
+from wx.lib.embeddedimage import PyEmbeddedImage
+
+updateIcon = PyEmbeddedImage(
+ "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"
+ "gg==")
+
+#----------------------------------------------------------------------
+# This file was generated by /usr/local/bin/img2py
+#
+from wx.lib.embeddedimage import PyEmbeddedImage
+
+developerIcon= PyEmbeddedImage(
+ "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")
+
diff --git a/dumblogger/utilities/setup.py b/dumblogger/utilities/setup.py
new file mode 100644
index 0000000..6f7154d
--- /dev/null
+++ b/dumblogger/utilities/setup.py
@@ -0,0 +1,31 @@
+"""
+This is a setup.py script altered from one generated by py2applet
+
+Usage:
+ python setup.py py2app
+"""
+
+from setuptools import setup
+
+# A custom plist for letting it associate with all files.
+Plist = dict(CFBundleDocumentTypes= [dict(CFBundleTypeExtensions=["*"],
+ #CFBundleTypeName="kUTTypeText", # this should be text files, but I'm not sure the details.
+ CFBundleTypeRole="Editor"),
+ ]
+ )
+
+
+APP = ['ConsoleApp.py']
+DATA_FILES = []
+OPTIONS = {'argv_emulation': True, # this puts the names of dropped files into sys.argv when starting the app.
+ 'iconfile': 'console.icns',
+ 'plist': Plist,
+ }
+
+
+setup(
+ app=APP,
+ data_files=DATA_FILES,
+ options={'py2app': OPTIONS},
+ setup_requires=['py2app'],
+) \ No newline at end of file
diff --git a/dumblogger/utilities/software_update.icns b/dumblogger/utilities/software_update.icns
new file mode 100644
index 0000000..8da364f
--- /dev/null
+++ b/dumblogger/utilities/software_update.icns
Binary files differ
diff --git a/dumblogger/utilities/software_update.ico b/dumblogger/utilities/software_update.ico
new file mode 100644
index 0000000..c8c8f02
--- /dev/null
+++ b/dumblogger/utilities/software_update.ico
Binary files differ
diff --git a/dumblogger/utilities/software_update_32x32x32.png b/dumblogger/utilities/software_update_32x32x32.png
new file mode 100644
index 0000000..6a1576f
--- /dev/null
+++ b/dumblogger/utilities/software_update_32x32x32.png
Binary files differ