gpscap.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. """
  2. gpscap - GPS/AIS capability dictionary class.
  3. This file is Copyright 2010 by the GPSD project
  4. SPDX-License-Identifier: BSD-2-clause
  5. """
  6. # This code runs compatibly under Python 2 and 3.x for x >= 2.
  7. # Preserve this property!
  8. from __future__ import absolute_import, print_function, division
  9. try:
  10. import configparser
  11. except ImportError:
  12. import ConfigParser as configparser
  13. class GPSDictionary(configparser.RawConfigParser):
  14. def __init__(self, *files):
  15. "Initialize the capability dictionary"
  16. configparser.RawConfigParser.__init__(self)
  17. if not files:
  18. files = ["gpscap.ini", "/usr/share/gpsd/gpscap.ini"]
  19. try:
  20. self.read(files, encoding='utf-8')
  21. except TypeError:
  22. self.read(files) # For Python 2.6
  23. # Resolve uses= members
  24. while True:
  25. keepgoing = False
  26. for section in self.sections():
  27. if self.has_option(section, "uses"):
  28. parent = self.get(section, "uses")
  29. if self.has_option(parent, "uses"):
  30. continue
  31. # Found a parent section without a uses = part.
  32. for heritable in self.options(parent):
  33. if not self.has_option(section, heritable):
  34. self.set(section,
  35. heritable,
  36. self.get(parent, heritable))
  37. keepgoing = True
  38. self.remove_option(section, "uses")
  39. if not keepgoing:
  40. break
  41. # Sanity check: All items must have a type field.
  42. for section in self.sections():
  43. if not self.has_option(section, "type"):
  44. raise configparser.Error("%s has no type" % section)
  45. if ((self.get(section, "type")
  46. not in ("engine", "vendor", "device"))):
  47. raise configparser.Error("%s has invalid type" % section)
  48. # Sanity check: All devices must point at a vendor object.
  49. # Side effect: build the lists of vendors and devices.
  50. self.vendors = []
  51. self.devices = []
  52. for section in self.sections():
  53. if self.get(section, "type") == "vendor":
  54. self.vendors.append(section)
  55. if self.get(section, "type") == "device":
  56. self.devices.append(section)
  57. self.vendors.sort()
  58. for section in self.sections():
  59. if self.get(section, "type") == "device":
  60. if not self.has_option(section, "vendor"):
  61. raise configparser.Error("%s has no vendor" % section)
  62. if self.get(section, "vendor") not in self.vendors:
  63. raise configparser.Error("%s has invalid vendor" % section)
  64. def HTMLDump(self, ofp):
  65. thead = """
  66. <table style='border:1px solid gray;font-size:small;background-color:#CCCCCC'>
  67. <caption>Listing %s devices from %s vendors</caption>
  68. <tr>
  69. <th>Name</th>
  70. <th>Packaging</th>
  71. <th>Engine</th>
  72. <th>Interface</th>
  73. <th>Tested with</th>
  74. <th>NMEA version</th>
  75. <th>PPS</th>
  76. <th style='width:50%%'>Notes</th>
  77. </tr>
  78. """
  79. vhead1 = "<tr><td style='text-align:center;' colspan='8'>" \
  80. "<a href='%s'>%s</a></td></tr>\n"
  81. vhead2 = "<tr><td style='text-align:center;' colspan='8'>" \
  82. "<a href='%s'>%s</a><br><p>%s</p></td></tr>\n"
  83. hotpluggables = ("pl2303", "CP2101")
  84. ofp.write(thead % (len(self.devices), len(self.vendors)))
  85. for vendor in self.vendors:
  86. if self.has_option(vendor, "notes"):
  87. ofp.write(vhead2 % (self.get(vendor, "vendor_site"), vendor,
  88. self.get(vendor, "notes")))
  89. else:
  90. ofp.write(vhead1 % (self.get(vendor, "vendor_site"), vendor))
  91. relevant = []
  92. for dev in self.devices:
  93. if self.get(dev, "vendor") == vendor:
  94. relevant.append(dev)
  95. relevant.sort()
  96. for dev in relevant:
  97. rowcolor = "white"
  98. if self.get(dev, "packaging") == "OEM module":
  99. rowcolor = "#32CD32"
  100. elif self.get(dev, "packaging") == "chipset":
  101. rowcolor = "#FFFFE0"
  102. elif self.get(dev, "packaging") == "handset":
  103. rowcolor = "#00FFFF"
  104. elif self.get(dev, "packaging") == "hansdfree":
  105. rowcolor = "#008B8B"
  106. ofp.write("<tr itemscope itemtype='http://schema.org/Product'"
  107. " style='background-color:%s'>\n" % rowcolor)
  108. namefield = dev
  109. if self.has_option(dev, "techdoc"):
  110. namefield = "<a href='%s'>%s</a>" \
  111. % (self.get(dev, "techdoc"), dev)
  112. if ((self.has_option(dev, "discontinued") and
  113. self.getboolean(dev, "discontinued"))):
  114. namefield = namefield + "&nbsp;<img title='Device " \
  115. "discontinued' src='discontinued.png' " \
  116. "alt='Discontinued icon'>"
  117. ofp.write("<td itemprop='name'>%s</td>\n" % namefield)
  118. ofp.write("<td>%s</td>\n" % self.get(dev, "packaging"))
  119. engine = self.get(dev, "engine")
  120. if self.has_option(engine, "techdoc"):
  121. engine = "<a href='%s'>%s</a>" \
  122. % (self.get(engine, "techdoc"), engine)
  123. if self.has_option(dev, "subtype"):
  124. engine += " (" + self.get(dev, "subtype") + ")"
  125. ofp.write("<td>%s</td>\n" % engine)
  126. interfaces = self.get(dev, "interfaces")
  127. if self.has_option(dev, "pps"):
  128. interfaces += ",PPS"
  129. ofp.write("<td>%s</td>\n" % interfaces)
  130. testfield = ""
  131. if self.has_option(dev, "tested"):
  132. tested = self.get(dev, "tested")
  133. if tested == "regression":
  134. testfield += "<img title='Have regression test' " \
  135. "src='regression.png' " \
  136. "alt='Regression-test icon'>"
  137. else:
  138. testfield += tested
  139. if ((self.has_option(dev, "configurable") and
  140. self.get(dev, "configurable") == 'insane')):
  141. testfield += "<img title='Requires -b option' " \
  142. "src='noconfigure.png' " \
  143. "alt='No-configure icon'>"
  144. if self.get(dev, "rating") == "excellent":
  145. testfield += "<img src='star.png' alt='Star icon'>" \
  146. "<img src='star.png' alt='Star icon'>" \
  147. "<img src='star.png' alt='Star icon'>" \
  148. "<img src='star.png' alt='Star icon'>"
  149. elif self.get(dev, "rating") == "good":
  150. testfield += "<img src='star.png' alt='Star icon'>" \
  151. "<img src='star.png' alt='Star icon'>" \
  152. "<img src='star.png' alt='Star icon'>"
  153. elif self.get(dev, "rating") == "fair":
  154. testfield += "<img src='star.png' alt='Star icon'>" \
  155. "<img src='star.png' alt='Star icon'>"
  156. elif self.get(dev, "rating") == "poor":
  157. testfield += "<img src='star.png' alt='Star icon'>"
  158. elif self.get(dev, "rating") == "broken":
  159. testfield += "<img title='Device is broken' " \
  160. "src='bomb.png' alt='Bomb icon'>"
  161. if ((self.has_option(dev, "usbchip") and
  162. self.get(dev, "usbchip") in hotpluggables)):
  163. testfield += "<img title='udev hotplug' " \
  164. "src='hotplug.png' alt='Hotplug icon'>"
  165. ofp.write("<td>%s</td>\n" % testfield)
  166. nmea = "&nbsp;"
  167. if self.has_option(dev, "nmea"):
  168. nmea = self.get(dev, "nmea")
  169. ofp.write("<td>%s</td>\n" % nmea)
  170. if ((self.has_option(dev, "pps") and
  171. self.get(dev, "pps") == "True")):
  172. pps_accuracy = time_offset = ""
  173. if self.has_option(dev, "pps_accuracy"):
  174. pps_accuracy = self.get(dev, "pps_accuracy")
  175. if self.has_option(dev, "time_offset"):
  176. time_offset = self.get(dev, "time_offset")
  177. if pps_accuracy and time_offset:
  178. ofp.write("<td>%s<br>%s</td>\n"
  179. % (pps_accuracy, time_offset))
  180. else:
  181. ofp.write("<td>?<br>\n")
  182. else:
  183. ofp.write("<td>No</td>\n")
  184. if self.has_option(dev, "notes"):
  185. notes = self.get(dev, "notes")
  186. else:
  187. notes = ""
  188. if self.has_option(dev, "submitter"):
  189. notes += " Reported by %s." % self.get(
  190. dev, "submitter").replace("@", "&#x40;").replace(
  191. "<", "&lt;").replace(">", "&gt;")
  192. ofp.write("<td itemscope itemtype='http://schema.org/"
  193. "description'>%s</td>\n" % notes)
  194. ofp.write("</tr>\n")
  195. ofp.write("</table>\n")
  196. if __name__ == "__main__":
  197. import sys
  198. try:
  199. d = GPSDictionary()
  200. d.HTMLDump(sys.stdout)
  201. except configparser.Error as e:
  202. sys.stderr.write(sys.argv[0] + ":%s\n" % e)
  203. raise SystemExit(1)
  204. # vim: set expandtab shiftwidth=4