Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Zielke <kg4gjy@takeovertheworld.org>2009-05-31 00:39:17 (GMT)
committer Jack Zielke <kg4gjy@takeovertheworld.org>2009-05-31 00:39:17 (GMT)
commit33bc81210c2bc293c79212755a49eb2432ef8ade (patch)
tree3e31604b07f97c2043327c94a363f072363364df
parentaba4abac7a2985a79e721fd494a7049878951dea (diff)
May 30 2009 - version 14 - cancel individual messages, auto-complete tocall, yahoo zip codesHEADmaster
-rw-r--r--NEWS3
-rw-r--r--TODO6
-rw-r--r--activity/activity.info2
-rwxr-xr-xaprs.py292
4 files changed, 230 insertions, 73 deletions
diff --git a/NEWS b/NEWS
index c0a9454..f27bf90 100644
--- a/NEWS
+++ b/NEWS
@@ -11,4 +11,5 @@ November 9 2008 - version 9 - journal works, message text in bold, scroll bugfix
November 22 2008 - version 10 - changed journal, old messages are bolded on load
December 29 2008 - version 11 - auto generate passwords, better bolding (NWS messages)
May 18 2009 - version 12 - cqsrvr, show new bulletins, better URIs, clear button
-May 19 2008 - version 13 - updated links, help, activity.info
+May 19 2009 - version 13 - updated links, help, activity.info
+May 30 2009 - version 14 - cancel individual messages, auto-complete tocall, yahoo zip codes
diff --git a/TODO b/TODO
index b773095..5f540a2 100644
--- a/TODO
+++ b/TODO
@@ -1,8 +1,5 @@
multi language
-cancel individual messages
-retry <timeout> messages
-
beep on message receive
cleanup timers on disconnect
@@ -11,3 +8,6 @@ cleanup old timers in self.timers[]
auto reconnect?
aprs.laptop.org?
+
+color coded status window
+gps
diff --git a/activity/activity.info b/activity/activity.info
index 7f725db..efa4d91 100644
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -1,6 +1,6 @@
[Activity]
name = APRS-XO
-activity_version = 13
+activity_version = 14
bundle_id = org.laptop.APRSXO
service_name = org.laptop.APRSXO
class = aprs.APRSActivity
diff --git a/aprs.py b/aprs.py
index 849ccce..8638d4a 100755
--- a/aprs.py
+++ b/aprs.py
@@ -31,9 +31,10 @@ from sugar.activity import activity
from sugar.bundle.activitybundle import ActivityBundle
from sugar.graphics.menuitem import MenuItem
from sugar.graphics.toolbutton import ToolButton
+from xml.dom.minidom import parseString
HOST = 'rotate.aprs2.net'
-#HOST = '192.168.50.6'
+#HOST = '192.168.50.14'
PORT = 14580
RECV_BUFFER = 4096
@@ -71,6 +72,10 @@ class APRSActivity(activity.Activity):
self.output_watch = []
self.cq_watch = []
self.current_message = {}
+ self.current_message_text = {}
+ self.current_message_count = {}
+ self.current_message_delay = {}
+ self.last_selected = ''
titlefont = pango.FontDescription('Sans bold 8')
mediumfont = pango.FontDescription('Sans 6.5')
@@ -198,7 +203,7 @@ class APRSActivity(activity.Activity):
self.passbutton = gtk.CheckButton()
self.passbutton.set_active(True)
- self.passbutton.connect("toggled", self.hide_password, "password")
+ self.passbutton.connect("toggled", self.hide_password)
bottomidentbox.pack_start(self.passbutton, False, False, 0)
self.passbutton.show()
@@ -434,23 +439,27 @@ class APRSActivity(activity.Activity):
rightwintopbox = gtk.HBox(False, 4)
clearbutton = gtk.Button()
-# clearbutton.set_label(" Cancel Messages ")
-# clearbutton.connect("clicked", self.clear_msg_queue)
- clearbutton.set_label(" Clear/Cancel ")
+ clearbutton.set_label(" Clear ")
clearbutton.connect("clicked", self.clear_message_button)
- rightwintopbox.pack_start(clearbutton, False, False, 5)
+ rightwintopbox.pack_start(clearbutton, False, False, 1)
clearbutton.show()
+ cancelbutton = gtk.Button()
+ cancelbutton.set_label(" Cancel ")
+ cancelbutton.connect("clicked", self.cancel_dialog)
+ rightwintopbox.pack_start(cancelbutton, False, False, 1)
+ cancelbutton.show()
+
self.cqbutton = gtk.CheckButton("CQ")
self.cqbutton.set_active(False)
- self.cqbutton.connect("toggled", self.enable_cq, "cq")
- rightwintopbox.pack_start(self.cqbutton, False, False, 5)
+ self.cqbutton.connect("toggled", self.enable_cq)
+ rightwintopbox.pack_start(self.cqbutton, False, False, 3)
self.cqbutton.show()
self.beaconbutton = gtk.CheckButton("Beacon every 10 minutes")
self.beaconbutton.set_active(True)
- self.beaconbutton.connect("toggled", self.enable_beacon, "beacon")
- rightwintopbox.pack_start(self.beaconbutton, False, False, 5)
+ self.beaconbutton.connect("toggled", self.enable_beacon)
+ rightwintopbox.pack_start(self.beaconbutton, False, False, 2)
self.beaconbutton.show()
rightwin.pack_start(rightwintopbox, False, False, 0)
@@ -476,31 +485,38 @@ class APRSActivity(activity.Activity):
messagebox = gtk.HBox(False, 4)
- self.messagecombo = gtk.combo_box_entry_new_text()
- self.messagecombo.append_text("ALL")
- self.messagecombo.append_text("BEACON")
- self.messagecombo.append_text("CQ")
- self.messagecombo.append_text("QST")
- self.messagecombo.append_text("CQSRVR")
- self.messagecombo.set_active(-1)
- self.messagedest = self.messagecombo.get_child()
- self.messagedest.set_max_length(9)
- self.messagedest.set_width_chars(5)
- self.messagedest.modify_font(smallfont)
- messagebox.pack_start(self.messagecombo, False, False, 0)
- self.messagecombo.show()
+ self.messagetocall = gtk.Entry()
+ self.messagetocall.set_max_length(9)
+ self.messagetocall.set_width_chars(9)
+ self.messagetocall.modify_font(smallfont)
+ tocallcompletion = gtk.EntryCompletion()
+ self.tocalllist = gtk.ListStore(str)
+ self.tocalllist.append(["ALL"])
+ self.tocalllist.append(["BEACON"])
+ self.tocalllist.append(["CQ"])
+ self.tocalllist.append(["QST"])
+ self.tocalllist.append(["CQSRVR"])
+ tocallcompletion.set_model(self.tocalllist)
+ self.messagetocall.set_completion(tocallcompletion)
+ tocallcompletion.set_text_column(0)
+ self.messagetocall.set_text("TO:")
+ self.messagetocall.select_region(0, -1)
+ tocallcompletion.connect("match-selected", self.tocall_selected)
+ messagebox.pack_start(self.messagetocall, False, False, 1)
+ self.messagetocall.show()
self.messagetext = gtk.Entry()
self.messagetext.set_max_length(67)
- self.messagetext.set_width_chars(31)
+ self.messagetext.set_width_chars(30)
self.messagetext.modify_font(smallfont)
- self.messagetext.connect("activate", self.send_message, self.messagetext)
+# self.messagetext.connect("activate", self.send_message, self.messagetext)
+ self.messagetext.connect("activate", self.send_message)
messagebox.pack_start(self.messagetext, False, False, 0)
self.messagetext.show()
messagebutton = gtk.Button()
messagebutton.set_label("Send")
- messagebutton.connect("clicked", self.send_message, "message")
+ messagebutton.connect("clicked", self.send_message)
messagebox.pack_start(messagebutton, False, False, 0)
messagebutton.show()
@@ -513,7 +529,7 @@ class APRSActivity(activity.Activity):
self.statusview.set_justification(gtk.JUSTIFY_LEFT)
self.statusview.modify_font(smallfont)
- self.statusbuffer.set_text("Position\nEnter your lat/long. You may leave the decimal minutes (mm)\nblank for position ambiguity. If you do not know your lat/long,\nenter your 5 digit zip code instead.\n\nCQ\nWhen selected, sends \"CQ CQ CQ From <Station Comment>\"\nto CQSRVR every 32 minutes.\n\nBeacon\nWhen selected, sends location data every 10 minutes. The\nbeacon will automatically stop when you edit personal\ninformation. Must be manually reselected after edit\ncompletion.\n\nThis message will self destruct when you press Connect.\n\nAPRS Copyright (c) Bob Bruninga WB4APR\n\n")
+ self.statusbuffer.set_text("Position\nEnter your lat/long. You may leave the decimal minutes (mm)\nblank for position ambiguity. If you do not know your lat/long,\nenter your 5 digit zip code instead.\n\nCQ\nWhen selected, sends \"CQ CQ CQ From <Station Comment>\"\nto CQSRVR every 32 minutes.\n\nBeacon\nWhen selected, sends location data. Automatically stops when\nyou edit personal information. Must be manually reselected.\n\nThis message will self destruct when you press Connect.\n\nAPRS Copyright (c) Bob Bruninga WB4APR\n")
self.statuswindow = gtk.ScrolledWindow()
self.statuswindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
@@ -560,14 +576,38 @@ class APRSActivity(activity.Activity):
self.messagebuffer.set_text("")
def clear_message_button(self, button=None):
- # cancel all outgoing messages, for now
+ temp_queue_list = self.queue_list.copy()
+ temp_message_list = self.message_list.copy()
+ temp_current_message = self.current_message.copy()
+ temp_recv_acks = self.recv_acks.copy()
+
+ # cancel all outgoing messages
self.clear_msg_queue()
- # clear bulletin list
+
+ # clear seen bulletin list
self.seen_bulletins = {}
+
# clear message screen
self.clear_message()
- # TODO re-add outgoing messages (reset text iters)
- # TODO re-add queued messages
+
+ # re-add outgoing messages (reset text iters)
+ if (temp_current_message != {}):
+ for call in temp_current_message:
+ sequence = temp_current_message[call]
+ id = "%s-%s" % (call, sequence)
+ message = self.current_message_text[call]
+ count = self.current_message_count[call]
+ delay = self.current_message_delay[call]
+ if (temp_recv_acks[id] != 1):
+ self.send_message(None, call, message, sequence, count, delay, False)
+
+ # re-add queued messages
+ if (temp_queue_list != {}):
+ for call in temp_queue_list:
+ for sequence in temp_queue_list[call]:
+ id = "%s-%s" % (call, sequence)
+ message = temp_message_list[id]
+ self.send_message(None, call, message, sequence)
def connect_aprs(self, button):
if (self.sock == None):
@@ -659,7 +699,7 @@ class APRSActivity(activity.Activity):
self.stop_cq()
# for test server only
- if(HOST == "192.168.50.6"):
+ if(HOST == "192.168.50.14"):
self.sock.sendall("q")
self.sock.close()
@@ -813,13 +853,14 @@ class APRSActivity(activity.Activity):
self.validating = False
return False
try:
- zipsock.connect(('geocoder.us', 80))
+ zipsock.connect(('local.yahooapis.com', 80))
except socket.error, msg:
self.status_write("[ERROR] %s\n" % msg[1])
self.validating = False
return False
try:
- zipsock.sendall("GET /service/csv/geocode?zip=%s\n" % self.ziptext.get_text())
+ APPID = "XtIUm.bV34FWFtK62fv24MszIckfwgYDjHJ1mTSmxoY2.iLe4zoPvPbM7Z8D"
+ zipsock.sendall("GET /MapsService/V1/geocode?appid=%s&zip=%s\n" % (APPID, self.ziptext.get_text()))
except socket.error, msg:
self.status_write("[ERROR] %s\n" % msg[1])
self.validating = False
@@ -837,7 +878,9 @@ class APRSActivity(activity.Activity):
self.calltext.set_text("X%s-%d%d" % (self.ziptext.get_text(), A, O))
self.passtext.set_text("-1")
try:
- (lat, lon, junk) = response.split(',', 2)
+ zipdata = parseString(response)
+ lat = (zipdata.getElementsByTagName('Latitude')[0]).childNodes[0].data.encode('iso-8859-1')
+ lon = (zipdata.getElementsByTagName('Longitude')[0]).childNodes[0].data.encode('iso-8859-1')
except:
self.status_write("%s\n" % response)
lat = "0"
@@ -968,7 +1011,7 @@ class APRSActivity(activity.Activity):
JournalData['cq'] = 'False'
callsignlist = []
- model = self.messagecombo.get_model()
+ model = self.tocalllist
iter = model.get_iter_first()
while iter:
callsignlist.append(model.get(iter, 0)[0])
@@ -1194,15 +1237,15 @@ class APRSActivity(activity.Activity):
pass
- def disable_beacon(self, widget, data=None):
+ def disable_beacon(self, widget):
if (self.sock != None):
self.beaconbutton.set_active(False)
- def enable_beacon(self, widget, data=None):
+ def enable_beacon(self, widget):
if (not self.validating and widget.get_active()):
self.validate_data()
- def raw_send(self, widget, data=None):
+ def raw_send(self, widget):
msg = "%s\n" % self.rawtext.get_text()
try:
self.sock.sendall(msg)
@@ -1215,11 +1258,11 @@ class APRSActivity(activity.Activity):
self.rawtext.set_text("")
return True
- def send_message(self, widget, data=None, tocall=None, message=None):
+ def send_message(self, widget=None, tocall=None, message=None, sequence=None, count=2, delay=7, start_timer=True):
sendnow = False
if (tocall == None and message == None):
- tocall = self.messagedest.get_text().upper()
+ tocall = self.messagetocall.get_text().upper()
message = self.messagetext.get_text()
if (message == ""):
@@ -1238,16 +1281,17 @@ class APRSActivity(activity.Activity):
return False
# add callsign to list if just entered
- if (self.messagecombo.get_active() == -1):
+ if (self.last_selected != tocall):
self.add_callsign(tocall, True)
# get a sequence number
- if (isbulletin):
- # don't waste a number on a bulletin
- sequence = ""
- else:
+ if (sequence == None):
sequence = "%s" % self.b90()
+ # in case clear window is called:
+ id = "%s-%s" % (tocall, sequence)
+ self.recv_acks[id] = 0
+
# TODO
# cancel message option - menu popup
@@ -1265,7 +1309,7 @@ class APRSActivity(activity.Activity):
self.messagetext.set_text("")
if (sendnow):
- self.send_msg_queue(tocall)
+ self.send_msg_queue(tocall, count, delay, start_timer)
def b90(self):
if (self.sequence > 8099):
@@ -1281,8 +1325,13 @@ class APRSActivity(activity.Activity):
if (self.recv_acks[id] == 1):
return False
else:
- replyack = self.replyack(tocall)
- self.send_data(":%s:%s{%s}%s" % (tocall.ljust(9), message, sequence, replyack))
+
+ isbulletin = self.bulletin_check(tocall)
+ if (isbulletin):
+ self.send_data(":%s:%s" % (tocall.ljust(9), message))
+ else:
+ replyack = self.replyack(tocall)
+ self.send_data(":%s:%s{%s}%s" % (tocall.ljust(9), message, sequence, replyack))
count_start = self.messagebuffer.get_iter_at_mark(self.message_marks[id])
count_end = self.messagebuffer.get_iter_at_mark(self.message_marks[id])
@@ -1319,11 +1368,15 @@ class APRSActivity(activity.Activity):
delay = 600
gobject.timeout_add(delay * 1000, self.msg_timer, tocall, message, sequence, count, delay)
+ # save count and delay for the clear button
+ self.current_message_count[tocall] = count
+ self.current_message_delay[tocall] = delay
+
# and stop this timer
return False
def add_callsign(self, callsign, activate):
- model = self.messagecombo.get_model()
+ model = self.tocalllist
notfound = True
iter = model.get_iter_first()
while iter:
@@ -1333,9 +1386,9 @@ class APRSActivity(activity.Activity):
break
iter = model.iter_next(iter)
if (notfound):
- self.messagecombo.prepend_text(callsign)
+ self.tocalllist.append([callsign])
if (activate):
- self.messagecombo.set_active(0)
+ self.messagetocall.set_text(callsign)
def bulletin_check(self, callsign):
# hard code CQSRVR
@@ -1387,7 +1440,7 @@ class APRSActivity(activity.Activity):
return True
- def send_msg_queue(self, call):
+ def send_msg_queue(self, call, count=2, delay=7, start_timer=True):
if (call in self.queue_list):
if (self.queue_list[call] == []):
del self.queue_list[call]
@@ -1401,16 +1454,24 @@ class APRSActivity(activity.Activity):
# record this so cancel can work on current messages
self.current_message[call] = sequence
+ # record this so clear can re-add current messages
+ self.current_message_text[call] = message
+
+ # save count and delay for the clear button
+ self.current_message_count[call] = count
+ self.current_message_delay[call] = delay
+
# send message
isbulletin = self.bulletin_check(call)
- if (isbulletin):
- self.send_data(":%s:%s" % (call.ljust(9), message))
- gobject.timeout_add(600 * 1000, self.msg_timer, call, message, "", 2, 600)
- else:
- replyack = self.replyack(call)
- self.send_data(":%s:%s{%s}%s" % (call.ljust(9), message, sequence, replyack))
- self.recv_acks["%s-%s" % (call, sequence)] = 0
- gobject.timeout_add(7 * 1000, self.msg_timer, call, message, sequence, 2, 7)
+ if (start_timer):
+ if (isbulletin):
+ self.send_data(":%s:%s" % (call.ljust(9), message))
+ gobject.timeout_add(600 * 1000, self.msg_timer, call, message, sequence, count, 600)
+ else:
+ replyack = self.replyack(call)
+ self.send_data(":%s:%s{%s}%s" % (call.ljust(9), message, sequence, replyack))
+ self.recv_acks["%s-%s" % (call, sequence)] = 0
+ gobject.timeout_add(delay * 1000, self.msg_timer, call, message, sequence, count, delay)
# reset the timestamp
line_start = self.messagebuffer.get_iter_at_mark(self.message_marks[id])
@@ -1441,10 +1502,10 @@ class APRSActivity(activity.Activity):
# add the counter
iter = self.messagebuffer.get_iter_at_mark(self.message_marks[id])
- self.messagebuffer.insert(iter, " 1/%s" % MAXRETRIES)
+ self.messagebuffer.insert(iter, " %i/%s" % (count - 1, MAXRETRIES))
def cancel_message(self, id):
- if (self.recv_acks[id] == 1):
+ if (id in self.recv_acks and self.recv_acks[id] == 1):
return
self.recv_acks[id] = 1
@@ -1525,7 +1586,7 @@ class APRSActivity(activity.Activity):
# convert to string and mask off the high bit so number is always positive
return str(hash & 0x7fff)
- def hide_password(self, widget, data=None):
+ def hide_password(self, widget):
if (widget.get_active()):
self.passtext.set_visibility(False)
else:
@@ -1571,22 +1632,117 @@ class APRSActivity(activity.Activity):
self.cq_watch = []
if (self.cqbutton.get_active()):
# send_message fails because disconnect happens too fast.
-# self.send_message(None, None, "CQSRVR", "U CQ")
+# self.send_message(None, "CQSRVR", "U CQ")
message = ":CQSRVR :U CQ{%s" % self.b90()
self.send_data(message)
- def enable_cq(self, widget, data=None):
+ def enable_cq(self, widget):
if (self.sock != None):
if (self.cqbutton.get_active()):
self.send_cq()
self.cq_watch.append(gobject.timeout_add(32 * 60 * 1000, self.send_cq))
else:
self.stop_cq()
- self.send_message(None, None, "CQSRVR", "U CQ")
+ self.send_message(None, "CQSRVR", "U CQ")
def send_cq(self):
if (self.sock == None):
return False
if (self.cqbutton.get_active()):
- self.send_message(None, None, "CQSRVR", "CQ CQ CQ From %s" % self.stationtext.get_text())
+ self.send_message(None, "CQSRVR", "CQ CQ CQ From %s" % self.stationtext.get_text())
+
+ def cancel_dialog(self, widget):
+ # I want this to be a palette popup menu instead of a dialog
+
+ canceldialog = gtk.Dialog("Cancel Messages", None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
+
+ if (self.queue_list == {} and self.current_message == {}):
+ label = gtk.Label(" No messages to cancel ")
+ canceldialog.vbox.pack_start(label, False, False, 0)
+ label.show()
+
+ separator = gtk.HSeparator()
+ canceldialog.vbox.pack_start(separator, False, False, 6)
+ separator.show()
+
+ else:
+
+ label = gtk.Label(" Click button to cancel message ")
+ canceldialog.vbox.pack_start(label, False, False, 0)
+ label.show()
+
+ separator = gtk.HSeparator()
+ canceldialog.vbox.pack_start(separator, False, False, 6)
+ separator.show()
+
+ if (self.current_message != {}):
+ label = gtk.Label(" Sending ")
+ canceldialog.vbox.pack_start(label, False, False, 0)
+ label.show()
+
+ for call in self.current_message:
+ sequence = self.current_message[call]
+ id = "%s-%s" % (call, sequence)
+ if (self.recv_acks[id] != 1):
+ button = gtk.Button()
+ button.set_label(str(call) + ", " + str(self.current_message_text[call]))
+ button.connect("clicked", self.cancel_cur_msg_button, call, sequence, id)
+ canceldialog.vbox.pack_start(button, False, False, 3)
+ button.show()
+
+ separator = gtk.HSeparator()
+ canceldialog.vbox.pack_start(separator, False, False, 6)
+ separator.show()
+
+ if (self.queue_list != {}):
+ label = gtk.Label(" In queue ")
+ canceldialog.vbox.pack_start(label, False, False, 0)
+ label.show()
+
+ for call in self.queue_list:
+ for sequence in self.queue_list[call]:
+ id = "%s-%s" % (call, sequence)
+ button = gtk.Button()
+ button.set_label(str(call) + ", " + str(self.message_list[id]))
+ button.connect("clicked", self.cancel_queue_msg_button, call, sequence, id)
+ canceldialog.vbox.pack_start(button, False, False, 3)
+ button.show()
+
+ separator = gtk.HSeparator()
+ canceldialog.vbox.pack_start(separator, False, False, 6)
+ separator.show()
+
+ button = gtk.Button()
+ button.set_label("Cancel All")
+ button.connect("clicked", self.cancel_all_button)
+ canceldialog.vbox.pack_start(button, False, False, 3)
+ button.show()
+
+ canceldialog.run()
+ canceldialog.destroy()
+
+ def cancel_all_button(self, widget):
+ # button.vbox.dialog.destroy()
+ widget.parent.parent.destroy()
+ self.clear_msg_queue()
+
+ def cancel_cur_msg_button(self, widget, call, sequence, id):
+ # button.vbox.dialog.destroy()
+ widget.parent.parent.destroy()
+ if (sequence == self.current_message[call]):
+ self.cancel_message(id)
+ del self.current_message[call]
+ self.send_msg_queue(call)
+
+ def cancel_queue_msg_button(self, widget, call, sequence, id):
+ # button.vbox.dialog.destroy()
+ widget.parent.parent.destroy()
+ if (sequence in self.queue_list[call]):
+ self.cancel_message(id)
+ del self.message_list[id]
+ self.queue_list[call].remove(sequence)
+ if (self.queue_list[call] == []):
+ del self.queue_list[call]
+ def tocall_selected(self, completion, model, iter):
+ self.last_selected = model[iter][0]