diff --git a/latest/Apps/maiana-update/.gitignore b/latest/Apps/maiana-update/.gitignore
new file mode 100644
index 0000000..ed8ebf5
--- /dev/null
+++ b/latest/Apps/maiana-update/.gitignore
@@ -0,0 +1 @@
+__pycache__
\ No newline at end of file
diff --git a/latest/Apps/maiana-update/.idea/.gitignore b/latest/Apps/maiana-update/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/latest/Apps/maiana-update/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/latest/Apps/maiana-update/.idea/inspectionProfiles/profiles_settings.xml b/latest/Apps/maiana-update/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/latest/Apps/maiana-update/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/latest/Apps/maiana-update/.idea/maiana-update.iml b/latest/Apps/maiana-update/.idea/maiana-update.iml
new file mode 100644
index 0000000..74d515a
--- /dev/null
+++ b/latest/Apps/maiana-update/.idea/maiana-update.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/latest/Apps/maiana-update/.idea/misc.xml b/latest/Apps/maiana-update/.idea/misc.xml
new file mode 100644
index 0000000..7a5e236
--- /dev/null
+++ b/latest/Apps/maiana-update/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/latest/Apps/maiana-update/.idea/modules.xml b/latest/Apps/maiana-update/.idea/modules.xml
new file mode 100644
index 0000000..f321b90
--- /dev/null
+++ b/latest/Apps/maiana-update/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/latest/Apps/maiana-update/.idea/vcs.xml b/latest/Apps/maiana-update/.idea/vcs.xml
new file mode 100644
index 0000000..c2365ab
--- /dev/null
+++ b/latest/Apps/maiana-update/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/latest/Apps/maiana-update/fwUpdateThread.py b/latest/Apps/maiana-update/fwUpdateThread.py
new file mode 100644
index 0000000..b29a144
--- /dev/null
+++ b/latest/Apps/maiana-update/fwUpdateThread.py
@@ -0,0 +1,124 @@
+from threading import Thread
+import wx
+import os
+import binascii
+import sys
+from enum import Enum
+
+
+EVT_FWUPDATE_ID = wx.NewId()
+
+class EventType(Enum):
+ DFU_ENABLED = 1
+ TRANSFER_STARTED = 2
+ TRANSFER_PROGRESS = 3
+ TRANSFER_COMPLETE = 4
+ ERROR = 5
+
+class FwUpdateEvent(wx.PyEvent):
+ def __init__(self, eventType, data):
+ wx.PyEvent.__init__(self)
+ self.SetEventType(EVT_FWUPDATE_ID)
+ self.eventType = eventType
+ self.data = data
+
+
+class FWUpdateThread(Thread):
+ def __init__(self, window, port, filename):
+ Thread.__init__(self)
+ self.daemon = True
+ self.window = window
+ self.port = port
+ self.filesize = 0
+ self.crc32 = 0
+
+ try:
+ self.file = open(filename, "rb")
+ except:
+ print("Unable to open file {0}".format(filename))
+ sys.exit(2)
+
+ self.filesize = os.stat(filename).st_size
+ # print filesize
+ self.data = self.file.read()
+ self.crc32 = (binascii.crc32(self.data) & 0xFFFFFFFF)
+ self.file.seek(0)
+
+ def is_unit_running(self):
+ print("Checking if unit is running")
+ s = self.port.readline().strip().decode('utf-8')
+ if len(s) > 0:
+ # print s
+ tokens = s.split(',')
+ if len(tokens) > 3:
+ return True
+ return False
+
+ def drain_port(self):
+ self.port.flushInput()
+ s = self.port.readline()
+ while len(s) > 0:
+ s = self.port.readline()
+
+ def enable_dfu(self):
+ if self.is_unit_running():
+ print("Unit is running, switching to DFU mode")
+ self.port.write(b'dfu\r\n')
+ self.drain_port()
+
+ for x in range(5):
+ self.port.write(b'\r\n')
+ s = self.port.readline().strip()
+ if s.find(b"MAIANA bootloader") >= 0:
+ wx.PostEvent(self.window, FwUpdateEvent(EventType.DFU_ENABLED, None))
+ return True
+
+ wx.PostEvent(self.window, FwUpdateEvent(EventType.ERROR, b'Could not enter DFU mode'))
+ return False
+
+ def begin_transfer(self):
+ self.drain_port()
+ command = "load {0} {1:x}\r\n".format(self.filesize, self.crc32).encode('utf-8')
+ self.port.write(command)
+ print(command)
+
+ s = self.port.readline().strip()
+ if s == b"READY":
+ wx.PostEvent(self.window, FwUpdateEvent(EventType.TRANSFER_STARTED, None))
+ return True
+
+ wx.PostEvent(self.window, FwUpdateEvent(EventType.ERROR, 'Failed:{}'.format(s)))
+ return False
+
+ def run(self):
+ if not self.enable_dfu():
+ print("Could not get unit into DFU mode")
+ return
+
+ print("Unit is in DFU mode")
+
+ if not self.begin_transfer():
+ print("Unable to begin transfer, restart the unit and retry")
+ return
+
+ print("Starting transfer")
+
+ bytes = self.file.read(2048)
+ resp = ''
+ bytesSent = 0
+ while len(bytes) > 0:
+ self.port.write(bytes)
+ resp = self.port.readline().strip()
+ if resp.find(b"OK") < 0:
+ break
+
+ bytesSent += len(bytes)
+ progress = bytesSent/self.filesize
+ wx.PostEvent(self.window, FwUpdateEvent(EventType.TRANSFER_PROGRESS, progress))
+ bytes = self.file.read(2048)
+
+ wx.PostEvent(self.window, FwUpdateEvent(EventType.TRANSFER_COMPLETE, None))
+
+
+
+
diff --git a/latest/Apps/maiana-update/maiana-update.fbp b/latest/Apps/maiana-update/maiana-update.fbp
new file mode 100644
index 0000000..d9fa3f4
--- /dev/null
+++ b/latest/Apps/maiana-update/maiana-update.fbp
@@ -0,0 +1,2518 @@
+
+
+
+
+
diff --git a/latest/Apps/maiana-update/maiana.py b/latest/Apps/maiana-update/maiana.py
new file mode 100644
index 0000000..41212ef
--- /dev/null
+++ b/latest/Apps/maiana-update/maiana.py
@@ -0,0 +1,9 @@
+import wx
+from mainwindow import MainWindow
+
+
+if __name__ == '__main__':
+ app = wx.App()
+ window = MainWindow()
+ window.Show(True)
+ app.MainLoop()
diff --git a/latest/Apps/maiana-update/maianaclient.py b/latest/Apps/maiana-update/maianaclient.py
new file mode 100644
index 0000000..208b3fe
--- /dev/null
+++ b/latest/Apps/maiana-update/maianaclient.py
@@ -0,0 +1,132 @@
+import serial
+import sys
+import glob
+import time
+import re
+from enum import Enum
+
+
+class MaianaStatus(Enum):
+ UNKNOWN = 0
+ RUNNING = 1
+ DFU = 2
+
+class MaianaClient:
+ VESSEL_TYPES = [30, 34, 36, 37]
+
+ @staticmethod
+ def serial_ports():
+ """ Lists serial port names
+
+ :raises EnvironmentError:
+ On unsupported or unknown platforms
+ :returns:
+ A list of the serial ports available on the system
+ """
+ if sys.platform.startswith('win'):
+ ports = ['COM%s' % (i + 1) for i in range(256)]
+ elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
+ # this excludes your current terminal "/dev/tty"
+ ports = glob.glob('/dev/tty[A-Za-z]*')
+ elif sys.platform.startswith('darwin'):
+ ports = glob.glob('/dev/tty\..*')
+ else:
+ raise EnvironmentError('Unsupported platform')
+
+ result = []
+ for port in ports:
+ # print port
+ try:
+ s = serial.Serial(port)
+ s.close()
+ result.append(port)
+ except (OSError, Exception):
+ pass
+ return result
+
+ @staticmethod
+ def determineStatus(port):
+ port.flushInput()
+ port.flushOutput()
+ s = port.readline().strip()
+ port.write(b'\r\n')
+ for i in range(5):
+ s = port.readline().strip()
+ if s.find(b"bootloader") > -1:
+ return MaianaStatus.DFU
+ else:
+ tokens = s.decode('utf-8').split(',')
+ if (tokens[0][0] == '$' or tokens[0][0] == '!') and len(tokens) >= 2:
+ return MaianaStatus.RUNNING
+
+ return MaianaStatus.UNKNOWN
+
+ @staticmethod
+ def sendCmdWithResponse(port, cmd, resp):
+ port.flushInput()
+ port.flushOutput()
+ port.write(cmd + b'\r\n')
+ for i in range(25):
+ s = port.readline().strip()
+ #print(s)
+ if s == b'':
+ break
+ if resp in s:
+ return s
+ return None
+
+ @staticmethod
+ def loadSys(port):
+ for i in range(2):
+ try:
+ sysline = MaianaClient.sendCmdWithResponse(port, b'sys?', b'$PAISYS')
+ if sysline is None:
+ return {}
+
+ systokens = re.split(',|\\*', sysline.decode('utf-8'))
+ sysd = {'fw': systokens[2], 'hw': systokens[1], 'cpu': systokens[4]}
+ return sysd
+ except:
+ pass
+
+ return {}
+
+ @staticmethod
+ def loadStation(port):
+ for i in range(2):
+ try:
+ stationline = MaianaClient.sendCmdWithResponse(port, b'station?', b'$PAISTN')
+ if stationline is None:
+ return {}
+
+ stationtokens = re.split(',|\\*', stationline.decode('utf-8'))
+ stad = {'mmsi': int(stationtokens[1]), 'name': stationtokens[2], 'callsign': stationtokens[3],
+ 'type': int(stationtokens[4]), 'len': int(stationtokens[5]), 'beam': int(stationtokens[6]),
+ 'portoffset': int(stationtokens[7]), 'bowoffset': int(stationtokens[8])}
+
+ return stad
+ except:
+ pass
+ return {}
+
+ @staticmethod
+ def setStationData(port, data):
+ line = 'station {},{},{},{},{},{},{},{}\r\n'.format(
+ data['mmsi'],
+ data['name'],
+ data['callsign'],
+ data['type'],
+ data['len'],
+ data['beam'],
+ data['portoffset'],
+ data['bowoffset']
+ )
+
+ resp = MaianaClient.sendCmdWithResponse(port, line.encode('utf-8'), b'$PAISTN')
+ #print(resp)
+ if resp:
+ resp = MaianaClient.sendCmdWithResponse(port, b'reboot', b'$PAISTN')
+ #print(resp)
+ return not resp is None
+
+ return False
\ No newline at end of file
diff --git a/latest/Apps/maiana-update/mainframe.py b/latest/Apps/maiana-update/mainframe.py
new file mode 100644
index 0000000..37f2f7c
--- /dev/null
+++ b/latest/Apps/maiana-update/mainframe.py
@@ -0,0 +1,291 @@
+# -*- coding: utf-8 -*-
+
+###########################################################################
+## Python code generated with wxFormBuilder (version 3.10.1-39-g978478d5-dirty)
+## http://www.wxformbuilder.org/
+##
+## PLEASE DO *NOT* EDIT THIS FILE!
+###########################################################################
+
+import wx
+import wx.xrc
+
+###########################################################################
+## Class MainFrame
+###########################################################################
+
+class MainFrame ( wx.Frame ):
+
+ def __init__( self, parent ):
+ wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"MAIANA Transponder Manager ver 0.1", pos = wx.DefaultPosition, size = wx.Size( 605,300 ), style = wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.TAB_TRAVERSAL )
+
+ self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )
+
+ bSizer1 = wx.BoxSizer( wx.VERTICAL )
+
+ self.m_notebook2 = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_StatusPnl = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
+ gSizer1 = wx.GridSizer( 5, 2, 0, 0 )
+
+ self.m_staticText1 = wx.StaticText( self.m_StatusPnl, wx.ID_ANY, u"Serial Port", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText1.Wrap( -1 )
+
+ gSizer1.Add( self.m_staticText1, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ bSizer4 = wx.BoxSizer( wx.HORIZONTAL )
+
+ m_SerialPortChoiceChoices = []
+ self.m_SerialPortChoice = wx.Choice( self.m_StatusPnl, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_SerialPortChoiceChoices, 0 )
+ self.m_SerialPortChoice.SetSelection( 0 )
+ self.m_SerialPortChoice.SetMinSize( wx.Size( 150,-1 ) )
+
+ bSizer4.Add( self.m_SerialPortChoice, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_SerialBtn = wx.Button( self.m_StatusPnl, wx.ID_ANY, u"Connect", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_SerialBtn.Enable( False )
+
+ bSizer4.Add( self.m_SerialBtn, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+
+ gSizer1.Add( bSizer4, 1, wx.EXPAND, 5 )
+
+ self.m_staticText7 = wx.StaticText( self.m_StatusPnl, wx.ID_ANY, u"CPU", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText7.Wrap( -1 )
+
+ gSizer1.Add( self.m_staticText7, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_CPULbl = wx.StaticText( self.m_StatusPnl, wx.ID_ANY, u"(unknown)", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_CPULbl.Wrap( -1 )
+
+ self.m_CPULbl.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
+
+ gSizer1.Add( self.m_CPULbl, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_staticText3 = wx.StaticText( self.m_StatusPnl, wx.ID_ANY, u"Hardware Revision", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText3.Wrap( -1 )
+
+ gSizer1.Add( self.m_staticText3, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_HWRevLbl = wx.StaticText( self.m_StatusPnl, wx.ID_ANY, u"(unknown)", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_HWRevLbl.Wrap( -1 )
+
+ self.m_HWRevLbl.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
+
+ gSizer1.Add( self.m_HWRevLbl, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_staticText5 = wx.StaticText( self.m_StatusPnl, wx.ID_ANY, u"Firmware Revision", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText5.Wrap( -1 )
+
+ gSizer1.Add( self.m_staticText5, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_FWRevLbl = wx.StaticText( self.m_StatusPnl, wx.ID_ANY, u"(unknown)", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_FWRevLbl.Wrap( -1 )
+
+ self.m_FWRevLbl.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
+
+ gSizer1.Add( self.m_FWRevLbl, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+
+ self.m_StatusPnl.SetSizer( gSizer1 )
+ self.m_StatusPnl.Layout()
+ gSizer1.Fit( self.m_StatusPnl )
+ self.m_notebook2.AddPage( self.m_StatusPnl, u"Status", True )
+ self.m_StationPnl = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
+ self.m_StationPnl.Enable( False )
+
+ gSizer2 = wx.GridSizer( 0, 4, 0, 0 )
+
+ self.m_staticText101 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, u"MMSI", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText101.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText101, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_MMSIText = wx.TextCtrl( self.m_StationPnl, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 100,-1 ), 0 )
+ gSizer2.Add( self.m_MMSIText, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_staticText11 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, u"Call Sign", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText11.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText11, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_CallsignText = wx.TextCtrl( self.m_StationPnl, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
+ gSizer2.Add( self.m_CallsignText, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_staticText12 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, u"Name", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText12.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText12, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_NameText = wx.TextCtrl( self.m_StationPnl, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_NameText.SetMaxLength( 20 )
+ self.m_NameText.SetMinSize( wx.Size( 140,-1 ) )
+
+ gSizer2.Add( self.m_NameText, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_staticText13 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, u"Vessel Type", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText13.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText13, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ m_VesselTypeChoiceChoices = [ u"Fishing", u"Diving", u"Sailing", u"Pleasure" ]
+ self.m_VesselTypeChoice = wx.Choice( self.m_StationPnl, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_VesselTypeChoiceChoices, 0 )
+ self.m_VesselTypeChoice.SetSelection( 0 )
+ gSizer2.Add( self.m_VesselTypeChoice, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self._ = wx.StaticText( self.m_StationPnl, wx.ID_ANY, u"Length (m)", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self._.Wrap( -1 )
+
+ gSizer2.Add( self._, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_LengthText = wx.TextCtrl( self.m_StationPnl, wx.ID_ANY, u"0", wx.DefaultPosition, wx.DefaultSize, 0 )
+ gSizer2.Add( self.m_LengthText, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_staticText15 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, u"Beam (m)", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText15.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText15, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_BeamText = wx.TextCtrl( self.m_StationPnl, wx.ID_ANY, u"0", wx.DefaultPosition, wx.DefaultSize, 0 )
+ gSizer2.Add( self.m_BeamText, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_staticText17 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, u"Port Offset (m)", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText17.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText17, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_PortOffsetText = wx.TextCtrl( self.m_StationPnl, wx.ID_ANY, u"0", wx.DefaultPosition, wx.DefaultSize, 0 )
+ gSizer2.Add( self.m_PortOffsetText, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_staticText16 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, u"Bow Offset (m)", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText16.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText16, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_BowOffsetText = wx.TextCtrl( self.m_StationPnl, wx.ID_ANY, u"0", wx.DefaultPosition, wx.DefaultSize, 0 )
+ gSizer2.Add( self.m_BowOffsetText, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_staticText18 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText18.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText18, 0, wx.ALL, 5 )
+
+ self.m_staticText19 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText19.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText19, 0, wx.ALL, 5 )
+
+ self.m_staticText20 = wx.StaticText( self.m_StationPnl, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_staticText20.Wrap( -1 )
+
+ gSizer2.Add( self.m_staticText20, 0, wx.ALL, 5 )
+
+ self.m_StationSaveBtn = wx.Button( self.m_StationPnl, wx.ID_ANY, u"Save", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_StationSaveBtn.Enable( False )
+
+ gSizer2.Add( self.m_StationSaveBtn, 0, wx.ALL|wx.ALIGN_BOTTOM, 5 )
+
+
+ self.m_StationPnl.SetSizer( gSizer2 )
+ self.m_StationPnl.Layout()
+ gSizer2.Fit( self.m_StationPnl )
+ self.m_notebook2.AddPage( self.m_StationPnl, u"Station Data", False )
+ self.m_FWUpdatePnl = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
+ self.m_FWUpdatePnl.Enable( False )
+
+ bSizer41 = wx.BoxSizer( wx.VERTICAL )
+
+ gSizer4 = wx.GridSizer( 1, 2, 0, 0 )
+
+ gSizer4.SetMinSize( wx.Size( -1,90 ) )
+ self.m_staticText28 = wx.StaticText( self.m_FWUpdatePnl, wx.ID_ANY, u"Binary File:", wx.DefaultPosition, wx.Size( 80,-1 ), 0 )
+ self.m_staticText28.Wrap( -1 )
+
+ gSizer4.Add( self.m_staticText28, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
+
+ self.m_FWBinaryPicker = wx.FilePickerCtrl( self.m_FWUpdatePnl, wx.ID_ANY, wx.EmptyString, u"Select a file", u"*.bin", wx.DefaultPosition, wx.Size( -1,-1 ), wx.FLP_CHANGE_DIR|wx.FLP_DEFAULT_STYLE|wx.FLP_FILE_MUST_EXIST|wx.FLP_OPEN )
+ gSizer4.Add( self.m_FWBinaryPicker, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 5 )
+
+
+ bSizer41.Add( gSizer4, 1, wx.EXPAND, 5 )
+
+ gSizer5 = wx.GridSizer( 1, 2, 0, 0 )
+
+ self.m_FWProgress = wx.Gauge( self.m_FWUpdatePnl, wx.ID_ANY, 100, wx.DefaultPosition, wx.Size( 350,-1 ), wx.GA_HORIZONTAL )
+ self.m_FWProgress.SetValue( 0 )
+ gSizer5.Add( self.m_FWProgress, 0, wx.ALL, 5 )
+
+ self.m_FWUpdateBtn = wx.Button( self.m_FWUpdatePnl, wx.ID_ANY, u"Update", wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_FWUpdateBtn.Enable( False )
+
+ gSizer5.Add( self.m_FWUpdateBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 )
+
+
+ bSizer41.Add( gSizer5, 1, wx.EXPAND, 5 )
+
+ self.m_FWUpdateStatusLbl = wx.StaticText( self.m_FWUpdatePnl, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
+ self.m_FWUpdateStatusLbl.Wrap( -1 )
+
+ self.m_FWUpdateStatusLbl.SetMinSize( wx.Size( 520,-1 ) )
+
+ bSizer41.Add( self.m_FWUpdateStatusLbl, 0, wx.ALL, 5 )
+
+
+ self.m_FWUpdatePnl.SetSizer( bSizer41 )
+ self.m_FWUpdatePnl.Layout()
+ bSizer41.Fit( self.m_FWUpdatePnl )
+ self.m_notebook2.AddPage( self.m_FWUpdatePnl, u"FW Update", False )
+
+ bSizer1.Add( self.m_notebook2, 1, wx.EXPAND |wx.ALL, 5 )
+
+
+ self.SetSizer( bSizer1 )
+ self.Layout()
+
+ self.Centre( wx.BOTH )
+
+ # Connect Events
+ self.m_SerialPortChoice.Bind( wx.EVT_CHOICE, self.onSerialPortSelection )
+ self.m_SerialBtn.Bind( wx.EVT_BUTTON, self.onSerialBtnClick )
+ self.m_MMSIText.Bind( wx.EVT_TEXT, self.onStationEdit )
+ self.m_CallsignText.Bind( wx.EVT_TEXT, self.onStationEdit )
+ self.m_NameText.Bind( wx.EVT_TEXT, self.onStationEdit )
+ self.m_VesselTypeChoice.Bind( wx.EVT_CHOICE, self.onStationEdit )
+ self.m_LengthText.Bind( wx.EVT_TEXT, self.onStationEdit )
+ self.m_BeamText.Bind( wx.EVT_TEXT, self.onStationEdit )
+ self.m_PortOffsetText.Bind( wx.EVT_TEXT, self.onStationEdit )
+ self.m_BowOffsetText.Bind( wx.EVT_TEXT, self.onStationEdit )
+ self.m_StationSaveBtn.Bind( wx.EVT_BUTTON, self.onStationSaveBtnClick )
+ self.m_FWBinaryPicker.Bind( wx.EVT_FILEPICKER_CHANGED, self.onFWBinarySelection )
+ self.m_FWUpdateBtn.Bind( wx.EVT_BUTTON, self.onFWUpdateBtnClick )
+
+ def __del__( self ):
+ pass
+
+
+ # Virtual event handlers, override them in your derived class
+ def onSerialPortSelection( self, event ):
+ event.Skip()
+
+ def onSerialBtnClick( self, event ):
+ event.Skip()
+
+ def onStationEdit( self, event ):
+ event.Skip()
+
+
+
+
+
+
+
+
+ def onStationSaveBtnClick( self, event ):
+ event.Skip()
+
+ def onFWBinarySelection( self, event ):
+ event.Skip()
+
+ def onFWUpdateBtnClick( self, event ):
+ event.Skip()
+
+
diff --git a/latest/Apps/maiana-update/mainwindow.py b/latest/Apps/maiana-update/mainwindow.py
new file mode 100644
index 0000000..77a6d9d
--- /dev/null
+++ b/latest/Apps/maiana-update/mainwindow.py
@@ -0,0 +1,146 @@
+from mainframe import MainFrame
+from maianaclient import MaianaClient, MaianaStatus
+import serial
+import wx
+from fwUpdateThread import *
+
+class MainWindow(MainFrame):
+ def __init__(self):
+ MainFrame.__init__(self, None)
+ self.m_SerialPortChoice.Set(MaianaClient.serial_ports())
+ self.portname = None
+ self.port = None
+ self.data = None
+
+ def onSerialPortSelection(self, event):
+ self.m_SerialBtn.Enable()
+
+ def onSerialBtnClick(self, event):
+ if self.port is None:
+ try:
+ self.portname = self.m_SerialPortChoice.GetString(self.m_SerialPortChoice.GetSelection())
+ self.port = serial.Serial(self.portname, 38400, timeout=1)
+ except:
+ wx.MessageBox(b'Unable to open port, it may be in use', 'Error', wx.OK | wx.ICON_ERROR)
+ return
+
+ status = MaianaClient.determineStatus(self.port)
+
+ if status == MaianaStatus.UNKNOWN:
+ pass
+ elif status == MaianaStatus.DFU:
+ wx.MessageBox(b'MAIANA is currently in firmware update mode. This is the only task you can perform.',
+ 'DFU warning', wx.OK)
+ self.enableUI()
+ self.m_SerialBtn.SetLabel(b'Disconnect')
+ self.m_StationSaveBtn.Disable()
+ else:
+ #Assuming status is RUNNING
+ self.m_SerialBtn.SetLabel(b'Disconnect')
+ self.enableUI()
+ if self.refreshSys():
+ self.refreshStation()
+ self.m_StationSaveBtn.Disable()
+ else:
+ self.port.close()
+ self.port = None
+ self.m_SerialBtn.SetLabel(b'Connect')
+ self.disableUI()
+
+ def onStationSaveBtnClick(self, event):
+ # Let's validate
+ if not self.validateStationInputs():
+ return
+
+ newdata = {'mmsi': int(self.m_MMSIText.Value),
+ 'name': self.m_NameText.Value,
+ 'callsign': self.m_CallsignText.Value,
+ 'len': int(self.m_LengthText.Value),
+ 'beam': int(self.m_BeamText.Value),
+ 'portoffset': int(self.m_PortOffsetText.Value),
+ 'bowoffset': int(self.m_BowOffsetText.Value),
+ 'type': MaianaClient.VESSEL_TYPES[self.m_VesselTypeChoice.Selection]}
+ #print(newdata)
+
+ if MaianaClient.setStationData(self.port, newdata):
+ self.stationdata = newdata
+ self.m_StationSaveBtn.Disable()
+
+
+ def onFWBinarySelection(self, event):
+ self.m_FWUpdateBtn.Enable()
+
+ def onFWUpdateBtnClick(self, event):
+ self.Connect(-1, -1, EVT_FWUPDATE_ID, self.onFwUpdateEvent)
+ thr = FWUpdateThread(self, self.port, self.m_FWBinaryPicker.Path)
+ thr.start()
+ self.m_FWUpdateBtn.Disable()
+
+ def onFwUpdateEvent(self, evt):
+ print(evt.eventType, evt.data)
+ if evt.eventType == EventType.DFU_ENABLED:
+ self.m_FWUpdateStatusLbl.SetLabel("DFU enabled")
+ elif evt.eventType == EventType.TRANSFER_STARTED:
+ self.m_FWUpdateStatusLbl.SetLabel("Transferring")
+ elif evt.eventType == EventType.TRANSFER_PROGRESS:
+ self.m_FWProgress.SetValue(evt.data * 100)
+ elif evt.eventType == EventType.TRANSFER_COMPLETE:
+ self.m_FWProgress.SetValue(0)
+ self.m_FWUpdateStatusLbl.SetLabel("Transfer completed")
+ self.refreshSys()
+ self.refreshStation()
+ elif evt.eventType == EventType.ERROR:
+ self.m_FWUpdateStatusLbl.SetLabel(evt.data)
+
+ def enableUI(self):
+ self.m_StationPnl.Enable()
+ self.m_FWUpdatePnl.Enable()
+
+ def disableUI(self):
+ self.m_StationPnl.Disable()
+ self.m_FWUpdatePnl.Disable()
+
+ def refreshSys(self):
+ self.sysdata = MaianaClient.loadSys(self.port)
+ return self.renderSys()
+
+ def refreshStation(self):
+ self.stationdata = MaianaClient.loadStation(self.port)
+ return self.renderStation()
+
+ def renderSys(self):
+ if not 'hw' in self.sysdata:
+ wx.MessageDialog(self, b'There was no response from MAIANA. Please check connections and try again.',
+ 'Timeout', wx.OK | wx.STAY_ON_TOP|wx.CENTRE).ShowModal()
+ return False
+
+ self.m_HWRevLbl.SetLabel(self.sysdata['hw'])
+ self.m_FWRevLbl.SetLabel(self.sysdata['fw'])
+ self.m_CPULbl.SetLabel(self.sysdata['cpu'])
+ return True
+
+ def renderStation(self):
+ if not 'mmsi' in self.stationdata:
+ wx.MessageDialog(self, b'There was no response from MAIANA. Please check connections and try again.',
+ 'Timeout', wx.OK | wx.STAY_ON_TOP|wx.CENTRE).ShowModal()
+ return False
+
+ self.m_MMSIText.SetValue('{}'.format(self.stationdata['mmsi']))
+ self.m_NameText.SetValue(self.stationdata['name'])
+ self.m_CallsignText.SetValue(self.stationdata['callsign'])
+ self.m_LengthText.SetValue('{}'.format(self.stationdata['len']))
+ self.m_BeamText.SetValue('{}'.format(self.stationdata['beam']))
+ self.m_PortOffsetText.SetValue('{}'.format(self.stationdata['portoffset']))
+ self.m_BowOffsetText.SetValue('{}'.format(self.stationdata['bowoffset']))
+
+ t = self.stationdata['type']
+ i = MaianaClient.VESSEL_TYPES.index(t)
+ self.m_VesselTypeChoice.SetSelection(i)
+ return True
+
+ def validateStationInputs(self):
+ return True
+
+ def onStationEdit( self, event ):
+ self.m_StationSaveBtn.Enable()
+ event.Skip()