alphasign.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. #!/usr/bin/python
  2. import datetime
  3. import os
  4. import sys
  5. import serial
  6. import constants
  7. class Alpha:
  8. def __init__(self, device="/dev/ttyS0"):
  9. self.device = device
  10. self.type = "Z" # Type Code (see protocol)
  11. self.address = "00" # Sign Address (see protocol)
  12. self.mode = "rotate" # Default display mode
  13. self.position = "middle_line" # Appropriate for one-line signs
  14. self.debug = False
  15. def connect(self):
  16. """Establish connection to the device.
  17. Args:
  18. device: character device (default: /dev/ttyS0)
  19. """
  20. # TODO(ms): these settings can probably be tweaked and still support most of
  21. # the devices.
  22. self._conn = serial.Serial(port=self.device,
  23. baudrate=4800,
  24. parity=serial.PARITY_EVEN,
  25. stopbits=serial.STOPBITS_TWO,
  26. timeout=1,
  27. xonxoff=0,
  28. rtscts=0)
  29. def disconnect(self):
  30. if self._conn:
  31. self._conn.close()
  32. def _packet(self, contents):
  33. pkt = ("%s%s%s%s%s%s%s" % (constants.NUL * 5, constants.SOH, self.type,
  34. self.address, constants.STX, contents,
  35. constants.EOT))
  36. return pkt
  37. def _write(self, packet):
  38. if not self._conn:
  39. return
  40. if self.debug:
  41. print "Writing packet: %s" % repr(packet)
  42. self._conn.write(packet)
  43. def write_text(self, msg, label="A"):
  44. # [WRITE_TEXT][File Label][ESC][Display Position][Mode Code]
  45. # [Special Specifier][ASCII Message]
  46. packet = self._packet("%s%s%s%s%s%s" % (constants.WRITE_TEXT, label,
  47. constants.ESC,
  48. constants.positions[self.position],
  49. constants.modes[self.mode],
  50. msg))
  51. self._write(packet)
  52. def create_string(self, string_label="1", string_size=32):
  53. """Create a STRING.
  54. This is necessary to allocate memory for the STRING on the sign
  55. Args:
  56. string_label: label of the STRING to create
  57. string_size: size of the STRING to create, in bytes. 125 max.
  58. Default is 32.
  59. """
  60. if string_size > 125:
  61. string_size = 125
  62. size_hex = "%04x" % string_size
  63. packet = self._packet("%s%s%s%s%s%s%s%s%s%s%s%s%s" %
  64. (constants.WRITE_SPECIAL, "\$",
  65. "A", # call label.. why does this matter?
  66. "A", # text file type
  67. "U", # this TEXT file is unlocked
  68. "0100", # text file size in hex
  69. "FF", # text file's start time (FF = always)
  70. "00", # text file's stop time
  71. string_label,
  72. "B", # string file type
  73. "L", # this string file is locked
  74. size_hex,
  75. "0000")) # padding
  76. self._write(packet)
  77. def write_string(self, data, label="1"):
  78. """Write a STRING.
  79. Args:
  80. data: data to write
  81. label: STRING label to write
  82. """
  83. packet = self._packet("%s%s%s" % (constants.WRITE_STRING, label, data))
  84. self._write(packet)
  85. def call_string(self, string_label="1"):
  86. """Call a STRING.
  87. This is for inserting a STRING file into a TEXT file.
  88. Args:
  89. string_label: label of string to call (default: 1)
  90. Returns:
  91. control code of specified string label
  92. """
  93. return "\x10%s" % string_label
  94. def call_date(self, format=0):
  95. """Call date for insertion into a TEXT file.
  96. Args:
  97. format: integer from 0 to 9
  98. 0 - MM/DD/YY
  99. 1 - DD/MM/YY
  100. 2 - MM-DD-YY
  101. 3 - DD-MM-YY
  102. 4 - MM.DD.YY
  103. 5 - DD.MM.YY
  104. 6 - MM DD YY
  105. 7 - DD MM YY
  106. 8 - MMM.DD, YYYY
  107. 9 - Day of week
  108. Returns:
  109. formatted string to use in a TEXT
  110. """
  111. if format < 0 or format > 9:
  112. format = 0
  113. return "\x0B%s" % format
  114. def call_time(self):
  115. """Call time for insertion into a TEXT file.
  116. Returns:
  117. formatted string to use in a TEXT
  118. """
  119. return "\x13"
  120. def set_mode(self, mode):
  121. """FIXME
  122. """
  123. if mode in self.modes:
  124. self.mode = mode
  125. # FIXME: error handling for invalid mode
  126. def set_position(self, mode):
  127. """FIXME
  128. """
  129. if position in self.positions:
  130. self.position = position
  131. # FIXME: error handling
  132. def clear_memory(self):
  133. """Clear the sign's memory.
  134. """
  135. packet = self._packet("%s%s" % (constants.WRITE_SPECIAL, "$"))
  136. self._write(packet)
  137. def beep(self, frequency=0, duration=0.1, repeat=0):
  138. """Make the sign beep.
  139. Args:
  140. frequency: frequency integer (not in Hz), 0 - 254
  141. duration: beep duration, 0.1 - 1.5
  142. repeat: number of times to repeat, 0 - 15
  143. """
  144. if frequency < 0:
  145. frequency = 0
  146. elif frequency > 254:
  147. frequency = 254
  148. duration = int(duration / 0.1)
  149. if duration < 1:
  150. duration = 1
  151. elif duration > 15:
  152. duration = 15
  153. if repeat < 0:
  154. repeat = 0
  155. elif repeat > 15:
  156. repeat = 15
  157. packet = self._packet("%s%s%02X%X%X" % (constants.WRITE_SPECIAL, "(2",
  158. frequency, duration, repeat))
  159. self._write(packet)
  160. def soft_reset(self):
  161. """Perform a soft reset on the sign.
  162. This is non-destructive and does not clear the sign's memory.
  163. """
  164. packet = self._packet("%s%s" % (constants.WRITE_SPECIAL, ","))
  165. self._write(packet)
  166. def set_day(self, day=None):
  167. """Set the day of the week on the sign.
  168. If the argument is omitted, today's day will be used.
  169. Args:
  170. day (optional): integer between 1 (Sunday) and 7 (Saturday)
  171. """
  172. if day is None or day < 1 or day > 7:
  173. day = datetime.datetime.today().weekday() + 1
  174. packet = self._packet("%s%s%s" % (constants.WRITE_SPECIAL, "&", day))
  175. self._write(packet)
  176. def set_date(self, year=None, month=None, day=None):
  177. """Sets the date in the memory of the sign. This must be done each day to
  178. keep the clock 'up to date', because the sign will not automatically advance
  179. the day.
  180. If the date is not specified in the arguments, today's date will be used.
  181. Args:
  182. year (optional): two-digit year (98, 99, 00, ...)
  183. month (optional): integer month (1, 2, ..., 12)
  184. day (optional): integer day (1, ..., 31)
  185. """
  186. today = datetime.datetime.today()
  187. if year is None:
  188. year = str(today.year)[2:4]
  189. if month is None:
  190. month = today.month
  191. if day is None:
  192. day = today.day
  193. packet = self._packet("%s%s%02d%02d%02d" % (constants.WRITE_SPECIAL, ";",
  194. year, month, day))
  195. self._write(packet)
  196. def set_time(self, hour=None, minute=None):
  197. """Sets ths hour and minute of the internal clock on the sign.
  198. If the time is not specified in the arguments, the time now will be used.
  199. Args:
  200. hour: hour in 24-hour format (18 instead of 6 for 6PM)
  201. minute: minute (0 - 59)
  202. """
  203. now = datetime.datetime.today()
  204. if hour is None:
  205. hour = now.hour
  206. if minute is None:
  207. minute = now.minute
  208. packet = self._packet("%s%s%02d%02d" % (constants.WRITE_SPECIAL, "\x20",
  209. hour, minute))
  210. self._write(packet)
  211. def set_time_format(self, format=1):
  212. """Sets the time format on the sign.
  213. Args:
  214. format: 1 - 24-hour (military) time
  215. 0 - 12-hour (standard am/pm) format
  216. """
  217. if format < 0 or format > 1:
  218. format = 1
  219. byte = (format == 0) and "S" or "M"
  220. packet = this._packet("%s%s%s" % (constants.WRITE_SPECIAL, "\x27", byte))
  221. self._write(packet)
  222. def color(self, color="autocolor"):
  223. """Returns color code for a specified color.
  224. Args:
  225. color: color string
  226. Returns:
  227. FIXME
  228. """
  229. if color not in constants.colors:
  230. color = "autocolor"
  231. return "%s%s" % ("\x1C", constants.colors[color])
  232. def charset(self, charset="five_high_std"):
  233. """Returns control code for a specified character set.
  234. Args:
  235. charset: charset name string
  236. Returns:
  237. FIXME
  238. """
  239. if charset not in constants.charsets:
  240. charset = "five_high_std"
  241. return "%s%s" % ("\x1A", constants.charsets[charset])
  242. def extchar(self, extchar="left_arrow"):
  243. """Returns control code for a specified extended character.
  244. Args:
  245. extchar: extended character name
  246. Returns:
  247. FIXME
  248. """
  249. if extchar not in constants.extchars:
  250. extchar = "left_arrow"
  251. return "%s%s" % ("\x08", constants.extchars[extchar])
  252. def spacing(self, option=0):
  253. """Returns control code to set the character spacing.
  254. Args:
  255. option: 0 - set proportional characters
  256. 1 - fixed width left justified characters
  257. Returns:
  258. FIXME
  259. """
  260. byte = (option == 0) and "0" or "1"
  261. return "\x1E%s" % byte
  262. def speed(self, speed):
  263. """Set the speed of the scrolling text.
  264. Args:
  265. speed: integer 1 (slowest) through 5 (fastest) inclusive
  266. Returns:
  267. FIXME
  268. """
  269. if speed < 1:
  270. speed = 1
  271. elif speed > 5:
  272. speed = 5
  273. n = 20 + speed
  274. return chr(n)